xref: /haiku/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
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 
26 HIDDevice::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 
218 HIDDevice::~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 
231 void
232 HIDDevice::SetParentCookie(int32 cookie)
233 {
234 	fParentCookie = cookie;
235 }
236 
237 
238 status_t
239 HIDDevice::Open(ProtocolHandler *handler, uint32 flags)
240 {
241 	atomic_add(&fOpenCount, 1);
242 	return B_OK;
243 }
244 
245 
246 status_t
247 HIDDevice::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 
257 void
258 HIDDevice::Removed()
259 {
260 	fRemoved = true;
261 	gUSBModule->cancel_queued_transfers(fInterruptPipe);
262 }
263 
264 
265 status_t
266 HIDDevice::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 
289 status_t
290 HIDDevice::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 
300 ProtocolHandler *
301 HIDDevice::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 
316 void
317 HIDDevice::_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 
330 void
331 HIDDevice::_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