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%08lx; length: %lu\n", result,
|
---|
99 | 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%08lx; length: %lu\n", result,
|
---|
127 | 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();
|
---|
185 | if (fTransferBufferSize == 0) {
|
---|
186 | TRACE_ALWAYS("report claims a report size of 0\n");
|
---|
187 | return;
|
---|
188 | } else if ( fTransferBufferSize == 7 ) {
|
---|
189 | //XXX: Dirty Hack for my decus gaming mouse!
|
---|
190 | fTransferBufferSize = 8;
|
---|
191 | }
|
---|
192 |
|
---|
193 | TRACE_ALWAYS("fTransferBufferSize = %d\n", fTransferBufferSize );
|
---|
194 |
|
---|
195 | // We pad the allocation size so that we can always read 32 bits at a time
|
---|
196 | // (as done in HIDReportItem) without the need for an additional boundary
|
---|
197 | // check. We don't increase the transfer buffer size though as to not expose
|
---|
198 | // this implementation detail onto the device when scheduling transfers.
|
---|
199 | fTransferBuffer = (uint8 *)malloc(fTransferBufferSize + 3);
|
---|
200 | if (fTransferBuffer == NULL) {
|
---|
201 | TRACE_ALWAYS("failed to allocate transfer buffer\n");
|
---|
202 | fStatus = B_NO_MEMORY;
|
---|
203 | return;
|
---|
204 | }
|
---|
205 |
|
---|
206 | if (quirkyIndex >= 0) {
|
---|
207 | quirky_init_function quirkyInit
|
---|
208 | = gQuirkyDevices[quirkyIndex].init_function;
|
---|
209 |
|
---|
210 | if (quirkyInit != NULL) {
|
---|
211 | fStatus = quirkyInit(device, config, interfaceIndex);
|
---|
212 | if (fStatus != B_OK)
|
---|
213 | return;
|
---|
214 | }
|
---|
215 | }
|
---|
216 |
|
---|
217 | ProtocolHandler::AddHandlers(*this, fProtocolHandlerList,
|
---|
218 | fProtocolHandlerCount);
|
---|
219 | fStatus = B_OK;
|
---|
220 | }
|
---|
221 |
|
---|
222 |
|
---|
223 | HIDDevice::~HIDDevice()
|
---|
224 | {
|
---|
225 | ProtocolHandler *handler = fProtocolHandlerList;
|
---|
226 | while (handler != NULL) {
|
---|
227 | ProtocolHandler *next = handler->NextHandler();
|
---|
228 | delete handler;
|
---|
229 | handler = next;
|
---|
230 | }
|
---|
231 |
|
---|
232 | free(fTransferBuffer);
|
---|
233 | }
|
---|
234 |
|
---|
235 |
|
---|
236 | void
|
---|
237 | HIDDevice::SetParentCookie(int32 cookie)
|
---|
238 | {
|
---|
239 | fParentCookie = cookie;
|
---|
240 | }
|
---|
241 |
|
---|
242 |
|
---|
243 | status_t
|
---|
244 | HIDDevice::Open(ProtocolHandler *handler, uint32 flags)
|
---|
245 | {
|
---|
246 | atomic_add(&fOpenCount, 1);
|
---|
247 | return B_OK;
|
---|
248 | }
|
---|
249 |
|
---|
250 |
|
---|
251 | status_t
|
---|
252 | HIDDevice::Close(ProtocolHandler *handler)
|
---|
253 | {
|
---|
254 | atomic_add(&fOpenCount, -1);
|
---|
255 | gUSBModule->cancel_queued_transfers(fInterruptPipe);
|
---|
256 | // This will wake up any listeners. Whether they should close or retry
|
---|
257 | // is handeled internally by the handlers.
|
---|
258 | return B_OK;
|
---|
259 | }
|
---|
260 |
|
---|
261 |
|
---|
262 | void
|
---|
263 | HIDDevice::Removed()
|
---|
264 | {
|
---|
265 | fRemoved = true;
|
---|
266 | gUSBModule->cancel_queued_transfers(fInterruptPipe);
|
---|
267 | }
|
---|
268 |
|
---|
269 |
|
---|
270 | status_t
|
---|
271 | HIDDevice::MaybeScheduleTransfer()
|
---|
272 | {
|
---|
273 | if (fRemoved)
|
---|
274 | return B_ERROR;
|
---|
275 |
|
---|
276 | if (atomic_get_and_set(&fTransferScheduled, 1) != 0) {
|
---|
277 | // someone else already caused a transfer to be scheduled
|
---|
278 | return B_OK;
|
---|
279 | }
|
---|
280 |
|
---|
281 | TRACE("scheduling interrupt transfer of %lu bytes\n", fTransferBufferSize);
|
---|
282 | status_t result = gUSBModule->queue_interrupt(fInterruptPipe,
|
---|
283 | fTransferBuffer, fTransferBufferSize, _TransferCallback, this);
|
---|
284 | if (result != B_OK) {
|
---|
285 | TRACE_ALWAYS("failed to schedule interrupt transfer 0x%08" B_PRIx32
|
---|
286 | "\n", result);
|
---|
287 | return result;
|
---|
288 | }
|
---|
289 |
|
---|
290 | return B_OK;
|
---|
291 | }
|
---|
292 |
|
---|
293 |
|
---|
294 | status_t
|
---|
295 | HIDDevice::SendReport(HIDReport *report)
|
---|
296 | {
|
---|
297 | size_t actualLength;
|
---|
298 | return gUSBModule->send_request(fDevice,
|
---|
299 | USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS,
|
---|
300 | B_USB_REQUEST_HID_SET_REPORT, 0x200 | report->ID(), fInterfaceIndex,
|
---|
301 | report->ReportSize(), report->CurrentReport(), &actualLength);
|
---|
302 | }
|
---|
303 |
|
---|
304 |
|
---|
305 | ProtocolHandler *
|
---|
306 | HIDDevice::ProtocolHandlerAt(uint32 index) const
|
---|
307 | {
|
---|
308 | ProtocolHandler *handler = fProtocolHandlerList;
|
---|
309 | while (handler != NULL) {
|
---|
310 | if (index == 0)
|
---|
311 | return handler;
|
---|
312 |
|
---|
313 | handler = handler->NextHandler();
|
---|
314 | index--;
|
---|
315 | }
|
---|
316 |
|
---|
317 | return NULL;
|
---|
318 | }
|
---|
319 |
|
---|
320 |
|
---|
321 | void
|
---|
322 | HIDDevice::_UnstallCallback(void *cookie, status_t status, void *data,
|
---|
323 | size_t actualLength)
|
---|
324 | {
|
---|
325 | HIDDevice *device = (HIDDevice *)cookie;
|
---|
326 | if (status != B_OK) {
|
---|
327 | TRACE_ALWAYS("Unable to unstall device: %s\n", strerror(status));
|
---|
328 | }
|
---|
329 |
|
---|
330 | // Now report the original failure, since we're ready to retry
|
---|
331 | _TransferCallback(cookie, B_ERROR, device->fTransferBuffer, 0);
|
---|
332 | }
|
---|
333 |
|
---|
334 |
|
---|
335 | void
|
---|
336 | HIDDevice::_TransferCallback(void *cookie, status_t status, void *data,
|
---|
337 | size_t actualLength)
|
---|
338 | {
|
---|
339 | HIDDevice *device = (HIDDevice *)cookie;
|
---|
340 | if (status == B_DEV_STALLED && !device->fRemoved) {
|
---|
341 | // try clearing stalls right away, the report listeners will resubmit
|
---|
342 | TRACE_ALWAYS("Trying to unstall device %u\n", device->fDevice );
|
---|
343 | gUSBModule->queue_request(device->fDevice,
|
---|
344 | USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
|
---|
345 | USB_REQUEST_CLEAR_FEATURE, USB_FEATURE_ENDPOINT_HALT,
|
---|
346 | device->fEndpointAddress, 0, NULL, _UnstallCallback, device);
|
---|
347 | return;
|
---|
348 | }
|
---|
349 |
|
---|
350 | atomic_set(&device->fTransferScheduled, 0);
|
---|
351 | device->fParser.SetReport(status, device->fTransferBuffer, actualLength);
|
---|
352 | }
|
---|