diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/drivers/usb/emul | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/drivers/usb/emul')
-rw-r--r-- | roms/u-boot/drivers/usb/emul/Kconfig | 8 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/emul/Makefile | 9 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/emul/sandbox_flash.c | 426 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/emul/sandbox_hub.c | 334 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/emul/sandbox_keyb.c | 246 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/emul/usb-emul-uclass.c | 304 |
6 files changed, 1327 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/usb/emul/Kconfig b/roms/u-boot/drivers/usb/emul/Kconfig new file mode 100644 index 000000000..ae1ab23a3 --- /dev/null +++ b/roms/u-boot/drivers/usb/emul/Kconfig @@ -0,0 +1,8 @@ +config USB_EMUL + bool "Support for USB device emulation" + depends on DM_USB && SANDBOX + help + Since sandbox does not have access to a real USB bus, it is possible + to use device emulators instead. This allows testing of the USB + stack on sandbox without needing a real device, or any host machine + USB resources. diff --git a/roms/u-boot/drivers/usb/emul/Makefile b/roms/u-boot/drivers/usb/emul/Makefile new file mode 100644 index 000000000..bf2d49a53 --- /dev/null +++ b/roms/u-boot/drivers/usb/emul/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2015 Google, Inc +# Written by Simon Glass <sjg@chromium.org> + +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/roms/u-boot/drivers/usb/emul/sandbox_flash.c b/roms/u-boot/drivers/usb/emul/sandbox_flash.c new file mode 100644 index 000000000..edabc1b3a --- /dev/null +++ b/roms/u-boot/drivers/usb/emul/sandbox_flash.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <os.h> +#include <scsi.h> +#include <usb.h> + +/* + * This driver emulates a flash stick using the UFI command specification and + * the BBB (bulk/bulk/bulk) protocol. It supports only a single logical unit + * number (LUN 0). + */ + +enum { + SANDBOX_FLASH_EP_OUT = 1, /* endpoints */ + SANDBOX_FLASH_EP_IN = 2, + SANDBOX_FLASH_BLOCK_LEN = 512, +}; + +enum cmd_phase { + PHASE_START, + PHASE_DATA, + PHASE_STATUS, +}; + +enum { + STRINGID_MANUFACTURER = 1, + STRINGID_PRODUCT, + STRINGID_SERIAL, + + STRINGID_COUNT, +}; + +/** + * struct sandbox_flash_priv - private state for this driver + * + * @error: true if there is an error condition + * @alloc_len: Allocation length from the last incoming command + * @transfer_len: Transfer length from CBW header + * @read_len: Number of blocks of data left in the current read command + * @tag: Tag value from last command + * @fd: File descriptor of backing file + * @file_size: Size of file in bytes + * @status_buff: Data buffer for outgoing status + * @buff_used: Number of bytes ready to transfer back to host + * @buff: Data buffer for outgoing data + */ +struct sandbox_flash_priv { + bool error; + int alloc_len; + int transfer_len; + int read_len; + enum cmd_phase phase; + u32 tag; + int fd; + loff_t file_size; + struct umass_bbb_csw status; + int buff_used; + u8 buff[512]; +}; + +struct sandbox_flash_plat { + const char *pathname; + struct usb_string flash_strings[STRINGID_COUNT]; +}; + +struct scsi_inquiry_resp { + u8 type; + u8 flags; + u8 version; + u8 data_format; + u8 additional_len; + u8 spare[3]; + char vendor[8]; + char product[16]; + char revision[4]; +}; + +struct scsi_read_capacity_resp { + u32 last_block_addr; + u32 block_len; +}; + +struct __packed scsi_read10_req { + u8 cmd; + u8 lun_flags; + u32 lba; + u8 spare; + u16 transfer_len; + u8 spare2[3]; +}; + +static struct usb_device_descriptor flash_device_desc = { + .bLength = sizeof(flash_device_desc), + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + + .idVendor = __constant_cpu_to_le16(0x1234), + .idProduct = __constant_cpu_to_le16(0x5678), + .iManufacturer = STRINGID_MANUFACTURER, + .iProduct = STRINGID_PRODUCT, + .iSerialNumber = STRINGID_SERIAL, + .bNumConfigurations = 1, +}; + +static struct usb_config_descriptor flash_config0 = { + .bLength = sizeof(flash_config0), + .bDescriptorType = USB_DT_CONFIG, + + /* wTotalLength is set up by usb-emul-uclass */ + .bNumInterfaces = 1, + .bConfigurationValue = 0, + .iConfiguration = 0, + .bmAttributes = 1 << 7, + .bMaxPower = 50, +}; + +static struct usb_interface_descriptor flash_interface0 = { + .bLength = sizeof(flash_interface0), + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = US_SC_UFI, + .bInterfaceProtocol = US_PR_BULK, + .iInterface = 0, +}; + +static struct usb_endpoint_descriptor flash_endpoint0_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = SANDBOX_FLASH_EP_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(1024), + .bInterval = 0, +}; + +static struct usb_endpoint_descriptor flash_endpoint1_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = SANDBOX_FLASH_EP_IN | USB_ENDPOINT_DIR_MASK, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(1024), + .bInterval = 0, +}; + +static void *flash_desc_list[] = { + &flash_device_desc, + &flash_config0, + &flash_interface0, + &flash_endpoint0_out, + &flash_endpoint1_in, + NULL, +}; + +static int sandbox_flash_control(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buff, int len, + struct devrequest *setup) +{ + struct sandbox_flash_priv *priv = dev_get_priv(dev); + + if (pipe == usb_rcvctrlpipe(udev, 0)) { + switch (setup->request) { + case US_BBB_RESET: + priv->error = false; + return 0; + case US_BBB_GET_MAX_LUN: + *(char *)buff = '\0'; + return 1; + default: + debug("request=%x\n", setup->request); + break; + } + } + debug("pipe=%lx\n", pipe); + + return -EIO; +} + +static void setup_fail_response(struct sandbox_flash_priv *priv) +{ + struct umass_bbb_csw *csw = &priv->status; + + csw->dCSWSignature = CSWSIGNATURE; + csw->dCSWTag = priv->tag; + csw->dCSWDataResidue = 0; + csw->bCSWStatus = CSWSTATUS_FAILED; + priv->buff_used = 0; +} + +/** + * setup_response() - set up a response to send back to the host + * + * @priv: Sandbox flash private data + * @resp: Response to send, or NULL if none + * @size: Size of response + */ +static void setup_response(struct sandbox_flash_priv *priv, void *resp, + int size) +{ + struct umass_bbb_csw *csw = &priv->status; + + csw->dCSWSignature = CSWSIGNATURE; + csw->dCSWTag = priv->tag; + csw->dCSWDataResidue = 0; + csw->bCSWStatus = CSWSTATUS_GOOD; + + assert(!resp || resp == priv->buff); + priv->buff_used = size; +} + +static void handle_read(struct sandbox_flash_priv *priv, ulong lba, + ulong transfer_len) +{ + debug("%s: lba=%lx, transfer_len=%lx\n", __func__, lba, transfer_len); + if (priv->fd != -1) { + os_lseek(priv->fd, lba * SANDBOX_FLASH_BLOCK_LEN, OS_SEEK_SET); + priv->read_len = transfer_len; + setup_response(priv, priv->buff, + transfer_len * SANDBOX_FLASH_BLOCK_LEN); + } else { + setup_fail_response(priv); + } +} + +static int handle_ufi_command(struct sandbox_flash_plat *plat, + struct sandbox_flash_priv *priv, const void *buff, + int len) +{ + const struct scsi_cmd *req = buff; + + switch (*req->cmd) { + case SCSI_INQUIRY: { + struct scsi_inquiry_resp *resp = (void *)priv->buff; + + priv->alloc_len = req->cmd[4]; + memset(resp, '\0', sizeof(*resp)); + resp->data_format = 1; + resp->additional_len = 0x1f; + strncpy(resp->vendor, + plat->flash_strings[STRINGID_MANUFACTURER - 1].s, + sizeof(resp->vendor)); + strncpy(resp->product, + plat->flash_strings[STRINGID_PRODUCT - 1].s, + sizeof(resp->product)); + strncpy(resp->revision, "1.0", sizeof(resp->revision)); + setup_response(priv, resp, sizeof(*resp)); + break; + } + case SCSI_TST_U_RDY: + setup_response(priv, NULL, 0); + break; + case SCSI_RD_CAPAC: { + struct scsi_read_capacity_resp *resp = (void *)priv->buff; + uint blocks; + + if (priv->file_size) + blocks = priv->file_size / SANDBOX_FLASH_BLOCK_LEN - 1; + else + blocks = 0; + resp->last_block_addr = cpu_to_be32(blocks); + resp->block_len = cpu_to_be32(SANDBOX_FLASH_BLOCK_LEN); + setup_response(priv, resp, sizeof(*resp)); + break; + } + case SCSI_READ10: { + struct scsi_read10_req *req = (void *)buff; + + handle_read(priv, be32_to_cpu(req->lba), + be16_to_cpu(req->transfer_len)); + break; + } + default: + debug("Command not supported: %x\n", req->cmd[0]); + return -EPROTONOSUPPORT; + } + + priv->phase = priv->transfer_len ? PHASE_DATA : PHASE_STATUS; + return 0; +} + +static int sandbox_flash_bulk(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buff, int len) +{ + struct sandbox_flash_plat *plat = dev_get_plat(dev); + struct sandbox_flash_priv *priv = dev_get_priv(dev); + int ep = usb_pipeendpoint(pipe); + struct umass_bbb_cbw *cbw = buff; + + debug("%s: dev=%s, pipe=%lx, ep=%x, len=%x, phase=%d\n", __func__, + dev->name, pipe, ep, len, priv->phase); + switch (ep) { + case SANDBOX_FLASH_EP_OUT: + switch (priv->phase) { + case PHASE_START: + priv->alloc_len = 0; + priv->read_len = 0; + if (priv->error || len != UMASS_BBB_CBW_SIZE || + cbw->dCBWSignature != CBWSIGNATURE) + goto err; + if ((cbw->bCBWFlags & CBWFLAGS_SBZ) || + cbw->bCBWLUN != 0) + goto err; + if (cbw->bCDBLength < 1 || cbw->bCDBLength >= 0x10) + goto err; + priv->transfer_len = cbw->dCBWDataTransferLength; + priv->tag = cbw->dCBWTag; + return handle_ufi_command(plat, priv, cbw->CBWCDB, + cbw->bCDBLength); + case PHASE_DATA: + debug("data out\n"); + break; + default: + break; + } + case SANDBOX_FLASH_EP_IN: + switch (priv->phase) { + case PHASE_DATA: + debug("data in, len=%x, alloc_len=%x, priv->read_len=%x\n", + len, priv->alloc_len, priv->read_len); + if (priv->read_len) { + ulong bytes_read; + + bytes_read = os_read(priv->fd, buff, len); + if (bytes_read != len) + return -EIO; + priv->read_len -= len / SANDBOX_FLASH_BLOCK_LEN; + if (!priv->read_len) + priv->phase = PHASE_STATUS; + } else { + if (priv->alloc_len && len > priv->alloc_len) + len = priv->alloc_len; + memcpy(buff, priv->buff, len); + priv->phase = PHASE_STATUS; + } + return len; + case PHASE_STATUS: + debug("status in, len=%x\n", len); + if (len > sizeof(priv->status)) + len = sizeof(priv->status); + memcpy(buff, &priv->status, len); + priv->phase = PHASE_START; + return len; + default: + break; + } + } +err: + priv->error = true; + debug("%s: Detected transfer error\n", __func__); + return 0; +} + +static int sandbox_flash_of_to_plat(struct udevice *dev) +{ + struct sandbox_flash_plat *plat = dev_get_plat(dev); + + plat->pathname = dev_read_string(dev, "sandbox,filepath"); + + return 0; +} + +static int sandbox_flash_bind(struct udevice *dev) +{ + struct sandbox_flash_plat *plat = dev_get_plat(dev); + struct usb_string *fs; + + fs = plat->flash_strings; + fs[0].id = STRINGID_MANUFACTURER; + fs[0].s = "sandbox"; + fs[1].id = STRINGID_PRODUCT; + fs[1].s = "flash"; + fs[2].id = STRINGID_SERIAL; + fs[2].s = dev->name; + + return usb_emul_setup_device(dev, plat->flash_strings, flash_desc_list); +} + +static int sandbox_flash_probe(struct udevice *dev) +{ + struct sandbox_flash_plat *plat = dev_get_plat(dev); + struct sandbox_flash_priv *priv = dev_get_priv(dev); + + priv->fd = os_open(plat->pathname, OS_O_RDONLY); + if (priv->fd != -1) + return os_get_filesize(plat->pathname, &priv->file_size); + + return 0; +} + +static const struct dm_usb_ops sandbox_usb_flash_ops = { + .control = sandbox_flash_control, + .bulk = sandbox_flash_bulk, +}; + +static const struct udevice_id sandbox_usb_flash_ids[] = { + { .compatible = "sandbox,usb-flash" }, + { } +}; + +U_BOOT_DRIVER(usb_sandbox_flash) = { + .name = "usb_sandbox_flash", + .id = UCLASS_USB_EMUL, + .of_match = sandbox_usb_flash_ids, + .bind = sandbox_flash_bind, + .probe = sandbox_flash_probe, + .of_to_plat = sandbox_flash_of_to_plat, + .ops = &sandbox_usb_flash_ops, + .priv_auto = sizeof(struct sandbox_flash_priv), + .plat_auto = sizeof(struct sandbox_flash_plat), +}; diff --git a/roms/u-boot/drivers/usb/emul/sandbox_hub.c b/roms/u-boot/drivers/usb/emul/sandbox_hub.c new file mode 100644 index 000000000..041ec3772 --- /dev/null +++ b/roms/u-boot/drivers/usb/emul/sandbox_hub.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <usb.h> +#include <dm/device-internal.h> + +/* We only support up to 8 */ +#define SANDBOX_NUM_PORTS 4 + +struct sandbox_hub_plat { + struct usb_dev_plat plat; + int port; /* Port number (numbered from 0) */ +}; + +enum { + STRING_MANUFACTURER = 1, + STRING_PRODUCT, + STRING_SERIAL, + + STRING_count, +}; + +static struct usb_string hub_strings[] = { + {STRING_MANUFACTURER, "sandbox"}, + {STRING_PRODUCT, "hub"}, + {STRING_SERIAL, "2345"}, + {}, +}; + +static struct usb_device_descriptor hub_device_desc = { + .bLength = sizeof(hub_device_desc), + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + + .bDeviceClass = USB_CLASS_HUB, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + + .idVendor = __constant_cpu_to_le16(0x1234), + .idProduct = __constant_cpu_to_le16(0x5678), + .iManufacturer = STRING_MANUFACTURER, + .iProduct = STRING_PRODUCT, + .iSerialNumber = STRING_SERIAL, + .bNumConfigurations = 1, +}; + +static struct usb_config_descriptor hub_config1 = { + .bLength = sizeof(hub_config1), + .bDescriptorType = USB_DT_CONFIG, + + /* wTotalLength is set up by usb-emul-uclass */ + .bNumInterfaces = 1, + .bConfigurationValue = 0, + .iConfiguration = 0, + .bmAttributes = 1 << 7, + .bMaxPower = 50, +}; + +static struct usb_interface_descriptor hub_interface0 = { + .bLength = sizeof(hub_interface0), + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HUB, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = US_PR_CB, + .iInterface = 0, +}; + +static struct usb_endpoint_descriptor hub_endpoint0_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(1024), + .bInterval = 0, +}; + +static struct usb_hub_descriptor hub_desc = { + .bLength = sizeof(hub_desc), + .bDescriptorType = USB_DT_HUB, + .bNbrPorts = SANDBOX_NUM_PORTS, + .wHubCharacteristics = __constant_cpu_to_le16(1 << 0 | 1 << 3 | + 1 << 7), + .bPwrOn2PwrGood = 2, + .bHubContrCurrent = 5, + { + { + /* all ports removeable */ + .DeviceRemovable = {0, 0xff} + } + } +#if SANDBOX_NUM_PORTS > 8 +#error "This code sets up an incorrect mask" +#endif +}; + +static void *hub_desc_list[] = { + &hub_device_desc, + &hub_config1, + &hub_interface0, + &hub_endpoint0_in, + &hub_desc, + NULL, +}; + +struct sandbox_hub_priv { + int status[SANDBOX_NUM_PORTS]; + int change[SANDBOX_NUM_PORTS]; +}; + +static struct udevice *hub_find_device(struct udevice *hub, int port, + enum usb_device_speed *speed) +{ + struct udevice *dev; + struct usb_generic_descriptor **gen_desc; + struct usb_device_descriptor **dev_desc; + + for (device_find_first_child(hub, &dev); + dev; + device_find_next_child(&dev)) { + struct sandbox_hub_plat *plat; + + plat = dev_get_parent_plat(dev); + if (plat->port == port) { + gen_desc = plat->plat.desc_list; + gen_desc = usb_emul_find_descriptor(gen_desc, + USB_DT_DEVICE, 0); + dev_desc = (struct usb_device_descriptor **)gen_desc; + + switch (le16_to_cpu((*dev_desc)->bcdUSB)) { + case 0x0100: + *speed = USB_SPEED_LOW; + break; + case 0x0101: + *speed = USB_SPEED_FULL; + break; + case 0x0200: + default: + *speed = USB_SPEED_HIGH; + break; + } + + return dev; + } + } + + return NULL; +} + +static int clrset_post_state(struct udevice *hub, int port, int clear, int set) +{ + struct sandbox_hub_priv *priv = dev_get_priv(hub); + int *status = &priv->status[port]; + int *change = &priv->change[port]; + int ret = 0; + + if ((clear | set) & USB_PORT_STAT_POWER) { + enum usb_device_speed speed; + struct udevice *dev = hub_find_device(hub, port, &speed); + + if (dev) { + if (set & USB_PORT_STAT_POWER) { + ret = device_probe(dev); + debug("%s: %s: power on, probed, ret=%d\n", + __func__, dev->name, ret); + if (!ret) { + set |= USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE; + if (speed == USB_SPEED_LOW) + set |= USB_PORT_STAT_LOW_SPEED; + else if (speed == USB_SPEED_HIGH) + set |= USB_PORT_STAT_HIGH_SPEED; + } + + } else if (clear & USB_PORT_STAT_POWER) { + debug("%s: %s: power off, removed, ret=%d\n", + __func__, dev->name, ret); + ret = device_remove(dev, DM_REMOVE_NORMAL); + clear |= USB_PORT_STAT_CONNECTION; + } + } + } + *change |= *status & clear; + *change |= ~*status & set; + *change &= 0x1f; + *status = (*status & ~clear) | set; + + return ret; +} + +static int sandbox_hub_submit_control_msg(struct udevice *bus, + struct usb_device *udev, + unsigned long pipe, + void *buffer, int length, + struct devrequest *setup) +{ + struct sandbox_hub_priv *priv = dev_get_priv(bus); + int ret = 0; + + if (pipe == usb_rcvctrlpipe(udev, 0)) { + switch (setup->requesttype) { + case USB_RT_HUB | USB_DIR_IN: + switch (setup->request) { + case USB_REQ_GET_STATUS: { + struct usb_hub_status *hubsts = buffer; + + hubsts->wHubStatus = 0; + hubsts->wHubChange = 0; + udev->status = 0; + udev->act_len = sizeof(*hubsts); + return 0; + } + default: + debug("%s: rx ctl requesttype=%x, request=%x\n", + __func__, setup->requesttype, + setup->request); + break; + } + case USB_RT_PORT | USB_DIR_IN: + switch (setup->request) { + case USB_REQ_GET_STATUS: { + struct usb_port_status *portsts = buffer; + int port; + + port = (setup->index & USB_HUB_PORT_MASK) - 1; + portsts->wPortStatus = priv->status[port]; + portsts->wPortChange = priv->change[port]; + udev->status = 0; + udev->act_len = sizeof(*portsts); + return 0; + } + } + default: + debug("%s: rx ctl requesttype=%x, request=%x\n", + __func__, setup->requesttype, setup->request); + break; + } + } else if (pipe == usb_sndctrlpipe(udev, 0)) { + switch (setup->requesttype) { + case USB_RT_PORT: + switch (setup->request) { + case USB_REQ_SET_FEATURE: { + int port; + + port = (setup->index & USB_HUB_PORT_MASK) - 1; + debug("set feature port=%x, feature=%x\n", + port, setup->value); + if (setup->value < USB_PORT_FEAT_C_CONNECTION) { + ret = clrset_post_state(bus, port, 0, + 1 << setup->value); + } else { + debug(" ** Invalid feature\n"); + } + return ret; + } + case USB_REQ_CLEAR_FEATURE: { + int port; + + port = (setup->index & USB_HUB_PORT_MASK) - 1; + debug("clear feature port=%x, feature=%x\n", + port, setup->value); + if (setup->value < USB_PORT_FEAT_C_CONNECTION) { + ret = clrset_post_state(bus, port, + 1 << setup->value, 0); + } else { + priv->change[port] &= 1 << + (setup->value - 16); + } + udev->status = 0; + return 0; + } + default: + debug("%s: tx ctl requesttype=%x, request=%x\n", + __func__, setup->requesttype, + setup->request); + break; + } + default: + debug("%s: tx ctl requesttype=%x, request=%x\n", + __func__, setup->requesttype, setup->request); + break; + } + } + debug("pipe=%lx\n", pipe); + + return -EIO; +} + +static int sandbox_hub_bind(struct udevice *dev) +{ + return usb_emul_setup_device(dev, hub_strings, hub_desc_list); +} + +static int sandbox_child_post_bind(struct udevice *dev) +{ + struct sandbox_hub_plat *plat = dev_get_parent_plat(dev); + struct usb_emul_plat *emul = dev_get_uclass_plat(dev); + + plat->port = dev_read_u32_default(dev, "reg", -1); + emul->port1 = plat->port + 1; + + return 0; +} + +static const struct dm_usb_ops sandbox_usb_hub_ops = { + .control = sandbox_hub_submit_control_msg, +}; + +static const struct udevice_id sandbox_usb_hub_ids[] = { + { .compatible = "sandbox,usb-hub" }, + { } +}; + +U_BOOT_DRIVER(usb_sandbox_hub) = { + .name = "usb_sandbox_hub", + .id = UCLASS_USB_EMUL, + .of_match = sandbox_usb_hub_ids, + .bind = sandbox_hub_bind, + .ops = &sandbox_usb_hub_ops, + .priv_auto = sizeof(struct sandbox_hub_priv), + .per_child_plat_auto = sizeof(struct sandbox_hub_plat), + .child_post_bind = sandbox_child_post_bind, +}; diff --git a/roms/u-boot/drivers/usb/emul/sandbox_keyb.c b/roms/u-boot/drivers/usb/emul/sandbox_keyb.c new file mode 100644 index 000000000..5ec1e98e4 --- /dev/null +++ b/roms/u-boot/drivers/usb/emul/sandbox_keyb.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <os.h> +#include <scsi.h> +#include <usb.h> + +/* + * 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, +}; + +/** + * sandbox_usb_keyb_add_string() - provide a USB scancode buffer + * + * @dev: the keyboard emulation device + * @scancode: scancode buffer with USB_KBD_BOOT_REPORT_SIZE bytes + */ +int sandbox_usb_keyb_add_string(struct udevice *dev, + const char scancode[USB_KBD_BOOT_REPORT_SIZE]) +{ + struct sandbox_keyb_priv *priv = dev_get_priv(dev); + int ret; + + ret = membuff_put(&priv->in, scancode, USB_KBD_BOOT_REPORT_SIZE); + if (ret != USB_KBD_BOOT_REPORT_SIZE) + 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, + bool nonblock) +{ + struct sandbox_keyb_priv *priv = dev_get_priv(dev); + uint8_t *data = buffer; + + memset(data, '\0', length); + if (length < USB_KBD_BOOT_REPORT_SIZE) + return 0; + + membuff_get(&priv->in, buffer, USB_KBD_BOOT_REPORT_SIZE); + + return 0; +} + +static int sandbox_keyb_bind(struct udevice *dev) +{ + struct sandbox_keyb_plat *plat = dev_get_plat(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, plat->keyb_strings, keyb_desc_list); +} + +static int sandbox_keyb_probe(struct udevice *dev) +{ + struct sandbox_keyb_priv *priv = dev_get_priv(dev); + + /* Provide an 80 character keyboard buffer */ + return membuff_new(&priv->in, 80 * USB_KBD_BOOT_REPORT_SIZE); +} + +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 = sizeof(struct sandbox_keyb_priv), + .plat_auto = sizeof(struct sandbox_keyb_plat), +}; diff --git a/roms/u-boot/drivers/usb/emul/usb-emul-uclass.c b/roms/u-boot/drivers/usb/emul/usb-emul-uclass.c new file mode 100644 index 000000000..f5d98b917 --- /dev/null +++ b/roms/u-boot/drivers/usb/emul/usb-emul-uclass.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <usb.h> +#include <dm/device-internal.h> + +static int copy_to_unicode(char *buff, int length, const char *str) +{ + int ptr; + int i; + + if (length < 2) + return 0; + buff[1] = USB_DT_STRING; + for (ptr = 2, i = 0; ptr + 1 < length && *str; i++, ptr += 2) { + buff[ptr] = str[i]; + buff[ptr + 1] = 0; + } + buff[0] = ptr; + + return ptr; +} + +static int usb_emul_get_string(struct usb_string *strings, int index, + char *buff, int length) +{ + if (index == 0) { + char *desc = buff; + + desc[0] = 4; + desc[1] = USB_DT_STRING; + desc[2] = 0x09; + desc[3] = 0x14; + return 4; + } else if (strings) { + struct usb_string *ptr; + + for (ptr = strings; ptr->s; ptr++) { + if (ptr->id == index) + return copy_to_unicode(buff, length, ptr->s); + } + } + + return -EINVAL; +} + +struct usb_generic_descriptor **usb_emul_find_descriptor( + struct usb_generic_descriptor **ptr, int type, int index) +{ + debug("%s: type=%x, index=%d\n", __func__, type, index); + for (; *ptr; ptr++) { + if ((*ptr)->bDescriptorType != type) + continue; + switch (type) { + case USB_DT_CONFIG: { + struct usb_config_descriptor *cdesc; + + cdesc = (struct usb_config_descriptor *)*ptr; + if (cdesc && cdesc->bConfigurationValue == index) + return ptr; + break; + } + default: + return ptr; + } + } + debug("%s: config ptr=%p\n", __func__, *ptr); + + return ptr; +} + +static int usb_emul_get_descriptor(struct usb_dev_plat *plat, int value, + void *buffer, int length) +{ + struct usb_generic_descriptor **ptr; + int type = value >> 8; + int index = value & 0xff; + int upto, todo; + + debug("%s: type=%d, index=%d, plat=%p\n", __func__, type, index, plat); + if (type == USB_DT_STRING) { + return usb_emul_get_string(plat->strings, index, buffer, + length); + } + + ptr = usb_emul_find_descriptor(plat->desc_list, type, index); + if (!ptr) { + debug("%s: Could not find descriptor type %d, index %d\n", + __func__, type, index); + return -ENOENT; + } + for (upto = 0; *ptr && upto < length; ptr++, upto += todo) { + todo = min(length - upto, (int)(*ptr)->bLength); + + memcpy(buffer + upto, *ptr, todo); + } + + return upto ? upto : length ? -EIO : 0; +} + +static int usb_emul_find_devnum(int devnum, int port1, struct udevice **emulp) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + *emulp = NULL; + ret = uclass_get(UCLASS_USB_EMUL, &uc); + if (ret) + return ret; + uclass_foreach_dev(dev, uc) { + struct usb_dev_plat *udev = dev_get_parent_plat(dev); + + /* + * devnum is initialzied to zero at the beginning of the + * enumeration process in usb_setup_device(). At this + * point, udev->devnum has not been assigned to any valid + * USB address either, so we can't rely on the comparison + * result between udev->devnum and devnum to select an + * emulator device. + */ + if (!devnum) { + struct usb_emul_plat *plat; + + /* + * If the parent is sandbox USB controller, we are + * the root hub. And there is only one root hub + * in the system. + */ + if (device_get_uclass_id(dev->parent) == UCLASS_USB) { + debug("%s: Found emulator '%s'\n", + __func__, dev->name); + *emulp = dev; + return 0; + } + + plat = dev_get_uclass_plat(dev); + if (plat->port1 == port1) { + debug("%s: Found emulator '%s', port %d\n", + __func__, dev->name, port1); + *emulp = dev; + return 0; + } + } else if (udev->devnum == devnum) { + debug("%s: Found emulator '%s', addr %d\n", __func__, + dev->name, udev->devnum); + *emulp = dev; + return 0; + } + } + + debug("%s: No emulator found, addr %d\n", __func__, devnum); + return -ENOENT; +} + +int usb_emul_find(struct udevice *bus, ulong pipe, int port1, + struct udevice **emulp) +{ + int devnum = usb_pipedevice(pipe); + + return usb_emul_find_devnum(devnum, port1, emulp); +} + +int usb_emul_find_for_dev(struct udevice *dev, struct udevice **emulp) +{ + struct usb_dev_plat *udev = dev_get_parent_plat(dev); + + return usb_emul_find_devnum(udev->devnum, 0, emulp); +} + +int usb_emul_control(struct udevice *emul, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + struct devrequest *setup) +{ + struct dm_usb_ops *ops = usb_get_emul_ops(emul); + struct usb_dev_plat *plat; + int ret; + + /* We permit getting the descriptor before we are probed */ + plat = dev_get_parent_plat(emul); + if (!ops->control) + return -ENOSYS; + debug("%s: dev=%s\n", __func__, emul->name); + if (pipe == usb_rcvctrlpipe(udev, 0)) { + switch (setup->request) { + case USB_REQ_GET_DESCRIPTOR: { + return usb_emul_get_descriptor(plat, setup->value, + buffer, length); + } + default: + ret = device_probe(emul); + if (ret) + return ret; + return ops->control(emul, udev, pipe, buffer, length, + setup); + } + } else if (pipe == usb_snddefctrl(udev)) { + switch (setup->request) { + case USB_REQ_SET_ADDRESS: + debug(" ** set address %s %d\n", emul->name, + setup->value); + plat->devnum = setup->value; + return 0; + default: + debug("requestsend =%x\n", setup->request); + break; + } + } else if (pipe == usb_sndctrlpipe(udev, 0)) { + switch (setup->request) { + case USB_REQ_SET_CONFIGURATION: + plat->configno = setup->value; + return 0; + default: + ret = device_probe(emul); + if (ret) + return ret; + return ops->control(emul, udev, pipe, buffer, length, + setup); + } + } + debug("pipe=%lx\n", pipe); + + return -EIO; +} + +int usb_emul_bulk(struct udevice *emul, struct usb_device *udev, + unsigned long pipe, void *buffer, int length) +{ + struct dm_usb_ops *ops = usb_get_emul_ops(emul); + int ret; + + /* We permit getting the descriptor before we are probed */ + if (!ops->bulk) + return -ENOSYS; + debug("%s: dev=%s\n", __func__, emul->name); + ret = device_probe(emul); + if (ret) + return ret; + return ops->bulk(emul, udev, pipe, buffer, length); +} + +int usb_emul_int(struct udevice *emul, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, int interval, + bool nonblock) +{ + struct dm_usb_ops *ops = usb_get_emul_ops(emul); + + if (!ops->interrupt) + return -ENOSYS; + debug("%s: dev=%s\n", __func__, emul->name); + + return ops->interrupt(emul, udev, pipe, buffer, length, interval, + nonblock); +} + +int usb_emul_setup_device(struct udevice *dev, struct usb_string *strings, + void **desc_list) +{ + struct usb_dev_plat *plat = dev_get_parent_plat(dev); + struct usb_generic_descriptor **ptr; + struct usb_config_descriptor *cdesc; + int upto; + + plat->strings = strings; + plat->desc_list = (struct usb_generic_descriptor **)desc_list; + + /* Fill in wTotalLength for each configuration descriptor */ + ptr = plat->desc_list; + for (cdesc = NULL, upto = 0; *ptr; upto += (*ptr)->bLength, ptr++) { + debug(" - upto=%d, type=%d\n", upto, (*ptr)->bDescriptorType); + if ((*ptr)->bDescriptorType == USB_DT_CONFIG) { + if (cdesc) { + cdesc->wTotalLength = upto; + debug("%s: config %d length %d\n", __func__, + cdesc->bConfigurationValue, + cdesc->bLength); + } + cdesc = (struct usb_config_descriptor *)*ptr; + upto = 0; + } + } + if (cdesc) { + cdesc->wTotalLength = upto; + debug("%s: config %d length %d\n", __func__, + cdesc->bConfigurationValue, cdesc->wTotalLength); + } + + return 0; +} + +UCLASS_DRIVER(usb_emul) = { + .id = UCLASS_USB_EMUL, + .name = "usb_emul", + .post_bind = dm_scan_fdt_dev, + .per_device_plat_auto = sizeof(struct usb_emul_plat), + .per_child_auto = sizeof(struct usb_device), + .per_child_plat_auto = sizeof(struct usb_dev_plat), +}; |