[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[cdi-devel] [PATCH 5/5] usb-hid: Add driver



Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
 usb-hid/main.c    |  74 ++++++++++++++++++++++++++
 usb-hid/usb-hid.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 usb-hid/usb-hid.h |  54 +++++++++++++++++++
 3 files changed, 281 insertions(+)
 create mode 100644 usb-hid/main.c
 create mode 100644 usb-hid/usb-hid.c
 create mode 100644 usb-hid/usb-hid.h

diff --git a/usb-hid/main.c b/usb-hid/main.c
new file mode 100644
index 0000000..4ff637e
--- /dev/null
+++ b/usb-hid/main.c
@@ -0,0 +1,74 @@
+/******************************************************************************
+ * Copyright (c) 2015 Max Reitz                                               *
+ *                                                                            *
+ * 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 <cdi.h>
+#include <cdi/hid.h>
+#include <cdi/usb.h>
+
+#include "usb-hid.h"
+
+
+#define DRIVER_NAME "usb-hid"
+
+
+static struct cdi_hid_driver drv;
+static struct cdi_usb_bus_device_pattern keyboard_bus_pattern;
+
+static int drv_init(void)
+{
+    cdi_driver_init(&drv.drv);
+    cdi_handle_bus_device(&drv.drv, &keyboard_bus_pattern.pattern);
+    return 0;
+}
+
+static int drv_destroy(void)
+{
+    cdi_driver_destroy(&drv.drv);
+    return 0;
+}
+
+
+static struct cdi_hid_driver drv = {
+    .drv = {
+        .name           = DRIVER_NAME,
+        .type           = CDI_HID,
+        .bus            = CDI_USB,
+        .init           = drv_init,
+        .destroy        = drv_destroy,
+        .init_device    = init_usb_device,
+    },
+};
+
+static struct cdi_usb_bus_device_pattern keyboard_bus_pattern = {
+    .pattern = {
+        .bus_type = CDI_USB,
+    },
+
+    .vendor_id  = -1,
+    .product_id = -1,
+
+    .class_id       = CDI_USB_IFCCLS_HID,
+    .subclass_id    = USBHID_SUBCLS_BOOT,
+    .protocol_id    = USBHID_PROTO_KEYBOARD,
+};
+
+CDI_DRIVER(DRIVER_NAME, drv)
diff --git a/usb-hid/usb-hid.c b/usb-hid/usb-hid.c
new file mode 100644
index 0000000..1b2edc9
--- /dev/null
+++ b/usb-hid/usb-hid.c
@@ -0,0 +1,153 @@
+/******************************************************************************
+ * Copyright (c) 2015 Max Reitz                                               *
+ *                                                                            *
+ * 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 <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cdi.h>
+#include <cdi/misc.h>
+#include <cdi/hid.h>
+#include <cdi/usb.h>
+
+#include "usb-hid.h"
+
+
+typedef struct usb_hid_keyboard {
+    struct cdi_hid dev;
+    struct cdi_usb_device usb;
+
+    cdi_usb_async_transmission_t interrupt_trans;
+    uint8_t interrupt_buffer[8];
+
+    int interrupt_ep;
+} usb_hid_keyboard_t;
+
+
+static void interrupt_cb(void *opaque)
+{
+    usb_hid_keyboard_t *dev = opaque;
+    // On rollover, all scancode bytes are set to 0x01 (Keyboard ErrorRollOver)
+    bool have_rollover = dev->interrupt_buffer[2] == 0x01;
+
+    if (have_rollover) {
+        // On rollover, we will only update the modifier keys
+        dev->dev.button_states[USBHID_BOOT_KEYBOARD_MODIFIER_MINIMUM_KEY] = 0;
+    } else {
+        // Otherwise, we will update all keys
+        memset(dev->dev.button_states, 0, 256 / 8);
+    }
+
+    uint8_t modifier = dev->interrupt_buffer[0];
+    for (int mod_key = 0; mod_key < 8; mod_key++) {
+        if (modifier & (1 << mod_key)) {
+            int button = mod_key + USBHID_BOOT_KEYBOARD_MODIFIER_MINIMUM_KEY;
+            dev->dev.button_states[button / 8] |= 1 << (button % 8);
+        }
+    }
+
+    if (!have_rollover) {
+        for (int i = 2; i < 8; i++) {
+            int button = dev->interrupt_buffer[i];
+            if (button) {
+                dev->dev.button_states[button / 8] |= 1 << (button % 8);
+            }
+        }
+    }
+
+    cdi_hid_state_update(&dev->dev);
+}
+
+
+struct cdi_device *init_usb_device(struct cdi_bus_data *bus_data)
+{
+    static int dev_counter = 0;
+    usb_hid_keyboard_t *dev = calloc(1, sizeof(*dev));
+    dev->usb = *CDI_UPCAST(bus_data, struct cdi_usb_device, bus_data);
+
+    // Switch to boot protocol
+    cdi_usb_transmission_result_t res;
+
+    res = cdi_usb_control_transfer(&dev->usb, 0, &(struct cdi_usb_setup_packet){
+            .bm_request_type    = CDI_USB_CREQ_CLASS | CDI_USB_CREQ_INTERFACE
+                                | CDI_USB_CREQ_OUT,
+            .b_request          = USBHID_CREQ_SET_PROTOCOL,
+            .w_value            = 0, // Boot Protocol
+            .w_index            = dev->usb.interface
+        }, NULL);
+
+    if (res != CDI_USB_OK) {
+        free(dev);
+        return NULL;
+    }
+
+    for (int ep = 1; ep < dev->usb.endpoint_count; ep++) {
+        struct cdi_usb_endpoint_descriptor ep_desc;
+        cdi_usb_get_endpoint_descriptor(&dev->usb, ep, &ep_desc);
+        if ((ep_desc.bm_attributes & CDI_USB_EPDSC_ATTR_XFER_TYPE_MASK)
+            == CDI_USB_EPDSC_ATTR_INTERRUPT &&
+            ep_desc.b_endpoint_address & CDI_USB_EPDSC_EPADR_DIR)
+        {
+            dev->interrupt_ep = ep;
+            break;
+        }
+    }
+
+    if (!dev->interrupt_ep) {
+        free(dev);
+        return NULL;
+    }
+
+
+    dev->interrupt_trans =
+        cdi_usb_interrupt_transfer(&dev->usb, dev->interrupt_ep,
+                                   dev->interrupt_buffer,
+                                   sizeof(dev->interrupt_buffer),
+                                   interrupt_cb, dev);
+
+    dev->dev.button_count = 256;
+    dev->dev.axes_count = 0;
+    dev->dev.led_count = 5;
+
+    dev->dev.button_states = calloc(1, 256 / 8);
+
+    asprintf((char **)&dev->dev.dev.name, "hid-kbd%i", dev_counter++);
+
+    return &dev->dev.dev;
+}
+
+
+void set_keyboard_leds(struct cdi_hid *device, const cdi_hid_bitmap_t *states)
+{
+    usb_hid_keyboard_t *dev = CDI_UPCAST(device, usb_hid_keyboard_t, dev);
+    uint8_t shifted_states = *(uint8_t *)states >> 1;
+
+    cdi_usb_control_transfer(&dev->usb, 0, &(struct cdi_usb_setup_packet){
+            .bm_request_type    = CDI_USB_CREQ_CLASS | CDI_USB_CREQ_INTERFACE
+                                | CDI_USB_CREQ_OUT,
+            .b_request          = USBHID_CREQ_SET_REPORT,
+            .w_index            = dev->usb.interface,
+            .w_length           = 1
+        }, &shifted_states);
+}
diff --git a/usb-hid/usb-hid.h b/usb-hid/usb-hid.h
new file mode 100644
index 0000000..54c63a6
--- /dev/null
+++ b/usb-hid/usb-hid.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+ * Copyright (c) 2015 Max Reitz                                               *
+ *                                                                            *
+ * 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.                                                  *
+ ******************************************************************************/
+
+#ifndef USB_HID_H
+#define USB_HID_H
+
+#include <cdi.h>
+#include <cdi/scsi.h>
+
+
+// HID subclass codes
+#define USBHID_SUBCLS_NONE  0x00 // No Subclass
+#define USBHID_SUBCLS_BOOT  0x01 // Boot Interface Subclass
+
+// HID protocol codes (only non-zero if the subclass is 0x01)
+#define USBHID_PROTO_NONE       0x00 // None
+#define USBHID_PROTO_KEYBOARD   0x01 // Keyboard
+#define USBHID_PROTO_MOUSE      0x02 // Mouse
+
+// HID control request codes
+#define USBHID_CREQ_GET_REPORT      0x01
+#define USBHID_CREQ_GET_IDLE        0x02
+#define USBHID_CREQ_GET_PROTOCOL    0x03
+#define USBHID_CREQ_SET_REPORT      0x09
+#define USBHID_CREQ_SET_IDLE        0x0a
+#define USBHID_CREQ_SET_PROTOCOL    0x0b
+
+// Usage Minimum of the modifier byte for the keyboard boot protocol
+// (see USB HID specification, section B.1)
+#define USBHID_BOOT_KEYBOARD_MODIFIER_MINIMUM_KEY   0xe0
+
+
+struct cdi_device *init_usb_device(struct cdi_bus_data *bus_data);
+
+#endif
-- 
2.6.4