1/*
2 * Copyright 2008-2011, Michael Lotz <mmlr@mlotz.ch>
3 * Distributed under the terms of the MIT license.
4 */
5
6
7//!	Driver for USB Human Interface Devices.
8
9
10#include "Driver.h"
11#include "HIDDevice.h"
12#include "HIDReport.h"
13#include "HIDWriter.h"
14#include "ProtocolHandler.h"
15#include "QuirkyDevices.h"
16
17#include <usb/USB_hid.h>
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22#include <unistd.h>
23#include <new>
24
25
26HIDDevice::HIDDevice(usb_device device, const usb_configuration_info *config,
27	size_t interfaceIndex, int32 quirkyIndex)
28	:	fStatus(B_NO_INIT),
29		fDevice(device),
30		fInterfaceIndex(interfaceIndex),
31		fTransferScheduled(0),
32		fTransferBufferSize(0),
33		fTransferBuffer(NULL),
34		fParentCookie(-1),
35		fOpenCount(0),
36		fRemoved(false),
37		fParser(this),
38		fProtocolHandlerCount(0),
39		fProtocolHandlerList(NULL)
40{
41	uint8 *reportDescriptor = NULL;
42	size_t descriptorLength = 0;
43
44	const usb_device_descriptor *deviceDescriptor
45		= gUSBModule->get_device_descriptor(device);
46
47	HIDWriter descriptorWriter;
48	bool hasFixedDescriptor = false;
49	if (quirkyIndex >= 0) {
50		quirky_build_descriptor quirkyBuildDescriptor
51			= gQuirkyDevices[quirkyIndex].build_descriptor;
52
53		if (quirkyBuildDescriptor != NULL
54			&& quirkyBuildDescriptor(descriptorWriter) == B_OK) {
55
56			reportDescriptor = (uint8 *)descriptorWriter.Buffer();
57			descriptorLength = descriptorWriter.BufferLength();
58			hasFixedDescriptor = true;
59		}
60	}
61
62	if (!hasFixedDescriptor) {
63		// Conforming device, find the HID descriptor and get the report
64		// descriptor from the device.
65		usb_hid_descriptor *hidDescriptor = NULL;
66
67		const usb_interface_info *interfaceInfo
68			= config->interface[interfaceIndex].active;
69		for (size_t i = 0; i < interfaceInfo->generic_count; i++) {
70			const usb_generic_descriptor &generic
71				= interfaceInfo->generic[i]->generic;
72			if (generic.descriptor_type == B_USB_HID_DESCRIPTOR_HID) {
73				hidDescriptor = (usb_hid_descriptor *)&generic;
74				descriptorLength
75					= hidDescriptor->descriptor_info[0].descriptor_length;
76				break;
77			}
78		}
79
80		if (hidDescriptor == NULL) {
81			TRACE_ALWAYS("didn't find a HID descriptor in the configuration, "
82				"trying to retrieve manually\n");
83
84			descriptorLength = sizeof(usb_hid_descriptor);
85			hidDescriptor = (usb_hid_descriptor *)malloc(descriptorLength);
86			if (hidDescriptor == NULL) {
87				TRACE_ALWAYS("failed to allocate buffer for hid descriptor\n");
88				fStatus = B_NO_MEMORY;
89				return;
90			}
91
92			status_t result = gUSBModule->send_request(device,
93				USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD,
94				USB_REQUEST_GET_DESCRIPTOR,
95				B_USB_HID_DESCRIPTOR_HID << 8, interfaceIndex, descriptorLength,
96				hidDescriptor, &descriptorLength);
97
98			TRACE("get hid descriptor: result: 0x%08" B_PRIx32 "; length: %lu"
99				"\n", result, descriptorLength);
100			if (result == B_OK) {
101				descriptorLength
102					= hidDescriptor->descriptor_info[0].descriptor_length;
103			} else {
104				descriptorLength = 256; /* XXX */
105				TRACE_ALWAYS("failed to get HID descriptor, trying with a "
106					"fallback report descriptor length of %lu\n",
107					descriptorLength);
108			}
109
110			free(hidDescriptor);
111		}
112
113		reportDescriptor = (uint8 *)malloc(descriptorLength);
114		if (reportDescriptor == NULL) {
115			TRACE_ALWAYS("failed to allocate buffer for report descriptor\n");
116			fStatus = B_NO_MEMORY;
117			return;
118		}
119
120		status_t result = gUSBModule->send_request(device,
121			USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD,
122			USB_REQUEST_GET_DESCRIPTOR,
123			B_USB_HID_DESCRIPTOR_REPORT << 8, interfaceIndex, descriptorLength,
124			reportDescriptor, &descriptorLength);
125
126		TRACE("get report descriptor: result: 0x%08" B_PRIx32 "; length: %"
127			B_PRIuSIZE "\n", result, descriptorLength);
128		if (result != B_OK) {
129			TRACE_ALWAYS("failed tot get report descriptor\n");
130			free(reportDescriptor);
131			return;
132		}
133	} else {
134		TRACE_ALWAYS("found quirky device, using patched descriptor\n");
135	}
136
137#if 1
138	// save report descriptor for troubleshooting
139	char outputFile[128];
140	sprintf(outputFile, "/tmp/usb_hid_report_descriptor_%04x_%04x_%lu.bin",
141		deviceDescriptor->vendor_id, deviceDescriptor->product_id,
142		interfaceIndex);
143	int fd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
144	if (fd >= 0) {
145		write(fd, reportDescriptor, descriptorLength);
146		close(fd);
147	}
148#endif
149
150	status_t result = fParser.ParseReportDescriptor(reportDescriptor,
151		descriptorLength);
152	if (!hasFixedDescriptor)
153		free(reportDescriptor);
154
155	if (result != B_OK) {
156		TRACE_ALWAYS("parsing the report descriptor failed\n");
157		fStatus = result;
158		return;
159	}
160
161#if 0
162	for (uint32 i = 0; i < fParser.CountReports(HID_REPORT_TYPE_ANY); i++)
163		fParser.ReportAt(HID_REPORT_TYPE_ANY, i)->PrintToStream();
164#endif
165
166	// find the interrupt in pipe
167	usb_interface_info *interface = config->interface[interfaceIndex].active;
168	for (size_t i = 0; i < interface->endpoint_count; i++) {
169		usb_endpoint_descriptor *descriptor = interface->endpoint[i].descr;
170		if ((descriptor->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
171			&& (descriptor->attributes & USB_ENDPOINT_ATTR_MASK)
172				== USB_ENDPOINT_ATTR_INTERRUPT) {
173			fEndpointAddress = descriptor->endpoint_address;
174			fInterruptPipe = interface->endpoint[i].handle;
175			break;
176		}
177	}
178
179	if (fInterruptPipe == 0) {
180		TRACE_ALWAYS("didn't find a suitable interrupt pipe\n");
181		return;
182	}
183
184	fTransferBufferSize = fParser.MaxReportSize(HID_REPORT_TYPE_INPUT);
185	if (fTransferBufferSize == 0) {
186		TRACE_ALWAYS("report claims a report size of 0\n");
187		return;
188	}
189
190	// We pad the allocation size so that we can always read 32 bits at a time
191	// (as done in HIDReportItem) without the need for an additional boundary
192	// check. We don't increase the transfer buffer size though as to not expose
193	// this implementation detail onto the device when scheduling transfers.
194	fTransferBuffer = (uint8 *)malloc(fTransferBufferSize + 3);
195	if (fTransferBuffer == NULL) {
196		TRACE_ALWAYS("failed to allocate transfer buffer\n");
197		fStatus = B_NO_MEMORY;
198		return;
199	}
200
201	if (quirkyIndex >= 0) {
202		quirky_init_function quirkyInit
203			= gQuirkyDevices[quirkyIndex].init_function;
204
205		if (quirkyInit != NULL) {
206			fStatus = quirkyInit(device, config, interfaceIndex);
207			if (fStatus != B_OK)
208				return;
209		}
210	}
211
212	ProtocolHandler::AddHandlers(*this, fProtocolHandlerList,
213		fProtocolHandlerCount);
214	fStatus = B_OK;
215}
216
217
218HIDDevice::~HIDDevice()
219{
220	ProtocolHandler *handler = fProtocolHandlerList;
221	while (handler != NULL) {
222		ProtocolHandler *next = handler->NextHandler();
223		delete handler;
224		handler = next;
225	}
226
227	free(fTransferBuffer);
228}
229
230
231void
232HIDDevice::SetParentCookie(int32 cookie)
233{
234	fParentCookie = cookie;
235}
236
237
238status_t
239HIDDevice::Open(ProtocolHandler *handler, uint32 flags)
240{
241	atomic_add(&fOpenCount, 1);
242	return B_OK;
243}
244
245
246status_t
247HIDDevice::Close(ProtocolHandler *handler)
248{
249	atomic_add(&fOpenCount, -1);
250	gUSBModule->cancel_queued_transfers(fInterruptPipe);
251		// This will wake up any listeners. Whether they should close or retry
252		// is handeled internally by the handlers.
253	return B_OK;
254}
255
256
257void
258HIDDevice::Removed()
259{
260	fRemoved = true;
261	gUSBModule->cancel_queued_transfers(fInterruptPipe);
262}
263
264
265status_t
266HIDDevice::MaybeScheduleTransfer(HIDReport*)
267{
268	if (fRemoved)
269		return ENODEV;
270
271	if (atomic_get_and_set(&fTransferScheduled, 1) != 0) {
272		// someone else already caused a transfer to be scheduled
273		return B_OK;
274	}
275
276	TRACE("scheduling interrupt transfer of %lu bytes\n", fTransferBufferSize);
277	status_t result = gUSBModule->queue_interrupt(fInterruptPipe,
278		fTransferBuffer, fTransferBufferSize, _TransferCallback, this);
279	if (result != B_OK) {
280		TRACE_ALWAYS("failed to schedule interrupt transfer 0x%08" B_PRIx32
281			"\n", result);
282		return result;
283	}
284
285	return B_OK;
286}
287
288
289status_t
290HIDDevice::SendReport(HIDReport *report)
291{
292	size_t actualLength;
293	return gUSBModule->send_request(fDevice,
294		USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS,
295		B_USB_REQUEST_HID_SET_REPORT, 0x200 | report->ID(), fInterfaceIndex,
296		report->ReportSize(), report->CurrentReport(), &actualLength);
297}
298
299
300ProtocolHandler *
301HIDDevice::ProtocolHandlerAt(uint32 index) const
302{
303	ProtocolHandler *handler = fProtocolHandlerList;
304	while (handler != NULL) {
305		if (index == 0)
306			return handler;
307
308		handler = handler->NextHandler();
309		index--;
310	}
311
312	return NULL;
313}
314
315
316void
317HIDDevice::_UnstallCallback(void *cookie, status_t status, void *data,
318	size_t actualLength)
319{
320	HIDDevice *device = (HIDDevice *)cookie;
321	if (status != B_OK) {
322		TRACE_ALWAYS("Unable to unstall device: %s\n", strerror(status));
323	}
324
325	// Now report the original failure, since we're ready to retry
326	_TransferCallback(cookie, B_ERROR, device->fTransferBuffer, 0);
327}
328
329
330void
331HIDDevice::_TransferCallback(void *cookie, status_t status, void *data,
332	size_t actualLength)
333{
334	HIDDevice *device = (HIDDevice *)cookie;
335	if (status == B_DEV_STALLED && !device->fRemoved) {
336		// try clearing stalls right away, the report listeners will resubmit
337		gUSBModule->queue_request(device->fDevice,
338			USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
339			USB_REQUEST_CLEAR_FEATURE, USB_FEATURE_ENDPOINT_HALT,
340			device->fEndpointAddress, 0, NULL, _UnstallCallback, device);
341		return;
342	}
343
344	atomic_set(&device->fTransferScheduled, 0);
345	device->fParser.SetReport(status, device->fTransferBuffer, actualLength);
346}
347