From 2b4bf3eef6a26d0221f803c296baae95a2ef2426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Sat, 4 Apr 2020 10:30:58 +0200 Subject: [PATCH] i2c_hid: driver for i2c-hid devices This is getting more common in tablets and laptops. It replaces PS/2 for the internal keyboard and pointing devices. This is simpler and cheaper than using up USB ports, and also simpler than the old and quirky PS/2 protocol. The HID spec is the same no matter what transport is used (it is also applicable for Bluetooth). Ideally we could create a separate HID bus manager that would handle all these devices in a generic way, but that is a lot of work nad extra complications for uncertain gains. For now, just move the common files to a shared directory where both drivers can use them. As a result the files are compiled twice, which is what we want, because currently they hardcode some device paths that need to be different for each driver. Change-Id: I0327f6864dd0a4372b708f7b7ecf299aa86a6ea9 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2466 Reviewed-by: Adrien Destugues Tested-by: Commit checker robot --- src/add-ons/kernel/drivers/input/Jamfile | 1 + .../{usb_hid => hid_shared}/HIDCollection.cpp | 0 .../{usb_hid => hid_shared}/HIDCollection.h | 0 .../{usb_hid => hid_shared}/HIDDataTypes.h | 0 .../{usb_hid => hid_shared}/HIDParser.cpp | 0 .../input/{usb_hid => hid_shared}/HIDParser.h | 0 .../{usb_hid => hid_shared}/HIDReport.cpp | 2 +- .../input/{usb_hid => hid_shared}/HIDReport.h | 0 .../{usb_hid => hid_shared}/HIDReportItem.cpp | 0 .../{usb_hid => hid_shared}/HIDReportItem.h | 0 .../{usb_hid => hid_shared}/HIDWriter.cpp | 0 .../input/{usb_hid => hid_shared}/HIDWriter.h | 0 .../JoystickProtocolHandler.cpp | 0 .../JoystickProtocolHandler.h | 0 .../KeyboardProtocolHandler.cpp | 14 + .../KeyboardProtocolHandler.h | 0 .../MouseProtocolHandler.cpp | 0 .../MouseProtocolHandler.h | 0 .../ProtocolHandler.cpp | 0 .../{usb_hid => hid_shared}/ProtocolHandler.h | 0 .../TabletProtocolHandler.cpp | 0 .../TabletProtocolHandler.h | 0 .../drivers/input/i2c_hid/DeviceList.cpp | 155 ++++ .../kernel/drivers/input/i2c_hid/DeviceList.h | 30 + .../kernel/drivers/input/i2c_hid/Driver.cpp | 520 +++++++++++++ .../kernel/drivers/input/i2c_hid/Driver.h | 33 + .../drivers/input/i2c_hid/HIDDevice.cpp | 341 ++++++++ .../kernel/drivers/input/i2c_hid/HIDDevice.h | 123 +++ .../kernel/drivers/input/i2c_hid/Jamfile | 29 + .../input/i2c_hid/KeyboardProtocolHandler.cpp | 732 ++++++++++++++++++ .../drivers/input/usb_hid/HIDDevice.cpp | 2 +- .../kernel/drivers/input/usb_hid/HIDDevice.h | 2 +- .../kernel/drivers/input/usb_hid/Jamfile | 5 +- 33 files changed, 1985 insertions(+), 4 deletions(-) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/HIDCollection.cpp (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/HIDCollection.h (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/HIDDataTypes.h (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/HIDParser.cpp (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/HIDParser.h (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/HIDReport.cpp (99%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/HIDReport.h (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/HIDReportItem.cpp (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/HIDReportItem.h (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/HIDWriter.cpp (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/HIDWriter.h (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/JoystickProtocolHandler.cpp (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/JoystickProtocolHandler.h (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/KeyboardProtocolHandler.cpp (98%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/KeyboardProtocolHandler.h (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/MouseProtocolHandler.cpp (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/MouseProtocolHandler.h (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/ProtocolHandler.cpp (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/ProtocolHandler.h (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/TabletProtocolHandler.cpp (100%) rename src/add-ons/kernel/drivers/input/{usb_hid => hid_shared}/TabletProtocolHandler.h (100%) create mode 100644 src/add-ons/kernel/drivers/input/i2c_hid/DeviceList.cpp create mode 100644 src/add-ons/kernel/drivers/input/i2c_hid/DeviceList.h create mode 100644 src/add-ons/kernel/drivers/input/i2c_hid/Driver.cpp create mode 100644 src/add-ons/kernel/drivers/input/i2c_hid/Driver.h create mode 100644 src/add-ons/kernel/drivers/input/i2c_hid/HIDDevice.cpp create mode 100644 src/add-ons/kernel/drivers/input/i2c_hid/HIDDevice.h create mode 100644 src/add-ons/kernel/drivers/input/i2c_hid/Jamfile create mode 100644 src/add-ons/kernel/drivers/input/i2c_hid/KeyboardProtocolHandler.cpp diff --git a/src/add-ons/kernel/drivers/input/Jamfile b/src/add-ons/kernel/drivers/input/Jamfile index cb1ad3accb..73ba672d6a 100644 --- a/src/add-ons/kernel/drivers/input/Jamfile +++ b/src/add-ons/kernel/drivers/input/Jamfile @@ -1,5 +1,6 @@ SubDir HAIKU_TOP src add-ons kernel drivers input ; +SubInclude HAIKU_TOP src add-ons kernel drivers input i2c_hid ; SubInclude HAIKU_TOP src add-ons kernel drivers input ps2_hid ; SubInclude HAIKU_TOP src add-ons kernel drivers input usb_hid ; SubInclude HAIKU_TOP src add-ons kernel drivers input wacom ; diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.cpp b/src/add-ons/kernel/drivers/input/hid_shared/HIDCollection.cpp similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.cpp rename to src/add-ons/kernel/drivers/input/hid_shared/HIDCollection.cpp diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.h b/src/add-ons/kernel/drivers/input/hid_shared/HIDCollection.h similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.h rename to src/add-ons/kernel/drivers/input/hid_shared/HIDCollection.h diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDDataTypes.h b/src/add-ons/kernel/drivers/input/hid_shared/HIDDataTypes.h similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/HIDDataTypes.h rename to src/add-ons/kernel/drivers/input/hid_shared/HIDDataTypes.h diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.cpp b/src/add-ons/kernel/drivers/input/hid_shared/HIDParser.cpp similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/HIDParser.cpp rename to src/add-ons/kernel/drivers/input/hid_shared/HIDParser.cpp diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.h b/src/add-ons/kernel/drivers/input/hid_shared/HIDParser.h similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/HIDParser.h rename to src/add-ons/kernel/drivers/input/hid_shared/HIDParser.h diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDReport.cpp b/src/add-ons/kernel/drivers/input/hid_shared/HIDReport.cpp similarity index 99% rename from src/add-ons/kernel/drivers/input/usb_hid/HIDReport.cpp rename to src/add-ons/kernel/drivers/input/hid_shared/HIDReport.cpp index e885db251c..27847262b9 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/HIDReport.cpp +++ b/src/add-ons/kernel/drivers/input/hid_shared/HIDReport.cpp @@ -234,7 +234,7 @@ HIDReport::WaitForReport(bigtime_t timeout) ConditionVariableEntry conditionVariableEntry; fConditionVariable.Add(&conditionVariableEntry); - status_t result = fParser->Device()->MaybeScheduleTransfer(); + status_t result = fParser->Device()->MaybeScheduleTransfer(this); if (result != B_OK) { TRACE_ALWAYS("scheduling transfer failed\n"); conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, 0); diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDReport.h b/src/add-ons/kernel/drivers/input/hid_shared/HIDReport.h similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/HIDReport.h rename to src/add-ons/kernel/drivers/input/hid_shared/HIDReport.h diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.cpp b/src/add-ons/kernel/drivers/input/hid_shared/HIDReportItem.cpp similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.cpp rename to src/add-ons/kernel/drivers/input/hid_shared/HIDReportItem.cpp diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.h b/src/add-ons/kernel/drivers/input/hid_shared/HIDReportItem.h similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.h rename to src/add-ons/kernel/drivers/input/hid_shared/HIDReportItem.h diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDWriter.cpp b/src/add-ons/kernel/drivers/input/hid_shared/HIDWriter.cpp similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/HIDWriter.cpp rename to src/add-ons/kernel/drivers/input/hid_shared/HIDWriter.cpp diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDWriter.h b/src/add-ons/kernel/drivers/input/hid_shared/HIDWriter.h similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/HIDWriter.h rename to src/add-ons/kernel/drivers/input/hid_shared/HIDWriter.h diff --git a/src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.cpp b/src/add-ons/kernel/drivers/input/hid_shared/JoystickProtocolHandler.cpp similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.cpp rename to src/add-ons/kernel/drivers/input/hid_shared/JoystickProtocolHandler.cpp diff --git a/src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.h b/src/add-ons/kernel/drivers/input/hid_shared/JoystickProtocolHandler.h similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.h rename to src/add-ons/kernel/drivers/input/hid_shared/JoystickProtocolHandler.h diff --git a/src/add-ons/kernel/drivers/input/usb_hid/KeyboardProtocolHandler.cpp b/src/add-ons/kernel/drivers/input/hid_shared/KeyboardProtocolHandler.cpp similarity index 98% rename from src/add-ons/kernel/drivers/input/usb_hid/KeyboardProtocolHandler.cpp rename to src/add-ons/kernel/drivers/input/hid_shared/KeyboardProtocolHandler.cpp index edd4db1898..547775a3b2 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/KeyboardProtocolHandler.cpp +++ b/src/add-ons/kernel/drivers/input/hid_shared/KeyboardProtocolHandler.cpp @@ -32,6 +32,7 @@ #define KEYBOARD_HANDLER_COOKIE_FLAG_DEBUGGER 0x02 +#if KEYBOARD_SUPPORTS_KDL static bool sDebugKeyboardFound = false; static usb_id sDebugKeyboardPipe = 0; static size_t sDebugKeyboardReportSize = 0; @@ -45,6 +46,7 @@ debug_get_keyboard_config(int argc, char **argv) set_debug_variable("_usbReportSize", (uint64)sDebugKeyboardReportSize); return 0; } +#endif // #pragma mark - @@ -105,6 +107,7 @@ KeyboardProtocolHandler::KeyboardProtocolHandler(HIDReport &inputReport, } } +#if KEYBOARD_SUPPORTS_KDL if (!sDebugKeyboardFound && debugUsable) { // It's a keyboard, not just some additional buttons, set up the kernel // debugger info here so that it is ready on panics or crashes that @@ -115,6 +118,7 @@ KeyboardProtocolHandler::KeyboardProtocolHandler(HIDReport &inputReport, if (outputReport != NULL) sDebugKeyboardFound = true; } +#endif TRACE("keyboard device with %" B_PRIu32 " keys and %" B_PRIu32 " modifiers\n", fKeyCount, fModifierCount); @@ -156,11 +160,13 @@ KeyboardProtocolHandler::KeyboardProtocolHandler(HIDReport &inputReport, } } +#if KEYBOARD_SUPPORTS_KDL if (atomic_add(&sDebuggerCommandAdded, 1) == 0) { add_debugger_command("get_usb_keyboard_config", &debug_get_keyboard_config, "Gets the required config of the USB keyboard"); } +#endif } @@ -168,10 +174,12 @@ KeyboardProtocolHandler::~KeyboardProtocolHandler() { free(fLastKeys); +#if KEYBOARD_SUPPORTS_KDL if (atomic_add(&sDebuggerCommandAdded, -1) == 1) { remove_debugger_command("get_usb_keyboard_config", &debug_get_keyboard_config); } +#endif mutex_destroy(&fLock); } @@ -441,12 +449,16 @@ KeyboardProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer, return B_OK; case KB_SET_DEBUG_READER: +#if KEYBOARD_SUPPORTS_KDL if (fHasDebugReader) return B_BUSY; *cookie |= KEYBOARD_HANDLER_COOKIE_FLAG_DEBUGGER; fHasDebugReader = true; return B_OK; +#else + return B_NOT_SUPPORTED; +#endif } TRACE_ALWAYS("keyboard device unhandled control 0x%08" B_PRIx32 "\n", op); @@ -759,10 +771,12 @@ KeyboardProtocolHandler::_ReadReport(bigtime_t timeout, uint32 *cookie) && current[i] >= 4 && current[i] <= 29 && (fLastModifiers & ALT_KEYS) != 0) { // Alt-SysReq+letter was pressed +#if KEYBOARD_SUPPORTS_KDL sDebugKeyboardPipe = fInputReport.Device()->InterruptPipe(); sDebugKeyboardReportSize = fInputReport.Parser()->MaxReportSize(); +#endif char letter = current[i] - 4 + 'a'; diff --git a/src/add-ons/kernel/drivers/input/usb_hid/KeyboardProtocolHandler.h b/src/add-ons/kernel/drivers/input/hid_shared/KeyboardProtocolHandler.h similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/KeyboardProtocolHandler.h rename to src/add-ons/kernel/drivers/input/hid_shared/KeyboardProtocolHandler.h diff --git a/src/add-ons/kernel/drivers/input/usb_hid/MouseProtocolHandler.cpp b/src/add-ons/kernel/drivers/input/hid_shared/MouseProtocolHandler.cpp similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/MouseProtocolHandler.cpp rename to src/add-ons/kernel/drivers/input/hid_shared/MouseProtocolHandler.cpp diff --git a/src/add-ons/kernel/drivers/input/usb_hid/MouseProtocolHandler.h b/src/add-ons/kernel/drivers/input/hid_shared/MouseProtocolHandler.h similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/MouseProtocolHandler.h rename to src/add-ons/kernel/drivers/input/hid_shared/MouseProtocolHandler.h diff --git a/src/add-ons/kernel/drivers/input/usb_hid/ProtocolHandler.cpp b/src/add-ons/kernel/drivers/input/hid_shared/ProtocolHandler.cpp similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/ProtocolHandler.cpp rename to src/add-ons/kernel/drivers/input/hid_shared/ProtocolHandler.cpp diff --git a/src/add-ons/kernel/drivers/input/usb_hid/ProtocolHandler.h b/src/add-ons/kernel/drivers/input/hid_shared/ProtocolHandler.h similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/ProtocolHandler.h rename to src/add-ons/kernel/drivers/input/hid_shared/ProtocolHandler.h diff --git a/src/add-ons/kernel/drivers/input/usb_hid/TabletProtocolHandler.cpp b/src/add-ons/kernel/drivers/input/hid_shared/TabletProtocolHandler.cpp similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/TabletProtocolHandler.cpp rename to src/add-ons/kernel/drivers/input/hid_shared/TabletProtocolHandler.cpp diff --git a/src/add-ons/kernel/drivers/input/usb_hid/TabletProtocolHandler.h b/src/add-ons/kernel/drivers/input/hid_shared/TabletProtocolHandler.h similarity index 100% rename from src/add-ons/kernel/drivers/input/usb_hid/TabletProtocolHandler.h rename to src/add-ons/kernel/drivers/input/hid_shared/TabletProtocolHandler.h diff --git a/src/add-ons/kernel/drivers/input/i2c_hid/DeviceList.cpp b/src/add-ons/kernel/drivers/input/i2c_hid/DeviceList.cpp new file mode 100644 index 0000000000..e9939355ed --- /dev/null +++ b/src/add-ons/kernel/drivers/input/i2c_hid/DeviceList.cpp @@ -0,0 +1,155 @@ +/* + Generic device list for use in drivers. + Copyright (C) 2008 Michael Lotz + Distributed under the terms of the MIT license. +*/ +#include "DeviceList.h" +#include +#include +#include +#include + +struct device_list_entry { + char * name; + void * device; + device_list_entry * next; +}; + + +DeviceList::DeviceList() + : fDeviceList(NULL), + fDeviceCount(0) +{ +} + + +DeviceList::~DeviceList() +{ + device_list_entry *current = fDeviceList; + while (current) { + device_list_entry *next = current->next; + free(current->name); + delete current; + current = next; + } +} + + +status_t +DeviceList::AddDevice(const char *name, void *device) +{ + device_list_entry *entry = new(std::nothrow) device_list_entry; + if (entry == NULL) + return B_NO_MEMORY; + + entry->name = strdup(name); + if (entry->name == NULL) { + delete entry; + return B_NO_MEMORY; + } + + entry->device = device; + entry->next = NULL; + + if (fDeviceList == NULL) + fDeviceList = entry; + else { + device_list_entry *current = fDeviceList; + while (current) { + if (current->next == NULL) { + current->next = entry; + break; + } + + current = current->next; + } + } + + fDeviceCount++; + return B_OK; +} + + +status_t +DeviceList::RemoveDevice(const char *name, void *device) +{ + if (name == NULL && device == NULL) + return B_BAD_VALUE; + + device_list_entry *previous = NULL; + device_list_entry *current = fDeviceList; + while (current) { + if ((name != NULL && strcmp(current->name, name) == 0) + || (device != NULL && current->device == device)) { + if (previous == NULL) + fDeviceList = current->next; + else + previous->next = current->next; + + free(current->name); + delete current; + fDeviceCount--; + return B_OK; + } + + previous = current; + current = current->next; + } + + return B_ENTRY_NOT_FOUND; +} + + +void * +DeviceList::FindDevice(const char *name, void *device) +{ + if (name == NULL && device == NULL) + return NULL; + + device_list_entry *current = fDeviceList; + while (current) { + if ((name != NULL && strcmp(current->name, name) == 0) + || (device != NULL && current->device == device)) + return current->device; + + current = current->next; + } + + return NULL; +} + + +int32 +DeviceList::CountDevices(const char *baseName) +{ + if (baseName == NULL) + return fDeviceCount; + + int32 count = 0; + int32 baseNameLength = strlen(baseName); + device_list_entry *current = fDeviceList; + while (current) { + if (strncmp(current->name, baseName, baseNameLength) == 0) + count++; + + current = current->next; + } + + return count; +} + + +void * +DeviceList::DeviceAt(int32 index) +{ + device_list_entry *current = fDeviceList; + while (current) { + if (index-- == 0) + return current->device; + + current = current->next; + } + + return NULL; +} + diff --git a/src/add-ons/kernel/drivers/input/i2c_hid/DeviceList.h b/src/add-ons/kernel/drivers/input/i2c_hid/DeviceList.h new file mode 100644 index 0000000000..2df3e83ad0 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/i2c_hid/DeviceList.h @@ -0,0 +1,30 @@ +/* + Generic device list for use in drivers. + Copyright (C) 2008 Michael Lotz + Distributed under the terms of the MIT license. +*/ +#ifndef _DEVICE_LIST_H_ +#define _DEVICE_LIST_H_ + +#include + +struct device_list_entry; + +class DeviceList { +public: + DeviceList(); + ~DeviceList(); + + status_t AddDevice(const char *name, void *device); + status_t RemoveDevice(const char *name, void *device = NULL); + void * FindDevice(const char *name, void *device = NULL); + + int32 CountDevices(const char *baseName = NULL); + void * DeviceAt(int32 index); + +private: + device_list_entry * fDeviceList; + int32 fDeviceCount; +}; + +#endif // _DEVICE_LIST_H_ diff --git a/src/add-ons/kernel/drivers/input/i2c_hid/Driver.cpp b/src/add-ons/kernel/drivers/input/i2c_hid/Driver.cpp new file mode 100644 index 0000000000..9c2bf2b1a6 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/i2c_hid/Driver.cpp @@ -0,0 +1,520 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * Copyright 2008-2011 Michael Lotz + * Distributed under the terms of the MIT license. + */ + + +//! Driver for I2C Human Interface Devices. + + +#include +#include +#include + +#include "DeviceList.h" +#include "Driver.h" +#include "HIDDevice.h" +#include "ProtocolHandler.h" + +#include +#include + +#include +#include +#include + + + +struct hid_driver_cookie { + device_node* node; + i2c_device_interface* i2c; + i2c_device i2c_cookie; + uint32 descriptorAddress; + HIDDevice* hidDevice; +}; + +struct device_cookie { + ProtocolHandler* handler; + uint32 cookie; + hid_driver_cookie* driver_cookie; +}; + + +#define I2C_HID_DRIVER_NAME "drivers/input/i2c_hid/driver_v1" +#define I2C_HID_DEVICE_NAME "drivers/input/i2c_hid/device_v1" + +/* Base Namespace devices are published to */ +#define I2C_HID_BASENAME "input/i2c_hid/%d" + +// name of pnp generator of path ids +#define I2C_HID_PATHID_GENERATOR "i2c_hid/path_id" + +#define ACPI_NAME_HID_DEVICE "PNP0C50" + +static device_manager_info *sDeviceManager; +static acpi_module_info* gACPI; + +DeviceList *gDeviceList = NULL; +static mutex sDriverLock; + + +static acpi_object_type* +acpi_evaluate_dsm(acpi_handle handle, const uint8 *guid, uint64 revision, uint64 function) +{ + acpi_data buffer; + buffer.pointer = NULL; + buffer.length = ACPI_ALLOCATE_BUFFER; + + acpi_object_type array[4]; + acpi_objects acpi_objects; + acpi_objects.count = 4; + acpi_objects.pointer = array; + + array[0].object_type = ACPI_TYPE_BUFFER; + array[0].buffer.buffer = (void*)guid; + array[0].buffer.length = 16; + + array[1].object_type = ACPI_TYPE_INTEGER; + array[1].integer.integer = revision; + + array[2].object_type = ACPI_TYPE_INTEGER; + array[2].integer.integer = function; + + array[3].object_type = ACPI_TYPE_PACKAGE; + array[3].package.objects = NULL; + array[3].package.count = 0; + + if (gACPI->evaluate_method(handle, "_DSM", &acpi_objects, &buffer) == B_OK) + return (acpi_object_type*)buffer.pointer; + return NULL; +} + + +// #pragma mark - notify hooks + + +/* +status_t +i2c_hid_device_removed(void *cookie) +{ + mutex_lock(&sDriverLock); + int32 parentCookie = (int32)(addr_t)cookie; + TRACE("device_removed(%" B_PRId32 ")\n", parentCookie); + + for (int32 i = 0; i < gDeviceList->CountDevices(); i++) { + ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->DeviceAt(i); + if (!handler) + continue; + + HIDDevice *device = handler->Device(); + if (device->ParentCookie() != parentCookie) + continue; + + // remove all the handlers + for (uint32 i = 0;; i++) { + handler = device->ProtocolHandlerAt(i); + if (handler == NULL) + break; + + gDeviceList->RemoveDevice(NULL, handler); + } + + // this handler's device belongs to the one removed + if (device->IsOpen()) { + // the device and it's handlers will be deleted in the free hook + device->Removed(); + } else + delete device; + + break; + } + + mutex_unlock(&sDriverLock); + return B_OK; +}*/ + + +// #pragma mark - driver hooks + + +static status_t +i2c_hid_init_device(void *driverCookie, void **cookie) +{ + *cookie = driverCookie; + return B_OK; +} + + +static void +i2c_hid_uninit_device(void *_cookie) +{ + +} + + +static status_t +i2c_hid_open(void *initCookie, const char *path, int flags, void **_cookie) +{ + TRACE("open(%s, %" B_PRIu32 ", %p)\n", path, flags, _cookie); + + device_cookie *cookie = new(std::nothrow) device_cookie(); + if (cookie == NULL) + return B_NO_MEMORY; + cookie->driver_cookie = (hid_driver_cookie*)initCookie; + + MutexLocker locker(sDriverLock); + + ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->FindDevice(path); + TRACE(" path %s: handler %p\n", path, handler); + + cookie->handler = handler; + cookie->cookie = 0; + + status_t result = handler == NULL ? B_ENTRY_NOT_FOUND : B_OK; + if (result == B_OK) + result = handler->Open(flags, &cookie->cookie); + + if (result != B_OK) { + delete cookie; + return result; + } + + *_cookie = cookie; + + return B_OK; +} + + +static status_t +i2c_hid_read(void *_cookie, off_t position, void *buffer, size_t *numBytes) +{ + device_cookie *cookie = (device_cookie *)_cookie; + + TRACE("read(%p, %" B_PRIu64 ", %p, %p (%" B_PRIuSIZE ")\n", cookie, position, buffer, numBytes, + numBytes != NULL ? *numBytes : 0); + return cookie->handler->Read(&cookie->cookie, position, buffer, numBytes); +} + + +static status_t +i2c_hid_write(void *_cookie, off_t position, const void *buffer, + size_t *numBytes) +{ + device_cookie *cookie = (device_cookie *)_cookie; + + TRACE("write(%p, %" B_PRIu64 ", %p, %p (%" B_PRIuSIZE ")\n", cookie, position, buffer, numBytes, + numBytes != NULL ? *numBytes : 0); + return cookie->handler->Write(&cookie->cookie, position, buffer, numBytes); +} + + +static status_t +i2c_hid_control(void *_cookie, uint32 op, void *buffer, size_t length) +{ + device_cookie *cookie = (device_cookie *)_cookie; + + TRACE("control(%p, %" B_PRIu32 ", %p, %" B_PRIuSIZE ")\n", cookie, op, buffer, length); + return cookie->handler->Control(&cookie->cookie, op, buffer, length); +} + + +static status_t +i2c_hid_close(void *_cookie) +{ + device_cookie *cookie = (device_cookie *)_cookie; + + TRACE("close(%p)\n", cookie); + return cookie->handler->Close(&cookie->cookie); +} + + +static status_t +i2c_hid_free(void *_cookie) +{ + device_cookie *cookie = (device_cookie *)_cookie; + TRACE("free(%p)\n", cookie); + + mutex_lock(&sDriverLock); + + HIDDevice *device = cookie->handler->Device(); + if (device->IsOpen()) { + // another handler of this device is still open so we can't free it + } else if (device->IsRemoved()) { + // the parent device is removed already and none of its handlers are + // open anymore so we can free it here + delete device; + } + + mutex_unlock(&sDriverLock); + + delete cookie; + return B_OK; +} + + +// #pragma mark - driver module API + + +static float +i2c_hid_support(device_node *parent) +{ + CALLED(); + + // make sure parent is really the I2C bus manager + const char *bus; + if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) + return -1; + + if (strcmp(bus, "i2c")) + return 0.0; + TRACE("i2c_hid_support found an i2c device %p\n", parent); + + // check whether it's an HID device + uint64 handlePointer; + if (sDeviceManager->get_attr_uint64(parent, ACPI_DEVICE_HANDLE_ITEM, + &handlePointer, false) != B_OK) { + TRACE("i2c_hid_support found an i2c device without acpi handle\n"); + return B_ERROR; + } + + const char *name; + if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &name, + false) == B_OK && strcmp(name, ACPI_NAME_HID_DEVICE) == 0) { + TRACE("i2c_hid_support found an hid i2c device\n"); + return 0.6; + } + + if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_CID_ITEM, &name, + false) == B_OK && strcmp(name, ACPI_NAME_HID_DEVICE) == 0) { + TRACE("i2c_hid_support found a compatible hid i2c device\n"); + return 0.6; + } + + uint16 slaveAddress; + if (sDeviceManager->get_attr_uint16(parent, I2C_DEVICE_SLAVE_ADDR_ITEM, + &slaveAddress, false) != B_OK) { + TRACE("i2c_hid_support found a non hid without addr i2c device\n"); + return B_ERROR; + } + + TRACE("i2c_hid_support found a non hid i2c device\n"); + + return 0.0; +} + + +static status_t +i2c_hid_register_device(device_node *node) +{ + CALLED(); + + acpi_handle handle; + if (sDeviceManager->get_attr_uint64(node, ACPI_DEVICE_HANDLE_ITEM, + (uint64*)&handle, false) != B_OK) { + return B_DEVICE_NOT_FOUND; + } + + static uint8_t acpiHidGuid[] = { 0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, + 0x45, 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE }; + acpi_object_type* object = acpi_evaluate_dsm(handle, acpiHidGuid, 1, 1); + if (object == NULL) + return B_DEVICE_NOT_FOUND; + if (object->object_type != ACPI_TYPE_INTEGER) { + free(object); + return B_DEVICE_NOT_FOUND; + } + + uint32 descriptorAddress = object->integer.integer; + free(object); + device_attr attrs[] = { + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "I2C HID Device" }}, + { "descriptorAddress", B_UINT32_TYPE, { ui32: descriptorAddress }}, + { NULL } + }; + + return sDeviceManager->register_node(node, I2C_HID_DRIVER_NAME, attrs, + NULL, NULL); +} + + +static status_t +i2c_hid_init_driver(device_node *node, void **driverCookie) +{ + CALLED(); + + uint32 descriptorAddress; + if (sDeviceManager->get_attr_uint32(node, "descriptorAddress", + &descriptorAddress, false) != B_OK) { + return B_DEVICE_NOT_FOUND; + } + + hid_driver_cookie *device + = (hid_driver_cookie *)calloc(1, sizeof(hid_driver_cookie)); + if (device == NULL) + return B_NO_MEMORY; + + *driverCookie = device; + + device->node = node; + device->descriptorAddress = descriptorAddress; + + device_node *parent; + parent = sDeviceManager->get_parent_node(node); + sDeviceManager->get_driver(parent, (driver_module_info **)&device->i2c, + (void **)&device->i2c_cookie); + sDeviceManager->put_node(parent); + + mutex_lock(&sDriverLock); + HIDDevice *hidDevice + = new(std::nothrow) HIDDevice(descriptorAddress, device->i2c, + device->i2c_cookie); + + if (hidDevice != NULL && hidDevice->InitCheck() == B_OK) { + device->hidDevice = hidDevice; + } else + delete hidDevice; + + mutex_unlock(&sDriverLock); + + return B_OK; +} + + +static void +i2c_hid_uninit_driver(void *driverCookie) +{ + CALLED(); + hid_driver_cookie *device = (hid_driver_cookie*)driverCookie; + + free(device); +} + + +static status_t +i2c_hid_register_child_devices(void *cookie) +{ + CALLED(); + hid_driver_cookie *device = (hid_driver_cookie*)cookie; + HIDDevice* hidDevice = device->hidDevice; + for (uint32 i = 0;; i++) { + ProtocolHandler *handler = hidDevice->ProtocolHandlerAt(i); + if (handler == NULL) + break; + + // As devices can be un- and replugged at will, we cannot + // simply rely on a device count. If there is just one + // keyboard, this does not mean that it uses the 0 name. + // There might have been two keyboards and the one using 0 + // might have been unplugged. So we just generate names + // until we find one that is not currently in use. + int32 index = 0; + char pathBuffer[128]; + const char *basePath = handler->BasePath(); + while (true) { + sprintf(pathBuffer, "%s%" B_PRId32, basePath, index++); + if (gDeviceList->FindDevice(pathBuffer) == NULL) { + // this name is still free, use it + handler->SetPublishPath(strdup(pathBuffer)); + break; + } + } + + gDeviceList->AddDevice(handler->PublishPath(), handler); + + sDeviceManager->publish_device(device->node, pathBuffer, + I2C_HID_DEVICE_NAME); + } + + +/* int pathID = sDeviceManager->create_id(I2C_HID_PATHID_GENERATOR); + if (pathID < 0) { + ERROR("register_child_devices: couldn't create a path_id\n"); + return B_ERROR; + }*/ + return B_OK; +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + gDeviceList = new(std::nothrow) DeviceList(); + if (gDeviceList == NULL) { + return B_NO_MEMORY; + } + mutex_init(&sDriverLock, "i2c hid driver lock"); + + return B_OK; + case B_MODULE_UNINIT: + delete gDeviceList; + gDeviceList = NULL; + mutex_destroy(&sDriverLock); + return B_OK; + + default: + break; + } + + return B_ERROR; +} + + +// #pragma mark - + + +driver_module_info i2c_hid_driver_module = { + { + I2C_HID_DRIVER_NAME, + 0, + &std_ops + }, + + i2c_hid_support, + i2c_hid_register_device, + i2c_hid_init_driver, + i2c_hid_uninit_driver, + i2c_hid_register_child_devices, + NULL, // rescan + NULL, // removed +}; + + +struct device_module_info i2c_hid_device_module = { + { + I2C_HID_DEVICE_NAME, + 0, + NULL + }, + + i2c_hid_init_device, + i2c_hid_uninit_device, + NULL, + + i2c_hid_open, + i2c_hid_close, + i2c_hid_free, + i2c_hid_read, + i2c_hid_write, + NULL, + i2c_hid_control, + + NULL, + NULL +}; + + +module_dependency module_dependencies[] = { + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, + { B_ACPI_MODULE_NAME, (module_info**)&gACPI }, + {} +}; + + +module_info *modules[] = { + (module_info *)&i2c_hid_driver_module, + (module_info *)&i2c_hid_device_module, + NULL +}; diff --git a/src/add-ons/kernel/drivers/input/i2c_hid/Driver.h b/src/add-ons/kernel/drivers/input/i2c_hid/Driver.h new file mode 100644 index 0000000000..bbdfe98534 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/i2c_hid/Driver.h @@ -0,0 +1,33 @@ +/* + Driver for I2C Human Interface Devices. + Copyright (C) 2008 Michael Lotz + Distributed under the terms of the MIT license. +*/ +#ifndef _I2C_HID_DRIVER_H_ +#define _I2C_HID_DRIVER_H_ + +#include +#include +#include +#include + +#include "DeviceList.h" + +#define DRIVER_NAME "i2c_hid" +#define DEVICE_PATH_SUFFIX "i2c" + + +extern DeviceList *gDeviceList; + + +//#define TRACE_I2C_HID +#ifdef TRACE_I2C_HID +# define TRACE(x...) dprintf(DRIVER_NAME ": " x) +#else +# define TRACE(x...) +#endif +#define ERROR(x...) dprintf(DRIVER_NAME ": " x) +#define TRACE_ALWAYS(x...) dprintf(DRIVER_NAME ": " x) +#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + +#endif //_I2C_HID_DRIVER_H_ diff --git a/src/add-ons/kernel/drivers/input/i2c_hid/HIDDevice.cpp b/src/add-ons/kernel/drivers/input/i2c_hid/HIDDevice.cpp new file mode 100644 index 0000000000..3b080b7a85 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/i2c_hid/HIDDevice.cpp @@ -0,0 +1,341 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * Copyright 2008-2011, Michael Lotz + * Distributed under the terms of the MIT license. + */ + + +//! Driver for I2C Human Interface Devices. + + +#include "Driver.h" +#include "HIDDevice.h" +#include "HIDReport.h" +#include "HIDWriter.h" +#include "ProtocolHandler.h" + +#include + +#include +#include +#include +#include +#include + + +HIDDevice::HIDDevice(uint16 descriptorAddress, i2c_device_interface* i2c, + i2c_device i2cCookie) + : fStatus(B_NO_INIT), + fTransferLastschedule(0), + fTransferScheduled(0), + fTransferBufferSize(0), + fTransferBuffer(NULL), + fOpenCount(0), + fRemoved(false), + fParser(this), + fProtocolHandlerCount(0), + fProtocolHandlerList(NULL), + fDescriptorAddress(descriptorAddress), + fI2C(i2c), + fI2CCookie(i2cCookie) +{ + // fetch HID descriptor + fStatus = _FetchBuffer((uint8*)&fDescriptorAddress, + sizeof(fDescriptorAddress), &fDescriptor, sizeof(fDescriptor)); + if (fStatus != B_OK) { + ERROR("failed to fetch HID descriptor\n"); + return; + } + + // fetch HID Report descriptor + + HIDWriter descriptorWriter; + + uint16 descriptorLength = fDescriptor.wReportDescLength; + fReportDescriptor = (uint8 *)malloc(descriptorLength); + if (fReportDescriptor == NULL) { + ERROR("failed to allocate buffer for report descriptor\n"); + fStatus = B_NO_MEMORY; + return; + } + + uint16 reportDescRegister = fDescriptor.wReportDescRegister; + fStatus = _FetchBuffer((uint8*)&reportDescRegister, + sizeof(reportDescRegister), fReportDescriptor, + descriptorLength); + if (fStatus != B_OK) { + ERROR("failed tot get report descriptor\n"); + free(fReportDescriptor); + return; + } + +#if 1 + // save report descriptor for troubleshooting + char outputFile[128]; + sprintf(outputFile, "/tmp/i2c_hid_report_descriptor_%04x_%04x.bin", + fDescriptor.wVendorID, fDescriptor.wProductID); + int fd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd >= 0) { + write(fd, fReportDescriptor, descriptorLength); + close(fd); + } +#endif + + status_t result = fParser.ParseReportDescriptor(fReportDescriptor, + descriptorLength); + free(fReportDescriptor); + + if (result != B_OK) { + ERROR("parsing the report descriptor failed\n"); + fStatus = result; + return; + } + +#if 0 + for (uint32 i = 0; i < fParser.CountReports(HID_REPORT_TYPE_ANY); i++) + fParser.ReportAt(HID_REPORT_TYPE_ANY, i)->PrintToStream(); +#endif + + fTransferBufferSize = fParser.MaxReportSize(); + if (fTransferBufferSize == 0) { + TRACE_ALWAYS("report claims a report size of 0\n"); + return; + } + + // We pad the allocation size so that we can always read 32 bits at a time + // (as done in HIDReportItem) without the need for an additional boundary + // check. We don't increase the transfer buffer size though as to not expose + // this implementation detail onto the device when scheduling transfers. + fTransferBuffer = (uint8 *)malloc(fTransferBufferSize + 3); + if (fTransferBuffer == NULL) { + TRACE_ALWAYS("failed to allocate transfer buffer\n"); + fStatus = B_NO_MEMORY; + return; + } + + ProtocolHandler::AddHandlers(*this, fProtocolHandlerList, + fProtocolHandlerCount); + fStatus = B_OK; +} + + +HIDDevice::~HIDDevice() +{ + ProtocolHandler *handler = fProtocolHandlerList; + while (handler != NULL) { + ProtocolHandler *next = handler->NextHandler(); + delete handler; + handler = next; + } + + free(fTransferBuffer); +} + + +status_t +HIDDevice::Open(ProtocolHandler *handler, uint32 flags) +{ + atomic_add(&fOpenCount, 1); + _Reset(); + + return B_OK; +} + + +status_t +HIDDevice::Close(ProtocolHandler *handler) +{ + atomic_add(&fOpenCount, -1); + _SetPower(I2C_HID_POWER_OFF); + + return B_OK; +} + + +void +HIDDevice::Removed() +{ + fRemoved = true; +} + + +status_t +HIDDevice::MaybeScheduleTransfer(HIDReport *report) +{ + if (fRemoved) + return B_ERROR; + + if (atomic_get_and_set(&fTransferScheduled, 1) != 0) { + // someone else already caused a transfer to be scheduled + return B_OK; + } + + snooze_until(fTransferLastschedule, B_SYSTEM_TIMEBASE); + fTransferLastschedule = system_time() + 10000; + + TRACE("scheduling interrupt transfer of %lu bytes\n", + report->ReportSize()); + return _FetchReport(report->Type(), report->ID(), report->ReportSize()); +} + + +status_t +HIDDevice::SendReport(HIDReport *report) +{ + // TODO + return B_OK; +} + + +ProtocolHandler * +HIDDevice::ProtocolHandlerAt(uint32 index) const +{ + ProtocolHandler *handler = fProtocolHandlerList; + while (handler != NULL) { + if (index == 0) + return handler; + + handler = handler->NextHandler(); + index--; + } + + return NULL; +} + + +void +HIDDevice::_UnstallCallback(void *cookie, status_t status, void *data, + size_t actualLength) +{ + HIDDevice *device = (HIDDevice *)cookie; + if (status != B_OK) { + TRACE_ALWAYS("Unable to unstall device: %s\n", strerror(status)); + } + + // Now report the original failure, since we're ready to retry + _TransferCallback(cookie, B_ERROR, device->fTransferBuffer, 0); +} + + +void +HIDDevice::_TransferCallback(void *cookie, status_t status, void *data, + size_t actualLength) +{ + HIDDevice *device = (HIDDevice *)cookie; + + atomic_set(&device->fTransferScheduled, 0); + device->fParser.SetReport(status, device->fTransferBuffer, actualLength); +} + + +status_t +HIDDevice::_Reset() +{ + CALLED(); + status_t status = _SetPower(I2C_HID_POWER_ON); + if (status != B_OK) + return status; + + snooze(1000); + + uint8 cmd[] = { + (uint8)(fDescriptor.wCommandRegister & 0xff), + (uint8)(fDescriptor.wCommandRegister >> 8), + 0, + I2C_HID_CMD_RESET, + }; + + status = _ExecCommand(I2C_OP_WRITE_STOP, cmd, sizeof(cmd), NULL, 0); + if (status != B_OK) { + _SetPower(I2C_HID_POWER_OFF); + return status; + } + + snooze(1000); + return B_OK; +} + + +status_t +HIDDevice::_SetPower(uint8 power) +{ + CALLED(); + uint8 cmd[] = { + (uint8)(fDescriptor.wCommandRegister & 0xff), + (uint8)(fDescriptor.wCommandRegister >> 8), + power, + I2C_HID_CMD_SET_POWER + }; + + return _ExecCommand(I2C_OP_WRITE_STOP, cmd, sizeof(cmd), NULL, 0); +} + + +status_t +HIDDevice::_FetchReport(uint8 type, uint8 id, size_t reportSize) +{ + uint8 reportId = id > 15 ? 15 : id; + size_t cmdLength = 6; + uint8 cmd[] = { + (uint8)(fDescriptor.wCommandRegister & 0xff), + (uint8)(fDescriptor.wCommandRegister >> 8), + (uint8)(reportId | (type << 4)), + I2C_HID_CMD_GET_REPORT, + 0, 0, 0, + }; + + int dataOffset = 4; + int reportIdLength = 1; + if (reportId == 15) { + cmd[dataOffset++] = id; + cmdLength++; + reportIdLength++; + } + cmd[dataOffset++] = fDescriptor.wDataRegister & 0xff; + cmd[dataOffset++] = fDescriptor.wDataRegister >> 8; + + size_t bufferLength = reportSize + reportIdLength + 2; + + status_t status = _FetchBuffer(cmd, cmdLength, fTransferBuffer, + bufferLength); + if (status != B_OK) { + atomic_set(&fTransferScheduled, 0); + return status; + } + + uint16 actualLength = fTransferBuffer[0] | (fTransferBuffer[1] << 8); + TRACE("_FetchReport %" B_PRIuSIZE " %" B_PRIu16 "\n", reportSize, + actualLength); + if (actualLength <= 2 || actualLength == 0xffff || bufferLength == 0) + actualLength = 0; + else + actualLength -= 2; + + atomic_set(&fTransferScheduled, 0); + fParser.SetReport(status, + (uint8*)((addr_t)fTransferBuffer + 2), actualLength); + return B_OK; +} + + +status_t +HIDDevice::_FetchBuffer(uint8* cmd, size_t cmdLength, void* buffer, + size_t bufferLength) +{ + return _ExecCommand(I2C_OP_READ_STOP, cmd, cmdLength, + buffer, bufferLength); +} + + +status_t +HIDDevice::_ExecCommand(i2c_op op, uint8* cmd, size_t cmdLength, void* buffer, + size_t bufferLength) +{ + status_t status = fI2C->acquire_bus(fI2CCookie); + if (status != B_OK) + return status; + status = fI2C->exec_command(fI2CCookie, I2C_OP_READ_STOP, cmd, cmdLength, + buffer, bufferLength); + fI2C->release_bus(fI2CCookie); + return status; +} diff --git a/src/add-ons/kernel/drivers/input/i2c_hid/HIDDevice.h b/src/add-ons/kernel/drivers/input/i2c_hid/HIDDevice.h new file mode 100644 index 0000000000..3ab8be7462 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/i2c_hid/HIDDevice.h @@ -0,0 +1,123 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * Copyright 2008-2011, Michael Lotz + * Distributed under the terms of the MIT license. + */ +#ifndef I2C_HID_DEVICE_H +#define I2C_HID_DEVICE_H + + +#include + +#include "HIDParser.h" + + +/* 5.1.1 - HID Descriptor Format */ +typedef struct i2c_hid_descriptor { + uint16 wHIDDescLength; + uint16 bcdVersion; + uint16 wReportDescLength; + uint16 wReportDescRegister; + uint16 wInputRegister; + uint16 wMaxInputLength; + uint16 wOutputRegister; + uint16 wMaxOutputLength; + uint16 wCommandRegister; + uint16 wDataRegister; + uint16 wVendorID; + uint16 wProductID; + uint16 wVersionID; + uint32 reserved; +} _PACKED i2c_hid_descriptor; + + +enum { + I2C_HID_CMD_RESET = 0x1, + I2C_HID_CMD_GET_REPORT = 0x2, + I2C_HID_CMD_SET_REPORT = 0x3, + I2C_HID_CMD_GET_IDLE = 0x4, + I2C_HID_CMD_SET_IDLE = 0x5, + I2C_HID_CMD_GET_PROTOCOL = 0x6, + I2C_HID_CMD_SET_PROTOCOL = 0x7, + I2C_HID_CMD_SET_POWER = 0x8, +}; + +enum { + I2C_HID_POWER_ON = 0x0, + I2C_HID_POWER_OFF = 0x1, +}; + + +class ProtocolHandler; + + +class HIDDevice { +public: + HIDDevice(uint16 descriptorAddress, i2c_device_interface* i2c, + i2c_device i2cCookie); + ~HIDDevice(); + + status_t InitCheck() const { return fStatus; } + + bool IsOpen() const { return fOpenCount > 0; } + status_t Open(ProtocolHandler *handler, uint32 flags); + status_t Close(ProtocolHandler *handler); + int32 OpenCount() const { return fOpenCount; } + + void Removed(); + bool IsRemoved() const { return fRemoved; } + + status_t MaybeScheduleTransfer(HIDReport *report); + + status_t SendReport(HIDReport *report); + + HIDParser & Parser() { return fParser; } + ProtocolHandler * ProtocolHandlerAt(uint32 index) const; + +private: + static void _TransferCallback(void *cookie, + status_t status, void *data, + size_t actualLength); + static void _UnstallCallback(void *cookie, + status_t status, void *data, + size_t actualLength); + + status_t _Reset(); + status_t _SetPower(uint8 power); + status_t _FetchBuffer(uint8* cmd, size_t cmdLength, + void* buffer, size_t bufferLength); + status_t _FetchReport(uint8 type, uint8 id, + size_t reportSize); + status_t _ExecCommand(i2c_op op, uint8* cmd, + size_t cmdLength, void* buffer, + size_t bufferLength); + +private: + status_t fStatus; + + + bigtime_t fTransferLastschedule; + int32 fTransferScheduled; + size_t fTransferBufferSize; + uint8 * fTransferBuffer; + + int32 fOpenCount; + bool fRemoved; + + HIDParser fParser; + + uint32 fProtocolHandlerCount; + ProtocolHandler * fProtocolHandlerList; + + uint16 fDescriptorAddress; + i2c_hid_descriptor fDescriptor; + + uint8* fReportDescriptor; + + i2c_device_interface* fI2C; + i2c_device fI2CCookie; + +}; + + +#endif // I2C_HID_DEVICE_H diff --git a/src/add-ons/kernel/drivers/input/i2c_hid/Jamfile b/src/add-ons/kernel/drivers/input/i2c_hid/Jamfile new file mode 100644 index 0000000000..fda48b6395 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/i2c_hid/Jamfile @@ -0,0 +1,29 @@ +SubDir HAIKU_TOP src add-ons kernel drivers input i2c_hid ; + +SubDirC++Flags -fno-rtti ; + +SubDirSysHdrs $(HAIKU_TOP) headers os drivers ; +SubDirSysHdrs $(HAIKU_TOP) src add-ons kernel drivers input hid_shared ; +UsePrivateHeaders [ FDirName kernel util ] input drivers device i2c ; +UsePrivateKernelHeaders ; + +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src add-ons kernel drivers input hid_shared ] ; + +KernelAddon i2c_hid : + DeviceList.cpp + Driver.cpp + HIDDevice.cpp + + HIDCollection.cpp + HIDParser.cpp + HIDReport.cpp + HIDReportItem.cpp + HIDWriter.cpp + + ProtocolHandler.cpp + + JoystickProtocolHandler.cpp + KeyboardProtocolHandler.cpp + MouseProtocolHandler.cpp + TabletProtocolHandler.cpp +; diff --git a/src/add-ons/kernel/drivers/input/i2c_hid/KeyboardProtocolHandler.cpp b/src/add-ons/kernel/drivers/input/i2c_hid/KeyboardProtocolHandler.cpp new file mode 100644 index 0000000000..9e9be8f82b --- /dev/null +++ b/src/add-ons/kernel/drivers/input/i2c_hid/KeyboardProtocolHandler.cpp @@ -0,0 +1,732 @@ +/* + * Copyright 2008-2011 Michael Lotz + * Distributed under the terms of the MIT license. + */ + + +#include +#include +#include + +#include +#include + +#include + +#include "Driver.h" +#include "KeyboardProtocolHandler.h" + +#include "HIDCollection.h" +#include "HIDDevice.h" +#include "HIDReport.h" +#include "HIDReportItem.h" + +#include + + +#define LEFT_ALT_KEY 0x04 +#define RIGHT_ALT_KEY 0x40 +#define ALT_KEYS (LEFT_ALT_KEY | RIGHT_ALT_KEY) + +#define KEYBOARD_HANDLER_COOKIE_FLAG_READER 0x01 +#define KEYBOARD_HANDLER_COOKIE_FLAG_DEBUGGER 0x02 + + +// #pragma mark - + + +KeyboardProtocolHandler::KeyboardProtocolHandler(HIDReport &inputReport, + HIDReport *outputReport) + : + ProtocolHandler(inputReport.Device(), "input/keyboard/i2c/", 512), + fInputReport(inputReport), + fOutputReport(outputReport), + fRepeatDelay(300000), + fRepeatRate(35000), + fCurrentRepeatDelay(B_INFINITE_TIMEOUT), + fCurrentRepeatKey(0), + fKeyCount(0), + fModifierCount(0), + fLastModifiers(0), + fCurrentKeys(NULL), + fLastKeys(NULL), + fHasReader(0) +{ + mutex_init(&fLock, "i2c keyboard"); + + // find modifiers and keys + for (uint32 i = 0; i < inputReport.CountItems(); i++) { + HIDReportItem *item = inputReport.ItemAt(i); + if (!item->HasData()) + continue; + + if (item->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD + || item->UsagePage() == B_HID_USAGE_PAGE_CONSUMER + || item->UsagePage() == B_HID_USAGE_PAGE_BUTTON) { + TRACE("keyboard item with usage %" B_PRIx32 "\n", + item->UsageMinimum()); + + if (item->Array()) { + // normal or "consumer"/button keys handled as array items + if (fKeyCount < MAX_KEYS) + fKeys[fKeyCount++] = item; + + } else { + if (item->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD + && item->UsageID() >= B_HID_UID_KB_LEFT_CONTROL + && item->UsageID() <= B_HID_UID_KB_RIGHT_GUI) { + // modifiers are generally implemented as bitmaps + if (fModifierCount < MAX_MODIFIERS) + fModifiers[fModifierCount++] = item; + } + } + } + } + + TRACE("keyboard device with %" B_PRIu32 " keys and %" B_PRIu32 + " modifiers\n", fKeyCount, fModifierCount); + TRACE("input report: %u; output report: %u\n", inputReport.ID(), + outputReport != NULL ? outputReport->ID() : 255); + + fLastKeys = (uint16 *)malloc(fKeyCount * 2 * sizeof(uint16)); + fCurrentKeys = &fLastKeys[fKeyCount]; + if (fLastKeys == NULL) { + fStatus = B_NO_MEMORY; + return; + } + + // find leds if we have an output report + for (uint32 i = 0; i < MAX_LEDS; i++) + fLEDs[i] = NULL; + + if (outputReport != NULL) { + for (uint32 i = 0; i < outputReport->CountItems(); i++) { + HIDReportItem *item = outputReport->ItemAt(i); + if (!item->HasData()) + continue; + + // the led item array is identity mapped with what we get from + // the input_server for the set-leds command + if (item->UsagePage() == B_HID_USAGE_PAGE_LED) { + switch (item->UsageID()) { + case B_HID_UID_LED_NUM_LOCK: + fLEDs[0] = item; + break; + case B_HID_UID_LED_CAPS_LOCK: + fLEDs[1] = item; + break; + case B_HID_UID_LED_SCROLL_LOCK: + fLEDs[2] = item; + break; + } + } + } + } +} + + +KeyboardProtocolHandler::~KeyboardProtocolHandler() +{ + free(fLastKeys); + + mutex_destroy(&fLock); +} + + +void +KeyboardProtocolHandler::AddHandlers(HIDDevice &device, + HIDCollection &collection, ProtocolHandler *&handlerList) +{ + bool handled = false; + switch (collection.UsagePage()) { + case B_HID_USAGE_PAGE_GENERIC_DESKTOP: + { + switch (collection.UsageID()) { + case B_HID_UID_GD_KEYBOARD: + case B_HID_UID_GD_KEYPAD: + case B_HID_UID_GD_SYSTEM_CONTROL: + handled = true; + } + + break; + } + + case B_HID_USAGE_PAGE_CONSUMER: + { + switch (collection.UsageID()) { + case B_HID_UID_CON_CONSUMER_CONTROL: + handled = true; + } + + break; + } + } + + if (!handled) { + TRACE("collection not a supported keyboard subset\n"); + return; + } + + HIDParser &parser = device.Parser(); + uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT); + if (maxReportCount == 0) + return; + + uint32 inputReportCount = 0; + HIDReport *inputReports[maxReportCount]; + collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports, + inputReportCount); + + TRACE("input report count: %" B_PRIu32 "\n", inputReportCount); + + for (uint32 i = 0; i < inputReportCount; i++) { + HIDReport *inputReport = inputReports[i]; + +// bool mayHaveOutput = false; + bool foundKeyboardUsage = false; + for (uint32 j = 0; j < inputReport->CountItems(); j++) { + HIDReportItem *item = inputReport->ItemAt(j); + if (!item->HasData()) + continue; + + if (item->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD + || (item->UsagePage() == B_HID_USAGE_PAGE_CONSUMER + && item->Array()) + || (item->UsagePage() == B_HID_USAGE_PAGE_BUTTON + && item->Array())) { + // found at least one item with a keyboard usage or with + // a consumer/button usage that is handled like a key +// mayHaveOutput = item->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD; + foundKeyboardUsage = true; + break; + } + } + + if (!foundKeyboardUsage) + continue; + + bool foundOutputReport = false; + HIDReport *outputReport = NULL; + do { + // try to find the led output report + maxReportCount = parser.CountReports(HID_REPORT_TYPE_OUTPUT); + if (maxReportCount == 0) + break; + + uint32 outputReportCount = 0; + HIDReport *outputReports[maxReportCount]; + collection.BuildReportList(HID_REPORT_TYPE_OUTPUT, + outputReports, outputReportCount); + + for (uint32 j = 0; j < outputReportCount; j++) { + outputReport = outputReports[j]; + + for (uint32 k = 0; k < outputReport->CountItems(); k++) { + HIDReportItem *item = outputReport->ItemAt(k); + if (item->UsagePage() == B_HID_USAGE_PAGE_LED) { + foundOutputReport = true; + break; + } + } + + if (foundOutputReport) + break; + } + } while (false); + + ProtocolHandler *newHandler = new(std::nothrow) KeyboardProtocolHandler( + *inputReport, foundOutputReport ? outputReport : NULL); + if (newHandler == NULL) { + TRACE("failed to allocated keyboard protocol handler\n"); + continue; + } + + newHandler->SetNextHandler(handlerList); + handlerList = newHandler; + } +} + + +status_t +KeyboardProtocolHandler::Open(uint32 flags, uint32 *cookie) +{ + status_t status = ProtocolHandler::Open(flags, cookie); + if (status != B_OK) { + TRACE_ALWAYS("keyboard device failed to open: %s\n", + strerror(status)); + return status; + } + + if (Device()->OpenCount() == 1) { + fCurrentRepeatDelay = B_INFINITE_TIMEOUT; + fCurrentRepeatKey = 0; + } + + return B_OK; +} + + +status_t +KeyboardProtocolHandler::Close(uint32 *cookie) +{ + if ((*cookie & KEYBOARD_HANDLER_COOKIE_FLAG_READER) != 0) + atomic_and(&fHasReader, 0); + + return ProtocolHandler::Close(cookie); +} + + +status_t +KeyboardProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer, + size_t length) +{ + switch (op) { + case KB_READ: + { + if (*cookie == 0) { + if (atomic_or(&fHasReader, 1) != 0) + return B_BUSY; + + // We're the first, so we become the only reader + *cookie = KEYBOARD_HANDLER_COOKIE_FLAG_READER; + } + + while (true) { + MutexLocker locker(fLock); + + bigtime_t enterTime = system_time(); + while (RingBufferReadable() == 0) { + status_t result = _ReadReport(fCurrentRepeatDelay, cookie); + if (result != B_OK && result != B_TIMED_OUT) + return result; + + if (!Device()->IsOpen()) + return B_ERROR; + + if (RingBufferReadable() == 0 && fCurrentRepeatKey != 0 + && system_time() - enterTime > fCurrentRepeatDelay) { + // this case is for handling key repeats, it means no + // interrupt transfer has happened or it didn't produce + // any new key events, but a repeated key down is due + _WriteKey(fCurrentRepeatKey, true); + + // the next timeout is reduced to the repeat_rate + fCurrentRepeatDelay = fRepeatRate; + break; + } + } + + if (!IS_USER_ADDRESS(buffer)) + return B_BAD_ADDRESS; + + // process what is in the ring_buffer, it could be written + // there because we handled an interrupt transfer or because + // we wrote the current repeat key + return RingBufferRead(buffer, sizeof(raw_key_info)); + } + } + + case KB_SET_LEDS: + { + uint8 ledData[4]; + if (!IS_USER_ADDRESS(buffer) + || user_memcpy(ledData, buffer, sizeof(ledData)) != B_OK) { + return B_BAD_ADDRESS; + } + return _SetLEDs(ledData); + } + + case KB_SET_KEY_REPEAT_RATE: + { + int32 repeatRate; + if (!IS_USER_ADDRESS(buffer) + || user_memcpy(&repeatRate, buffer, sizeof(repeatRate)) + != B_OK) { + return B_BAD_ADDRESS; + } + + if (repeatRate == 0 || repeatRate > 1000000) + return B_BAD_VALUE; + + fRepeatRate = 10000000 / repeatRate; + return B_OK; + } + + case KB_GET_KEY_REPEAT_RATE: + { + int32 repeatRate = 10000000 / fRepeatRate; + if (!IS_USER_ADDRESS(buffer) + || user_memcpy(buffer, &repeatRate, sizeof(repeatRate)) + != B_OK) { + return B_BAD_ADDRESS; + } + return B_OK; + } + + case KB_SET_KEY_REPEAT_DELAY: + if (!IS_USER_ADDRESS(buffer) + || user_memcpy(&fRepeatDelay, buffer, sizeof(fRepeatDelay)) + != B_OK) { + return B_BAD_ADDRESS; + } + return B_OK; + + case KB_GET_KEY_REPEAT_DELAY: + if (!IS_USER_ADDRESS(buffer) + || user_memcpy(buffer, &fRepeatDelay, sizeof(fRepeatDelay)) + != B_OK) { + return B_BAD_ADDRESS; + } + return B_OK; + + } + + TRACE_ALWAYS("keyboard device unhandled control 0x%08" B_PRIx32 "\n", op); + return B_ERROR; +} + + +void +KeyboardProtocolHandler::_WriteKey(uint32 key, bool down) +{ + raw_key_info info; + info.keycode = key; + info.is_keydown = down; + info.timestamp = system_time(); + RingBufferWrite(&info, sizeof(raw_key_info)); +} + + +status_t +KeyboardProtocolHandler::_SetLEDs(uint8 *data) +{ + if (fOutputReport == NULL || fOutputReport->Device()->IsRemoved()) + return B_ERROR; + + for (uint32 i = 0; i < MAX_LEDS; i++) { + if (fLEDs[i] == NULL) + continue; + + fLEDs[i]->SetData(data[i]); + } + + return fOutputReport->SendReport(); +} + + +status_t +KeyboardProtocolHandler::_ReadReport(bigtime_t timeout, uint32 *cookie) +{ + status_t result = fInputReport.WaitForReport(timeout); + if (result != B_OK) { + if (fInputReport.Device()->IsRemoved()) { + TRACE("device has been removed\n"); + return B_ERROR; + } + + if ((*cookie & PROTOCOL_HANDLER_COOKIE_FLAG_CLOSED) != 0) + return B_CANCELED; + + if (result != B_TIMED_OUT && result != B_INTERRUPTED) { + // we expect timeouts as we do repeat key handling this way, + // interrupts happen when other reports come in on the same + // endpoint + TRACE_ALWAYS("error waiting for report: %s\n", strerror(result)); + } + + // signal that we simply want to try again + return B_OK; + } + + TRACE("got keyboard input report\n"); + + uint8 modifiers = 0; + for (uint32 i = 0; i < fModifierCount; i++) { + HIDReportItem *modifier = fModifiers[i]; + if (modifier == NULL) + break; + + if (modifier->Extract() == B_OK && modifier->Valid()) { + modifiers |= (modifier->Data() & 1) + << (modifier->UsageID() - B_HID_UID_KB_LEFT_CONTROL); + } + } + + for (uint32 i = 0; i < fKeyCount; i++) { + HIDReportItem *key = fKeys[i]; + if (key == NULL) + break; + + if (key->Extract() == B_OK && key->Valid()) + fCurrentKeys[i] = key->Data(); + else + fCurrentKeys[i] = 0; + } + + fInputReport.DoneProcessing(); + + static const uint32 kModifierTable[] = { + KEY_ControlL, + KEY_ShiftL, + KEY_AltL, + KEY_WinL, + KEY_ControlR, + KEY_ShiftR, + KEY_AltR, + KEY_WinR + }; + + // find modifier changes and push them into the buffer + uint8 modifierChange = fLastModifiers ^ modifiers; + for (uint8 i = 0; modifierChange; i++, modifierChange >>= 1) { + if (modifierChange & 1) + _WriteKey(kModifierTable[i], (modifiers >> i) & 1); + } + + fLastModifiers = modifiers; + + static const uint32 kKeyTable[] = { + 0x00, // ERROR + 0x00, // ERROR + 0x00, // ERROR + 0x00, // ERROR + 0x3c, // A + 0x50, // B + 0x4e, // C + 0x3e, // D + 0x29, // E + 0x3f, // F + 0x40, // G + 0x41, // H + 0x2e, // I + 0x42, // J + 0x43, // K + 0x44, // L + 0x52, // M + 0x51, // N + 0x2f, // O + 0x30, // P + 0x27, // Q + 0x2a, // R + 0x3d, // S + 0x2b, // T + 0x2d, // U + 0x4f, // V + 0x28, // W + 0x4d, // X + 0x2c, // Y + 0x4c, // Z + 0x12, // 1 + 0x13, // 2 + 0x14, // 3 + 0x15, // 4 + 0x16, // 5 + 0x17, // 6 + 0x18, // 7 + 0x19, // 8 + 0x1a, // 9 + 0x1b, // 0 + 0x47, // enter + 0x01, // Esc + 0x1e, // Backspace + 0x26, // Tab + 0x5e, // Space + 0x1c, // - + 0x1d, // = + 0x31, // [ + 0x32, // ] + 0x33, // backslash + 0x33, // backslash + 0x45, // ; + 0x46, // ' + 0x11, // ` + 0x53, // , + 0x54, // . + 0x55, // / + KEY_CapsLock, // Caps + 0x02, // F1 + 0x03, // F2 + 0x04, // F3 + 0x05, // F4 + 0x06, // F5 + 0x07, // F6 + 0x08, // F7 + 0x09, // F8 + 0x0a, // F9 + 0x0b, // F10 + 0x0c, // F11 + 0x0d, // F12 + 0x0e, // PrintScreen + KEY_Scroll, // Scroll Lock + KEY_Pause, // Pause (0x7f with Ctrl) + 0x1f, // Insert + 0x20, // Home + 0x21, // Page up + 0x34, // Delete + 0x35, // End + 0x36, // Page down + 0x63, // Right arrow + 0x61, // Left arrow + 0x62, // Down arrow + 0x57, // Up arrow + 0x22, // Num Lock + 0x23, // Pad / + 0x24, // Pad * + 0x25, // Pad - + 0x3a, // Pad + + 0x5b, // Pad Enter + 0x58, // Pad 1 + 0x59, // Pad 2 + 0x5a, // Pad 3 + 0x48, // Pad 4 + 0x49, // Pad 5 + 0x4a, // Pad 6 + 0x37, // Pad 7 + 0x38, // Pad 8 + 0x39, // Pad 9 + 0x64, // Pad 0 + 0x65, // Pad . + 0x69, // < + KEY_Menu, // Menu + KEY_Power, // Power + KEY_NumEqual, // Pad = + 0x00, // F13 unmapped + 0x00, // F14 unmapped + 0x00, // F15 unmapped + 0x00, // F16 unmapped + 0x00, // F17 unmapped + 0x00, // F18 unmapped + 0x00, // F19 unmapped + 0x00, // F20 unmapped + 0x00, // F21 unmapped + 0x00, // F22 unmapped + 0x00, // F23 unmapped + 0x00, // F24 unmapped + 0x00, // Execute unmapped + 0x00, // Help unmapped + 0x00, // Menu unmapped + 0x00, // Select unmapped + 0x00, // Stop unmapped + 0x00, // Again unmapped + 0x00, // Undo unmapped + 0x00, // Cut unmapped + 0x00, // Copy unmapped + 0x00, // Paste unmapped + 0x00, // Find unmapped + 0x00, // Mute unmapped + 0x00, // Volume up unmapped + 0x00, // Volume down unmapped + 0x00, // CapsLock unmapped + 0x00, // NumLock unmapped + 0x00, // Scroll lock unmapped + 0x70, // Keypad . on Brazilian ABNT2 + 0x00, // = sign + 0x6b, // Ro (\\ key, japanese) + 0x6e, // Katakana/Hiragana, second key right to spacebar, japanese + 0x6a, // Yen (macron key, japanese) + 0x6d, // Henkan, first key right to spacebar, japanese + 0x6c, // Muhenkan, key left to spacebar, japanese + 0x00, // Keyboard International6 unmapped + 0x00, // Keyboard International7 unmapped + 0x00, // Keyboard International8 unmapped + 0x00, // Keyboard International9 unmapped + 0xf0, // Hangul, korean, Kana, Mac japanese USB + 0xf1, // Hangul_Hanja, korean, Eisu, Mac japanese USB + }; + + static const size_t kKeyTableSize + = sizeof(kKeyTable) / sizeof(kKeyTable[0]); + + bool phantomState = true; + for (size_t i = 0; i < fKeyCount; i++) { + if (fCurrentKeys[i] != 1 + || fKeys[i]->UsagePage() != B_HID_USAGE_PAGE_KEYBOARD) { + phantomState = false; + break; + } + } + + if (phantomState) { + // no valid key information is present in this state and we don't + // want to overwrite our last buffer as otherwise we generate + // spurious key ups now and spurious key downs when leaving the + // phantom state again + return B_OK; + } + + static bool sysReqPressed = false; + + bool keyDown = false; + uint16 *current = fLastKeys; + uint16 *compare = fCurrentKeys; + for (int32 twice = 0; twice < 2; twice++) { + for (size_t i = 0; i < fKeyCount; i++) { + if (current[i] == 0 || (current[i] == 1 + && fKeys[i]->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD)) + continue; + + bool found = false; + for (size_t j = 0; j < fKeyCount; j++) { + if (compare[j] == current[i]) { + found = true; + break; + } + } + + if (found) + continue; + + // a change occured + uint32 key = 0; + if (fKeys[i]->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD) { + if (current[i] < kKeyTableSize) + key = kKeyTable[current[i]]; + + if (key == KEY_Pause && (modifiers & ALT_KEYS) != 0) + key = KEY_Break; + else if (key == 0xe && (modifiers & ALT_KEYS) != 0) { + key = KEY_SysRq; + sysReqPressed = keyDown; + } else if (sysReqPressed && keyDown + && current[i] >= 4 && current[i] <= 29 + && (fLastModifiers & ALT_KEYS) != 0) { + + char letter = current[i] - 4 + 'a'; + + if (debug_emergency_key_pressed(letter)) { + // we probably have lost some keys, so reset our key + // state + sysReqPressed = false; + continue; + } + } + } + + if (key == 0) { + // unmapped normal key or consumer/button key + key = fKeys[i]->UsageMinimum() + current[i]; + } + + _WriteKey(key, keyDown); + + if (keyDown) { + // repeat handling + fCurrentRepeatKey = key; + fCurrentRepeatDelay = fRepeatDelay; + } else { + // cancel the repeats if they are for this key + if (fCurrentRepeatKey == key) { + fCurrentRepeatDelay = B_INFINITE_TIMEOUT; + fCurrentRepeatKey = 0; + } + } + } + + current = fCurrentKeys; + compare = fLastKeys; + keyDown = true; + } + + memcpy(fLastKeys, fCurrentKeys, fKeyCount * sizeof(uint16)); + return B_OK; +} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp index 7c8bda2da7..e0304c3497 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp @@ -263,7 +263,7 @@ HIDDevice::Removed() status_t -HIDDevice::MaybeScheduleTransfer() +HIDDevice::MaybeScheduleTransfer(HIDReport*) { if (fRemoved) return B_ERROR; diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h index 8cd8b957f9..a19f97cb49 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h @@ -34,7 +34,7 @@ public: void Removed(); bool IsRemoved() const { return fRemoved; } - status_t MaybeScheduleTransfer(); + status_t MaybeScheduleTransfer(HIDReport*); status_t SendReport(HIDReport *report); diff --git a/src/add-ons/kernel/drivers/input/usb_hid/Jamfile b/src/add-ons/kernel/drivers/input/usb_hid/Jamfile index 63d6180946..a63ab9b9ee 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/Jamfile +++ b/src/add-ons/kernel/drivers/input/usb_hid/Jamfile @@ -1,11 +1,14 @@ SubDir HAIKU_TOP src add-ons kernel drivers input usb_hid ; -SubDirC++Flags -fno-rtti ; +SubDirC++Flags -fno-rtti -DKEYBOARD_SUPPORTS_KDL ; SubDirSysHdrs $(HAIKU_TOP) headers os drivers ; +SubDirSysHdrs $(HAIKU_TOP) src add-ons kernel drivers input hid_shared ; UsePrivateHeaders [ FDirName kernel util ] input drivers device ; UsePrivateKernelHeaders ; +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src add-ons kernel drivers input hid_shared ] ; + KernelAddon usb_hid : DeviceList.cpp Driver.cpp