From patchwork Wed Mar 15 07:56:03 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eddie Cai X-Patchwork-Id: 739044 X-Patchwork-Delegate: sjg@chromium.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 3vjkTk550lz9ryv for ; Wed, 15 Mar 2017 18:57:14 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="TMW5tfGY"; dkim-atps=neutral Received: by lists.denx.de (Postfix, from userid 105) id AA2C4C21C5F; Wed, 15 Mar 2017 07:56:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=-0.0 required=5.0 tests=FREEMAIL_FROM, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 160F8C21C8C; Wed, 15 Mar 2017 07:56:52 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 68DAAC21C36; Wed, 15 Mar 2017 07:56:49 +0000 (UTC) Received: from mail-pg0-f68.google.com (mail-pg0-f68.google.com [74.125.83.68]) by lists.denx.de (Postfix) with ESMTPS id E28B9C21C7C for ; Wed, 15 Mar 2017 07:56:46 +0000 (UTC) Received: by mail-pg0-f68.google.com with SMTP id b5so1415158pgg.1 for ; Wed, 15 Mar 2017 00:56:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=6rFGkho9+sVFnRlqiGa9Ka7nBpyd9Rwfz1G3UvPT/IY=; b=TMW5tfGYORalZ8vOfnGXgA0Lzrq3s2k9ZU5M64e3zoDa1wQq4gmEVvoIYJt0fr07bP k4+lBpNGAkkPDdE8CTUpM1IwdQMi4hpYxtj1I4HOF2dmgzEirK5MPyDOKSu1VpBITffv uo+7VEYyqT2maU/MQi43tCJ6IK0sg0bts27tF2n5LNN/YymWgg21BVdhBQaDdrSvCAew dWdxg+2tLA1xEGRraNsnuELoVQiuD7wXX0cbGNvkm46KQpHYLPxHXIS2HwHbX0rW40LD rbEC2lW55VOa9pcofdGuHE2asu7XrKh5tKoNV4XwrEZSj0kHVd4zbMxv+ulkUbnhpO1Z slHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=6rFGkho9+sVFnRlqiGa9Ka7nBpyd9Rwfz1G3UvPT/IY=; b=POt7hxol2vJnRh3EFyyTMwR/Ph72+CeDE2Y6RmXzwM5F+ypPD2uolETRg2nPUTrYQG uERlSVs1j6HsWt0LCzSuCg0EVL14bp+P8JI32kVBR+oLi5nAb208It0yeDuOqA9cmkEO qiM5CCEeEHTw2SUJSSe1TNoiGgFeCVppOT7GuAapsuFOx5GC+mLNVabffG2ivKHTQrN0 JW8u8VUL8hop5rbIsWDLEjoPoP+PWsR7A3iN9UWbElc4NykqGz0ZzX5V+O/ijtYYD6Gd /ZmsxL8h4mEDmegvvys/sr3k7YwmXOYoJ10365H+MFcgCkXWN9CMoevstzi3Sg94Ikv7 Lc/g== X-Gm-Message-State: AFeK/H1CnVF9sn6EGf/J21wOXvtpSdkTN/HnGfoW2cNYF7t77wLaYAYn7sTze59mQw5BJQ== X-Received: by 10.98.78.8 with SMTP id c8mr1385140pfb.155.1489564605389; Wed, 15 Mar 2017 00:56:45 -0700 (PDT) Received: from localhost.localdomain ([103.29.142.67]) by smtp.gmail.com with ESMTPSA id 10sm2323770pfs.113.2017.03.15.00.56.41 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 15 Mar 2017 00:56:44 -0700 (PDT) From: Eddie Cai To: lukma@denx.de, marex@denx.de, sjg@chromium.org, kever.yang@rock-chips.com, william.wu@rock-chips.com Date: Wed, 15 Mar 2017 15:56:03 +0800 Message-Id: <1489564565-20856-2-git-send-email-eddie.cai.linux@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1489564565-20856-1-git-send-email-eddie.cai.linux@gmail.com> References: <1489564565-20856-1-git-send-email-eddie.cai.linux@gmail.com> Cc: u-boot@lists.denx.de Subject: [U-Boot] [U-Boot 1/3] drivers: usb: gadget: add the rockusb gadget X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" this patch implement fastboot protocol on the device side. this is based on USB download gadget infrastructure. the rockusb function implements the rd, wl, rid commands. it can work with rkdeveloptool; https://github.com/rockchip-linux/rkdeveloptool Signed-off-by: Eddie Cai --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_rockusb.c | 801 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 802 insertions(+) create mode 100644 drivers/usb/gadget/f_rockusb.c diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 0fbbb7c..0eb27a3 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o obj-$(CONFIG_USB_FUNCTION_DFU) += f_dfu.o obj-$(CONFIG_USB_FUNCTION_MASS_STORAGE) += f_mass_storage.o obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o +obj-$(CONFIG_USB_FUNCTION_ROCKUSB) += f_rockusb.o endif endif ifdef CONFIG_USB_ETHER diff --git a/drivers/usb/gadget/f_rockusb.c b/drivers/usb/gadget/f_rockusb.c new file mode 100644 index 0000000..a67cb35 --- /dev/null +++ b/drivers/usb/gadget/f_rockusb.c @@ -0,0 +1,801 @@ +/* + * (C) Copyright 2017 + * + * Eddie Cai + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ROCKUSB_VERSION "0.1" + +#define ROCKUSB_INTERFACE_CLASS 0xff +#define ROCKUSB_INTERFACE_SUB_CLASS 0x06 +#define ROCKUSB_INTERFACE_PROTOCOL 0x05 + +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200) +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040) +#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040) + +#define EP_BUFFER_SIZE 4096 +/* + * EP_BUFFER_SIZE must always be an integral multiple of maxpacket size + * (64 or 512 or 1024), else we break on certain controllers like DWC3 + * that expect bulk OUT requests to be divisible by maxpacket size. + */ + +struct f_rockusb { + struct usb_function usb_function; + + /* IN/OUT EP's and corresponding requests */ + struct usb_ep *in_ep, *out_ep; + struct usb_request *in_req, *out_req; +}; + +static inline struct f_rockusb *func_to_rockusb(struct usb_function *f) +{ + return container_of(f, struct f_rockusb, usb_function); +} + +static struct f_rockusb *rockusb_func; + +static struct usb_endpoint_descriptor fs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_endpoint_descriptor fs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_endpoint_descriptor hs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_interface_descriptor interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = ROCKUSB_INTERFACE_CLASS, + .bInterfaceSubClass = ROCKUSB_INTERFACE_SUB_CLASS, + .bInterfaceProtocol = ROCKUSB_INTERFACE_PROTOCOL, +}; + +static struct usb_descriptor_header *rkusb_fs_function[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&fs_ep_in, + (struct usb_descriptor_header *)&fs_ep_out, +}; + +static struct usb_descriptor_header *rkusb_hs_function[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&hs_ep_in, + (struct usb_descriptor_header *)&hs_ep_out, + NULL, +}; + +static struct usb_endpoint_descriptor * +rkusb_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +/* + * static strings, in UTF-8 + */ +static const char rkusb_name[] = "Rockchip Rockusb"; + +static struct usb_string rkusb_string_defs[] = { + [0].s = rkusb_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_rkusb = { + .language = 0x0409, /* en-us */ + .strings = rkusb_string_defs, +}; + +static struct usb_gadget_strings *rkusb_strings[] = { + &stringtab_rkusb, + NULL, +}; + +static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); +static int rockusb_tx_write_csw(u32 Tag, int residue, u8 Status, int size); + +static void rockusb_complete(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + if (!status) + return; + debug("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual); +} + +static int rockusb_bind(struct usb_configuration *c, struct usb_function *f) +{ + int id; + struct usb_gadget *gadget = c->cdev->gadget; + struct f_rockusb *f_rkusb = func_to_rockusb(f); + const char *s; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + interface_desc.bInterfaceNumber = id; + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + + rkusb_string_defs[0].id = id; + interface_desc.iInterface = id; + + f_rkusb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in); + if (!f_rkusb->in_ep) + return -ENODEV; + f_rkusb->in_ep->driver_data = c->cdev; + + f_rkusb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out); + if (!f_rkusb->out_ep) + return -ENODEV; + f_rkusb->out_ep->driver_data = c->cdev; + + f->descriptors = rkusb_fs_function; + + if (gadget_is_dualspeed(gadget)) { + hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; + hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + f->hs_descriptors = rkusb_hs_function; + } + + s = getenv("serial#"); + if (s) + g_dnl_set_serialnumber((char *)s); + + return 0; +} + +static void rockusb_unbind(struct usb_configuration *c, struct usb_function *f) +{ + memset(rockusb_func, 0, sizeof(*rockusb_func)); +} + +static void rockusb_disable(struct usb_function *f) +{ + struct f_rockusb *f_rkusb = func_to_rockusb(f); + + usb_ep_disable(f_rkusb->out_ep); + usb_ep_disable(f_rkusb->in_ep); + + if (f_rkusb->out_req) { + free(f_rkusb->out_req->buf); + usb_ep_free_request(f_rkusb->out_ep, f_rkusb->out_req); + f_rkusb->out_req = NULL; + } + if (f_rkusb->in_req) { + free(f_rkusb->in_req->buf); + usb_ep_free_request(f_rkusb->in_ep, f_rkusb->in_req); + f_rkusb->in_req = NULL; + } +} + +static struct usb_request *rockusb_start_ep(struct usb_ep *ep) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, 0); + if (!req) + return NULL; + + req->length = EP_BUFFER_SIZE; + req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + memset(req->buf, 0, req->length); + return req; +} + +static int rockusb_set_alt(struct usb_function *f, + unsigned interface, unsigned alt) +{ + int ret; + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct f_rockusb *f_rkusb = func_to_rockusb(f); + const struct usb_endpoint_descriptor *d; + + debug("%s: func: %s intf: %d alt: %d\n", + __func__, f->name, interface, alt); + + d = rkusb_ep_desc(gadget, &fs_ep_out, &hs_ep_out); + ret = usb_ep_enable(f_rkusb->out_ep, d); + if (ret) { + puts("failed to enable out ep\n"); + return ret; + } + + f_rkusb->out_req = rockusb_start_ep(f_rkusb->out_ep); + if (!f_rkusb->out_req) { + puts("failed to alloc out req\n"); + ret = -EINVAL; + goto err; + } + f_rkusb->out_req->complete = rx_handler_command; + + d = rkusb_ep_desc(gadget, &fs_ep_in, &hs_ep_in); + ret = usb_ep_enable(f_rkusb->in_ep, d); + if (ret) { + puts("failed to enable in ep\n"); + goto err; + } + + f_rkusb->in_req = rockusb_start_ep(f_rkusb->in_ep); + if (!f_rkusb->in_req) { + puts("failed alloc req in\n"); + ret = -EINVAL; + goto err; + } + f_rkusb->in_req->complete = rockusb_complete; + + ret = usb_ep_queue(f_rkusb->out_ep, f_rkusb->out_req, 0); + if (ret) + goto err; + + return 0; +err: + rockusb_disable(f); + return ret; +} + +static int rockusb_add(struct usb_configuration *c) +{ + struct f_rockusb *f_rkusb = rockusb_func; + int status; + + debug("%s: cdev: 0x%p\n", __func__, c->cdev); + + if (!f_rkusb) { + f_rkusb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_rkusb)); + if (!f_rkusb) + return -ENOMEM; + + rockusb_func = f_rkusb; + memset(f_rkusb, 0, sizeof(*f_rkusb)); + } + + f_rkusb->usb_function.name = "f_rockusb"; + f_rkusb->usb_function.bind = rockusb_bind; + f_rkusb->usb_function.unbind = rockusb_unbind; + f_rkusb->usb_function.set_alt = rockusb_set_alt; + f_rkusb->usb_function.disable = rockusb_disable; + f_rkusb->usb_function.strings = rkusb_strings; + + status = usb_add_function(c, &f_rkusb->usb_function); + if (status) { + free(f_rkusb); + rockusb_func = f_rkusb; + } + return status; +} + +char *rockusb_dev_type = 0; +int rockusb_dev_index = 0; + +void rockusb_dev_init(char *dev_type, int dev_index) +{ + rockusb_dev_type = dev_type; + rockusb_dev_index = dev_index; +} + +DECLARE_GADGET_BIND_CALLBACK(usb_dnl_rockusb, rockusb_add); + +static int rockusb_tx_write(const char *buffer, unsigned int buffer_size) +{ + struct usb_request *in_req = rockusb_func->in_req; + int ret; + + memcpy(in_req->buf, buffer, buffer_size); + in_req->length = buffer_size; + usb_ep_dequeue(rockusb_func->in_ep, in_req); + ret = usb_ep_queue(rockusb_func->in_ep, in_req, 0); + if (ret) + printf("Error %d on queue\n", ret); + return 0; +} + +static int rockusb_tx_write_str(const char *buffer) +{ + return rockusb_tx_write(buffer, strlen(buffer)); +} + +/* Bulk-only data structures */ + +/* Command Block Wrapper */ +struct fsg_bulk_cb_wrap { + __le32 Signature; /* Contains 'USBC' */ + u32 Tag; /* Unique per command id */ + __le32 DataTransferLength; /* Size of the data */ + u8 Flags; /* Direction in bit 7 */ + u8 Lun; /* LUN (normally 0) */ + u8 Length; /* Of the CDB, <= MAX_COMMAND_SIZE */ + u8 CDB[16]; /* Command Data Block */ +}; + +#define USB_BULK_CB_WRAP_LEN 31 +#define USB_BULK_CB_SIG 0x43425355 /* Spells out USBC */ +#define USB_BULK_IN_FLAG 0x80 + +/* Command Status Wrapper */ +struct bulk_cs_wrap { + __le32 Signature; /* Should = 'USBS' */ + u32 Tag; /* Same as original command */ + __le32 Residue; /* Amount not transferred */ + u8 Status; /* See below */ +}; + +#define USB_BULK_CS_WRAP_LEN 13 +#define USB_BULK_CS_SIG 0x53425355 /* Spells out 'USBS' */ +#define USB_STATUS_PASS 0 +#define USB_STATUS_FAIL 1 +#define USB_STATUS_PHASE_ERROR 2 + + +#define CSW_GOOD 0x00 +#define CSW_FAIL 0x01 + +static void printcbw(char *buf) +{ + struct fsg_bulk_cb_wrap cbw __attribute__((aligned(ARCH_DMA_MINALIGN))); + + memcpy((char*)&cbw, buf, USB_BULK_CB_WRAP_LEN); + + debug("cbw: Signature:%x\n", cbw.Signature); + debug("cbw: Tag=%x\n", cbw.Tag); + debug("cbw: DataTransferLength=%d\n", cbw.DataTransferLength); + debug("cbw: Flags=%x\n", cbw.Flags); + debug("cbw: Lun=%d\n", cbw.Lun); + debug("cbw: Length=%d\n", cbw.Length); + debug("cbw: ucOperCode=%x\n", cbw.CDB[0]); + debug("cbw: ucReserved=%x\n", cbw.CDB[1]); + debug("cbw: dwAddress:%x %x %x %x\n", cbw.CDB[5], cbw.CDB[4], + cbw.CDB[3], cbw.CDB[2]); + debug("cbw: ucReserved2=%x\n", cbw.CDB[6]); + debug("cbw: usLength:%x %x\n", cbw.CDB[8], cbw.CDB[7]); + +} + +static void printcsw(char *buf) +{ + struct bulk_cs_wrap csw __attribute__((aligned(ARCH_DMA_MINALIGN))); + + memcpy((char*)&csw, buf, USB_BULK_CS_WRAP_LEN); + debug("csw: Signature:%x\n", csw.Signature); + debug("csw: Tag:%x\n", csw.Tag); + debug("csw: Residue:%x\n", csw.Residue); + debug("csw: Status:%x\n", csw.Status); + +} + +static int rockusb_tx_write_csw(u32 Tag, int residue, u8 Status, int size) +{ + struct bulk_cs_wrap csw __attribute__((aligned(ARCH_DMA_MINALIGN))); + csw.Signature = cpu_to_le32(USB_BULK_CS_SIG); + csw.Tag = Tag; + csw.Residue = cpu_to_be32(residue); + csw.Status = Status; + printcsw((char*)&csw); + return rockusb_tx_write((char*)&csw, size); +} + +int download_tag = 0; +int download_lba = 0; +static unsigned int download_size; +static unsigned int download_bytes; +struct blk_desc *download_desc = 0; + +static unsigned int rx_bytes_expected(struct usb_ep *ep) +{ + int rx_remain = download_size - download_bytes; + unsigned int rem; + unsigned int maxpacket = ep->maxpacket; + + if (rx_remain <= 0) + return 0; + else if (rx_remain > EP_BUFFER_SIZE) + return EP_BUFFER_SIZE; + + rem = rx_remain % maxpacket; + if (rem > 0) + rx_remain = rx_remain + (maxpacket - rem); + + return rx_remain; +} + +static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) +{ + unsigned int transfer_size = download_size - download_bytes; + const unsigned char *buffer = req->buf; + unsigned int buffer_size = req->actual; + + if(!download_desc){ + printf("rx_handler_dl_image blk_get_dev\n"); + download_desc = blk_get_dev(rockusb_dev_type, rockusb_dev_index); + if (!download_desc || download_desc->type == DEV_TYPE_UNKNOWN){ + error("invalid mmc device\n"); + rockusb_tx_write_csw(download_tag, 0, CSW_FAIL, + USB_BULK_CS_WRAP_LEN); + return; + } + } + + if (req->status != 0) { + printf("Bad status: %d\n", req->status); + rockusb_tx_write_csw(download_tag, 0, + CSW_FAIL, USB_BULK_CS_WRAP_LEN); + return; + } + + if (buffer_size < transfer_size) + transfer_size = buffer_size; + + memcpy((void *)CONFIG_FASTBOOT_BUF_ADDR + download_bytes, + buffer, transfer_size); + download_bytes += transfer_size; + + /* Check if transfer is done */ + if (download_bytes >= download_size) { + int blks = 0, blkcnt = download_size/512; + printf("download %d bytes finished, start writing to lba %x\n", + download_bytes, download_lba); + blks = blk_dwrite(download_desc, download_lba, blkcnt, + (char*)CONFIG_FASTBOOT_BUF_ADDR); + if (blks != blkcnt) { + error("failed writing to device %s: %d\n", + rockusb_dev_type, rockusb_dev_index); + rockusb_tx_write_csw(download_tag, 0, + CSW_FAIL, USB_BULK_CS_WRAP_LEN); + return; + } + + req->complete = rx_handler_command; + req->length = EP_BUFFER_SIZE; + download_size = 0; + printf("done\n"); + rockusb_tx_write_csw(download_tag, 0, CSW_GOOD, + USB_BULK_CS_WRAP_LEN); + } else { + req->length = rx_bytes_expected(ep); + } + + req->actual = 0; + usb_ep_queue(ep, req, 0); +} + +static void cb_test_unit_ready(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_bulk_cb_wrap cbw __attribute__((aligned(ARCH_DMA_MINALIGN))); + + memcpy((char*)&cbw, req->buf, USB_BULK_CB_WRAP_LEN); + + rockusb_tx_write_csw(cbw.Tag, cbw.DataTransferLength, + CSW_GOOD, USB_BULK_CS_WRAP_LEN); + +} + +static void cb_read_storage_id(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_bulk_cb_wrap cbw __attribute__((aligned(ARCH_DMA_MINALIGN))); + char emmc_id[] = "EMMC "; + + printf("cb_read_storage_id\n"); + memcpy((char*)&cbw, req->buf, USB_BULK_CB_WRAP_LEN); + rockusb_tx_write_str(emmc_id);//only support emmc now + rockusb_tx_write_csw(cbw.Tag, cbw.DataTransferLength, CSW_GOOD, + USB_BULK_CS_WRAP_LEN); +} + +static void cb_write_lba(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_bulk_cb_wrap cbw __attribute__((aligned(ARCH_DMA_MINALIGN))); + int sector_count; + + memcpy((char*)&cbw, req->buf, USB_BULK_CB_WRAP_LEN); + sector_count = (int)get_unaligned_be16(&cbw.CDB[7]); + download_lba = get_unaligned_be32(&cbw.CDB[2]); + download_size = sector_count * 512; + download_bytes = 0; + download_tag = cbw.Tag; + if ((0 == download_size) || (download_size>CONFIG_FASTBOOT_BUF_SIZE)){ + rockusb_tx_write_csw(cbw.Tag, cbw.DataTransferLength, + CSW_FAIL, USB_BULK_CS_WRAP_LEN); + } else { + req->complete = rx_handler_dl_image; + req->length = rx_bytes_expected(ep); + } +} + +static int reboot_flag; + +int __weak rkusb_set_reboot_flag(int flag) +{ + printf("rkusb_set_reboot_flag: %d\n", reboot_flag); + return -ENOSYS; +} + +static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) +{ + rkusb_set_reboot_flag(reboot_flag); + do_reset(NULL, 0, 0, NULL); +} + +static void cb_reboot(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_bulk_cb_wrap cbw __attribute__((aligned(ARCH_DMA_MINALIGN))); + reboot_flag = 0; + memcpy((char*)&cbw, req->buf, USB_BULK_CB_WRAP_LEN); + reboot_flag = cbw.CDB[1]; + rockusb_func->in_req->complete = compl_do_reset; + rockusb_tx_write_csw(cbw.Tag, cbw.DataTransferLength, CSW_GOOD, + USB_BULK_CS_WRAP_LEN); +} + +static void cb_not_support(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_bulk_cb_wrap cbw __attribute__((aligned(ARCH_DMA_MINALIGN))); + + memcpy((char*)&cbw, req->buf, USB_BULK_CB_WRAP_LEN); + printf("Rockusb command %x not support yet\n", cbw.CDB[0]); + rockusb_tx_write_csw(cbw.Tag, 0, CSW_FAIL, USB_BULK_CS_WRAP_LEN); +} + +#define RKUSB_STATUS_IDLE 0 +#define RKUSB_STATUS_CMD 1 +#define RKUSB_STATUS_RXDATA 2 +#define RKUSB_STATUS_TXDATA 3 +#define RKUSB_STATUS_CSW 4 +#define RKUSB_STATUS_RXDATA_PREPARE 5 +#define RKUSB_STATUS_TXDATA_PREPARE 6 + +typedef enum { + TEST_UNIT_READY = 0, + READ_FLASH_ID = 0x01, + TEST_BAD_BLOCK = 0x03, + READ_SECTOR = 0x04, + WRITE_SECTOR = 0x05, + ERASE_NORMAL = 0x06, + ERASE_FORCE = 0x0B, + READ_LBA = 0x14, + WRITE_LBA = 0x15, + ERASE_SYSTEMDISK = 0x16, + READ_SDRAM = 0x17, + WRITE_SDRAM = 0x18, + EXECUTE_SDRAM = 0x19, + READ_FLASH_INFO = 0x1A, + READ_CHIP_INFO = 0x1B, + SET_RESET_FLAG = 0x1E, + WRITE_EFUSE = 0x1F, + READ_EFUSE = 0x20, + READ_SPI_FLASH = 0x21, + WRITE_SPI_FLASH = 0x22, + WRITE_NEW_EFUSE = 0x23, + READ_NEW_EFUSE = 0x24, + DEVICE_RESET = 0xFF +} USB_OPERATION_CODE; + +#define K_FW_TEST_UNIT_READY 0x00 +#define K_FW_READ_FLASH_ID 0x01 +#define K_FW_SET_DEVICE_ID 0x02 +#define K_FW_TEST_BAD_BLOCK 0x03 +#define K_FW_READ_10 0x04 +#define K_FW_WRITE_10 0x05 +#define K_FW_ERASE_10 0x06 +#define K_FW_WRITE_SPARE 0x07 +#define K_FW_READ_SPARE 0x08 + +#define K_FW_ERASE_10_FORCE 0x0b +#define K_FW_GET_VERSION 0x0c + +#define K_FW_LBA_READ_10 0x14 +#define K_FW_LBA_WRITE_10 0x15 +#define K_FW_ERASE_SYS_DISK 0x16 +#define K_FW_SDRAM_READ_10 0x17 +#define K_FW_SDRAM_WRITE_10 0x18 +#define K_FW_SDRAM_EXECUTE 0x19 +#define K_FW_READ_FLASH_INFO 0x1A +#define K_FW_GET_CHIP_VER 0x1B +#define K_FW_LOW_FORMAT 0x1C +#define K_FW_SET_RESET_FLAG 0x1E +#define K_FW_SPI_READ_10 0x21 +#define K_FW_SPI_WRITE_10 0x22 + +#define K_FW_SESSION 0X30 +#define K_FW_RESET 0xff + +#define CBW_DIRECTION_OUT 0x00 +#define CBW_DIRECTION_IN 0x80 + +struct cmd_dispatch_info { + int cmd; + void (*cb)(struct usb_ep *ep, struct usb_request *req); +}; + +static const struct cmd_dispatch_info cmd_dispatch_info[] = { + { + .cmd = K_FW_TEST_UNIT_READY, + .cb = cb_test_unit_ready, + }, + { + .cmd = K_FW_READ_FLASH_ID, + .cb = cb_read_storage_id, + }, + { + .cmd = K_FW_SET_DEVICE_ID, + .cb = cb_not_support, + }, + { + .cmd = K_FW_TEST_BAD_BLOCK, + .cb = cb_not_support, + }, + { + .cmd = K_FW_READ_10, + .cb = cb_not_support, + }, + { + .cmd = K_FW_WRITE_10, + .cb = cb_not_support, + }, + { + .cmd = K_FW_ERASE_10, + .cb = cb_not_support, + }, + { + .cmd = K_FW_WRITE_SPARE, + .cb = cb_not_support, + }, + { + .cmd = K_FW_READ_SPARE, + .cb = cb_not_support, + }, + { + .cmd = K_FW_ERASE_10_FORCE, + .cb = cb_not_support, + }, + { + .cmd = K_FW_GET_VERSION, + .cb = cb_not_support, + }, + { + .cmd = K_FW_LBA_READ_10, + .cb = cb_not_support, + }, + { + .cmd = K_FW_LBA_WRITE_10, + .cb = cb_write_lba, + }, + { + .cmd = K_FW_ERASE_SYS_DISK, + .cb = cb_not_support, + }, + { + .cmd = K_FW_SDRAM_READ_10, + .cb = cb_not_support, + }, + { + .cmd = K_FW_SDRAM_WRITE_10, + .cb = cb_not_support, + }, + { + .cmd = K_FW_SDRAM_EXECUTE, + .cb = cb_not_support, + }, + { + .cmd = K_FW_READ_FLASH_INFO, + .cb = cb_not_support, + }, + { + .cmd = K_FW_GET_CHIP_VER, + .cb = cb_not_support, + }, + { + .cmd = K_FW_LOW_FORMAT, + .cb = cb_not_support, + }, + { + .cmd = K_FW_SET_RESET_FLAG, + .cb = cb_not_support, + }, + { + .cmd = K_FW_SPI_READ_10, + .cb = cb_not_support, + }, + { + .cmd = K_FW_SPI_WRITE_10, + .cb = cb_not_support, + }, + { + .cmd = K_FW_SESSION, + .cb = cb_not_support, + }, + { + .cmd = K_FW_RESET, + .cb = cb_reboot, + }, +}; + + + +static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) +{ + void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL; + struct fsg_bulk_cb_wrap cbw __attribute__((aligned(ARCH_DMA_MINALIGN))); + int i; + char *cmdbuf = req->buf; + + if (req->status != 0 || req->length == 0) + return; + + memcpy((char*)&cbw, req->buf, USB_BULK_CB_WRAP_LEN); + printcbw(req->buf); + + for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) { + if (cmd_dispatch_info[i].cmd == cbw.CDB[0]) { + func_cb = cmd_dispatch_info[i].cb; + break; + } + } + + if (!func_cb) { + error("unknown command: %s", (char*)req->buf); + rockusb_tx_write_str("FAILunknown command"); + } else { + if (req->actual < req->length) { + u8 *buf = (u8 *)req->buf; + buf[req->actual] = 0; + func_cb(ep, req); + } else { + error("buffer overflow"); + rockusb_tx_write_str("FAILbuffer overflow"); + } + } + + *cmdbuf = '\0'; + req->actual = 0; + usb_ep_queue(ep, req, 0); +}