[coreboot-gerrit] New patch to review for coreboot: libpayload: Add HID mouse support

Patrick Rudolph (siro@das-labor.org) gerrit at coreboot.org
Mon Mar 6 18:51:56 CET 2017


Patrick Rudolph (siro at das-labor.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18598

-gerrit

commit 003b616b6d4526ff737c38faa24971d837948f3f
Author: Patrick Rudolph <siro at das-labor.org>
Date:   Mon Feb 6 15:41:00 2017 +0100

    libpayload: Add HID mouse support
    
    Add support for HID mouse.
    
    Tested on Lenovo T500 and thinkpad docking using libpayload's
    EHCI usb stack.
    
    Depends on Change-Id: Ib7cec736631b8acf81a14d28daa29ff720777b10
    Signed-off-by: Patrick Rudolph <siro at das-labor.org>
    
    Change-Id: Ibce251d77f7ccc85e17d42fc471b14d294c0c3e9
---
 payloads/libpayload/Kconfig                |   2 +-
 payloads/libpayload/drivers/mouse_cursor.c |   3 +
 payloads/libpayload/drivers/usb/usbhid.c   | 144 +++++++++++++++++++++++++++--
 3 files changed, 139 insertions(+), 10 deletions(-)

diff --git a/payloads/libpayload/Kconfig b/payloads/libpayload/Kconfig
index a6255cc..d50ff2c 100644
--- a/payloads/libpayload/Kconfig
+++ b/payloads/libpayload/Kconfig
@@ -358,7 +358,7 @@ config NVRAM
 
 config MOUSE_CURSOR
 	bool "Support for mouse cursor handling"
-	default y if PC_MOUSE
+	default y if PC_MOUSE || USB_HID
 	default n
 	help
 	  Provides a common interface for various mouse cursor drivers.
diff --git a/payloads/libpayload/drivers/mouse_cursor.c b/payloads/libpayload/drivers/mouse_cursor.c
index 50220c6..b733779 100644
--- a/payloads/libpayload/drivers/mouse_cursor.c
+++ b/payloads/libpayload/drivers/mouse_cursor.c
@@ -62,6 +62,9 @@ void mouse_cursor_init(void)
 #if IS_ENABLED(CONFIG_LP_PC_MOUSE)
 	i8042_mouse_init();
 #endif
+#if IS_ENABLED(CONFIG_LP_USB) && IS_ENABLED(CONFIG_LP_USB_HID)
+	usb_initialize();
+#endif
 }
 
 static u32 mouse_buttons;
diff --git a/payloads/libpayload/drivers/usb/usbhid.c b/payloads/libpayload/drivers/usb/usbhid.c
index 3100d37..1cfdff6 100644
--- a/payloads/libpayload/drivers/usb/usbhid.c
+++ b/payloads/libpayload/drivers/usb/usbhid.c
@@ -2,6 +2,7 @@
  * This file is part of the libpayload project.
  *
  * Copyright (C) 2008-2010 coresystems GmbH
+ * Copyright (C) 2017 Patrick Rudolph <siro at das-labor.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -51,6 +52,16 @@ typedef union {
 	u8 buffer[8];
 } usb_hid_keyboard_event_t;
 
+typedef union {
+	struct {
+		u8 keys;
+		char x;
+		char y;
+		char wheel;
+	};
+	u8 buffer[4];
+} usb_hid_mouse_event_t;
+
 typedef struct {
 	void* queue;
 	hid_descriptor_t *descriptor;
@@ -88,6 +99,12 @@ static int keycount;
 #define KEYBOARD_BUFFER_SIZE 16
 static short keybuffer[KEYBOARD_BUFFER_SIZE];
 
+/* relative mouse movement is global to all USB mice */
+static int mouse_rel_x;
+static int mouse_rel_y;
+static int mouse_rel_z;
+static u32 mouse_buttons;
+
 const char *countries[36][2] = {
 	{ "not supported", "us" },
 	{ "Arabic", "ae" },
@@ -255,6 +272,7 @@ static void usb_hid_keyboard_queue(int ch) {
 }
 
 #define KEYBOARD_REPEAT_MS	30
+#define MOUSE_REPEAT_MS	30
 #define INITIAL_REPEAT_DELAY	10
 #define REPEAT_DELAY		 2
 
@@ -348,8 +366,47 @@ usb_hid_process_keyboard_event(usbhid_inst_t *const inst,
 	}
 }
 
+/** Mouse cursor interface method
+ * Return and clear internal state.
+ */
+static void usbhid_mouse_state(int *x, int *y, int *z, u32 *buttons)
+{
+	if (x) {
+		*x = mouse_rel_x;
+		mouse_rel_x = 0;
+	}
+	if (y) {
+		*y = mouse_rel_y;
+		mouse_rel_y = 0;
+	}
+	if (z) {
+		*z = mouse_rel_z;
+		mouse_rel_z = 0;
+	}
+	if (buttons)
+		*buttons = mouse_buttons;
+}
+
+/** Decode mouse events
+ * The HID boot protocol supports 2 axis and 3 buttons for sure.
+ * For additional axis and buttons the HID report has to be
+ * requested and parsed.
+ */
 static void
-usb_hid_poll (usbdev_t *dev)
+usb_hid_process_mouse_event(usbhid_inst_t *const inst,
+		const usb_hid_mouse_event_t *const current)
+{
+	mouse_rel_x += current->x;
+	mouse_rel_y += current->y;
+	/* TODO: Parse HID report for optional third axis */
+	mouse_rel_z = 0;
+
+	/* TODO: Parse HID report for optional buttons */
+	mouse_buttons = current->keys & 0x7;
+}
+
+static void
+usb_hid_poll_keyboard(usbdev_t *dev)
 {
 	usb_hid_keyboard_event_t current;
 	const u8 *buf;
@@ -361,6 +418,19 @@ usb_hid_poll (usbdev_t *dev)
 	}
 }
 
+/** Process interrupt queue */
+static void
+usb_hid_poll_mouse(usbdev_t *dev)
+{
+	usb_hid_mouse_event_t current;
+	const u8 *buf;
+
+	while((buf = dev->controller->poll_intr_queue (HID_INST(dev)->queue))) {
+		memcpy(&current.buffer, buf, 4);
+		usb_hid_process_mouse_event(HID_INST(dev), &current);
+	}
+}
+
 static void
 usb_hid_set_idle (usbdev_t *dev, interface_descriptor_t *interface, u16 duration)
 {
@@ -395,6 +465,11 @@ static struct console_input_driver cons = {
 	.input_type = CONSOLE_INPUT_TYPE_USB,
 };
 
+static struct mouse_cursor_input_driver curs = {
+	.get_state = usbhid_mouse_state,
+	.input_type = CURSOR_INPUT_TYPE_USB,
+};
+
 
 static int usb_hid_set_layout (const char *country)
 {
@@ -422,11 +497,15 @@ static int usb_hid_set_layout (const char *country)
 void
 usb_hid_init (usbdev_t *dev)
 {
-
+	hid_descriptor_t *desc;
+	endpoint_descriptor_t ep_desc;
 	static int installed = 0;
+	int i, interval;
+
 	if (!installed) {
 		installed = 1;
-		console_add_input_driver (&cons);
+		console_add_input_driver(&cons);
+		mouse_cursor_add_input_driver(&curs);
 	}
 
 	configuration_descriptor_t *cd = (configuration_descriptor_t*)dev->configuration;
@@ -439,7 +518,7 @@ usb_hid_init (usbdev_t *dev)
 			boot_protos[interface->bInterfaceProtocol]);
 		switch (interface->bInterfaceProtocol) {
 		case hid_boot_proto_keyboard:
-			dev->data = malloc (sizeof (usbhid_inst_t));
+			dev->data = malloc(sizeof(usbhid_inst_t));
 			if (!dev->data)
 				fatal("Not enough memory for USB HID device.\n");
 			memset(&HID_INST(dev)->previous, 0x00,
@@ -449,7 +528,7 @@ usb_hid_init (usbdev_t *dev)
 			usb_hid_set_idle(dev, interface, KEYBOARD_REPEAT_MS);
 			usb_debug ("  activating...\n");
 
-			hid_descriptor_t *desc = malloc(sizeof(hid_descriptor_t));
+			desc = malloc(sizeof(hid_descriptor_t));
 			if (!desc || get_descriptor(dev, gen_bmRequestType(
 				device_to_host, standard_type, iface_recp),
 				0x21, 0, desc, sizeof(*desc)) != sizeof(*desc)) {
@@ -468,10 +547,8 @@ usb_hid_init (usbdev_t *dev)
 			/* Set keyboard layout accordingly */
 			usb_hid_set_layout(countries[countrycode][1]);
 
-			// only add here, because we only support boot-keyboard HID devices
 			dev->destroy = usb_hid_destroy;
-			dev->poll = usb_hid_poll;
-			int i;
+			dev->poll = usb_hid_poll_keyboard;
 			for (i = 1; i < dev->num_endp; i++) {
 				if (dev->endpoints[i].type != INTERRUPT)
 					continue;
@@ -491,7 +568,56 @@ usb_hid_init (usbdev_t *dev)
 			usb_debug ("  configuration done.\n");
 			break;
 		case hid_boot_proto_mouse:
-			usb_debug("NOTICE: USB mice are not supported.\n");
+			dev->data = malloc(sizeof(usbhid_inst_t));
+			if (!dev->data)
+				fatal("Not enough memory for USB HID device.\n");
+			usb_debug("  configuring...\n");
+			usb_hid_set_protocol(dev, interface, hid_proto_boot);
+			usb_hid_set_idle(dev, interface, MOUSE_REPEAT_MS);
+			usb_debug("  activating...\n");
+
+			desc = malloc(sizeof(hid_descriptor_t));
+			if (!desc || get_descriptor(dev, gen_bmRequestType(
+				device_to_host, standard_type, iface_recp),
+				0x21, 0, desc, sizeof(*desc)) != sizeof(*desc)) {
+				usb_debug("get_descriptor(HID) failed\n");
+				usb_detach_device(dev->controller, dev->address);
+				return;
+			}
+			HID_INST (dev)->descriptor = desc;
+			if (desc->bCountryCode) {
+				usb_debug("WARN: invalid country code %02x\n",
+						desc->bCountryCode);
+			}
+
+			dev->destroy = usb_hid_destroy;
+			dev->poll = usb_hid_poll_mouse;
+			for (i = 1; i < dev->num_endp; i++) {
+				if (dev->endpoints[i].type != INTERRUPT)
+					continue;
+				if (dev->endpoints[i].direction != IN)
+					continue;
+				break;
+			}
+			if (i >= dev->num_endp) {
+				usb_debug("Could not find HID endpoint\n");
+				usb_detach_device (dev->controller, dev->address);
+				return;
+			}
+			usb_debug("  found endpoint %x for interrupt-in\n", i);
+
+			memset(&ep_desc, 0, sizeof(ep_desc));
+			if (get_descriptor(dev, gen_bmRequestType(
+				device_to_host, standard_type, endp_recp),
+				0x21, 0, &ep_desc, sizeof(ep_desc)) != sizeof(ep_desc)) {
+				usb_debug("get_descriptor(endpoint) failed\n");
+			}
+			interval = MAX(ep_desc.bInterval, MOUSE_REPEAT_MS);
+
+			/* 20 buffers of 4 bytes (+4 optional bytes), at given interval */
+			HID_INST(dev)->queue = dev->controller->create_intr_queue (&dev->endpoints[i], 8, 20, interval);
+			keycount = 0;
+			usb_debug("  configuration done.\n");
 			break;
 		}
 	}



More information about the coreboot-gerrit mailing list