From 3e7e86bd2628dee155f09b1c94b6c5b955a5aa37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Sat, 18 Sep 2004 23:30:05 +0000 Subject: [PATCH] Added a HID USB driver Keyboards working (missing features : leds, key repeating) Only mouse detection, not working git-svn-id: file:///srv/svn/repos/haiku/trunk/current@8999 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/drivers/input/hid/Jamfile | 17 + .../kernel/drivers/input/hid/devlist.c | 132 +++ src/add-ons/kernel/drivers/input/hid/hid.c | 973 ++++++++++++++++++ src/add-ons/kernel/drivers/input/hid/hid.h | 171 +++ .../kernel/drivers/input/hid/hidparse.c | 415 ++++++++ .../kernel/drivers/input/hid/hidparse.h | 222 ++++ 6 files changed, 1930 insertions(+) create mode 100644 src/add-ons/kernel/drivers/input/hid/Jamfile create mode 100644 src/add-ons/kernel/drivers/input/hid/devlist.c create mode 100644 src/add-ons/kernel/drivers/input/hid/hid.c create mode 100644 src/add-ons/kernel/drivers/input/hid/hid.h create mode 100644 src/add-ons/kernel/drivers/input/hid/hidparse.c create mode 100644 src/add-ons/kernel/drivers/input/hid/hidparse.h diff --git a/src/add-ons/kernel/drivers/input/hid/Jamfile b/src/add-ons/kernel/drivers/input/hid/Jamfile new file mode 100644 index 0000000000..ac53b97d0f --- /dev/null +++ b/src/add-ons/kernel/drivers/input/hid/Jamfile @@ -0,0 +1,17 @@ +SubDir OBOS_TOP src add-ons kernel drivers input hid ; + +UsePrivateHeaders input ; + +R5KernelAddon hid : kernel drivers bin : + hid.c + hidparse.c + devlist.c +; + +# Link to kernel/drivers/dev/input/ +{ + local dir = [ FDirName $(OBOS_ADDON_DIR) kernel drivers dev input ] ; + local instDriver = hid ; + MakeLocate $(instDriver) : $(dir) ; + RelSymLink $(instDriver) : hid ; +} diff --git a/src/add-ons/kernel/drivers/input/hid/devlist.c b/src/add-ons/kernel/drivers/input/hid/devlist.c new file mode 100644 index 0000000000..6f1df42435 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/hid/devlist.c @@ -0,0 +1,132 @@ +/*****************************************************************************/ +// HID usb driver +// Written by Jérôme Duval +// +// devlist.c +// +// Copyright (c) 2004 Haiku Project +// +// Some portions of code are copyrighted by +// USB Joystick driver for BeOS R5 +// Copyright 2000 (C) ITO, Takayuki +// All rights reserved +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +/*****************************************************************************/ + +#include +#include +#include "hid.h" + +sem_id my_device_list_lock = -1; +bool my_device_list_changed = true; /* added or removed */ + +static my_device_info *my_device_list = NULL; +static int my_device_count = 0; + +void add_device_info (my_device_info *my_dev) +{ + assert (my_dev != NULL); + acquire_sem (my_device_list_lock); + my_dev->next = my_device_list; + my_device_list = my_dev; + my_device_count++; + my_device_list_changed = true; + release_sem (my_device_list_lock); +} + +void remove_device_info (my_device_info *my_dev) +{ + assert (my_dev != NULL); + acquire_sem (my_device_list_lock); + if (my_device_list == my_dev) + my_device_list = my_dev->next; + else + { + my_device_info *d; + for (d = my_device_list; d != NULL; d = d->next) + { + if (d->next == my_dev) + { + d->next = my_dev->next; + --my_device_count; + my_device_list_changed = true; + break; + } + } + assert (d != NULL); + } + release_sem (my_device_list_lock); +} + +my_device_info *search_device_info (const char* name) +{ + my_device_info *my_dev; + + acquire_sem (my_device_list_lock); + for (my_dev = my_device_list; my_dev != NULL; my_dev = my_dev->next) + { + if (strcmp(my_dev->name, name) == 0) + break; + } + release_sem (my_device_list_lock); + return my_dev; +} + +/* + device names +*/ + +/* dynamically generated */ +char **my_device_names = NULL; + +void alloc_device_names (void) +{ + assert (my_device_names == NULL); + my_device_names = malloc (sizeof (char *) * (my_device_count + 1)); +} + +void free_device_names (void) +{ + if (my_device_names != NULL) + { + int i; + for (i = 0; my_device_names [i] != NULL; i++) + free (my_device_names [i]); + free (my_device_names); + my_device_names = NULL; + } +} + +void rebuild_device_names (void) +{ + int i; + my_device_info *my_dev; + + assert (my_device_names != NULL); + acquire_sem (my_device_list_lock); + for (i = 0, my_dev = my_device_list; my_dev != NULL; my_dev = my_dev->next) + { + my_device_names [i++] = strdup(my_dev->name); + DPRINTF_INFO ((MY_ID "publishing %s\n", my_dev->name)); + } + my_device_names [i] = NULL; + release_sem (my_device_list_lock); +} + diff --git a/src/add-ons/kernel/drivers/input/hid/hid.c b/src/add-ons/kernel/drivers/input/hid/hid.c new file mode 100644 index 0000000000..5c2f905bc3 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/hid/hid.c @@ -0,0 +1,973 @@ +/*****************************************************************************/ +// HID usb driver +// Written by Jérôme Duval +// +// hid.c +// +// Copyright (c) 2004 Haiku Project +// +// Some portions of code are copyrighted by +// USB Joystick driver for BeOS R5 +// Copyright 2000 (C) ITO, Takayuki +// All rights reserved +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +/*****************************************************************************/ +#include +#include +#include +#include "hid.h" +#include "kb_mouse_driver.h" + +static int keyboard_device_number = 0; +static int mouse_device_number = 0; +const char *keyboard_base_name = "input/keyboard/usb/"; +const char *mouse_base_name = "input/mouse/usb/"; + +my_device_info * +create_device(const usb_device *dev, const usb_interface_info *ii, + bool is_keyboard) +{ + my_device_info *my_dev = NULL; + int number; + area_id area; + sem_id sem; + char area_name [32]; + const char *base_name; + + assert (usb != NULL && dev != NULL); + + if (is_keyboard) { + number = keyboard_device_number++; + base_name = keyboard_base_name; + } else { + number = mouse_device_number++; + base_name = mouse_base_name; + } + + my_dev = malloc (sizeof (my_device_info)); + if (my_dev == NULL) + return NULL; + + my_dev->sem_cb = sem = create_sem (0, DRIVER_NAME "_cb"); + if (sem < 0) { + DPRINTF_ERR ((MY_ID "create_sem() failed %d\n", (int) sem)); + free (my_dev); + return NULL; + } + + my_dev->sem_lock = sem = create_sem (1, DRIVER_NAME "_lock"); + if (sem < 0) { + DPRINTF_ERR ((MY_ID "create_sem() failed %d\n", (int) sem)); + delete_sem (my_dev->sem_cb); + free (my_dev); + return NULL; + } + + sprintf (area_name, DRIVER_NAME "_buffer%d", number); + my_dev->buffer_area = area = create_area (area_name, + (void **) &my_dev->buffer, B_ANY_KERNEL_ADDRESS, + B_PAGE_SIZE, B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA); + if (area < 0) { + DPRINTF_ERR ((MY_ID "create_area() failed %d\n", (int) area)); + delete_sem (my_dev->sem_cb); + delete_sem (my_dev->sem_lock); + free (my_dev); + return NULL; + } + + sprintf(my_dev->name, "%s%d", base_name, number); + my_dev->dev = dev; + my_dev->open = 0; + my_dev->open_fds = NULL; + my_dev->active = true; + my_dev->insns = NULL; + my_dev->num_insns = 0; + my_dev->flags = 0; + my_dev->cbuf = cbuf_init(384); + my_dev->is_keyboard = is_keyboard; + + return my_dev; +} + +void +remove_device (my_device_info *my_dev) +{ + assert (my_dev != NULL); + if (my_dev->cbuf != NULL) { + cbuf_delete(my_dev->cbuf); + my_dev->cbuf = NULL; + } + + delete_area (my_dev->buffer_area); + delete_sem (my_dev->sem_cb); + delete_sem (my_dev->sem_lock); + free (my_dev); +} + + +/* gameport driver cookie (per open) */ + +typedef struct driver_cookie +{ + struct driver_cookie *next; + + my_device_info *my_dev; + bool enhanced; + +} driver_cookie; + +/* NB global variables are valid only while driver is loaded */ + +_EXPORT int32 api_version = B_CUR_DRIVER_API_VERSION; + +const char *hid_driver_name = "hid"; + +usb_module_info *usb; + +const uint32 modifier_table[] = { + KEY_ControlL, + KEY_ShiftL, + KEY_AltL, + KEY_WinL, + KEY_ControlR, + KEY_ShiftR, + KEY_AltR, + KEY_WinR +}; + +const uint32 key_table[] = { + 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, // ] + 0x00, // unmapped + 0x33, // \ + 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, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + 0x00, // unmapped + +}; + +static void +interpret_kb_buffer(my_device_info *my_dev) +{ + raw_key_info info; + uint8 modifiers = ((uint8*)my_dev->buffer)[0]; + uint8 bits = my_dev->last_buffer[0] ^ modifiers; + uint32 i,j; + + info.timestamp = my_dev->timestamp; + + if (bits) + for (j=0; bits; j++,bits>>=1) + if (bits & 1) { + info.be_keycode = modifier_table[j]; + info.is_keydown = (modifiers >> j) & 1; + cbuf_putn(my_dev->cbuf, &info, sizeof(info)); + release_sem_etc(my_dev->sem_cb, 1, B_DO_NOT_RESCHEDULE); + } + + for (i=2; itotal_report_size; i++) + if (((uint8*)my_dev->buffer)[i] && ((uint8*)my_dev->last_buffer)[i]!=0x1) { + bool found = false; + for (j=2; jtotal_report_size; j++) + if (((uint8*)my_dev->last_buffer)[j] + && ((uint8*)my_dev->last_buffer)[j] == ((uint8*)my_dev->buffer)[i]) { + found = true; + break; + } + + if (!found) { + info.be_keycode = key_table[((uint8*)my_dev->buffer)[i]]; + if (info.be_keycode == KEY_Pause && modifiers & 1) + info.be_keycode = KEY_Break; + if (info.be_keycode == 0xe && modifiers & 1) + info.be_keycode = KEY_SysRq; + info.is_keydown = 1; + cbuf_putn(my_dev->cbuf, &info, sizeof(info)); + release_sem_etc(my_dev->sem_cb, 1, B_DO_NOT_RESCHEDULE); + } + } else + break; + + for (i=2; itotal_report_size; i++) + if (((uint8*)my_dev->last_buffer)[i] && ((uint8*)my_dev->last_buffer)[i]!=0x1) { + bool found = false; + for (j=2; jtotal_report_size; j++) + if (((uint8*)my_dev->buffer)[j] + && ((uint8*)my_dev->buffer)[j] == ((uint8*)my_dev->last_buffer)[i]) { + found = true; + break; + } + + if (!found) { + info.be_keycode = key_table[((uint8*)my_dev->last_buffer)[i]]; + if (info.be_keycode == KEY_Pause && modifiers & 1) + info.be_keycode = KEY_Break; + if (info.be_keycode == 0xe && modifiers & 1) + info.be_keycode = KEY_SysRq; + info.is_keydown = 0; + cbuf_putn(my_dev->cbuf, &info, sizeof(info)); + release_sem_etc(my_dev->sem_cb, 1, B_DO_NOT_RESCHEDULE); + } + } else + break; + +} + +/* + callback: got a report, issue next request +*/ + +static void +usb_callback(void *cookie, uint32 status, + void *data, uint32 actual_len) +{ + status_t st; + my_device_info *my_dev = cookie; + + assert (cookie != NULL); + + acquire_sem (my_dev->sem_lock); + my_dev->actual_length = actual_len; + my_dev->bus_status = status; /* B_USB_STATUS_* */ + if (status != B_USB_STATUS_SUCCESS) { + /* request failed */ + release_sem (my_dev->sem_lock); + DPRINTF_ERR ((MY_ID "bus status %d\n", (int)status)); + if (status == B_USB_STATUS_IRP_CANCELLED_BY_REQUEST) { + /* cancelled: device is unplugged */ + return; + } +#if 1 + st = usb->clear_feature (my_dev->ept->handle, USB_FEATURE_ENDPOINT_HALT); + if (st != B_OK) + DPRINTF_ERR ((MY_ID "clear_feature() error %d\n", (int)st)); +#endif + } else { + /* got a report */ +#if 0 + uint32 i; + char linbuf [256]; + uint8 *buffer = my_dev->buffer; + + for (i = 0; i < my_dev->total_report_size; i++) + sprintf (&linbuf[i*3], "%02X ", buffer [i]); + DPRINTF_INFO ((MY_ID "input report: %s\n", linbuf)); +#endif + my_dev->timestamp = system_time (); + + if (my_dev->is_keyboard) + interpret_kb_buffer(my_dev); + + memcpy(my_dev->last_buffer, my_dev->buffer, my_dev->total_report_size); + + release_sem (my_dev->sem_lock); + } + + /* issue next request */ + + st = usb->queue_interrupt (my_dev->ept->handle, my_dev->buffer, + my_dev->total_report_size, usb_callback, my_dev); + if (st != B_OK) { + /* XXX probably endpoint stall */ + DPRINTF_ERR ((MY_ID "queue_interrupt() error %d\n", (int)st)); + } +} + +/* + USB specific device hooks +*/ + +static status_t +kb_device_added(const usb_device *dev, void **cookie) +{ + my_device_info *my_dev; + const usb_device_descriptor *dev_desc; + const usb_configuration_info *conf; + const usb_interface_info *intf; + status_t st; + usb_hid_descriptor *hid_desc; + uint8 *rep_desc = NULL; + size_t desc_len, actual; + decomp_item *items; + size_t num_items; + int fd, report_id; + uint16 ifno; + bool is_keyboard; + + assert (dev != NULL && cookie != NULL); + DPRINTF_INFO ((MY_ID "device_added()\n")); + + dev_desc = usb->get_device_descriptor (dev); + DPRINTF_INFO ((MY_ID "vendor ID 0x%04X, product ID 0x%04X\n", + dev_desc->vendor_id, dev_desc->product_id)); + + /* check interface class */ + + if ((conf = usb->get_nth_configuration(dev, DEFAULT_CONFIGURATION)) == NULL) { + DPRINTF_ERR ((MY_ID "cannot get default configuration\n")); + return B_ERROR; + } + + for (ifno = 0; ifno < conf->interface_count; ifno++) { + /* This is C; I can use "class" :-> */ + int class, subclass, protocol; + + intf = conf->interface [ifno].active; + class = intf->descr->interface_class; + subclass = intf->descr->interface_subclass; + protocol = intf->descr->interface_protocol; + DPRINTF_INFO ((MY_ID "interface %d: class %d, subclass %d, protocol %d\n", + ifno, class, subclass, protocol)); + if (class == USB_CLASS_HID && subclass == 1) + break; + } + + if (ifno >= conf->interface_count) { + DPRINTF_INFO ((MY_ID "Boot HID interface not found\n")); + return B_ERROR; + } + + /* read HID descriptor */ + + desc_len = sizeof (usb_hid_descriptor); + hid_desc = malloc (desc_len); + st = usb->send_request (dev, + USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD, + USB_REQUEST_GET_DESCRIPTOR, + USB_DESCRIPTOR_HID << 8, ifno, desc_len, + hid_desc, desc_len, &desc_len); + DPRINTF_INFO ((MY_ID "get_hid_desc: st=%d, len=%d\n", + (int) st, (int)desc_len)); + if (st != B_OK) + desc_len = 256; /* XXX */ + + /* read report descriptor */ + + desc_len = hid_desc->descriptor_info [0].descriptor_length; + free (hid_desc); + rep_desc = malloc (desc_len); + assert (rep_desc != NULL); + st = usb->send_request (dev, + USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD, + USB_REQUEST_GET_DESCRIPTOR, + USB_DESCRIPTOR_HID_REPORT << 8, ifno, desc_len, + rep_desc, desc_len, &desc_len); + DPRINTF_INFO ((MY_ID "get_hid_rep_desc: st=%d, len=%d\n", + (int) st, (int)desc_len)); + if (st != B_OK) { + free (rep_desc); + return B_ERROR; + } + + /* save report descriptor for troubleshooting */ + + fd = open ("/tmp/rep_desc.bin", O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd >= 0) { + write (fd, rep_desc, desc_len); + close (fd); + } + + /* XXX check application type */ + /* Generic Desktop : Keyboard or Mouse */ + + if (memcmp (rep_desc, "\x05\x01\x09\x06", 4) != 0 && + memcmp (rep_desc, "\x05\x01\x09\x02", 4) != 0) { + DPRINTF_INFO ((MY_ID "not a keyboard or a mouse %08lx\n", *(uint32*)rep_desc)); + free (rep_desc); + return B_ERROR; + } + + /* configuration */ + + if ((st = usb->set_configuration (dev, conf)) != B_OK) { + DPRINTF_ERR ((MY_ID "set_configuration() failed %d\n", (int)st)); + free (rep_desc); + return B_ERROR; + } + + is_keyboard = memcmp (rep_desc, "\x05\x01\x09\x06", 4) == 0; + + if ((my_dev = create_device (dev, intf, is_keyboard)) == NULL) { + free (rep_desc); + return B_ERROR; + } + + /* decompose report descriptor */ + + num_items = desc_len; /* XXX */ + items = malloc (sizeof (decomp_item) * num_items); + assert (items != NULL); + decompose_report_descriptor (rep_desc, desc_len, items, &num_items); + free (rep_desc); + + /* parse report descriptor */ + + my_dev->num_insns = num_items; /* XXX */ + my_dev->insns = malloc (sizeof (report_insn) * my_dev->num_insns); + assert (my_dev->insns != NULL); + parse_report_descriptor (items, num_items, my_dev->insns, + &my_dev->num_insns, &my_dev->total_report_size, &report_id); + free (items); + realloc (my_dev->insns, sizeof (report_insn) * my_dev->num_insns); + DPRINTF_INFO ((MY_ID "%d items, %d insns, %d bytes\n", + (int)num_items, (int)my_dev->num_insns, (int)my_dev->total_report_size)); + + /* count axes, hats and buttons */ + + count_controls (my_dev->insns, my_dev->num_insns, + &my_dev->num_axes, &my_dev->num_hats, &my_dev->num_buttons); + DPRINTF_INFO ((MY_ID "%d axes, %d hats, %d buttons\n", + my_dev->num_axes, my_dev->num_hats, my_dev->num_buttons)); + + /* get initial state */ + + + st = usb->send_request (dev, + USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS, + USB_REQUEST_HID_GET_REPORT, + 0x0100 | report_id, ifno, my_dev->total_report_size, + my_dev->buffer, my_dev->total_report_size, &actual); + if (st != B_OK) + DPRINTF_ERR ((MY_ID "Get_Report failed %d\n", (int)st)); + my_dev->timestamp = system_time (); + + DPRINTF_INFO ((MY_ID "%08lx %08lx %08lx\n", *(((uint32*)my_dev->buffer)), *(((uint32*)my_dev->buffer)+1), *(((uint32*)my_dev->buffer)+2))); + + /* issue interrupt transfer */ + + my_dev->ept = &intf->endpoint [0]; /* interrupt IN */ + st = usb->queue_interrupt (my_dev->ept->handle, my_dev->buffer, + my_dev->total_report_size, usb_callback, my_dev); + if (st != B_OK) { + DPRINTF_ERR ((MY_ID "queue_interrupt() error %d\n", (int)st)); + return B_ERROR; + } + + /* create a port */ + + add_device_info (my_dev); + + *cookie = my_dev; + DPRINTF_INFO ((MY_ID "added %s\n", my_dev->name)); + + return B_OK; +} + +static status_t +kb_device_removed (void *cookie) +{ + my_device_info *my_dev = cookie; + + assert (cookie != NULL); + + DPRINTF_INFO ((MY_ID "device_removed(%s)\n", my_dev->name)); + usb->cancel_queued_transfers (my_dev->ept->handle); + remove_device_info (my_dev); + if (my_dev->open == 0) { + if (my_dev->insns != NULL) + free (my_dev->insns); + remove_device (my_dev); + } else { + DPRINTF_INFO ((MY_ID "%s still open\n", my_dev->name)); + my_dev->active = false; + } + return B_OK; +} + +static usb_notify_hooks my_notify_hooks = +{ + kb_device_added, kb_device_removed +}; + +#define SUPPORTED_DEVICES 1 +usb_support_descriptor my_supported_devices [SUPPORTED_DEVICES] = +{ + { USB_CLASS_HID, 0, 0, 0, 0 }, +}; + + +/* ---------- + my_device_open - handle open() calls +----- */ + +static status_t +my_device_open(const char *name, uint32 flags, + driver_cookie **out_cookie) +{ + driver_cookie *cookie; + my_device_info *my_dev; + + assert (name != NULL); + assert (out_cookie != NULL); + DPRINTF_INFO ((MY_ID "open(%s)\n", name)); + + if ((my_dev = search_device_info (name)) == NULL) + return B_ENTRY_NOT_FOUND; + if ((cookie = malloc (sizeof (driver_cookie))) == NULL) + return B_NO_MEMORY; + + acquire_sem (my_dev->sem_lock); + cookie->enhanced = false; + cookie->my_dev = my_dev; + cookie->next = my_dev->open_fds; + my_dev->open_fds = cookie; + my_dev->open++; + release_sem (my_dev->sem_lock); + + *out_cookie = cookie; + DPRINTF_INFO ((MY_ID "device %s open (%d)\n", name, my_dev->open)); + return B_OK; +} + + +/* ---------- + my_device_read - handle read() calls +----- */ + +static status_t +my_device_read(driver_cookie *cookie, off_t position, + void *buf, size_t *num_bytes) +{ + return B_ERROR; +} + + +/* ---------- + my_device_write - handle write() calls +----- */ + +static status_t +my_device_write(driver_cookie *cookie, off_t position, + const void *buf, size_t *num_bytes) +{ + return B_ERROR; +} + +/* ---------- + my_device_control - handle ioctl calls +----- */ + +static status_t +my_device_control(driver_cookie *cookie, uint32 op, + void *arg, size_t len) +{ + status_t err = B_ERROR; + my_device_info *my_dev; + + assert (cookie != NULL); + my_dev = cookie->my_dev; + assert (my_dev != NULL); + DPRINTF_INFO ((MY_ID "ioctl(0x%x)\n", (int)op)); + + if (!my_dev->active) + return B_ERROR; /* already unplugged */ + + switch (op) { + case 0x270f: + err = acquire_sem_etc(my_dev->sem_cb, 1, B_CAN_INTERRUPT, 0LL); + if (err != B_OK) + return err; + cbuf_getn(my_dev->cbuf, arg, sizeof(raw_key_info)); + return err; + break; + + case 0x2711: + + break; + + default: + /* not implemented */ + break; + } + + return err; +} + + +/* ---------- + my_device_close - handle close() calls +----- */ + +static status_t +my_device_close(driver_cookie *cookie) +{ + my_device_info *my_dev; + + assert (cookie != NULL && cookie->my_dev != NULL); + my_dev = cookie->my_dev; + DPRINTF_INFO ((MY_ID "close(%s)\n", my_dev->name)); + + /* detach the cookie from list */ + + acquire_sem (my_dev->sem_lock); + if (my_dev->open_fds == cookie) + my_dev->open_fds = cookie->next; + else { + driver_cookie *p; + for (p = my_dev->open_fds; p != NULL; p = p->next) { + if (p->next == cookie) { + p->next = cookie->next; + break; + } + } + } + --my_dev->open; + release_sem (my_dev->sem_lock); + + return B_OK; +} + + +/* ----- + my_device_free - called after the last device is closed, and after + all i/o is complete. +----- */ +static status_t +my_device_free(driver_cookie *cookie) +{ + my_device_info *my_dev; + + assert (cookie != NULL && cookie->my_dev != NULL); + my_dev = cookie->my_dev; + DPRINTF_INFO ((MY_ID "free(%s)\n", my_dev->name)); + + free (cookie); + if (my_dev->open > 0) + DPRINTF_INFO ((MY_ID "%d opens left\n", my_dev->open)); + else if (!my_dev->active) { + DPRINTF_INFO ((MY_ID "removed %s\n", my_dev->name)); + if (my_dev->insns != NULL) + free (my_dev->insns); + remove_device (my_dev); + } + + return B_OK; +} + + +/* ----- + function pointers for the device hooks entry points +----- */ + +static device_hooks my_device_hooks = { + (device_open_hook) my_device_open, + (device_close_hook) my_device_close, + (device_free_hook) my_device_free, + (device_control_hook) my_device_control, + (device_read_hook) my_device_read, + (device_write_hook) my_device_write, + NULL, NULL, NULL, NULL +}; + + +/* ---------- + init_hardware - called once the first time the driver is loaded +----- */ +_EXPORT status_t +init_hardware (void) +{ + DPRINTF_INFO ((MY_ID "init_hardware() " __DATE__ " " __TIME__ "\n")); + return B_OK; +} + +/* ---------- + init_driver - optional function - called every time the driver + is loaded. +----- */ +_EXPORT status_t +init_driver (void) +{ + + DPRINTF_INFO ((MY_ID "init_driver() " __DATE__ " " __TIME__ "\n")); + + if (get_module (B_USB_MODULE_NAME, (module_info **) &usb) != B_OK) + return B_ERROR; + + if ((my_device_list_lock = create_sem (1, "dev_list_lock")) < 0) { + put_module (B_USB_MODULE_NAME); + return my_device_list_lock; /* error code */ + } + + usb->register_driver (hid_driver_name, my_supported_devices, + SUPPORTED_DEVICES, NULL); + usb->install_notify (hid_driver_name, &my_notify_hooks); + DPRINTF_INFO ((MY_ID "init_driver() OK\n")); + + return B_OK; +} + + +/* ---------- + uninit_driver - optional function - called every time the driver + is unloaded +----- */ +_EXPORT void +uninit_driver (void) +{ + DPRINTF_INFO ((MY_ID "uninit_driver()\n")); + usb->uninstall_notify (hid_driver_name); + + delete_sem (my_device_list_lock); + put_module (B_USB_MODULE_NAME); + free_device_names (); +} + +/* + publish_devices + device names are generated dynamically +*/ + +_EXPORT const char ** +publish_devices (void) +{ + DPRINTF_INFO ((MY_ID "publish_devices()\n")); + + if (my_device_list_changed) { + free_device_names (); + alloc_device_names (); + if (my_device_names != NULL) + rebuild_device_names (); + my_device_list_changed = false; + } + assert (my_device_names != NULL); + return (const char **) my_device_names; +} + +/* ---------- + find_device - return ptr to device hooks structure for a + given device name +----- */ + +_EXPORT device_hooks * +find_device(const char *name) +{ + assert (name != NULL); + DPRINTF_INFO ((MY_ID "find_device(%s)\n", name)); + if (search_device_info(name) == NULL) + return NULL; + return &my_device_hooks; +} + diff --git a/src/add-ons/kernel/drivers/input/hid/hid.h b/src/add-ons/kernel/drivers/input/hid/hid.h new file mode 100644 index 0000000000..21de56a355 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/hid/hid.h @@ -0,0 +1,171 @@ +/*****************************************************************************/ +// HID usb driver +// Written by Jérôme Duval +// +// hid.h +// +// Copyright (c) 2004 Haiku Project +// +// Some portions of code are copyrighted by +// USB Joystick driver for BeOS R5 +// Copyright 2000 (C) ITO, Takayuki +// All rights reserved +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +/*****************************************************************************/ + +#include +#include +#include "hidparse.h" + +#if DEBUG + #define DPRINTF_INFO(x) dprintf x + #define DPRINTF_ERR(x) dprintf x +#else + #define DPRINTF_INFO(x) + #define DPRINTF_ERR(x) dprintf x +#endif + + +/* Undocumented kernel cbuf_* functions */ +struct cbuffer_t; +typedef struct cbuffer_t cbuffer; + +size_t cbuf_getn_no_lock(cbuffer *, char *, size_t); +size_t cbuf_putn_no_lock(cbuffer *, char *, size_t); +cbuffer *cbuf_init(size_t size); +void cbuf_delete(cbuffer *buffer); +char cbuf_get(cbuffer *); +bool cbuf_mt(cbuffer *); +bool cbuf_full(cbuffer *); +status_t cbuf_put(cbuffer *, char); +status_t cbuf_unput(cbuffer *); +void cbuf_flush(cbuffer *); +size_t cbuf_size(cbuffer *); +size_t cbuf_avail(cbuffer *); +size_t cbuf_free(cbuffer *); +size_t cbuf_putn(cbuffer *, void *, size_t num_bytes); +size_t cbuf_getn(cbuffer *, void *, size_t num_bytes); +cpu_status cbuf_lock(cbuffer *); +void cbuf_unlock(cbuffer *, cpu_status); + +/* HID class-specific definitions */ + +#define USB_CLASS_HID 3 + +#define USB_DESCRIPTOR_HID 0x21 +#define USB_DESCRIPTOR_HID_REPORT 0x22 + +#define USB_REQUEST_HID_GET_REPORT 0x01 + +typedef struct +{ + uint8 length; + uint8 descriptor_type; + uint16 hid_version; + uint8 country_code; + uint8 num_descriptors; + struct + { + uint8 descriptor_type; + uint16 descriptor_length; + } _PACKED descriptor_info [1]; +} _PACKED usb_hid_descriptor; + +/* driver specific definitions */ + +#define DRIVER_NAME "hid" + +#define MY_ID "\033[34m" DRIVER_NAME ":\033[m " +#define MY_ERR "\033[31merror:\033[m " +#define MY_WARN "\033[31mwarning:\033[m " +#define assert(x) \ + ((x) ? 0 : dprintf (MY_ID "assertion failed at " __FILE__ ", line %d\n", __LINE__)) + +/* 0-origin */ +#define DEFAULT_CONFIGURATION 0 + +#define BUF_SIZ B_PAGE_SIZE + +struct driver_cookie; + +typedef struct my_device_info +{ + /* list structure */ + struct my_device_info *next; + + /* maintain device */ + sem_id sem_cb; + sem_id sem_lock; + area_id buffer_area; + void *buffer; + + uint8 last_buffer[32]; + const usb_device *dev; + char name[30]; + + cbuffer *cbuf; + + bool active; + int open; + struct driver_cookie *open_fds; + + /* workarea for transfer */ + int usbd_status, bus_status, cmd_status; + int actual_length; + const usb_endpoint_info *ept; + + report_insn *insns; + size_t num_insns; + size_t total_report_size; + int num_buttons, num_axes, num_hats; + bigtime_t timestamp; + uint flags; + bool is_keyboard; +} my_device_info; + +/* driver.c */ + +extern usb_module_info *usb; +extern const char *my_driver_name; +extern const char *keyboard_base_name; +extern const char *mouse_base_name; + +/* devmgmt.c */ + +my_device_info * +create_device (const usb_device *dev, const usb_interface_info *ii, bool is_keyboard); + +void +remove_device (my_device_info *my_dev); + +/* devlist.c */ + +extern sem_id my_device_list_lock; +extern bool my_device_list_changed; + +void add_device_info (my_device_info *my_dev); +void remove_device_info (my_device_info *my_dev); +my_device_info *search_device_info (const char *name); + +extern char **my_device_names; + +void alloc_device_names (void); +void free_device_names (void); +void rebuild_device_names (void); diff --git a/src/add-ons/kernel/drivers/input/hid/hidparse.c b/src/add-ons/kernel/drivers/input/hid/hidparse.c new file mode 100644 index 0000000000..e5563becbb --- /dev/null +++ b/src/add-ons/kernel/drivers/input/hid/hidparse.c @@ -0,0 +1,415 @@ +/*****************************************************************************/ +// HID usb driver +// Written by Jérôme Duval +// +// hidparse.c +// +// Copyright (c) 2004 Haiku Project +// +// Some portions of code are copyrighted by +// USB Joystick driver for BeOS R5 +// Copyright 2000 (C) ITO, Takayuki +// All rights reserved +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +/*****************************************************************************/ + + +#include +#include "hidparse.h" + +/* + decompose report descriptor into array of uniform structure +*/ + +int +decompose_report_descriptor(const unsigned char *desc, + size_t desc_len, decomp_item *items, size_t *num_items) +{ + unsigned int desc_idx, item_idx; + static int item_size [4] = { 0, 1, 2, 4 }; + + if (desc == NULL || items == NULL || num_items == NULL || + desc_len <= 0 || *num_items <= 0) + return FAILURE; + + /* process one item per loop */ + for (desc_idx = item_idx = 0; + desc_idx < desc_len && item_idx < *num_items; item_idx++) { + item_prefix pfx; + int size; + unsigned short value16; + unsigned char value8; + + decomp_item *item = &items [item_idx]; + item->offset = desc_idx; + item->prefix = pfx.i = desc [desc_idx++]; + item->type = pfx.b.bType; + + if (pfx.i == LONG_ITEM_PREFIX) { + /* XXX no long item support */ + item->size = desc [desc_idx++]; + item->tag = desc [desc_idx++]; + item->value = item->signed_value = 0; + desc_idx += item->size; + continue; + } + + item->size = size = item_size [pfx.b.bSize]; + item->tag = pfx.b.bTag; + + switch (size) { + case 4: + item->signed_value = + item->value = + ((unsigned long) desc [desc_idx + 3] << 24) | + ((unsigned long) desc [desc_idx + 2] << 16) | + ( desc [desc_idx + 1] << 8) | + desc [desc_idx ]; + break; + case 2: + value16 = (desc [desc_idx + 1] << 8) | desc [desc_idx]; + item->value = value16; + item->signed_value = (signed short) value16; + break; + case 1: + value8 = desc [desc_idx]; + item->value = value8; + item->signed_value = (signed char) value8; + break; + case 0: + item->value = item->signed_value = 0; + break; + } + + desc_idx += size; + } + + *num_items = item_idx; + return SUCCESS; +} + +/* + parse decomposed items and generate "instructions" to interpret a report + + limitations: + only frequently used items are supported + only first collection is used + report ID is abandoned + output/feature reports are not implemented +*/ + +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + +int +parse_report_descriptor(const decomp_item *items, size_t num_items, + report_insn *insns, size_t *num_insns, + size_t *total_report_size, int *first_report_id) +{ + #define ITEM_STACK_SIZE 32 + + /* global items */ + typedef struct global_items { + unsigned int usage_page; + BOOL is_log_signed, is_phy_signed; + unsigned long log_max, log_min, + phy_max, phy_min; + signed long signed_log_max, signed_log_min, + signed_phy_max, signed_phy_min; + int report_size, report_id; + unsigned int report_count; + } global_items; + + global_items item_state, item_stack [ITEM_STACK_SIZE]; + int item_sp = ITEM_STACK_SIZE; + + /* local items */ + unsigned int usage_id = 0, usage_min = 0, usage_max; + unsigned int insn_idx_bias = 0, i, insn_idx, item_idx; + + int bit_pos = 0, byte_idx = 0, collection = 0; + + if (items == NULL || insns == NULL || num_insns == NULL || + *num_insns <= 0 || num_items <= 0) + return FAILURE; + + memset (&item_state, 0, sizeof (item_state)); + + /* interpret one item per loop */ + for (insn_idx = item_idx = 0; + item_idx < num_items && insn_idx < *num_insns; + item_idx++) + { + const decomp_item *item = &items [item_idx]; + report_insn *insn = &insns [insn_idx]; + int tag = item->tag; + switch (item->type) { + case ITEM_TYPE_MAIN: + switch (tag) { + case ITEM_TAG_INPUT: + for (i = 0; i < item_state.report_count && insn_idx < *num_insns; i++) { + if ((item->value & 3) == 2) { /* data,variable */ + if (insn_idx_bias == 0) { + insn->usage_page = item_state.usage_page; + insn->usage_id = usage_id; + } else + --insn_idx_bias; + + insn->attributes = item->value; + + insn->is_log_signed = item_state.is_log_signed; + if (item_state.is_log_signed) { + insn->signed_log_min = item_state.signed_log_min; + insn->signed_log_max = item_state.signed_log_max; + } else { + insn->log_min = item_state.log_min; + insn->log_max = item_state.log_max; + } + + insn->is_phy_signed = item_state.is_phy_signed; + if (item_state.is_phy_signed) { + insn->signed_phy_min = item_state.signed_phy_min; + insn->signed_phy_max = item_state.signed_phy_max; + } else { + insn->phy_min = item_state.phy_min; + insn->phy_max = item_state.phy_max; + } + + insn->byte_idx = byte_idx; + insn->bit_pos = bit_pos; + insn->num_bits = item_state.report_size; + + insn++; + insn_idx++; + } + else + insn_idx_bias = 0; /* XXX */ + + /* to next bit-field */ + bit_pos += item_state.report_size; + if (bit_pos >= 8) { + byte_idx += bit_pos / 8; + bit_pos = bit_pos % 8; + } + } + break; + + case ITEM_TAG_OUTPUT: + /* XXX similar to input */ + break; + case ITEM_TAG_COLLECTION: + collection++; + insn_idx_bias = 0; + break; + case ITEM_TAG_FEATURE: + /* XXX similar to input */ + break; + case ITEM_TAG_END_COLLECTION: + if (--collection == 0) + /* exit at end of first collection */ + goto end; + break; + default: + break; + } + break; + + case ITEM_TYPE_GLOBAL: + switch (tag) { + case ITEM_TAG_USAGE_PAGE: + item_state.usage_page = item->value; + break; + case ITEM_TAG_LOGICAL_MINIMUM: + item_state.log_min = item->value; + item_state.signed_log_min = item->signed_value; + /* FALLTHROUGH */ + case ITEM_TAG_PHYSICAL_MINIMUM: + item_state.phy_min = item->value; + item_state.signed_phy_min = item->signed_value; + break; + case ITEM_TAG_LOGICAL_MAXIMUM: + item_state.log_max = item->value; + item_state.signed_log_max = item->signed_value; + item_state.is_log_signed = item_state.log_min > item_state.log_max; + /* FALLTHROUGH */ + case ITEM_TAG_PHYSICAL_MAXIMUM: + item_state.phy_max = item->value; + item_state.signed_phy_max = item->signed_value; + item_state.is_phy_signed = item_state.phy_min > item_state.phy_max; + break; + case ITEM_TAG_UNIT_EXPONENT: + break; + case ITEM_TAG_UNIT: + break; + case ITEM_TAG_REPORT_SIZE: + item_state.report_size = item->value; + break; + case ITEM_TAG_REPORT_ID: + /* FIXME */ + if (item_state.report_id == 0) { + item_state.report_id = item->value; + byte_idx++; + } + else + goto end; + break; + case ITEM_TAG_REPORT_COUNT: + item_state.report_count = item->value; + break; + case ITEM_TAG_PUSH: + if (item_sp > 0) + item_stack [--item_sp] = item_state; + break; + case ITEM_TAG_POP: + if (item_sp < ITEM_STACK_SIZE) + item_state = item_stack [item_sp++]; + break; + } + break; + + case ITEM_TYPE_LOCAL: + switch (tag) + { + case ITEM_TAG_USAGE: + /* can be sequentially used */ + /* XXX should be limited to "practical" usages */ + /* XXX should support extended (32-bit) usage */ + if (insn_idx + insn_idx_bias < *num_insns) { + insn [insn_idx_bias].usage_page = item_state.usage_page; + insn [insn_idx_bias].usage_id = usage_id = item->value; + insn_idx_bias++; + } + break; + case ITEM_TAG_USAGE_MINIMUM: + usage_min = item->value; + break; + case ITEM_TAG_USAGE_MAXIMUM: + usage_max = item->value; + insn_idx_bias = min (usage_max - usage_min + 1, + *num_insns - insn_idx); + for (i = 0; i < insn_idx_bias; i++) { + insn [i].usage_page = item_state.usage_page; + insn [i].usage_id = usage_min + i; + } + break; + case ITEM_TAG_DESIGNATOR_INDEX: + break; + case ITEM_TAG_DESIGNATOR_MINIMUM: + break; + case ITEM_TAG_DESIGNATOR_MAXIMUM: + break; + case ITEM_TAG_STRING_INDEX: + break; + case ITEM_TAG_STRING_MINIMUM: + break; + case ITEM_TAG_STRING_MAXIMUM: + break; + case ITEM_TAG_DELIMITER: + break; + } + break; + } + } + +end: + *num_insns = insn_idx; + if (total_report_size != NULL) + *total_report_size = byte_idx; + if (first_report_id != NULL) + *first_report_id = item_state.report_id; + return SUCCESS; +} + +/* + designed for game controllers +*/ + +int +count_controls(report_insn *insns, size_t num_insns, + int *num_axes, int *num_hats, int *num_buttons) +{ + unsigned int insn_idx; + int hats = 0, buttons = 0, + axes = 2; /* X and Y axes are reserved */ + + if (insns == NULL || num_insns <= 0) + return FAILURE; + + for (insn_idx = 0; insn_idx < num_insns; insn_idx++) { + report_insn *insn = &insns [insn_idx]; + insn->ctrl_type = TYPE_NONE; + switch (insn->usage_page) { + case USAGE_PAGE_GENERIC_DESKTOP: + switch (insn->usage_id) { + case USAGE_ID_X: + insn->ctrl_type = TYPE_AXIS_X; + break; + + case USAGE_ID_Y: + insn->ctrl_type = TYPE_AXIS_Y; + break; + + case USAGE_ID_Z: + case USAGE_ID_RX: + case USAGE_ID_RY: + case USAGE_ID_RZ: + case USAGE_ID_SLIDER: + case USAGE_ID_DIAL: + case USAGE_ID_WHEEL: + axes++; + insn->ctrl_type = TYPE_AXIS; + break; + + case USAGE_ID_HAT_SWITCH: + hats++; + insn->ctrl_type = TYPE_HAT; + break; + } + break; + + case USAGE_PAGE_SIMULATION: + switch (insn->usage_id) { + case USAGE_ID_RUDDER: + case USAGE_ID_THROTTLE: + axes++; + insn->ctrl_type = TYPE_AXIS; + break; + } + break; + + case USAGE_PAGE_BUTTONS: + if (insn->num_bits == 1) { + buttons++; + insn->ctrl_type = TYPE_BUTTON; + } + break; + } + } + + if (num_axes != NULL) + *num_axes = axes; + if (num_hats != NULL) + *num_hats = hats; + if (num_buttons != NULL) + *num_buttons = buttons; + return SUCCESS; +} + diff --git a/src/add-ons/kernel/drivers/input/hid/hidparse.h b/src/add-ons/kernel/drivers/input/hid/hidparse.h new file mode 100644 index 0000000000..e63a414ba5 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/hid/hidparse.h @@ -0,0 +1,222 @@ +/*****************************************************************************/ +// HID usb driver +// Written by Jérôme Duval +// +// hidparse.h +// +// Copyright (c) 2004 Haiku Project +// +// Some portions of code are copyrighted by +// USB Joystick driver for BeOS R5 +// Copyright 2000 (C) ITO, Takayuki +// All rights reserved +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +/*****************************************************************************/ + + +#include + +#define SUCCESS 0 +#define FAILURE (-1) + +#define BOOL int +#define TRUE 1 +#define FALSE 0 + +/* + Report Descriptor Items + see + 5.3 Generic Item Format + 6.2.2 Report Descriptor + 8. Report Protocol + */ + +enum +{ + ITEM_SIZE_0, + ITEM_SIZE_1, + ITEM_SIZE_2, /* long item */ + ITEM_SIZE_4, +}; + +enum +{ + ITEM_TYPE_MAIN, + ITEM_TYPE_GLOBAL, + ITEM_TYPE_LOCAL, + ITEM_TYPE_RESERVED, /* long item */ +}; + +enum +{ + /* main items */ + ITEM_TAG_INPUT = 8, + ITEM_TAG_OUTPUT, + ITEM_TAG_COLLECTION, + ITEM_TAG_FEATURE, + ITEM_TAG_END_COLLECTION, + + /* global items */ + ITEM_TAG_USAGE_PAGE = 0, + ITEM_TAG_LOGICAL_MINIMUM, + ITEM_TAG_LOGICAL_MAXIMUM, + ITEM_TAG_PHYSICAL_MINIMUM, + ITEM_TAG_PHYSICAL_MAXIMUM, + ITEM_TAG_UNIT_EXPONENT, + ITEM_TAG_UNIT, + ITEM_TAG_REPORT_SIZE, + ITEM_TAG_REPORT_ID, + ITEM_TAG_REPORT_COUNT, + ITEM_TAG_PUSH, + ITEM_TAG_POP, + + /* local items */ + ITEM_TAG_USAGE = 0, + ITEM_TAG_USAGE_MINIMUM, + ITEM_TAG_USAGE_MAXIMUM, + ITEM_TAG_DESIGNATOR_INDEX, + ITEM_TAG_DESIGNATOR_MINIMUM, + ITEM_TAG_DESIGNATOR_MAXIMUM, + /* 6 is missing */ + ITEM_TAG_STRING_INDEX = 7, + ITEM_TAG_STRING_MINIMUM, + ITEM_TAG_STRING_MAXIMUM, + ITEM_TAG_DELIMITER, +}; + +typedef struct +{ + unsigned char bSize:2, bType:2, bTag:4; +} item_prefix_bitfields; + +typedef union +{ + item_prefix_bitfields b; + unsigned char i; +} item_prefix; + +#define LONG_ITEM_PREFIX 0xfe + +/* + parsed item +*/ + +typedef struct +{ + int offset; /* byte offset in report descriptor */ + int prefix, size, type, tag; + unsigned long value; + signed long signed_value; /* sign extended: for logical min/max */ +} decomp_item; + +/* + report "instruction" +*/ + +/* control types for game controllers */ +enum +{ + TYPE_NONE, TYPE_HAT, TYPE_AXIS, TYPE_AXIS_X, TYPE_AXIS_Y, TYPE_BUTTON +}; + +typedef struct +{ + unsigned int usage_page, usage_id; + int ctrl_type; /* TYPE_*; set by count_controls() */ + int attributes; + + BOOL is_log_signed, is_phy_signed; + unsigned long log_min, log_max; + unsigned long phy_min, phy_max; + signed long signed_log_min, signed_log_max; + signed long signed_phy_min, signed_phy_max; + + /* used to extract bit-field from report */ + /* value = (report [byte_idx] >> bit_pos) & ((1 << num_bits) - 1) */ + /* N.B. you need sign extension if signed */ + int byte_idx, bit_pos, num_bits; + +} report_insn; + +int decompose_report_descriptor + (const unsigned char *desc, + size_t desc_len, + decomp_item *items, + size_t *num_items); + +int parse_report_descriptor + (const decomp_item *items, + size_t num_items, + report_insn *insns, + size_t *num_insns, + size_t *total_report_size, + int *first_report_id); + +/* + Usage Pages/IDs +*/ + +enum +{ + USAGE_PAGE_GENERIC_DESKTOP = 1, + USAGE_PAGE_SIMULATION = 2, + USAGE_PAGE_GAME = 5, + USAGE_PAGE_KEYBOARD = 7, + USAGE_PAGE_LED, + USAGE_PAGE_BUTTONS, +}; + +/* Page 1: Generic Desktop */ + +enum +{ + USAGE_ID_POINTER = 1, + USAGE_ID_MOUSE = 2, + USAGE_ID_JOYSTICK = 4, + USAGE_ID_GAMEPAD, + USAGE_ID_KEYBOARD, + USAGE_ID_KEYPAD, + + USAGE_ID_X = 0x30, + USAGE_ID_Y, + USAGE_ID_Z, + USAGE_ID_RX, + USAGE_ID_RY, + USAGE_ID_RZ, + USAGE_ID_SLIDER, + USAGE_ID_DIAL, + USAGE_ID_WHEEL, + USAGE_ID_HAT_SWITCH, +}; + +/* Page 2: Simulation */ + +enum +{ + USAGE_ID_RUDDER = 0xBA, + USAGE_ID_THROTTLE = 0xBB, +}; + +int count_controls + (report_insn *insns, + size_t num_insns, + int *num_axes, + int *num_hats, + int *num_buttons);