2 * Copyright 2008-2011, Michael Lotz <mmlr@mlotz.ch>
3 * Distributed under the terms of the MIT license.
7 //! Driver for USB Human Interface Devices.
11 #include "HIDDevice.h"
12 #include "HIDReport.h"
13 #include "HIDWriter.h"
14 #include "ProtocolHandler.h"
15 #include "QuirkyDevices.h"
17 #include <usb/USB_hid.h>
26 HIDDevice::HIDDevice(usb_device device
, const usb_configuration_info
*config
,
27 size_t interfaceIndex
, int32 quirkyIndex
)
30 fInterfaceIndex(interfaceIndex
),
31 fTransferScheduled(0),
32 fTransferBufferSize(0),
33 fTransferBuffer(NULL
),
38 fProtocolHandlerCount(0),
39 fProtocolHandlerList(NULL
)
41 uint8
*reportDescriptor
= NULL
;
42 size_t descriptorLength
= 0;
44 const usb_device_descriptor
*deviceDescriptor
45 = gUSBModule
->get_device_descriptor(device
);
47 HIDWriter descriptorWriter
;
48 bool hasFixedDescriptor
= false;
49 if (quirkyIndex
>= 0) {
50 quirky_build_descriptor quirkyBuildDescriptor
51 = gQuirkyDevices
[quirkyIndex
].build_descriptor
;
53 if (quirkyBuildDescriptor
!= NULL
54 && quirkyBuildDescriptor(descriptorWriter
) == B_OK
) {
56 reportDescriptor
= (uint8
*)descriptorWriter
.Buffer();
57 descriptorLength
= descriptorWriter
.BufferLength();
58 hasFixedDescriptor
= true;
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
;
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
;
75 = hidDescriptor
->descriptor_info
[0].descriptor_length
;
80 if (hidDescriptor
== NULL
) {
81 TRACE_ALWAYS("didn't find a HID descriptor in the configuration, "
82 "trying to retrieve manually\n");
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
;
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
);
98 TRACE("get hid descriptor: result: 0x%08lx; length: %lu\n", result
,
100 if (result
== B_OK
) {
102 = hidDescriptor
->descriptor_info
[0].descriptor_length
;
104 descriptorLength
= 256; /* XXX */
105 TRACE_ALWAYS("failed to get HID descriptor, trying with a "
106 "fallback report descriptor length of %lu\n",
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
;
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
);
126 TRACE("get report descriptor: result: 0x%08lx; length: %lu\n", result
,
128 if (result
!= B_OK
) {
129 TRACE_ALWAYS("failed tot get report descriptor\n");
130 free(reportDescriptor
);
134 TRACE_ALWAYS("found quirky device, using patched descriptor\n");
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
,
143 int fd
= open(outputFile
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
145 write(fd
, reportDescriptor
, descriptorLength
);
150 status_t result
= fParser
.ParseReportDescriptor(reportDescriptor
,
152 if (!hasFixedDescriptor
)
153 free(reportDescriptor
);
155 if (result
!= B_OK
) {
156 TRACE_ALWAYS("parsing the report descriptor failed\n");
162 for (uint32 i
= 0; i
< fParser
.CountReports(HID_REPORT_TYPE_ANY
); i
++)
163 fParser
.ReportAt(HID_REPORT_TYPE_ANY
, i
)->PrintToStream();
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
;
179 if (fInterruptPipe
== 0) {
180 TRACE_ALWAYS("didn't find a suitable interrupt pipe\n");
184 fTransferBufferSize
= fParser
.MaxReportSize();
185 if (fTransferBufferSize
== 0) {
186 TRACE_ALWAYS("report claims a report size of 0\n");
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
;
201 if (quirkyIndex
>= 0) {
202 quirky_init_function quirkyInit
203 = gQuirkyDevices
[quirkyIndex
].init_function
;
205 if (quirkyInit
!= NULL
) {
206 fStatus
= quirkyInit(device
, config
, interfaceIndex
);
212 ProtocolHandler::AddHandlers(*this, fProtocolHandlerList
,
213 fProtocolHandlerCount
);
218 HIDDevice::~HIDDevice()
220 ProtocolHandler
*handler
= fProtocolHandlerList
;
221 while (handler
!= NULL
) {
222 ProtocolHandler
*next
= handler
->NextHandler();
227 free(fTransferBuffer
);
232 HIDDevice::SetParentCookie(int32 cookie
)
234 fParentCookie
= cookie
;
239 HIDDevice::Open(ProtocolHandler
*handler
, uint32 flags
)
241 atomic_add(&fOpenCount
, 1);
247 HIDDevice::Close(ProtocolHandler
*handler
)
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.
261 gUSBModule
->cancel_queued_transfers(fInterruptPipe
);
266 HIDDevice::MaybeScheduleTransfer()
271 if (atomic_get_and_set(&fTransferScheduled
, 1) != 0) {
272 // someone else already caused a transfer to be scheduled
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
290 HIDDevice::SendReport(HIDReport
*report
)
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
);
301 HIDDevice::ProtocolHandlerAt(uint32 index
) const
303 ProtocolHandler
*handler
= fProtocolHandlerList
;
304 while (handler
!= NULL
) {
308 handler
= handler
->NextHandler();
317 HIDDevice::_UnstallCallback(void *cookie
, status_t status
, void *data
,
320 HIDDevice
*device
= (HIDDevice
*)cookie
;
321 if (status
!= B_OK
) {
322 TRACE_ALWAYS("Unable to unstall device: %s\n", strerror(status
));
325 // Now report the original failure, since we're ready to retry
326 _TransferCallback(cookie
, B_ERROR
, device
->fTransferBuffer
, 0);
331 HIDDevice::_TransferCallback(void *cookie
, status_t status
, void *data
,
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
);
344 atomic_set(&device
->fTransferScheduled
, 0);
345 device
->fParser
.SetReport(status
, device
->fTransferBuffer
, actualLength
);