BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / drivers / input / usb_hid / HIDDevice.cpp
blobc444ef60fe20a41d64f79c4a7c458a3019f52e24
1 /*
2 * Copyright 2008-2011, Michael Lotz <mmlr@mlotz.ch>
3 * Distributed under the terms of the MIT license.
4 */
7 //! Driver for USB Human Interface Devices.
10 #include "Driver.h"
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>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <new>
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)
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;
74 descriptorLength
75 = hidDescriptor->descriptor_info[0].descriptor_length;
76 break;
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;
89 return;
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,
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);
110 free(hidDescriptor);
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;
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,
127 descriptorLength);
128 if (result != B_OK) {
129 TRACE_ALWAYS("failed tot get report descriptor\n");
130 free(reportDescriptor);
131 return;
133 } else {
134 TRACE_ALWAYS("found quirky device, using patched descriptor\n");
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);
148 #endif
150 status_t result = fParser.ParseReportDescriptor(reportDescriptor,
151 descriptorLength);
152 if (!hasFixedDescriptor)
153 free(reportDescriptor);
155 if (result != B_OK) {
156 TRACE_ALWAYS("parsing the report descriptor failed\n");
157 fStatus = result;
158 return;
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
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;
179 if (fInterruptPipe == 0) {
180 TRACE_ALWAYS("didn't find a suitable interrupt pipe\n");
181 return;
184 fTransferBufferSize = fParser.MaxReportSize();
185 if (fTransferBufferSize == 0) {
186 TRACE_ALWAYS("report claims a report size of 0\n");
187 return;
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;
201 if (quirkyIndex >= 0) {
202 quirky_init_function quirkyInit
203 = gQuirkyDevices[quirkyIndex].init_function;
205 if (quirkyInit != NULL) {
206 fStatus = quirkyInit(device, config, interfaceIndex);
207 if (fStatus != B_OK)
208 return;
212 ProtocolHandler::AddHandlers(*this, fProtocolHandlerList,
213 fProtocolHandlerCount);
214 fStatus = B_OK;
218 HIDDevice::~HIDDevice()
220 ProtocolHandler *handler = fProtocolHandlerList;
221 while (handler != NULL) {
222 ProtocolHandler *next = handler->NextHandler();
223 delete handler;
224 handler = next;
227 free(fTransferBuffer);
231 void
232 HIDDevice::SetParentCookie(int32 cookie)
234 fParentCookie = cookie;
238 status_t
239 HIDDevice::Open(ProtocolHandler *handler, uint32 flags)
241 atomic_add(&fOpenCount, 1);
242 return B_OK;
246 status_t
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.
253 return B_OK;
257 void
258 HIDDevice::Removed()
260 fRemoved = true;
261 gUSBModule->cancel_queued_transfers(fInterruptPipe);
265 status_t
266 HIDDevice::MaybeScheduleTransfer()
268 if (fRemoved)
269 return B_ERROR;
271 if (atomic_get_and_set(&fTransferScheduled, 1) != 0) {
272 // someone else already caused a transfer to be scheduled
273 return B_OK;
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;
285 return B_OK;
289 status_t
290 HIDDevice::SendReport(HIDReport *report)
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);
300 ProtocolHandler *
301 HIDDevice::ProtocolHandlerAt(uint32 index) const
303 ProtocolHandler *handler = fProtocolHandlerList;
304 while (handler != NULL) {
305 if (index == 0)
306 return handler;
308 handler = handler->NextHandler();
309 index--;
312 return NULL;
316 void
317 HIDDevice::_UnstallCallback(void *cookie, status_t status, void *data,
318 size_t actualLength)
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);
330 void
331 HIDDevice::_TransferCallback(void *cookie, status_t status, void *data,
332 size_t actualLength)
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;
344 atomic_set(&device->fTransferScheduled, 0);
345 device->fParser.SetReport(status, device->fTransferBuffer, actualLength);