From d8a26f0300e0fae51de277e5d5892926de12102f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 8 Nov 2015 23:48:06 -0700 Subject: [PATCH] usb: sandbox: Add a USB emulation driver Add a simple USB keyboard driver for sandbox. It provides a function to 'load' it with input data, which it will then stream through to the normal U-Boot input subsystem. When the input data is exhausted, the keyboard stops providing data. Signed-off-by: Simon Glass --- arch/sandbox/include/asm/test.h | 2 + drivers/usb/emul/Makefile | 1 + drivers/usb/emul/sandbox_keyb.c | 241 ++++++++++++++++++++++++++++++++ include/linux/usb/ch9.h | 20 +++ 4 files changed, 264 insertions(+) create mode 100644 drivers/usb/emul/sandbox_keyb.c diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index d3c7851bb5..224b0ebaf9 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -86,4 +86,6 @@ long sandbox_i2c_rtc_set_offset(struct udevice *dev, bool use_system_time, */ long sandbox_i2c_rtc_get_set_base_time(struct udevice *dev, long base_time); +int sandbox_usb_keyb_add_string(struct udevice *dev, const char *str); + #endif diff --git a/drivers/usb/emul/Makefile b/drivers/usb/emul/Makefile index 8fd83d5d06..b64ac6d721 100644 --- a/drivers/usb/emul/Makefile +++ b/drivers/usb/emul/Makefile @@ -7,4 +7,5 @@ obj-$(CONFIG_USB_EMUL) += sandbox_flash.o obj-$(CONFIG_USB_EMUL) += sandbox_hub.o +obj-$(CONFIG_USB_EMUL) += sandbox_keyb.o obj-$(CONFIG_USB_EMUL) += usb-emul-uclass.o diff --git a/drivers/usb/emul/sandbox_keyb.c b/drivers/usb/emul/sandbox_keyb.c new file mode 100644 index 0000000000..27359851df --- /dev/null +++ b/drivers/usb/emul/sandbox_keyb.c @@ -0,0 +1,241 @@ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* + * This driver emulates a USB keyboard using the USB HID specification (boot + * protocol) + */ + +enum { + SANDBOX_KEYB_EP_IN = 1, /* endpoints */ +}; + +enum cmd_phase { + PHASE_START, + PHASE_DATA, + PHASE_STATUS, +}; + +enum { + STRINGID_MANUFACTURER = 1, + STRINGID_PRODUCT, + STRINGID_SERIAL, + + STRINGID_COUNT, +}; + +/** + * struct sandbox_keyb_priv - private state for this driver + * + */ +struct sandbox_keyb_priv { + struct membuff in; +}; + +struct sandbox_keyb_plat { + struct usb_string keyb_strings[STRINGID_COUNT]; +}; + +static struct usb_device_descriptor keyb_device_desc = { + .bLength = sizeof(keyb_device_desc), + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0100), + + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + + .idVendor = __constant_cpu_to_le16(0x1234), + .idProduct = __constant_cpu_to_le16(0x5679), + .iManufacturer = STRINGID_MANUFACTURER, + .iProduct = STRINGID_PRODUCT, + .iSerialNumber = STRINGID_SERIAL, + .bNumConfigurations = 1, +}; + +static struct usb_config_descriptor keyb_config0 = { + .bLength = sizeof(keyb_config0), + .bDescriptorType = USB_DT_CONFIG, + + /* wTotalLength is set up by usb-emul-uclass */ + .bNumInterfaces = 2, + .bConfigurationValue = 0, + .iConfiguration = 0, + .bmAttributes = 1 << 7 | 1 << 5, + .bMaxPower = 50, +}; + +static struct usb_interface_descriptor keyb_interface0 = { + .bLength = sizeof(keyb_interface0), + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = USB_SUB_HID_BOOT, + .bInterfaceProtocol = USB_PROT_HID_KEYBOARD, + .iInterface = 0, +}; + +static struct usb_class_hid_descriptor keyb_report0 = { + .bLength = sizeof(keyb_report0), + .bDescriptorType = USB_DT_HID, + .bcdCDC = 0x101, + .bCountryCode = 0, + .bNumDescriptors = 1, + .bDescriptorType0 = USB_DT_HID_REPORT, + .wDescriptorLength0 = 0x3f, +}; + +static struct usb_endpoint_descriptor keyb_endpoint0_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = SANDBOX_KEYB_EP_IN | USB_ENDPOINT_DIR_MASK, + .bmAttributes = USB_ENDPOINT_XFER_BULK | + USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(8), + .bInterval = 0xa, +}; + +static struct usb_interface_descriptor keyb_interface1 = { + .bLength = sizeof(keyb_interface1), + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = USB_SUB_HID_BOOT, + .bInterfaceProtocol = USB_PROT_HID_MOUSE, + .iInterface = 0, +}; + +static struct usb_class_hid_descriptor keyb_report1 = { + .bLength = sizeof(struct usb_class_hid_descriptor), + .bDescriptorType = USB_DT_HID, + .bcdCDC = 0x101, + .bCountryCode = 0, + .bNumDescriptors = 1, + .bDescriptorType0 = USB_DT_HID_REPORT, + .wDescriptorLength0 = 0x32, +}; + +static struct usb_endpoint_descriptor keyb_endpoint1_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = SANDBOX_KEYB_EP_IN | USB_ENDPOINT_DIR_MASK, + .bmAttributes = USB_ENDPOINT_XFER_BULK | + USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(8), + .bInterval = 0xa, +}; + +static void *keyb_desc_list[] = { + &keyb_device_desc, + &keyb_config0, + &keyb_interface0, + &keyb_report0, + &keyb_endpoint0_in, + &keyb_interface1, + &keyb_report1, + &keyb_endpoint1_in, + NULL, +}; + +int sandbox_usb_keyb_add_string(struct udevice *dev, const char *str) +{ + struct sandbox_keyb_priv *priv = dev_get_priv(dev); + int len, ret; + + len = strlen(str); + ret = membuff_put(&priv->in, str, len); + if (ret != len) + return -ENOSPC; + + return 0; +} + +static int sandbox_keyb_control(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buff, int len, + struct devrequest *setup) +{ + debug("pipe=%lx\n", pipe); + + return -EIO; +} + +static int sandbox_keyb_interrupt(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, int interval) +{ + struct sandbox_keyb_priv *priv = dev_get_priv(dev); + uint8_t *data = buffer; + int ch; + + memset(data, '\0', length); + ch = membuff_getbyte(&priv->in); + if (ch != -1) + data[2] = 4 + ch - 'a'; + + return 0; +} + +static int sandbox_keyb_bind(struct udevice *dev) +{ + struct sandbox_keyb_plat *plat = dev_get_platdata(dev); + struct usb_string *fs; + + fs = plat->keyb_strings; + fs[0].id = STRINGID_MANUFACTURER; + fs[0].s = "sandbox"; + fs[1].id = STRINGID_PRODUCT; + fs[1].s = "keyboard"; + fs[2].id = STRINGID_SERIAL; + fs[2].s = dev->name; + + return usb_emul_setup_device(dev, PACKET_SIZE_8, plat->keyb_strings, + keyb_desc_list); +} + +static int sandbox_keyb_probe(struct udevice *dev) +{ + struct sandbox_keyb_priv *priv = dev_get_priv(dev); + + return membuff_new(&priv->in, 256); +} + +static const struct dm_usb_ops sandbox_usb_keyb_ops = { + .control = sandbox_keyb_control, + .interrupt = sandbox_keyb_interrupt, +}; + +static const struct udevice_id sandbox_usb_keyb_ids[] = { + { .compatible = "sandbox,usb-keyb" }, + { } +}; + +U_BOOT_DRIVER(usb_sandbox_keyb) = { + .name = "usb_sandbox_keyb", + .id = UCLASS_USB_EMUL, + .of_match = sandbox_usb_keyb_ids, + .bind = sandbox_keyb_bind, + .probe = sandbox_keyb_probe, + .ops = &sandbox_usb_keyb_ops, + .priv_auto_alloc_size = sizeof(struct sandbox_keyb_priv), + .platdata_auto_alloc_size = sizeof(struct sandbox_keyb_plat), +}; diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 822fca0357..0ad4782a36 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -229,6 +229,8 @@ struct usb_ctrlrequest { #define USB_DT_PIPE_USAGE 0x24 /* From the USB 3.0 spec */ #define USB_DT_SS_ENDPOINT_COMP 0x30 +/* From HID 1.11 spec */ +#define USB_DT_HID_REPORT 0x22 /* Conventional codes for class-specific descriptors. The convention is * defined in the USB "Common Class" Spec (3.11). Individual class specs @@ -385,6 +387,24 @@ struct usb_generic_descriptor { __u8 bDescriptorType; }; +struct __packed usb_class_hid_descriptor { + u8 bLength; + u8 bDescriptorType; + u16 bcdCDC; + u8 bCountryCode; + u8 bNumDescriptors; /* 0x01 */ + u8 bDescriptorType0; + u16 wDescriptorLength0; + /* optional descriptors are not supported. */ +}; + +struct __packed usb_class_report_descriptor { + u8 bLength; /* dummy */ + u8 bDescriptorType; + u16 wLength; + u8 bData[0]; +}; + /* * Endpoints */