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 fInterruptPipe = interface->endpoint[i].handle; 174 break; 175 } 176 } 177 178 if (fInterruptPipe == 0) { 179 TRACE_ALWAYS("didn't find a suitable interrupt pipe\n"); 180 return; 181 } 182 183 fTransferBufferSize = fParser.MaxReportSize(); 184 if (fTransferBufferSize == 0) { 185 TRACE_ALWAYS("report claims a report size of 0\n"); 186 return; 187 } 188 189 fTransferBuffer = (uint8 *)malloc(fTransferBufferSize); 190 if (fTransferBuffer == NULL) { 191 TRACE_ALWAYS("failed to allocate transfer buffer\n"); 192 fStatus = B_NO_MEMORY; 193 return; 194 } 195 196 if (quirkyIndex >= 0) { 197 quirky_init_function quirkyInit 198 = gQuirkyDevices[quirkyIndex].init_function; 199 200 if (quirkyInit != NULL) { 201 fStatus = quirkyInit(device, config, interfaceIndex); 202 if (fStatus != B_OK) 203 return; 204 } 205 } 206 207 ProtocolHandler::AddHandlers(*this, fProtocolHandlerList, 208 fProtocolHandlerCount); 209 fStatus = B_OK; 210 } 211 212 213 HIDDevice::~HIDDevice() 214 { 215 ProtocolHandler *handler = fProtocolHandlerList; 216 while (handler != NULL) { 217 ProtocolHandler *next = handler->NextHandler(); 218 delete handler; 219 handler = next; 220 } 221 222 free(fTransferBuffer); 223 } 224 225 226 void 227 HIDDevice::SetParentCookie(int32 cookie) 228 { 229 fParentCookie = cookie; 230 } 231 232 233 status_t 234 HIDDevice::Open(ProtocolHandler *handler, uint32 flags) 235 { 236 atomic_add(&fOpenCount, 1); 237 return B_OK; 238 } 239 240 241 status_t 242 HIDDevice::Close(ProtocolHandler *handler) 243 { 244 atomic_add(&fOpenCount, -1); 245 return B_OK; 246 } 247 248 249 void 250 HIDDevice::Removed() 251 { 252 fRemoved = true; 253 gUSBModule->cancel_queued_transfers(fInterruptPipe); 254 } 255 256 257 status_t 258 HIDDevice::MaybeScheduleTransfer() 259 { 260 if (fRemoved) 261 return B_ERROR; 262 263 if (atomic_set(&fTransferScheduled, 1) != 0) { 264 // someone else already caused a transfer to be scheduled 265 return B_OK; 266 } 267 268 TRACE("scheduling interrupt transfer of %lu bytes\n", fTransferBufferSize); 269 status_t result = gUSBModule->queue_interrupt(fInterruptPipe, 270 fTransferBuffer, fTransferBufferSize, _TransferCallback, this); 271 if (result != B_OK) { 272 TRACE_ALWAYS("failed to schedule interrupt transfer 0x%08lx\n", result); 273 return result; 274 } 275 276 return B_OK; 277 } 278 279 280 status_t 281 HIDDevice::SendReport(HIDReport *report) 282 { 283 size_t actualLength; 284 return gUSBModule->send_request(fDevice, 285 USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS, 286 B_USB_REQUEST_HID_SET_REPORT, 0x200 | report->ID(), fInterfaceIndex, 287 report->ReportSize(), report->CurrentReport(), &actualLength); 288 } 289 290 291 ProtocolHandler * 292 HIDDevice::ProtocolHandlerAt(uint32 index) const 293 { 294 ProtocolHandler *handler = fProtocolHandlerList; 295 while (handler != NULL) { 296 if (index == 0) 297 return handler; 298 299 handler = handler->NextHandler(); 300 index--; 301 } 302 303 return NULL; 304 } 305 306 307 void 308 HIDDevice::_TransferCallback(void *cookie, status_t status, void *data, 309 size_t actualLength) 310 { 311 HIDDevice *device = (HIDDevice *)cookie; 312 if (status == B_DEV_STALLED && !device->fRemoved) { 313 // try clearing stalls right away, the report listeners will resubmit 314 gUSBModule->clear_feature(device->fInterruptPipe, 315 USB_FEATURE_ENDPOINT_HALT); 316 } 317 318 atomic_set(&device->fTransferScheduled, 0); 319 device->fParser.SetReport(status, device->fTransferBuffer, actualLength); 320 } 321