// This file is part of the Essence operating system. // It is released under the terms of the MIT license -- see LICENSE.md. // Written by: nakst. #include // #define TRACE_REPORTS // TODO Key repeat not working on Qemu. struct ReportItem { uint32_t usage, application, arrayCount; int32_t logicalMinimum, logicalMaximum; uint8_t reportPrefix; uint8_t bits; uint8_t group; #define REPORT_ITEM_CONSTANT (1 << 0) #define REPORT_ITEM_RELATIVE (1 << 1) #define REPORT_ITEM_WRAP (1 << 2) #define REPORT_ITEM_NON_LINEAR (1 << 3) #define REPORT_ITEM_SIGNED (1 << 4) #define REPORT_ITEM_ARRAY (1 << 5) uint8_t flags; #define REPORT_ITEM_INPUT (1) #define REPORT_ITEM_OUTPUT (2) #define REPORT_ITEM_FEATURE (3) uint8_t type; }; struct BitBuffer { const uint8_t *buffer; size_t bytes; uintptr_t index; void Discard(size_t count); uint32_t ReadUnsigned(size_t count); int32_t ReadSigned(size_t count); }; struct GameController { uint64_t id; uint8_t reportPrefix; }; struct HIDDevice : KHIDevice { KUSBDevice *device; Array reportItems; bool usesReportPrefixes; Array gameControllers; KUSBEndpointDescriptor *reportEndpoint; uint8_t *lastReport; size_t lastReportBytes; void Initialise(); bool ParseReportDescriptor(const uint8_t *report, size_t reportBytes); void ReportReceived(BitBuffer *buffer); }; struct HIDDescriptorLink { uint8_t type; uint8_t length[2]; }; struct HIDDescriptor : KUSBDescriptorHeader { uint8_t specification[2]; uint8_t countryCode; uint8_t linkCount; HIDDescriptorLink links[1]; }; struct ReportGlobalState { int32_t logicalMinimum, logicalMaximum; uint16_t usagePage; uint8_t reportSize, reportCount; uint8_t reportID; }; struct ReportLocalState { #define USAGE_ARRAY_SIZE (32) uint32_t usages[USAGE_ARRAY_SIZE]; uint32_t usageMinimum, usageMaximum; uint8_t usageCount; #define DELIMITER_NONE (0) #define DELIMITER_FIRST (1) #define DELIMITER_IGNORE (2) uint8_t delimiterState; }; struct UsageString { uint32_t usage; const char *string; }; #define HID_APPLICATION_MOUSE (0x010002) #define HID_APPLICATION_JOYSTICK (0x010004) #define HID_APPLICATION_KEYBOARD (0x010006) #define HID_USAGE_X_AXIS (0x010030) #define HID_USAGE_Y_AXIS (0x010031) #define HID_USAGE_Z_AXIS (0x010032) #define HID_USAGE_X_ROTATION (0x010033) #define HID_USAGE_Y_ROTATION (0x010034) #define HID_USAGE_Z_ROTATION (0x010035) #define HID_USAGE_WHEEL (0x010038) #define HID_USAGE_HAT_SWITCH (0x010039) #define HID_USAGE_KEYCODES (0x070000) #define HID_USAGE_BUTTON_1 (0x090001) #define HID_USAGE_BUTTON_2 (0x090002) #define HID_USAGE_BUTTON_3 (0x090003) #define HID_USAGE_BUTTON_16 (0x090010) UsageString usageStrings[] = { { 0x000000, "padding" }, // Generic desktop page. { 0x010001, "pointer" }, { 0x010002, "mouse" }, { 0x010004, "joystick" }, { 0x010005, "gamepad" }, { 0x010006, "keyboard" }, { 0x010007, "keypad" }, { 0x010008, "multi-axis controller" }, { 0x010009, "tablet PC system controls" }, { 0x010030, "X axis" }, { 0x010031, "Y axis" }, { 0x010032, "Z axis" }, { 0x010033, "X rotation" }, { 0x010034, "Y rotation" }, { 0x010035, "Z rotation" }, { 0x010036, "slider" }, { 0x010037, "dial" }, { 0x010038, "wheel" }, { 0x010039, "hat switch" }, // Keyboard/keypad page. { 0x070000, "keycodes" }, { 0x0700E0, "left ctrl" }, { 0x0700E1, "left shift" }, { 0x0700E2, "left alt" }, { 0x0700E3, "left gui" }, { 0x0700E4, "right ctrl" }, { 0x0700E5, "right shift" }, { 0x0700E6, "right alt" }, { 0x0700E7, "right gui" }, // LED page. { 0x080001, "num lock" }, { 0x080002, "caps lock" }, { 0x080003, "scroll lock" }, { 0x080004, "compose" }, { 0x080005, "kana" }, // Button page. { 0x090001, "button 1" }, { 0x090002, "button 2" }, { 0x090003, "button 3" }, { 0x090004, "button 4" }, { 0x090005, "button 5" }, { 0x090006, "button 6" }, { 0x090007, "button 7" }, { 0x090008, "button 8" }, { 0x090009, "button 9" }, { 0x09000A, "button 10" }, { 0x09000B, "button 11" }, { 0x09000C, "button 12" }, { 0x09000D, "button 13" }, { 0x09000E, "button 14" }, { 0x09000F, "button 15" }, { 0x090010, "button 16" }, }; const char *LookupUsageString(uint32_t usage) { if (usage > 0xFF000000) { return "vendor-specific"; } for (uintptr_t i = 0; i < sizeof(usageStrings) / sizeof(usageStrings[0]); i++) { if (usageStrings[i].usage == usage) { return usageStrings[i].string; } } EsPrint("unknown usage %x\n", usage); return "unknown"; } void BitBuffer::Discard(size_t count) { index += count; } uint32_t BitBuffer::ReadUnsigned(size_t count) { uint32_t result = 0; uint32_t bit = 0; while (bit != count) { uintptr_t byte = index >> 3; if (byte >= bytes) { break; } if (buffer[byte] & (1 << (index & 7))) { result |= 1 << bit; } bit++, index++; } return result; } int32_t BitBuffer::ReadSigned(size_t count) { if (!count) return 0; uint32_t result = ReadUnsigned(count); if (result & (1 << (count - 1))) { for (uintptr_t i = count; i < 32; i++) { result |= 1 << i; } } return result; } bool HIDDevice::ParseReportDescriptor(const uint8_t *report, size_t reportBytes) { #define REPORT_GLOBAL_STACK_SIZE (8) ReportGlobalState global[REPORT_GLOBAL_STACK_SIZE] = {}; uintptr_t gIndex = 0; ReportLocalState local = {}; uint32_t application = 0; uint8_t group = 0; uintptr_t position = 0; while (position < reportBytes) { uint8_t header = report[position]; if (header == 0xFE) { // Long items, unused. if (position + 3 > reportBytes) return false; position += 3 + report[position + 1]; continue; } uint8_t size = header & 3; uint8_t type = header & ~3; position++; if (size == 3) size++; if (position + size > reportBytes) return false; uint32_t uData = 0; int32_t sData = 0; for (uintptr_t i = 0; i < size; i++) { uData |= report[position + i] << (i * 8); } sData = uData; if (size && (report[position + size - 1] & 0x80)) { for (uintptr_t i = size; i < 4; i++) { sData |= 0xFF << (i * 8); } } position += size; switch (type) { case 0b00000100: { global[gIndex].usagePage = uData; } break; case 0b00010100: { global[gIndex].logicalMinimum = sData; } break; case 0b00100100: { global[gIndex].logicalMaximum = sData; } break; case 0b01110100: { global[gIndex].reportSize = uData; } break; case 0b10010100: { global[gIndex].reportCount = uData; } break; case 0b10000100: { global[gIndex].reportID = uData; if (uData) usesReportPrefixes = true; } break; case 0b10100100: { if (gIndex + 1 == REPORT_GLOBAL_STACK_SIZE) return false; gIndex++; global[gIndex] = global[gIndex - 1]; } break; case 0b10110100: { if (gIndex == 0) return false; gIndex--; } break; case 0b00001000: { if (local.usageCount == USAGE_ARRAY_SIZE) return false; if (local.delimiterState != DELIMITER_IGNORE) { local.usages[local.usageCount++] = uData | (size < 4 ? (global[gIndex].usagePage << 16) : 0); } if (local.delimiterState == DELIMITER_FIRST) local.delimiterState = DELIMITER_IGNORE; } break; case 0b00011000: { local.usageMinimum = uData | (size < 4 ? (global[gIndex].usagePage << 16) : 0); } break; case 0b00101000: { local.usageMaximum = uData | (size < 4 ? (global[gIndex].usagePage << 16) : 0); } break; case 0b10101000: { if (uData) local.delimiterState = DELIMITER_FIRST; else local.delimiterState = DELIMITER_NONE; } break; case 0b10100000: { if (uData == 1) { if (local.usageCount == 0) return false; application = local.usages[0]; group++; } } break; case 0b10010000: case 0b10110000: case 0b10000000: { for (uintptr_t i = 0; i < global[gIndex].reportCount; i++) { ReportItem item = { .application = application, .logicalMinimum = global[gIndex].logicalMinimum, .logicalMaximum = global[gIndex].logicalMaximum, .reportPrefix = global[gIndex].reportID, .bits = global[gIndex].reportSize, }; if (type == 0b10000000) { item.type = REPORT_ITEM_INPUT; } else if (type == 0b10010000) { item.type = REPORT_ITEM_OUTPUT; } else if (type == 0b10110000) { item.type = REPORT_ITEM_FEATURE; } if ( uData & (1 << 0)) item.flags |= REPORT_ITEM_CONSTANT; if (~uData & (1 << 1)) item.flags |= REPORT_ITEM_ARRAY; if ( uData & (1 << 2)) item.flags |= REPORT_ITEM_RELATIVE; if ( uData & (1 << 3)) item.flags |= REPORT_ITEM_WRAP; if ( uData & (1 << 4)) item.flags |= REPORT_ITEM_NON_LINEAR; if (item.logicalMinimum < 0 || item.logicalMaximum < 0) item.flags |= REPORT_ITEM_SIGNED; if (local.usageCount) { item.usage = local.usages[i >= local.usageCount ? local.usageCount - 1 : i]; } else { item.usage = (i > local.usageMaximum - local.usageMinimum) ? local.usageMaximum : (local.usageMinimum + i); } if (item.flags & REPORT_ITEM_ARRAY) { item.arrayCount = global[gIndex].reportCount; } if (!reportItems.Add(item)) { return false; } KernelLog(LOG_INFO, "USBHID", "parsed report item", "Parsed report item - group: %d, application: '%z', usage: '%z', range: %i->%i, report: %d, bits: %d, " "flags: %z%z%z%z%z%z, type: %z, array count: %d\n", group, LookupUsageString(item.application), LookupUsageString(item.usage), item.logicalMinimum, item.logicalMaximum, item.reportPrefix, item.bits, (item.flags & REPORT_ITEM_CONSTANT) ? "constant|" : "", (item.flags & REPORT_ITEM_RELATIVE) ? "relative|" : "", (item.flags & REPORT_ITEM_WRAP) ? "wrap|" : "", (item.flags & REPORT_ITEM_NON_LINEAR) ? "non-linear|" : "", (item.flags & REPORT_ITEM_ARRAY) ? "array|" : "", (item.flags & REPORT_ITEM_SIGNED) ? "signed" : "unsigned", item.type == REPORT_ITEM_INPUT ? "input" : item.type == REPORT_ITEM_OUTPUT ? "output" : "feature", item.arrayCount); if (item.application == HID_APPLICATION_KEYBOARD) { cDebugName = "USB HID keyboard"; } else if (item.application == HID_APPLICATION_MOUSE) { cDebugName = "USB HID mouse"; } else if (item.application == HID_APPLICATION_JOYSTICK) { cDebugName = "USB HID joystick"; } if (item.flags & REPORT_ITEM_ARRAY) { break; } } } break; } if ((type & 0b00001100) == 0) { EsMemoryZero(&local, sizeof(ReportLocalState)); } } return true; } void ReportReceivedCallback(ptrdiff_t bytesNotTransferred, EsGeneric context) { HIDDevice *device = (HIDDevice *) context.p; size_t bytesTransferred = device->reportEndpoint->GetMaximumPacketSize() - bytesNotTransferred; if (bytesNotTransferred == -1) { KernelLog(LOG_ERROR, "USBHID", "report transfer failure", "Report transfer failed.\n"); bytesTransferred = 0; } BitBuffer buffer = { device->lastReport, bytesTransferred }; device->ReportReceived(&buffer); } void HIDDevice::ReportReceived(BitBuffer *buffer) { uint8_t prefix = 0; if (usesReportPrefixes) { prefix = buffer->ReadUnsigned(8); } #ifdef TRACE_REPORTS EsPrint("-- report (%d) --\n", prefix); #endif bool mouseEvent = false; KMouseUpdateData mouse = {}; bool keyboardEvent = false; uint16_t keysDown[32]; size_t keysDownCount = 0; bool gameControllerEvent = false; EsGameControllerState controllerState = {}; controllerState.directionalPad = 15; controllerState.analogCount = 2; for (uintptr_t i = 0; i < gameControllers.Length(); i++) { if (gameControllers[i].reportPrefix == prefix) { controllerState.id = gameControllers[i].id; } } for (uintptr_t i = 0; i < reportItems.Length(); i++) { ReportItem *item = &reportItems[i]; if (item->type != REPORT_ITEM_INPUT || item->reportPrefix != prefix) { continue; } #ifdef TRACE_REPORTS uintptr_t startIndex = buffer->index; EsPrint("%d/%z: ", item->group, LookupUsageString(item->usage)); size_t count = (item->flags & REPORT_ITEM_ARRAY) ? item->arrayCount : 1; for (uintptr_t i = 0; i < count; i++) { if (item->flags & REPORT_ITEM_SIGNED) { EsPrint("%i", buffer->ReadSigned(item->bits)); } else { EsPrint("%d", buffer->ReadUnsigned(item->bits)); } if (i != count - 1) { EsPrint(", "); } } EsPrint("\n"); buffer->index = startIndex; #endif if (item->flags & REPORT_ITEM_ARRAY) { bool handled = false; if (item->application == HID_APPLICATION_KEYBOARD) { if (item->usage == HID_USAGE_KEYCODES) { for (uintptr_t i = 0; i < item->arrayCount; i++) { uint32_t scancode = buffer->ReadUnsigned(item->bits); keyboardEvent = true; if (scancode > 0 && scancode < 0x200 && keysDownCount != 32) { keysDown[keysDownCount++] = scancode; } } } } if (!handled) { buffer->Discard(item->bits * item->arrayCount); } } else { bool handled = false; if (item->application == HID_APPLICATION_MOUSE) { // TODO Handle absolute, and wrapping movements. mouseEvent = true; handled = true; if (item->usage == HID_USAGE_X_AXIS) { if (item->flags & REPORT_ITEM_SIGNED) { mouse.xMovement = buffer->ReadSigned(item->bits) * K_CURSOR_MOVEMENT_SCALE; } else { mouse.xMovement = buffer->ReadUnsigned(item->bits) * K_CURSOR_MOVEMENT_SCALE; } mouse.xIsAbsolute = !(item->flags & REPORT_ITEM_RELATIVE); mouse.xFrom = item->logicalMinimum; mouse.xTo = item->logicalMaximum; } else if (item->usage == HID_USAGE_Y_AXIS) { if (item->flags & REPORT_ITEM_SIGNED) { mouse.yMovement = buffer->ReadSigned(item->bits) * K_CURSOR_MOVEMENT_SCALE; } else { mouse.yMovement = buffer->ReadUnsigned(item->bits) * K_CURSOR_MOVEMENT_SCALE; } mouse.yIsAbsolute = !(item->flags & REPORT_ITEM_RELATIVE); mouse.yFrom = item->logicalMinimum; mouse.yTo = item->logicalMaximum; } else if (item->usage == HID_USAGE_BUTTON_1) { if (buffer->ReadUnsigned(item->bits)) mouse.buttons |= 1 << 0; } else if (item->usage == HID_USAGE_BUTTON_2) { if (buffer->ReadUnsigned(item->bits)) mouse.buttons |= 1 << 2; } else if (item->usage == HID_USAGE_BUTTON_3) { if (buffer->ReadUnsigned(item->bits)) mouse.buttons |= 1 << 1; } else if (item->usage == HID_USAGE_WHEEL) { mouse.yScroll = buffer->ReadSigned(item->bits) * K_CURSOR_MOVEMENT_SCALE; } else { handled = false; } } else if (item->application == HID_APPLICATION_KEYBOARD) { handled = true; if (item->usage > HID_USAGE_KEYCODES && item->usage < HID_USAGE_KEYCODES + 0x200) { handled = true; keyboardEvent = true; if (buffer->ReadUnsigned(item->bits) && keysDownCount != 32) { keysDown[keysDownCount++] = item->usage - HID_USAGE_KEYCODES; } } } else if (item->application == HID_APPLICATION_JOYSTICK) { handled = true; if (item->usage >= HID_USAGE_BUTTON_1 && item->usage <= HID_USAGE_BUTTON_16) { gameControllerEvent = true; if (buffer->ReadUnsigned(item->bits)) { controllerState.buttons |= 1 << controllerState.buttonCount; } controllerState.buttonCount++; } else if (item->usage == HID_USAGE_X_AXIS) { gameControllerEvent = true; controllerState.analog[0].x = buffer->ReadUnsigned(item->bits) * 0xFF / item->logicalMaximum; } else if (item->usage == HID_USAGE_Y_AXIS) { gameControllerEvent = true; controllerState.analog[0].y = buffer->ReadUnsigned(item->bits) * 0xFF / item->logicalMaximum; } else if (item->usage == HID_USAGE_Z_AXIS) { gameControllerEvent = true; controllerState.analog[0].z = buffer->ReadUnsigned(item->bits) * 0xFF / item->logicalMaximum; } else if (item->usage == HID_USAGE_X_ROTATION) { gameControllerEvent = true; controllerState.analog[1].x = buffer->ReadUnsigned(item->bits) * 0xFF / item->logicalMaximum; } else if (item->usage == HID_USAGE_Y_ROTATION) { gameControllerEvent = true; controllerState.analog[1].y = buffer->ReadUnsigned(item->bits) * 0xFF / item->logicalMaximum; } else if (item->usage == HID_USAGE_Z_ROTATION) { gameControllerEvent = true; controllerState.analog[1].z = buffer->ReadUnsigned(item->bits) * 0xFF / item->logicalMaximum; } else if (item->usage == HID_USAGE_HAT_SWITCH) { gameControllerEvent = true; controllerState.directionalPad = buffer->ReadUnsigned(item->bits); } else { handled = false; } } if (!handled) { buffer->Discard(item->bits); } } } if (mouseEvent) { KMouseUpdate(&mouse); } if (keyboardEvent) { KKeyboardUpdate(keysDown, keysDownCount); } if (gameControllerEvent) { KGameControllerUpdate(&controllerState); } if (device->flags & K_DEVICE_REMOVED) { KDeviceCloseHandle(this); } else { if (!device->queueTransfer(device, reportEndpoint, ReportReceivedCallback, lastReport, reportEndpoint->GetMaximumPacketSize(), this)) { KernelLog(LOG_ERROR, "USBHID", "setup transfer failure", "Could not setup the interrupt input transfer to receive the next report packet.\n"); KDeviceCloseHandle(this); } } } void HIDDevice::Initialise() { // Find the HID descriptor. HIDDescriptor *hidDescriptor = (HIDDescriptor *) device->GetCommonDescriptor(0x21, 0); if (!hidDescriptor) { KernelLog(LOG_ERROR, "USBHID", "missing descriptor", "Could not find the HID descriptor.\n"); return; } else if (hidDescriptor->length < sizeof(HIDDescriptor) || hidDescriptor->linkCount == 0 || (hidDescriptor->linkCount - 1) * sizeof(HIDDescriptorLink) + sizeof(HIDDescriptor) > hidDescriptor->length) { KernelLog(LOG_ERROR, "USBHID", "bad descriptor length", "HID descriptor too short (%D) for %d links.\n", hidDescriptor->length, hidDescriptor->linkCount); return; } // Get the size of the report descriptor. size_t reportBytes = 0; for (uintptr_t i = 0; i < hidDescriptor->linkCount; i++) { if (hidDescriptor->links[i].type == 0x22) { reportBytes = (size_t) hidDescriptor->links[i].length[0] | ((size_t) hidDescriptor->links[i].length[1] << 8); } } if (!reportBytes) { KernelLog(LOG_ERROR, "USBHID", "no report descriptor", "Could not find report descriptor link in HID descriptor.\n"); return; } // Switch to the report protocol. if (!device->controlTransfer(device, 0x21, 0x0B /* set protocol */, 1 /* report protocol */, device->interfaceDescriptor.interfaceIndex, nullptr, 0, K_ACCESS_WRITE, nullptr)) { KernelLog(LOG_ERROR, "USBHID", "set protocol failure", "Could not switch to the report protocol.\n"); } // Get the report descriptor and parse it. uint8_t *report = (uint8_t *) EsHeapAllocate(reportBytes, false, K_FIXED); if (!report) { KernelLog(LOG_ERROR, "USBHID", "allocation failure", "Could not allocate buffer to store the report descriptor.\n"); return; } EsDefer(EsHeapFree(report, reportBytes, K_FIXED)); uint16_t transferred = 0; if (!device->controlTransfer(device, 0x81, 0x06, 0x22 << 8, 0, report, reportBytes, K_ACCESS_READ, &transferred) || transferred != reportBytes) { KernelLog(LOG_ERROR, "USBHID", "no report descriptor", "Could not read the report descriptor from the device.\n"); return; } if (!ParseReportDescriptor(report, reportBytes)) { KernelLog(LOG_ERROR, "USBHID", "invalid report descriptor", "Could not parse the report descriptor.\n"); return; } // Set idle. if (!device->controlTransfer(device, 0x21, 0x0A /* set idle */, 0 /* infinite duration, apply to all report IDs */, device->interfaceDescriptor.interfaceIndex, nullptr, 0, K_ACCESS_WRITE, nullptr)) { KernelLog(LOG_ERROR, "USBHID", "enable idle failure", "Could not enable idle mode on the device.\n"); return; } // Get the interrupt-in endpoint descriptor. KUSBEndpointDescriptor *endpoint = nullptr; { uintptr_t index = 0; while (true) { KUSBEndpointDescriptor *e = (KUSBEndpointDescriptor *) device->GetCommonDescriptor(0x05 /* endpoint */, index++); if (!e) { break; } else if (e->IsInterrupt() && e->IsInput()) { endpoint = e; break; } } } lastReport = (uint8_t *) EsHeapAllocate(endpoint->GetMaximumPacketSize(), true, K_FIXED); if (!lastReport) { KernelLog(LOG_ERROR, "USBHID", "allocation failure", "Could not allocate buffer to store received reports.\n"); return; } // Start receiving interrupt packets. reportEndpoint = endpoint; KDeviceOpenHandle(this); if (!device->queueTransfer(device, endpoint, ReportReceivedCallback, lastReport, endpoint->GetMaximumPacketSize(), this)) { KernelLog(LOG_ERROR, "USBHID", "setup transfer failure", "Could not setup the interrupt input transfer to receive report packets.\n"); KDeviceCloseHandle(this); return; } // Work out if this is a keyboard or mouse. for (uintptr_t i = 0; i < reportItems.Length(); i++) { ReportItem *item = &reportItems[i]; if (item->application == HID_APPLICATION_KEYBOARD && item->usage == HID_USAGE_KEYCODES) { KDeviceSendConnectedMessage(this, ES_DEVICE_KEYBOARD); break; } else if (item->application == HID_APPLICATION_MOUSE) { KDeviceSendConnectedMessage(this, ES_DEVICE_MOUSE); break; } } // If this is a game controller, tell the window manager it's been connected. { uint64_t seen = 0; for (uintptr_t i = 0; i < reportItems.Length(); i++) { ReportItem *item = &reportItems[i]; if (item->application == HID_APPLICATION_JOYSTICK && item->reportPrefix < 64 && (~seen & (1 << item->reportPrefix))) { seen |= (1 << item->reportPrefix); GameController controller = {}; controller.id = KGameControllerConnect(); if (controller.id) { controller.reportPrefix = item->reportPrefix; gameControllers.Add(controller); } } } } } static void DeviceDestroy(KDevice *_device) { HIDDevice *device = (HIDDevice *) _device; for (uintptr_t i = 0; i < device->gameControllers.Length(); i++) { KGameControllerDisconnect(device->gameControllers[i].id); } device->reportItems.Free(); device->gameControllers.Free(); EsHeapFree(device->lastReport, 0, K_FIXED); } static void DeviceAttach(KDevice *parent) { HIDDevice *device = (HIDDevice *) KDeviceCreate("USB HID", parent, sizeof(HIDDevice)); if (!device) { KernelLog(LOG_ERROR, "USBHID", "allocation failure", "Could not allocate HIDDevice structure.\n"); return; } device->destroy = DeviceDestroy; device->device = (KUSBDevice *) parent; device->Initialise(); KRegisterHIDevice(device); } KDriver driverUSBHID = { .attach = DeviceAttach, };