1 /* 2 * Copyright 2008-2009 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 "ProtocolHandler.h" 14 15 #include <usb/USB_hid.h> 16 17 #include <stdlib.h> 18 #include <stdio.h> 19 #include <string.h> 20 #include <unistd.h> 21 #include <new> 22 23 24 HIDDevice::HIDDevice(usb_device device, const usb_configuration_info *config, 25 size_t interfaceIndex) 26 : fStatus(B_NO_INIT), 27 fDevice(device), 28 fInterfaceIndex(interfaceIndex), 29 fTransferScheduled(0), 30 fTransferBufferSize(0), 31 fTransferBuffer(NULL), 32 fParentCookie(-1), 33 fOpenCount(0), 34 fRemoved(false), 35 fParser(this), 36 fProtocolHandlerCount(0), 37 fProtocolHandlers(NULL) 38 { 39 // read HID descriptor 40 size_t descriptorLength = sizeof(usb_hid_descriptor); 41 usb_hid_descriptor *hidDescriptor 42 = (usb_hid_descriptor *)malloc(descriptorLength); 43 if (hidDescriptor == NULL) { 44 TRACE_ALWAYS("failed to allocate buffer for hid descriptor\n"); 45 fStatus = B_NO_MEMORY; 46 return; 47 } 48 49 status_t result = gUSBModule->send_request(device, 50 USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD, 51 USB_REQUEST_GET_DESCRIPTOR, 52 USB_HID_DESCRIPTOR_HID << 8, interfaceIndex, descriptorLength, 53 hidDescriptor, &descriptorLength); 54 55 TRACE("get hid descriptor: result: 0x%08lx; length: %lu\n", result, 56 descriptorLength); 57 if (result == B_OK) 58 descriptorLength = hidDescriptor->descriptor_info[0].descriptor_length; 59 else 60 descriptorLength = 256; /* XXX */ 61 free(hidDescriptor); 62 63 uint8 *reportDescriptor = (uint8 *)malloc(descriptorLength); 64 if (reportDescriptor == NULL) { 65 TRACE_ALWAYS("failed to allocate buffer for report descriptor\n"); 66 fStatus = B_NO_MEMORY; 67 return; 68 } 69 70 result = gUSBModule->send_request(device, 71 USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD, 72 USB_REQUEST_GET_DESCRIPTOR, 73 USB_HID_DESCRIPTOR_REPORT << 8, interfaceIndex, descriptorLength, 74 reportDescriptor, &descriptorLength); 75 76 TRACE("get report descriptor: result: 0x%08lx; length: %lu\n", result, 77 descriptorLength); 78 if (result != B_OK) { 79 TRACE_ALWAYS("failed tot get report descriptor\n"); 80 free(reportDescriptor); 81 return; 82 } 83 84 #if 1 85 // save report descriptor for troubleshooting 86 const usb_device_descriptor *deviceDescriptor 87 = gUSBModule->get_device_descriptor(device); 88 char outputFile[128]; 89 sprintf(outputFile, "/tmp/usb_hid_report_descriptor_%04x_%04x_%lu.bin", 90 deviceDescriptor->vendor_id, deviceDescriptor->product_id, 91 interfaceIndex); 92 int fd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); 93 if (fd >= 0) { 94 write(fd, reportDescriptor, descriptorLength); 95 close(fd); 96 } 97 #endif 98 99 result = fParser.ParseReportDescriptor(reportDescriptor, descriptorLength); 100 free(reportDescriptor); 101 if (result != B_OK) { 102 TRACE_ALWAYS("parsing the report descriptor failed\n"); 103 fStatus = result; 104 return; 105 } 106 107 #if 0 108 for (uint32 i = 0; i < fParser.CountReports(HID_REPORT_TYPE_ANY); i++) 109 fParser.ReportAt(HID_REPORT_TYPE_ANY, i)->PrintToStream(); 110 #endif 111 112 // find the interrupt in pipe 113 usb_interface_info *interface = config->interface[interfaceIndex].active; 114 for (size_t i = 0; i < interface->endpoint_count; i++) { 115 usb_endpoint_descriptor *descriptor = interface->endpoint[i].descr; 116 if ((descriptor->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) 117 && (descriptor->attributes & USB_ENDPOINT_ATTR_MASK) 118 == USB_ENDPOINT_ATTR_INTERRUPT) { 119 fInterruptPipe = interface->endpoint[i].handle; 120 break; 121 } 122 } 123 124 if (fInterruptPipe == 0) { 125 TRACE_ALWAYS("didn't find a suitable interrupt pipe\n"); 126 return; 127 } 128 129 fTransferBufferSize = fParser.MaxReportSize(); 130 if (fTransferBufferSize == 0) { 131 TRACE_ALWAYS("report claims a report size of 0\n"); 132 return; 133 } 134 135 fTransferBuffer = (uint8 *)malloc(fTransferBufferSize); 136 if (fTransferBuffer == NULL) { 137 TRACE_ALWAYS("failed to allocate transfer buffer\n"); 138 fStatus = B_NO_MEMORY; 139 return; 140 } 141 142 ProtocolHandler::AddHandlers(this, &fProtocolHandlers, 143 &fProtocolHandlerCount); 144 fStatus = B_OK; 145 } 146 147 148 HIDDevice::~HIDDevice() 149 { 150 for (uint32 i = 0; i < fProtocolHandlerCount; i++) 151 delete fProtocolHandlers[i]; 152 153 free(fProtocolHandlers); 154 free(fTransferBuffer); 155 } 156 157 158 void 159 HIDDevice::SetParentCookie(int32 cookie) 160 { 161 fParentCookie = cookie; 162 } 163 164 165 status_t 166 HIDDevice::Open(ProtocolHandler *handler, uint32 flags) 167 { 168 atomic_add(&fOpenCount, 1); 169 return B_OK; 170 } 171 172 173 status_t 174 HIDDevice::Close(ProtocolHandler *handler) 175 { 176 atomic_add(&fOpenCount, -1); 177 return B_OK; 178 } 179 180 181 void 182 HIDDevice::Removed() 183 { 184 fRemoved = true; 185 gUSBModule->cancel_queued_transfers(fInterruptPipe); 186 } 187 188 189 status_t 190 HIDDevice::MaybeScheduleTransfer() 191 { 192 if (fRemoved) 193 return B_ERROR; 194 195 if (atomic_set(&fTransferScheduled, 1) != 0) { 196 // someone else already caused a transfer to be scheduled 197 return B_OK; 198 } 199 200 TRACE("scheduling interrupt transfer of %lu bytes\n", fTransferBufferSize); 201 status_t result = gUSBModule->queue_interrupt(fInterruptPipe, 202 fTransferBuffer, fTransferBufferSize, _TransferCallback, this); 203 if (result != B_OK) { 204 TRACE_ALWAYS("failed to schedule interrupt transfer 0x%08lx\n", result); 205 return result; 206 } 207 208 return B_OK; 209 } 210 211 212 status_t 213 HIDDevice::SendReport(HIDReport *report) 214 { 215 size_t actualLength; 216 return gUSBModule->send_request(fDevice, 217 USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS, 218 USB_REQUEST_HID_SET_REPORT, 0x200 | report->ID(), fInterfaceIndex, 219 report->ReportSize(), report->CurrentReport(), &actualLength); 220 } 221 222 223 ProtocolHandler * 224 HIDDevice::ProtocolHandlerAt(uint32 index) const 225 { 226 if (index >= fProtocolHandlerCount) 227 return NULL; 228 return fProtocolHandlers[index]; 229 } 230 231 232 void 233 HIDDevice::_TransferCallback(void *cookie, status_t status, void *data, 234 size_t actualLength) 235 { 236 HIDDevice *device = (HIDDevice *)cookie; 237 if (status == B_DEV_STALLED && !device->fRemoved) { 238 // try clearing stalls right away, the report listeners will resubmit 239 gUSBModule->clear_feature(device->fInterruptPipe, 240 USB_FEATURE_ENDPOINT_HALT); 241 } 242 243 atomic_set(&device->fTransferScheduled, 0); 244 device->fParser.SetReport(status, device->fTransferBuffer, actualLength); 245 } 246