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