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 <[email protected]> Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
@@ -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 ;
|
||||
|
||||
+1
-1
@@ -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);
|
||||
+14
@@ -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';
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
Generic device list for use in drivers.
|
||||
Copyright (C) 2008 Michael Lotz <[email protected]>
|
||||
Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include "DeviceList.h"
|
||||
#include <util/kernel_cpp.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Generic device list for use in drivers.
|
||||
Copyright (C) 2008 Michael Lotz <[email protected]>
|
||||
Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef _DEVICE_LIST_H_
|
||||
#define _DEVICE_LIST_H_
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
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_
|
||||
@@ -0,0 +1,520 @@
|
||||
/*
|
||||
* Copyright 2020, Jérôme Duval, [email protected].
|
||||
* Copyright 2008-2011 Michael Lotz <[email protected]>
|
||||
* Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
|
||||
//! Driver for I2C Human Interface Devices.
|
||||
|
||||
|
||||
#include <ACPI.h>
|
||||
#include <device_manager.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#include "DeviceList.h"
|
||||
#include "Driver.h"
|
||||
#include "HIDDevice.h"
|
||||
#include "ProtocolHandler.h"
|
||||
|
||||
#include <lock.h>
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
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
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Driver for I2C Human Interface Devices.
|
||||
Copyright (C) 2008 Michael Lotz <[email protected]>
|
||||
Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef _I2C_HID_DRIVER_H_
|
||||
#define _I2C_HID_DRIVER_H_
|
||||
|
||||
#include <Drivers.h>
|
||||
#include <KernelExport.h>
|
||||
#include <OS.h>
|
||||
#include <util/kernel_cpp.h>
|
||||
|
||||
#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_
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright 2020, Jérôme Duval, [email protected].
|
||||
* Copyright 2008-2011, Michael Lotz <[email protected]>
|
||||
* 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 <usb/USB_hid.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <new>
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2020, Jérôme Duval, [email protected].
|
||||
* Copyright 2008-2011, Michael Lotz <[email protected]>
|
||||
* Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef I2C_HID_DEVICE_H
|
||||
#define I2C_HID_DEVICE_H
|
||||
|
||||
|
||||
#include <i2c.h>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
;
|
||||
@@ -0,0 +1,732 @@
|
||||
/*
|
||||
* Copyright 2008-2011 Michael Lotz <[email protected]>
|
||||
* Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <usb/USB_hid.h>
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include <debug.h>
|
||||
|
||||
#include "Driver.h"
|
||||
#include "KeyboardProtocolHandler.h"
|
||||
|
||||
#include "HIDCollection.h"
|
||||
#include "HIDDevice.h"
|
||||
#include "HIDReport.h"
|
||||
#include "HIDReportItem.h"
|
||||
|
||||
#include <keyboard_mouse_driver.h>
|
||||
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -263,7 +263,7 @@ HIDDevice::Removed()
|
||||
|
||||
|
||||
status_t
|
||||
HIDDevice::MaybeScheduleTransfer()
|
||||
HIDDevice::MaybeScheduleTransfer(HIDReport*)
|
||||
{
|
||||
if (fRemoved)
|
||||
return B_ERROR;
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
void Removed();
|
||||
bool IsRemoved() const { return fRemoved; }
|
||||
|
||||
status_t MaybeScheduleTransfer();
|
||||
status_t MaybeScheduleTransfer(HIDReport*);
|
||||
|
||||
status_t SendReport(HIDReport *report);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user