1 /* 2 * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. 3 * Copyright 2008-2011, Michael Lotz <mmlr@mlotz.ch> 4 * Distributed under the terms of the MIT license. 5 */ 6 7 8 //! Driver for I2C Human Interface Devices. 9 10 11 #include "Driver.h" 12 #include "HIDDevice.h" 13 #include "HIDReport.h" 14 #include "HIDWriter.h" 15 #include "ProtocolHandler.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(uint16 descriptorAddress, i2c_device_interface* i2c, 27 i2c_device i2cCookie) 28 : fStatus(B_NO_INIT), 29 fTransferLastschedule(0), 30 fTransferScheduled(0), 31 fTransferBufferSize(0), 32 fTransferBuffer(NULL), 33 fOpenCount(0), 34 fRemoved(false), 35 fParser(this), 36 fProtocolHandlerCount(0), 37 fProtocolHandlerList(NULL), 38 fDescriptorAddress(descriptorAddress), 39 fI2C(i2c), 40 fI2CCookie(i2cCookie) 41 { 42 // fetch HID descriptor 43 fStatus = _FetchBuffer((uint8*)&fDescriptorAddress, 44 sizeof(fDescriptorAddress), &fDescriptor, sizeof(fDescriptor)); 45 if (fStatus != B_OK) { 46 ERROR("failed to fetch HID descriptor\n"); 47 return; 48 } 49 50 // fetch HID Report descriptor 51 52 HIDWriter descriptorWriter; 53 54 uint16 descriptorLength = fDescriptor.wReportDescLength; 55 fReportDescriptor = (uint8 *)malloc(descriptorLength); 56 if (fReportDescriptor == NULL) { 57 ERROR("failed to allocate buffer for report descriptor\n"); 58 fStatus = B_NO_MEMORY; 59 return; 60 } 61 62 uint16 reportDescRegister = fDescriptor.wReportDescRegister; 63 fStatus = _FetchBuffer((uint8*)&reportDescRegister, 64 sizeof(reportDescRegister), fReportDescriptor, 65 descriptorLength); 66 if (fStatus != B_OK) { 67 ERROR("failed tot get report descriptor\n"); 68 free(fReportDescriptor); 69 return; 70 } 71 72 #if 1 73 // save report descriptor for troubleshooting 74 char outputFile[128]; 75 sprintf(outputFile, "/tmp/i2c_hid_report_descriptor_%04x_%04x.bin", 76 fDescriptor.wVendorID, fDescriptor.wProductID); 77 int fd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); 78 if (fd >= 0) { 79 write(fd, fReportDescriptor, descriptorLength); 80 close(fd); 81 } 82 #endif 83 84 status_t result = fParser.ParseReportDescriptor(fReportDescriptor, 85 descriptorLength); 86 free(fReportDescriptor); 87 88 if (result != B_OK) { 89 ERROR("parsing the report descriptor failed\n"); 90 fStatus = result; 91 return; 92 } 93 94 #if 0 95 for (uint32 i = 0; i < fParser.CountReports(HID_REPORT_TYPE_ANY); i++) 96 fParser.ReportAt(HID_REPORT_TYPE_ANY, i)->PrintToStream(); 97 #endif 98 99 fTransferBufferSize = fParser.MaxReportSize(); 100 if (fTransferBufferSize == 0) { 101 TRACE_ALWAYS("report claims a report size of 0\n"); 102 return; 103 } 104 105 // We pad the allocation size so that we can always read 32 bits at a time 106 // (as done in HIDReportItem) without the need for an additional boundary 107 // check. We don't increase the transfer buffer size though as to not expose 108 // this implementation detail onto the device when scheduling transfers. 109 fTransferBuffer = (uint8 *)malloc(fTransferBufferSize + 3); 110 if (fTransferBuffer == NULL) { 111 TRACE_ALWAYS("failed to allocate transfer buffer\n"); 112 fStatus = B_NO_MEMORY; 113 return; 114 } 115 116 ProtocolHandler::AddHandlers(*this, fProtocolHandlerList, 117 fProtocolHandlerCount); 118 fStatus = B_OK; 119 } 120 121 122 HIDDevice::~HIDDevice() 123 { 124 ProtocolHandler *handler = fProtocolHandlerList; 125 while (handler != NULL) { 126 ProtocolHandler *next = handler->NextHandler(); 127 delete handler; 128 handler = next; 129 } 130 131 free(fTransferBuffer); 132 } 133 134 135 status_t 136 HIDDevice::Open(ProtocolHandler *handler, uint32 flags) 137 { 138 atomic_add(&fOpenCount, 1); 139 _Reset(); 140 141 return B_OK; 142 } 143 144 145 status_t 146 HIDDevice::Close(ProtocolHandler *handler) 147 { 148 atomic_add(&fOpenCount, -1); 149 _SetPower(I2C_HID_POWER_OFF); 150 151 return B_OK; 152 } 153 154 155 void 156 HIDDevice::Removed() 157 { 158 fRemoved = true; 159 } 160 161 162 status_t 163 HIDDevice::MaybeScheduleTransfer(HIDReport *report) 164 { 165 if (fRemoved) 166 return ENODEV; 167 168 if (atomic_get_and_set(&fTransferScheduled, 1) != 0) { 169 // someone else already caused a transfer to be scheduled 170 return B_OK; 171 } 172 173 snooze_until(fTransferLastschedule, B_SYSTEM_TIMEBASE); 174 fTransferLastschedule = system_time() + 10000; 175 176 TRACE("scheduling interrupt transfer of %lu bytes\n", 177 report->ReportSize()); 178 return _FetchReport(report->Type(), report->ID(), report->ReportSize()); 179 } 180 181 182 status_t 183 HIDDevice::SendReport(HIDReport *report) 184 { 185 // TODO 186 return B_OK; 187 } 188 189 190 ProtocolHandler * 191 HIDDevice::ProtocolHandlerAt(uint32 index) const 192 { 193 ProtocolHandler *handler = fProtocolHandlerList; 194 while (handler != NULL) { 195 if (index == 0) 196 return handler; 197 198 handler = handler->NextHandler(); 199 index--; 200 } 201 202 return NULL; 203 } 204 205 206 void 207 HIDDevice::_UnstallCallback(void *cookie, status_t status, void *data, 208 size_t actualLength) 209 { 210 HIDDevice *device = (HIDDevice *)cookie; 211 if (status != B_OK) { 212 TRACE_ALWAYS("Unable to unstall device: %s\n", strerror(status)); 213 } 214 215 // Now report the original failure, since we're ready to retry 216 _TransferCallback(cookie, B_ERROR, device->fTransferBuffer, 0); 217 } 218 219 220 void 221 HIDDevice::_TransferCallback(void *cookie, status_t status, void *data, 222 size_t actualLength) 223 { 224 HIDDevice *device = (HIDDevice *)cookie; 225 226 atomic_set(&device->fTransferScheduled, 0); 227 device->fParser.SetReport(status, device->fTransferBuffer, actualLength); 228 } 229 230 231 status_t 232 HIDDevice::_Reset() 233 { 234 CALLED(); 235 status_t status = _SetPower(I2C_HID_POWER_ON); 236 if (status != B_OK) 237 return status; 238 239 snooze(1000); 240 241 uint8 cmd[] = { 242 (uint8)(fDescriptor.wCommandRegister & 0xff), 243 (uint8)(fDescriptor.wCommandRegister >> 8), 244 0, 245 I2C_HID_CMD_RESET, 246 }; 247 248 status = _ExecCommand(I2C_OP_WRITE_STOP, cmd, sizeof(cmd), NULL, 0); 249 if (status != B_OK) { 250 _SetPower(I2C_HID_POWER_OFF); 251 return status; 252 } 253 254 snooze(1000); 255 return B_OK; 256 } 257 258 259 status_t 260 HIDDevice::_SetPower(uint8 power) 261 { 262 CALLED(); 263 uint8 cmd[] = { 264 (uint8)(fDescriptor.wCommandRegister & 0xff), 265 (uint8)(fDescriptor.wCommandRegister >> 8), 266 power, 267 I2C_HID_CMD_SET_POWER 268 }; 269 270 return _ExecCommand(I2C_OP_WRITE_STOP, cmd, sizeof(cmd), NULL, 0); 271 } 272 273 274 status_t 275 HIDDevice::_FetchReport(uint8 type, uint8 id, size_t reportSize) 276 { 277 uint8 reportId = id > 15 ? 15 : id; 278 size_t cmdLength = 6; 279 uint8 cmd[] = { 280 (uint8)(fDescriptor.wCommandRegister & 0xff), 281 (uint8)(fDescriptor.wCommandRegister >> 8), 282 (uint8)(reportId | (type << 4)), 283 I2C_HID_CMD_GET_REPORT, 284 0, 0, 0, 285 }; 286 287 int dataOffset = 4; 288 int reportIdLength = 1; 289 if (reportId == 15) { 290 cmd[dataOffset++] = id; 291 cmdLength++; 292 reportIdLength++; 293 } 294 cmd[dataOffset++] = fDescriptor.wDataRegister & 0xff; 295 cmd[dataOffset++] = fDescriptor.wDataRegister >> 8; 296 297 size_t bufferLength = reportSize + reportIdLength + 2; 298 299 status_t status = _FetchBuffer(cmd, cmdLength, fTransferBuffer, 300 bufferLength); 301 if (status != B_OK) { 302 atomic_set(&fTransferScheduled, 0); 303 return status; 304 } 305 306 uint16 actualLength = fTransferBuffer[0] | (fTransferBuffer[1] << 8); 307 TRACE("_FetchReport %" B_PRIuSIZE " %" B_PRIu16 "\n", reportSize, 308 actualLength); 309 if (actualLength <= 2 || actualLength == 0xffff || bufferLength == 0) 310 actualLength = 0; 311 else 312 actualLength -= 2; 313 314 atomic_set(&fTransferScheduled, 0); 315 fParser.SetReport(status, 316 (uint8*)((addr_t)fTransferBuffer + 2), actualLength); 317 return B_OK; 318 } 319 320 321 status_t 322 HIDDevice::_FetchBuffer(uint8* cmd, size_t cmdLength, void* buffer, 323 size_t bufferLength) 324 { 325 return _ExecCommand(I2C_OP_READ_STOP, cmd, cmdLength, 326 buffer, bufferLength); 327 } 328 329 330 status_t 331 HIDDevice::_ExecCommand(i2c_op op, uint8* cmd, size_t cmdLength, void* buffer, 332 size_t bufferLength) 333 { 334 status_t status = fI2C->acquire_bus(fI2CCookie); 335 if (status != B_OK) 336 return status; 337 status = fI2C->exec_command(fI2CCookie, I2C_OP_READ_STOP, cmd, cmdLength, 338 buffer, bufferLength); 339 fI2C->release_bus(fI2CCookie); 340 return status; 341 } 342