123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- /*
- * (C) Copyright 2015 Google, Inc
- * Written by Simon Glass <sjg@chromium.org>
- *
- * SPDX-License-Identifier: GPL-2.0+
- */
- #include <common.h>
- #include <dm.h>
- #include <os.h>
- #include <scsi.h>
- #include <usb.h>
- DECLARE_GLOBAL_DATA_PTR;
- /*
- * 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_block *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_platdata(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_ofdata_to_platdata(struct udevice *dev)
- {
- struct sandbox_flash_plat *plat = dev_get_platdata(dev);
- const void *blob = gd->fdt_blob;
- plat->pathname = fdt_getprop(blob, dev->of_offset, "sandbox,filepath",
- NULL);
- return 0;
- }
- static int sandbox_flash_bind(struct udevice *dev)
- {
- struct sandbox_flash_plat *plat = dev_get_platdata(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, PACKET_SIZE_64, plat->flash_strings,
- flash_desc_list);
- }
- static int sandbox_flash_probe(struct udevice *dev)
- {
- struct sandbox_flash_plat *plat = dev_get_platdata(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,
- .ofdata_to_platdata = sandbox_flash_ofdata_to_platdata,
- .ops = &sandbox_usb_flash_ops,
- .priv_auto_alloc_size = sizeof(struct sandbox_flash_priv),
- .platdata_auto_alloc_size = sizeof(struct sandbox_flash_plat),
- };
|