summaryrefslogtreecommitdiffstats
path: root/src/roms/u-boot/drivers/usb/gadget
diff options
context:
space:
mode:
Diffstat (limited to 'src/roms/u-boot/drivers/usb/gadget')
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/Makefile38
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c1306
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h326
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/ci_udc.c760
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/ci_udc.h144
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/composite.c1091
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/config.c108
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/core.c669
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/designware_udc.c1019
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/ep0.c597
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/ep0.h26
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/epautoconf.c296
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/ether.c2555
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/f_dfu.c822
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/f_dfu.h87
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/f_mass_storage.c2783
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/f_thor.c1008
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/f_thor.h124
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/fotg210.c962
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/g_dnl.c264
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/gadget_chips.h227
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/mpc8xx_udc.c1386
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/ndis.h217
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/omap1510_udc.c1555
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c2047
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h150
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c703
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/regs-otg.h273
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/rndis.c1316
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/rndis.h260
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/s3c_udc_otg.c890
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c1480
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/storage_common.c624
-rw-r--r--src/roms/u-boot/drivers/usb/gadget/usbstring.c139
34 files changed, 26252 insertions, 0 deletions
diff --git a/src/roms/u-boot/drivers/usb/gadget/Makefile b/src/roms/u-boot/drivers/usb/gadget/Makefile
new file mode 100644
index 0000000..896c8d4
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/Makefile
@@ -0,0 +1,38 @@
+#
+# (C) Copyright 2000-2007
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-$(CONFIG_USB_GADGET) += epautoconf.o config.o usbstring.o
+obj-$(CONFIG_USB_ETHER) += epautoconf.o config.o usbstring.o
+
+# new USB gadget layer dependencies
+ifdef CONFIG_USB_GADGET
+obj-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o
+obj-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o
+obj-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o
+obj-$(CONFIG_CI_UDC) += ci_udc.o
+obj-$(CONFIG_THOR_FUNCTION) += f_thor.o
+obj-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o
+obj-$(CONFIG_DFU_FUNCTION) += f_dfu.o
+obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o
+endif
+ifdef CONFIG_USB_ETHER
+obj-y += ether.o
+obj-$(CONFIG_USB_ETH_RNDIS) += rndis.o
+obj-$(CONFIG_CI_UDC) += ci_udc.o
+obj-$(CONFIG_CPU_PXA25X) += pxa25x_udc.o
+else
+# Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE
+ifdef CONFIG_USB_DEVICE
+obj-y += core.o
+obj-y += ep0.o
+obj-$(CONFIG_DW_UDC) += designware_udc.o
+obj-$(CONFIG_OMAP1510) += omap1510_udc.o
+obj-$(CONFIG_OMAP1610) += omap1510_udc.o
+obj-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o
+obj-$(CONFIG_CPU_PXA27X) += pxa27x_udc.o
+endif
+endif
diff --git a/src/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c b/src/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c
new file mode 100644
index 0000000..c99208d
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c
@@ -0,0 +1,1306 @@
+/*
+ * Driver for the Atmel USBA high speed USB device controller
+ * [Original from Linux kernel: drivers/usb/gadget/atmel_usba_udc.c]
+ *
+ * Copyright (C) 2005-2013 Atmel Corporation
+ * Bo Shen <voice.shen@atmel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/errno.h>
+#include <asm/gpio.h>
+#include <asm/hardware.h>
+#include <linux/list.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/atmel_usba_udc.h>
+#include <malloc.h>
+#include <usb/lin_gadget_compat.h>
+
+#include "atmel_usba_udc.h"
+
+static int vbus_is_present(struct usba_udc *udc)
+{
+ /* No Vbus detection: Assume always present */
+ return 1;
+}
+
+static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
+{
+ unsigned int transaction_len;
+
+ transaction_len = req->req.length - req->req.actual;
+ req->last_transaction = 1;
+ if (transaction_len > ep->ep.maxpacket) {
+ transaction_len = ep->ep.maxpacket;
+ req->last_transaction = 0;
+ } else if (transaction_len == ep->ep.maxpacket && req->req.zero) {
+ req->last_transaction = 0;
+ }
+
+ DBG(DBG_QUEUE, "%s: submit_transaction, req %p (length %d)%s\n",
+ ep->ep.name, req, transaction_len,
+ req->last_transaction ? ", done" : "");
+
+ memcpy(ep->fifo, req->req.buf + req->req.actual, transaction_len);
+ usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+ req->req.actual += transaction_len;
+}
+
+static void submit_request(struct usba_ep *ep, struct usba_request *req)
+{
+ DBG(DBG_QUEUE, "%s: submit_request: req %p (length %d), dma: %d\n",
+ ep->ep.name, req, req->req.length, req->using_dma);
+
+ req->req.actual = 0;
+ req->submitted = 1;
+
+ next_fifo_transaction(ep, req);
+ if (req->last_transaction) {
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+ usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+ } else {
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+ usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+ }
+}
+
+static void submit_next_request(struct usba_ep *ep)
+{
+ struct usba_request *req;
+
+ if (list_empty(&ep->queue)) {
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY | USBA_RX_BK_RDY);
+ return;
+ }
+
+ req = list_entry(ep->queue.next, struct usba_request, queue);
+ if (!req->submitted)
+ submit_request(ep, req);
+}
+
+static void send_status(struct usba_udc *udc, struct usba_ep *ep)
+{
+ ep->state = STATUS_STAGE_IN;
+ usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+ usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+}
+
+static void receive_data(struct usba_ep *ep)
+{
+ struct usba_udc *udc = ep->udc;
+ struct usba_request *req;
+ unsigned long status;
+ unsigned int bytecount, nr_busy;
+ int is_complete = 0;
+
+ status = usba_ep_readl(ep, STA);
+ nr_busy = USBA_BFEXT(BUSY_BANKS, status);
+
+ DBG(DBG_QUEUE, "receive data: nr_busy=%u\n", nr_busy);
+
+ while (nr_busy > 0) {
+ if (list_empty(&ep->queue)) {
+ usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+ break;
+ }
+ req = list_entry(ep->queue.next,
+ struct usba_request, queue);
+
+ bytecount = USBA_BFEXT(BYTE_COUNT, status);
+
+ if (status & USBA_SHORT_PACKET)
+ is_complete = 1;
+ if (req->req.actual + bytecount >= req->req.length) {
+ is_complete = 1;
+ bytecount = req->req.length - req->req.actual;
+ }
+
+ memcpy(req->req.buf + req->req.actual, ep->fifo, bytecount);
+ req->req.actual += bytecount;
+
+ usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+
+ if (is_complete) {
+ DBG(DBG_QUEUE, "%s: request done\n", ep->ep.name);
+ req->req.status = 0;
+ list_del_init(&req->queue);
+ usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+ spin_lock(&udc->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_unlock(&udc->lock);
+ }
+
+ status = usba_ep_readl(ep, STA);
+ nr_busy = USBA_BFEXT(BUSY_BANKS, status);
+
+ if (is_complete && ep_is_control(ep)) {
+ send_status(udc, ep);
+ break;
+ }
+ }
+}
+
+static void
+request_complete(struct usba_ep *ep, struct usba_request *req, int status)
+{
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+
+ DBG(DBG_GADGET | DBG_REQ, "%s: req %p complete: status %d, actual %u\n",
+ ep->ep.name, req, req->req.status, req->req.actual);
+
+ req->req.complete(&ep->ep, &req->req);
+}
+
+static void
+request_complete_list(struct usba_ep *ep, struct list_head *list, int status)
+{
+ struct usba_request *req, *tmp_req;
+
+ list_for_each_entry_safe(req, tmp_req, list, queue) {
+ list_del_init(&req->queue);
+ request_complete(ep, req, status);
+ }
+}
+
+static int
+usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+ struct usba_ep *ep = to_usba_ep(_ep);
+ struct usba_udc *udc = ep->udc;
+ unsigned long flags, ept_cfg, maxpacket;
+ unsigned int nr_trans;
+
+ DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc);
+
+ maxpacket = usb_endpoint_maxp(desc) & 0x7ff;
+
+ if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)
+ != ep->index) ||
+ ep->index == 0 ||
+ desc->bDescriptorType != USB_DT_ENDPOINT ||
+ maxpacket == 0 ||
+ maxpacket > ep->fifo_size) {
+ DBG(DBG_ERR, "ep_enable: Invalid argument");
+ return -EINVAL;
+ }
+
+ ep->is_isoc = 0;
+ ep->is_in = 0;
+
+ if (maxpacket <= 8)
+ ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
+ else
+ /* LSB is bit 1, not 0 */
+ ept_cfg = USBA_BF(EPT_SIZE, fls(maxpacket - 1) - 3);
+
+ DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n",
+ ep->ep.name, ept_cfg, maxpacket);
+
+ if (usb_endpoint_dir_in(desc)) {
+ ep->is_in = 1;
+ ept_cfg |= USBA_EPT_DIR_IN;
+ }
+
+ switch (usb_endpoint_type(desc)) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
+ ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (!ep->can_isoc) {
+ DBG(DBG_ERR, "ep_enable: %s is not isoc capable\n",
+ ep->ep.name);
+ return -EINVAL;
+ }
+
+ /*
+ * Bits 11:12 specify number of _additional_
+ * transactions per microframe.
+ */
+ nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1;
+ if (nr_trans > 3)
+ return -EINVAL;
+
+ ep->is_isoc = 1;
+ ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO);
+
+ /*
+ * Do triple-buffering on high-bandwidth iso endpoints.
+ */
+ if (nr_trans > 1 && ep->nr_banks == 3)
+ ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_TRIPLE);
+ else
+ ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
+ ept_cfg |= USBA_BF(NB_TRANS, nr_trans);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK);
+ ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT);
+ ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+ break;
+ }
+
+ spin_lock_irqsave(&ep->udc->lock, flags);
+
+ ep->desc = desc;
+ ep->ep.maxpacket = maxpacket;
+
+ usba_ep_writel(ep, CFG, ept_cfg);
+ usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);
+
+ usba_writel(udc, INT_ENB,
+ (usba_readl(udc, INT_ENB)
+ | USBA_BF(EPT_INT, 1 << ep->index)));
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index,
+ (unsigned long)usba_ep_readl(ep, CFG));
+ DBG(DBG_HW, "INT_ENB after init: %#08lx\n",
+ (unsigned long)usba_readl(udc, INT_ENB));
+
+ return 0;
+}
+
+static int usba_ep_disable(struct usb_ep *_ep)
+{
+ struct usba_ep *ep = to_usba_ep(_ep);
+ struct usba_udc *udc = ep->udc;
+ LIST_HEAD(req_list);
+ unsigned long flags;
+
+ DBG(DBG_GADGET, "ep_disable: %s\n", ep->ep.name);
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (!ep->desc) {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ /* REVISIT because this driver disables endpoints in
+ * reset_all_endpoints() before calling disconnect(),
+ * most gadget drivers would trigger this non-error ...
+ */
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN)
+ DBG(DBG_ERR, "ep_disable: %s not enabled\n",
+ ep->ep.name);
+ return -EINVAL;
+ }
+ ep->desc = NULL;
+
+ list_splice_init(&ep->queue, &req_list);
+ usba_ep_writel(ep, CFG, 0);
+ usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE);
+ usba_writel(udc, INT_ENB,
+ usba_readl(udc, INT_ENB) &
+ ~USBA_BF(EPT_INT, 1 << ep->index));
+
+ request_complete_list(ep, &req_list, -ESHUTDOWN);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static struct usb_request *
+usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct usba_request *req;
+
+ DBG(DBG_GADGET, "ep_alloc_request: %p, 0x%x\n", _ep, gfp_flags);
+
+ req = malloc(sizeof(struct usba_request));
+ if (!req)
+ return NULL;
+
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+static void
+usba_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct usba_request *req = to_usba_req(_req);
+
+ DBG(DBG_GADGET, "ep_free_request: %p, %p\n", _ep, _req);
+
+ free(req);
+}
+
+static int
+usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct usba_request *req = to_usba_req(_req);
+ struct usba_ep *ep = to_usba_ep(_ep);
+ struct usba_udc *udc = ep->udc;
+ unsigned long flags;
+ int ret;
+
+ DBG(DBG_GADGET | DBG_QUEUE | DBG_REQ, "%s: queue req %p, len %u\n",
+ ep->ep.name, req, _req->length);
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN ||
+ !ep->desc)
+ return -ESHUTDOWN;
+
+ req->submitted = 0;
+ req->using_dma = 0;
+ req->last_transaction = 0;
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ /* May have received a reset since last time we checked */
+ ret = -ESHUTDOWN;
+ spin_lock_irqsave(&udc->lock, flags);
+ if (ep->desc) {
+ list_add_tail(&req->queue, &ep->queue);
+
+ if ((!ep_is_control(ep) && ep->is_in) ||
+ (ep_is_control(ep) && (ep->state == DATA_STAGE_IN ||
+ ep->state == STATUS_STAGE_IN)))
+ usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+ else
+ usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY);
+
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return ret;
+}
+
+static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct usba_ep *ep = to_usba_ep(_ep);
+ struct usba_request *req = to_usba_req(_req);
+
+ DBG(DBG_GADGET | DBG_QUEUE, "ep_dequeue: %s, req %p\n",
+ ep->ep.name, req);
+
+ /*
+ * Errors should stop the queue from advancing until the
+ * completion function returns.
+ */
+ list_del_init(&req->queue);
+
+ request_complete(ep, req, -ECONNRESET);
+
+ /* Process the next request if any */
+ submit_next_request(ep);
+
+ return 0;
+}
+
+static int usba_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct usba_ep *ep = to_usba_ep(_ep);
+ unsigned long flags;
+ int ret = 0;
+
+ DBG(DBG_GADGET, "endpoint %s: %s HALT\n", ep->ep.name,
+ value ? "set" : "clear");
+
+ if (!ep->desc) {
+ DBG(DBG_ERR, "Attempted to halt uninitialized ep %s\n",
+ ep->ep.name);
+ return -ENODEV;
+ }
+
+ if (ep->is_isoc) {
+ DBG(DBG_ERR, "Attempted to halt isochronous ep %s\n",
+ ep->ep.name);
+ return -ENOTTY;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /*
+ * We can't halt IN endpoints while there are still data to be
+ * transferred
+ */
+ if (!list_empty(&ep->queue) ||
+ ((value && ep->is_in && (usba_ep_readl(ep, STA) &
+ USBA_BF(BUSY_BANKS, -1L))))) {
+ ret = -EAGAIN;
+ } else {
+ if (value)
+ usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL);
+ else
+ usba_ep_writel(ep, CLR_STA,
+ USBA_FORCE_STALL | USBA_TOGGLE_CLR);
+ usba_ep_readl(ep, STA);
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return ret;
+}
+
+static int usba_ep_fifo_status(struct usb_ep *_ep)
+{
+ struct usba_ep *ep = to_usba_ep(_ep);
+
+ return USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA));
+}
+
+static void usba_ep_fifo_flush(struct usb_ep *_ep)
+{
+ struct usba_ep *ep = to_usba_ep(_ep);
+ struct usba_udc *udc = ep->udc;
+
+ usba_writel(udc, EPT_RST, 1 << ep->index);
+}
+
+static const struct usb_ep_ops usba_ep_ops = {
+ .enable = usba_ep_enable,
+ .disable = usba_ep_disable,
+ .alloc_request = usba_ep_alloc_request,
+ .free_request = usba_ep_free_request,
+ .queue = usba_ep_queue,
+ .dequeue = usba_ep_dequeue,
+ .set_halt = usba_ep_set_halt,
+ .fifo_status = usba_ep_fifo_status,
+ .fifo_flush = usba_ep_fifo_flush,
+};
+
+static int usba_udc_get_frame(struct usb_gadget *gadget)
+{
+ struct usba_udc *udc = to_usba_udc(gadget);
+
+ return USBA_BFEXT(FRAME_NUMBER, usba_readl(udc, FNUM));
+}
+
+static int usba_udc_wakeup(struct usb_gadget *gadget)
+{
+ struct usba_udc *udc = to_usba_udc(gadget);
+ unsigned long flags;
+ u32 ctrl;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (udc->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
+ ctrl = usba_readl(udc, CTRL);
+ usba_writel(udc, CTRL, ctrl | USBA_REMOTE_WAKE_UP);
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return ret;
+}
+
+static int
+usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
+{
+ struct usba_udc *udc = to_usba_udc(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (is_selfpowered)
+ udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
+ else
+ udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops usba_udc_ops = {
+ .get_frame = usba_udc_get_frame,
+ .wakeup = usba_udc_wakeup,
+ .set_selfpowered = usba_udc_set_selfpowered,
+};
+
+static struct usb_endpoint_descriptor usba_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = cpu_to_le16(64),
+ /* FIXME: I have no idea what to put here */
+ .bInterval = 1,
+};
+
+/*
+ * Called with interrupts disabled and udc->lock held.
+ */
+static void reset_all_endpoints(struct usba_udc *udc)
+{
+ struct usba_ep *ep;
+ struct usba_request *req, *tmp_req;
+
+ usba_writel(udc, EPT_RST, ~0UL);
+
+ ep = to_usba_ep(udc->gadget.ep0);
+ list_for_each_entry_safe(req, tmp_req, &ep->queue, queue) {
+ list_del_init(&req->queue);
+ request_complete(ep, req, -ECONNRESET);
+ }
+
+ /* NOTE: normally, the next call to the gadget driver is in
+ * charge of disabling endpoints... usually disconnect().
+ * The exception would be entering a high speed test mode.
+ *
+ * FIXME remove this code ... and retest thoroughly.
+ */
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+ if (ep->desc) {
+ spin_unlock(&udc->lock);
+ usba_ep_disable(&ep->ep);
+ spin_lock(&udc->lock);
+ }
+ }
+}
+
+static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex)
+{
+ struct usba_ep *ep;
+
+ if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+ return to_usba_ep(udc->gadget.ep0);
+
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+ u8 bEndpointAddress;
+
+ if (!ep->desc)
+ continue;
+ bEndpointAddress = ep->desc->bEndpointAddress;
+ if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+ continue;
+ if ((bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)
+ == (wIndex & USB_ENDPOINT_NUMBER_MASK))
+ return ep;
+ }
+
+ return NULL;
+}
+
+/* Called with interrupts disabled and udc->lock held */
+static inline void set_protocol_stall(struct usba_udc *udc, struct usba_ep *ep)
+{
+ usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL);
+ ep->state = WAIT_FOR_SETUP;
+}
+
+static inline int is_stalled(struct usba_udc *udc, struct usba_ep *ep)
+{
+ if (usba_ep_readl(ep, STA) & USBA_FORCE_STALL)
+ return 1;
+ return 0;
+}
+
+static inline void set_address(struct usba_udc *udc, unsigned int addr)
+{
+ u32 regval;
+
+ DBG(DBG_BUS, "setting address %u...\n", addr);
+ regval = usba_readl(udc, CTRL);
+ regval = USBA_BFINS(DEV_ADDR, addr, regval);
+ usba_writel(udc, CTRL, regval);
+}
+
+static int do_test_mode(struct usba_udc *udc)
+{
+ static const char test_packet_buffer[] = {
+ /* JKJKJKJK * 9 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* JJKKJJKK * 8 */
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ /* JJKKJJKK * 8 */
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ /* JJJJJJJKKKKKKK * 8 */
+ 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ /* JJJJJJJK * 8 */
+ 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD,
+ /* {JKKKKKKK * 10}, JK */
+ 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E
+ };
+ struct usba_ep *ep;
+ int test_mode;
+
+ test_mode = udc->test_mode;
+
+ /* Start from a clean slate */
+ reset_all_endpoints(udc);
+
+ switch (test_mode) {
+ case 0x0100:
+ /* Test_J */
+ usba_writel(udc, TST, USBA_TST_J_MODE);
+ DBG(DBG_ALL, "Entering Test_J mode...\n");
+ break;
+ case 0x0200:
+ /* Test_K */
+ usba_writel(udc, TST, USBA_TST_K_MODE);
+ DBG(DBG_ALL, "Entering Test_K mode...\n");
+ break;
+ case 0x0300:
+ /*
+ * Test_SE0_NAK: Force high-speed mode and set up ep0
+ * for Bulk IN transfers
+ */
+ ep = &udc->usba_ep[0];
+ usba_writel(udc, TST,
+ USBA_BF(SPEED_CFG, USBA_SPEED_CFG_FORCE_HIGH));
+ usba_ep_writel(ep, CFG,
+ USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64)
+ | USBA_EPT_DIR_IN
+ | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK)
+ | USBA_BF(BK_NUMBER, 1));
+ if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) {
+ set_protocol_stall(udc, ep);
+ DBG(DBG_ALL, "Test_SE0_NAK: ep0 not mapped\n");
+ } else {
+ usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);
+ DBG(DBG_ALL, "Entering Test_SE0_NAK mode...\n");
+ }
+ break;
+ case 0x0400:
+ /* Test_Packet */
+ ep = &udc->usba_ep[0];
+ usba_ep_writel(ep, CFG,
+ USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64)
+ | USBA_EPT_DIR_IN
+ | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK)
+ | USBA_BF(BK_NUMBER, 1));
+ if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) {
+ set_protocol_stall(udc, ep);
+ DBG(DBG_ALL, "Test_Packet: ep0 not mapped\n");
+ } else {
+ usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);
+ usba_writel(udc, TST, USBA_TST_PKT_MODE);
+ memcpy(ep->fifo, test_packet_buffer,
+ sizeof(test_packet_buffer));
+ usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+ DBG(DBG_ALL, "Entering Test_Packet mode...\n");
+ }
+ break;
+ default:
+ DBG(DBG_ERR, "Invalid test mode: 0x%04x\n", test_mode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Avoid overly long expressions */
+static inline bool feature_is_dev_remote_wakeup(struct usb_ctrlrequest *crq)
+{
+ if (crq->wValue == cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP))
+ return true;
+ return false;
+}
+
+static inline bool feature_is_dev_test_mode(struct usb_ctrlrequest *crq)
+{
+ if (crq->wValue == cpu_to_le16(USB_DEVICE_TEST_MODE))
+ return true;
+ return false;
+}
+
+static inline bool feature_is_ep_halt(struct usb_ctrlrequest *crq)
+{
+ if (crq->wValue == cpu_to_le16(USB_ENDPOINT_HALT))
+ return true;
+ return false;
+}
+
+static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
+ struct usb_ctrlrequest *crq)
+{
+ int retval = 0;
+
+ switch (crq->bRequest) {
+ case USB_REQ_GET_STATUS: {
+ u16 status;
+
+ if (crq->bRequestType == (USB_DIR_IN | USB_RECIP_DEVICE)) {
+ status = cpu_to_le16(udc->devstatus);
+ } else if (crq->bRequestType
+ == (USB_DIR_IN | USB_RECIP_INTERFACE)) {
+ status = cpu_to_le16(0);
+ } else if (crq->bRequestType
+ == (USB_DIR_IN | USB_RECIP_ENDPOINT)) {
+ struct usba_ep *target;
+
+ target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+ if (!target)
+ goto stall;
+
+ status = 0;
+ if (is_stalled(udc, target))
+ status |= cpu_to_le16(1);
+ } else {
+ goto delegate;
+ }
+
+ /* Write directly to the FIFO. No queueing is done. */
+ if (crq->wLength != cpu_to_le16(sizeof(status)))
+ goto stall;
+ ep->state = DATA_STAGE_IN;
+ __raw_writew(status, ep->fifo);
+ usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+ break;
+ }
+
+ case USB_REQ_CLEAR_FEATURE: {
+ if (crq->bRequestType == USB_RECIP_DEVICE) {
+ if (feature_is_dev_remote_wakeup(crq))
+ udc->devstatus
+ &= ~(1 << USB_DEVICE_REMOTE_WAKEUP);
+ else
+ /* Can't CLEAR_FEATURE TEST_MODE */
+ goto stall;
+ } else if (crq->bRequestType == USB_RECIP_ENDPOINT) {
+ struct usba_ep *target;
+
+ if (crq->wLength != cpu_to_le16(0) ||
+ !feature_is_ep_halt(crq))
+ goto stall;
+ target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+ if (!target)
+ goto stall;
+
+ usba_ep_writel(target, CLR_STA, USBA_FORCE_STALL);
+ if (target->index != 0)
+ usba_ep_writel(target, CLR_STA,
+ USBA_TOGGLE_CLR);
+ } else {
+ goto delegate;
+ }
+
+ send_status(udc, ep);
+ break;
+ }
+
+ case USB_REQ_SET_FEATURE: {
+ if (crq->bRequestType == USB_RECIP_DEVICE) {
+ if (feature_is_dev_test_mode(crq)) {
+ send_status(udc, ep);
+ ep->state = STATUS_STAGE_TEST;
+ udc->test_mode = le16_to_cpu(crq->wIndex);
+ return 0;
+ } else if (feature_is_dev_remote_wakeup(crq)) {
+ udc->devstatus |= 1 << USB_DEVICE_REMOTE_WAKEUP;
+ } else {
+ goto stall;
+ }
+ } else if (crq->bRequestType == USB_RECIP_ENDPOINT) {
+ struct usba_ep *target;
+
+ if (crq->wLength != cpu_to_le16(0) ||
+ !feature_is_ep_halt(crq))
+ goto stall;
+
+ target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+ if (!target)
+ goto stall;
+
+ usba_ep_writel(target, SET_STA, USBA_FORCE_STALL);
+ } else {
+ goto delegate;
+ }
+
+ send_status(udc, ep);
+ break;
+ }
+
+ case USB_REQ_SET_ADDRESS:
+ if (crq->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE))
+ goto delegate;
+
+ set_address(udc, le16_to_cpu(crq->wValue));
+ send_status(udc, ep);
+ ep->state = STATUS_STAGE_ADDR;
+ break;
+
+ default:
+delegate:
+ spin_unlock(&udc->lock);
+ retval = udc->driver->setup(&udc->gadget, crq);
+ spin_lock(&udc->lock);
+ }
+
+ return retval;
+
+stall:
+ DBG(DBG_ALL, "%s: Invalid setup request: %02x.%02x v%04x i%04x l%d\n",
+ ep->ep.name, crq->bRequestType, crq->bRequest,
+ le16_to_cpu(crq->wValue), le16_to_cpu(crq->wIndex),
+ le16_to_cpu(crq->wLength));
+ set_protocol_stall(udc, ep);
+
+ return -1;
+}
+
+static void usba_control_irq(struct usba_udc *udc, struct usba_ep *ep)
+{
+ struct usba_request *req;
+ u32 epstatus;
+ u32 epctrl;
+
+restart:
+ epstatus = usba_ep_readl(ep, STA);
+ epctrl = usba_ep_readl(ep, CTL);
+
+ DBG(DBG_INT, "%s [%d]: s/%08x c/%08x\n",
+ ep->ep.name, ep->state, epstatus, epctrl);
+
+ req = NULL;
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next,
+ struct usba_request, queue);
+
+ if ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) {
+ if (req->submitted)
+ next_fifo_transaction(ep, req);
+ else
+ submit_request(ep, req);
+
+ if (req->last_transaction) {
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+ usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+ }
+ goto restart;
+ }
+ if ((epstatus & epctrl) & USBA_TX_COMPLETE) {
+ usba_ep_writel(ep, CLR_STA, USBA_TX_COMPLETE);
+
+ switch (ep->state) {
+ case DATA_STAGE_IN:
+ usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY);
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+ ep->state = STATUS_STAGE_OUT;
+ break;
+ case STATUS_STAGE_ADDR:
+ /* Activate our new address */
+ usba_writel(udc, CTRL, (usba_readl(udc, CTRL)
+ | USBA_FADDR_EN));
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+ ep->state = WAIT_FOR_SETUP;
+ break;
+ case STATUS_STAGE_IN:
+ if (req) {
+ list_del_init(&req->queue);
+ request_complete(ep, req, 0);
+ submit_next_request(ep);
+ }
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+ ep->state = WAIT_FOR_SETUP;
+ break;
+ case STATUS_STAGE_TEST:
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+ ep->state = WAIT_FOR_SETUP;
+ if (do_test_mode(udc))
+ set_protocol_stall(udc, ep);
+ break;
+ default:
+ DBG(DBG_ALL, "%s: TXCOMP: Invalid endpoint state %d\n",
+ ep->ep.name, ep->state);
+ set_protocol_stall(udc, ep);
+ break;
+ }
+
+ goto restart;
+ }
+ if ((epstatus & epctrl) & USBA_RX_BK_RDY) {
+ switch (ep->state) {
+ case STATUS_STAGE_OUT:
+ usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+ usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+
+ if (req) {
+ list_del_init(&req->queue);
+ request_complete(ep, req, 0);
+ }
+ ep->state = WAIT_FOR_SETUP;
+ break;
+
+ case DATA_STAGE_OUT:
+ receive_data(ep);
+ break;
+
+ default:
+ usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+ usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+ DBG(DBG_ALL, "%s: RXRDY: Invalid endpoint state %d\n",
+ ep->ep.name, ep->state);
+ set_protocol_stall(udc, ep);
+ break;
+ }
+
+ goto restart;
+ }
+ if (epstatus & USBA_RX_SETUP) {
+ union {
+ struct usb_ctrlrequest crq;
+ unsigned long data[2];
+ } crq;
+ unsigned int pkt_len;
+ int ret;
+
+ if (ep->state != WAIT_FOR_SETUP) {
+ /*
+ * Didn't expect a SETUP packet at this
+ * point. Clean up any pending requests (which
+ * may be successful).
+ */
+ int status = -EPROTO;
+
+ /*
+ * RXRDY and TXCOMP are dropped when SETUP
+ * packets arrive. Just pretend we received
+ * the status packet.
+ */
+ if (ep->state == STATUS_STAGE_OUT ||
+ ep->state == STATUS_STAGE_IN) {
+ usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+ status = 0;
+ }
+
+ if (req) {
+ list_del_init(&req->queue);
+ request_complete(ep, req, status);
+ }
+ }
+
+ pkt_len = USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA));
+ DBG(DBG_HW, "Packet length: %u\n", pkt_len);
+ if (pkt_len != sizeof(crq)) {
+ DBG(DBG_ALL, "udc: Invalid length %u (expected %zu)\n",
+ pkt_len, sizeof(crq));
+ set_protocol_stall(udc, ep);
+ return;
+ }
+
+ DBG(DBG_FIFO, "Copying ctrl request from 0x%p:\n", ep->fifo);
+ memcpy(crq.data, ep->fifo, sizeof(crq));
+
+ /* Free up one bank in the FIFO so that we can
+ * generate or receive a reply right away. */
+ usba_ep_writel(ep, CLR_STA, USBA_RX_SETUP);
+
+ if (crq.crq.bRequestType & USB_DIR_IN) {
+ /*
+ * The USB 2.0 spec states that "if wLength is
+ * zero, there is no data transfer phase."
+ * However, testusb #14 seems to actually
+ * expect a data phase even if wLength = 0...
+ */
+ ep->state = DATA_STAGE_IN;
+ } else {
+ if (crq.crq.wLength != cpu_to_le16(0))
+ ep->state = DATA_STAGE_OUT;
+ else
+ ep->state = STATUS_STAGE_IN;
+ }
+
+ ret = -1;
+ if (ep->index == 0) {
+ ret = handle_ep0_setup(udc, ep, &crq.crq);
+ } else {
+ spin_unlock(&udc->lock);
+ ret = udc->driver->setup(&udc->gadget, &crq.crq);
+ spin_lock(&udc->lock);
+ }
+
+ DBG(DBG_BUS, "req %02x.%02x, length %d, state %d, ret %d\n",
+ crq.crq.bRequestType, crq.crq.bRequest,
+ le16_to_cpu(crq.crq.wLength), ep->state, ret);
+
+ if (ret < 0) {
+ /* Let the host know that we failed */
+ set_protocol_stall(udc, ep);
+ }
+ }
+}
+
+static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep)
+{
+ struct usba_request *req;
+ u32 epstatus;
+ u32 epctrl;
+
+ epstatus = usba_ep_readl(ep, STA);
+ epctrl = usba_ep_readl(ep, CTL);
+
+ DBG(DBG_INT, "%s: interrupt, status: 0x%08x\n", ep->ep.name, epstatus);
+
+ while ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) {
+ DBG(DBG_BUS, "%s: TX PK ready\n", ep->ep.name);
+
+ if (list_empty(&ep->queue)) {
+ DBG(DBG_INT, "ep_irq: queue empty\n");
+ usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+ return;
+ }
+
+ req = list_entry(ep->queue.next, struct usba_request, queue);
+
+ if (req->submitted)
+ next_fifo_transaction(ep, req);
+ else
+ submit_request(ep, req);
+
+ if (req->last_transaction) {
+ list_del_init(&req->queue);
+ submit_next_request(ep);
+ request_complete(ep, req, 0);
+ }
+
+ epstatus = usba_ep_readl(ep, STA);
+ epctrl = usba_ep_readl(ep, CTL);
+ }
+
+ if ((epstatus & epctrl) & USBA_RX_BK_RDY) {
+ DBG(DBG_BUS, "%s: RX data ready\n", ep->ep.name);
+ receive_data(ep);
+ usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+ }
+}
+
+static int usba_udc_irq(struct usba_udc *udc)
+{
+ u32 status, ep_status;
+
+ spin_lock(&udc->lock);
+
+ status = usba_readl(udc, INT_STA);
+ DBG(DBG_INT, "irq, status=%#08x\n", status);
+
+ if (status & USBA_DET_SUSPEND) {
+ usba_writel(udc, INT_CLR, USBA_DET_SUSPEND);
+ DBG(DBG_BUS, "Suspend detected\n");
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
+ udc->driver && udc->driver->suspend) {
+ spin_unlock(&udc->lock);
+ udc->driver->suspend(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+ }
+
+ if (status & USBA_WAKE_UP) {
+ usba_writel(udc, INT_CLR, USBA_WAKE_UP);
+ DBG(DBG_BUS, "Wake Up CPU detected\n");
+ }
+
+ if (status & USBA_END_OF_RESUME) {
+ usba_writel(udc, INT_CLR, USBA_END_OF_RESUME);
+ DBG(DBG_BUS, "Resume detected\n");
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
+ udc->driver && udc->driver->resume) {
+ spin_unlock(&udc->lock);
+ udc->driver->resume(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+ }
+
+ ep_status = USBA_BFEXT(EPT_INT, status);
+ if (ep_status) {
+ int i;
+
+ for (i = 0; i < USBA_NR_ENDPOINTS; i++)
+ if (ep_status & (1 << i)) {
+ if (ep_is_control(&udc->usba_ep[i]))
+ usba_control_irq(udc, &udc->usba_ep[i]);
+ else
+ usba_ep_irq(udc, &udc->usba_ep[i]);
+ }
+ }
+
+ if (status & USBA_END_OF_RESET) {
+ struct usba_ep *ep0;
+
+ usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
+ reset_all_endpoints(udc);
+
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
+ udc->driver->disconnect) {
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ spin_unlock(&udc->lock);
+ udc->driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+
+ if (status & USBA_HIGH_SPEED)
+ udc->gadget.speed = USB_SPEED_HIGH;
+ else
+ udc->gadget.speed = USB_SPEED_FULL;
+
+ ep0 = &udc->usba_ep[0];
+ ep0->desc = &usba_ep0_desc;
+ ep0->state = WAIT_FOR_SETUP;
+ usba_ep_writel(ep0, CFG,
+ (USBA_BF(EPT_SIZE, EP0_EPT_SIZE)
+ | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL)
+ | USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE)));
+ usba_ep_writel(ep0, CTL_ENB,
+ USBA_EPT_ENABLE | USBA_RX_SETUP);
+ usba_writel(udc, INT_ENB,
+ (usba_readl(udc, INT_ENB)
+ | USBA_BF(EPT_INT, 1)
+ | USBA_DET_SUSPEND
+ | USBA_END_OF_RESUME));
+
+ /*
+ * Unclear why we hit this irregularly, e.g. in usbtest,
+ * but it's clearly harmless...
+ */
+ if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED))
+ DBG(DBG_ALL, "ODD: EP0 configuration is invalid!\n");
+ }
+
+ spin_unlock(&udc->lock);
+
+ return 0;
+}
+
+static int atmel_usba_start(struct usba_udc *udc)
+{
+ udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
+
+ udc->vbus_prev = 0;
+
+ /* If Vbus is present, enable the controller and wait for reset */
+ if (vbus_is_present(udc) && udc->vbus_prev == 0) {
+ usba_writel(udc, CTRL, USBA_ENABLE_MASK);
+ usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+ }
+
+ return 0;
+}
+
+static int atmel_usba_stop(struct usba_udc *udc)
+{
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ reset_all_endpoints(udc);
+
+ /* This will also disable the DP pullup */
+ usba_writel(udc, CTRL, USBA_DISABLE_MASK);
+
+ return 0;
+}
+
+static struct usba_udc controller = {
+ .regs = (unsigned *)ATMEL_BASE_UDPHS,
+ .fifo = (unsigned *)ATMEL_BASE_UDPHS_FIFO,
+ .gadget = {
+ .ops = &usba_udc_ops,
+ .ep_list = LIST_HEAD_INIT(controller.gadget.ep_list),
+ .speed = USB_SPEED_HIGH,
+ .is_dualspeed = 1,
+ .name = "atmel_usba_udc",
+ },
+};
+
+int usb_gadget_handle_interrupts(void)
+{
+ struct usba_udc *udc = &controller;
+
+ return usba_udc_irq(udc);
+}
+
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct usba_udc *udc = &controller;
+ int ret;
+
+ if (!driver || !driver->bind || !driver->setup) {
+ printf("bad paramter\n");
+ return -EINVAL;
+ }
+
+ if (udc->driver) {
+ printf("UDC already has a gadget driver\n");
+ return -EBUSY;
+ }
+
+ atmel_usba_start(udc);
+
+ udc->driver = driver;
+
+ ret = driver->bind(&udc->gadget);
+ if (ret) {
+ error("driver->bind() returned %d\n", ret);
+ udc->driver = NULL;
+ }
+
+ return ret;
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct usba_udc *udc = &controller;
+
+ if (!driver || !driver->unbind || !driver->disconnect) {
+ error("bad paramter\n");
+ return -EINVAL;
+ }
+
+ driver->disconnect(&udc->gadget);
+ driver->unbind(&udc->gadget);
+ udc->driver = NULL;
+
+ atmel_usba_stop(udc);
+
+ return 0;
+}
+
+static struct usba_ep *usba_udc_pdata(struct usba_platform_data *pdata,
+ struct usba_udc *udc)
+{
+ struct usba_ep *eps;
+ int i;
+
+ eps = malloc(sizeof(struct usba_ep) * pdata->num_ep);
+ if (!eps) {
+ error("failed to alloc eps\n");
+ return NULL;
+ }
+
+ udc->gadget.ep0 = &eps[0].ep;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+ INIT_LIST_HEAD(&eps[0].ep.ep_list);
+
+ for (i = 0; i < pdata->num_ep; i++) {
+ struct usba_ep *ep = &eps[i];
+
+ ep->ep_regs = udc->regs + USBA_EPT_BASE(i);
+ ep->dma_regs = udc->regs + USBA_DMA_BASE(i);
+ ep->fifo = udc->fifo + USBA_FIFO_BASE(i);
+ ep->ep.ops = &usba_ep_ops;
+ ep->ep.name = pdata->ep[i].name;
+ ep->ep.maxpacket = pdata->ep[i].fifo_size;
+ ep->fifo_size = ep->ep.maxpacket;
+ ep->udc = udc;
+ INIT_LIST_HEAD(&ep->queue);
+ ep->nr_banks = pdata->ep[i].nr_banks;
+ ep->index = pdata->ep[i].index;
+ ep->can_dma = pdata->ep[i].can_dma;
+ ep->can_isoc = pdata->ep[i].can_isoc;
+ if (i)
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+ };
+
+ return eps;
+}
+
+int usba_udc_probe(struct usba_platform_data *pdata)
+{
+ struct usba_udc *udc;
+
+ udc = &controller;
+
+ udc->usba_ep = usba_udc_pdata(pdata, udc);
+
+ return 0;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h b/src/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h
new file mode 100644
index 0000000..92e462d
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h
@@ -0,0 +1,326 @@
+/*
+ * Register definition for Atmel USBA high speed USB device controller
+ * [Original from Linux kernel: drivers/usb/gadget/atmel_usba_udc.h]
+ *
+ * Copyright (C) 2005-2013 Atmel Corporation
+ * Bo Shen <voice.shen@atmel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __LINUX_USB_GADGET_USBA_UDC_H__
+#define __LINUX_USB_GADGET_USBA_UDC_H__
+
+/* USB register offsets */
+#define USBA_CTRL 0x0000
+#define USBA_FNUM 0x0004
+#define USBA_INT_ENB 0x0010
+#define USBA_INT_STA 0x0014
+#define USBA_INT_CLR 0x0018
+#define USBA_EPT_RST 0x001c
+#define USBA_TST 0x00e0
+
+/* USB endpoint register offsets */
+#define USBA_EPT_CFG 0x0000
+#define USBA_EPT_CTL_ENB 0x0004
+#define USBA_EPT_CTL_DIS 0x0008
+#define USBA_EPT_CTL 0x000c
+#define USBA_EPT_SET_STA 0x0014
+#define USBA_EPT_CLR_STA 0x0018
+#define USBA_EPT_STA 0x001c
+
+/* USB DMA register offsets */
+#define USBA_DMA_NXT_DSC 0x0000
+#define USBA_DMA_ADDRESS 0x0004
+#define USBA_DMA_CONTROL 0x0008
+#define USBA_DMA_STATUS 0x000c
+
+/* Bitfields in CTRL */
+#define USBA_DEV_ADDR_OFFSET 0
+#define USBA_DEV_ADDR_SIZE 7
+#define USBA_FADDR_EN (1 << 7)
+#define USBA_EN_USBA (1 << 8)
+#define USBA_DETACH (1 << 9)
+#define USBA_REMOTE_WAKE_UP (1 << 10)
+#define USBA_PULLD_DIS (1 << 11)
+
+#if defined(CONFIG_AVR32)
+#define USBA_ENABLE_MASK USBA_EN_USBA
+#define USBA_DISABLE_MASK 0
+#elif defined(CONFIG_AT91FAMILY)
+#define USBA_ENABLE_MASK (USBA_EN_USBA | USBA_PULLD_DIS)
+#define USBA_DISABLE_MASK USBA_DETACH
+#endif /* CONFIG_ARCH_AT91 */
+
+/* Bitfields in FNUM */
+#define USBA_MICRO_FRAME_NUM_OFFSET 0
+#define USBA_MICRO_FRAME_NUM_SIZE 3
+#define USBA_FRAME_NUMBER_OFFSET 3
+#define USBA_FRAME_NUMBER_SIZE 11
+#define USBA_FRAME_NUM_ERROR (1 << 31)
+
+/* Bitfields in INT_ENB/INT_STA/INT_CLR */
+#define USBA_HIGH_SPEED (1 << 0)
+#define USBA_DET_SUSPEND (1 << 1)
+#define USBA_MICRO_SOF (1 << 2)
+#define USBA_SOF (1 << 3)
+#define USBA_END_OF_RESET (1 << 4)
+#define USBA_WAKE_UP (1 << 5)
+#define USBA_END_OF_RESUME (1 << 6)
+#define USBA_UPSTREAM_RESUME (1 << 7)
+#define USBA_EPT_INT_OFFSET 8
+#define USBA_EPT_INT_SIZE 16
+#define USBA_DMA_INT_OFFSET 24
+#define USBA_DMA_INT_SIZE 8
+
+/* Bitfields in EPT_RST */
+#define USBA_RST_OFFSET 0
+#define USBA_RST_SIZE 16
+
+/* Bitfields in USBA_TST */
+#define USBA_SPEED_CFG_OFFSET 0
+#define USBA_SPEED_CFG_SIZE 2
+#define USBA_TST_J_MODE (1 << 2)
+#define USBA_TST_K_MODE (1 << 3)
+#define USBA_TST_PKT_MODE (1 << 4)
+#define USBA_OPMODE2 (1 << 5)
+
+/* Bitfields in EPT_CFG */
+#define USBA_EPT_SIZE_OFFSET 0
+#define USBA_EPT_SIZE_SIZE 3
+#define USBA_EPT_DIR_IN (1 << 3)
+#define USBA_EPT_TYPE_OFFSET 4
+#define USBA_EPT_TYPE_SIZE 2
+#define USBA_BK_NUMBER_OFFSET 6
+#define USBA_BK_NUMBER_SIZE 2
+#define USBA_NB_TRANS_OFFSET 8
+#define USBA_NB_TRANS_SIZE 2
+#define USBA_EPT_MAPPED (1 << 31)
+
+/* Bitfields in EPT_CTL/EPT_CTL_ENB/EPT_CTL_DIS */
+#define USBA_EPT_ENABLE (1 << 0)
+#define USBA_AUTO_VALID (1 << 1)
+#define USBA_INTDIS_DMA (1 << 3)
+#define USBA_NYET_DIS (1 << 4)
+#define USBA_DATAX_RX (1 << 6)
+#define USBA_MDATA_RX (1 << 7)
+/* Bits 8-15 and 31 enable interrupts for respective bits in EPT_STA */
+#define USBA_BUSY_BANK_IE (1 << 18)
+
+/* Bitfields in EPT_SET_STA/EPT_CLR_STA/EPT_STA */
+#define USBA_FORCE_STALL (1 << 5)
+#define USBA_TOGGLE_CLR (1 << 6)
+#define USBA_TOGGLE_SEQ_OFFSET 6
+#define USBA_TOGGLE_SEQ_SIZE 2
+#define USBA_ERR_OVFLW (1 << 8)
+#define USBA_RX_BK_RDY (1 << 9)
+#define USBA_KILL_BANK (1 << 9)
+#define USBA_TX_COMPLETE (1 << 10)
+#define USBA_TX_PK_RDY (1 << 11)
+#define USBA_ISO_ERR_TRANS (1 << 11)
+#define USBA_RX_SETUP (1 << 12)
+#define USBA_ISO_ERR_FLOW (1 << 12)
+#define USBA_STALL_SENT (1 << 13)
+#define USBA_ISO_ERR_CRC (1 << 13)
+#define USBA_ISO_ERR_NBTRANS (1 << 13)
+#define USBA_NAK_IN (1 << 14)
+#define USBA_ISO_ERR_FLUSH (1 << 14)
+#define USBA_NAK_OUT (1 << 15)
+#define USBA_CURRENT_BANK_OFFSET 16
+#define USBA_CURRENT_BANK_SIZE 2
+#define USBA_BUSY_BANKS_OFFSET 18
+#define USBA_BUSY_BANKS_SIZE 2
+#define USBA_BYTE_COUNT_OFFSET 20
+#define USBA_BYTE_COUNT_SIZE 11
+#define USBA_SHORT_PACKET (1 << 31)
+
+/* Bitfields in DMA_CONTROL */
+#define USBA_DMA_CH_EN (1 << 0)
+#define USBA_DMA_LINK (1 << 1)
+#define USBA_DMA_END_TR_EN (1 << 2)
+#define USBA_DMA_END_BUF_EN (1 << 3)
+#define USBA_DMA_END_TR_IE (1 << 4)
+#define USBA_DMA_END_BUF_IE (1 << 5)
+#define USBA_DMA_DESC_LOAD_IE (1 << 6)
+#define USBA_DMA_BURST_LOCK (1 << 7)
+#define USBA_DMA_BUF_LEN_OFFSET 16
+#define USBA_DMA_BUF_LEN_SIZE 16
+
+/* Bitfields in DMA_STATUS */
+#define USBA_DMA_CH_ACTIVE (1 << 1)
+#define USBA_DMA_END_TR_ST (1 << 4)
+#define USBA_DMA_END_BUF_ST (1 << 5)
+#define USBA_DMA_DESC_LOAD_ST (1 << 6)
+
+/* Constants for SPEED_CFG */
+#define USBA_SPEED_CFG_NORMAL 0
+#define USBA_SPEED_CFG_FORCE_HIGH 2
+#define USBA_SPEED_CFG_FORCE_FULL 3
+
+/* Constants for EPT_SIZE */
+#define USBA_EPT_SIZE_8 0
+#define USBA_EPT_SIZE_16 1
+#define USBA_EPT_SIZE_32 2
+#define USBA_EPT_SIZE_64 3
+#define USBA_EPT_SIZE_128 4
+#define USBA_EPT_SIZE_256 5
+#define USBA_EPT_SIZE_512 6
+#define USBA_EPT_SIZE_1024 7
+
+/* Constants for EPT_TYPE */
+#define USBA_EPT_TYPE_CONTROL 0
+#define USBA_EPT_TYPE_ISO 1
+#define USBA_EPT_TYPE_BULK 2
+#define USBA_EPT_TYPE_INT 3
+
+/* Constants for BK_NUMBER */
+#define USBA_BK_NUMBER_ZERO 0
+#define USBA_BK_NUMBER_ONE 1
+#define USBA_BK_NUMBER_DOUBLE 2
+#define USBA_BK_NUMBER_TRIPLE 3
+
+/* Bit manipulation macros */
+#define USBA_BF(name, value) \
+ (((value) & ((1 << USBA_##name##_SIZE) - 1)) \
+ << USBA_##name##_OFFSET)
+#define USBA_BFEXT(name, value) \
+ (((value) >> USBA_##name##_OFFSET) \
+ & ((1 << USBA_##name##_SIZE) - 1))
+#define USBA_BFINS(name, value, old) \
+ (((old) & ~(((1 << USBA_##name##_SIZE) - 1) \
+ << USBA_##name##_OFFSET)) \
+ | USBA_BF(name, value))
+
+/* Register access macros */
+#define usba_readl(udc, reg) \
+ __raw_readl((udc)->regs + USBA_##reg)
+#define usba_writel(udc, reg, value) \
+ __raw_writel((value), (udc)->regs + USBA_##reg)
+#define usba_ep_readl(ep, reg) \
+ __raw_readl((ep)->ep_regs + USBA_EPT_##reg)
+#define usba_ep_writel(ep, reg, value) \
+ __raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
+#define usba_dma_readl(ep, reg) \
+ __raw_readl((ep)->dma_regs + USBA_DMA_##reg)
+#define usba_dma_writel(ep, reg, value) \
+ __raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
+
+/* Calculate base address for a given endpoint or DMA controller */
+#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20)
+#define USBA_DMA_BASE(x) (0x300 + (x) * 0x10)
+#define USBA_FIFO_BASE(x) ((x) << 16)
+
+/* Synth parameters */
+#define USBA_NR_ENDPOINTS 7
+
+#define EP0_FIFO_SIZE 64
+#define EP0_EPT_SIZE USBA_EPT_SIZE_64
+#define EP0_NR_BANKS 1
+
+#define DBG_ERR 0x0001 /* report all error returns */
+#define DBG_HW 0x0002 /* debug hardware initialization */
+#define DBG_GADGET 0x0004 /* calls to/from gadget driver */
+#define DBG_INT 0x0008 /* interrupts */
+#define DBG_BUS 0x0010 /* report changes in bus state */
+#define DBG_QUEUE 0x0020 /* debug request queue processing */
+#define DBG_FIFO 0x0040 /* debug FIFO contents */
+#define DBG_DMA 0x0080 /* debug DMA handling */
+#define DBG_REQ 0x0100 /* print out queued request length */
+#define DBG_ALL 0xffff
+#define DBG_NONE 0x0000
+
+#define DEBUG_LEVEL (DBG_ERR)
+
+#define DBG(level, fmt, ...) \
+ do { \
+ if ((level) & DEBUG_LEVEL) \
+ debug("udc: " fmt, ## __VA_ARGS__); \
+ } while (0)
+
+enum usba_ctrl_state {
+ WAIT_FOR_SETUP,
+ DATA_STAGE_IN,
+ DATA_STAGE_OUT,
+ STATUS_STAGE_IN,
+ STATUS_STAGE_OUT,
+ STATUS_STAGE_ADDR,
+ STATUS_STAGE_TEST,
+};
+
+struct usba_dma_desc {
+ dma_addr_t next;
+ dma_addr_t addr;
+ u32 ctrl;
+};
+
+struct usba_ep {
+ int state;
+ void *ep_regs;
+ void *dma_regs;
+ void *fifo;
+ struct usb_ep ep;
+ struct usba_udc *udc;
+
+ struct list_head queue;
+
+ u16 fifo_size;
+ u8 nr_banks;
+ u8 index;
+ unsigned int can_dma:1;
+ unsigned int can_isoc:1;
+ unsigned int is_isoc:1;
+ unsigned int is_in:1;
+
+ const struct usb_endpoint_descriptor *desc;
+};
+
+struct usba_request {
+ struct usb_request req;
+ struct list_head queue;
+
+ u32 ctrl;
+
+ unsigned int submitted:1;
+ unsigned int last_transaction:1;
+ unsigned int using_dma:1;
+ unsigned int mapped:1;
+};
+
+struct usba_udc {
+ void *regs;
+ void *fifo;
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct platform_device *pdev;
+ int irq;
+ int vbus_pin;
+ int vbus_pin_inverted;
+ int num_ep;
+ struct usba_ep *usba_ep;
+
+ u16 devstatus;
+
+ u16 test_mode;
+ int vbus_prev;
+};
+
+static inline struct usba_ep *to_usba_ep(struct usb_ep *ep)
+{
+ return container_of(ep, struct usba_ep, ep);
+}
+
+static inline struct usba_request *to_usba_req(struct usb_request *req)
+{
+ return container_of(req, struct usba_request, req);
+}
+
+static inline struct usba_udc *to_usba_udc(struct usb_gadget *gadget)
+{
+ return container_of(gadget, struct usba_udc, gadget);
+}
+
+#define ep_is_control(ep) ((ep)->index == 0)
+#define ep_is_idle(ep) ((ep)->state == EP_STATE_IDLE)
+
+#endif /* __LINUX_USB_GADGET_USBA_UDC_H */
diff --git a/src/roms/u-boot/drivers/usb/gadget/ci_udc.c b/src/roms/u-boot/drivers/usb/gadget/ci_udc.c
new file mode 100644
index 0000000..02d3fda
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/ci_udc.c
@@ -0,0 +1,760 @@
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ * Lei Wen <leiwen@marvell.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Back ported to the 8xx platform (from the 8260 platform) by
+ * Murray.Jensen@cmst.csiro.au, 27-Jan-01.
+ */
+
+#include <common.h>
+#include <command.h>
+#include <config.h>
+#include <net.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <linux/types.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <usb/ci_udc.h>
+#include "../host/ehci.h"
+#include "ci_udc.h"
+
+/*
+ * Check if the system has too long cachelines. If the cachelines are
+ * longer then 128b, the driver will not be able flush/invalidate data
+ * cache over separate QH entries. We use 128b because one QH entry is
+ * 64b long and there are always two QH list entries for each endpoint.
+ */
+#if ARCH_DMA_MINALIGN > 128
+#error This driver can not work on systems with caches longer than 128b
+#endif
+
+#ifndef DEBUG
+#define DBG(x...) do {} while (0)
+#else
+#define DBG(x...) printf(x)
+static const char *reqname(unsigned r)
+{
+ switch (r) {
+ case USB_REQ_GET_STATUS: return "GET_STATUS";
+ case USB_REQ_CLEAR_FEATURE: return "CLEAR_FEATURE";
+ case USB_REQ_SET_FEATURE: return "SET_FEATURE";
+ case USB_REQ_SET_ADDRESS: return "SET_ADDRESS";
+ case USB_REQ_GET_DESCRIPTOR: return "GET_DESCRIPTOR";
+ case USB_REQ_SET_DESCRIPTOR: return "SET_DESCRIPTOR";
+ case USB_REQ_GET_CONFIGURATION: return "GET_CONFIGURATION";
+ case USB_REQ_SET_CONFIGURATION: return "SET_CONFIGURATION";
+ case USB_REQ_GET_INTERFACE: return "GET_INTERFACE";
+ case USB_REQ_SET_INTERFACE: return "SET_INTERFACE";
+ default: return "*UNKNOWN*";
+ }
+}
+#endif
+
+static struct usb_endpoint_descriptor ep0_out_desc = {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+};
+
+static struct usb_endpoint_descriptor ep0_in_desc = {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+};
+
+static int ci_pullup(struct usb_gadget *gadget, int is_on);
+static int ci_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc);
+static int ci_ep_disable(struct usb_ep *ep);
+static int ci_ep_queue(struct usb_ep *ep,
+ struct usb_request *req, gfp_t gfp_flags);
+static struct usb_request *
+ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags);
+static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req);
+
+static struct usb_gadget_ops ci_udc_ops = {
+ .pullup = ci_pullup,
+};
+
+static struct usb_ep_ops ci_ep_ops = {
+ .enable = ci_ep_enable,
+ .disable = ci_ep_disable,
+ .queue = ci_ep_queue,
+ .alloc_request = ci_ep_alloc_request,
+ .free_request = ci_ep_free_request,
+};
+
+/* Init values for USB endpoints. */
+static const struct usb_ep ci_ep_init[2] = {
+ [0] = { /* EP 0 */
+ .maxpacket = 64,
+ .name = "ep0",
+ .ops = &ci_ep_ops,
+ },
+ [1] = { /* EP 1..n */
+ .maxpacket = 512,
+ .name = "ep-",
+ .ops = &ci_ep_ops,
+ },
+};
+
+static struct ci_drv controller = {
+ .gadget = {
+ .name = "ci_udc",
+ .ops = &ci_udc_ops,
+ .is_dualspeed = 1,
+ },
+};
+
+/**
+ * ci_get_qh() - return queue head for endpoint
+ * @ep_num: Endpoint number
+ * @dir_in: Direction of the endpoint (IN = 1, OUT = 0)
+ *
+ * This function returns the QH associated with particular endpoint
+ * and it's direction.
+ */
+static struct ept_queue_head *ci_get_qh(int ep_num, int dir_in)
+{
+ return &controller.epts[(ep_num * 2) + dir_in];
+}
+
+/**
+ * ci_get_qtd() - return queue item for endpoint
+ * @ep_num: Endpoint number
+ * @dir_in: Direction of the endpoint (IN = 1, OUT = 0)
+ *
+ * This function returns the QH associated with particular endpoint
+ * and it's direction.
+ */
+static struct ept_queue_item *ci_get_qtd(int ep_num, int dir_in)
+{
+ return controller.items[(ep_num * 2) + dir_in];
+}
+
+/**
+ * ci_flush_qh - flush cache over queue head
+ * @ep_num: Endpoint number
+ *
+ * This function flushes cache over QH for particular endpoint.
+ */
+static void ci_flush_qh(int ep_num)
+{
+ struct ept_queue_head *head = ci_get_qh(ep_num, 0);
+ const uint32_t start = (uint32_t)head;
+ const uint32_t end = start + 2 * sizeof(*head);
+
+ flush_dcache_range(start, end);
+}
+
+/**
+ * ci_invalidate_qh - invalidate cache over queue head
+ * @ep_num: Endpoint number
+ *
+ * This function invalidates cache over QH for particular endpoint.
+ */
+static void ci_invalidate_qh(int ep_num)
+{
+ struct ept_queue_head *head = ci_get_qh(ep_num, 0);
+ uint32_t start = (uint32_t)head;
+ uint32_t end = start + 2 * sizeof(*head);
+
+ invalidate_dcache_range(start, end);
+}
+
+/**
+ * ci_flush_qtd - flush cache over queue item
+ * @ep_num: Endpoint number
+ *
+ * This function flushes cache over qTD pair for particular endpoint.
+ */
+static void ci_flush_qtd(int ep_num)
+{
+ struct ept_queue_item *item = ci_get_qtd(ep_num, 0);
+ const uint32_t start = (uint32_t)item;
+ const uint32_t end_raw = start + 2 * sizeof(*item);
+ const uint32_t end = roundup(end_raw, ARCH_DMA_MINALIGN);
+
+ flush_dcache_range(start, end);
+}
+
+/**
+ * ci_invalidate_qtd - invalidate cache over queue item
+ * @ep_num: Endpoint number
+ *
+ * This function invalidates cache over qTD pair for particular endpoint.
+ */
+static void ci_invalidate_qtd(int ep_num)
+{
+ struct ept_queue_item *item = ci_get_qtd(ep_num, 0);
+ const uint32_t start = (uint32_t)item;
+ const uint32_t end_raw = start + 2 * sizeof(*item);
+ const uint32_t end = roundup(end_raw, ARCH_DMA_MINALIGN);
+
+ invalidate_dcache_range(start, end);
+}
+
+static struct usb_request *
+ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags)
+{
+ struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
+ return &ci_ep->req;
+}
+
+static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+ return;
+}
+
+static void ep_enable(int num, int in, int maxpacket)
+{
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+ unsigned n;
+
+ n = readl(&udc->epctrl[num]);
+ if (in)
+ n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK);
+ else
+ n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK);
+
+ if (num != 0) {
+ struct ept_queue_head *head = ci_get_qh(num, in);
+
+ head->config = CONFIG_MAX_PKT(maxpacket) | CONFIG_ZLT;
+ ci_flush_qh(num);
+ }
+ writel(n, &udc->epctrl[num]);
+}
+
+static int ci_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
+ int num, in;
+ num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
+ ci_ep->desc = desc;
+
+ if (num) {
+ int max = get_unaligned_le16(&desc->wMaxPacketSize);
+
+ if ((max > 64) && (controller.gadget.speed == USB_SPEED_FULL))
+ max = 64;
+ if (ep->maxpacket != max) {
+ DBG("%s: from %d to %d\n", __func__,
+ ep->maxpacket, max);
+ ep->maxpacket = max;
+ }
+ }
+ ep_enable(num, in, ep->maxpacket);
+ DBG("%s: num=%d maxpacket=%d\n", __func__, num, ep->maxpacket);
+ return 0;
+}
+
+static int ci_ep_disable(struct usb_ep *ep)
+{
+ struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
+
+ ci_ep->desc = NULL;
+ return 0;
+}
+
+static int ci_bounce(struct ci_ep *ep, int in)
+{
+ uint32_t addr = (uint32_t)ep->req.buf;
+ uint32_t ba;
+
+ /* Input buffer address is not aligned. */
+ if (addr & (ARCH_DMA_MINALIGN - 1))
+ goto align;
+
+ /* Input buffer length is not aligned. */
+ if (ep->req.length & (ARCH_DMA_MINALIGN - 1))
+ goto align;
+
+ /* The buffer is well aligned, only flush cache. */
+ ep->b_len = ep->req.length;
+ ep->b_buf = ep->req.buf;
+ goto flush;
+
+align:
+ /* Use internal buffer for small payloads. */
+ if (ep->req.length <= 64) {
+ ep->b_len = 64;
+ ep->b_buf = ep->b_fast;
+ } else {
+ ep->b_len = roundup(ep->req.length, ARCH_DMA_MINALIGN);
+ ep->b_buf = memalign(ARCH_DMA_MINALIGN, ep->b_len);
+ if (!ep->b_buf)
+ return -ENOMEM;
+ }
+ if (in)
+ memcpy(ep->b_buf, ep->req.buf, ep->req.length);
+
+flush:
+ ba = (uint32_t)ep->b_buf;
+ flush_dcache_range(ba, ba + ep->b_len);
+
+ return 0;
+}
+
+static void ci_debounce(struct ci_ep *ep, int in)
+{
+ uint32_t addr = (uint32_t)ep->req.buf;
+ uint32_t ba = (uint32_t)ep->b_buf;
+
+ if (in) {
+ if (addr == ba)
+ return; /* not a bounce */
+ goto free;
+ }
+ invalidate_dcache_range(ba, ba + ep->b_len);
+
+ if (addr == ba)
+ return; /* not a bounce */
+
+ memcpy(ep->req.buf, ep->b_buf, ep->req.actual);
+free:
+ /* Large payloads use allocated buffer, free it. */
+ if (ep->b_buf != ep->b_fast)
+ free(ep->b_buf);
+}
+
+static int ci_ep_queue(struct usb_ep *ep,
+ struct usb_request *req, gfp_t gfp_flags)
+{
+ struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+ struct ept_queue_item *item;
+ struct ept_queue_head *head;
+ int bit, num, len, in, ret;
+ num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
+ item = ci_get_qtd(num, in);
+ head = ci_get_qh(num, in);
+ len = req->length;
+
+ ret = ci_bounce(ci_ep, in);
+ if (ret)
+ return ret;
+
+ item->next = TERMINATE;
+ item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE;
+ item->page0 = (uint32_t)ci_ep->b_buf;
+ item->page1 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x1000;
+ item->page2 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x2000;
+ item->page3 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x3000;
+ item->page4 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x4000;
+ ci_flush_qtd(num);
+
+ head->next = (unsigned) item;
+ head->info = 0;
+
+ DBG("ept%d %s queue len %x, buffer %p\n",
+ num, in ? "in" : "out", len, ci_ep->b_buf);
+ ci_flush_qh(num);
+
+ if (in)
+ bit = EPT_TX(num);
+ else
+ bit = EPT_RX(num);
+
+ writel(bit, &udc->epprime);
+
+ return 0;
+}
+
+static void handle_ep_complete(struct ci_ep *ep)
+{
+ struct ept_queue_item *item;
+ int num, in, len;
+ num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
+ if (num == 0)
+ ep->desc = &ep0_out_desc;
+ item = ci_get_qtd(num, in);
+ ci_invalidate_qtd(num);
+
+ if (item->info & 0xff)
+ printf("EP%d/%s FAIL info=%x pg0=%x\n",
+ num, in ? "in" : "out", item->info, item->page0);
+
+ len = (item->info >> 16) & 0x7fff;
+ ep->req.actual = ep->req.length - len;
+ ci_debounce(ep, in);
+
+ DBG("ept%d %s complete %x\n",
+ num, in ? "in" : "out", len);
+ ep->req.complete(&ep->ep, &ep->req);
+ if (num == 0) {
+ ep->req.length = 0;
+ usb_ep_queue(&ep->ep, &ep->req, 0);
+ ep->desc = &ep0_in_desc;
+ }
+}
+
+#define SETUP(type, request) (((type) << 8) | (request))
+
+static void handle_setup(void)
+{
+ struct usb_request *req = &controller.ep[0].req;
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+ struct ept_queue_head *head;
+ struct usb_ctrlrequest r;
+ int status = 0;
+ int num, in, _num, _in, i;
+ char *buf;
+ head = ci_get_qh(0, 0); /* EP0 OUT */
+
+ ci_invalidate_qh(0);
+ memcpy(&r, head->setup_data, sizeof(struct usb_ctrlrequest));
+#ifdef CONFIG_CI_UDC_HAS_HOSTPC
+ writel(EPT_RX(0), &udc->epsetupstat);
+#else
+ writel(EPT_RX(0), &udc->epstat);
+#endif
+ DBG("handle setup %s, %x, %x index %x value %x\n", reqname(r.bRequest),
+ r.bRequestType, r.bRequest, r.wIndex, r.wValue);
+
+ switch (SETUP(r.bRequestType, r.bRequest)) {
+ case SETUP(USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE):
+ _num = r.wIndex & 15;
+ _in = !!(r.wIndex & 0x80);
+
+ if ((r.wValue == 0) && (r.wLength == 0)) {
+ req->length = 0;
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ struct ci_ep *ep = &controller.ep[i];
+
+ if (!ep->desc)
+ continue;
+ num = ep->desc->bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK;
+ in = (ep->desc->bEndpointAddress
+ & USB_DIR_IN) != 0;
+ if ((num == _num) && (in == _in)) {
+ ep_enable(num, in, ep->ep.maxpacket);
+ usb_ep_queue(controller.gadget.ep0,
+ req, 0);
+ break;
+ }
+ }
+ }
+ return;
+
+ case SETUP(USB_RECIP_DEVICE, USB_REQ_SET_ADDRESS):
+ /*
+ * write address delayed (will take effect
+ * after the next IN txn)
+ */
+ writel((r.wValue << 25) | (1 << 24), &udc->devaddr);
+ req->length = 0;
+ usb_ep_queue(controller.gadget.ep0, req, 0);
+ return;
+
+ case SETUP(USB_DIR_IN | USB_RECIP_DEVICE, USB_REQ_GET_STATUS):
+ req->length = 2;
+ buf = (char *)req->buf;
+ buf[0] = 1 << USB_DEVICE_SELF_POWERED;
+ buf[1] = 0;
+ usb_ep_queue(controller.gadget.ep0, req, 0);
+ return;
+ }
+ /* pass request up to the gadget driver */
+ if (controller.driver)
+ status = controller.driver->setup(&controller.gadget, &r);
+ else
+ status = -ENODEV;
+
+ if (!status)
+ return;
+ DBG("STALL reqname %s type %x value %x, index %x\n",
+ reqname(r.bRequest), r.bRequestType, r.wValue, r.wIndex);
+ writel((1<<16) | (1 << 0), &udc->epctrl[0]);
+}
+
+static void stop_activity(void)
+{
+ int i, num, in;
+ struct ept_queue_head *head;
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+ writel(readl(&udc->epcomp), &udc->epcomp);
+#ifdef CONFIG_CI_UDC_HAS_HOSTPC
+ writel(readl(&udc->epsetupstat), &udc->epsetupstat);
+#endif
+ writel(readl(&udc->epstat), &udc->epstat);
+ writel(0xffffffff, &udc->epflush);
+
+ /* error out any pending reqs */
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ if (i != 0)
+ writel(0, &udc->epctrl[i]);
+ if (controller.ep[i].desc) {
+ num = controller.ep[i].desc->bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK;
+ in = (controller.ep[i].desc->bEndpointAddress
+ & USB_DIR_IN) != 0;
+ head = ci_get_qh(num, in);
+ head->info = INFO_ACTIVE;
+ ci_flush_qh(num);
+ }
+ }
+}
+
+void udc_irq(void)
+{
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+ unsigned n = readl(&udc->usbsts);
+ writel(n, &udc->usbsts);
+ int bit, i, num, in;
+
+ n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
+ if (n == 0)
+ return;
+
+ if (n & STS_URI) {
+ DBG("-- reset --\n");
+ stop_activity();
+ }
+ if (n & STS_SLI)
+ DBG("-- suspend --\n");
+
+ if (n & STS_PCI) {
+ int max = 64;
+ int speed = USB_SPEED_FULL;
+
+#ifdef CONFIG_CI_UDC_HAS_HOSTPC
+ bit = (readl(&udc->hostpc1_devlc) >> 25) & 3;
+#else
+ bit = (readl(&udc->portsc) >> 26) & 3;
+#endif
+ DBG("-- portchange %x %s\n", bit, (bit == 2) ? "High" : "Full");
+ if (bit == 2) {
+ speed = USB_SPEED_HIGH;
+ max = 512;
+ }
+ controller.gadget.speed = speed;
+ for (i = 1; i < NUM_ENDPOINTS; i++) {
+ if (controller.ep[i].ep.maxpacket > max)
+ controller.ep[i].ep.maxpacket = max;
+ }
+ }
+
+ if (n & STS_UEI)
+ printf("<UEI %x>\n", readl(&udc->epcomp));
+
+ if ((n & STS_UI) || (n & STS_UEI)) {
+#ifdef CONFIG_CI_UDC_HAS_HOSTPC
+ n = readl(&udc->epsetupstat);
+#else
+ n = readl(&udc->epstat);
+#endif
+ if (n & EPT_RX(0))
+ handle_setup();
+
+ n = readl(&udc->epcomp);
+ if (n != 0)
+ writel(n, &udc->epcomp);
+
+ for (i = 0; i < NUM_ENDPOINTS && n; i++) {
+ if (controller.ep[i].desc) {
+ num = controller.ep[i].desc->bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK;
+ in = (controller.ep[i].desc->bEndpointAddress
+ & USB_DIR_IN) != 0;
+ bit = (in) ? EPT_TX(num) : EPT_RX(num);
+ if (n & bit)
+ handle_ep_complete(&controller.ep[i]);
+ }
+ }
+ }
+}
+
+int usb_gadget_handle_interrupts(void)
+{
+ u32 value;
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+
+ value = readl(&udc->usbsts);
+ if (value)
+ udc_irq();
+
+ return value;
+}
+
+static int ci_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+ if (is_on) {
+ /* RESET */
+ writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RST, &udc->usbcmd);
+ udelay(200);
+
+ writel((unsigned)controller.epts, &udc->epinitaddr);
+
+ /* select DEVICE mode */
+ writel(USBMODE_DEVICE, &udc->usbmode);
+
+ writel(0xffffffff, &udc->epflush);
+
+ /* Turn on the USB connection by enabling the pullup resistor */
+ writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RUN, &udc->usbcmd);
+ } else {
+ stop_activity();
+ writel(USBCMD_FS2, &udc->usbcmd);
+ udelay(800);
+ if (controller.driver)
+ controller.driver->disconnect(gadget);
+ }
+
+ return 0;
+}
+
+void udc_disconnect(void)
+{
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+ /* disable pullup */
+ stop_activity();
+ writel(USBCMD_FS2, &udc->usbcmd);
+ udelay(800);
+ if (controller.driver)
+ controller.driver->disconnect(&controller.gadget);
+}
+
+static int ci_udc_probe(void)
+{
+ struct ept_queue_head *head;
+ uint8_t *imem;
+ int i;
+
+ const int num = 2 * NUM_ENDPOINTS;
+
+ const int eplist_min_align = 4096;
+ const int eplist_align = roundup(eplist_min_align, ARCH_DMA_MINALIGN);
+ const int eplist_raw_sz = num * sizeof(struct ept_queue_head);
+ const int eplist_sz = roundup(eplist_raw_sz, ARCH_DMA_MINALIGN);
+
+ const int ilist_align = roundup(ARCH_DMA_MINALIGN, 32);
+ const int ilist_ent_raw_sz = 2 * sizeof(struct ept_queue_item);
+ const int ilist_ent_sz = roundup(ilist_ent_raw_sz, ARCH_DMA_MINALIGN);
+ const int ilist_sz = NUM_ENDPOINTS * ilist_ent_sz;
+
+ /* The QH list must be aligned to 4096 bytes. */
+ controller.epts = memalign(eplist_align, eplist_sz);
+ if (!controller.epts)
+ return -ENOMEM;
+ memset(controller.epts, 0, eplist_sz);
+
+ /*
+ * Each qTD item must be 32-byte aligned, each qTD touple must be
+ * cacheline aligned. There are two qTD items for each endpoint and
+ * only one of them is used for the endpoint at time, so we can group
+ * them together.
+ */
+ controller.items_mem = memalign(ilist_align, ilist_sz);
+ if (!controller.items_mem) {
+ free(controller.epts);
+ return -ENOMEM;
+ }
+ memset(controller.items_mem, 0, ilist_sz);
+
+ for (i = 0; i < 2 * NUM_ENDPOINTS; i++) {
+ /*
+ * Configure QH for each endpoint. The structure of the QH list
+ * is such that each two subsequent fields, N and N+1 where N is
+ * even, in the QH list represent QH for one endpoint. The Nth
+ * entry represents OUT configuration and the N+1th entry does
+ * represent IN configuration of the endpoint.
+ */
+ head = controller.epts + i;
+ if (i < 2)
+ head->config = CONFIG_MAX_PKT(EP0_MAX_PACKET_SIZE)
+ | CONFIG_ZLT | CONFIG_IOS;
+ else
+ head->config = CONFIG_MAX_PKT(EP_MAX_PACKET_SIZE)
+ | CONFIG_ZLT;
+ head->next = TERMINATE;
+ head->info = 0;
+
+ imem = controller.items_mem + ((i >> 1) * ilist_ent_sz);
+ if (i & 1)
+ imem += sizeof(struct ept_queue_item);
+
+ controller.items[i] = (struct ept_queue_item *)imem;
+
+ if (i & 1) {
+ ci_flush_qh(i - 1);
+ ci_flush_qtd(i - 1);
+ }
+ }
+
+ INIT_LIST_HEAD(&controller.gadget.ep_list);
+
+ /* Init EP 0 */
+ memcpy(&controller.ep[0].ep, &ci_ep_init[0], sizeof(*ci_ep_init));
+ controller.ep[0].desc = &ep0_in_desc;
+ controller.gadget.ep0 = &controller.ep[0].ep;
+ INIT_LIST_HEAD(&controller.gadget.ep0->ep_list);
+
+ /* Init EP 1..n */
+ for (i = 1; i < NUM_ENDPOINTS; i++) {
+ memcpy(&controller.ep[i].ep, &ci_ep_init[1],
+ sizeof(*ci_ep_init));
+ list_add_tail(&controller.ep[i].ep.ep_list,
+ &controller.gadget.ep_list);
+ }
+
+ return 0;
+}
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ int ret;
+
+ if (!driver)
+ return -EINVAL;
+ if (!driver->bind || !driver->setup || !driver->disconnect)
+ return -EINVAL;
+ if (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH)
+ return -EINVAL;
+
+ ret = usb_lowlevel_init(0, USB_INIT_DEVICE, (void **)&controller.ctrl);
+ if (ret)
+ return ret;
+
+ ret = ci_udc_probe();
+#if defined(CONFIG_USB_EHCI_MX6) || defined(CONFIG_USB_EHCI_MXS)
+ /*
+ * FIXME: usb_lowlevel_init()->ehci_hcd_init() should be doing all
+ * HW-specific initialization, e.g. ULPI-vs-UTMI PHY selection
+ */
+ if (!ret) {
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+
+ /* select ULPI phy */
+ writel(PTS(PTS_ENABLE) | PFSC, &udc->portsc);
+ }
+#endif
+
+ ret = driver->bind(&controller.gadget);
+ if (ret) {
+ DBG("driver->bind() returned %d\n", ret);
+ return ret;
+ }
+ controller.driver = driver;
+
+ return 0;
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ return 0;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/ci_udc.h b/src/roms/u-boot/drivers/usb/gadget/ci_udc.h
new file mode 100644
index 0000000..4425fd9
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/ci_udc.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+#ifndef __GADGET__CI_UDC_H__
+#define __GADGET__CI_UDC_H__
+
+#define NUM_ENDPOINTS 6
+
+#ifdef CONFIG_CI_UDC_HAS_HOSTPC
+struct ci_udc {
+ u32 usbcmd; /* 0x130 */
+ u32 usbsts; /* 0x134 */
+ u32 pad1[3];
+ u32 devaddr; /* 0x144 */
+ u32 epinitaddr; /* 0x148 */
+ u32 pad2[10];
+ u32 portsc; /* 0x174 */
+ u32 pad178[(0x1b4 - (0x174 + 4)) / 4];
+ u32 hostpc1_devlc; /* 0x1b4 */
+ u32 pad1b8[(0x1f8 - (0x1b4 + 4)) / 4];
+ u32 usbmode; /* 0x1f8 */
+ u32 pad1fc[(0x208 - (0x1f8 + 4)) / 4];
+ u32 epsetupstat; /* 0x208 */
+ u32 epprime; /* 0x20c */
+ u32 epflush; /* 0x210 */
+ u32 epstat; /* 0x214 */
+ u32 epcomp; /* 0x218 */
+ u32 epctrl[16]; /* 0x21c */
+};
+#else
+struct ci_udc {
+ u32 usbcmd; /* 0x140 */
+ u32 usbsts; /* 0x144 */
+ u32 pad1[3];
+ u32 devaddr; /* 0x154 */
+ u32 epinitaddr; /* 0x158 */
+ u32 pad2[10];
+ u32 portsc; /* 0x184 */
+ u32 pad3[8];
+ u32 usbmode; /* 0x1a8 */
+ u32 epstat; /* 0x1ac */
+ u32 epprime; /* 0x1b0 */
+ u32 epflush; /* 0x1b4 */
+ u32 pad4;
+ u32 epcomp; /* 0x1bc */
+ u32 epctrl[16]; /* 0x1c0 */
+};
+
+#define PTS_ENABLE 2
+#define PTS(x) (((x) & 0x3) << 30)
+#define PFSC (1 << 24)
+#endif
+
+#define MICRO_8FRAME 0x8
+#define USBCMD_ITC(x) ((((x) > 0xff) ? 0xff : x) << 16)
+#define USBCMD_FS2 (1 << 15)
+#define USBCMD_RST (1 << 1)
+#define USBCMD_RUN (1)
+
+#define STS_SLI (1 << 8)
+#define STS_URI (1 << 6)
+#define STS_PCI (1 << 2)
+#define STS_UEI (1 << 1)
+#define STS_UI (1 << 0)
+
+#define USBMODE_DEVICE 2
+
+#define EPT_TX(x) (1 << (((x) & 0xffff) + 16))
+#define EPT_RX(x) (1 << ((x) & 0xffff))
+
+#define CTRL_TXE (1 << 23)
+#define CTRL_TXR (1 << 22)
+#define CTRL_RXE (1 << 7)
+#define CTRL_RXR (1 << 6)
+#define CTRL_TXT_BULK (2 << 18)
+#define CTRL_RXT_BULK (2 << 2)
+
+struct ci_ep {
+ struct usb_ep ep;
+ struct list_head queue;
+ const struct usb_endpoint_descriptor *desc;
+
+ struct usb_request req;
+ uint8_t *b_buf;
+ uint32_t b_len;
+ uint8_t b_fast[64] __aligned(ARCH_DMA_MINALIGN);
+};
+
+struct ci_drv {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct ehci_ctrl *ctrl;
+ struct ept_queue_head *epts;
+ struct ept_queue_item *items[2 * NUM_ENDPOINTS];
+ uint8_t *items_mem;
+ struct ci_ep ep[NUM_ENDPOINTS];
+};
+
+struct ept_queue_head {
+ unsigned config;
+ unsigned current; /* read-only */
+
+ unsigned next;
+ unsigned info;
+ unsigned page0;
+ unsigned page1;
+ unsigned page2;
+ unsigned page3;
+ unsigned page4;
+ unsigned reserved_0;
+
+ unsigned char setup_data[8];
+
+ unsigned reserved_1;
+ unsigned reserved_2;
+ unsigned reserved_3;
+ unsigned reserved_4;
+};
+
+#define CONFIG_MAX_PKT(n) ((n) << 16)
+#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */
+#define CONFIG_IOS (1 << 15) /* IRQ on setup */
+
+struct ept_queue_item {
+ unsigned next;
+ unsigned info;
+ unsigned page0;
+ unsigned page1;
+ unsigned page2;
+ unsigned page3;
+ unsigned page4;
+ unsigned reserved;
+};
+
+#define TERMINATE 1
+#define INFO_BYTES(n) ((n) << 16)
+#define INFO_IOC (1 << 15)
+#define INFO_ACTIVE (1 << 7)
+#define INFO_HALTED (1 << 6)
+#define INFO_BUFFER_ERROR (1 << 5)
+#define INFO_TX_ERROR (1 << 3)
+#endif
diff --git a/src/roms/u-boot/drivers/usb/gadget/composite.c b/src/roms/u-boot/drivers/usb/gadget/composite.c
new file mode 100644
index 0000000..7bd2562
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/composite.c
@@ -0,0 +1,1091 @@
+/*
+ * composite.c - infrastructure for Composite USB Gadgets
+ *
+ * Copyright (C) 2006-2008 David Brownell
+ * U-boot porting: Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#undef DEBUG
+
+#include <linux/bitops.h>
+#include <linux/usb/composite.h>
+
+#define USB_BUFSIZ 4096
+
+static struct usb_composite_driver *composite;
+
+/**
+ * usb_add_function() - add a function to a configuration
+ * @config: the configuration
+ * @function: the function being added
+ * Context: single threaded during gadget setup
+ *
+ * After initialization, each configuration must have one or more
+ * functions added to it. Adding a function involves calling its @bind()
+ * method to allocate resources such as interface and string identifiers
+ * and endpoints.
+ *
+ * This function returns the value of the function's bind(), which is
+ * zero for success else a negative errno value.
+ */
+int usb_add_function(struct usb_configuration *config,
+ struct usb_function *function)
+{
+ int value = -EINVAL;
+
+ debug("adding '%s'/%p to config '%s'/%p\n",
+ function->name, function,
+ config->label, config);
+
+ if (!function->set_alt || !function->disable)
+ goto done;
+
+ function->config = config;
+ list_add_tail(&function->list, &config->functions);
+
+ if (function->bind) {
+ value = function->bind(config, function);
+ if (value < 0) {
+ list_del(&function->list);
+ function->config = NULL;
+ }
+ } else
+ value = 0;
+
+ if (!config->fullspeed && function->descriptors)
+ config->fullspeed = 1;
+ if (!config->highspeed && function->hs_descriptors)
+ config->highspeed = 1;
+
+done:
+ if (value)
+ debug("adding '%s'/%p --> %d\n",
+ function->name, function, value);
+ return value;
+}
+
+/**
+ * usb_function_deactivate - prevent function and gadget enumeration
+ * @function: the function that isn't yet ready to respond
+ *
+ * Blocks response of the gadget driver to host enumeration by
+ * preventing the data line pullup from being activated. This is
+ * normally called during @bind() processing to change from the
+ * initial "ready to respond" state, or when a required resource
+ * becomes available.
+ *
+ * For example, drivers that serve as a passthrough to a userspace
+ * daemon can block enumeration unless that daemon (such as an OBEX,
+ * MTP, or print server) is ready to handle host requests.
+ *
+ * Not all systems support software control of their USB peripheral
+ * data pullups.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_deactivate(struct usb_function *function)
+{
+ struct usb_composite_dev *cdev = function->config->cdev;
+ int status = 0;
+
+ if (cdev->deactivations == 0)
+ status = usb_gadget_disconnect(cdev->gadget);
+ if (status == 0)
+ cdev->deactivations++;
+
+ return status;
+}
+
+/**
+ * usb_function_activate - allow function and gadget enumeration
+ * @function: function on which usb_function_activate() was called
+ *
+ * Reverses effect of usb_function_deactivate(). If no more functions
+ * are delaying their activation, the gadget driver will respond to
+ * host enumeration procedures.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_activate(struct usb_function *function)
+{
+ struct usb_composite_dev *cdev = function->config->cdev;
+ int status = 0;
+
+ if (cdev->deactivations == 0)
+ status = -EINVAL;
+ else {
+ cdev->deactivations--;
+ if (cdev->deactivations == 0)
+ status = usb_gadget_connect(cdev->gadget);
+ }
+
+ return status;
+}
+
+/**
+ * usb_interface_id() - allocate an unused interface ID
+ * @config: configuration associated with the interface
+ * @function: function handling the interface
+ * Context: single threaded during gadget setup
+ *
+ * usb_interface_id() is called from usb_function.bind() callbacks to
+ * allocate new interface IDs. The function driver will then store that
+ * ID in interface, association, CDC union, and other descriptors. It
+ * will also handle any control requests targetted at that interface,
+ * particularly changing its altsetting via set_alt(). There may
+ * also be class-specific or vendor-specific requests to handle.
+ *
+ * All interface identifier should be allocated using this routine, to
+ * ensure that for example different functions don't wrongly assign
+ * different meanings to the same identifier. Note that since interface
+ * identifers are configuration-specific, functions used in more than
+ * one configuration (or more than once in a given configuration) need
+ * multiple versions of the relevant descriptors.
+ *
+ * Returns the interface ID which was allocated; or -ENODEV if no
+ * more interface IDs can be allocated.
+ */
+int usb_interface_id(struct usb_configuration *config,
+ struct usb_function *function)
+{
+ unsigned char id = config->next_interface_id;
+
+ if (id < MAX_CONFIG_INTERFACES) {
+ config->interface[id] = function;
+ config->next_interface_id = id + 1;
+ return id;
+ }
+ return -ENODEV;
+}
+
+static int config_buf(struct usb_configuration *config,
+ enum usb_device_speed speed, void *buf, u8 type)
+{
+ int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
+ void *next = buf + USB_DT_CONFIG_SIZE;
+ struct usb_descriptor_header **descriptors;
+ struct usb_config_descriptor *c = buf;
+ int status;
+ struct usb_function *f;
+
+ /* write the config descriptor */
+ c = buf;
+ c->bLength = USB_DT_CONFIG_SIZE;
+ c->bDescriptorType = type;
+
+ c->bNumInterfaces = config->next_interface_id;
+ c->bConfigurationValue = config->bConfigurationValue;
+ c->iConfiguration = config->iConfiguration;
+ c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
+ c->bMaxPower = config->bMaxPower ? : (CONFIG_USB_GADGET_VBUS_DRAW / 2);
+
+ /* There may be e.g. OTG descriptors */
+ if (config->descriptors) {
+ status = usb_descriptor_fillbuf(next, len,
+ config->descriptors);
+ if (status < 0)
+ return status;
+ len -= status;
+ next += status;
+ }
+
+ /* add each function's descriptors */
+ list_for_each_entry(f, &config->functions, list) {
+ if (speed == USB_SPEED_HIGH)
+ descriptors = f->hs_descriptors;
+ else
+ descriptors = f->descriptors;
+ if (!descriptors)
+ continue;
+ status = usb_descriptor_fillbuf(next, len,
+ (const struct usb_descriptor_header **) descriptors);
+ if (status < 0)
+ return status;
+ len -= status;
+ next += status;
+ }
+
+ len = next - buf;
+ c->wTotalLength = cpu_to_le16(len);
+ return len;
+}
+
+static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
+{
+ enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+ struct usb_gadget *gadget = cdev->gadget;
+ u8 type = w_value >> 8;
+ int hs = 0;
+ struct usb_configuration *c;
+
+ if (gadget_is_dualspeed(gadget)) {
+ if (gadget->speed == USB_SPEED_HIGH)
+ hs = 1;
+ if (type == USB_DT_OTHER_SPEED_CONFIG)
+ hs = !hs;
+ if (hs)
+ speed = USB_SPEED_HIGH;
+ }
+
+ w_value &= 0xff;
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (speed == USB_SPEED_HIGH) {
+ if (!c->highspeed)
+ continue;
+ } else {
+ if (!c->fullspeed)
+ continue;
+ }
+ if (w_value == 0)
+ return config_buf(c, speed, cdev->req->buf, type);
+ w_value--;
+ }
+ return -EINVAL;
+}
+
+static int count_configs(struct usb_composite_dev *cdev, unsigned type)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ unsigned count = 0;
+ int hs = 0;
+ struct usb_configuration *c;
+
+ if (gadget_is_dualspeed(gadget)) {
+ if (gadget->speed == USB_SPEED_HIGH)
+ hs = 1;
+ if (type == USB_DT_DEVICE_QUALIFIER)
+ hs = !hs;
+ }
+ list_for_each_entry(c, &cdev->configs, list) {
+ /* ignore configs that won't work at this speed */
+ if (hs) {
+ if (!c->highspeed)
+ continue;
+ } else {
+ if (!c->fullspeed)
+ continue;
+ }
+ count++;
+ }
+ return count;
+}
+
+static void device_qual(struct usb_composite_dev *cdev)
+{
+ struct usb_qualifier_descriptor *qual = cdev->req->buf;
+
+ qual->bLength = sizeof(*qual);
+ qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+ /* POLICY: same bcdUSB and device type info at both speeds */
+ qual->bcdUSB = cdev->desc.bcdUSB;
+ qual->bDeviceClass = cdev->desc.bDeviceClass;
+ qual->bDeviceSubClass = cdev->desc.bDeviceSubClass;
+ qual->bDeviceProtocol = cdev->desc.bDeviceProtocol;
+ /* ASSUME same EP0 fifo size at both speeds */
+ qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0;
+ qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER);
+ qual->bRESERVED = 0;
+}
+
+static void reset_config(struct usb_composite_dev *cdev)
+{
+ struct usb_function *f;
+
+ debug("%s:\n", __func__);
+
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->disable)
+ f->disable(f);
+
+ bitmap_zero(f->endpoints, 32);
+ }
+ cdev->config = NULL;
+}
+
+static int set_config(struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *ctrl, unsigned number)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ unsigned power = gadget_is_otg(gadget) ? 8 : 100;
+ struct usb_descriptor_header **descriptors;
+ int result = -EINVAL;
+ struct usb_endpoint_descriptor *ep;
+ struct usb_configuration *c = NULL;
+ int addr;
+ int tmp;
+ struct usb_function *f;
+
+ if (cdev->config)
+ reset_config(cdev);
+
+ if (number) {
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->bConfigurationValue == number) {
+ result = 0;
+ break;
+ }
+ }
+ if (result < 0)
+ goto done;
+ } else
+ result = 0;
+
+ debug("%s: %s speed config #%d: %s\n", __func__,
+ ({ char *speed;
+ switch (gadget->speed) {
+ case USB_SPEED_LOW:
+ speed = "low";
+ break;
+ case USB_SPEED_FULL:
+ speed = "full";
+ break;
+ case USB_SPEED_HIGH:
+ speed = "high";
+ break;
+ default:
+ speed = "?";
+ break;
+ };
+ speed;
+ }), number, c ? c->label : "unconfigured");
+
+ if (!c)
+ goto done;
+
+ cdev->config = c;
+
+ /* Initialize all interfaces by setting them to altsetting zero. */
+ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
+ f = c->interface[tmp];
+ if (!f)
+ break;
+
+ /*
+ * Record which endpoints are used by the function. This is used
+ * to dispatch control requests targeted at that endpoint to the
+ * function's setup callback instead of the current
+ * configuration's setup callback.
+ */
+ if (gadget->speed == USB_SPEED_HIGH)
+ descriptors = f->hs_descriptors;
+ else
+ descriptors = f->descriptors;
+
+ for (; *descriptors; ++descriptors) {
+ if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
+ continue;
+
+ ep = (struct usb_endpoint_descriptor *)*descriptors;
+ addr = ((ep->bEndpointAddress & 0x80) >> 3)
+ | (ep->bEndpointAddress & 0x0f);
+ __set_bit(addr, f->endpoints);
+ }
+
+ result = f->set_alt(f, tmp, 0);
+ if (result < 0) {
+ debug("interface %d (%s/%p) alt 0 --> %d\n",
+ tmp, f->name, f, result);
+
+ reset_config(cdev);
+ goto done;
+ }
+ }
+
+ /* when we return, be sure our power usage is valid */
+ power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
+done:
+ usb_gadget_vbus_draw(gadget, power);
+ return result;
+}
+
+/**
+ * usb_add_config() - add a configuration to a device.
+ * @cdev: wraps the USB gadget
+ * @config: the configuration, with bConfigurationValue assigned
+ * Context: single threaded during gadget setup
+ *
+ * One of the main tasks of a composite driver's bind() routine is to
+ * add each of the configurations it supports, using this routine.
+ *
+ * This function returns the value of the configuration's bind(), which
+ * is zero for success else a negative errno value. Binding configurations
+ * assigns global resources including string IDs, and per-configuration
+ * resources such as interface IDs and endpoints.
+ */
+int usb_add_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *config)
+{
+ int status = -EINVAL;
+ struct usb_configuration *c;
+ struct usb_function *f;
+ unsigned int i;
+
+ debug("%s: adding config #%u '%s'/%p\n", __func__,
+ config->bConfigurationValue,
+ config->label, config);
+
+ if (!config->bConfigurationValue || !config->bind)
+ goto done;
+
+ /* Prevent duplicate configuration identifiers */
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->bConfigurationValue == config->bConfigurationValue) {
+ status = -EBUSY;
+ goto done;
+ }
+ }
+
+ config->cdev = cdev;
+ list_add_tail(&config->list, &cdev->configs);
+
+ INIT_LIST_HEAD(&config->functions);
+ config->next_interface_id = 0;
+
+ status = config->bind(config);
+ if (status < 0) {
+ list_del(&config->list);
+ config->cdev = NULL;
+ } else {
+ debug("cfg %d/%p speeds:%s%s\n",
+ config->bConfigurationValue, config,
+ config->highspeed ? " high" : "",
+ config->fullspeed
+ ? (gadget_is_dualspeed(cdev->gadget)
+ ? " full"
+ : " full/low")
+ : "");
+
+ for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
+ f = config->interface[i];
+ if (!f)
+ continue;
+ debug("%s: interface %d = %s/%p\n",
+ __func__, i, f->name, f);
+ }
+ }
+
+ usb_ep_autoconfig_reset(cdev->gadget);
+
+done:
+ if (status)
+ debug("added config '%s'/%u --> %d\n", config->label,
+ config->bConfigurationValue, status);
+ return status;
+}
+
+/*
+ * We support strings in multiple languages ... string descriptor zero
+ * says which languages are supported. The typical case will be that
+ * only one language (probably English) is used, with I18N handled on
+ * the host side.
+ */
+
+static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
+{
+ const struct usb_gadget_strings *s;
+ u16 language;
+ __le16 *tmp;
+
+ while (*sp) {
+ s = *sp;
+ language = cpu_to_le16(s->language);
+ for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
+ if (*tmp == language)
+ goto repeat;
+ }
+ *tmp++ = language;
+repeat:
+ sp++;
+ }
+}
+
+static int lookup_string(
+ struct usb_gadget_strings **sp,
+ void *buf,
+ u16 language,
+ int id
+)
+{
+ int value;
+ struct usb_gadget_strings *s;
+
+ while (*sp) {
+ s = *sp++;
+ if (s->language != language)
+ continue;
+ value = usb_gadget_get_string(s, id, buf);
+ if (value > 0)
+ return value;
+ }
+ return -EINVAL;
+}
+
+static int get_string(struct usb_composite_dev *cdev,
+ void *buf, u16 language, int id)
+{
+ struct usb_string_descriptor *s = buf;
+ struct usb_gadget_strings **sp;
+ int len;
+ struct usb_configuration *c;
+ struct usb_function *f;
+
+ /*
+ * Yes, not only is USB's I18N support probably more than most
+ * folk will ever care about ... also, it's all supported here.
+ * (Except for UTF8 support for Unicode's "Astral Planes".)
+ */
+
+ /* 0 == report all available language codes */
+ if (id == 0) {
+ memset(s, 0, 256);
+ s->bDescriptorType = USB_DT_STRING;
+
+ sp = composite->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+
+ list_for_each_entry(c, &cdev->configs, list) {
+ sp = c->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+
+ list_for_each_entry(f, &c->functions, list) {
+ sp = f->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+ }
+ }
+
+ for (len = 0; len <= 126 && s->wData[len]; len++)
+ continue;
+ if (!len)
+ return -EINVAL;
+
+ s->bLength = 2 * (len + 1);
+ return s->bLength;
+ }
+
+ /*
+ * Otherwise, look up and return a specified string. String IDs
+ * are device-scoped, so we look up each string table we're told
+ * about. These lookups are infrequent; simpler-is-better here.
+ */
+ if (composite->strings) {
+ len = lookup_string(composite->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->strings) {
+ len = lookup_string(c->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ list_for_each_entry(f, &c->functions, list) {
+ if (!f->strings)
+ continue;
+ len = lookup_string(f->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ }
+ return -EINVAL;
+}
+
+/**
+ * usb_string_id() - allocate an unused string ID
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_id() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure
+ * that for example different functions don't wrongly assign different
+ * meanings to the same identifier.
+ */
+int usb_string_id(struct usb_composite_dev *cdev)
+{
+ if (cdev->next_string_id < 254) {
+ /*
+ * string id 0 is reserved by USB spec for list of
+ * supported languages
+ * 255 reserved as well? -- mina86
+ */
+ cdev->next_string_id++;
+ return cdev->next_string_id;
+ }
+ return -ENODEV;
+}
+
+/**
+ * usb_string_ids() - allocate unused string IDs in batch
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * @str: an array of usb_string objects to assign numbers to
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_ids() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then copy IDs from the string table to the appropriate descriptors
+ * and string table for other languages.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
+{
+ u8 next = cdev->next_string_id;
+
+ for (; str->s; ++str) {
+ if (next >= 254)
+ return -ENODEV;
+ str->id = ++next;
+ }
+
+ cdev->next_string_id = next;
+
+ return 0;
+}
+
+/**
+ * usb_string_ids_n() - allocate unused string IDs in batch
+ * @c: the device whose string descriptor IDs are being allocated
+ * @n: number of string IDs to allocate
+ * Context: single threaded during gadget setup
+ *
+ * Returns the first requested ID. This ID and next @n-1 IDs are now
+ * valid IDs. At least provided that @n is non-zero because if it
+ * is, returns last requested ID which is now very useful information.
+ *
+ * @usb_string_ids_n() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)
+{
+ u8 next = c->next_string_id;
+
+ if (n > 254 || next + n > 254)
+ return -ENODEV;
+
+ c->next_string_id += n;
+ return next + 1;
+}
+
+static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->status || req->actual != req->length)
+ debug("%s: setup complete --> %d, %d/%d\n", __func__,
+ req->status, req->actual, req->length);
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver(like
+ * device and endpoint feature flags, and their status). It's all
+ * housekeeping for the gadget function we're implementing. Most of
+ * the work is in config and function specific setup.
+ */
+static int
+composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ u8 intf = w_index & 0xFF;
+ int value = -EOPNOTSUPP;
+ struct usb_request *req = cdev->req;
+ struct usb_function *f = NULL;
+ int standard;
+ u8 endp;
+ struct usb_configuration *c;
+
+ /*
+ * partial re-init of the response message; the function or the
+ * gadget might need to intercept e.g. a control-OUT completion
+ * when we delegate to it.
+ */
+ req->zero = 0;
+ req->complete = composite_setup_complete;
+ req->length = USB_BUFSIZ;
+ gadget->ep0->driver_data = cdev;
+ standard = (ctrl->bRequestType & USB_TYPE_MASK)
+ == USB_TYPE_STANDARD;
+ if (!standard)
+ goto unknown;
+
+ switch (ctrl->bRequest) {
+
+ /* we handle all standard USB descriptors */
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ goto unknown;
+ switch (w_value >> 8) {
+
+ case USB_DT_DEVICE:
+ cdev->desc.bNumConfigurations =
+ count_configs(cdev, USB_DT_DEVICE);
+ value = min(w_length, (u16) sizeof cdev->desc);
+ memcpy(req->buf, &cdev->desc, value);
+ break;
+ case USB_DT_DEVICE_QUALIFIER:
+ if (!gadget_is_dualspeed(gadget))
+ break;
+ device_qual(cdev);
+ value = min(w_length,
+ sizeof(struct usb_qualifier_descriptor));
+ break;
+ case USB_DT_OTHER_SPEED_CONFIG:
+ if (!gadget_is_dualspeed(gadget))
+ break;
+
+ case USB_DT_CONFIG:
+ value = config_desc(cdev, w_value);
+ if (value >= 0)
+ value = min(w_length, (u16) value);
+ break;
+ case USB_DT_STRING:
+ value = get_string(cdev, req->buf,
+ w_index, w_value & 0xff);
+ if (value >= 0)
+ value = min(w_length, (u16) value);
+ break;
+ default:
+ goto unknown;
+ }
+ break;
+
+ /* any number of configs can work */
+ case USB_REQ_SET_CONFIGURATION:
+ if (ctrl->bRequestType != 0)
+ goto unknown;
+ if (gadget_is_otg(gadget)) {
+ if (gadget->a_hnp_support)
+ debug("HNP available\n");
+ else if (gadget->a_alt_hnp_support)
+ debug("HNP on another port\n");
+ else
+ debug("HNP inactive\n");
+ }
+
+ value = set_config(cdev, ctrl, w_value);
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ goto unknown;
+ if (cdev->config)
+ *(u8 *)req->buf = cdev->config->bConfigurationValue;
+ else
+ *(u8 *)req->buf = 0;
+ value = min(w_length, (u16) 1);
+ break;
+
+ /*
+ * function drivers must handle get/set altsetting; if there's
+ * no get() method, we know only altsetting zero works.
+ */
+ case USB_REQ_SET_INTERFACE:
+ if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+ goto unknown;
+ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ if (w_value && !f->set_alt)
+ break;
+ value = f->set_alt(f, w_index, w_value);
+ break;
+ case USB_REQ_GET_INTERFACE:
+ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
+ goto unknown;
+ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ /* lots of interfaces only need altsetting zero... */
+ value = f->get_alt ? f->get_alt(f, w_index) : 0;
+ if (value < 0)
+ break;
+ *((u8 *)req->buf) = value;
+ value = min(w_length, (u16) 1);
+ break;
+ default:
+unknown:
+ debug("non-core control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+
+ /*
+ * functions always handle their interfaces and endpoints...
+ * punt other recipients (other, WUSB, ...) to the current
+ * configuration code.
+ */
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ f = cdev->config->interface[intf];
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (test_bit(endp, f->endpoints))
+ break;
+ }
+ if (&f->list == &cdev->config->functions)
+ f = NULL;
+ break;
+ /*
+ * dfu-util (version 0.5) sets bmRequestType.Receipent = Device
+ * for non-standard request (w_value = 0x21,
+ * bRequest = GET_DESCRIPTOR in this case).
+ * When only one interface is registered (as it is done now),
+ * then this request shall be handled as it was requested for
+ * interface.
+ *
+ * In the below code it is checked if only one interface is
+ * present and proper function for it is extracted. Due to that
+ * function's setup (f->setup) is called to handle this
+ * special non-standard request.
+ */
+ case USB_RECIP_DEVICE:
+ debug("cdev->config->next_interface_id: %d intf: %d\n",
+ cdev->config->next_interface_id, intf);
+ if (cdev->config->next_interface_id == 1)
+ f = cdev->config->interface[intf];
+ break;
+ }
+
+ if (f && f->setup)
+ value = f->setup(f, ctrl);
+ else {
+ c = cdev->config;
+ if (c && c->setup)
+ value = c->setup(c, ctrl);
+ }
+
+ goto done;
+ }
+
+ /* respond with data transfer before status phase? */
+ if (value >= 0) {
+ req->length = value;
+ req->zero = value < w_length;
+ value = usb_ep_queue(gadget->ep0, req, GFP_KERNEL);
+ if (value < 0) {
+ debug("ep_queue --> %d\n", value);
+ req->status = 0;
+ composite_setup_complete(gadget->ep0, req);
+ }
+ }
+
+done:
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static void composite_disconnect(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+
+ if (cdev->config)
+ reset_config(cdev);
+ if (composite->disconnect)
+ composite->disconnect(cdev);
+}
+
+static void composite_unbind(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_configuration *c;
+ struct usb_function *f;
+
+ /*
+ * composite_disconnect() must already have been called
+ * by the underlying peripheral controller driver!
+ * so there's no i/o concurrency that could affect the
+ * state protected by cdev->lock.
+ */
+ BUG_ON(cdev->config);
+
+ while (!list_empty(&cdev->configs)) {
+ c = list_first_entry(&cdev->configs,
+ struct usb_configuration, list);
+ while (!list_empty(&c->functions)) {
+ f = list_first_entry(&c->functions,
+ struct usb_function, list);
+ list_del(&f->list);
+ if (f->unbind) {
+ debug("unbind function '%s'/%p\n",
+ f->name, f);
+ f->unbind(c, f);
+ }
+ }
+ list_del(&c->list);
+ if (c->unbind) {
+ debug("unbind config '%s'/%p\n", c->label, c);
+ c->unbind(c);
+ }
+ }
+ if (composite->unbind)
+ composite->unbind(cdev);
+
+ if (cdev->req) {
+ kfree(cdev->req->buf);
+ usb_ep_free_request(gadget->ep0, cdev->req);
+ }
+ kfree(cdev);
+ set_gadget_data(gadget, NULL);
+
+ composite = NULL;
+}
+
+static int composite_bind(struct usb_gadget *gadget)
+{
+ int status = -ENOMEM;
+ struct usb_composite_dev *cdev;
+
+ cdev = calloc(sizeof *cdev, 1);
+ if (!cdev)
+ return status;
+
+ cdev->gadget = gadget;
+ set_gadget_data(gadget, cdev);
+ INIT_LIST_HEAD(&cdev->configs);
+
+ /* preallocate control response and buffer */
+ cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!cdev->req)
+ goto fail;
+ cdev->req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, USB_BUFSIZ);
+ if (!cdev->req->buf)
+ goto fail;
+ cdev->req->complete = composite_setup_complete;
+ gadget->ep0->driver_data = cdev;
+
+ cdev->bufsiz = USB_BUFSIZ;
+ cdev->driver = composite;
+
+ usb_gadget_set_selfpowered(gadget);
+ usb_ep_autoconfig_reset(cdev->gadget);
+
+ status = composite->bind(cdev);
+ if (status < 0)
+ goto fail;
+
+ memcpy(&cdev->desc, composite->dev,
+ sizeof(struct usb_device_descriptor));
+ cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+
+ debug("%s: ready\n", composite->name);
+ return 0;
+
+fail:
+ composite_unbind(gadget);
+ return status;
+}
+
+static void
+composite_suspend(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+
+ debug("%s: suspend\n", __func__);
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->suspend)
+ f->suspend(f);
+ }
+ }
+ if (composite->suspend)
+ composite->suspend(cdev);
+
+ cdev->suspended = 1;
+}
+
+static void
+composite_resume(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+
+ debug("%s: resume\n", __func__);
+ if (composite->resume)
+ composite->resume(cdev);
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->resume)
+ f->resume(f);
+ }
+ }
+
+ cdev->suspended = 0;
+}
+
+static struct usb_gadget_driver composite_driver = {
+ .speed = USB_SPEED_HIGH,
+
+ .bind = composite_bind,
+ .unbind = composite_unbind,
+
+ .setup = composite_setup,
+ .disconnect = composite_disconnect,
+
+ .suspend = composite_suspend,
+ .resume = composite_resume,
+};
+
+/**
+ * usb_composite_register() - register a composite driver
+ * @driver: the driver to register
+ * Context: single threaded during gadget setup
+ *
+ * This function is used to register drivers using the composite driver
+ * framework. The return value is zero, or a negative errno value.
+ * Those values normally come from the driver's @bind method, which does
+ * all the work of setting up the driver to match the hardware.
+ *
+ * On successful return, the gadget is ready to respond to requests from
+ * the host, unless one of its components invokes usb_gadget_disconnect()
+ * while it was binding. That would usually be done in order to wait for
+ * some userspace participation.
+ */
+int usb_composite_register(struct usb_composite_driver *driver)
+{
+ if (!driver || !driver->dev || !driver->bind || composite)
+ return -EINVAL;
+
+ if (!driver->name)
+ driver->name = "composite";
+ composite = driver;
+
+ return usb_gadget_register_driver(&composite_driver);
+}
+
+/**
+ * usb_composite_unregister() - unregister a composite driver
+ * @driver: the driver to unregister
+ *
+ * This function is used to unregister drivers using the composite
+ * driver framework.
+ */
+void usb_composite_unregister(struct usb_composite_driver *driver)
+{
+ if (composite != driver)
+ return;
+ usb_gadget_unregister_driver(&composite_driver);
+ composite = NULL;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/config.c b/src/roms/u-boot/drivers/usb/gadget/config.c
new file mode 100644
index 0000000..014a679
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/config.c
@@ -0,0 +1,108 @@
+/*
+ * usb/gadget/config.c -- simplify building config descriptors
+ *
+ * Copyright (C) 2003 David Brownell
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and
+ * Remy Bohmer <linux@bohmer.net>
+ */
+
+#include <common.h>
+#include <asm/unaligned.h>
+#include <asm/errno.h>
+#include <linux/list.h>
+#include <linux/string.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+
+/**
+ * usb_descriptor_fillbuf - fill buffer with descriptors
+ * @buf: Buffer to be filled
+ * @buflen: Size of buf
+ * @src: Array of descriptor pointers, terminated by null pointer.
+ *
+ * Copies descriptors into the buffer, returning the length or a
+ * negative error code if they can't all be copied. Useful when
+ * assembling descriptors for an associated set of interfaces used
+ * as part of configuring a composite device; or in other cases where
+ * sets of descriptors need to be marshaled.
+ */
+int
+usb_descriptor_fillbuf(void *buf, unsigned buflen,
+ const struct usb_descriptor_header **src)
+{
+ u8 *dest = buf;
+
+ if (!src)
+ return -EINVAL;
+
+ /* fill buffer from src[] until null descriptor ptr */
+ for (; NULL != *src; src++) {
+ unsigned len = (*src)->bLength;
+
+ if (len > buflen)
+ return -EINVAL;
+ memcpy(dest, *src, len);
+ buflen -= len;
+ dest += len;
+ }
+ return dest - (u8 *)buf;
+}
+
+
+/**
+ * usb_gadget_config_buf - builts a complete configuration descriptor
+ * @config: Header for the descriptor, including characteristics such
+ * as power requirements and number of interfaces.
+ * @desc: Null-terminated vector of pointers to the descriptors (interface,
+ * endpoint, etc) defining all functions in this device configuration.
+ * @buf: Buffer for the resulting configuration descriptor.
+ * @length: Length of buffer. If this is not big enough to hold the
+ * entire configuration descriptor, an error code will be returned.
+ *
+ * This copies descriptors into the response buffer, building a descriptor
+ * for that configuration. It returns the buffer length or a negative
+ * status code. The config.wTotalLength field is set to match the length
+ * of the result, but other descriptor fields (including power usage and
+ * interface count) must be set by the caller.
+ *
+ * Gadget drivers could use this when constructing a config descriptor
+ * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
+ * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
+ */
+int usb_gadget_config_buf(
+ const struct usb_config_descriptor *config,
+ void *buf,
+ unsigned length,
+ const struct usb_descriptor_header **desc
+)
+{
+ struct usb_config_descriptor *cp = buf;
+ int len;
+
+ /* config descriptor first */
+ if (length < USB_DT_CONFIG_SIZE || !desc)
+ return -EINVAL;
+ /* config need not be aligned */
+ memcpy(cp, config, sizeof(*cp));
+
+ /* then interface/endpoint/class/vendor/... */
+ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf,
+ length - USB_DT_CONFIG_SIZE, desc);
+ if (len < 0)
+ return len;
+ len += USB_DT_CONFIG_SIZE;
+ if (len > 0xffff)
+ return -EINVAL;
+
+ /* patch up the config descriptor */
+ cp->bLength = USB_DT_CONFIG_SIZE;
+ cp->bDescriptorType = USB_DT_CONFIG;
+ put_unaligned_le16(len, &cp->wTotalLength);
+ cp->bmAttributes |= USB_CONFIG_ATT_ONE;
+ return len;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/core.c b/src/roms/u-boot/drivers/usb/gadget/core.c
new file mode 100644
index 0000000..30d55a4
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/core.c
@@ -0,0 +1,669 @@
+/*
+ * (C) Copyright 2003
+ * Gerry Hamel, geh@ti.com, Texas Instruments
+ *
+ * Based on
+ * linux/drivers/usbd/usbd.c.c - USB Device Core Layer
+ *
+ * Copyright (c) 2000, 2001, 2002 Lineo
+ * Copyright (c) 2001 Hewlett Packard
+ *
+ * By:
+ * Stuart Lynne <sl@lineo.com>,
+ * Tom Rushworth <tbr@lineo.com>,
+ * Bruce Balden <balden@lineo.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <malloc.h>
+#include <usbdevice.h>
+
+#define MAX_INTERFACES 2
+
+
+int maxstrings = 20;
+
+/* Global variables ************************************************************************** */
+
+struct usb_string_descriptor **usb_strings;
+
+int usb_devices;
+
+extern struct usb_function_driver ep0_driver;
+
+int registered_functions;
+int registered_devices;
+
+char *usbd_device_events[] = {
+ "DEVICE_UNKNOWN",
+ "DEVICE_INIT",
+ "DEVICE_CREATE",
+ "DEVICE_HUB_CONFIGURED",
+ "DEVICE_RESET",
+ "DEVICE_ADDRESS_ASSIGNED",
+ "DEVICE_CONFIGURED",
+ "DEVICE_SET_INTERFACE",
+ "DEVICE_SET_FEATURE",
+ "DEVICE_CLEAR_FEATURE",
+ "DEVICE_DE_CONFIGURED",
+ "DEVICE_BUS_INACTIVE",
+ "DEVICE_BUS_ACTIVITY",
+ "DEVICE_POWER_INTERRUPTION",
+ "DEVICE_HUB_RESET",
+ "DEVICE_DESTROY",
+ "DEVICE_FUNCTION_PRIVATE",
+};
+
+char *usbd_device_states[] = {
+ "STATE_INIT",
+ "STATE_CREATED",
+ "STATE_ATTACHED",
+ "STATE_POWERED",
+ "STATE_DEFAULT",
+ "STATE_ADDRESSED",
+ "STATE_CONFIGURED",
+ "STATE_UNKNOWN",
+};
+
+char *usbd_device_requests[] = {
+ "GET STATUS", /* 0 */
+ "CLEAR FEATURE", /* 1 */
+ "RESERVED", /* 2 */
+ "SET FEATURE", /* 3 */
+ "RESERVED", /* 4 */
+ "SET ADDRESS", /* 5 */
+ "GET DESCRIPTOR", /* 6 */
+ "SET DESCRIPTOR", /* 7 */
+ "GET CONFIGURATION", /* 8 */
+ "SET CONFIGURATION", /* 9 */
+ "GET INTERFACE", /* 10 */
+ "SET INTERFACE", /* 11 */
+ "SYNC FRAME", /* 12 */
+};
+
+char *usbd_device_descriptors[] = {
+ "UNKNOWN", /* 0 */
+ "DEVICE", /* 1 */
+ "CONFIG", /* 2 */
+ "STRING", /* 3 */
+ "INTERFACE", /* 4 */
+ "ENDPOINT", /* 5 */
+ "DEVICE QUALIFIER", /* 6 */
+ "OTHER SPEED", /* 7 */
+ "INTERFACE POWER", /* 8 */
+};
+
+char *usbd_device_status[] = {
+ "USBD_OPENING",
+ "USBD_OK",
+ "USBD_SUSPENDED",
+ "USBD_CLOSING",
+};
+
+
+/* Descriptor support functions ************************************************************** */
+
+
+/**
+ * usbd_get_string - find and return a string descriptor
+ * @index: string index to return
+ *
+ * Find an indexed string and return a pointer to a it.
+ */
+struct usb_string_descriptor *usbd_get_string (__u8 index)
+{
+ if (index >= maxstrings) {
+ return NULL;
+ }
+ return usb_strings[index];
+}
+
+
+/* Access to device descriptor functions ***************************************************** */
+
+
+/* *
+ * usbd_device_configuration_instance - find a configuration instance for this device
+ * @device:
+ * @configuration: index to configuration, 0 - N-1
+ *
+ * Get specifed device configuration. Index should be bConfigurationValue-1.
+ */
+static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device,
+ unsigned int port, unsigned int configuration)
+{
+ if (configuration >= device->configurations)
+ return NULL;
+
+ return device->configuration_instance_array + configuration;
+}
+
+
+/* *
+ * usbd_device_interface_instance
+ * @device:
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ *
+ * Return the specified interface descriptor for the specified device.
+ */
+struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface)
+{
+ struct usb_configuration_instance *configuration_instance;
+
+ if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) {
+ return NULL;
+ }
+ if (interface >= configuration_instance->interfaces) {
+ return NULL;
+ }
+ return configuration_instance->interface_instance_array + interface;
+}
+
+/* *
+ * usbd_device_alternate_descriptor_list
+ * @device:
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ * @alternate: alternate setting
+ *
+ * Return the specified alternate descriptor for the specified device.
+ */
+struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate)
+{
+ struct usb_interface_instance *interface_instance;
+
+ if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) {
+ return NULL;
+ }
+
+ if (alternate >= interface_instance->alternates) {
+ return NULL;
+ }
+
+ return interface_instance->alternates_instance_array + alternate;
+}
+
+
+/* *
+ * usbd_device_device_descriptor
+ * @device: which device
+ * @configuration: index to configuration, 0 - N-1
+ * @port: which port
+ *
+ * Return the specified configuration descriptor for the specified device.
+ */
+struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port)
+{
+ return (device->device_descriptor);
+}
+
+/**
+ * usbd_device_configuration_descriptor
+ * @device: which device
+ * @port: which port
+ * @configuration: index to configuration, 0 - N-1
+ *
+ * Return the specified configuration descriptor for the specified device.
+ */
+struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct
+ usb_device_instance
+ *device, int port, int configuration)
+{
+ struct usb_configuration_instance *configuration_instance;
+ if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) {
+ return NULL;
+ }
+ return (configuration_instance->configuration_descriptor);
+}
+
+
+/**
+ * usbd_device_interface_descriptor
+ * @device: which device
+ * @port: which port
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ * @alternate: alternate setting
+ *
+ * Return the specified interface descriptor for the specified device.
+ */
+struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance
+ *device, int port, int configuration, int interface, int alternate)
+{
+ struct usb_interface_instance *interface_instance;
+ if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) {
+ return NULL;
+ }
+ if ((alternate < 0) || (alternate >= interface_instance->alternates)) {
+ return NULL;
+ }
+ return (interface_instance->alternates_instance_array[alternate].interface_descriptor);
+}
+
+/**
+ * usbd_device_endpoint_descriptor_index
+ * @device: which device
+ * @port: which port
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ * @alternate: index setting
+ * @index: which index
+ *
+ * Return the specified endpoint descriptor for the specified device.
+ */
+struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance
+ *device, int port, int configuration, int interface, int alternate, int index)
+{
+ struct usb_alternate_instance *alternate_instance;
+
+ if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) {
+ return NULL;
+ }
+ if (index >= alternate_instance->endpoints) {
+ return NULL;
+ }
+ return *(alternate_instance->endpoints_descriptor_array + index);
+}
+
+
+/**
+ * usbd_device_endpoint_transfersize
+ * @device: which device
+ * @port: which port
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ * @index: which index
+ *
+ * Return the specified endpoint transfer size;
+ */
+int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index)
+{
+ struct usb_alternate_instance *alternate_instance;
+
+ if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) {
+ return 0;
+ }
+ if (index >= alternate_instance->endpoints) {
+ return 0;
+ }
+ return *(alternate_instance->endpoint_transfersize_array + index);
+}
+
+
+/**
+ * usbd_device_endpoint_descriptor
+ * @device: which device
+ * @port: which port
+ * @configuration: index to configuration, 0 - N-1
+ * @interface: index to interface
+ * @alternate: alternate setting
+ * @endpoint: which endpoint
+ *
+ * Return the specified endpoint descriptor for the specified device.
+ */
+struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint)
+{
+ struct usb_endpoint_descriptor *endpoint_descriptor;
+ int i;
+
+ for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) {
+ if (endpoint_descriptor->bEndpointAddress == endpoint) {
+ return endpoint_descriptor;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * usbd_endpoint_halted
+ * @device: point to struct usb_device_instance
+ * @endpoint: endpoint to check
+ *
+ * Return non-zero if endpoint is halted.
+ */
+int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint)
+{
+ return (device->status == USB_STATUS_HALT);
+}
+
+
+/**
+ * usbd_rcv_complete - complete a receive
+ * @endpoint:
+ * @len:
+ * @urb_bad:
+ *
+ * Called from rcv interrupt to complete.
+ */
+void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad)
+{
+ if (endpoint) {
+ struct urb *rcv_urb;
+
+ /*usbdbg("len: %d urb: %p\n", len, endpoint->rcv_urb); */
+
+ /* if we had an urb then update actual_length, dispatch if neccessary */
+ if ((rcv_urb = endpoint->rcv_urb)) {
+
+ /*usbdbg("actual: %d buffer: %d\n", */
+ /*rcv_urb->actual_length, rcv_urb->buffer_length); */
+
+ /* check the urb is ok, are we adding data less than the packetsize */
+ if (!urb_bad && (len <= endpoint->rcv_packetSize)) {
+ /*usbdbg("updating actual_length by %d\n",len); */
+
+ /* increment the received data size */
+ rcv_urb->actual_length += len;
+
+ } else {
+ usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n",
+ rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad);
+
+ rcv_urb->actual_length = 0;
+ rcv_urb->status = RECV_ERROR;
+ }
+ } else {
+ usberr("no rcv_urb!");
+ }
+ } else {
+ usberr("no endpoint!");
+ }
+
+}
+
+/**
+ * usbd_tx_complete - complete a transmit
+ * @endpoint:
+ * @resetart:
+ *
+ * Called from tx interrupt to complete.
+ */
+void usbd_tx_complete (struct usb_endpoint_instance *endpoint)
+{
+ if (endpoint) {
+ struct urb *tx_urb;
+
+ /* if we have a tx_urb advance or reset, finish if complete */
+ if ((tx_urb = endpoint->tx_urb)) {
+ int sent = endpoint->last;
+ endpoint->sent += sent;
+ endpoint->last -= sent;
+
+ if( (endpoint->tx_urb->actual_length - endpoint->sent) <= 0 ) {
+ tx_urb->actual_length = 0;
+ endpoint->sent = 0;
+ endpoint->last = 0;
+
+ /* Remove from active, save for re-use */
+ urb_detach(tx_urb);
+ urb_append(&endpoint->done, tx_urb);
+ /*usbdbg("done->next %p, tx_urb %p, done %p", */
+ /* endpoint->done.next, tx_urb, &endpoint->done); */
+
+ endpoint->tx_urb = first_urb_detached(&endpoint->tx);
+ if( endpoint->tx_urb ) {
+ endpoint->tx_queue--;
+ usbdbg("got urb from tx list");
+ }
+ if( !endpoint->tx_urb ) {
+ /*usbdbg("taking urb from done list"); */
+ endpoint->tx_urb = first_urb_detached(&endpoint->done);
+ }
+ if( !endpoint->tx_urb ) {
+ usbdbg("allocating new urb for tx_urb");
+ endpoint->tx_urb = usbd_alloc_urb(tx_urb->device, endpoint);
+ }
+ }
+ }
+ }
+}
+
+/* URB linked list functions ***************************************************** */
+
+/*
+ * Initialize an urb_link to be a single element list.
+ * If the urb_link is being used as a distinguished list head
+ * the list is empty when the head is the only link in the list.
+ */
+void urb_link_init (urb_link * ul)
+{
+ if (ul) {
+ ul->prev = ul->next = ul;
+ }
+}
+
+/*
+ * Detach an urb_link from a list, and set it
+ * up as a single element list, so no dangling
+ * pointers can be followed, and so it can be
+ * joined to another list if so desired.
+ */
+void urb_detach (struct urb *urb)
+{
+ if (urb) {
+ urb_link *ul = &urb->link;
+ ul->next->prev = ul->prev;
+ ul->prev->next = ul->next;
+ urb_link_init (ul);
+ }
+}
+
+/*
+ * Return the first urb_link in a list with a distinguished
+ * head "hd", or NULL if the list is empty. This will also
+ * work as a predicate, returning NULL if empty, and non-NULL
+ * otherwise.
+ */
+urb_link *first_urb_link (urb_link * hd)
+{
+ urb_link *nx;
+ if (NULL != hd && NULL != (nx = hd->next) && nx != hd) {
+ /* There is at least one element in the list */
+ /* (besides the distinguished head). */
+ return (nx);
+ }
+ /* The list is empty */
+ return (NULL);
+}
+
+/*
+ * Return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ */
+struct urb *first_urb (urb_link * hd)
+{
+ urb_link *nx;
+ if (NULL == (nx = first_urb_link (hd))) {
+ /* The list is empty */
+ return (NULL);
+ }
+ return (p2surround (struct urb, link, nx));
+}
+
+/*
+ * Detach and return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ *
+ */
+struct urb *first_urb_detached (urb_link * hd)
+{
+ struct urb *urb;
+ if ((urb = first_urb (hd))) {
+ urb_detach (urb);
+ }
+ return urb;
+}
+
+
+/*
+ * Append an urb_link (or a whole list of
+ * urb_links) to the tail of another list
+ * of urb_links.
+ */
+void urb_append (urb_link * hd, struct urb *urb)
+{
+ if (hd && urb) {
+ urb_link *new = &urb->link;
+
+ /* This allows the new urb to be a list of urbs, */
+ /* with new pointing at the first, but the link */
+ /* must be initialized. */
+ /* Order is important here... */
+ urb_link *pul = hd->prev;
+ new->prev->next = hd;
+ hd->prev = new->prev;
+ new->prev = pul;
+ pul->next = new;
+ }
+}
+
+/* URB create/destroy functions ***************************************************** */
+
+/**
+ * usbd_alloc_urb - allocate an URB appropriate for specified endpoint
+ * @device: device instance
+ * @endpoint: endpoint
+ *
+ * Allocate an urb structure. The usb device urb structure is used to
+ * contain all data associated with a transfer, including a setup packet for
+ * control transfers.
+ *
+ * NOTE: endpoint_address MUST contain a direction flag.
+ */
+struct urb *usbd_alloc_urb (struct usb_device_instance *device,
+ struct usb_endpoint_instance *endpoint)
+{
+ struct urb *urb;
+
+ if (!(urb = (struct urb *) malloc (sizeof (struct urb)))) {
+ usberr (" F A T A L: malloc(%zu) FAILED!!!!",
+ sizeof (struct urb));
+ return NULL;
+ }
+
+ /* Fill in known fields */
+ memset (urb, 0, sizeof (struct urb));
+ urb->endpoint = endpoint;
+ urb->device = device;
+ urb->buffer = (u8 *) urb->buffer_data;
+ urb->buffer_length = sizeof (urb->buffer_data);
+
+ urb_link_init (&urb->link);
+
+ return urb;
+}
+
+/**
+ * usbd_dealloc_urb - deallocate an URB and associated buffer
+ * @urb: pointer to an urb structure
+ *
+ * Deallocate an urb structure and associated data.
+ */
+void usbd_dealloc_urb (struct urb *urb)
+{
+ if (urb) {
+ free (urb);
+ }
+}
+
+/* Event signaling functions ***************************************************** */
+
+/**
+ * usbd_device_event - called to respond to various usb events
+ * @device: pointer to struct device
+ * @event: event to respond to
+ *
+ * Used by a Bus driver to indicate an event.
+ */
+void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data)
+{
+ usb_device_state_t state;
+
+ if (!device || !device->bus) {
+ usberr("(%p,%d) NULL device or device->bus", device, event);
+ return;
+ }
+
+ state = device->device_state;
+
+ usbinfo("%s", usbd_device_events[event]);
+
+ switch (event) {
+ case DEVICE_UNKNOWN:
+ break;
+ case DEVICE_INIT:
+ device->device_state = STATE_INIT;
+ break;
+
+ case DEVICE_CREATE:
+ device->device_state = STATE_ATTACHED;
+ break;
+
+ case DEVICE_HUB_CONFIGURED:
+ device->device_state = STATE_POWERED;
+ break;
+
+ case DEVICE_RESET:
+ device->device_state = STATE_DEFAULT;
+ device->address = 0;
+ break;
+
+ case DEVICE_ADDRESS_ASSIGNED:
+ device->device_state = STATE_ADDRESSED;
+ break;
+
+ case DEVICE_CONFIGURED:
+ device->device_state = STATE_CONFIGURED;
+ break;
+
+ case DEVICE_DE_CONFIGURED:
+ device->device_state = STATE_ADDRESSED;
+ break;
+
+ case DEVICE_BUS_INACTIVE:
+ if (device->status != USBD_CLOSING) {
+ device->status = USBD_SUSPENDED;
+ }
+ break;
+ case DEVICE_BUS_ACTIVITY:
+ if (device->status != USBD_CLOSING) {
+ device->status = USBD_OK;
+ }
+ break;
+
+ case DEVICE_SET_INTERFACE:
+ break;
+ case DEVICE_SET_FEATURE:
+ break;
+ case DEVICE_CLEAR_FEATURE:
+ break;
+
+ case DEVICE_POWER_INTERRUPTION:
+ device->device_state = STATE_POWERED;
+ break;
+ case DEVICE_HUB_RESET:
+ device->device_state = STATE_ATTACHED;
+ break;
+ case DEVICE_DESTROY:
+ device->device_state = STATE_UNKNOWN;
+ break;
+
+ case DEVICE_FUNCTION_PRIVATE:
+ break;
+
+ default:
+ usbdbg("event %d - not handled",event);
+ break;
+ }
+ debug("%s event: %d oldstate: %d newstate: %d status: %d address: %d",
+ device->name, event, state,
+ device->device_state, device->status, device->address);
+
+ /* tell the bus interface driver */
+ if( device->event ) {
+ /* usbdbg("calling device->event"); */
+ device->event(device, event, data);
+ }
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/designware_udc.c b/src/roms/u-boot/drivers/usb/gadget/designware_udc.c
new file mode 100644
index 0000000..b7c1038
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/designware_udc.c
@@ -0,0 +1,1019 @@
+/*
+ * Based on drivers/usb/gadget/omap1510_udc.c
+ * TI OMAP1510 USB bus interface driver
+ *
+ * (C) Copyright 2009
+ * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+
+#include <usbdevice.h>
+#include "ep0.h"
+#include <usb/designware_udc.h>
+#include <usb/udc.h>
+#include <asm/arch/hardware.h>
+
+#define UDC_INIT_MDELAY 80 /* Device settle delay */
+
+/* Some kind of debugging output... */
+#ifndef DEBUG_DWUSBTTY
+#define UDCDBG(str)
+#define UDCDBGA(fmt, args...)
+#else
+#define UDCDBG(str) serial_printf(str "\n")
+#define UDCDBGA(fmt, args...) serial_printf(fmt "\n", ##args)
+#endif
+
+static struct urb *ep0_urb;
+static struct usb_device_instance *udc_device;
+
+static struct plug_regs *const plug_regs_p =
+ (struct plug_regs * const)CONFIG_SYS_PLUG_BASE;
+static struct udc_regs *const udc_regs_p =
+ (struct udc_regs * const)CONFIG_SYS_USBD_BASE;
+static struct udc_endp_regs *const outep_regs_p =
+ &((struct udc_regs * const)CONFIG_SYS_USBD_BASE)->out_regs[0];
+static struct udc_endp_regs *const inep_regs_p =
+ &((struct udc_regs * const)CONFIG_SYS_USBD_BASE)->in_regs[0];
+
+/*
+ * udc_state_transition - Write the next packet to TxFIFO.
+ * @initial: Initial state.
+ * @final: Final state.
+ *
+ * Helper function to implement device state changes. The device states and
+ * the events that transition between them are:
+ *
+ * STATE_ATTACHED
+ * || /\
+ * \/ ||
+ * DEVICE_HUB_CONFIGURED DEVICE_HUB_RESET
+ * || /\
+ * \/ ||
+ * STATE_POWERED
+ * || /\
+ * \/ ||
+ * DEVICE_RESET DEVICE_POWER_INTERRUPTION
+ * || /\
+ * \/ ||
+ * STATE_DEFAULT
+ * || /\
+ * \/ ||
+ * DEVICE_ADDRESS_ASSIGNED DEVICE_RESET
+ * || /\
+ * \/ ||
+ * STATE_ADDRESSED
+ * || /\
+ * \/ ||
+ * DEVICE_CONFIGURED DEVICE_DE_CONFIGURED
+ * || /\
+ * \/ ||
+ * STATE_CONFIGURED
+ *
+ * udc_state_transition transitions up (in the direction from STATE_ATTACHED
+ * to STATE_CONFIGURED) from the specified initial state to the specified final
+ * state, passing through each intermediate state on the way. If the initial
+ * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then
+ * no state transitions will take place.
+ *
+ * udc_state_transition also transitions down (in the direction from
+ * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the
+ * specified final state, passing through each intermediate state on the way.
+ * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final
+ * state, then no state transitions will take place.
+ *
+ * This function must only be called with interrupts disabled.
+ */
+static void udc_state_transition(usb_device_state_t initial,
+ usb_device_state_t final)
+{
+ if (initial < final) {
+ switch (initial) {
+ case STATE_ATTACHED:
+ usbd_device_event_irq(udc_device,
+ DEVICE_HUB_CONFIGURED, 0);
+ if (final == STATE_POWERED)
+ break;
+ case STATE_POWERED:
+ usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
+ if (final == STATE_DEFAULT)
+ break;
+ case STATE_DEFAULT:
+ usbd_device_event_irq(udc_device,
+ DEVICE_ADDRESS_ASSIGNED, 0);
+ if (final == STATE_ADDRESSED)
+ break;
+ case STATE_ADDRESSED:
+ usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0);
+ case STATE_CONFIGURED:
+ break;
+ default:
+ break;
+ }
+ } else if (initial > final) {
+ switch (initial) {
+ case STATE_CONFIGURED:
+ usbd_device_event_irq(udc_device,
+ DEVICE_DE_CONFIGURED, 0);
+ if (final == STATE_ADDRESSED)
+ break;
+ case STATE_ADDRESSED:
+ usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
+ if (final == STATE_DEFAULT)
+ break;
+ case STATE_DEFAULT:
+ usbd_device_event_irq(udc_device,
+ DEVICE_POWER_INTERRUPTION, 0);
+ if (final == STATE_POWERED)
+ break;
+ case STATE_POWERED:
+ usbd_device_event_irq(udc_device, DEVICE_HUB_RESET, 0);
+ case STATE_ATTACHED:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* Stall endpoint */
+static void udc_stall_ep(u32 ep_num)
+{
+ writel(readl(&inep_regs_p[ep_num].endp_cntl) | ENDP_CNTL_STALL,
+ &inep_regs_p[ep_num].endp_cntl);
+
+ writel(readl(&outep_regs_p[ep_num].endp_cntl) | ENDP_CNTL_STALL,
+ &outep_regs_p[ep_num].endp_cntl);
+}
+
+static void *get_fifo(int ep_num, int in)
+{
+ u32 *fifo_ptr = (u32 *)CONFIG_SYS_FIFO_BASE;
+
+ switch (ep_num) {
+ case UDC_EP3:
+ fifo_ptr += readl(&inep_regs_p[1].endp_bsorfn);
+ /* break intentionally left out */
+
+ case UDC_EP1:
+ fifo_ptr += readl(&inep_regs_p[0].endp_bsorfn);
+ /* break intentionally left out */
+
+ case UDC_EP0:
+ default:
+ if (in) {
+ fifo_ptr +=
+ readl(&outep_regs_p[2].endp_maxpacksize) >> 16;
+ /* break intentionally left out */
+ } else {
+ break;
+ }
+
+ case UDC_EP2:
+ fifo_ptr += readl(&outep_regs_p[0].endp_maxpacksize) >> 16;
+ /* break intentionally left out */
+ }
+
+ return (void *)fifo_ptr;
+}
+
+static int usbgetpckfromfifo(int epNum, u8 *bufp, u32 len)
+{
+ u8 *fifo_ptr = (u8 *)get_fifo(epNum, 0);
+ u32 i, nw, nb;
+ u32 *wrdp;
+ u8 *bytp;
+ u32 tmp[128];
+
+ if (readl(&udc_regs_p->dev_stat) & DEV_STAT_RXFIFO_EMPTY)
+ return -1;
+
+ nw = len / sizeof(u32);
+ nb = len % sizeof(u32);
+
+ /* use tmp buf if bufp is not word aligned */
+ if ((int)bufp & 0x3)
+ wrdp = (u32 *)&tmp[0];
+ else
+ wrdp = (u32 *)bufp;
+
+ for (i = 0; i < nw; i++) {
+ writel(readl(fifo_ptr), wrdp);
+ wrdp++;
+ }
+
+ bytp = (u8 *)wrdp;
+ for (i = 0; i < nb; i++) {
+ writeb(readb(fifo_ptr), bytp);
+ fifo_ptr++;
+ bytp++;
+ }
+ readl(&outep_regs_p[epNum].write_done);
+
+ /* copy back tmp buffer to bufp if bufp is not word aligned */
+ if ((int)bufp & 0x3)
+ memcpy(bufp, tmp, len);
+
+ return 0;
+}
+
+static void usbputpcktofifo(int epNum, u8 *bufp, u32 len)
+{
+ u32 i, nw, nb;
+ u32 *wrdp;
+ u8 *bytp;
+ u8 *fifo_ptr = get_fifo(epNum, 1);
+
+ nw = len / sizeof(int);
+ nb = len % sizeof(int);
+ wrdp = (u32 *)bufp;
+ for (i = 0; i < nw; i++) {
+ writel(*wrdp, fifo_ptr);
+ wrdp++;
+ }
+
+ bytp = (u8 *)wrdp;
+ for (i = 0; i < nb; i++) {
+ writeb(*bytp, fifo_ptr);
+ fifo_ptr++;
+ bytp++;
+ }
+}
+
+/*
+ * dw_write_noniso_tx_fifo - Write the next packet to TxFIFO.
+ * @endpoint: Endpoint pointer.
+ *
+ * If the endpoint has an active tx_urb, then the next packet of data from the
+ * URB is written to the tx FIFO. The total amount of data in the urb is given
+ * by urb->actual_length. The maximum amount of data that can be sent in any
+ * one packet is given by endpoint->tx_packetSize. The number of data bytes
+ * from this URB that have already been transmitted is given by endpoint->sent.
+ * endpoint->last is updated by this routine with the number of data bytes
+ * transmitted in this packet.
+ *
+ */
+static void dw_write_noniso_tx_fifo(struct usb_endpoint_instance
+ *endpoint)
+{
+ struct urb *urb = endpoint->tx_urb;
+ int align;
+
+ if (urb) {
+ u32 last;
+
+ UDCDBGA("urb->buffer %p, buffer_length %d, actual_length %d",
+ urb->buffer, urb->buffer_length, urb->actual_length);
+
+ last = MIN(urb->actual_length - endpoint->sent,
+ endpoint->tx_packetSize);
+
+ if (last) {
+ u8 *cp = urb->buffer + endpoint->sent;
+
+ /*
+ * This ensures that USBD packet fifo is accessed
+ * - through word aligned pointer or
+ * - through non word aligned pointer but only
+ * with a max length to make the next packet
+ * word aligned
+ */
+
+ align = ((ulong)cp % sizeof(int));
+ if (align)
+ last = MIN(last, sizeof(int) - align);
+
+ UDCDBGA("endpoint->sent %d, tx_packetSize %d, last %d",
+ endpoint->sent, endpoint->tx_packetSize, last);
+
+ usbputpcktofifo(endpoint->endpoint_address &
+ USB_ENDPOINT_NUMBER_MASK, cp, last);
+ }
+ endpoint->last = last;
+ }
+}
+
+/*
+ * Handle SETUP USB interrupt.
+ * This function implements TRM Figure 14-14.
+ */
+static void dw_udc_setup(struct usb_endpoint_instance *endpoint)
+{
+ u8 *datap = (u8 *)&ep0_urb->device_request;
+ int ep_addr = endpoint->endpoint_address;
+
+ UDCDBG("-> Entering device setup");
+ usbgetpckfromfifo(ep_addr, datap, 8);
+
+ /* Try to process setup packet */
+ if (ep0_recv_setup(ep0_urb)) {
+ /* Not a setup packet, stall next EP0 transaction */
+ udc_stall_ep(0);
+ UDCDBG("can't parse setup packet, still waiting for setup");
+ return;
+ }
+
+ /* Check direction */
+ if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK)
+ == USB_REQ_HOST2DEVICE) {
+ UDCDBG("control write on EP0");
+ if (le16_to_cpu(ep0_urb->device_request.wLength)) {
+ /* Stall this request */
+ UDCDBG("Stalling unsupported EP0 control write data "
+ "stage.");
+ udc_stall_ep(0);
+ }
+ } else {
+
+ UDCDBG("control read on EP0");
+ /*
+ * The ep0_recv_setup function has already placed our response
+ * packet data in ep0_urb->buffer and the packet length in
+ * ep0_urb->actual_length.
+ */
+ endpoint->tx_urb = ep0_urb;
+ endpoint->sent = 0;
+ /*
+ * Write packet data to the FIFO. dw_write_noniso_tx_fifo
+ * will update endpoint->last with the number of bytes written
+ * to the FIFO.
+ */
+ dw_write_noniso_tx_fifo(endpoint);
+
+ writel(0x0, &inep_regs_p[ep_addr].write_done);
+ }
+
+ udc_unset_nak(endpoint->endpoint_address);
+
+ UDCDBG("<- Leaving device setup");
+}
+
+/*
+ * Handle endpoint 0 RX interrupt
+ */
+static void dw_udc_ep0_rx(struct usb_endpoint_instance *endpoint)
+{
+ u8 dummy[64];
+
+ UDCDBG("RX on EP0");
+
+ /* Check direction */
+ if ((ep0_urb->device_request.bmRequestType
+ & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) {
+ /*
+ * This rx interrupt must be for a control write data
+ * stage packet.
+ *
+ * We don't support control write data stages.
+ * We should never end up here.
+ */
+
+ UDCDBG("Stalling unexpected EP0 control write "
+ "data stage packet");
+ udc_stall_ep(0);
+ } else {
+ /*
+ * This rx interrupt must be for a control read status
+ * stage packet.
+ */
+ UDCDBG("ACK on EP0 control read status stage packet");
+ u32 len = (readl(&outep_regs_p[0].endp_status) >> 11) & 0xfff;
+ usbgetpckfromfifo(0, dummy, len);
+ }
+}
+
+/*
+ * Handle endpoint 0 TX interrupt
+ */
+static void dw_udc_ep0_tx(struct usb_endpoint_instance *endpoint)
+{
+ struct usb_device_request *request = &ep0_urb->device_request;
+ int ep_addr;
+
+ UDCDBG("TX on EP0");
+
+ /* Check direction */
+ if ((request->bmRequestType & USB_REQ_DIRECTION_MASK) ==
+ USB_REQ_HOST2DEVICE) {
+ /*
+ * This tx interrupt must be for a control write status
+ * stage packet.
+ */
+ UDCDBG("ACK on EP0 control write status stage packet");
+ } else {
+ /*
+ * This tx interrupt must be for a control read data
+ * stage packet.
+ */
+ int wLength = le16_to_cpu(request->wLength);
+
+ /*
+ * Update our count of bytes sent so far in this
+ * transfer.
+ */
+ endpoint->sent += endpoint->last;
+
+ /*
+ * We are finished with this transfer if we have sent
+ * all of the bytes in our tx urb (urb->actual_length)
+ * unless we need a zero-length terminating packet. We
+ * need a zero-length terminating packet if we returned
+ * fewer bytes than were requested (wLength) by the host,
+ * and the number of bytes we returned is an exact
+ * multiple of the packet size endpoint->tx_packetSize.
+ */
+ if ((endpoint->sent == ep0_urb->actual_length) &&
+ ((ep0_urb->actual_length == wLength) ||
+ (endpoint->last != endpoint->tx_packetSize))) {
+ /* Done with control read data stage. */
+ UDCDBG("control read data stage complete");
+ } else {
+ /*
+ * We still have another packet of data to send
+ * in this control read data stage or else we
+ * need a zero-length terminating packet.
+ */
+ UDCDBG("ACK control read data stage packet");
+ dw_write_noniso_tx_fifo(endpoint);
+
+ ep_addr = endpoint->endpoint_address;
+ writel(0x0, &inep_regs_p[ep_addr].write_done);
+ }
+ }
+}
+
+static struct usb_endpoint_instance *dw_find_ep(int ep)
+{
+ int i;
+
+ for (i = 0; i < udc_device->bus->max_endpoints; i++) {
+ if ((udc_device->bus->endpoint_array[i].endpoint_address &
+ USB_ENDPOINT_NUMBER_MASK) == ep)
+ return &udc_device->bus->endpoint_array[i];
+ }
+ return NULL;
+}
+
+/*
+ * Handle RX transaction on non-ISO endpoint.
+ * The ep argument is a physical endpoint number for a non-ISO IN endpoint
+ * in the range 1 to 15.
+ */
+static void dw_udc_epn_rx(int ep)
+{
+ int nbytes = 0;
+ struct urb *urb;
+ struct usb_endpoint_instance *endpoint = dw_find_ep(ep);
+
+ if (endpoint) {
+ urb = endpoint->rcv_urb;
+
+ if (urb) {
+ u8 *cp = urb->buffer + urb->actual_length;
+
+ nbytes = (readl(&outep_regs_p[ep].endp_status) >> 11) &
+ 0xfff;
+ usbgetpckfromfifo(ep, cp, nbytes);
+ usbd_rcv_complete(endpoint, nbytes, 0);
+ }
+ }
+}
+
+/*
+ * Handle TX transaction on non-ISO endpoint.
+ * The ep argument is a physical endpoint number for a non-ISO IN endpoint
+ * in the range 16 to 30.
+ */
+static void dw_udc_epn_tx(int ep)
+{
+ struct usb_endpoint_instance *endpoint = dw_find_ep(ep);
+
+ if (!endpoint)
+ return;
+
+ /*
+ * We need to transmit a terminating zero-length packet now if
+ * we have sent all of the data in this URB and the transfer
+ * size was an exact multiple of the packet size.
+ */
+ if (endpoint->tx_urb &&
+ (endpoint->last == endpoint->tx_packetSize) &&
+ (endpoint->tx_urb->actual_length - endpoint->sent -
+ endpoint->last == 0)) {
+ /* handle zero length packet here */
+ writel(0x0, &inep_regs_p[ep].write_done);
+
+ }
+
+ if (endpoint->tx_urb && endpoint->tx_urb->actual_length) {
+ /* retire the data that was just sent */
+ usbd_tx_complete(endpoint);
+ /*
+ * Check to see if we have more data ready to transmit
+ * now.
+ */
+ if (endpoint->tx_urb && endpoint->tx_urb->actual_length) {
+ /* write data to FIFO */
+ dw_write_noniso_tx_fifo(endpoint);
+ writel(0x0, &inep_regs_p[ep].write_done);
+
+ } else if (endpoint->tx_urb
+ && (endpoint->tx_urb->actual_length == 0)) {
+ /* udc_set_nak(ep); */
+ }
+ }
+}
+
+/*
+ * Start of public functions.
+ */
+
+/* Called to start packet transmission. */
+int udc_endpoint_write(struct usb_endpoint_instance *endpoint)
+{
+ udc_unset_nak(endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK);
+ return 0;
+}
+
+/* Start to initialize h/w stuff */
+int udc_init(void)
+{
+ int i;
+ u32 plug_st;
+
+ udc_device = NULL;
+
+ UDCDBG("starting");
+
+ readl(&plug_regs_p->plug_pending);
+
+ for (i = 0; i < UDC_INIT_MDELAY; i++)
+ udelay(1000);
+
+ plug_st = readl(&plug_regs_p->plug_state);
+ writel(plug_st | PLUG_STATUS_EN, &plug_regs_p->plug_state);
+
+ writel(~0x0, &udc_regs_p->endp_int);
+ writel(~0x0, &udc_regs_p->dev_int_mask);
+ writel(~0x0, &udc_regs_p->endp_int_mask);
+
+#ifndef CONFIG_USBD_HS
+ writel(DEV_CONF_FS_SPEED | DEV_CONF_REMWAKEUP | DEV_CONF_SELFPOW |
+ DEV_CONF_PHYINT_16, &udc_regs_p->dev_conf);
+#else
+ writel(DEV_CONF_HS_SPEED | DEV_CONF_REMWAKEUP | DEV_CONF_SELFPOW |
+ DEV_CONF_PHYINT_16, &udc_regs_p->dev_conf);
+#endif
+
+ writel(DEV_CNTL_SOFTDISCONNECT, &udc_regs_p->dev_cntl);
+
+ /* Clear all interrupts pending */
+ writel(DEV_INT_MSK, &udc_regs_p->dev_int);
+
+ return 0;
+}
+
+int is_usbd_high_speed(void)
+{
+ return (readl(&udc_regs_p->dev_stat) & DEV_STAT_ENUM) ? 0 : 1;
+}
+
+/*
+ * udc_setup_ep - setup endpoint
+ * Associate a physical endpoint with endpoint_instance
+ */
+void udc_setup_ep(struct usb_device_instance *device,
+ u32 ep, struct usb_endpoint_instance *endpoint)
+{
+ UDCDBGA("setting up endpoint addr %x", endpoint->endpoint_address);
+ int ep_addr;
+ int ep_num, ep_type;
+ int packet_size;
+ int buffer_size;
+ int attributes;
+ char *tt;
+ u32 endp_intmask;
+
+ if ((ep != 0) && (udc_device->device_state < STATE_ADDRESSED))
+ return;
+
+ tt = getenv("usbtty");
+ if (!tt)
+ tt = "generic";
+
+ ep_addr = endpoint->endpoint_address;
+ ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+
+ if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+ /* IN endpoint */
+ packet_size = endpoint->tx_packetSize;
+ buffer_size = packet_size * 2;
+ attributes = endpoint->tx_attributes;
+ } else {
+ /* OUT endpoint */
+ packet_size = endpoint->rcv_packetSize;
+ buffer_size = packet_size * 2;
+ attributes = endpoint->rcv_attributes;
+ }
+
+ switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ ep_type = ENDP_EPTYPE_CNTL;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ default:
+ ep_type = ENDP_EPTYPE_BULK;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ ep_type = ENDP_EPTYPE_INT;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ ep_type = ENDP_EPTYPE_ISO;
+ break;
+ }
+
+ struct udc_endp_regs *out_p = &outep_regs_p[ep_num];
+ struct udc_endp_regs *in_p = &inep_regs_p[ep_num];
+
+ if (!ep_addr) {
+ /* Setup endpoint 0 */
+ buffer_size = packet_size;
+
+ writel(readl(&in_p->endp_cntl) | ENDP_CNTL_CNAK,
+ &in_p->endp_cntl);
+
+ writel(readl(&out_p->endp_cntl) | ENDP_CNTL_CNAK,
+ &out_p->endp_cntl);
+
+ writel(ENDP_CNTL_CONTROL | ENDP_CNTL_FLUSH, &in_p->endp_cntl);
+
+ writel(buffer_size / sizeof(int), &in_p->endp_bsorfn);
+
+ writel(packet_size, &in_p->endp_maxpacksize);
+
+ writel(ENDP_CNTL_CONTROL | ENDP_CNTL_RRDY, &out_p->endp_cntl);
+
+ writel(packet_size | ((buffer_size / sizeof(int)) << 16),
+ &out_p->endp_maxpacksize);
+
+ } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+ /* Setup the IN endpoint */
+ writel(0x0, &in_p->endp_status);
+ writel((ep_type << 4) | ENDP_CNTL_RRDY, &in_p->endp_cntl);
+ writel(buffer_size / sizeof(int), &in_p->endp_bsorfn);
+ writel(packet_size, &in_p->endp_maxpacksize);
+
+ if (!strcmp(tt, "cdc_acm")) {
+ if (ep_type == ENDP_EPTYPE_INT) {
+ /* Conf no. 1 Interface no. 0 */
+ writel((packet_size << 19) |
+ ENDP_EPDIR_IN | (1 << 7) |
+ (0 << 11) | (ep_type << 5) | ep_num,
+ &udc_regs_p->udc_endp_reg[ep_num]);
+ } else {
+ /* Conf no. 1 Interface no. 1 */
+ writel((packet_size << 19) |
+ ENDP_EPDIR_IN | (1 << 7) |
+ (1 << 11) | (ep_type << 5) | ep_num,
+ &udc_regs_p->udc_endp_reg[ep_num]);
+ }
+ } else {
+ /* Conf no. 1 Interface no. 0 */
+ writel((packet_size << 19) |
+ ENDP_EPDIR_IN | (1 << 7) |
+ (0 << 11) | (ep_type << 5) | ep_num,
+ &udc_regs_p->udc_endp_reg[ep_num]);
+ }
+
+ } else {
+ /* Setup the OUT endpoint */
+ writel(0x0, &out_p->endp_status);
+ writel((ep_type << 4) | ENDP_CNTL_RRDY, &out_p->endp_cntl);
+ writel(packet_size | ((buffer_size / sizeof(int)) << 16),
+ &out_p->endp_maxpacksize);
+
+ if (!strcmp(tt, "cdc_acm")) {
+ writel((packet_size << 19) |
+ ENDP_EPDIR_OUT | (1 << 7) |
+ (1 << 11) | (ep_type << 5) | ep_num,
+ &udc_regs_p->udc_endp_reg[ep_num]);
+ } else {
+ writel((packet_size << 19) |
+ ENDP_EPDIR_OUT | (1 << 7) |
+ (0 << 11) | (ep_type << 5) | ep_num,
+ &udc_regs_p->udc_endp_reg[ep_num]);
+ }
+
+ }
+
+ endp_intmask = readl(&udc_regs_p->endp_int_mask);
+ endp_intmask &= ~((1 << ep_num) | 0x10000 << ep_num);
+ writel(endp_intmask, &udc_regs_p->endp_int_mask);
+}
+
+/* Turn on the USB connection by enabling the pullup resistor */
+void udc_connect(void)
+{
+ u32 plug_st, dev_cntl;
+
+ dev_cntl = readl(&udc_regs_p->dev_cntl);
+ dev_cntl |= DEV_CNTL_SOFTDISCONNECT;
+ writel(dev_cntl, &udc_regs_p->dev_cntl);
+
+ udelay(1000);
+
+ dev_cntl = readl(&udc_regs_p->dev_cntl);
+ dev_cntl &= ~DEV_CNTL_SOFTDISCONNECT;
+ writel(dev_cntl, &udc_regs_p->dev_cntl);
+
+ plug_st = readl(&plug_regs_p->plug_state);
+ plug_st &= ~(PLUG_STATUS_PHY_RESET | PLUG_STATUS_PHY_MODE);
+ writel(plug_st, &plug_regs_p->plug_state);
+}
+
+/* Turn off the USB connection by disabling the pullup resistor */
+void udc_disconnect(void)
+{
+ u32 plug_st;
+
+ writel(DEV_CNTL_SOFTDISCONNECT, &udc_regs_p->dev_cntl);
+
+ plug_st = readl(&plug_regs_p->plug_state);
+ plug_st |= (PLUG_STATUS_PHY_RESET | PLUG_STATUS_PHY_MODE);
+ writel(plug_st, &plug_regs_p->plug_state);
+}
+
+/* Switch on the UDC */
+void udc_enable(struct usb_device_instance *device)
+{
+ UDCDBGA("enable device %p, status %d", device, device->status);
+
+ /* Save the device structure pointer */
+ udc_device = device;
+
+ /* Setup ep0 urb */
+ if (!ep0_urb) {
+ ep0_urb =
+ usbd_alloc_urb(udc_device, udc_device->bus->endpoint_array);
+ } else {
+ serial_printf("udc_enable: ep0_urb already allocated %p\n",
+ ep0_urb);
+ }
+
+ writel(DEV_INT_SOF, &udc_regs_p->dev_int_mask);
+}
+
+/**
+ * udc_startup - allow udc code to do any additional startup
+ */
+void udc_startup_events(struct usb_device_instance *device)
+{
+ /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */
+ usbd_device_event_irq(device, DEVICE_INIT, 0);
+
+ /*
+ * The DEVICE_CREATE event puts the USB device in the state
+ * STATE_ATTACHED.
+ */
+ usbd_device_event_irq(device, DEVICE_CREATE, 0);
+
+ /*
+ * Some USB controller driver implementations signal
+ * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here.
+ * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED,
+ * and DEVICE_RESET causes a transition to the state STATE_DEFAULT.
+ * The DW USB client controller has the capability to detect when the
+ * USB cable is connected to a powered USB bus, so we will defer the
+ * DEVICE_HUB_CONFIGURED and DEVICE_RESET events until later.
+ */
+
+ udc_enable(device);
+}
+
+/*
+ * Plug detection interrupt handling
+ */
+static void dw_udc_plug_irq(void)
+{
+ if (readl(&plug_regs_p->plug_state) & PLUG_STATUS_ATTACHED) {
+ /*
+ * USB cable attached
+ * Turn off PHY reset bit (PLUG detect).
+ * Switch PHY opmode to normal operation (PLUG detect).
+ */
+ udc_connect();
+ writel(DEV_INT_SOF, &udc_regs_p->dev_int_mask);
+
+ UDCDBG("device attached and powered");
+ udc_state_transition(udc_device->device_state, STATE_POWERED);
+ } else {
+ writel(~0x0, &udc_regs_p->dev_int_mask);
+
+ UDCDBG("device detached or unpowered");
+ udc_state_transition(udc_device->device_state, STATE_ATTACHED);
+ }
+}
+
+/*
+ * Device interrupt handling
+ */
+static void dw_udc_dev_irq(void)
+{
+ if (readl(&udc_regs_p->dev_int) & DEV_INT_USBRESET) {
+ writel(~0x0, &udc_regs_p->endp_int_mask);
+
+ writel(readl(&inep_regs_p[0].endp_cntl) | ENDP_CNTL_FLUSH,
+ &inep_regs_p[0].endp_cntl);
+
+ writel(DEV_INT_USBRESET, &udc_regs_p->dev_int);
+
+ /*
+ * This endpoint0 specific register can be programmed only
+ * after the phy clock is initialized
+ */
+ writel((EP0_MAX_PACKET_SIZE << 19) | ENDP_EPTYPE_CNTL,
+ &udc_regs_p->udc_endp_reg[0]);
+
+ UDCDBG("device reset in progess");
+ udc_state_transition(udc_device->device_state, STATE_DEFAULT);
+ }
+
+ /* Device Enumeration completed */
+ if (readl(&udc_regs_p->dev_int) & DEV_INT_ENUM) {
+ writel(DEV_INT_ENUM, &udc_regs_p->dev_int);
+
+ /* Endpoint interrupt enabled for Ctrl IN & Ctrl OUT */
+ writel(readl(&udc_regs_p->endp_int_mask) & ~0x10001,
+ &udc_regs_p->endp_int_mask);
+
+ UDCDBG("default -> addressed");
+ udc_state_transition(udc_device->device_state, STATE_ADDRESSED);
+ }
+
+ /* The USB will be in SUSPEND in 3 ms */
+ if (readl(&udc_regs_p->dev_int) & DEV_INT_INACTIVE) {
+ writel(DEV_INT_INACTIVE, &udc_regs_p->dev_int);
+
+ UDCDBG("entering inactive state");
+ /* usbd_device_event_irq(udc_device, DEVICE_BUS_INACTIVE, 0); */
+ }
+
+ /* SetConfiguration command received */
+ if (readl(&udc_regs_p->dev_int) & DEV_INT_SETCFG) {
+ writel(DEV_INT_SETCFG, &udc_regs_p->dev_int);
+
+ UDCDBG("entering configured state");
+ udc_state_transition(udc_device->device_state,
+ STATE_CONFIGURED);
+ }
+
+ /* SetInterface command received */
+ if (readl(&udc_regs_p->dev_int) & DEV_INT_SETINTF)
+ writel(DEV_INT_SETINTF, &udc_regs_p->dev_int);
+
+ /* USB Suspend detected on cable */
+ if (readl(&udc_regs_p->dev_int) & DEV_INT_SUSPUSB) {
+ writel(DEV_INT_SUSPUSB, &udc_regs_p->dev_int);
+
+ UDCDBG("entering suspended state");
+ usbd_device_event_irq(udc_device, DEVICE_BUS_INACTIVE, 0);
+ }
+
+ /* USB Start-Of-Frame detected on cable */
+ if (readl(&udc_regs_p->dev_int) & DEV_INT_SOF)
+ writel(DEV_INT_SOF, &udc_regs_p->dev_int);
+}
+
+/*
+ * Endpoint interrupt handling
+ */
+static void dw_udc_endpoint_irq(void)
+{
+ while (readl(&udc_regs_p->endp_int) & ENDP0_INT_CTRLOUT) {
+
+ writel(ENDP0_INT_CTRLOUT, &udc_regs_p->endp_int);
+
+ if ((readl(&outep_regs_p[0].endp_status) & ENDP_STATUS_OUTMSK)
+ == ENDP_STATUS_OUT_SETUP) {
+ dw_udc_setup(udc_device->bus->endpoint_array + 0);
+ writel(ENDP_STATUS_OUT_SETUP,
+ &outep_regs_p[0].endp_status);
+
+ } else if ((readl(&outep_regs_p[0].endp_status) &
+ ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_DATA) {
+ dw_udc_ep0_rx(udc_device->bus->endpoint_array + 0);
+ writel(ENDP_STATUS_OUT_DATA,
+ &outep_regs_p[0].endp_status);
+
+ } else if ((readl(&outep_regs_p[0].endp_status) &
+ ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_NONE) {
+ /* NONE received */
+ }
+
+ writel(0x0, &outep_regs_p[0].endp_status);
+ }
+
+ if (readl(&udc_regs_p->endp_int) & ENDP0_INT_CTRLIN) {
+ dw_udc_ep0_tx(udc_device->bus->endpoint_array + 0);
+
+ writel(ENDP_STATUS_IN, &inep_regs_p[0].endp_status);
+ writel(ENDP0_INT_CTRLIN, &udc_regs_p->endp_int);
+ }
+
+ if (readl(&udc_regs_p->endp_int) & ENDP_INT_NONISOOUT_MSK) {
+ u32 epnum = 0;
+ u32 ep_int = readl(&udc_regs_p->endp_int) &
+ ENDP_INT_NONISOOUT_MSK;
+
+ ep_int >>= 16;
+ while (0x0 == (ep_int & 0x1)) {
+ ep_int >>= 1;
+ epnum++;
+ }
+
+ writel((1 << 16) << epnum, &udc_regs_p->endp_int);
+
+ if ((readl(&outep_regs_p[epnum].endp_status) &
+ ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_DATA) {
+
+ dw_udc_epn_rx(epnum);
+ writel(ENDP_STATUS_OUT_DATA,
+ &outep_regs_p[epnum].endp_status);
+ } else if ((readl(&outep_regs_p[epnum].endp_status) &
+ ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_NONE) {
+ writel(0x0, &outep_regs_p[epnum].endp_status);
+ }
+ }
+
+ if (readl(&udc_regs_p->endp_int) & ENDP_INT_NONISOIN_MSK) {
+ u32 epnum = 0;
+ u32 ep_int = readl(&udc_regs_p->endp_int) &
+ ENDP_INT_NONISOIN_MSK;
+
+ while (0x0 == (ep_int & 0x1)) {
+ ep_int >>= 1;
+ epnum++;
+ }
+
+ if (readl(&inep_regs_p[epnum].endp_status) & ENDP_STATUS_IN) {
+ writel(ENDP_STATUS_IN,
+ &outep_regs_p[epnum].endp_status);
+ dw_udc_epn_tx(epnum);
+
+ writel(ENDP_STATUS_IN,
+ &outep_regs_p[epnum].endp_status);
+ }
+
+ writel((1 << epnum), &udc_regs_p->endp_int);
+ }
+}
+
+/*
+ * UDC interrupts
+ */
+void udc_irq(void)
+{
+ /*
+ * Loop while we have interrupts.
+ * If we don't do this, the input chain
+ * polling delay is likely to miss
+ * host requests.
+ */
+ while (readl(&plug_regs_p->plug_pending))
+ dw_udc_plug_irq();
+
+ while (readl(&udc_regs_p->dev_int))
+ dw_udc_dev_irq();
+
+ if (readl(&udc_regs_p->endp_int))
+ dw_udc_endpoint_irq();
+}
+
+/* Flow control */
+void udc_set_nak(int epid)
+{
+ writel(readl(&inep_regs_p[epid].endp_cntl) | ENDP_CNTL_SNAK,
+ &inep_regs_p[epid].endp_cntl);
+
+ writel(readl(&outep_regs_p[epid].endp_cntl) | ENDP_CNTL_SNAK,
+ &outep_regs_p[epid].endp_cntl);
+}
+
+void udc_unset_nak(int epid)
+{
+ u32 val;
+
+ val = readl(&inep_regs_p[epid].endp_cntl);
+ val &= ~ENDP_CNTL_SNAK;
+ val |= ENDP_CNTL_CNAK;
+ writel(val, &inep_regs_p[epid].endp_cntl);
+
+ val = readl(&outep_regs_p[epid].endp_cntl);
+ val &= ~ENDP_CNTL_SNAK;
+ val |= ENDP_CNTL_CNAK;
+ writel(val, &outep_regs_p[epid].endp_cntl);
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/ep0.c b/src/roms/u-boot/drivers/usb/gadget/ep0.c
new file mode 100644
index 0000000..b321488
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/ep0.c
@@ -0,0 +1,597 @@
+/*
+ * (C) Copyright 2003
+ * Gerry Hamel, geh@ti.com, Texas Instruments
+ *
+ * (C) Copyright 2006
+ * Bryan O'Donoghue, deckard@CodeHermit.ie
+ *
+ * Based on
+ * linux/drivers/usbd/ep0.c
+ *
+ * Copyright (c) 2000, 2001, 2002 Lineo
+ * Copyright (c) 2001 Hewlett Packard
+ *
+ * By:
+ * Stuart Lynne <sl@lineo.com>,
+ * Tom Rushworth <tbr@lineo.com>,
+ * Bruce Balden <balden@lineo.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * This is the builtin ep0 control function. It implements all required functionality
+ * for responding to control requests (SETUP packets).
+ *
+ * XXX
+ *
+ * Currently we do not pass any SETUP packets (or other) to the configured
+ * function driver. This may need to change.
+ *
+ * XXX
+ *
+ * As alluded to above, a simple callback cdc_recv_setup has been implemented
+ * in the usb_device data structure to facilicate passing
+ * Common Device Class packets to a function driver.
+ *
+ * XXX
+ */
+
+#include <common.h>
+#include <usbdevice.h>
+
+#if 0
+#define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: "fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args)
+#else
+#define dbg_ep0(lvl,fmt,args...)
+#endif
+
+/* EP0 Configuration Set ********************************************************************* */
+
+
+/**
+ * ep0_get_status - fill in URB data with appropriate status
+ * @device:
+ * @urb:
+ * @index:
+ * @requesttype:
+ *
+ */
+static int ep0_get_status (struct usb_device_instance *device,
+ struct urb *urb, int index, int requesttype)
+{
+ char *cp;
+
+ urb->actual_length = 2;
+ cp = (char*)urb->buffer;
+ cp[0] = cp[1] = 0;
+
+ switch (requesttype) {
+ case USB_REQ_RECIPIENT_DEVICE:
+ cp[0] = USB_STATUS_SELFPOWERED;
+ break;
+ case USB_REQ_RECIPIENT_INTERFACE:
+ break;
+ case USB_REQ_RECIPIENT_ENDPOINT:
+ cp[0] = usbd_endpoint_halted (device, index);
+ break;
+ case USB_REQ_RECIPIENT_OTHER:
+ urb->actual_length = 0;
+ default:
+ break;
+ }
+ dbg_ep0 (2, "%02x %02x", cp[0], cp[1]);
+ return 0;
+}
+
+/**
+ * ep0_get_one
+ * @device:
+ * @urb:
+ * @result:
+ *
+ * Set a single byte value in the urb send buffer. Return non-zero to signal
+ * a request error.
+ */
+static int ep0_get_one (struct usb_device_instance *device, struct urb *urb,
+ __u8 result)
+{
+ urb->actual_length = 1; /* XXX 2? */
+ ((char *) urb->buffer)[0] = result;
+ return 0;
+}
+
+/**
+ * copy_config
+ * @urb: pointer to urb
+ * @data: pointer to configuration data
+ * @length: length of data
+ *
+ * Copy configuration data to urb transfer buffer if there is room for it.
+ */
+void copy_config (struct urb *urb, void *data, int max_length,
+ int max_buf)
+{
+ int available;
+ int length;
+
+ /*dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", */
+ /* urb->actual_length, urb->buffer_length, max_buf, max_length, data); */
+
+ if (!data) {
+ dbg_ep0 (1, "data is NULL");
+ return;
+ }
+ length = max_length;
+
+ if (length > max_length) {
+ dbg_ep0 (1, "length: %d >= max_length: %d", length,
+ max_length);
+ return;
+ }
+ /*dbg_ep0(1, " actual: %d buf: %d max_buf: %d max_length: %d length: %d", */
+ /* urb->actual_length, urb->buffer_length, max_buf, max_length, length); */
+
+ if ((available =
+ /*urb->buffer_length */ max_buf - urb->actual_length) <= 0) {
+ return;
+ }
+ /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */
+ /* urb->actual_length, urb->buffer_length, max_buf, length, available); */
+
+ if (length > available) {
+ length = available;
+ }
+ /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */
+ /* urb->actual_length, urb->buffer_length, max_buf, length, available); */
+
+ memcpy (urb->buffer + urb->actual_length, data, length);
+ urb->actual_length += length;
+
+ dbg_ep0 (3,
+ "copy_config: <- actual: %d buf: %d max_buf: %d max_length: %d available: %d",
+ urb->actual_length, urb->buffer_length, max_buf, max_length,
+ available);
+}
+
+/**
+ * ep0_get_descriptor
+ * @device:
+ * @urb:
+ * @max:
+ * @descriptor_type:
+ * @index:
+ *
+ * Called by ep0_rx_process for a get descriptor device command. Determine what
+ * descriptor is being requested, copy to send buffer. Return zero if ok to send,
+ * return non-zero to signal a request error.
+ */
+static int ep0_get_descriptor (struct usb_device_instance *device,
+ struct urb *urb, int max, int descriptor_type,
+ int index)
+{
+ int port = 0; /* XXX compound device */
+
+ /*dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index); */
+
+ if (!urb || !urb->buffer || !urb->buffer_length
+ || (urb->buffer_length < 255)) {
+ dbg_ep0 (2, "invalid urb %p", urb);
+ return -1L;
+ }
+
+ /* setup tx urb */
+ urb->actual_length = 0;
+
+ dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type));
+
+ switch (descriptor_type) {
+ case USB_DESCRIPTOR_TYPE_DEVICE:
+ {
+ struct usb_device_descriptor *device_descriptor;
+ if (!
+ (device_descriptor =
+ usbd_device_device_descriptor (device, port))) {
+ return -1;
+ }
+ /* copy descriptor for this device */
+ copy_config (urb, device_descriptor,
+ sizeof (struct usb_device_descriptor),
+ max);
+
+ /* correct the correct control endpoint 0 max packet size into the descriptor */
+ device_descriptor =
+ (struct usb_device_descriptor *) urb->buffer;
+
+ }
+ dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length);
+ break;
+
+ case USB_DESCRIPTOR_TYPE_CONFIGURATION:
+ {
+ struct usb_configuration_descriptor
+ *configuration_descriptor;
+ struct usb_device_descriptor *device_descriptor;
+ if (!
+ (device_descriptor =
+ usbd_device_device_descriptor (device, port))) {
+ return -1;
+ }
+ /*dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations); */
+ if (index >= device_descriptor->bNumConfigurations) {
+ dbg_ep0 (0, "index too large: %d >= %d", index,
+ device_descriptor->
+ bNumConfigurations);
+ return -1;
+ }
+
+ if (!
+ (configuration_descriptor =
+ usbd_device_configuration_descriptor (device,
+ port,
+ index))) {
+ dbg_ep0 (0,
+ "usbd_device_configuration_descriptor failed: %d",
+ index);
+ return -1;
+ }
+ dbg_ep0(0, "attempt to copy %d bytes to urb\n",cpu_to_le16(configuration_descriptor->wTotalLength));
+ copy_config (urb, configuration_descriptor,
+
+ cpu_to_le16(configuration_descriptor->wTotalLength),
+ max);
+ }
+
+ break;
+
+ case USB_DESCRIPTOR_TYPE_STRING:
+ {
+ struct usb_string_descriptor *string_descriptor;
+ if (!(string_descriptor = usbd_get_string (index))) {
+ serial_printf("Invalid string index %d\n", index);
+ return -1;
+ }
+ dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength);
+ copy_config (urb, string_descriptor, string_descriptor->bLength, max);
+ }
+ break;
+ case USB_DESCRIPTOR_TYPE_INTERFACE:
+ serial_printf("USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n");
+ return -1;
+ case USB_DESCRIPTOR_TYPE_ENDPOINT:
+ serial_printf("USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n");
+ return -1;
+ case USB_DESCRIPTOR_TYPE_HID:
+ {
+ serial_printf("USB_DESCRIPTOR_TYPE_HID - error not implemented\n");
+ return -1; /* unsupported at this time */
+#if 0
+ int bNumInterface =
+ le16_to_cpu (urb->device_request.wIndex);
+ int bAlternateSetting = 0;
+ int class = 0;
+ struct usb_class_descriptor *class_descriptor;
+
+ if (!(class_descriptor =
+ usbd_device_class_descriptor_index (device,
+ port, 0,
+ bNumInterface,
+ bAlternateSetting,
+ class))
+ || class_descriptor->descriptor.hid.bDescriptorType != USB_DT_HID) {
+ dbg_ep0 (3, "[%d] interface is not HID",
+ bNumInterface);
+ return -1;
+ }
+ /* copy descriptor for this class */
+ copy_config (urb, class_descriptor,
+ class_descriptor->descriptor.hid.bLength,
+ max);
+#endif
+ }
+ break;
+ case USB_DESCRIPTOR_TYPE_REPORT:
+ {
+ serial_printf("USB_DESCRIPTOR_TYPE_REPORT - error not implemented\n");
+ return -1; /* unsupported at this time */
+#if 0
+ int bNumInterface =
+ le16_to_cpu (urb->device_request.wIndex);
+ int bAlternateSetting = 0;
+ int class = 0;
+ struct usb_class_report_descriptor *report_descriptor;
+
+ if (!(report_descriptor =
+ usbd_device_class_report_descriptor_index
+ (device, port, 0, bNumInterface,
+ bAlternateSetting, class))
+ || report_descriptor->bDescriptorType !=
+ USB_DT_REPORT) {
+ dbg_ep0 (3, "[%d] descriptor is not REPORT",
+ bNumInterface);
+ return -1;
+ }
+ /* copy report descriptor for this class */
+ /*copy_config(urb, &report_descriptor->bData[0], report_descriptor->wLength, max); */
+ if (max - urb->actual_length > 0) {
+ int length =
+ MIN (report_descriptor->wLength,
+ max - urb->actual_length);
+ memcpy (urb->buffer + urb->actual_length,
+ &report_descriptor->bData[0], length);
+ urb->actual_length += length;
+ }
+#endif
+ }
+ break;
+ case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
+#if defined(CONFIG_USBD_HS)
+ {
+ struct usb_qualifier_descriptor *qualifier_descriptor =
+ device->qualifier_descriptor;
+
+ if (!qualifier_descriptor)
+ return -1;
+
+ /* copy descriptor for this device */
+ copy_config(urb, qualifier_descriptor,
+ sizeof(struct usb_qualifier_descriptor),
+ max);
+
+ }
+ dbg_ep0(3, "copied qualifier descriptor, actual_length: 0x%x",
+ urb->actual_length);
+#else
+ return -1;
+#endif
+ break;
+
+ default:
+ return -1;
+ }
+
+
+ dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d tx_packetSize: %2d",
+ urb->buffer, urb->buffer_length, urb->actual_length,
+ device->bus->endpoint_array[0].tx_packetSize);
+/*
+ if ((urb->actual_length < max) && !(urb->actual_length % device->bus->endpoint_array[0].tx_packetSize)) {
+ dbg_ep0(0, "adding null byte");
+ urb->buffer[urb->actual_length++] = 0;
+ dbg_ep0(0, "urb: buffer_length: %2d actual_length: %2d packet size: %2d",
+ urb->buffer_length, urb->actual_length device->bus->endpoint_array[0].tx_packetSize);
+ }
+*/
+ return 0;
+
+}
+
+/**
+ * ep0_recv_setup - called to indicate URB has been received
+ * @urb: pointer to struct urb
+ *
+ * Check if this is a setup packet, process the device request, put results
+ * back into the urb and return zero or non-zero to indicate success (DATA)
+ * or failure (STALL).
+ *
+ */
+int ep0_recv_setup (struct urb *urb)
+{
+ /*struct usb_device_request *request = urb->buffer; */
+ /*struct usb_device_instance *device = urb->device; */
+
+ struct usb_device_request *request;
+ struct usb_device_instance *device;
+ int address;
+
+ dbg_ep0 (0, "entering ep0_recv_setup()");
+ if (!urb || !urb->device) {
+ dbg_ep0 (3, "invalid URB %p", urb);
+ return -1;
+ }
+
+ request = &urb->device_request;
+ device = urb->device;
+
+ dbg_ep0 (3, "urb: %p device: %p", urb, urb->device);
+
+
+ /*dbg_ep0(2, "- - - - - - - - - -"); */
+
+ dbg_ep0 (2,
+ "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %s",
+ request->bmRequestType, request->bRequest,
+ le16_to_cpu (request->wValue), le16_to_cpu (request->wIndex),
+ le16_to_cpu (request->wLength),
+ USBD_DEVICE_REQUESTS (request->bRequest));
+
+ /* handle USB Standard Request (c.f. USB Spec table 9-2) */
+ if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) {
+ if(device->device_state <= STATE_CONFIGURED){
+ /* Attempt to handle a CDC specific request if we are
+ * in the configured state.
+ */
+ return device->cdc_recv_setup(request,urb);
+ }
+ dbg_ep0 (1, "non standard request: %x",
+ request->bmRequestType & USB_REQ_TYPE_MASK);
+ return -1; /* Stall here */
+ }
+
+ switch (device->device_state) {
+ case STATE_CREATED:
+ case STATE_ATTACHED:
+ case STATE_POWERED:
+ /* It actually is important to allow requests in these states,
+ * Windows will request descriptors before assigning an
+ * address to the client.
+ */
+
+ /*dbg_ep0 (1, "request %s not allowed in this state: %s", */
+ /* USBD_DEVICE_REQUESTS(request->bRequest), */
+ /* usbd_device_states[device->device_state]); */
+ /*return -1; */
+ break;
+
+ case STATE_INIT:
+ case STATE_DEFAULT:
+ switch (request->bRequest) {
+ case USB_REQ_GET_STATUS:
+ case USB_REQ_GET_INTERFACE:
+ case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ case USB_REQ_SET_DESCRIPTOR:
+ /* case USB_REQ_SET_CONFIGURATION: */
+ case USB_REQ_SET_INTERFACE:
+ dbg_ep0 (1,
+ "request %s not allowed in DEFAULT state: %s",
+ USBD_DEVICE_REQUESTS (request->bRequest),
+ usbd_device_states[device->device_state]);
+ return -1;
+
+ case USB_REQ_SET_CONFIGURATION:
+ case USB_REQ_SET_ADDRESS:
+ case USB_REQ_GET_DESCRIPTOR:
+ case USB_REQ_GET_CONFIGURATION:
+ break;
+ }
+ case STATE_ADDRESSED:
+ case STATE_CONFIGURED:
+ break;
+ case STATE_UNKNOWN:
+ dbg_ep0 (1, "request %s not allowed in UNKNOWN state: %s",
+ USBD_DEVICE_REQUESTS (request->bRequest),
+ usbd_device_states[device->device_state]);
+ return -1;
+ }
+
+ /* handle all requests that return data (direction bit set on bm RequestType) */
+ if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) {
+
+ dbg_ep0 (3, "Device-to-Host");
+
+ switch (request->bRequest) {
+
+ case USB_REQ_GET_STATUS:
+ return ep0_get_status (device, urb, request->wIndex,
+ request->bmRequestType &
+ USB_REQ_RECIPIENT_MASK);
+
+ case USB_REQ_GET_DESCRIPTOR:
+ return ep0_get_descriptor (device, urb,
+ le16_to_cpu (request->wLength),
+ le16_to_cpu (request->wValue) >> 8,
+ le16_to_cpu (request->wValue) & 0xff);
+
+ case USB_REQ_GET_CONFIGURATION:
+ serial_printf("get config %d\n", device->configuration);
+ return ep0_get_one (device, urb,
+ device->configuration);
+
+ case USB_REQ_GET_INTERFACE:
+ return ep0_get_one (device, urb, device->alternate);
+
+ case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
+ return -1;
+
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ case USB_REQ_SET_ADDRESS:
+ case USB_REQ_SET_DESCRIPTOR:
+ case USB_REQ_SET_CONFIGURATION:
+ case USB_REQ_SET_INTERFACE:
+ return -1;
+ }
+ }
+ /* handle the requests that do not return data */
+ else {
+
+
+ /*dbg_ep0(3, "Host-to-Device"); */
+ switch (request->bRequest) {
+
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ dbg_ep0 (0, "Host-to-Device");
+ switch (request->
+ bmRequestType & USB_REQ_RECIPIENT_MASK) {
+ case USB_REQ_RECIPIENT_DEVICE:
+ /* XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here */
+ /* XXX fall through for now as we do not support either */
+ case USB_REQ_RECIPIENT_INTERFACE:
+ case USB_REQ_RECIPIENT_OTHER:
+ dbg_ep0 (0, "request %s not",
+ USBD_DEVICE_REQUESTS (request->bRequest));
+ default:
+ return -1;
+
+ case USB_REQ_RECIPIENT_ENDPOINT:
+ dbg_ep0 (0, "ENDPOINT: %x", le16_to_cpu (request->wValue));
+ if (le16_to_cpu (request->wValue) == USB_ENDPOINT_HALT) {
+ /*return usbd_device_feature (device, le16_to_cpu (request->wIndex), */
+ /* request->bRequest == USB_REQ_SET_FEATURE); */
+ /* NEED TO IMPLEMENT THIS!!! */
+ return -1;
+ } else {
+ dbg_ep0 (1, "request %s bad wValue: %04x",
+ USBD_DEVICE_REQUESTS
+ (request->bRequest),
+ le16_to_cpu (request->wValue));
+ return -1;
+ }
+ }
+
+ case USB_REQ_SET_ADDRESS:
+ /* check if this is a re-address, reset first if it is (this shouldn't be possible) */
+ if (device->device_state != STATE_DEFAULT) {
+ dbg_ep0 (1, "set_address: %02x state: %s",
+ le16_to_cpu (request->wValue),
+ usbd_device_states[device->device_state]);
+ return -1;
+ }
+ address = le16_to_cpu (request->wValue);
+ if ((address & 0x7f) != address) {
+ dbg_ep0 (1, "invalid address %04x %04x",
+ address, address & 0x7f);
+ return -1;
+ }
+ device->address = address;
+
+ /*dbg_ep0(2, "address: %d %d %d", */
+ /* request->wValue, le16_to_cpu(request->wValue), device->address); */
+
+ return 0;
+
+ case USB_REQ_SET_DESCRIPTOR: /* XXX should we support this? */
+ dbg_ep0 (0, "set descriptor: NOT SUPPORTED");
+ return -1;
+
+ case USB_REQ_SET_CONFIGURATION:
+ /* c.f. 9.4.7 - the top half of wValue is reserved */
+ device->configuration = le16_to_cpu(request->wValue) & 0xff;
+
+ /* reset interface and alternate settings */
+ device->interface = device->alternate = 0;
+
+ /*dbg_ep0(2, "set configuration: %d", device->configuration); */
+ /*serial_printf("DEVICE_CONFIGURED.. event?\n"); */
+ return 0;
+
+ case USB_REQ_SET_INTERFACE:
+ device->interface = le16_to_cpu (request->wIndex);
+ device->alternate = le16_to_cpu (request->wValue);
+ /*dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); */
+ serial_printf ("DEVICE_SET_INTERFACE.. event?\n");
+ return 0;
+
+ case USB_REQ_GET_STATUS:
+ case USB_REQ_GET_DESCRIPTOR:
+ case USB_REQ_GET_CONFIGURATION:
+ case USB_REQ_GET_INTERFACE:
+ case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
+ return -1;
+ }
+ }
+ return -1;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/ep0.h b/src/roms/u-boot/drivers/usb/gadget/ep0.h
new file mode 100644
index 0000000..6042e75
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/ep0.h
@@ -0,0 +1,26 @@
+/*
+ * (C) Copyright 2003
+ * Gerry Hamel, geh@ti.com, Texas Instruments
+ *
+ * Based on
+ * linux/drivers/usbd/ep0.c
+ *
+ * Copyright (c) 2000, 2001, 2002 Lineo
+ * Copyright (c) 2001 Hewlett Packard
+ *
+ * By:
+ * Stuart Lynne <sl@lineo.com>,
+ * Tom Rushworth <tbr@lineo.com>,
+ * Bruce Balden <balden@lineo.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __USBDCORE_EP0_H__
+#define __USBDCORE_EP0_H__
+
+
+int ep0_recv_setup (struct urb *urb);
+
+
+#endif
diff --git a/src/roms/u-boot/drivers/usb/gadget/epautoconf.c b/src/roms/u-boot/drivers/usb/gadget/epautoconf.c
new file mode 100644
index 0000000..0df4b2a
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/epautoconf.c
@@ -0,0 +1,296 @@
+/*
+ * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
+ *
+ * Copyright (C) 2004 David Brownell
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and
+ * Remy Bohmer <linux@bohmer.net>
+ */
+
+#include <common.h>
+#include <linux/usb/ch9.h>
+#include <asm/errno.h>
+#include <linux/usb/gadget.h>
+#include <asm/unaligned.h>
+#include "gadget_chips.h"
+
+#define isdigit(c) ('0' <= (c) && (c) <= '9')
+
+/* we must assign addresses for configurable endpoints (like net2280) */
+static unsigned epnum;
+
+/* #define MANY_ENDPOINTS */
+#ifdef MANY_ENDPOINTS
+/* more than 15 configurable endpoints */
+static unsigned in_epnum;
+#endif
+
+
+/*
+ * This should work with endpoints from controller drivers sharing the
+ * same endpoint naming convention. By example:
+ *
+ * - ep1, ep2, ... address is fixed, not direction or type
+ * - ep1in, ep2out, ... address and direction are fixed, not type
+ * - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
+ * - ep1in-bulk, ep2out-iso, ... all three are fixed
+ * - ep-* ... no functionality restrictions
+ *
+ * Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal.
+ * Less common restrictions are implied by gadget_is_*().
+ *
+ * NOTE: each endpoint is unidirectional, as specified by its USB
+ * descriptor; and isn't specific to a configuration or altsetting.
+ */
+static int ep_matches(
+ struct usb_gadget *gadget,
+ struct usb_ep *ep,
+ struct usb_endpoint_descriptor *desc
+)
+{
+ u8 type;
+ const char *tmp;
+ u16 max;
+
+ /* endpoint already claimed? */
+ if (NULL != ep->driver_data)
+ return 0;
+
+ /* only support ep0 for portable CONTROL traffic */
+ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ if (USB_ENDPOINT_XFER_CONTROL == type)
+ return 0;
+
+ /* some other naming convention */
+ if ('e' != ep->name[0])
+ return 0;
+
+ /* type-restriction: "-iso", "-bulk", or "-int".
+ * direction-restriction: "in", "out".
+ */
+ if ('-' != ep->name[2]) {
+ tmp = strrchr(ep->name, '-');
+ if (tmp) {
+ switch (type) {
+ case USB_ENDPOINT_XFER_INT:
+ /* bulk endpoints handle interrupt transfers,
+ * except the toggle-quirky iso-synch kind
+ */
+ if ('s' == tmp[2]) /* == "-iso" */
+ return 0;
+ /* for now, avoid PXA "interrupt-in";
+ * it's documented as never using DATA1.
+ */
+ if (gadget_is_pxa(gadget)
+ && 'i' == tmp[1])
+ return 0;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ if ('b' != tmp[1]) /* != "-bulk" */
+ return 0;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ if ('s' != tmp[2]) /* != "-iso" */
+ return 0;
+ }
+ } else {
+ tmp = ep->name + strlen(ep->name);
+ }
+
+ /* direction-restriction: "..in-..", "out-.." */
+ tmp--;
+ if (!isdigit(*tmp)) {
+ if (desc->bEndpointAddress & USB_DIR_IN) {
+ if ('n' != *tmp)
+ return 0;
+ } else {
+ if ('t' != *tmp)
+ return 0;
+ }
+ }
+ }
+
+ /* endpoint maxpacket size is an input parameter, except for bulk
+ * where it's an output parameter representing the full speed limit.
+ * the usb spec fixes high speed bulk maxpacket at 512 bytes.
+ */
+ max = 0x7ff & le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));
+ switch (type) {
+ case USB_ENDPOINT_XFER_INT:
+ /* INT: limit 64 bytes full speed, 1024 high speed */
+ if (!gadget->is_dualspeed && max > 64)
+ return 0;
+ /* FALLTHROUGH */
+
+ case USB_ENDPOINT_XFER_ISOC:
+ /* ISO: limit 1023 bytes full speed, 1024 high speed */
+ if (ep->maxpacket < max)
+ return 0;
+ if (!gadget->is_dualspeed && max > 1023)
+ return 0;
+
+ /* BOTH: "high bandwidth" works only at high speed */
+ if ((get_unaligned(&desc->wMaxPacketSize) &
+ __constant_cpu_to_le16(3<<11))) {
+ if (!gadget->is_dualspeed)
+ return 0;
+ /* configure your hardware with enough buffering!! */
+ }
+ break;
+ }
+
+ /* MATCH!! */
+
+ /* report address */
+ if (isdigit(ep->name[2])) {
+ u8 num = simple_strtoul(&ep->name[2], NULL, 10);
+ desc->bEndpointAddress |= num;
+#ifdef MANY_ENDPOINTS
+ } else if (desc->bEndpointAddress & USB_DIR_IN) {
+ if (++in_epnum > 15)
+ return 0;
+ desc->bEndpointAddress = USB_DIR_IN | in_epnum;
+#endif
+ } else {
+ if (++epnum > 15)
+ return 0;
+ desc->bEndpointAddress |= epnum;
+ }
+
+ /* report (variable) full speed bulk maxpacket */
+ if (USB_ENDPOINT_XFER_BULK == type) {
+ int size = ep->maxpacket;
+
+ /* min() doesn't work on bitfields with gcc-3.5 */
+ if (size > 64)
+ size = 64;
+ put_unaligned(cpu_to_le16(size), &desc->wMaxPacketSize);
+ }
+ return 1;
+}
+
+static struct usb_ep *
+find_ep(struct usb_gadget *gadget, const char *name)
+{
+ struct usb_ep *ep;
+
+ list_for_each_entry(ep, &gadget->ep_list, ep_list) {
+ if (0 == strcmp(ep->name, name))
+ return ep;
+ }
+ return NULL;
+}
+
+/**
+ * usb_ep_autoconfig - choose an endpoint matching the descriptor
+ * @gadget: The device to which the endpoint must belong.
+ * @desc: Endpoint descriptor, with endpoint direction and transfer mode
+ * initialized. For periodic transfers, the maximum packet
+ * size must also be initialized. This is modified on success.
+ *
+ * By choosing an endpoint to use with the specified descriptor, this
+ * routine simplifies writing gadget drivers that work with multiple
+ * USB device controllers. The endpoint would be passed later to
+ * usb_ep_enable(), along with some descriptor.
+ *
+ * That second descriptor won't always be the same as the first one.
+ * For example, isochronous endpoints can be autoconfigured for high
+ * bandwidth, and then used in several lower bandwidth altsettings.
+ * Also, high and full speed descriptors will be different.
+ *
+ * Be sure to examine and test the results of autoconfiguration on your
+ * hardware. This code may not make the best choices about how to use the
+ * USB controller, and it can't know all the restrictions that may apply.
+ * Some combinations of driver and hardware won't be able to autoconfigure.
+ *
+ * On success, this returns an un-claimed usb_ep, and modifies the endpoint
+ * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
+ * is initialized as if the endpoint were used at full speed. To prevent
+ * the endpoint from being returned by a later autoconfig call, claim it
+ * by assigning ep->driver_data to some non-null value.
+ *
+ * On failure, this returns a null endpoint descriptor.
+ */
+struct usb_ep *usb_ep_autoconfig(
+ struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc
+)
+{
+ struct usb_ep *ep;
+ u8 type;
+
+ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+ /* First, apply chip-specific "best usage" knowledge.
+ * This might make a good usb_gadget_ops hook ...
+ */
+ if (gadget_is_net2280(gadget) && type == USB_ENDPOINT_XFER_INT) {
+ /* ep-e, ep-f are PIO with only 64 byte fifos */
+ ep = find_ep(gadget, "ep-e");
+ if (ep && ep_matches(gadget, ep, desc))
+ return ep;
+ ep = find_ep(gadget, "ep-f");
+ if (ep && ep_matches(gadget, ep, desc))
+ return ep;
+
+ } else if (gadget_is_goku(gadget)) {
+ if (USB_ENDPOINT_XFER_INT == type) {
+ /* single buffering is enough */
+ ep = find_ep(gadget, "ep3-bulk");
+ if (ep && ep_matches(gadget, ep, desc))
+ return ep;
+ } else if (USB_ENDPOINT_XFER_BULK == type
+ && (USB_DIR_IN & desc->bEndpointAddress)) {
+ /* DMA may be available */
+ ep = find_ep(gadget, "ep2-bulk");
+ if (ep && ep_matches(gadget, ep, desc))
+ return ep;
+ }
+
+ } else if (gadget_is_sh(gadget) && USB_ENDPOINT_XFER_INT == type) {
+ /* single buffering is enough; maybe 8 byte fifo is too */
+ ep = find_ep(gadget, "ep3in-bulk");
+ if (ep && ep_matches(gadget, ep, desc))
+ return ep;
+
+ } else if (gadget_is_mq11xx(gadget) && USB_ENDPOINT_XFER_INT == type) {
+ ep = find_ep(gadget, "ep1-bulk");
+ if (ep && ep_matches(gadget, ep, desc))
+ return ep;
+ }
+
+ /* Second, look at endpoints until an unclaimed one looks usable */
+ list_for_each_entry(ep, &gadget->ep_list, ep_list) {
+ if (ep_matches(gadget, ep, desc))
+ return ep;
+ }
+
+ /* Fail */
+ return NULL;
+}
+
+/**
+ * usb_ep_autoconfig_reset - reset endpoint autoconfig state
+ * @gadget: device for which autoconfig state will be reset
+ *
+ * Use this for devices where one configuration may need to assign
+ * endpoint resources very differently from the next one. It clears
+ * state such as ep->driver_data and the record of assigned endpoints
+ * used by usb_ep_autoconfig().
+ */
+void usb_ep_autoconfig_reset(struct usb_gadget *gadget)
+{
+ struct usb_ep *ep;
+
+ list_for_each_entry(ep, &gadget->ep_list, ep_list) {
+ ep->driver_data = NULL;
+ }
+#ifdef MANY_ENDPOINTS
+ in_epnum = 0;
+#endif
+ epnum = 0;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/ether.c b/src/roms/u-boot/drivers/usb/gadget/ether.c
new file mode 100644
index 0000000..cc6cc1f
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/ether.c
@@ -0,0 +1,2555 @@
+/*
+ * ether.c -- Ethernet gadget driver, with CDC and non-CDC options
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/errno.h>
+#include <linux/netdevice.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/gadget.h>
+#include <net.h>
+#include <malloc.h>
+#include <linux/ctype.h>
+
+#include "gadget_chips.h"
+#include "rndis.h"
+
+#define USB_NET_NAME "usb_ether"
+
+#define atomic_read
+extern struct platform_data brd;
+#define spin_lock(x)
+#define spin_unlock(x)
+
+
+unsigned packet_received, packet_sent;
+
+#define GFP_ATOMIC ((gfp_t) 0)
+#define GFP_KERNEL ((gfp_t) 0)
+
+/*
+ * Ethernet gadget driver -- with CDC and non-CDC options
+ * Builds on hardware support for a full duplex link.
+ *
+ * CDC Ethernet is the standard USB solution for sending Ethernet frames
+ * using USB. Real hardware tends to use the same framing protocol but look
+ * different for control features. This driver strongly prefers to use
+ * this USB-IF standard as its open-systems interoperability solution;
+ * most host side USB stacks (except from Microsoft) support it.
+ *
+ * This is sometimes called "CDC ECM" (Ethernet Control Model) to support
+ * TLA-soup. "CDC ACM" (Abstract Control Model) is for modems, and a new
+ * "CDC EEM" (Ethernet Emulation Model) is starting to spread.
+ *
+ * There's some hardware that can't talk CDC ECM. We make that hardware
+ * implement a "minimalist" vendor-agnostic CDC core: same framing, but
+ * link-level setup only requires activating the configuration. Only the
+ * endpoint descriptors, and product/vendor IDs, are relevant; no control
+ * operations are available. Linux supports it, but other host operating
+ * systems may not. (This is a subset of CDC Ethernet.)
+ *
+ * It turns out that if you add a few descriptors to that "CDC Subset",
+ * (Windows) host side drivers from MCCI can treat it as one submode of
+ * a proprietary scheme called "SAFE" ... without needing to know about
+ * specific product/vendor IDs. So we do that, making it easier to use
+ * those MS-Windows drivers. Those added descriptors make it resemble a
+ * CDC MDLM device, but they don't change device behavior at all. (See
+ * MCCI Engineering report 950198 "SAFE Networking Functions".)
+ *
+ * A third option is also in use. Rather than CDC Ethernet, or something
+ * simpler, Microsoft pushes their own approach: RNDIS. The published
+ * RNDIS specs are ambiguous and appear to be incomplete, and are also
+ * needlessly complex. They borrow more from CDC ACM than CDC ECM.
+ */
+#define ETH_ALEN 6 /* Octets in one ethernet addr */
+#define ETH_HLEN 14 /* Total octets in header. */
+#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN 1500 /* Max. octets in payload */
+#define ETH_FRAME_LEN PKTSIZE_ALIGN /* Max. octets in frame sans FCS */
+#define ETH_FCS_LEN 4 /* Octets in the FCS */
+
+#define DRIVER_DESC "Ethernet Gadget"
+/* Based on linux 2.6.27 version */
+#define DRIVER_VERSION "May Day 2005"
+
+static const char shortname[] = "ether";
+static const char driver_desc[] = DRIVER_DESC;
+
+#define RX_EXTRA 20 /* guard against rx overflows */
+
+#ifndef CONFIG_USB_ETH_RNDIS
+#define rndis_uninit(x) do {} while (0)
+#define rndis_deregister(c) do {} while (0)
+#define rndis_exit() do {} while (0)
+#endif
+
+/* CDC and RNDIS support the same host-chosen outgoing packet filters. */
+#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
+ |USB_CDC_PACKET_TYPE_ALL_MULTICAST \
+ |USB_CDC_PACKET_TYPE_PROMISCUOUS \
+ |USB_CDC_PACKET_TYPE_DIRECTED)
+
+#define USB_CONNECT_TIMEOUT (3 * CONFIG_SYS_HZ)
+
+/*-------------------------------------------------------------------------*/
+
+struct eth_dev {
+ struct usb_gadget *gadget;
+ struct usb_request *req; /* for control responses */
+ struct usb_request *stat_req; /* for cdc & rndis status */
+
+ u8 config;
+ struct usb_ep *in_ep, *out_ep, *status_ep;
+ const struct usb_endpoint_descriptor
+ *in, *out, *status;
+
+ struct usb_request *tx_req, *rx_req;
+
+ struct eth_device *net;
+ struct net_device_stats stats;
+ unsigned int tx_qlen;
+
+ unsigned zlp:1;
+ unsigned cdc:1;
+ unsigned rndis:1;
+ unsigned suspended:1;
+ unsigned network_started:1;
+ u16 cdc_filter;
+ unsigned long todo;
+ int mtu;
+#define WORK_RX_MEMORY 0
+ int rndis_config;
+ u8 host_mac[ETH_ALEN];
+};
+
+/*
+ * This version autoconfigures as much as possible at run-time.
+ *
+ * It also ASSUMES a self-powered device, without remote wakeup,
+ * although remote wakeup support would make sense.
+ */
+
+/*-------------------------------------------------------------------------*/
+static struct eth_dev l_ethdev;
+static struct eth_device l_netdev;
+static struct usb_gadget_driver eth_driver;
+
+/*-------------------------------------------------------------------------*/
+
+/* "main" config is either CDC, or its simple subset */
+static inline int is_cdc(struct eth_dev *dev)
+{
+#if !defined(CONFIG_USB_ETH_SUBSET)
+ return 1; /* only cdc possible */
+#elif !defined(CONFIG_USB_ETH_CDC)
+ return 0; /* only subset possible */
+#else
+ return dev->cdc; /* depends on what hardware we found */
+#endif
+}
+
+/* "secondary" RNDIS config may sometimes be activated */
+static inline int rndis_active(struct eth_dev *dev)
+{
+#ifdef CONFIG_USB_ETH_RNDIS
+ return dev->rndis;
+#else
+ return 0;
+#endif
+}
+
+#define subset_active(dev) (!is_cdc(dev) && !rndis_active(dev))
+#define cdc_active(dev) (is_cdc(dev) && !rndis_active(dev))
+
+#define DEFAULT_QLEN 2 /* double buffering by default */
+
+/* peak bulk transfer bits-per-second */
+#define HS_BPS (13 * 512 * 8 * 1000 * 8)
+#define FS_BPS (19 * 64 * 1 * 1000 * 8)
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+#define DEVSPEED USB_SPEED_HIGH
+
+#ifdef CONFIG_USB_ETH_QMULT
+#define qmult CONFIG_USB_ETH_QMULT
+#else
+#define qmult 5
+#endif
+
+/* for dual-speed hardware, use deeper queues at highspeed */
+#define qlen(gadget) \
+ (DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1))
+
+static inline int BITRATE(struct usb_gadget *g)
+{
+ return (g->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS;
+}
+
+#else /* full speed (low speed doesn't do bulk) */
+
+#define qmult 1
+
+#define DEVSPEED USB_SPEED_FULL
+
+#define qlen(gadget) DEFAULT_QLEN
+
+static inline int BITRATE(struct usb_gadget *g)
+{
+ return FS_BPS;
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+
+/*
+ * Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only CDC Ethernet configurations.
+ */
+#define CDC_VENDOR_NUM 0x0525 /* NetChip */
+#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
+
+/*
+ * For hardware that can't talk CDC, we use the same vendor ID that
+ * ARM Linux has used for ethernet-over-usb, both with sa1100 and
+ * with pxa250. We're protocol-compatible, if the host-side drivers
+ * use the endpoint descriptors. bcdDevice (version) is nonzero, so
+ * drivers that need to hard-wire endpoint numbers have a hook.
+ *
+ * The protocol is a minimal subset of CDC Ether, which works on any bulk
+ * hardware that's not deeply broken ... even on hardware that can't talk
+ * RNDIS (like SA-1100, with no interrupt endpoint, or anything that
+ * doesn't handle control-OUT).
+ */
+#define SIMPLE_VENDOR_NUM 0x049f /* Compaq Computer Corp. */
+#define SIMPLE_PRODUCT_NUM 0x505a /* Linux-USB "CDC Subset" Device */
+
+/*
+ * For hardware that can talk RNDIS and either of the above protocols,
+ * use this ID ... the windows INF files will know it. Unless it's
+ * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
+ * the non-RNDIS configuration.
+ */
+#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */
+#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */
+
+/*
+ * Some systems will want different product identifers published in the
+ * device descriptor, either numbers or strings or both. These string
+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+/*
+ * Emulating them in eth_bind:
+ * static ushort idVendor;
+ * static ushort idProduct;
+ */
+
+#if defined(CONFIG_USBNET_MANUFACTURER)
+static char *iManufacturer = CONFIG_USBNET_MANUFACTURER;
+#else
+static char *iManufacturer = "U-boot";
+#endif
+
+/* These probably need to be configurable. */
+static ushort bcdDevice;
+static char *iProduct;
+static char *iSerialNumber;
+
+static char dev_addr[18];
+
+static char host_addr[18];
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB DRIVER HOOKUP (to the hardware driver, below us), mostly
+ * ep0 implementation: descriptors, config management, setup().
+ * also optional class-specific notification interrupt transfer.
+ */
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand. For now we do either full CDC, or
+ * our simple subset, with RNDIS as an optional second configuration.
+ *
+ * RNDIS includes some CDC ACM descriptors ... like CDC Ethernet. But
+ * the class descriptors match a modem (they're ignored; it's really just
+ * Ethernet functionality), they don't need the NOP altsetting, and the
+ * status transfer endpoint isn't optional.
+ */
+
+#define STRING_MANUFACTURER 1
+#define STRING_PRODUCT 2
+#define STRING_ETHADDR 3
+#define STRING_DATA 4
+#define STRING_CONTROL 5
+#define STRING_RNDIS_CONTROL 6
+#define STRING_CDC 7
+#define STRING_SUBSET 8
+#define STRING_RNDIS 9
+#define STRING_SERIALNUMBER 10
+
+/* holds our biggest descriptor (or RNDIS response) */
+#define USB_BUFSIZ 256
+
+/*
+ * This device advertises one configuration, eth_config, unless RNDIS
+ * is enabled (rndis_config) on hardware supporting at least two configs.
+ *
+ * NOTE: Controllers like superh_udc should probably be able to use
+ * an RNDIS-only configuration.
+ *
+ * FIXME define some higher-powered configurations to make it easier
+ * to recharge batteries ...
+ */
+
+#define DEV_CONFIG_VALUE 1 /* cdc or subset */
+#define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */
+
+static struct usb_device_descriptor
+device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+
+ .bDeviceClass = USB_CLASS_COMM,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+
+ .idVendor = __constant_cpu_to_le16(CDC_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(CDC_PRODUCT_NUM),
+ .iManufacturer = STRING_MANUFACTURER,
+ .iProduct = STRING_PRODUCT,
+ .bNumConfigurations = 1,
+};
+
+static struct usb_otg_descriptor
+otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ .bmAttributes = USB_OTG_SRP,
+};
+
+static struct usb_config_descriptor
+eth_config = {
+ .bLength = sizeof eth_config,
+ .bDescriptorType = USB_DT_CONFIG,
+
+ /* compute wTotalLength on the fly */
+ .bNumInterfaces = 2,
+ .bConfigurationValue = DEV_CONFIG_VALUE,
+ .iConfiguration = STRING_CDC,
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = 1,
+};
+
+#ifdef CONFIG_USB_ETH_RNDIS
+static struct usb_config_descriptor
+rndis_config = {
+ .bLength = sizeof rndis_config,
+ .bDescriptorType = USB_DT_CONFIG,
+
+ /* compute wTotalLength on the fly */
+ .bNumInterfaces = 2,
+ .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE,
+ .iConfiguration = STRING_RNDIS,
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = 1,
+};
+#endif
+
+/*
+ * Compared to the simple CDC subset, the full CDC Ethernet model adds
+ * three class descriptors, two interface descriptors, optional status
+ * endpoint. Both have a "data" interface and two bulk endpoints.
+ * There are also differences in how control requests are handled.
+ *
+ * RNDIS shares a lot with CDC-Ethernet, since it's a variant of the
+ * CDC-ACM (modem) spec. Unfortunately MSFT's RNDIS driver is buggy; it
+ * may hang or oops. Since bugfixes (or accurate specs, letting Linux
+ * work around those bugs) are unlikely to ever come from MSFT, you may
+ * wish to avoid using RNDIS.
+ *
+ * MCCI offers an alternative to RNDIS if you need to connect to Windows
+ * but have hardware that can't support CDC Ethernet. We add descriptors
+ * to present the CDC Subset as a (nonconformant) CDC MDLM variant called
+ * "SAFE". That borrows from both CDC Ethernet and CDC MDLM. You can
+ * get those drivers from MCCI, or bundled with various products.
+ */
+
+#ifdef CONFIG_USB_ETH_CDC
+static struct usb_interface_descriptor
+control_intf = {
+ .bLength = sizeof control_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = 0,
+ /* status endpoint is optional; this may be patched later */
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET,
+ .bInterfaceProtocol = USB_CDC_PROTO_NONE,
+ .iInterface = STRING_CONTROL,
+};
+#endif
+
+#ifdef CONFIG_USB_ETH_RNDIS
+static const struct usb_interface_descriptor
+rndis_control_intf = {
+ .bLength = sizeof rndis_control_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
+ .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+ .iInterface = STRING_RNDIS_CONTROL,
+};
+#endif
+
+static const struct usb_cdc_header_desc header_desc = {
+ .bLength = sizeof header_desc,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+
+ .bcdCDC = __constant_cpu_to_le16(0x0110),
+};
+
+#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+
+static const struct usb_cdc_union_desc union_desc = {
+ .bLength = sizeof union_desc,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_UNION_TYPE,
+
+ .bMasterInterface0 = 0, /* index of control interface */
+ .bSlaveInterface0 = 1, /* index of DATA interface */
+};
+
+#endif /* CDC || RNDIS */
+
+#ifdef CONFIG_USB_ETH_RNDIS
+
+static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
+ .bLength = sizeof call_mgmt_descriptor,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
+
+ .bmCapabilities = 0x00,
+ .bDataInterface = 0x01,
+};
+
+static const struct usb_cdc_acm_descriptor acm_descriptor = {
+ .bLength = sizeof acm_descriptor,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_ACM_TYPE,
+
+ .bmCapabilities = 0x00,
+};
+
+#endif
+
+#ifndef CONFIG_USB_ETH_CDC
+
+/*
+ * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various
+ * ways: data endpoints live in the control interface, there's no data
+ * interface, and it's not used to talk to a cell phone radio.
+ */
+
+static const struct usb_cdc_mdlm_desc mdlm_desc = {
+ .bLength = sizeof mdlm_desc,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_MDLM_TYPE,
+
+ .bcdVersion = __constant_cpu_to_le16(0x0100),
+ .bGUID = {
+ 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
+ 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
+ },
+};
+
+/*
+ * since "usb_cdc_mdlm_detail_desc" is a variable length structure, we
+ * can't really use its struct. All we do here is say that we're using
+ * the submode of "SAFE" which directly matches the CDC Subset.
+ */
+static const u8 mdlm_detail_desc[] = {
+ 6,
+ USB_DT_CS_INTERFACE,
+ USB_CDC_MDLM_DETAIL_TYPE,
+
+ 0, /* "SAFE" */
+ 0, /* network control capabilities (none) */
+ 0, /* network data capabilities ("raw" encapsulation) */
+};
+
+#endif
+
+static const struct usb_cdc_ether_desc ether_desc = {
+ .bLength = sizeof(ether_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
+
+ /* this descriptor actually adds value, surprise! */
+ .iMACAddress = STRING_ETHADDR,
+ .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */
+ .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN),
+ .wNumberMCFilters = __constant_cpu_to_le16(0),
+ .bNumberPowerFilters = 0,
+};
+
+#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+
+/*
+ * include the status endpoint if we can, even where it's optional.
+ * use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
+ * packet, to simplify cancellation; and a big transfer interval, to
+ * waste less bandwidth.
+ *
+ * some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even
+ * if they ignore the connect/disconnect notifications that real aether
+ * can provide. more advanced cdc configurations might want to support
+ * encapsulated commands (vendor-specific, using control-OUT).
+ *
+ * RNDIS requires the status endpoint, since it uses that encapsulation
+ * mechanism for its funky RPC scheme.
+ */
+
+#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
+#define STATUS_BYTECOUNT 16 /* 8 byte header + data */
+
+static struct usb_endpoint_descriptor
+fs_status_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT),
+ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+#endif
+
+#ifdef CONFIG_USB_ETH_CDC
+
+/* the default data interface has no endpoints ... */
+
+static const struct usb_interface_descriptor
+data_nop_intf = {
+ .bLength = sizeof data_nop_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+};
+
+/* ... but the "real" data interface has two bulk endpoints */
+
+static const struct usb_interface_descriptor
+data_intf = {
+ .bLength = sizeof data_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .iInterface = STRING_DATA,
+};
+
+#endif
+
+#ifdef CONFIG_USB_ETH_RNDIS
+
+/* RNDIS doesn't activate by changing to the "real" altsetting */
+
+static const struct usb_interface_descriptor
+rndis_data_intf = {
+ .bLength = sizeof rndis_data_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .iInterface = STRING_DATA,
+};
+
+#endif
+
+#ifdef CONFIG_USB_ETH_SUBSET
+
+/*
+ * "Simple" CDC-subset option is a simple vendor-neutral model that most
+ * full speed controllers can handle: one interface, two bulk endpoints.
+ *
+ * To assist host side drivers, we fancy it up a bit, and add descriptors
+ * so some host side drivers will understand it as a "SAFE" variant.
+ */
+
+static const struct usb_interface_descriptor
+subset_data_intf = {
+ .bLength = sizeof subset_data_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM,
+ .bInterfaceProtocol = 0,
+ .iInterface = STRING_DATA,
+};
+
+#endif /* SUBSET */
+
+static struct usb_endpoint_descriptor
+fs_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor
+fs_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(64),
+};
+
+static const struct usb_descriptor_header *fs_eth_function[11] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+#ifdef CONFIG_USB_ETH_CDC
+ /* "cdc" mode descriptors */
+ (struct usb_descriptor_header *) &control_intf,
+ (struct usb_descriptor_header *) &header_desc,
+ (struct usb_descriptor_header *) &union_desc,
+ (struct usb_descriptor_header *) &ether_desc,
+ /* NOTE: status endpoint may need to be removed */
+ (struct usb_descriptor_header *) &fs_status_desc,
+ /* data interface, with altsetting */
+ (struct usb_descriptor_header *) &data_nop_intf,
+ (struct usb_descriptor_header *) &data_intf,
+ (struct usb_descriptor_header *) &fs_source_desc,
+ (struct usb_descriptor_header *) &fs_sink_desc,
+ NULL,
+#endif /* CONFIG_USB_ETH_CDC */
+};
+
+static inline void fs_subset_descriptors(void)
+{
+#ifdef CONFIG_USB_ETH_SUBSET
+ /* behavior is "CDC Subset"; extra descriptors say "SAFE" */
+ fs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf;
+ fs_eth_function[2] = (struct usb_descriptor_header *) &header_desc;
+ fs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc;
+ fs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc;
+ fs_eth_function[5] = (struct usb_descriptor_header *) &ether_desc;
+ fs_eth_function[6] = (struct usb_descriptor_header *) &fs_source_desc;
+ fs_eth_function[7] = (struct usb_descriptor_header *) &fs_sink_desc;
+ fs_eth_function[8] = NULL;
+#else
+ fs_eth_function[1] = NULL;
+#endif
+}
+
+#ifdef CONFIG_USB_ETH_RNDIS
+static const struct usb_descriptor_header *fs_rndis_function[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ /* control interface matches ACM, not Ethernet */
+ (struct usb_descriptor_header *) &rndis_control_intf,
+ (struct usb_descriptor_header *) &header_desc,
+ (struct usb_descriptor_header *) &call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &union_desc,
+ (struct usb_descriptor_header *) &fs_status_desc,
+ /* data interface has no altsetting */
+ (struct usb_descriptor_header *) &rndis_data_intf,
+ (struct usb_descriptor_header *) &fs_source_desc,
+ (struct usb_descriptor_header *) &fs_sink_desc,
+ NULL,
+};
+#endif
+
+/*
+ * usb 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ */
+
+#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+static struct usb_endpoint_descriptor
+hs_status_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT),
+ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+#endif /* CONFIG_USB_ETH_CDC */
+
+static struct usb_endpoint_descriptor
+hs_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor
+hs_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_qualifier_descriptor
+dev_qualifier = {
+ .bLength = sizeof dev_qualifier,
+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_COMM,
+
+ .bNumConfigurations = 1,
+};
+
+static const struct usb_descriptor_header *hs_eth_function[11] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+#ifdef CONFIG_USB_ETH_CDC
+ /* "cdc" mode descriptors */
+ (struct usb_descriptor_header *) &control_intf,
+ (struct usb_descriptor_header *) &header_desc,
+ (struct usb_descriptor_header *) &union_desc,
+ (struct usb_descriptor_header *) &ether_desc,
+ /* NOTE: status endpoint may need to be removed */
+ (struct usb_descriptor_header *) &hs_status_desc,
+ /* data interface, with altsetting */
+ (struct usb_descriptor_header *) &data_nop_intf,
+ (struct usb_descriptor_header *) &data_intf,
+ (struct usb_descriptor_header *) &hs_source_desc,
+ (struct usb_descriptor_header *) &hs_sink_desc,
+ NULL,
+#endif /* CONFIG_USB_ETH_CDC */
+};
+
+static inline void hs_subset_descriptors(void)
+{
+#ifdef CONFIG_USB_ETH_SUBSET
+ /* behavior is "CDC Subset"; extra descriptors say "SAFE" */
+ hs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf;
+ hs_eth_function[2] = (struct usb_descriptor_header *) &header_desc;
+ hs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc;
+ hs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc;
+ hs_eth_function[5] = (struct usb_descriptor_header *) &ether_desc;
+ hs_eth_function[6] = (struct usb_descriptor_header *) &hs_source_desc;
+ hs_eth_function[7] = (struct usb_descriptor_header *) &hs_sink_desc;
+ hs_eth_function[8] = NULL;
+#else
+ hs_eth_function[1] = NULL;
+#endif
+}
+
+#ifdef CONFIG_USB_ETH_RNDIS
+static const struct usb_descriptor_header *hs_rndis_function[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ /* control interface matches ACM, not Ethernet */
+ (struct usb_descriptor_header *) &rndis_control_intf,
+ (struct usb_descriptor_header *) &header_desc,
+ (struct usb_descriptor_header *) &call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &union_desc,
+ (struct usb_descriptor_header *) &hs_status_desc,
+ /* data interface has no altsetting */
+ (struct usb_descriptor_header *) &rndis_data_intf,
+ (struct usb_descriptor_header *) &hs_source_desc,
+ (struct usb_descriptor_header *) &hs_sink_desc,
+ NULL,
+};
+#endif
+
+
+/* maxpacket and other transfer characteristics vary by speed. */
+static inline struct usb_endpoint_descriptor *
+ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
+ struct usb_endpoint_descriptor *fs)
+{
+ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return hs;
+ return fs;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* descriptors that are built on-demand */
+
+static char manufacturer[50];
+static char product_desc[40] = DRIVER_DESC;
+static char serial_number[20];
+
+/* address that the host will use ... usually assigned at random */
+static char ethaddr[2 * ETH_ALEN + 1];
+
+/* static strings, in UTF-8 */
+static struct usb_string strings[] = {
+ { STRING_MANUFACTURER, manufacturer, },
+ { STRING_PRODUCT, product_desc, },
+ { STRING_SERIALNUMBER, serial_number, },
+ { STRING_DATA, "Ethernet Data", },
+ { STRING_ETHADDR, ethaddr, },
+#ifdef CONFIG_USB_ETH_CDC
+ { STRING_CDC, "CDC Ethernet", },
+ { STRING_CONTROL, "CDC Communications Control", },
+#endif
+#ifdef CONFIG_USB_ETH_SUBSET
+ { STRING_SUBSET, "CDC Ethernet Subset", },
+#endif
+#ifdef CONFIG_USB_ETH_RNDIS
+ { STRING_RNDIS, "RNDIS", },
+ { STRING_RNDIS_CONTROL, "RNDIS Communications Control", },
+#endif
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = strings,
+};
+
+/*============================================================================*/
+DEFINE_CACHE_ALIGN_BUFFER(u8, control_req, USB_BUFSIZ);
+
+#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+DEFINE_CACHE_ALIGN_BUFFER(u8, status_req, STATUS_BYTECOUNT);
+#endif
+
+
+/**
+ * strlcpy - Copy a %NUL terminated string into a sized buffer
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @size: size of destination buffer
+ *
+ * Compatible with *BSD: the result is always a valid
+ * NUL-terminated string that fits in the buffer (unless,
+ * of course, the buffer size is zero). It does not pad
+ * out the result like strncpy() does.
+ */
+size_t strlcpy(char *dest, const char *src, size_t size)
+{
+ size_t ret = strlen(src);
+
+ if (size) {
+ size_t len = (ret >= size) ? size - 1 : ret;
+ memcpy(dest, src, len);
+ dest[len] = '\0';
+ }
+ return ret;
+}
+
+/*============================================================================*/
+
+/*
+ * one config, two interfaces: control, data.
+ * complications: class descriptors, and an altsetting.
+ */
+static int
+config_buf(struct usb_gadget *g, u8 *buf, u8 type, unsigned index, int is_otg)
+{
+ int len;
+ const struct usb_config_descriptor *config;
+ const struct usb_descriptor_header **function;
+ int hs = 0;
+
+ if (gadget_is_dualspeed(g)) {
+ hs = (g->speed == USB_SPEED_HIGH);
+ if (type == USB_DT_OTHER_SPEED_CONFIG)
+ hs = !hs;
+ }
+#define which_fn(t) (hs ? hs_ ## t ## _function : fs_ ## t ## _function)
+
+ if (index >= device_desc.bNumConfigurations)
+ return -EINVAL;
+
+#ifdef CONFIG_USB_ETH_RNDIS
+ /*
+ * list the RNDIS config first, to make Microsoft's drivers
+ * happy. DOCSIS 1.0 needs this too.
+ */
+ if (device_desc.bNumConfigurations == 2 && index == 0) {
+ config = &rndis_config;
+ function = which_fn(rndis);
+ } else
+#endif
+ {
+ config = &eth_config;
+ function = which_fn(eth);
+ }
+
+ /* for now, don't advertise srp-only devices */
+ if (!is_otg)
+ function++;
+
+ len = usb_gadget_config_buf(config, buf, USB_BUFSIZ, function);
+ if (len < 0)
+ return len;
+ ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+ return len;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void eth_start(struct eth_dev *dev, gfp_t gfp_flags);
+static int alloc_requests(struct eth_dev *dev, unsigned n, gfp_t gfp_flags);
+
+static int
+set_ether_config(struct eth_dev *dev, gfp_t gfp_flags)
+{
+ int result = 0;
+ struct usb_gadget *gadget = dev->gadget;
+
+#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+ /* status endpoint used for RNDIS and (optionally) CDC */
+ if (!subset_active(dev) && dev->status_ep) {
+ dev->status = ep_desc(gadget, &hs_status_desc,
+ &fs_status_desc);
+ dev->status_ep->driver_data = dev;
+
+ result = usb_ep_enable(dev->status_ep, dev->status);
+ if (result != 0) {
+ debug("enable %s --> %d\n",
+ dev->status_ep->name, result);
+ goto done;
+ }
+ }
+#endif
+
+ dev->in = ep_desc(gadget, &hs_source_desc, &fs_source_desc);
+ dev->in_ep->driver_data = dev;
+
+ dev->out = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc);
+ dev->out_ep->driver_data = dev;
+
+ /*
+ * With CDC, the host isn't allowed to use these two data
+ * endpoints in the default altsetting for the interface.
+ * so we don't activate them yet. Reset from SET_INTERFACE.
+ *
+ * Strictly speaking RNDIS should work the same: activation is
+ * a side effect of setting a packet filter. Deactivation is
+ * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG.
+ */
+ if (!cdc_active(dev)) {
+ result = usb_ep_enable(dev->in_ep, dev->in);
+ if (result != 0) {
+ debug("enable %s --> %d\n",
+ dev->in_ep->name, result);
+ goto done;
+ }
+
+ result = usb_ep_enable(dev->out_ep, dev->out);
+ if (result != 0) {
+ debug("enable %s --> %d\n",
+ dev->out_ep->name, result);
+ goto done;
+ }
+ }
+
+done:
+ if (result == 0)
+ result = alloc_requests(dev, qlen(gadget), gfp_flags);
+
+ /* on error, disable any endpoints */
+ if (result < 0) {
+ if (!subset_active(dev) && dev->status_ep)
+ (void) usb_ep_disable(dev->status_ep);
+ dev->status = NULL;
+ (void) usb_ep_disable(dev->in_ep);
+ (void) usb_ep_disable(dev->out_ep);
+ dev->in = NULL;
+ dev->out = NULL;
+ } else if (!cdc_active(dev)) {
+ /*
+ * activate non-CDC configs right away
+ * this isn't strictly according to the RNDIS spec
+ */
+ eth_start(dev, GFP_ATOMIC);
+ }
+
+ /* caller is responsible for cleanup on error */
+ return result;
+}
+
+static void eth_reset_config(struct eth_dev *dev)
+{
+ if (dev->config == 0)
+ return;
+
+ debug("%s\n", __func__);
+
+ rndis_uninit(dev->rndis_config);
+
+ /*
+ * disable endpoints, forcing (synchronous) completion of
+ * pending i/o. then free the requests.
+ */
+
+ if (dev->in) {
+ usb_ep_disable(dev->in_ep);
+ if (dev->tx_req) {
+ usb_ep_free_request(dev->in_ep, dev->tx_req);
+ dev->tx_req = NULL;
+ }
+ }
+ if (dev->out) {
+ usb_ep_disable(dev->out_ep);
+ if (dev->rx_req) {
+ usb_ep_free_request(dev->out_ep, dev->rx_req);
+ dev->rx_req = NULL;
+ }
+ }
+ if (dev->status)
+ usb_ep_disable(dev->status_ep);
+
+ dev->rndis = 0;
+ dev->cdc_filter = 0;
+ dev->config = 0;
+}
+
+/*
+ * change our operational config. must agree with the code
+ * that returns config descriptors, and altsetting code.
+ */
+static int eth_set_config(struct eth_dev *dev, unsigned number,
+ gfp_t gfp_flags)
+{
+ int result = 0;
+ struct usb_gadget *gadget = dev->gadget;
+
+ if (gadget_is_sa1100(gadget)
+ && dev->config
+ && dev->tx_qlen != 0) {
+ /* tx fifo is full, but we can't clear it...*/
+ error("can't change configurations");
+ return -ESPIPE;
+ }
+ eth_reset_config(dev);
+
+ switch (number) {
+ case DEV_CONFIG_VALUE:
+ result = set_ether_config(dev, gfp_flags);
+ break;
+#ifdef CONFIG_USB_ETH_RNDIS
+ case DEV_RNDIS_CONFIG_VALUE:
+ dev->rndis = 1;
+ result = set_ether_config(dev, gfp_flags);
+ break;
+#endif
+ default:
+ result = -EINVAL;
+ /* FALL THROUGH */
+ case 0:
+ break;
+ }
+
+ if (result) {
+ if (number)
+ eth_reset_config(dev);
+ usb_gadget_vbus_draw(dev->gadget,
+ gadget_is_otg(dev->gadget) ? 8 : 100);
+ } else {
+ char *speed;
+ unsigned power;
+
+ power = 2 * eth_config.bMaxPower;
+ usb_gadget_vbus_draw(dev->gadget, power);
+
+ switch (gadget->speed) {
+ case USB_SPEED_FULL:
+ speed = "full"; break;
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+ case USB_SPEED_HIGH:
+ speed = "high"; break;
+#endif
+ default:
+ speed = "?"; break;
+ }
+
+ dev->config = number;
+ printf("%s speed config #%d: %d mA, %s, using %s\n",
+ speed, number, power, driver_desc,
+ rndis_active(dev)
+ ? "RNDIS"
+ : (cdc_active(dev)
+ ? "CDC Ethernet"
+ : "CDC Ethernet Subset"));
+ }
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_ETH_CDC
+
+/*
+ * The interrupt endpoint is used in CDC networking models (Ethernet, ATM)
+ * only to notify the host about link status changes (which we support) or
+ * report completion of some encapsulated command (as used in RNDIS). Since
+ * we want this CDC Ethernet code to be vendor-neutral, we don't use that
+ * command mechanism; and only one status request is ever queued.
+ */
+static void eth_status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usb_cdc_notification *event = req->buf;
+ int value = req->status;
+ struct eth_dev *dev = ep->driver_data;
+
+ /* issue the second notification if host reads the first */
+ if (event->bNotificationType == USB_CDC_NOTIFY_NETWORK_CONNECTION
+ && value == 0) {
+ __le32 *data = req->buf + sizeof *event;
+
+ event->bmRequestType = 0xA1;
+ event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
+ event->wValue = __constant_cpu_to_le16(0);
+ event->wIndex = __constant_cpu_to_le16(1);
+ event->wLength = __constant_cpu_to_le16(8);
+
+ /* SPEED_CHANGE data is up/down speeds in bits/sec */
+ data[0] = data[1] = cpu_to_le32(BITRATE(dev->gadget));
+
+ req->length = STATUS_BYTECOUNT;
+ value = usb_ep_queue(ep, req, GFP_ATOMIC);
+ debug("send SPEED_CHANGE --> %d\n", value);
+ if (value == 0)
+ return;
+ } else if (value != -ECONNRESET) {
+ debug("event %02x --> %d\n",
+ event->bNotificationType, value);
+ if (event->bNotificationType ==
+ USB_CDC_NOTIFY_SPEED_CHANGE) {
+ l_ethdev.network_started = 1;
+ printf("USB network up!\n");
+ }
+ }
+ req->context = NULL;
+}
+
+static void issue_start_status(struct eth_dev *dev)
+{
+ struct usb_request *req = dev->stat_req;
+ struct usb_cdc_notification *event;
+ int value;
+
+ /*
+ * flush old status
+ *
+ * FIXME ugly idiom, maybe we'd be better with just
+ * a "cancel the whole queue" primitive since any
+ * unlink-one primitive has way too many error modes.
+ * here, we "know" toggle is already clear...
+ *
+ * FIXME iff req->context != null just dequeue it
+ */
+ usb_ep_disable(dev->status_ep);
+ usb_ep_enable(dev->status_ep, dev->status);
+
+ /*
+ * 3.8.1 says to issue first NETWORK_CONNECTION, then
+ * a SPEED_CHANGE. could be useful in some configs.
+ */
+ event = req->buf;
+ event->bmRequestType = 0xA1;
+ event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+ event->wValue = __constant_cpu_to_le16(1); /* connected */
+ event->wIndex = __constant_cpu_to_le16(1);
+ event->wLength = 0;
+
+ req->length = sizeof *event;
+ req->complete = eth_status_complete;
+ req->context = dev;
+
+ value = usb_ep_queue(dev->status_ep, req, GFP_ATOMIC);
+ if (value < 0)
+ debug("status buf queue --> %d\n", value);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static void eth_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->status || req->actual != req->length)
+ debug("setup complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
+}
+
+#ifdef CONFIG_USB_ETH_RNDIS
+
+static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->status || req->actual != req->length)
+ debug("rndis response complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
+
+ /* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */
+}
+
+static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct eth_dev *dev = ep->driver_data;
+ int status;
+
+ /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
+ status = rndis_msg_parser(dev->rndis_config, (u8 *) req->buf);
+ if (status < 0)
+ error("%s: rndis parse error %d", __func__, status);
+}
+
+#endif /* RNDIS */
+
+/*
+ * The setup() callback implements all the ep0 functionality that's not
+ * handled lower down. CDC has a number of less-common features:
+ *
+ * - two interfaces: control, and ethernet data
+ * - Ethernet data interface has two altsettings: default, and active
+ * - class-specific descriptors for the control interface
+ * - class-specific control requests
+ */
+static int
+eth_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+ struct eth_dev *dev = get_gadget_data(gadget);
+ struct usb_request *req = dev->req;
+ int value = -EOPNOTSUPP;
+ u16 wIndex = le16_to_cpu(ctrl->wIndex);
+ u16 wValue = le16_to_cpu(ctrl->wValue);
+ u16 wLength = le16_to_cpu(ctrl->wLength);
+
+ /*
+ * descriptors just go into the pre-allocated ep0 buffer,
+ * while config change events may enable network traffic.
+ */
+
+ debug("%s\n", __func__);
+
+ req->complete = eth_setup_complete;
+ switch (ctrl->bRequest) {
+
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ break;
+ switch (wValue >> 8) {
+
+ case USB_DT_DEVICE:
+ value = min(wLength, (u16) sizeof device_desc);
+ memcpy(req->buf, &device_desc, value);
+ break;
+ case USB_DT_DEVICE_QUALIFIER:
+ if (!gadget_is_dualspeed(gadget))
+ break;
+ value = min(wLength, (u16) sizeof dev_qualifier);
+ memcpy(req->buf, &dev_qualifier, value);
+ break;
+
+ case USB_DT_OTHER_SPEED_CONFIG:
+ if (!gadget_is_dualspeed(gadget))
+ break;
+ /* FALLTHROUGH */
+ case USB_DT_CONFIG:
+ value = config_buf(gadget, req->buf,
+ wValue >> 8,
+ wValue & 0xff,
+ gadget_is_otg(gadget));
+ if (value >= 0)
+ value = min(wLength, (u16) value);
+ break;
+
+ case USB_DT_STRING:
+ value = usb_gadget_get_string(&stringtab,
+ wValue & 0xff, req->buf);
+
+ if (value >= 0)
+ value = min(wLength, (u16) value);
+
+ break;
+ }
+ break;
+
+ case USB_REQ_SET_CONFIGURATION:
+ if (ctrl->bRequestType != 0)
+ break;
+ if (gadget->a_hnp_support)
+ debug("HNP available\n");
+ else if (gadget->a_alt_hnp_support)
+ debug("HNP needs a different root port\n");
+ value = eth_set_config(dev, wValue, GFP_ATOMIC);
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ break;
+ *(u8 *)req->buf = dev->config;
+ value = min(wLength, (u16) 1);
+ break;
+
+ case USB_REQ_SET_INTERFACE:
+ if (ctrl->bRequestType != USB_RECIP_INTERFACE
+ || !dev->config
+ || wIndex > 1)
+ break;
+ if (!cdc_active(dev) && wIndex != 0)
+ break;
+
+ /*
+ * PXA hardware partially handles SET_INTERFACE;
+ * we need to kluge around that interference.
+ */
+ if (gadget_is_pxa(gadget)) {
+ value = eth_set_config(dev, DEV_CONFIG_VALUE,
+ GFP_ATOMIC);
+ /*
+ * PXA25x driver use non-CDC ethernet gadget.
+ * But only _CDC and _RNDIS code can signalize
+ * that network is working. So we signalize it
+ * here.
+ */
+ l_ethdev.network_started = 1;
+ debug("USB network up!\n");
+ goto done_set_intf;
+ }
+
+#ifdef CONFIG_USB_ETH_CDC
+ switch (wIndex) {
+ case 0: /* control/master intf */
+ if (wValue != 0)
+ break;
+ if (dev->status) {
+ usb_ep_disable(dev->status_ep);
+ usb_ep_enable(dev->status_ep, dev->status);
+ }
+
+ value = 0;
+ break;
+ case 1: /* data intf */
+ if (wValue > 1)
+ break;
+ usb_ep_disable(dev->in_ep);
+ usb_ep_disable(dev->out_ep);
+
+ /*
+ * CDC requires the data transfers not be done from
+ * the default interface setting ... also, setting
+ * the non-default interface resets filters etc.
+ */
+ if (wValue == 1) {
+ if (!cdc_active(dev))
+ break;
+ usb_ep_enable(dev->in_ep, dev->in);
+ usb_ep_enable(dev->out_ep, dev->out);
+ dev->cdc_filter = DEFAULT_FILTER;
+ if (dev->status)
+ issue_start_status(dev);
+ eth_start(dev, GFP_ATOMIC);
+ }
+ value = 0;
+ break;
+ }
+#else
+ /*
+ * FIXME this is wrong, as is the assumption that
+ * all non-PXA hardware talks real CDC ...
+ */
+ debug("set_interface ignored!\n");
+#endif /* CONFIG_USB_ETH_CDC */
+
+done_set_intf:
+ break;
+ case USB_REQ_GET_INTERFACE:
+ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
+ || !dev->config
+ || wIndex > 1)
+ break;
+ if (!(cdc_active(dev) || rndis_active(dev)) && wIndex != 0)
+ break;
+
+ /* for CDC, iff carrier is on, data interface is active. */
+ if (rndis_active(dev) || wIndex != 1)
+ *(u8 *)req->buf = 0;
+ else {
+ /* *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; */
+ /* carrier always ok ...*/
+ *(u8 *)req->buf = 1 ;
+ }
+ value = min(wLength, (u16) 1);
+ break;
+
+#ifdef CONFIG_USB_ETH_CDC
+ case USB_CDC_SET_ETHERNET_PACKET_FILTER:
+ /*
+ * see 6.2.30: no data, wIndex = interface,
+ * wValue = packet filter bitmap
+ */
+ if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+ || !cdc_active(dev)
+ || wLength != 0
+ || wIndex > 1)
+ break;
+ debug("packet filter %02x\n", wValue);
+ dev->cdc_filter = wValue;
+ value = 0;
+ break;
+
+ /*
+ * and potentially:
+ * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
+ * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
+ * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
+ * case USB_CDC_GET_ETHERNET_STATISTIC:
+ */
+
+#endif /* CONFIG_USB_ETH_CDC */
+
+#ifdef CONFIG_USB_ETH_RNDIS
+ /*
+ * RNDIS uses the CDC command encapsulation mechanism to implement
+ * an RPC scheme, with much getting/setting of attributes by OID.
+ */
+ case USB_CDC_SEND_ENCAPSULATED_COMMAND:
+ if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+ || !rndis_active(dev)
+ || wLength > USB_BUFSIZ
+ || wValue
+ || rndis_control_intf.bInterfaceNumber
+ != wIndex)
+ break;
+ /* read the request, then process it */
+ value = wLength;
+ req->complete = rndis_command_complete;
+ /* later, rndis_control_ack () sends a notification */
+ break;
+
+ case USB_CDC_GET_ENCAPSULATED_RESPONSE:
+ if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+ == ctrl->bRequestType
+ && rndis_active(dev)
+ /* && wLength >= 0x0400 */
+ && !wValue
+ && rndis_control_intf.bInterfaceNumber
+ == wIndex) {
+ u8 *buf;
+ u32 n;
+
+ /* return the result */
+ buf = rndis_get_next_response(dev->rndis_config, &n);
+ if (buf) {
+ memcpy(req->buf, buf, n);
+ req->complete = rndis_response_complete;
+ rndis_free_response(dev->rndis_config, buf);
+ value = n;
+ }
+ /* else stalls ... spec says to avoid that */
+ }
+ break;
+#endif /* RNDIS */
+
+ default:
+ debug("unknown control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ wValue, wIndex, wLength);
+ }
+
+ /* respond with data transfer before status phase? */
+ if (value >= 0) {
+ debug("respond with data transfer before status phase\n");
+ req->length = value;
+ req->zero = value < wLength
+ && (value % gadget->ep0->maxpacket) == 0;
+ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0) {
+ debug("ep_queue --> %d\n", value);
+ req->status = 0;
+ eth_setup_complete(gadget->ep0, req);
+ }
+ }
+
+ /* host either stalls (value < 0) or reports success */
+ return value;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req);
+
+static int rx_submit(struct eth_dev *dev, struct usb_request *req,
+ gfp_t gfp_flags)
+{
+ int retval = -ENOMEM;
+ size_t size;
+
+ /*
+ * Padding up to RX_EXTRA handles minor disagreements with host.
+ * Normally we use the USB "terminate on short read" convention;
+ * so allow up to (N*maxpacket), since that memory is normally
+ * already allocated. Some hardware doesn't deal well with short
+ * reads (e.g. DMA must be N*maxpacket), so for now don't trim a
+ * byte off the end (to force hardware errors on overflow).
+ *
+ * RNDIS uses internal framing, and explicitly allows senders to
+ * pad to end-of-packet. That's potentially nice for speed,
+ * but means receivers can't recover synch on their own.
+ */
+
+ debug("%s\n", __func__);
+ if (!req)
+ return -EINVAL;
+
+ size = (ETHER_HDR_SIZE + dev->mtu + RX_EXTRA);
+ size += dev->out_ep->maxpacket - 1;
+ if (rndis_active(dev))
+ size += sizeof(struct rndis_packet_msg_type);
+ size -= size % dev->out_ep->maxpacket;
+
+ /*
+ * Some platforms perform better when IP packets are aligned,
+ * but on at least one, checksumming fails otherwise. Note:
+ * RNDIS headers involve variable numbers of LE32 values.
+ */
+
+ req->buf = (u8 *) NetRxPackets[0];
+ req->length = size;
+ req->complete = rx_complete;
+
+ retval = usb_ep_queue(dev->out_ep, req, gfp_flags);
+
+ if (retval)
+ error("rx submit --> %d", retval);
+
+ return retval;
+}
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct eth_dev *dev = ep->driver_data;
+
+ debug("%s: status %d\n", __func__, req->status);
+ switch (req->status) {
+ /* normal completion */
+ case 0:
+ if (rndis_active(dev)) {
+ /* we know MaxPacketsPerTransfer == 1 here */
+ int length = rndis_rm_hdr(req->buf, req->actual);
+ if (length < 0)
+ goto length_err;
+ req->length -= length;
+ req->actual -= length;
+ }
+ if (req->actual < ETH_HLEN || ETH_FRAME_LEN < req->actual) {
+length_err:
+ dev->stats.rx_errors++;
+ dev->stats.rx_length_errors++;
+ debug("rx length %d\n", req->length);
+ break;
+ }
+
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += req->length;
+ break;
+
+ /* software-driven interface shutdown */
+ case -ECONNRESET: /* unlink */
+ case -ESHUTDOWN: /* disconnect etc */
+ /* for hardware automagic (such as pxa) */
+ case -ECONNABORTED: /* endpoint reset */
+ break;
+
+ /* data overrun */
+ case -EOVERFLOW:
+ dev->stats.rx_over_errors++;
+ /* FALLTHROUGH */
+ default:
+ dev->stats.rx_errors++;
+ break;
+ }
+
+ packet_received = 1;
+}
+
+static int alloc_requests(struct eth_dev *dev, unsigned n, gfp_t gfp_flags)
+{
+
+ dev->tx_req = usb_ep_alloc_request(dev->in_ep, 0);
+
+ if (!dev->tx_req)
+ goto fail1;
+
+ dev->rx_req = usb_ep_alloc_request(dev->out_ep, 0);
+
+ if (!dev->rx_req)
+ goto fail2;
+
+ return 0;
+
+fail2:
+ usb_ep_free_request(dev->in_ep, dev->tx_req);
+fail1:
+ error("can't alloc requests");
+ return -1;
+}
+
+static void tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct eth_dev *dev = ep->driver_data;
+
+ debug("%s: status %s\n", __func__, (req->status) ? "failed" : "ok");
+ switch (req->status) {
+ default:
+ dev->stats.tx_errors++;
+ debug("tx err %d\n", req->status);
+ /* FALLTHROUGH */
+ case -ECONNRESET: /* unlink */
+ case -ESHUTDOWN: /* disconnect etc */
+ break;
+ case 0:
+ dev->stats.tx_bytes += req->length;
+ }
+ dev->stats.tx_packets++;
+
+ packet_sent = 1;
+}
+
+static inline int eth_is_promisc(struct eth_dev *dev)
+{
+ /* no filters for the CDC subset; always promisc */
+ if (subset_active(dev))
+ return 1;
+ return dev->cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
+}
+
+#if 0
+static int eth_start_xmit (struct sk_buff *skb, struct net_device *net)
+{
+ struct eth_dev *dev = netdev_priv(net);
+ int length = skb->len;
+ int retval;
+ struct usb_request *req = NULL;
+ unsigned long flags;
+
+ /* apply outgoing CDC or RNDIS filters */
+ if (!eth_is_promisc (dev)) {
+ u8 *dest = skb->data;
+
+ if (is_multicast_ether_addr(dest)) {
+ u16 type;
+
+ /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
+ * SET_ETHERNET_MULTICAST_FILTERS requests
+ */
+ if (is_broadcast_ether_addr(dest))
+ type = USB_CDC_PACKET_TYPE_BROADCAST;
+ else
+ type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
+ if (!(dev->cdc_filter & type)) {
+ dev_kfree_skb_any (skb);
+ return 0;
+ }
+ }
+ /* ignores USB_CDC_PACKET_TYPE_DIRECTED */
+ }
+
+ spin_lock_irqsave(&dev->req_lock, flags);
+ /*
+ * this freelist can be empty if an interrupt triggered disconnect()
+ * and reconfigured the gadget (shutting down this queue) after the
+ * network stack decided to xmit but before we got the spinlock.
+ */
+ if (list_empty(&dev->tx_reqs)) {
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+ return 1;
+ }
+
+ req = container_of (dev->tx_reqs.next, struct usb_request, list);
+ list_del (&req->list);
+
+ /* temporarily stop TX queue when the freelist empties */
+ if (list_empty (&dev->tx_reqs))
+ netif_stop_queue (net);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+
+ /* no buffer copies needed, unless the network stack did it
+ * or the hardware can't use skb buffers.
+ * or there's not enough space for any RNDIS headers we need
+ */
+ if (rndis_active(dev)) {
+ struct sk_buff *skb_rndis;
+
+ skb_rndis = skb_realloc_headroom (skb,
+ sizeof (struct rndis_packet_msg_type));
+ if (!skb_rndis)
+ goto drop;
+
+ dev_kfree_skb_any (skb);
+ skb = skb_rndis;
+ rndis_add_hdr (skb);
+ length = skb->len;
+ }
+ req->buf = skb->data;
+ req->context = skb;
+ req->complete = tx_complete;
+
+ /* use zlp framing on tx for strict CDC-Ether conformance,
+ * though any robust network rx path ignores extra padding.
+ * and some hardware doesn't like to write zlps.
+ */
+ req->zero = 1;
+ if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0)
+ length++;
+
+ req->length = length;
+
+ /* throttle highspeed IRQ rate back slightly */
+ if (gadget_is_dualspeed(dev->gadget))
+ req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
+ ? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
+ : 0;
+
+ retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);
+ switch (retval) {
+ default:
+ DEBUG (dev, "tx queue err %d\n", retval);
+ break;
+ case 0:
+ net->trans_start = jiffies;
+ atomic_inc (&dev->tx_qlen);
+ }
+
+ if (retval) {
+drop:
+ dev->stats.tx_dropped++;
+ dev_kfree_skb_any (skb);
+ spin_lock_irqsave(&dev->req_lock, flags);
+ if (list_empty (&dev->tx_reqs))
+ netif_start_queue (net);
+ list_add (&req->list, &dev->tx_reqs);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+#endif
+
+static void eth_unbind(struct usb_gadget *gadget)
+{
+ struct eth_dev *dev = get_gadget_data(gadget);
+
+ debug("%s...\n", __func__);
+ rndis_deregister(dev->rndis_config);
+ rndis_exit();
+
+ /* we've already been disconnected ... no i/o is active */
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
+ if (dev->stat_req) {
+ usb_ep_free_request(dev->status_ep, dev->stat_req);
+ dev->stat_req = NULL;
+ }
+
+ if (dev->tx_req) {
+ usb_ep_free_request(dev->in_ep, dev->tx_req);
+ dev->tx_req = NULL;
+ }
+
+ if (dev->rx_req) {
+ usb_ep_free_request(dev->out_ep, dev->rx_req);
+ dev->rx_req = NULL;
+ }
+
+/* unregister_netdev (dev->net);*/
+/* free_netdev(dev->net);*/
+
+ dev->gadget = NULL;
+ set_gadget_data(gadget, NULL);
+}
+
+static void eth_disconnect(struct usb_gadget *gadget)
+{
+ eth_reset_config(get_gadget_data(gadget));
+ /* FIXME RNDIS should enter RNDIS_UNINITIALIZED */
+}
+
+static void eth_suspend(struct usb_gadget *gadget)
+{
+ /* Not used */
+}
+
+static void eth_resume(struct usb_gadget *gadget)
+{
+ /* Not used */
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_ETH_RNDIS
+
+/*
+ * The interrupt endpoint is used in RNDIS to notify the host when messages
+ * other than data packets are available ... notably the REMOTE_NDIS_*_CMPLT
+ * messages, but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even
+ * REMOTE_NDIS_KEEPALIVE_MSG.
+ *
+ * The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE, and
+ * normally just one notification will be queued.
+ */
+
+static void rndis_control_ack_complete(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct eth_dev *dev = ep->driver_data;
+
+ debug("%s...\n", __func__);
+ if (req->status || req->actual != req->length)
+ debug("rndis control ack complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
+
+ if (!l_ethdev.network_started) {
+ if (rndis_get_state(dev->rndis_config)
+ == RNDIS_DATA_INITIALIZED) {
+ l_ethdev.network_started = 1;
+ printf("USB RNDIS network up!\n");
+ }
+ }
+
+ req->context = NULL;
+
+ if (req != dev->stat_req)
+ usb_ep_free_request(ep, req);
+}
+
+static char rndis_resp_buf[8] __attribute__((aligned(sizeof(__le32))));
+
+static int rndis_control_ack(struct eth_device *net)
+{
+ struct eth_dev *dev = &l_ethdev;
+ int length;
+ struct usb_request *resp = dev->stat_req;
+
+ /* in case RNDIS calls this after disconnect */
+ if (!dev->status) {
+ debug("status ENODEV\n");
+ return -ENODEV;
+ }
+
+ /* in case queue length > 1 */
+ if (resp->context) {
+ resp = usb_ep_alloc_request(dev->status_ep, GFP_ATOMIC);
+ if (!resp)
+ return -ENOMEM;
+ resp->buf = rndis_resp_buf;
+ }
+
+ /*
+ * Send RNDIS RESPONSE_AVAILABLE notification;
+ * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too
+ */
+ resp->length = 8;
+ resp->complete = rndis_control_ack_complete;
+ resp->context = dev;
+
+ *((__le32 *) resp->buf) = __constant_cpu_to_le32(1);
+ *((__le32 *) (resp->buf + 4)) = __constant_cpu_to_le32(0);
+
+ length = usb_ep_queue(dev->status_ep, resp, GFP_ATOMIC);
+ if (length < 0) {
+ resp->status = 0;
+ rndis_control_ack_complete(dev->status_ep, resp);
+ }
+
+ return 0;
+}
+
+#else
+
+#define rndis_control_ack NULL
+
+#endif /* RNDIS */
+
+static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
+{
+ if (rndis_active(dev)) {
+ rndis_set_param_medium(dev->rndis_config,
+ NDIS_MEDIUM_802_3,
+ BITRATE(dev->gadget)/100);
+ rndis_signal_connect(dev->rndis_config);
+ }
+}
+
+static int eth_stop(struct eth_dev *dev)
+{
+#ifdef RNDIS_COMPLETE_SIGNAL_DISCONNECT
+ unsigned long ts;
+ unsigned long timeout = CONFIG_SYS_HZ; /* 1 sec to stop RNDIS */
+#endif
+
+ if (rndis_active(dev)) {
+ rndis_set_param_medium(dev->rndis_config, NDIS_MEDIUM_802_3, 0);
+ rndis_signal_disconnect(dev->rndis_config);
+
+#ifdef RNDIS_COMPLETE_SIGNAL_DISCONNECT
+ /* Wait until host receives OID_GEN_MEDIA_CONNECT_STATUS */
+ ts = get_timer(0);
+ while (get_timer(ts) < timeout)
+ usb_gadget_handle_interrupts();
+#endif
+
+ rndis_uninit(dev->rndis_config);
+ dev->rndis = 0;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int is_eth_addr_valid(char *str)
+{
+ if (strlen(str) == 17) {
+ int i;
+ char *p, *q;
+ uchar ea[6];
+
+ /* see if it looks like an ethernet address */
+
+ p = str;
+
+ for (i = 0; i < 6; i++) {
+ char term = (i == 5 ? '\0' : ':');
+
+ ea[i] = simple_strtol(p, &q, 16);
+
+ if ((q - p) != 2 || *q++ != term)
+ break;
+
+ p = q;
+ }
+
+ /* Now check the contents. */
+ return is_valid_ether_addr(ea);
+ }
+ return 0;
+}
+
+static u8 nibble(unsigned char c)
+{
+ if (likely(isdigit(c)))
+ return c - '0';
+ c = toupper(c);
+ if (likely(isxdigit(c)))
+ return 10 + c - 'A';
+ return 0;
+}
+
+static int get_ether_addr(const char *str, u8 *dev_addr)
+{
+ if (str) {
+ unsigned i;
+
+ for (i = 0; i < 6; i++) {
+ unsigned char num;
+
+ if ((*str == '.') || (*str == ':'))
+ str++;
+ num = nibble(*str++) << 4;
+ num |= (nibble(*str++));
+ dev_addr[i] = num;
+ }
+ if (is_valid_ether_addr(dev_addr))
+ return 0;
+ }
+ return 1;
+}
+
+static int eth_bind(struct usb_gadget *gadget)
+{
+ struct eth_dev *dev = &l_ethdev;
+ u8 cdc = 1, zlp = 1, rndis = 1;
+ struct usb_ep *in_ep, *out_ep, *status_ep = NULL;
+ int status = -ENOMEM;
+ int gcnum;
+ u8 tmp[7];
+
+ /* these flags are only ever cleared; compiler take note */
+#ifndef CONFIG_USB_ETH_CDC
+ cdc = 0;
+#endif
+#ifndef CONFIG_USB_ETH_RNDIS
+ rndis = 0;
+#endif
+ /*
+ * Because most host side USB stacks handle CDC Ethernet, that
+ * standard protocol is _strongly_ preferred for interop purposes.
+ * (By everyone except Microsoft.)
+ */
+ if (gadget_is_pxa(gadget)) {
+ /* pxa doesn't support altsettings */
+ cdc = 0;
+ } else if (gadget_is_musbhdrc(gadget)) {
+ /* reduce tx dma overhead by avoiding special cases */
+ zlp = 0;
+ } else if (gadget_is_sh(gadget)) {
+ /* sh doesn't support multiple interfaces or configs */
+ cdc = 0;
+ rndis = 0;
+ } else if (gadget_is_sa1100(gadget)) {
+ /* hardware can't write zlps */
+ zlp = 0;
+ /*
+ * sa1100 CAN do CDC, without status endpoint ... we use
+ * non-CDC to be compatible with ARM Linux-2.4 "usb-eth".
+ */
+ cdc = 0;
+ }
+
+ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum >= 0)
+ device_desc.bcdDevice = cpu_to_le16(0x0300 + gcnum);
+ else {
+ /*
+ * can't assume CDC works. don't want to default to
+ * anything less functional on CDC-capable hardware,
+ * so we fail in this case.
+ */
+ error("controller '%s' not recognized",
+ gadget->name);
+ return -ENODEV;
+ }
+
+ /*
+ * If there's an RNDIS configuration, that's what Windows wants to
+ * be using ... so use these product IDs here and in the "linux.inf"
+ * needed to install MSFT drivers. Current Linux kernels will use
+ * the second configuration if it's CDC Ethernet, and need some help
+ * to choose the right configuration otherwise.
+ */
+ if (rndis) {
+#if defined(CONFIG_USB_RNDIS_VENDOR_ID) && defined(CONFIG_USB_RNDIS_PRODUCT_ID)
+ device_desc.idVendor =
+ __constant_cpu_to_le16(CONFIG_USB_RNDIS_VENDOR_ID);
+ device_desc.idProduct =
+ __constant_cpu_to_le16(CONFIG_USB_RNDIS_PRODUCT_ID);
+#else
+ device_desc.idVendor =
+ __constant_cpu_to_le16(RNDIS_VENDOR_NUM);
+ device_desc.idProduct =
+ __constant_cpu_to_le16(RNDIS_PRODUCT_NUM);
+#endif
+ sprintf(product_desc, "RNDIS/%s", driver_desc);
+
+ /*
+ * CDC subset ... recognized by Linux since 2.4.10, but Windows
+ * drivers aren't widely available. (That may be improved by
+ * supporting one submode of the "SAFE" variant of MDLM.)
+ */
+ } else {
+#if defined(CONFIG_USB_CDC_VENDOR_ID) && defined(CONFIG_USB_CDC_PRODUCT_ID)
+ device_desc.idVendor = cpu_to_le16(CONFIG_USB_CDC_VENDOR_ID);
+ device_desc.idProduct = cpu_to_le16(CONFIG_USB_CDC_PRODUCT_ID);
+#else
+ if (!cdc) {
+ device_desc.idVendor =
+ __constant_cpu_to_le16(SIMPLE_VENDOR_NUM);
+ device_desc.idProduct =
+ __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM);
+ }
+#endif
+ }
+ /* support optional vendor/distro customization */
+ if (bcdDevice)
+ device_desc.bcdDevice = cpu_to_le16(bcdDevice);
+ if (iManufacturer)
+ strlcpy(manufacturer, iManufacturer, sizeof manufacturer);
+ if (iProduct)
+ strlcpy(product_desc, iProduct, sizeof product_desc);
+ if (iSerialNumber) {
+ device_desc.iSerialNumber = STRING_SERIALNUMBER,
+ strlcpy(serial_number, iSerialNumber, sizeof serial_number);
+ }
+
+ /* all we really need is bulk IN/OUT */
+ usb_ep_autoconfig_reset(gadget);
+ in_ep = usb_ep_autoconfig(gadget, &fs_source_desc);
+ if (!in_ep) {
+autoconf_fail:
+ error("can't autoconfigure on %s\n",
+ gadget->name);
+ return -ENODEV;
+ }
+ in_ep->driver_data = in_ep; /* claim */
+
+ out_ep = usb_ep_autoconfig(gadget, &fs_sink_desc);
+ if (!out_ep)
+ goto autoconf_fail;
+ out_ep->driver_data = out_ep; /* claim */
+
+#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+ /*
+ * CDC Ethernet control interface doesn't require a status endpoint.
+ * Since some hosts expect one, try to allocate one anyway.
+ */
+ if (cdc || rndis) {
+ status_ep = usb_ep_autoconfig(gadget, &fs_status_desc);
+ if (status_ep) {
+ status_ep->driver_data = status_ep; /* claim */
+ } else if (rndis) {
+ error("can't run RNDIS on %s", gadget->name);
+ return -ENODEV;
+#ifdef CONFIG_USB_ETH_CDC
+ } else if (cdc) {
+ control_intf.bNumEndpoints = 0;
+ /* FIXME remove endpoint from descriptor list */
+#endif
+ }
+ }
+#endif
+
+ /* one config: cdc, else minimal subset */
+ if (!cdc) {
+ eth_config.bNumInterfaces = 1;
+ eth_config.iConfiguration = STRING_SUBSET;
+
+ /*
+ * use functions to set these up, in case we're built to work
+ * with multiple controllers and must override CDC Ethernet.
+ */
+ fs_subset_descriptors();
+ hs_subset_descriptors();
+ }
+
+ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+ usb_gadget_set_selfpowered(gadget);
+
+ /* For now RNDIS is always a second config */
+ if (rndis)
+ device_desc.bNumConfigurations = 2;
+
+ if (gadget_is_dualspeed(gadget)) {
+ if (rndis)
+ dev_qualifier.bNumConfigurations = 2;
+ else if (!cdc)
+ dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+
+ /* assumes ep0 uses the same value for both speeds ... */
+ dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
+
+ /* and that all endpoints are dual-speed */
+ hs_source_desc.bEndpointAddress =
+ fs_source_desc.bEndpointAddress;
+ hs_sink_desc.bEndpointAddress =
+ fs_sink_desc.bEndpointAddress;
+#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+ if (status_ep)
+ hs_status_desc.bEndpointAddress =
+ fs_status_desc.bEndpointAddress;
+#endif
+ }
+
+ if (gadget_is_otg(gadget)) {
+ otg_descriptor.bmAttributes |= USB_OTG_HNP,
+ eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ eth_config.bMaxPower = 4;
+#ifdef CONFIG_USB_ETH_RNDIS
+ rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ rndis_config.bMaxPower = 4;
+#endif
+ }
+
+
+ /* network device setup */
+ dev->net = &l_netdev;
+
+ dev->cdc = cdc;
+ dev->zlp = zlp;
+
+ dev->in_ep = in_ep;
+ dev->out_ep = out_ep;
+ dev->status_ep = status_ep;
+
+ /*
+ * Module params for these addresses should come from ID proms.
+ * The host side address is used with CDC and RNDIS, and commonly
+ * ends up in a persistent config database. It's not clear if
+ * host side code for the SAFE thing cares -- its original BLAN
+ * thing didn't, Sharp never assigned those addresses on Zaurii.
+ */
+ get_ether_addr(dev_addr, dev->net->enetaddr);
+
+ memset(tmp, 0, sizeof(tmp));
+ memcpy(tmp, dev->net->enetaddr, sizeof(dev->net->enetaddr));
+
+ get_ether_addr(host_addr, dev->host_mac);
+
+ sprintf(ethaddr, "%02X%02X%02X%02X%02X%02X",
+ dev->host_mac[0], dev->host_mac[1],
+ dev->host_mac[2], dev->host_mac[3],
+ dev->host_mac[4], dev->host_mac[5]);
+
+ if (rndis) {
+ status = rndis_init();
+ if (status < 0) {
+ error("can't init RNDIS, %d", status);
+ goto fail;
+ }
+ }
+
+ /*
+ * use PKTSIZE (or aligned... from u-boot) and set
+ * wMaxSegmentSize accordingly
+ */
+ dev->mtu = PKTSIZE_ALIGN; /* RNDIS does not like this, only 1514, TODO*/
+
+ /* preallocate control message data and buffer */
+ dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!dev->req)
+ goto fail;
+ dev->req->buf = control_req;
+ dev->req->complete = eth_setup_complete;
+
+ /* ... and maybe likewise for status transfer */
+#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+ if (dev->status_ep) {
+ dev->stat_req = usb_ep_alloc_request(dev->status_ep,
+ GFP_KERNEL);
+ if (!dev->stat_req) {
+ usb_ep_free_request(dev->status_ep, dev->req);
+
+ goto fail;
+ }
+ dev->stat_req->buf = status_req;
+ dev->stat_req->context = NULL;
+ }
+#endif
+
+ /* finish hookup to lower layer ... */
+ dev->gadget = gadget;
+ set_gadget_data(gadget, dev);
+ gadget->ep0->driver_data = dev;
+
+ /*
+ * two kinds of host-initiated state changes:
+ * - iff DATA transfer is active, carrier is "on"
+ * - tx queueing enabled if open *and* carrier is "on"
+ */
+
+ printf("using %s, OUT %s IN %s%s%s\n", gadget->name,
+ out_ep->name, in_ep->name,
+ status_ep ? " STATUS " : "",
+ status_ep ? status_ep->name : ""
+ );
+ printf("MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->net->enetaddr[0], dev->net->enetaddr[1],
+ dev->net->enetaddr[2], dev->net->enetaddr[3],
+ dev->net->enetaddr[4], dev->net->enetaddr[5]);
+
+ if (cdc || rndis)
+ printf("HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->host_mac[0], dev->host_mac[1],
+ dev->host_mac[2], dev->host_mac[3],
+ dev->host_mac[4], dev->host_mac[5]);
+
+ if (rndis) {
+ u32 vendorID = 0;
+
+ /* FIXME RNDIS vendor id == "vendor NIC code" == ? */
+
+ dev->rndis_config = rndis_register(rndis_control_ack);
+ if (dev->rndis_config < 0) {
+fail0:
+ eth_unbind(gadget);
+ debug("RNDIS setup failed\n");
+ status = -ENODEV;
+ goto fail;
+ }
+
+ /* these set up a lot of the OIDs that RNDIS needs */
+ rndis_set_host_mac(dev->rndis_config, dev->host_mac);
+ if (rndis_set_param_dev(dev->rndis_config, dev->net, dev->mtu,
+ &dev->stats, &dev->cdc_filter))
+ goto fail0;
+ if (rndis_set_param_vendor(dev->rndis_config, vendorID,
+ manufacturer))
+ goto fail0;
+ if (rndis_set_param_medium(dev->rndis_config,
+ NDIS_MEDIUM_802_3, 0))
+ goto fail0;
+ printf("RNDIS ready\n");
+ }
+ return 0;
+
+fail:
+ error("%s failed, status = %d", __func__, status);
+ eth_unbind(gadget);
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int usb_eth_init(struct eth_device *netdev, bd_t *bd)
+{
+ struct eth_dev *dev = &l_ethdev;
+ struct usb_gadget *gadget;
+ unsigned long ts;
+ unsigned long timeout = USB_CONNECT_TIMEOUT;
+
+ if (!netdev) {
+ error("received NULL ptr");
+ goto fail;
+ }
+
+ /* Configure default mac-addresses for the USB ethernet device */
+#ifdef CONFIG_USBNET_DEV_ADDR
+ strlcpy(dev_addr, CONFIG_USBNET_DEV_ADDR, sizeof(dev_addr));
+#endif
+#ifdef CONFIG_USBNET_HOST_ADDR
+ strlcpy(host_addr, CONFIG_USBNET_HOST_ADDR, sizeof(host_addr));
+#endif
+ /* Check if the user overruled the MAC addresses */
+ if (getenv("usbnet_devaddr"))
+ strlcpy(dev_addr, getenv("usbnet_devaddr"),
+ sizeof(dev_addr));
+
+ if (getenv("usbnet_hostaddr"))
+ strlcpy(host_addr, getenv("usbnet_hostaddr"),
+ sizeof(host_addr));
+
+ if (!is_eth_addr_valid(dev_addr)) {
+ error("Need valid 'usbnet_devaddr' to be set");
+ goto fail;
+ }
+ if (!is_eth_addr_valid(host_addr)) {
+ error("Need valid 'usbnet_hostaddr' to be set");
+ goto fail;
+ }
+
+ if (usb_gadget_register_driver(&eth_driver) < 0)
+ goto fail;
+
+ dev->network_started = 0;
+
+ packet_received = 0;
+ packet_sent = 0;
+
+ gadget = dev->gadget;
+ usb_gadget_connect(gadget);
+
+ if (getenv("cdc_connect_timeout"))
+ timeout = simple_strtoul(getenv("cdc_connect_timeout"),
+ NULL, 10) * CONFIG_SYS_HZ;
+ ts = get_timer(0);
+ while (!l_ethdev.network_started) {
+ /* Handle control-c and timeouts */
+ if (ctrlc() || (get_timer(ts) > timeout)) {
+ error("The remote end did not respond in time.");
+ goto fail;
+ }
+ usb_gadget_handle_interrupts();
+ }
+
+ packet_received = 0;
+ rx_submit(dev, dev->rx_req, 0);
+ return 0;
+fail:
+ return -1;
+}
+
+static int usb_eth_send(struct eth_device *netdev, void *packet, int length)
+{
+ int retval;
+ void *rndis_pkt = NULL;
+ struct eth_dev *dev = &l_ethdev;
+ struct usb_request *req = dev->tx_req;
+ unsigned long ts;
+ unsigned long timeout = USB_CONNECT_TIMEOUT;
+
+ debug("%s:...\n", __func__);
+
+ /* new buffer is needed to include RNDIS header */
+ if (rndis_active(dev)) {
+ rndis_pkt = malloc(length +
+ sizeof(struct rndis_packet_msg_type));
+ if (!rndis_pkt) {
+ error("No memory to alloc RNDIS packet");
+ goto drop;
+ }
+ rndis_add_hdr(rndis_pkt, length);
+ memcpy(rndis_pkt + sizeof(struct rndis_packet_msg_type),
+ packet, length);
+ packet = rndis_pkt;
+ length += sizeof(struct rndis_packet_msg_type);
+ }
+ req->buf = packet;
+ req->context = NULL;
+ req->complete = tx_complete;
+
+ /*
+ * use zlp framing on tx for strict CDC-Ether conformance,
+ * though any robust network rx path ignores extra padding.
+ * and some hardware doesn't like to write zlps.
+ */
+ req->zero = 1;
+ if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0)
+ length++;
+
+ req->length = length;
+#if 0
+ /* throttle highspeed IRQ rate back slightly */
+ if (gadget_is_dualspeed(dev->gadget))
+ req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
+ ? ((dev->tx_qlen % qmult) != 0) : 0;
+#endif
+ dev->tx_qlen = 1;
+ ts = get_timer(0);
+ packet_sent = 0;
+
+ retval = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC);
+
+ if (!retval)
+ debug("%s: packet queued\n", __func__);
+ while (!packet_sent) {
+ if (get_timer(ts) > timeout) {
+ printf("timeout sending packets to usb ethernet\n");
+ return -1;
+ }
+ usb_gadget_handle_interrupts();
+ }
+ if (rndis_pkt)
+ free(rndis_pkt);
+
+ return 0;
+drop:
+ dev->stats.tx_dropped++;
+ return -ENOMEM;
+}
+
+static int usb_eth_recv(struct eth_device *netdev)
+{
+ struct eth_dev *dev = &l_ethdev;
+
+ usb_gadget_handle_interrupts();
+
+ if (packet_received) {
+ debug("%s: packet received\n", __func__);
+ if (dev->rx_req) {
+ NetReceive(NetRxPackets[0], dev->rx_req->length);
+ packet_received = 0;
+
+ rx_submit(dev, dev->rx_req, 0);
+ } else
+ error("dev->rx_req invalid");
+ }
+ return 0;
+}
+
+void usb_eth_halt(struct eth_device *netdev)
+{
+ struct eth_dev *dev = &l_ethdev;
+
+ if (!netdev) {
+ error("received NULL ptr");
+ return;
+ }
+
+ /* If the gadget not registered, simple return */
+ if (!dev->gadget)
+ return;
+
+ /*
+ * Some USB controllers may need additional deinitialization here
+ * before dropping pull-up (also due to hardware issues).
+ * For example: unhandled interrupt with status stage started may
+ * bring the controller to fully broken state (until board reset).
+ * There are some variants to debug and fix such cases:
+ * 1) In the case of RNDIS connection eth_stop can perform additional
+ * interrupt handling. See RNDIS_COMPLETE_SIGNAL_DISCONNECT definition.
+ * 2) 'pullup' callback in your UDC driver can be improved to perform
+ * this deinitialization.
+ */
+ eth_stop(dev);
+
+ usb_gadget_disconnect(dev->gadget);
+
+ /* Clear pending interrupt */
+ if (dev->network_started) {
+ usb_gadget_handle_interrupts();
+ dev->network_started = 0;
+ }
+
+ usb_gadget_unregister_driver(&eth_driver);
+}
+
+static struct usb_gadget_driver eth_driver = {
+ .speed = DEVSPEED,
+
+ .bind = eth_bind,
+ .unbind = eth_unbind,
+
+ .setup = eth_setup,
+ .disconnect = eth_disconnect,
+
+ .suspend = eth_suspend,
+ .resume = eth_resume,
+};
+
+int usb_eth_initialize(bd_t *bi)
+{
+ struct eth_device *netdev = &l_netdev;
+
+ strlcpy(netdev->name, USB_NET_NAME, sizeof(netdev->name));
+
+ netdev->init = usb_eth_init;
+ netdev->send = usb_eth_send;
+ netdev->recv = usb_eth_recv;
+ netdev->halt = usb_eth_halt;
+
+#ifdef CONFIG_MCAST_TFTP
+ #error not supported
+#endif
+ eth_register(netdev);
+ return 0;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/f_dfu.c b/src/roms/u-boot/drivers/usb/gadget/f_dfu.c
new file mode 100644
index 0000000..1b1e179
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/f_dfu.c
@@ -0,0 +1,822 @@
+/*
+ * f_dfu.c -- Device Firmware Update USB function
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * Based on OpenMoko u-boot: drivers/usb/usbdfu.c
+ * (C) 2007 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ *
+ * based on existing SAM7DFU code from OpenPCD:
+ * (C) Copyright 2006 by Harald Welte <hwelte at hmw-consulting.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <errno.h>
+#include <common.h>
+#include <malloc.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+#include <dfu.h>
+#include <g_dnl.h>
+#include "f_dfu.h"
+
+struct f_dfu {
+ struct usb_function usb_function;
+
+ struct usb_descriptor_header **function;
+ struct usb_string *strings;
+
+ /* when configured, we have one config */
+ u8 config;
+ u8 altsetting;
+ enum dfu_state dfu_state;
+ unsigned int dfu_status;
+
+ /* Send/received block number is handy for data integrity check */
+ int blk_seq_num;
+ unsigned int poll_timeout;
+};
+
+typedef int (*dfu_state_fn) (struct f_dfu *,
+ const struct usb_ctrlrequest *,
+ struct usb_gadget *,
+ struct usb_request *);
+
+static inline struct f_dfu *func_to_dfu(struct usb_function *f)
+{
+ return container_of(f, struct f_dfu, usb_function);
+}
+
+static const struct dfu_function_descriptor dfu_func = {
+ .bLength = sizeof dfu_func,
+ .bDescriptorType = DFU_DT_FUNC,
+ .bmAttributes = DFU_BIT_WILL_DETACH |
+ DFU_BIT_MANIFESTATION_TOLERANT |
+ DFU_BIT_CAN_UPLOAD |
+ DFU_BIT_CAN_DNLOAD,
+ .wDetachTimeOut = 0,
+ .wTransferSize = DFU_USB_BUFSIZ,
+ .bcdDFUVersion = __constant_cpu_to_le16(0x0110),
+};
+
+static struct usb_interface_descriptor dfu_intf_runtime = {
+ .bLength = sizeof dfu_intf_runtime,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_APP_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 1,
+ /* .iInterface = DYNAMIC */
+};
+
+static struct usb_descriptor_header *dfu_runtime_descs[] = {
+ (struct usb_descriptor_header *) &dfu_intf_runtime,
+ NULL,
+};
+
+static const struct usb_qualifier_descriptor dev_qualifier = {
+ .bLength = sizeof dev_qualifier,
+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+ .bNumConfigurations = 1,
+};
+
+static const char dfu_name[] = "Device Firmware Upgrade";
+
+/*
+ * static strings, in UTF-8
+ *
+ * dfu_generic configuration
+ */
+static struct usb_string strings_dfu_generic[] = {
+ [0].s = dfu_name,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dfu_generic = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dfu_generic,
+};
+
+static struct usb_gadget_strings *dfu_generic_strings[] = {
+ &stringtab_dfu_generic,
+ NULL,
+};
+
+/*
+ * usb_function specific
+ */
+static struct usb_gadget_strings stringtab_dfu = {
+ .language = 0x0409, /* en-us */
+ /*
+ * .strings
+ *
+ * assigned during initialization,
+ * depends on number of flash entities
+ *
+ */
+};
+
+static struct usb_gadget_strings *dfu_strings[] = {
+ &stringtab_dfu,
+ NULL,
+};
+
+static void dfu_set_poll_timeout(struct dfu_status *dstat, unsigned int ms)
+{
+ /*
+ * The bwPollTimeout DFU_GETSTATUS request payload provides information
+ * about minimum time, in milliseconds, that the host should wait before
+ * sending a subsequent DFU_GETSTATUS request
+ *
+ * This permits the device to vary the delay depending on its need to
+ * erase or program the memory
+ *
+ */
+
+ unsigned char *p = (unsigned char *)&ms;
+
+ if (!ms || (ms & ~DFU_POLL_TIMEOUT_MASK)) {
+ dstat->bwPollTimeout[0] = 0;
+ dstat->bwPollTimeout[1] = 0;
+ dstat->bwPollTimeout[2] = 0;
+
+ return;
+ }
+
+ dstat->bwPollTimeout[0] = *p++;
+ dstat->bwPollTimeout[1] = *p++;
+ dstat->bwPollTimeout[2] = *p;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_dfu *f_dfu = req->context;
+
+ dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf,
+ req->length, f_dfu->blk_seq_num);
+}
+
+static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_dfu *f_dfu = req->context;
+
+ dfu_flush(dfu_get_entity(f_dfu->altsetting), req->buf,
+ req->length, f_dfu->blk_seq_num);
+}
+
+static void handle_getstatus(struct usb_request *req)
+{
+ struct dfu_status *dstat = (struct dfu_status *)req->buf;
+ struct f_dfu *f_dfu = req->context;
+
+ dfu_set_poll_timeout(dstat, 0);
+
+ switch (f_dfu->dfu_state) {
+ case DFU_STATE_dfuDNLOAD_SYNC:
+ case DFU_STATE_dfuDNBUSY:
+ f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
+ break;
+ case DFU_STATE_dfuMANIFEST_SYNC:
+ f_dfu->dfu_state = DFU_STATE_dfuMANIFEST;
+ break;
+ case DFU_STATE_dfuMANIFEST:
+ dfu_set_poll_timeout(dstat, DFU_MANIFEST_POLL_TIMEOUT);
+ default:
+ break;
+ }
+
+ if (f_dfu->poll_timeout)
+ if (!(f_dfu->blk_seq_num %
+ (dfu_get_buf_size() / DFU_USB_BUFSIZ)))
+ dfu_set_poll_timeout(dstat, f_dfu->poll_timeout);
+
+ /* send status response */
+ dstat->bStatus = f_dfu->dfu_status;
+ dstat->bState = f_dfu->dfu_state;
+ dstat->iString = 0;
+}
+
+static void handle_getstate(struct usb_request *req)
+{
+ struct f_dfu *f_dfu = req->context;
+
+ ((u8 *)req->buf)[0] = f_dfu->dfu_state;
+ req->actual = sizeof(u8);
+}
+
+static inline void to_dfu_mode(struct f_dfu *f_dfu)
+{
+ f_dfu->usb_function.strings = dfu_strings;
+ f_dfu->usb_function.hs_descriptors = f_dfu->function;
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+}
+
+static inline void to_runtime_mode(struct f_dfu *f_dfu)
+{
+ f_dfu->usb_function.strings = NULL;
+ f_dfu->usb_function.hs_descriptors = dfu_runtime_descs;
+}
+
+static int handle_upload(struct usb_request *req, u16 len)
+{
+ struct f_dfu *f_dfu = req->context;
+
+ return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf,
+ req->length, f_dfu->blk_seq_num);
+}
+
+static int handle_dnload(struct usb_gadget *gadget, u16 len)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_request *req = cdev->req;
+ struct f_dfu *f_dfu = req->context;
+
+ if (len == 0)
+ f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
+
+ req->complete = dnload_request_complete;
+
+ return len;
+}
+
+/*-------------------------------------------------------------------------*/
+/* DFU state machine */
+static int state_app_idle(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ case USB_REQ_DFU_DETACH:
+ f_dfu->dfu_state = DFU_STATE_appDETACH;
+ to_dfu_mode(f_dfu);
+ value = RET_ZLP;
+ break;
+ default:
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_app_detach(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_appIDLE;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_idle(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_DNLOAD:
+ if (len == 0) {
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+ f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+ f_dfu->blk_seq_num = w_value;
+ value = handle_dnload(gadget, len);
+ break;
+ case USB_REQ_DFU_UPLOAD:
+ f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
+ f_dfu->blk_seq_num = 0;
+ value = handle_upload(req, len);
+ break;
+ case USB_REQ_DFU_ABORT:
+ /* no zlp? */
+ value = RET_ZLP;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ case USB_REQ_DFU_DETACH:
+ /*
+ * Proprietary extension: 'detach' from idle mode and
+ * get back to runtime mode in case of USB Reset. As
+ * much as I dislike this, we just can't use every USB
+ * bus reset to switch back to runtime mode, since at
+ * least the Linux USB stack likes to send a number of
+ * resets in a row :(
+ */
+ f_dfu->dfu_state =
+ DFU_STATE_dfuMANIFEST_WAIT_RST;
+ to_runtime_mode(f_dfu);
+ f_dfu->dfu_state = DFU_STATE_appIDLE;
+
+ dfu_trigger_reset();
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_dnload_sync(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_dnbusy(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_dnload_idle(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_DNLOAD:
+ f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
+ f_dfu->blk_seq_num = w_value;
+ value = handle_dnload(gadget, len);
+ break;
+ case USB_REQ_DFU_ABORT:
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ value = RET_ZLP;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_manifest_sync(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ /* We're MainfestationTolerant */
+ f_dfu->dfu_state = DFU_STATE_dfuMANIFEST;
+ handle_getstatus(req);
+ f_dfu->blk_seq_num = 0;
+ value = RET_STAT_LEN;
+ req->complete = dnload_request_flush;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_manifest(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ /* We're MainfestationTolerant */
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ handle_getstatus(req);
+ f_dfu->blk_seq_num = 0;
+ value = RET_STAT_LEN;
+ puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n");
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+ return value;
+}
+
+static int state_dfu_upload_idle(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_UPLOAD:
+ /* state transition if less data then requested */
+ f_dfu->blk_seq_num = w_value;
+ value = handle_upload(req, len);
+ if (value >= 0 && value < len)
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ break;
+ case USB_REQ_DFU_ABORT:
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ /* no zlp? */
+ value = RET_ZLP;
+ break;
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static int state_dfu_error(struct f_dfu *f_dfu,
+ const struct usb_ctrlrequest *ctrl,
+ struct usb_gadget *gadget,
+ struct usb_request *req)
+{
+ int value = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_DFU_GETSTATUS:
+ handle_getstatus(req);
+ value = RET_STAT_LEN;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ handle_getstate(req);
+ break;
+ case USB_REQ_DFU_CLRSTATUS:
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ f_dfu->dfu_status = DFU_STATUS_OK;
+ /* no zlp? */
+ value = RET_ZLP;
+ break;
+ default:
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ value = RET_STALL;
+ break;
+ }
+
+ return value;
+}
+
+static dfu_state_fn dfu_state[] = {
+ state_app_idle, /* DFU_STATE_appIDLE */
+ state_app_detach, /* DFU_STATE_appDETACH */
+ state_dfu_idle, /* DFU_STATE_dfuIDLE */
+ state_dfu_dnload_sync, /* DFU_STATE_dfuDNLOAD_SYNC */
+ state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */
+ state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */
+ state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */
+ state_dfu_manifest, /* DFU_STATE_dfuMANIFEST */
+ NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */
+ state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */
+ state_dfu_error /* DFU_STATE_dfuERROR */
+};
+
+static int
+dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_gadget *gadget = f->config->cdev->gadget;
+ struct usb_request *req = f->config->cdev->req;
+ struct f_dfu *f_dfu = f->config->cdev->req->context;
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ int value = 0;
+ u8 req_type = ctrl->bRequestType & USB_TYPE_MASK;
+
+ debug("w_value: 0x%x len: 0x%x\n", w_value, len);
+ debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n",
+ req_type, ctrl->bRequest, f_dfu->dfu_state);
+
+ if (req_type == USB_TYPE_STANDARD) {
+ if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR &&
+ (w_value >> 8) == DFU_DT_FUNC) {
+ value = min(len, (u16) sizeof(dfu_func));
+ memcpy(req->buf, &dfu_func, value);
+ }
+ } else /* DFU specific request */
+ value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req);
+
+ if (value >= 0) {
+ req->length = value;
+ req->zero = value < len;
+ value = usb_ep_queue(gadget->ep0, req, 0);
+ if (value < 0) {
+ debug("ep_queue --> %d\n", value);
+ req->status = 0;
+ }
+ }
+
+ return value;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+dfu_prepare_strings(struct f_dfu *f_dfu, int n)
+{
+ struct dfu_entity *de = NULL;
+ int i = 0;
+
+ f_dfu->strings = calloc(sizeof(struct usb_string), n + 1);
+ if (!f_dfu->strings)
+ goto enomem;
+
+ for (i = 0; i < n; ++i) {
+ de = dfu_get_entity(i);
+ f_dfu->strings[i].s = de->name;
+ }
+
+ f_dfu->strings[i].id = 0;
+ f_dfu->strings[i].s = NULL;
+
+ return 0;
+
+enomem:
+ while (i)
+ f_dfu->strings[--i].s = NULL;
+
+ free(f_dfu->strings);
+
+ return -ENOMEM;
+}
+
+static int dfu_prepare_function(struct f_dfu *f_dfu, int n)
+{
+ struct usb_interface_descriptor *d;
+ int i = 0;
+
+ f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n + 1);
+ if (!f_dfu->function)
+ goto enomem;
+
+ for (i = 0; i < n; ++i) {
+ d = calloc(sizeof(*d), 1);
+ if (!d)
+ goto enomem;
+
+ d->bLength = sizeof(*d);
+ d->bDescriptorType = USB_DT_INTERFACE;
+ d->bAlternateSetting = i;
+ d->bNumEndpoints = 0;
+ d->bInterfaceClass = USB_CLASS_APP_SPEC;
+ d->bInterfaceSubClass = 1;
+ d->bInterfaceProtocol = 2;
+
+ f_dfu->function[i] = (struct usb_descriptor_header *)d;
+ }
+ f_dfu->function[i] = NULL;
+
+ return 0;
+
+enomem:
+ while (i) {
+ free(f_dfu->function[--i]);
+ f_dfu->function[i] = NULL;
+ }
+ free(f_dfu->function);
+
+ return -ENOMEM;
+}
+
+static int dfu_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_dfu *f_dfu = func_to_dfu(f);
+ int alt_num = dfu_get_alt_number();
+ int rv, id, i;
+
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ dfu_intf_runtime.bInterfaceNumber = id;
+
+ f_dfu->dfu_state = DFU_STATE_appIDLE;
+ f_dfu->dfu_status = DFU_STATUS_OK;
+
+ rv = dfu_prepare_function(f_dfu, alt_num);
+ if (rv)
+ goto error;
+
+ rv = dfu_prepare_strings(f_dfu, alt_num);
+ if (rv)
+ goto error;
+ for (i = 0; i < alt_num; i++) {
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ f_dfu->strings[i].id = id;
+ ((struct usb_interface_descriptor *)f_dfu->function[i])
+ ->iInterface = id;
+ }
+
+ to_dfu_mode(f_dfu);
+
+ stringtab_dfu.strings = f_dfu->strings;
+
+ cdev->req->context = f_dfu;
+
+error:
+ return rv;
+}
+
+static void dfu_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_dfu *f_dfu = func_to_dfu(f);
+ int alt_num = dfu_get_alt_number();
+ int i;
+
+ if (f_dfu->strings) {
+ i = alt_num;
+ while (i)
+ f_dfu->strings[--i].s = NULL;
+
+ free(f_dfu->strings);
+ }
+
+ if (f_dfu->function) {
+ i = alt_num;
+ while (i) {
+ free(f_dfu->function[--i]);
+ f_dfu->function[i] = NULL;
+ }
+ free(f_dfu->function);
+ }
+
+ free(f_dfu);
+}
+
+static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_dfu *f_dfu = func_to_dfu(f);
+
+ debug("%s: intf:%d alt:%d\n", __func__, intf, alt);
+
+ f_dfu->altsetting = alt;
+
+ return 0;
+}
+
+/* TODO: is this really what we need here? */
+static void dfu_disable(struct usb_function *f)
+{
+ struct f_dfu *f_dfu = func_to_dfu(f);
+ if (f_dfu->config == 0)
+ return;
+
+ debug("%s: reset config\n", __func__);
+
+ f_dfu->config = 0;
+}
+
+static int dfu_bind_config(struct usb_configuration *c)
+{
+ struct f_dfu *f_dfu;
+ int status;
+
+ f_dfu = calloc(sizeof(*f_dfu), 1);
+ if (!f_dfu)
+ return -ENOMEM;
+ f_dfu->usb_function.name = "dfu";
+ f_dfu->usb_function.hs_descriptors = dfu_runtime_descs;
+ f_dfu->usb_function.bind = dfu_bind;
+ f_dfu->usb_function.unbind = dfu_unbind;
+ f_dfu->usb_function.set_alt = dfu_set_alt;
+ f_dfu->usb_function.disable = dfu_disable;
+ f_dfu->usb_function.strings = dfu_generic_strings;
+ f_dfu->usb_function.setup = dfu_handle;
+ f_dfu->poll_timeout = DFU_DEFAULT_POLL_TIMEOUT;
+
+ status = usb_add_function(c, &f_dfu->usb_function);
+ if (status)
+ free(f_dfu);
+
+ return status;
+}
+
+int dfu_add(struct usb_configuration *c)
+{
+ int id;
+
+ id = usb_string_id(c->cdev);
+ if (id < 0)
+ return id;
+ strings_dfu_generic[0].id = id;
+ dfu_intf_runtime.iInterface = id;
+
+ debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__,
+ c->cdev, c->cdev->gadget, c->cdev->gadget->ep0);
+
+ return dfu_bind_config(c);
+}
+
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_dfu, dfu_add);
diff --git a/src/roms/u-boot/drivers/usb/gadget/f_dfu.h b/src/roms/u-boot/drivers/usb/gadget/f_dfu.h
new file mode 100644
index 0000000..0c29954
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/f_dfu.h
@@ -0,0 +1,87 @@
+/*
+ * f_dfu.h -- Device Firmware Update gadget
+ *
+ * Copyright (C) 2011-2012 Samsung Electronics
+ * author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __F_DFU_H_
+#define __F_DFU_H_
+
+#include <linux/compiler.h>
+#include <linux/usb/composite.h>
+
+#define DFU_CONFIG_VAL 1
+#define DFU_DT_FUNC 0x21
+
+#define DFU_BIT_WILL_DETACH (0x1 << 3)
+#define DFU_BIT_MANIFESTATION_TOLERANT (0x1 << 2)
+#define DFU_BIT_CAN_UPLOAD (0x1 << 1)
+#define DFU_BIT_CAN_DNLOAD 0x1
+
+/* big enough to hold our biggest descriptor */
+#define DFU_USB_BUFSIZ 4096
+
+#define USB_REQ_DFU_DETACH 0x00
+#define USB_REQ_DFU_DNLOAD 0x01
+#define USB_REQ_DFU_UPLOAD 0x02
+#define USB_REQ_DFU_GETSTATUS 0x03
+#define USB_REQ_DFU_CLRSTATUS 0x04
+#define USB_REQ_DFU_GETSTATE 0x05
+#define USB_REQ_DFU_ABORT 0x06
+
+#define DFU_STATUS_OK 0x00
+#define DFU_STATUS_errTARGET 0x01
+#define DFU_STATUS_errFILE 0x02
+#define DFU_STATUS_errWRITE 0x03
+#define DFU_STATUS_errERASE 0x04
+#define DFU_STATUS_errCHECK_ERASED 0x05
+#define DFU_STATUS_errPROG 0x06
+#define DFU_STATUS_errVERIFY 0x07
+#define DFU_STATUS_errADDRESS 0x08
+#define DFU_STATUS_errNOTDONE 0x09
+#define DFU_STATUS_errFIRMWARE 0x0a
+#define DFU_STATUS_errVENDOR 0x0b
+#define DFU_STATUS_errUSBR 0x0c
+#define DFU_STATUS_errPOR 0x0d
+#define DFU_STATUS_errUNKNOWN 0x0e
+#define DFU_STATUS_errSTALLEDPKT 0x0f
+
+#define RET_STALL -1
+#define RET_ZLP 0
+#define RET_STAT_LEN 6
+
+enum dfu_state {
+ DFU_STATE_appIDLE = 0,
+ DFU_STATE_appDETACH = 1,
+ DFU_STATE_dfuIDLE = 2,
+ DFU_STATE_dfuDNLOAD_SYNC = 3,
+ DFU_STATE_dfuDNBUSY = 4,
+ DFU_STATE_dfuDNLOAD_IDLE = 5,
+ DFU_STATE_dfuMANIFEST_SYNC = 6,
+ DFU_STATE_dfuMANIFEST = 7,
+ DFU_STATE_dfuMANIFEST_WAIT_RST = 8,
+ DFU_STATE_dfuUPLOAD_IDLE = 9,
+ DFU_STATE_dfuERROR = 10,
+};
+
+struct dfu_status {
+ __u8 bStatus;
+ __u8 bwPollTimeout[3];
+ __u8 bState;
+ __u8 iString;
+} __packed;
+
+struct dfu_function_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bmAttributes;
+ __le16 wDetachTimeOut;
+ __le16 wTransferSize;
+ __le16 bcdDFUVersion;
+} __packed;
+
+#define DFU_POLL_TIMEOUT_MASK (0xFFFFFFUL)
+#endif /* __F_DFU_H_ */
diff --git a/src/roms/u-boot/drivers/usb/gadget/f_mass_storage.c b/src/roms/u-boot/drivers/usb/gadget/f_mass_storage.c
new file mode 100644
index 0000000..6374bb9
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/f_mass_storage.c
@@ -0,0 +1,2783 @@
+/*
+ * f_mass_storage.c -- Mass Storage USB Composite Function
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyright (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause
+ */
+
+/*
+ * The Mass Storage Function acts as a USB Mass Storage device,
+ * appearing to the host as a disk drive or as a CD-ROM drive. In
+ * addition to providing an example of a genuinely useful composite
+ * function for a USB device, it also illustrates a technique of
+ * double-buffering for increased throughput.
+ *
+ * Function supports multiple logical units (LUNs). Backing storage
+ * for each LUN is provided by a regular file or a block device.
+ * Access for each LUN can be limited to read-only. Moreover, the
+ * function can indicate that LUN is removable and/or CD-ROM. (The
+ * later implies read-only access.)
+ *
+ * MSF is configured by specifying a fsg_config structure. It has the
+ * following fields:
+ *
+ * nluns Number of LUNs function have (anywhere from 1
+ * to FSG_MAX_LUNS which is 8).
+ * luns An array of LUN configuration values. This
+ * should be filled for each LUN that
+ * function will include (ie. for "nluns"
+ * LUNs). Each element of the array has
+ * the following fields:
+ * ->filename The path to the backing file for the LUN.
+ * Required if LUN is not marked as
+ * removable.
+ * ->ro Flag specifying access to the LUN shall be
+ * read-only. This is implied if CD-ROM
+ * emulation is enabled as well as when
+ * it was impossible to open "filename"
+ * in R/W mode.
+ * ->removable Flag specifying that LUN shall be indicated as
+ * being removable.
+ * ->cdrom Flag specifying that LUN shall be reported as
+ * being a CD-ROM.
+ *
+ * lun_name_format A printf-like format for names of the LUN
+ * devices. This determines how the
+ * directory in sysfs will be named.
+ * Unless you are using several MSFs in
+ * a single gadget (as opposed to single
+ * MSF in many configurations) you may
+ * leave it as NULL (in which case
+ * "lun%d" will be used). In the format
+ * you can use "%d" to index LUNs for
+ * MSF's with more than one LUN. (Beware
+ * that there is only one integer given
+ * as an argument for the format and
+ * specifying invalid format may cause
+ * unspecified behaviour.)
+ * thread_name Name of the kernel thread process used by the
+ * MSF. You can safely set it to NULL
+ * (in which case default "file-storage"
+ * will be used).
+ *
+ * vendor_name
+ * product_name
+ * release Information used as a reply to INQUIRY
+ * request. To use default set to NULL,
+ * NULL, 0xffff respectively. The first
+ * field should be 8 and the second 16
+ * characters or less.
+ *
+ * can_stall Set to permit function to halt bulk endpoints.
+ * Disabled on some USB devices known not
+ * to work correctly. You should set it
+ * to true.
+ *
+ * If "removable" is not set for a LUN then a backing file must be
+ * specified. If it is set, then NULL filename means the LUN's medium
+ * is not loaded (an empty string as "filename" in the fsg_config
+ * structure causes error). The CD-ROM emulation includes a single
+ * data track and no audio tracks; hence there need be only one
+ * backing file per LUN. Note also that the CD-ROM block length is
+ * set to 512 rather than the more common value 2048.
+ *
+ *
+ * MSF includes support for module parameters. If gadget using it
+ * decides to use it, the following module parameters will be
+ * available:
+ *
+ * file=filename[,filename...]
+ * Names of the files or block devices used for
+ * backing storage.
+ * ro=b[,b...] Default false, boolean for read-only access.
+ * removable=b[,b...]
+ * Default true, boolean for removable media.
+ * cdrom=b[,b...] Default false, boolean for whether to emulate
+ * a CD-ROM drive.
+ * luns=N Default N = number of filenames, number of
+ * LUNs to support.
+ * stall Default determined according to the type of
+ * USB device controller (usually true),
+ * boolean to permit the driver to halt
+ * bulk endpoints.
+ *
+ * The module parameters may be prefixed with some string. You need
+ * to consult gadget's documentation or source to verify whether it is
+ * using those module parameters and if it does what are the prefixes
+ * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is
+ * the prefix).
+ *
+ *
+ * Requirements are modest; only a bulk-in and a bulk-out endpoint are
+ * needed. The memory requirement amounts to two 16K buffers, size
+ * configurable by a parameter. Support is included for both
+ * full-speed and high-speed operation.
+ *
+ * Note that the driver is slightly non-portable in that it assumes a
+ * single memory/DMA buffer will be useable for bulk-in, bulk-out, and
+ * interrupt-in endpoints. With most device controllers this isn't an
+ * issue, but there may be some with hardware restrictions that prevent
+ * a buffer from being used by more than one endpoint.
+ *
+ *
+ * The pathnames of the backing files and the ro settings are
+ * available in the attribute files "file" and "ro" in the lun<n> (or
+ * to be more precise in a directory which name comes from
+ * "lun_name_format" option!) subdirectory of the gadget's sysfs
+ * directory. If the "removable" option is set, writing to these
+ * files will simulate ejecting/loading the medium (writing an empty
+ * line means eject) and adjusting a write-enable tab. Changes to the
+ * ro setting are not allowed when the medium is loaded or if CD-ROM
+ * emulation is being used.
+ *
+ * When a LUN receive an "eject" SCSI request (Start/Stop Unit),
+ * if the LUN is removable, the backing file is released to simulate
+ * ejection.
+ *
+ *
+ * This function is heavily based on "File-backed Storage Gadget" by
+ * Alan Stern which in turn is heavily based on "Gadget Zero" by David
+ * Brownell. The driver's SCSI command interface was based on the
+ * "Information technology - Small Computer System Interface - 2"
+ * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93,
+ * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>.
+ * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which
+ * was based on the "Universal Serial Bus Mass Storage Class UFI
+ * Command Specification" document, Revision 1.0, December 14, 1998,
+ * available at
+ * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>.
+ */
+
+/*
+ * Driver Design
+ *
+ * The MSF is fairly straightforward. There is a main kernel
+ * thread that handles most of the work. Interrupt routines field
+ * callbacks from the controller driver: bulk- and interrupt-request
+ * completion notifications, endpoint-0 events, and disconnect events.
+ * Completion events are passed to the main thread by wakeup calls. Many
+ * ep0 requests are handled at interrupt time, but SetInterface,
+ * SetConfiguration, and device reset requests are forwarded to the
+ * thread in the form of "exceptions" using SIGUSR1 signals (since they
+ * should interrupt any ongoing file I/O operations).
+ *
+ * The thread's main routine implements the standard command/data/status
+ * parts of a SCSI interaction. It and its subroutines are full of tests
+ * for pending signals/exceptions -- all this polling is necessary since
+ * the kernel has no setjmp/longjmp equivalents. (Maybe this is an
+ * indication that the driver really wants to be running in userspace.)
+ * An important point is that so long as the thread is alive it keeps an
+ * open reference to the backing file. This will prevent unmounting
+ * the backing file's underlying filesystem and could cause problems
+ * during system shutdown, for example. To prevent such problems, the
+ * thread catches INT, TERM, and KILL signals and converts them into
+ * an EXIT exception.
+ *
+ * In normal operation the main thread is started during the gadget's
+ * fsg_bind() callback and stopped during fsg_unbind(). But it can
+ * also exit when it receives a signal, and there's no point leaving
+ * the gadget running when the thread is dead. At of this moment, MSF
+ * provides no way to deregister the gadget when thread dies -- maybe
+ * a callback functions is needed.
+ *
+ * To provide maximum throughput, the driver uses a circular pipeline of
+ * buffer heads (struct fsg_buffhd). In principle the pipeline can be
+ * arbitrarily long; in practice the benefits don't justify having more
+ * than 2 stages (i.e., double buffering). But it helps to think of the
+ * pipeline as being a long one. Each buffer head contains a bulk-in and
+ * a bulk-out request pointer (since the buffer can be used for both
+ * output and input -- directions always are given from the host's
+ * point of view) as well as a pointer to the buffer and various state
+ * variables.
+ *
+ * Use of the pipeline follows a simple protocol. There is a variable
+ * (fsg->next_buffhd_to_fill) that points to the next buffer head to use.
+ * At any time that buffer head may still be in use from an earlier
+ * request, so each buffer head has a state variable indicating whether
+ * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the
+ * buffer head to be EMPTY, filling the buffer either by file I/O or by
+ * USB I/O (during which the buffer head is BUSY), and marking the buffer
+ * head FULL when the I/O is complete. Then the buffer will be emptied
+ * (again possibly by USB I/O, during which it is marked BUSY) and
+ * finally marked EMPTY again (possibly by a completion routine).
+ *
+ * A module parameter tells the driver to avoid stalling the bulk
+ * endpoints wherever the transport specification allows. This is
+ * necessary for some UDCs like the SuperH, which cannot reliably clear a
+ * halt on a bulk endpoint. However, under certain circumstances the
+ * Bulk-only specification requires a stall. In such cases the driver
+ * will halt the endpoint and set a flag indicating that it should clear
+ * the halt in software during the next device reset. Hopefully this
+ * will permit everything to work correctly. Furthermore, although the
+ * specification allows the bulk-out endpoint to halt when the host sends
+ * too much data, implementing this would cause an unavoidable race.
+ * The driver will always use the "no-stall" approach for OUT transfers.
+ *
+ * One subtle point concerns sending status-stage responses for ep0
+ * requests. Some of these requests, such as device reset, can involve
+ * interrupting an ongoing file I/O operation, which might take an
+ * arbitrarily long time. During that delay the host might give up on
+ * the original ep0 request and issue a new one. When that happens the
+ * driver should not notify the host about completion of the original
+ * request, as the host will no longer be waiting for it. So the driver
+ * assigns to each ep0 request a unique tag, and it keeps track of the
+ * tag value of the request associated with a long-running exception
+ * (device-reset, interface-change, or configuration-change). When the
+ * exception handler is finished, the status-stage response is submitted
+ * only if the current ep0 request tag is equal to the exception request
+ * tag. Thus only the most recently received ep0 request will get a
+ * status-stage response.
+ *
+ * Warning: This driver source file is too long. It ought to be split up
+ * into a header file plus about 3 separate .c files, to handle the details
+ * of the Gadget, USB Mass Storage, and SCSI protocols.
+ */
+
+/* #define VERBOSE_DEBUG */
+/* #define DUMP_MSGS */
+
+#include <config.h>
+#include <malloc.h>
+#include <common.h>
+#include <g_dnl.h>
+
+#include <linux/err.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <usb_mass_storage.h>
+
+#include <asm/unaligned.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include <usb/lin_gadget_compat.h>
+#include <g_dnl.h>
+
+/*------------------------------------------------------------------------*/
+
+#define FSG_DRIVER_DESC "Mass Storage Function"
+#define FSG_DRIVER_VERSION "2012/06/5"
+
+static const char fsg_string_interface[] = "Mass Storage";
+
+#define FSG_NO_INTR_EP 1
+#define FSG_NO_DEVICE_STRINGS 1
+#define FSG_NO_OTG 1
+#define FSG_NO_INTR_EP 1
+
+#include "storage_common.c"
+
+/*-------------------------------------------------------------------------*/
+
+#define GFP_ATOMIC ((gfp_t) 0)
+#define PAGE_CACHE_SHIFT 12
+#define PAGE_CACHE_SIZE (1 << PAGE_CACHE_SHIFT)
+#define kthread_create(...) __builtin_return_address(0)
+#define wait_for_completion(...) do {} while (0)
+
+struct kref {int x; };
+struct completion {int x; };
+
+inline void set_bit(int nr, volatile void *addr)
+{
+ int mask;
+ unsigned int *a = (unsigned int *) addr;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ *a |= mask;
+}
+
+inline void clear_bit(int nr, volatile void *addr)
+{
+ int mask;
+ unsigned int *a = (unsigned int *) addr;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ *a &= ~mask;
+}
+
+struct fsg_dev;
+struct fsg_common;
+
+/* Data shared by all the FSG instances. */
+struct fsg_common {
+ struct usb_gadget *gadget;
+ struct fsg_dev *fsg, *new_fsg;
+
+ struct usb_ep *ep0; /* Copy of gadget->ep0 */
+ struct usb_request *ep0req; /* Copy of cdev->req */
+ unsigned int ep0_req_tag;
+
+ struct fsg_buffhd *next_buffhd_to_fill;
+ struct fsg_buffhd *next_buffhd_to_drain;
+ struct fsg_buffhd buffhds[FSG_NUM_BUFFERS];
+
+ int cmnd_size;
+ u8 cmnd[MAX_COMMAND_SIZE];
+
+ unsigned int nluns;
+ unsigned int lun;
+ struct fsg_lun luns[FSG_MAX_LUNS];
+
+ unsigned int bulk_out_maxpacket;
+ enum fsg_state state; /* For exception handling */
+ unsigned int exception_req_tag;
+
+ enum data_direction data_dir;
+ u32 data_size;
+ u32 data_size_from_cmnd;
+ u32 tag;
+ u32 residue;
+ u32 usb_amount_left;
+
+ unsigned int can_stall:1;
+ unsigned int free_storage_on_release:1;
+ unsigned int phase_error:1;
+ unsigned int short_packet_received:1;
+ unsigned int bad_lun_okay:1;
+ unsigned int running:1;
+
+ int thread_wakeup_needed;
+ struct completion thread_notifier;
+ struct task_struct *thread_task;
+
+ /* Callback functions. */
+ const struct fsg_operations *ops;
+ /* Gadget's private data. */
+ void *private_data;
+
+ const char *vendor_name; /* 8 characters or less */
+ const char *product_name; /* 16 characters or less */
+ u16 release;
+
+ /* Vendor (8 chars), product (16 chars), release (4
+ * hexadecimal digits) and NUL byte */
+ char inquiry_string[8 + 16 + 4 + 1];
+
+ struct kref ref;
+};
+
+struct fsg_config {
+ unsigned nluns;
+ struct fsg_lun_config {
+ const char *filename;
+ char ro;
+ char removable;
+ char cdrom;
+ char nofua;
+ } luns[FSG_MAX_LUNS];
+
+ /* Callback functions. */
+ const struct fsg_operations *ops;
+ /* Gadget's private data. */
+ void *private_data;
+
+ const char *vendor_name; /* 8 characters or less */
+ const char *product_name; /* 16 characters or less */
+
+ char can_stall;
+};
+
+struct fsg_dev {
+ struct usb_function function;
+ struct usb_gadget *gadget; /* Copy of cdev->gadget */
+ struct fsg_common *common;
+
+ u16 interface_number;
+
+ unsigned int bulk_in_enabled:1;
+ unsigned int bulk_out_enabled:1;
+
+ unsigned long atomic_bitflags;
+#define IGNORE_BULK_OUT 0
+
+ struct usb_ep *bulk_in;
+ struct usb_ep *bulk_out;
+};
+
+
+static inline int __fsg_is_set(struct fsg_common *common,
+ const char *func, unsigned line)
+{
+ if (common->fsg)
+ return 1;
+ ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
+ WARN_ON(1);
+ return 0;
+}
+
+#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__))
+
+
+static inline struct fsg_dev *fsg_from_func(struct usb_function *f)
+{
+ return container_of(f, struct fsg_dev, function);
+}
+
+
+typedef void (*fsg_routine_t)(struct fsg_dev *);
+
+static int exception_in_progress(struct fsg_common *common)
+{
+ return common->state > FSG_STATE_IDLE;
+}
+
+/* Make bulk-out requests be divisible by the maxpacket size */
+static void set_bulk_out_req_length(struct fsg_common *common,
+ struct fsg_buffhd *bh, unsigned int length)
+{
+ unsigned int rem;
+
+ bh->bulk_out_intended_length = length;
+ rem = length % common->bulk_out_maxpacket;
+ if (rem > 0)
+ length += common->bulk_out_maxpacket - rem;
+ bh->outreq->length = length;
+}
+
+/*-------------------------------------------------------------------------*/
+
+struct ums *ums;
+struct fsg_common *the_fsg_common;
+
+static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
+{
+ const char *name;
+
+ if (ep == fsg->bulk_in)
+ name = "bulk-in";
+ else if (ep == fsg->bulk_out)
+ name = "bulk-out";
+ else
+ name = ep->name;
+ DBG(fsg, "%s set halt\n", name);
+ return usb_ep_set_halt(ep);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* These routines may be called in process context or in_irq */
+
+/* Caller must hold fsg->lock */
+static void wakeup_thread(struct fsg_common *common)
+{
+ common->thread_wakeup_needed = 1;
+}
+
+static void raise_exception(struct fsg_common *common, enum fsg_state new_state)
+{
+ /* Do nothing if a higher-priority exception is already in progress.
+ * If a lower-or-equal priority exception is in progress, preempt it
+ * and notify the main thread by sending it a signal. */
+ if (common->state <= new_state) {
+ common->exception_req_tag = common->ep0_req_tag;
+ common->state = new_state;
+ common->thread_wakeup_needed = 1;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ep0_queue(struct fsg_common *common)
+{
+ int rc;
+
+ rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC);
+ common->ep0->driver_data = common;
+ if (rc != 0 && rc != -ESHUTDOWN) {
+ /* We can't do much more than wait for a reset */
+ WARNING(common, "error in submission: %s --> %d\n",
+ common->ep0->name, rc);
+ }
+ return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Bulk and interrupt endpoint completion handlers.
+ * These always run in_irq. */
+
+static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct fsg_common *common = ep->driver_data;
+ struct fsg_buffhd *bh = req->context;
+
+ if (req->status || req->actual != req->length)
+ DBG(common, "%s --> %d, %u/%u\n", __func__,
+ req->status, req->actual, req->length);
+ if (req->status == -ECONNRESET) /* Request was cancelled */
+ usb_ep_fifo_flush(ep);
+
+ /* Hold the lock while we update the request and buffer states */
+ bh->inreq_busy = 0;
+ bh->state = BUF_STATE_EMPTY;
+ wakeup_thread(common);
+}
+
+static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct fsg_common *common = ep->driver_data;
+ struct fsg_buffhd *bh = req->context;
+
+ dump_msg(common, "bulk-out", req->buf, req->actual);
+ if (req->status || req->actual != bh->bulk_out_intended_length)
+ DBG(common, "%s --> %d, %u/%u\n", __func__,
+ req->status, req->actual,
+ bh->bulk_out_intended_length);
+ if (req->status == -ECONNRESET) /* Request was cancelled */
+ usb_ep_fifo_flush(ep);
+
+ /* Hold the lock while we update the request and buffer states */
+ bh->outreq_busy = 0;
+ bh->state = BUF_STATE_FULL;
+ wakeup_thread(common);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Ep0 class-specific handlers. These always run in_irq. */
+
+static int fsg_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct usb_request *req = fsg->common->ep0req;
+ u16 w_index = get_unaligned_le16(&ctrl->wIndex);
+ u16 w_value = get_unaligned_le16(&ctrl->wValue);
+ u16 w_length = get_unaligned_le16(&ctrl->wLength);
+
+ if (!fsg_is_set(fsg->common))
+ return -EOPNOTSUPP;
+
+ switch (ctrl->bRequest) {
+
+ case USB_BULK_RESET_REQUEST:
+ if (ctrl->bRequestType !=
+ (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+ break;
+ if (w_index != fsg->interface_number || w_value != 0)
+ return -EDOM;
+
+ /* Raise an exception to stop the current operation
+ * and reinitialize our state. */
+ DBG(fsg, "bulk reset request\n");
+ raise_exception(fsg->common, FSG_STATE_RESET);
+ return DELAYED_STATUS;
+
+ case USB_BULK_GET_MAX_LUN_REQUEST:
+ if (ctrl->bRequestType !=
+ (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+ break;
+ if (w_index != fsg->interface_number || w_value != 0)
+ return -EDOM;
+ VDBG(fsg, "get max LUN\n");
+ *(u8 *) req->buf = fsg->common->nluns - 1;
+
+ /* Respond with data/status */
+ req->length = min((u16)1, w_length);
+ return ep0_queue(fsg->common);
+ }
+
+ VDBG(fsg,
+ "unknown class-specific control req "
+ "%02x.%02x v%04x i%04x l%u\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ get_unaligned_le16(&ctrl->wValue), w_index, w_length);
+ return -EOPNOTSUPP;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* All the following routines run in process context */
+
+/* Use this for bulk or interrupt transfers, not ep0 */
+static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
+ struct usb_request *req, int *pbusy,
+ enum fsg_buffer_state *state)
+{
+ int rc;
+
+ if (ep == fsg->bulk_in)
+ dump_msg(fsg, "bulk-in", req->buf, req->length);
+
+ *pbusy = 1;
+ *state = BUF_STATE_BUSY;
+ rc = usb_ep_queue(ep, req, GFP_KERNEL);
+ if (rc != 0) {
+ *pbusy = 0;
+ *state = BUF_STATE_EMPTY;
+
+ /* We can't do much more than wait for a reset */
+
+ /* Note: currently the net2280 driver fails zero-length
+ * submissions if DMA is enabled. */
+ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP &&
+ req->length == 0))
+ WARNING(fsg, "error in submission: %s --> %d\n",
+ ep->name, rc);
+ }
+}
+
+#define START_TRANSFER_OR(common, ep_name, req, pbusy, state) \
+ if (fsg_is_set(common)) \
+ start_transfer((common)->fsg, (common)->fsg->ep_name, \
+ req, pbusy, state); \
+ else
+
+#define START_TRANSFER(common, ep_name, req, pbusy, state) \
+ START_TRANSFER_OR(common, ep_name, req, pbusy, state) (void)0
+
+static void busy_indicator(void)
+{
+ static int state;
+
+ switch (state) {
+ case 0:
+ puts("\r|"); break;
+ case 1:
+ puts("\r/"); break;
+ case 2:
+ puts("\r-"); break;
+ case 3:
+ puts("\r\\"); break;
+ case 4:
+ puts("\r|"); break;
+ case 5:
+ puts("\r/"); break;
+ case 6:
+ puts("\r-"); break;
+ case 7:
+ puts("\r\\"); break;
+ default:
+ state = 0;
+ }
+ if (state++ == 8)
+ state = 0;
+}
+
+static int sleep_thread(struct fsg_common *common)
+{
+ int rc = 0;
+ int i = 0, k = 0;
+
+ /* Wait until a signal arrives or we are woken up */
+ for (;;) {
+ if (common->thread_wakeup_needed)
+ break;
+
+ if (++i == 50000) {
+ busy_indicator();
+ i = 0;
+ k++;
+ }
+
+ if (k == 10) {
+ /* Handle CTRL+C */
+ if (ctrlc())
+ return -EPIPE;
+
+ /* Check cable connection */
+ if (!g_dnl_board_usb_cable_connected())
+ return -EIO;
+
+ k = 0;
+ }
+
+ usb_gadget_handle_interrupts();
+ }
+ common->thread_wakeup_needed = 0;
+ return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int do_read(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ u32 lba;
+ struct fsg_buffhd *bh;
+ int rc;
+ u32 amount_left;
+ loff_t file_offset;
+ unsigned int amount;
+ unsigned int partial_page;
+ ssize_t nread;
+
+ /* Get the starting Logical Block Address and check that it's
+ * not too big */
+ if (common->cmnd[0] == SC_READ_6)
+ lba = get_unaligned_be24(&common->cmnd[1]);
+ else {
+ lba = get_unaligned_be32(&common->cmnd[2]);
+
+ /* We allow DPO (Disable Page Out = don't save data in the
+ * cache) and FUA (Force Unit Access = don't read from the
+ * cache), but we don't implement them. */
+ if ((common->cmnd[1] & ~0x18) != 0) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ }
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ file_offset = ((loff_t) lba) << 9;
+
+ /* Carry out the file reads */
+ amount_left = common->data_size_from_cmnd;
+ if (unlikely(amount_left == 0))
+ return -EIO; /* No default reply */
+
+ for (;;) {
+
+ /* Figure out how much we need to read:
+ * Try to read the remaining amount.
+ * But don't read more than the buffer size.
+ * And don't try to read past the end of the file.
+ * Finally, if we're not at a page boundary, don't read past
+ * the next page.
+ * If this means reading 0 then we were asked to read past
+ * the end of file. */
+ amount = min(amount_left, FSG_BUFLEN);
+ partial_page = file_offset & (PAGE_CACHE_SIZE - 1);
+ if (partial_page > 0)
+ amount = min(amount, (unsigned int) PAGE_CACHE_SIZE -
+ partial_page);
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ /* If we were asked to read past the end of file,
+ * end with an empty buffer. */
+ if (amount == 0) {
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->info_valid = 1;
+ bh->inreq->length = 0;
+ bh->state = BUF_STATE_FULL;
+ break;
+ }
+
+ /* Perform the read */
+ rc = ums->read_sector(ums,
+ file_offset / SECTOR_SIZE,
+ amount / SECTOR_SIZE,
+ (char __user *)bh->buf);
+ if (!rc)
+ return -EIO;
+
+ nread = rc * SECTOR_SIZE;
+
+ VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+ (unsigned long long) file_offset,
+ (int) nread);
+
+ if (nread < 0) {
+ LDBG(curlun, "error in file read: %d\n",
+ (int) nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun, "partial file read: %d/%u\n",
+ (int) nread, amount);
+ nread -= (nread & 511); /* Round down to a block */
+ }
+ file_offset += nread;
+ amount_left -= nread;
+ common->residue -= nread;
+ bh->inreq->length = nread;
+ bh->state = BUF_STATE_FULL;
+
+ /* If an error occurred, report it and its position */
+ if (nread < amount) {
+ curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ if (amount_left == 0)
+ break; /* No more left to read */
+
+ /* Send this buffer and go read some more */
+ bh->inreq->zero = 0;
+ START_TRANSFER_OR(common, bulk_in, bh->inreq,
+ &bh->inreq_busy, &bh->state)
+ /* Don't know what to do if
+ * common->fsg is NULL */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ }
+
+ return -EIO; /* No default reply */
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int do_write(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ u32 lba;
+ struct fsg_buffhd *bh;
+ int get_some_more;
+ u32 amount_left_to_req, amount_left_to_write;
+ loff_t usb_offset, file_offset;
+ unsigned int amount;
+ unsigned int partial_page;
+ ssize_t nwritten;
+ int rc;
+
+ if (curlun->ro) {
+ curlun->sense_data = SS_WRITE_PROTECTED;
+ return -EINVAL;
+ }
+
+ /* Get the starting Logical Block Address and check that it's
+ * not too big */
+ if (common->cmnd[0] == SC_WRITE_6)
+ lba = get_unaligned_be24(&common->cmnd[1]);
+ else {
+ lba = get_unaligned_be32(&common->cmnd[2]);
+
+ /* We allow DPO (Disable Page Out = don't save data in the
+ * cache) and FUA (Force Unit Access = write directly to the
+ * medium). We don't implement DPO; we implement FUA by
+ * performing synchronous output. */
+ if (common->cmnd[1] & ~0x18) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ }
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ /* Carry out the file writes */
+ get_some_more = 1;
+ file_offset = usb_offset = ((loff_t) lba) << 9;
+ amount_left_to_req = common->data_size_from_cmnd;
+ amount_left_to_write = common->data_size_from_cmnd;
+
+ while (amount_left_to_write > 0) {
+
+ /* Queue a request for more data from the host */
+ bh = common->next_buffhd_to_fill;
+ if (bh->state == BUF_STATE_EMPTY && get_some_more) {
+
+ /* Figure out how much we want to get:
+ * Try to get the remaining amount.
+ * But don't get more than the buffer size.
+ * And don't try to go past the end of the file.
+ * If we're not at a page boundary,
+ * don't go past the next page.
+ * If this means getting 0, then we were asked
+ * to write past the end of file.
+ * Finally, round down to a block boundary. */
+ amount = min(amount_left_to_req, FSG_BUFLEN);
+ partial_page = usb_offset & (PAGE_CACHE_SIZE - 1);
+ if (partial_page > 0)
+ amount = min(amount,
+ (unsigned int) PAGE_CACHE_SIZE - partial_page);
+
+ if (amount == 0) {
+ get_some_more = 0;
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->info_valid = 1;
+ continue;
+ }
+ amount -= (amount & 511);
+ if (amount == 0) {
+
+ /* Why were we were asked to transfer a
+ * partial block? */
+ get_some_more = 0;
+ continue;
+ }
+
+ /* Get the next buffer */
+ usb_offset += amount;
+ common->usb_amount_left -= amount;
+ amount_left_to_req -= amount;
+ if (amount_left_to_req == 0)
+ get_some_more = 0;
+
+ /* amount is always divisible by 512, hence by
+ * the bulk-out maxpacket size */
+ bh->outreq->length = amount;
+ bh->bulk_out_intended_length = amount;
+ bh->outreq->short_not_ok = 1;
+ START_TRANSFER_OR(common, bulk_out, bh->outreq,
+ &bh->outreq_busy, &bh->state)
+ /* Don't know what to do if
+ * common->fsg is NULL */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ continue;
+ }
+
+ /* Write the received data to the backing file */
+ bh = common->next_buffhd_to_drain;
+ if (bh->state == BUF_STATE_EMPTY && !get_some_more)
+ break; /* We stopped early */
+ if (bh->state == BUF_STATE_FULL) {
+ common->next_buffhd_to_drain = bh->next;
+ bh->state = BUF_STATE_EMPTY;
+
+ /* Did something go wrong with the transfer? */
+ if (bh->outreq->status != 0) {
+ curlun->sense_data = SS_COMMUNICATION_FAILURE;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ amount = bh->outreq->actual;
+
+ /* Perform the write */
+ rc = ums->write_sector(ums,
+ file_offset / SECTOR_SIZE,
+ amount / SECTOR_SIZE,
+ (char __user *)bh->buf);
+ if (!rc)
+ return -EIO;
+ nwritten = rc * SECTOR_SIZE;
+
+ VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
+ (unsigned long long) file_offset,
+ (int) nwritten);
+
+ if (nwritten < 0) {
+ LDBG(curlun, "error in file write: %d\n",
+ (int) nwritten);
+ nwritten = 0;
+ } else if (nwritten < amount) {
+ LDBG(curlun, "partial file write: %d/%u\n",
+ (int) nwritten, amount);
+ nwritten -= (nwritten & 511);
+ /* Round down to a block */
+ }
+ file_offset += nwritten;
+ amount_left_to_write -= nwritten;
+ common->residue -= nwritten;
+
+ /* If an error occurred, report it and its position */
+ if (nwritten < amount) {
+ printf("nwritten:%d amount:%d\n", nwritten,
+ amount);
+ curlun->sense_data = SS_WRITE_ERROR;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ /* Did the host decide to stop early? */
+ if (bh->outreq->actual != bh->outreq->length) {
+ common->short_packet_received = 1;
+ break;
+ }
+ continue;
+ }
+
+ /* Wait for something to happen */
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ return -EIO; /* No default reply */
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int do_synchronize_cache(struct fsg_common *common)
+{
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int do_verify(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ u32 lba;
+ u32 verification_length;
+ struct fsg_buffhd *bh = common->next_buffhd_to_fill;
+ loff_t file_offset;
+ u32 amount_left;
+ unsigned int amount;
+ ssize_t nread;
+ int rc;
+
+ /* Get the starting Logical Block Address and check that it's
+ * not too big */
+ lba = get_unaligned_be32(&common->cmnd[2]);
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ /* We allow DPO (Disable Page Out = don't save data in the
+ * cache) but we don't implement it. */
+ if (common->cmnd[1] & ~0x10) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ verification_length = get_unaligned_be16(&common->cmnd[7]);
+ if (unlikely(verification_length == 0))
+ return -EIO; /* No default reply */
+
+ /* Prepare to carry out the file verify */
+ amount_left = verification_length << 9;
+ file_offset = ((loff_t) lba) << 9;
+
+ /* Write out all the dirty buffers before invalidating them */
+
+ /* Just try to read the requested blocks */
+ while (amount_left > 0) {
+
+ /* Figure out how much we need to read:
+ * Try to read the remaining amount, but not more than
+ * the buffer size.
+ * And don't try to read past the end of the file.
+ * If this means reading 0 then we were asked to read
+ * past the end of file. */
+ amount = min(amount_left, FSG_BUFLEN);
+ if (amount == 0) {
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ /* Perform the read */
+ rc = ums->read_sector(ums,
+ file_offset / SECTOR_SIZE,
+ amount / SECTOR_SIZE,
+ (char __user *)bh->buf);
+ if (!rc)
+ return -EIO;
+ nread = rc * SECTOR_SIZE;
+
+ VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+ (unsigned long long) file_offset,
+ (int) nread);
+ if (nread < 0) {
+ LDBG(curlun, "error in file verify: %d\n",
+ (int) nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun, "partial file verify: %d/%u\n",
+ (int) nread, amount);
+ nread -= (nread & 511); /* Round down to a sector */
+ }
+ if (nread == 0) {
+ curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+ curlun->info_valid = 1;
+ break;
+ }
+ file_offset += nread;
+ amount_left -= nread;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ static const char vendor_id[] = "Linux ";
+ u8 *buf = (u8 *) bh->buf;
+
+ if (!curlun) { /* Unsupported LUNs are okay */
+ common->bad_lun_okay = 1;
+ memset(buf, 0, 36);
+ buf[0] = 0x7f; /* Unsupported, no device-type */
+ buf[4] = 31; /* Additional length */
+ return 36;
+ }
+
+ memset(buf, 0, 8);
+ buf[0] = TYPE_DISK;
+ buf[2] = 2; /* ANSI SCSI level 2 */
+ buf[3] = 2; /* SCSI-2 INQUIRY data format */
+ buf[4] = 31; /* Additional length */
+ /* No special options */
+ sprintf((char *) (buf + 8), "%-8s%-16s%04x", (char*) vendor_id ,
+ ums->name, (u16) 0xffff);
+
+ return 36;
+}
+
+
+static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ u8 *buf = (u8 *) bh->buf;
+ u32 sd, sdinfo;
+ int valid;
+
+ /*
+ * From the SCSI-2 spec., section 7.9 (Unit attention condition):
+ *
+ * If a REQUEST SENSE command is received from an initiator
+ * with a pending unit attention condition (before the target
+ * generates the contingent allegiance condition), then the
+ * target shall either:
+ * a) report any pending sense data and preserve the unit
+ * attention condition on the logical unit, or,
+ * b) report the unit attention condition, may discard any
+ * pending sense data, and clear the unit attention
+ * condition on the logical unit for that initiator.
+ *
+ * FSG normally uses option a); enable this code to use option b).
+ */
+#if 0
+ if (curlun && curlun->unit_attention_data != SS_NO_SENSE) {
+ curlun->sense_data = curlun->unit_attention_data;
+ curlun->unit_attention_data = SS_NO_SENSE;
+ }
+#endif
+
+ if (!curlun) { /* Unsupported LUNs are okay */
+ common->bad_lun_okay = 1;
+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ sdinfo = 0;
+ valid = 0;
+ } else {
+ sd = curlun->sense_data;
+ valid = curlun->info_valid << 7;
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->info_valid = 0;
+ }
+
+ memset(buf, 0, 18);
+ buf[0] = valid | 0x70; /* Valid, current error */
+ buf[2] = SK(sd);
+ put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */
+ buf[7] = 18 - 8; /* Additional sense length */
+ buf[12] = ASC(sd);
+ buf[13] = ASCQ(sd);
+ return 18;
+}
+
+static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ u32 lba = get_unaligned_be32(&common->cmnd[2]);
+ int pmi = common->cmnd[8];
+ u8 *buf = (u8 *) bh->buf;
+
+ /* Check the PMI and LBA fields */
+ if (pmi > 1 || (pmi == 0 && lba != 0)) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
+ /* Max logical block */
+ put_unaligned_be32(512, &buf[4]); /* Block length */
+ return 8;
+}
+
+static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ int msf = common->cmnd[1] & 0x02;
+ u32 lba = get_unaligned_be32(&common->cmnd[2]);
+ u8 *buf = (u8 *) bh->buf;
+
+ if (common->cmnd[1] & ~0x02) { /* Mask away MSF */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ memset(buf, 0, 8);
+ buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */
+ store_cdrom_address(&buf[4], msf, lba);
+ return 8;
+}
+
+
+static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ int msf = common->cmnd[1] & 0x02;
+ int start_track = common->cmnd[6];
+ u8 *buf = (u8 *) bh->buf;
+
+ if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
+ start_track > 1) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ memset(buf, 0, 20);
+ buf[1] = (20-2); /* TOC data length */
+ buf[2] = 1; /* First track number */
+ buf[3] = 1; /* Last track number */
+ buf[5] = 0x16; /* Data track, copying allowed */
+ buf[6] = 0x01; /* Only track is number 1 */
+ store_cdrom_address(&buf[8], msf, 0);
+
+ buf[13] = 0x16; /* Lead-out track is data */
+ buf[14] = 0xAA; /* Lead-out track number */
+ store_cdrom_address(&buf[16], msf, curlun->num_sectors);
+
+ return 20;
+}
+
+static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ int mscmnd = common->cmnd[0];
+ u8 *buf = (u8 *) bh->buf;
+ u8 *buf0 = buf;
+ int pc, page_code;
+ int changeable_values, all_pages;
+ int valid_page = 0;
+ int len, limit;
+
+ if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ pc = common->cmnd[2] >> 6;
+ page_code = common->cmnd[2] & 0x3f;
+ if (pc == 3) {
+ curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+ return -EINVAL;
+ }
+ changeable_values = (pc == 1);
+ all_pages = (page_code == 0x3f);
+
+ /* Write the mode parameter header. Fixed values are: default
+ * medium type, no cache control (DPOFUA), and no block descriptors.
+ * The only variable value is the WriteProtect bit. We will fill in
+ * the mode data length later. */
+ memset(buf, 0, 8);
+ if (mscmnd == SC_MODE_SENSE_6) {
+ buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */
+ buf += 4;
+ limit = 255;
+ } else { /* SC_MODE_SENSE_10 */
+ buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */
+ buf += 8;
+ limit = 65535; /* Should really be FSG_BUFLEN */
+ }
+
+ /* No block descriptors */
+
+ /* The mode pages, in numerical order. The only page we support
+ * is the Caching page. */
+ if (page_code == 0x08 || all_pages) {
+ valid_page = 1;
+ buf[0] = 0x08; /* Page code */
+ buf[1] = 10; /* Page length */
+ memset(buf+2, 0, 10); /* None of the fields are changeable */
+
+ if (!changeable_values) {
+ buf[2] = 0x04; /* Write cache enable, */
+ /* Read cache not disabled */
+ /* No cache retention priorities */
+ put_unaligned_be16(0xffff, &buf[4]);
+ /* Don't disable prefetch */
+ /* Minimum prefetch = 0 */
+ put_unaligned_be16(0xffff, &buf[8]);
+ /* Maximum prefetch */
+ put_unaligned_be16(0xffff, &buf[10]);
+ /* Maximum prefetch ceiling */
+ }
+ buf += 12;
+ }
+
+ /* Check that a valid page was requested and the mode data length
+ * isn't too long. */
+ len = buf - buf0;
+ if (!valid_page || len > limit) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ /* Store the mode data length */
+ if (mscmnd == SC_MODE_SENSE_6)
+ buf0[0] = len - 1;
+ else
+ put_unaligned_be16(len - 2, buf0);
+ return len;
+}
+
+
+static int do_start_stop(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+
+ if (!curlun) {
+ return -EINVAL;
+ } else if (!curlun->removable) {
+ curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int do_prevent_allow(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ int prevent;
+
+ if (!curlun->removable) {
+ curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+ }
+
+ prevent = common->cmnd[4] & 0x01;
+ if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ if (curlun->prevent_medium_removal && !prevent)
+ fsg_lun_fsync_sub(curlun);
+ curlun->prevent_medium_removal = prevent;
+ return 0;
+}
+
+
+static int do_read_format_capacities(struct fsg_common *common,
+ struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ u8 *buf = (u8 *) bh->buf;
+
+ buf[0] = buf[1] = buf[2] = 0;
+ buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */
+ buf += 4;
+
+ put_unaligned_be32(curlun->num_sectors, &buf[0]);
+ /* Number of blocks */
+ put_unaligned_be32(512, &buf[4]); /* Block length */
+ buf[4] = 0x02; /* Current capacity */
+ return 12;
+}
+
+
+static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+
+ /* We don't support MODE SELECT */
+ if (curlun)
+ curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+ int rc;
+
+ rc = fsg_set_halt(fsg, fsg->bulk_in);
+ if (rc == -EAGAIN)
+ VDBG(fsg, "delayed bulk-in endpoint halt\n");
+ while (rc != 0) {
+ if (rc != -EAGAIN) {
+ WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
+ rc = 0;
+ break;
+ }
+
+ rc = usb_ep_set_halt(fsg->bulk_in);
+ }
+ return rc;
+}
+
+static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+ int rc;
+
+ DBG(fsg, "bulk-in set wedge\n");
+ rc = 0; /* usb_ep_set_wedge(fsg->bulk_in); */
+ if (rc == -EAGAIN)
+ VDBG(fsg, "delayed bulk-in endpoint wedge\n");
+ while (rc != 0) {
+ if (rc != -EAGAIN) {
+ WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
+ rc = 0;
+ break;
+ }
+ }
+ return rc;
+}
+
+static int pad_with_zeros(struct fsg_dev *fsg)
+{
+ struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill;
+ u32 nkeep = bh->inreq->length;
+ u32 nsend;
+ int rc;
+
+ bh->state = BUF_STATE_EMPTY; /* For the first iteration */
+ fsg->common->usb_amount_left = nkeep + fsg->common->residue;
+ while (fsg->common->usb_amount_left > 0) {
+
+ /* Wait for the next buffer to be free */
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(fsg->common);
+ if (rc)
+ return rc;
+ }
+
+ nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN);
+ memset(bh->buf + nkeep, 0, nsend - nkeep);
+ bh->inreq->length = nsend;
+ bh->inreq->zero = 0;
+ start_transfer(fsg, fsg->bulk_in, bh->inreq,
+ &bh->inreq_busy, &bh->state);
+ bh = fsg->common->next_buffhd_to_fill = bh->next;
+ fsg->common->usb_amount_left -= nsend;
+ nkeep = 0;
+ }
+ return 0;
+}
+
+static int throw_away_data(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ u32 amount;
+ int rc;
+
+ for (bh = common->next_buffhd_to_drain;
+ bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0;
+ bh = common->next_buffhd_to_drain) {
+
+ /* Throw away the data in a filled buffer */
+ if (bh->state == BUF_STATE_FULL) {
+ bh->state = BUF_STATE_EMPTY;
+ common->next_buffhd_to_drain = bh->next;
+
+ /* A short packet or an error ends everything */
+ if (bh->outreq->actual != bh->outreq->length ||
+ bh->outreq->status != 0) {
+ raise_exception(common,
+ FSG_STATE_ABORT_BULK_OUT);
+ return -EINTR;
+ }
+ continue;
+ }
+
+ /* Try to submit another request if we need one */
+ bh = common->next_buffhd_to_fill;
+ if (bh->state == BUF_STATE_EMPTY
+ && common->usb_amount_left > 0) {
+ amount = min(common->usb_amount_left, FSG_BUFLEN);
+
+ /* amount is always divisible by 512, hence by
+ * the bulk-out maxpacket size */
+ bh->outreq->length = amount;
+ bh->bulk_out_intended_length = amount;
+ bh->outreq->short_not_ok = 1;
+ START_TRANSFER_OR(common, bulk_out, bh->outreq,
+ &bh->outreq_busy, &bh->state)
+ /* Don't know what to do if
+ * common->fsg is NULL */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ common->usb_amount_left -= amount;
+ continue;
+ }
+
+ /* Otherwise wait for something to happen */
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+
+static int finish_reply(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh = common->next_buffhd_to_fill;
+ int rc = 0;
+
+ switch (common->data_dir) {
+ case DATA_DIR_NONE:
+ break; /* Nothing to send */
+
+ /* If we don't know whether the host wants to read or write,
+ * this must be CB or CBI with an unknown command. We mustn't
+ * try to send or receive any data. So stall both bulk pipes
+ * if we can and wait for a reset. */
+ case DATA_DIR_UNKNOWN:
+ if (!common->can_stall) {
+ /* Nothing */
+ } else if (fsg_is_set(common)) {
+ fsg_set_halt(common->fsg, common->fsg->bulk_out);
+ rc = halt_bulk_in_endpoint(common->fsg);
+ } else {
+ /* Don't know what to do if common->fsg is NULL */
+ rc = -EIO;
+ }
+ break;
+
+ /* All but the last buffer of data must have already been sent */
+ case DATA_DIR_TO_HOST:
+ if (common->data_size == 0) {
+ /* Nothing to send */
+
+ /* If there's no residue, simply send the last buffer */
+ } else if (common->residue == 0) {
+ bh->inreq->zero = 0;
+ START_TRANSFER_OR(common, bulk_in, bh->inreq,
+ &bh->inreq_busy, &bh->state)
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+
+ /* For Bulk-only, if we're allowed to stall then send the
+ * short packet and halt the bulk-in endpoint. If we can't
+ * stall, pad out the remaining data with 0's. */
+ } else if (common->can_stall) {
+ bh->inreq->zero = 1;
+ START_TRANSFER_OR(common, bulk_in, bh->inreq,
+ &bh->inreq_busy, &bh->state)
+ /* Don't know what to do if
+ * common->fsg is NULL */
+ rc = -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ if (common->fsg)
+ rc = halt_bulk_in_endpoint(common->fsg);
+ } else if (fsg_is_set(common)) {
+ rc = pad_with_zeros(common->fsg);
+ } else {
+ /* Don't know what to do if common->fsg is NULL */
+ rc = -EIO;
+ }
+ break;
+
+ /* We have processed all we want from the data the host has sent.
+ * There may still be outstanding bulk-out requests. */
+ case DATA_DIR_FROM_HOST:
+ if (common->residue == 0) {
+ /* Nothing to receive */
+
+ /* Did the host stop sending unexpectedly early? */
+ } else if (common->short_packet_received) {
+ raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+ rc = -EINTR;
+
+ /* We haven't processed all the incoming data. Even though
+ * we may be allowed to stall, doing so would cause a race.
+ * The controller may already have ACK'ed all the remaining
+ * bulk-out packets, in which case the host wouldn't see a
+ * STALL. Not realizing the endpoint was halted, it wouldn't
+ * clear the halt -- leading to problems later on. */
+#if 0
+ } else if (common->can_stall) {
+ if (fsg_is_set(common))
+ fsg_set_halt(common->fsg,
+ common->fsg->bulk_out);
+ raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+ rc = -EINTR;
+#endif
+
+ /* We can't stall. Read in the excess data and throw it
+ * all away. */
+ } else {
+ rc = throw_away_data(common);
+ }
+ break;
+ }
+ return rc;
+}
+
+
+static int send_status(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = &common->luns[common->lun];
+ struct fsg_buffhd *bh;
+ struct bulk_cs_wrap *csw;
+ int rc;
+ u8 status = USB_STATUS_PASS;
+ u32 sd, sdinfo = 0;
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ if (curlun)
+ sd = curlun->sense_data;
+ else if (common->bad_lun_okay)
+ sd = SS_NO_SENSE;
+ else
+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+
+ if (common->phase_error) {
+ DBG(common, "sending phase-error status\n");
+ status = USB_STATUS_PHASE_ERROR;
+ sd = SS_INVALID_COMMAND;
+ } else if (sd != SS_NO_SENSE) {
+ DBG(common, "sending command-failure status\n");
+ status = USB_STATUS_FAIL;
+ VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
+ " info x%x\n",
+ SK(sd), ASC(sd), ASCQ(sd), sdinfo);
+ }
+
+ /* Store and send the Bulk-only CSW */
+ csw = (void *)bh->buf;
+
+ csw->Signature = cpu_to_le32(USB_BULK_CS_SIG);
+ csw->Tag = common->tag;
+ csw->Residue = cpu_to_le32(common->residue);
+ csw->Status = status;
+
+ bh->inreq->length = USB_BULK_CS_WRAP_LEN;
+ bh->inreq->zero = 0;
+ START_TRANSFER_OR(common, bulk_in, bh->inreq,
+ &bh->inreq_busy, &bh->state)
+ /* Don't know what to do if common->fsg is NULL */
+ return -EIO;
+
+ common->next_buffhd_to_fill = bh->next;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Check whether the command is properly formed and whether its data size
+ * and direction agree with the values we already have. */
+static int check_command(struct fsg_common *common, int cmnd_size,
+ enum data_direction data_dir, unsigned int mask,
+ int needs_medium, const char *name)
+{
+ int i;
+ int lun = common->cmnd[1] >> 5;
+ static const char dirletter[4] = {'u', 'o', 'i', 'n'};
+ char hdlen[20];
+ struct fsg_lun *curlun;
+
+ hdlen[0] = 0;
+ if (common->data_dir != DATA_DIR_UNKNOWN)
+ sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir],
+ common->data_size);
+ VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n",
+ name, cmnd_size, dirletter[(int) data_dir],
+ common->data_size_from_cmnd, common->cmnd_size, hdlen);
+
+ /* We can't reply at all until we know the correct data direction
+ * and size. */
+ if (common->data_size_from_cmnd == 0)
+ data_dir = DATA_DIR_NONE;
+ if (common->data_size < common->data_size_from_cmnd) {
+ /* Host data size < Device data size is a phase error.
+ * Carry out the command, but only transfer as much as
+ * we are allowed. */
+ common->data_size_from_cmnd = common->data_size;
+ common->phase_error = 1;
+ }
+ common->residue = common->data_size;
+ common->usb_amount_left = common->data_size;
+
+ /* Conflicting data directions is a phase error */
+ if (common->data_dir != data_dir
+ && common->data_size_from_cmnd > 0) {
+ common->phase_error = 1;
+ return -EINVAL;
+ }
+
+ /* Verify the length of the command itself */
+ if (cmnd_size != common->cmnd_size) {
+
+ /* Special case workaround: There are plenty of buggy SCSI
+ * implementations. Many have issues with cbw->Length
+ * field passing a wrong command size. For those cases we
+ * always try to work around the problem by using the length
+ * sent by the host side provided it is at least as large
+ * as the correct command length.
+ * Examples of such cases would be MS-Windows, which issues
+ * REQUEST SENSE with cbw->Length == 12 where it should
+ * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and
+ * REQUEST SENSE with cbw->Length == 10 where it should
+ * be 6 as well.
+ */
+ if (cmnd_size <= common->cmnd_size) {
+ DBG(common, "%s is buggy! Expected length %d "
+ "but we got %d\n", name,
+ cmnd_size, common->cmnd_size);
+ cmnd_size = common->cmnd_size;
+ } else {
+ common->phase_error = 1;
+ return -EINVAL;
+ }
+ }
+
+ /* Check that the LUN values are consistent */
+ if (common->lun != lun)
+ DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n",
+ common->lun, lun);
+
+ /* Check the LUN */
+ if (common->lun >= 0 && common->lun < common->nluns) {
+ curlun = &common->luns[common->lun];
+ if (common->cmnd[0] != SC_REQUEST_SENSE) {
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->info_valid = 0;
+ }
+ } else {
+ curlun = NULL;
+ common->bad_lun_okay = 0;
+
+ /* INQUIRY and REQUEST SENSE commands are explicitly allowed
+ * to use unsupported LUNs; all others may not. */
+ if (common->cmnd[0] != SC_INQUIRY &&
+ common->cmnd[0] != SC_REQUEST_SENSE) {
+ DBG(common, "unsupported LUN %d\n", common->lun);
+ return -EINVAL;
+ }
+ }
+#if 0
+ /* If a unit attention condition exists, only INQUIRY and
+ * REQUEST SENSE commands are allowed; anything else must fail. */
+ if (curlun && curlun->unit_attention_data != SS_NO_SENSE &&
+ common->cmnd[0] != SC_INQUIRY &&
+ common->cmnd[0] != SC_REQUEST_SENSE) {
+ curlun->sense_data = curlun->unit_attention_data;
+ curlun->unit_attention_data = SS_NO_SENSE;
+ return -EINVAL;
+ }
+#endif
+ /* Check that only command bytes listed in the mask are non-zero */
+ common->cmnd[1] &= 0x1f; /* Mask away the LUN */
+ for (i = 1; i < cmnd_size; ++i) {
+ if (common->cmnd[i] && !(mask & (1 << i))) {
+ if (curlun)
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+
+static int do_scsi_command(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ int rc;
+ int reply = -EINVAL;
+ int i;
+ static char unknown[16];
+ struct fsg_lun *curlun = &common->luns[common->lun];
+
+ dump_cdb(common);
+
+ /* Wait for the next buffer to become available for data or status */
+ bh = common->next_buffhd_to_fill;
+ common->next_buffhd_to_drain = bh;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+ common->phase_error = 0;
+ common->short_packet_received = 0;
+
+ down_read(&common->filesem); /* We're using the backing file */
+ switch (common->cmnd[0]) {
+
+ case SC_INQUIRY:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<4), 0,
+ "INQUIRY");
+ if (reply == 0)
+ reply = do_inquiry(common, bh);
+ break;
+
+ case SC_MODE_SELECT_6:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_FROM_HOST,
+ (1<<1) | (1<<4), 0,
+ "MODE SELECT(6)");
+ if (reply == 0)
+ reply = do_mode_select(common, bh);
+ break;
+
+ case SC_MODE_SELECT_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_FROM_HOST,
+ (1<<1) | (3<<7), 0,
+ "MODE SELECT(10)");
+ if (reply == 0)
+ reply = do_mode_select(common, bh);
+ break;
+
+ case SC_MODE_SENSE_6:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<1) | (1<<2) | (1<<4), 0,
+ "MODE SENSE(6)");
+ if (reply == 0)
+ reply = do_mode_sense(common, bh);
+ break;
+
+ case SC_MODE_SENSE_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (1<<1) | (1<<2) | (3<<7), 0,
+ "MODE SENSE(10)");
+ if (reply == 0)
+ reply = do_mode_sense(common, bh);
+ break;
+
+ case SC_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ (1<<4), 0,
+ "PREVENT-ALLOW MEDIUM REMOVAL");
+ if (reply == 0)
+ reply = do_prevent_allow(common);
+ break;
+
+ case SC_READ_6:
+ i = common->cmnd[4];
+ common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (7<<1) | (1<<4), 1,
+ "READ(6)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case SC_READ_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]) << 9;
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "READ(10)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case SC_READ_12:
+ common->data_size_from_cmnd =
+ get_unaligned_be32(&common->cmnd[6]) << 9;
+ reply = check_command(common, 12, DATA_DIR_TO_HOST,
+ (1<<1) | (0xf<<2) | (0xf<<6), 1,
+ "READ(12)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case SC_READ_CAPACITY:
+ common->data_size_from_cmnd = 8;
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (0xf<<2) | (1<<8), 1,
+ "READ CAPACITY");
+ if (reply == 0)
+ reply = do_read_capacity(common, bh);
+ break;
+
+ case SC_READ_HEADER:
+ if (!common->luns[common->lun].cdrom)
+ goto unknown_cmnd;
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (3<<7) | (0x1f<<1), 1,
+ "READ HEADER");
+ if (reply == 0)
+ reply = do_read_header(common, bh);
+ break;
+
+ case SC_READ_TOC:
+ if (!common->luns[common->lun].cdrom)
+ goto unknown_cmnd;
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (7<<6) | (1<<1), 1,
+ "READ TOC");
+ if (reply == 0)
+ reply = do_read_toc(common, bh);
+ break;
+
+ case SC_READ_FORMAT_CAPACITIES:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (3<<7), 1,
+ "READ FORMAT CAPACITIES");
+ if (reply == 0)
+ reply = do_read_format_capacities(common, bh);
+ break;
+
+ case SC_REQUEST_SENSE:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<4), 0,
+ "REQUEST SENSE");
+ if (reply == 0)
+ reply = do_request_sense(common, bh);
+ break;
+
+ case SC_START_STOP_UNIT:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ (1<<1) | (1<<4), 0,
+ "START-STOP UNIT");
+ if (reply == 0)
+ reply = do_start_stop(common);
+ break;
+
+ case SC_SYNCHRONIZE_CACHE:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 10, DATA_DIR_NONE,
+ (0xf<<2) | (3<<7), 1,
+ "SYNCHRONIZE CACHE");
+ if (reply == 0)
+ reply = do_synchronize_cache(common);
+ break;
+
+ case SC_TEST_UNIT_READY:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ 0, 1,
+ "TEST UNIT READY");
+ break;
+
+ /* Although optional, this command is used by MS-Windows. We
+ * support a minimal version: BytChk must be 0. */
+ case SC_VERIFY:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 10, DATA_DIR_NONE,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "VERIFY");
+ if (reply == 0)
+ reply = do_verify(common);
+ break;
+
+ case SC_WRITE_6:
+ i = common->cmnd[4];
+ common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
+ reply = check_command(common, 6, DATA_DIR_FROM_HOST,
+ (7<<1) | (1<<4), 1,
+ "WRITE(6)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ case SC_WRITE_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]) << 9;
+ reply = check_command(common, 10, DATA_DIR_FROM_HOST,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "WRITE(10)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ case SC_WRITE_12:
+ common->data_size_from_cmnd =
+ get_unaligned_be32(&common->cmnd[6]) << 9;
+ reply = check_command(common, 12, DATA_DIR_FROM_HOST,
+ (1<<1) | (0xf<<2) | (0xf<<6), 1,
+ "WRITE(12)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ /* Some mandatory commands that we recognize but don't implement.
+ * They don't mean much in this setting. It's left as an exercise
+ * for anyone interested to implement RESERVE and RELEASE in terms
+ * of Posix locks. */
+ case SC_FORMAT_UNIT:
+ case SC_RELEASE:
+ case SC_RESERVE:
+ case SC_SEND_DIAGNOSTIC:
+ /* Fall through */
+
+ default:
+unknown_cmnd:
+ common->data_size_from_cmnd = 0;
+ sprintf(unknown, "Unknown x%02x", common->cmnd[0]);
+ reply = check_command(common, common->cmnd_size,
+ DATA_DIR_UNKNOWN, 0xff, 0, unknown);
+ if (reply == 0) {
+ curlun->sense_data = SS_INVALID_COMMAND;
+ reply = -EINVAL;
+ }
+ break;
+ }
+ up_read(&common->filesem);
+
+ if (reply == -EINTR)
+ return -EINTR;
+
+ /* Set up the single reply buffer for finish_reply() */
+ if (reply == -EINVAL)
+ reply = 0; /* Error reply length */
+ if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) {
+ reply = min((u32) reply, common->data_size_from_cmnd);
+ bh->inreq->length = reply;
+ bh->state = BUF_STATE_FULL;
+ common->residue -= reply;
+ } /* Otherwise it's already set */
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+ struct usb_request *req = bh->outreq;
+ struct fsg_bulk_cb_wrap *cbw = req->buf;
+ struct fsg_common *common = fsg->common;
+
+ /* Was this a real packet? Should it be ignored? */
+ if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
+ return -EINVAL;
+
+ /* Is the CBW valid? */
+ if (req->actual != USB_BULK_CB_WRAP_LEN ||
+ cbw->Signature != cpu_to_le32(
+ USB_BULK_CB_SIG)) {
+ DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
+ req->actual,
+ le32_to_cpu(cbw->Signature));
+
+ /* The Bulk-only spec says we MUST stall the IN endpoint
+ * (6.6.1), so it's unavoidable. It also says we must
+ * retain this state until the next reset, but there's
+ * no way to tell the controller driver it should ignore
+ * Clear-Feature(HALT) requests.
+ *
+ * We aren't required to halt the OUT endpoint; instead
+ * we can simply accept and discard any data received
+ * until the next reset. */
+ wedge_bulk_in_endpoint(fsg);
+ set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+ return -EINVAL;
+ }
+
+ /* Is the CBW meaningful? */
+ if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG ||
+ cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
+ DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
+ "cmdlen %u\n",
+ cbw->Lun, cbw->Flags, cbw->Length);
+
+ /* We can do anything we want here, so let's stall the
+ * bulk pipes if we are allowed to. */
+ if (common->can_stall) {
+ fsg_set_halt(fsg, fsg->bulk_out);
+ halt_bulk_in_endpoint(fsg);
+ }
+ return -EINVAL;
+ }
+
+ /* Save the command for later */
+ common->cmnd_size = cbw->Length;
+ memcpy(common->cmnd, cbw->CDB, common->cmnd_size);
+ if (cbw->Flags & USB_BULK_IN_FLAG)
+ common->data_dir = DATA_DIR_TO_HOST;
+ else
+ common->data_dir = DATA_DIR_FROM_HOST;
+ common->data_size = le32_to_cpu(cbw->DataTransferLength);
+ if (common->data_size == 0)
+ common->data_dir = DATA_DIR_NONE;
+ common->lun = cbw->Lun;
+ common->tag = cbw->Tag;
+ return 0;
+}
+
+
+static int get_next_command(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ int rc = 0;
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ /* Queue a request to read a Bulk-only CBW */
+ set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN);
+ bh->outreq->short_not_ok = 1;
+ START_TRANSFER_OR(common, bulk_out, bh->outreq,
+ &bh->outreq_busy, &bh->state)
+ /* Don't know what to do if common->fsg is NULL */
+ return -EIO;
+
+ /* We will drain the buffer in software, which means we
+ * can reuse it for the next filling. No need to advance
+ * next_buffhd_to_fill. */
+
+ /* Wait for the CBW to arrive */
+ while (bh->state != BUF_STATE_FULL) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO;
+ bh->state = BUF_STATE_EMPTY;
+
+ return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *d)
+{
+ int rc;
+
+ ep->driver_data = common;
+ rc = usb_ep_enable(ep, d);
+ if (rc)
+ ERROR(common, "can't enable %s, result %d\n", ep->name, rc);
+ return rc;
+}
+
+static int alloc_request(struct fsg_common *common, struct usb_ep *ep,
+ struct usb_request **preq)
+{
+ *preq = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (*preq)
+ return 0;
+ ERROR(common, "can't allocate request for %s\n", ep->name);
+ return -ENOMEM;
+}
+
+/* Reset interface setting and re-init endpoint state (toggle etc). */
+static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg)
+{
+ const struct usb_endpoint_descriptor *d;
+ struct fsg_dev *fsg;
+ int i, rc = 0;
+
+ if (common->running)
+ DBG(common, "reset interface\n");
+
+reset:
+ /* Deallocate the requests */
+ if (common->fsg) {
+ fsg = common->fsg;
+
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ struct fsg_buffhd *bh = &common->buffhds[i];
+
+ if (bh->inreq) {
+ usb_ep_free_request(fsg->bulk_in, bh->inreq);
+ bh->inreq = NULL;
+ }
+ if (bh->outreq) {
+ usb_ep_free_request(fsg->bulk_out, bh->outreq);
+ bh->outreq = NULL;
+ }
+ }
+
+ /* Disable the endpoints */
+ if (fsg->bulk_in_enabled) {
+ usb_ep_disable(fsg->bulk_in);
+ fsg->bulk_in_enabled = 0;
+ }
+ if (fsg->bulk_out_enabled) {
+ usb_ep_disable(fsg->bulk_out);
+ fsg->bulk_out_enabled = 0;
+ }
+
+ common->fsg = NULL;
+ /* wake_up(&common->fsg_wait); */
+ }
+
+ common->running = 0;
+ if (!new_fsg || rc)
+ return rc;
+
+ common->fsg = new_fsg;
+ fsg = common->fsg;
+
+ /* Enable the endpoints */
+ d = fsg_ep_desc(common->gadget,
+ &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc);
+ rc = enable_endpoint(common, fsg->bulk_in, d);
+ if (rc)
+ goto reset;
+ fsg->bulk_in_enabled = 1;
+
+ d = fsg_ep_desc(common->gadget,
+ &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc);
+ rc = enable_endpoint(common, fsg->bulk_out, d);
+ if (rc)
+ goto reset;
+ fsg->bulk_out_enabled = 1;
+ common->bulk_out_maxpacket =
+ le16_to_cpu(get_unaligned(&d->wMaxPacketSize));
+ clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+
+ /* Allocate the requests */
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ struct fsg_buffhd *bh = &common->buffhds[i];
+
+ rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
+ if (rc)
+ goto reset;
+ rc = alloc_request(common, fsg->bulk_out, &bh->outreq);
+ if (rc)
+ goto reset;
+ bh->inreq->buf = bh->outreq->buf = bh->buf;
+ bh->inreq->context = bh->outreq->context = bh;
+ bh->inreq->complete = bulk_in_complete;
+ bh->outreq->complete = bulk_out_complete;
+ }
+
+ common->running = 1;
+
+ return rc;
+}
+
+
+/****************************** ALT CONFIGS ******************************/
+
+
+static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ fsg->common->new_fsg = fsg;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+ return 0;
+}
+
+static void fsg_disable(struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ fsg->common->new_fsg = NULL;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void handle_exception(struct fsg_common *common)
+{
+ int i;
+ struct fsg_buffhd *bh;
+ enum fsg_state old_state;
+ struct fsg_lun *curlun;
+ unsigned int exception_req_tag;
+
+ /* Cancel all the pending transfers */
+ if (common->fsg) {
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ bh = &common->buffhds[i];
+ if (bh->inreq_busy)
+ usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
+ if (bh->outreq_busy)
+ usb_ep_dequeue(common->fsg->bulk_out,
+ bh->outreq);
+ }
+
+ /* Wait until everything is idle */
+ for (;;) {
+ int num_active = 0;
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ bh = &common->buffhds[i];
+ num_active += bh->inreq_busy + bh->outreq_busy;
+ }
+ if (num_active == 0)
+ break;
+ if (sleep_thread(common))
+ return;
+ }
+
+ /* Clear out the controller's fifos */
+ if (common->fsg->bulk_in_enabled)
+ usb_ep_fifo_flush(common->fsg->bulk_in);
+ if (common->fsg->bulk_out_enabled)
+ usb_ep_fifo_flush(common->fsg->bulk_out);
+ }
+
+ /* Reset the I/O buffer states and pointers, the SCSI
+ * state, and the exception. Then invoke the handler. */
+
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ bh = &common->buffhds[i];
+ bh->state = BUF_STATE_EMPTY;
+ }
+ common->next_buffhd_to_fill = &common->buffhds[0];
+ common->next_buffhd_to_drain = &common->buffhds[0];
+ exception_req_tag = common->exception_req_tag;
+ old_state = common->state;
+
+ if (old_state == FSG_STATE_ABORT_BULK_OUT)
+ common->state = FSG_STATE_STATUS_PHASE;
+ else {
+ for (i = 0; i < common->nluns; ++i) {
+ curlun = &common->luns[i];
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->info_valid = 0;
+ }
+ common->state = FSG_STATE_IDLE;
+ }
+
+ /* Carry out any extra actions required for the exception */
+ switch (old_state) {
+ case FSG_STATE_ABORT_BULK_OUT:
+ send_status(common);
+
+ if (common->state == FSG_STATE_STATUS_PHASE)
+ common->state = FSG_STATE_IDLE;
+ break;
+
+ case FSG_STATE_RESET:
+ /* In case we were forced against our will to halt a
+ * bulk endpoint, clear the halt now. (The SuperH UDC
+ * requires this.) */
+ if (!fsg_is_set(common))
+ break;
+ if (test_and_clear_bit(IGNORE_BULK_OUT,
+ &common->fsg->atomic_bitflags))
+ usb_ep_clear_halt(common->fsg->bulk_in);
+
+ if (common->ep0_req_tag == exception_req_tag)
+ ep0_queue(common); /* Complete the status stage */
+
+ break;
+
+ case FSG_STATE_CONFIG_CHANGE:
+ do_set_interface(common, common->new_fsg);
+ break;
+
+ case FSG_STATE_EXIT:
+ case FSG_STATE_TERMINATED:
+ do_set_interface(common, NULL); /* Free resources */
+ common->state = FSG_STATE_TERMINATED; /* Stop the thread */
+ break;
+
+ case FSG_STATE_INTERFACE_CHANGE:
+ case FSG_STATE_DISCONNECT:
+ case FSG_STATE_COMMAND_PHASE:
+ case FSG_STATE_DATA_PHASE:
+ case FSG_STATE_STATUS_PHASE:
+ case FSG_STATE_IDLE:
+ break;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+int fsg_main_thread(void *common_)
+{
+ int ret;
+ struct fsg_common *common = the_fsg_common;
+ /* The main loop */
+ do {
+ if (exception_in_progress(common)) {
+ handle_exception(common);
+ continue;
+ }
+
+ if (!common->running) {
+ ret = sleep_thread(common);
+ if (ret)
+ return ret;
+
+ continue;
+ }
+
+ ret = get_next_command(common);
+ if (ret)
+ return ret;
+
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_DATA_PHASE;
+
+ if (do_scsi_command(common) || finish_reply(common))
+ continue;
+
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_STATUS_PHASE;
+
+ if (send_status(common))
+ continue;
+
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_IDLE;
+ } while (0);
+
+ common->thread_task = NULL;
+
+ return 0;
+}
+
+static void fsg_common_release(struct kref *ref);
+
+static struct fsg_common *fsg_common_init(struct fsg_common *common,
+ struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct fsg_buffhd *bh;
+ struct fsg_lun *curlun;
+ int nluns, i, rc;
+
+ /* Find out how many LUNs there should be */
+ nluns = 1;
+ if (nluns < 1 || nluns > FSG_MAX_LUNS) {
+ printf("invalid number of LUNs: %u\n", nluns);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Allocate? */
+ if (!common) {
+ common = calloc(sizeof *common, 1);
+ if (!common)
+ return ERR_PTR(-ENOMEM);
+ common->free_storage_on_release = 1;
+ } else {
+ memset(common, 0, sizeof common);
+ common->free_storage_on_release = 0;
+ }
+
+ common->ops = NULL;
+ common->private_data = NULL;
+
+ common->gadget = gadget;
+ common->ep0 = gadget->ep0;
+ common->ep0req = cdev->req;
+
+ /* Maybe allocate device-global string IDs, and patch descriptors */
+ if (fsg_strings[FSG_STRING_INTERFACE].id == 0) {
+ rc = usb_string_id(cdev);
+ if (unlikely(rc < 0))
+ goto error_release;
+ fsg_strings[FSG_STRING_INTERFACE].id = rc;
+ fsg_intf_desc.iInterface = rc;
+ }
+
+ /* Create the LUNs, open their backing files, and register the
+ * LUN devices in sysfs. */
+ curlun = calloc(nluns, sizeof *curlun);
+ if (!curlun) {
+ rc = -ENOMEM;
+ goto error_release;
+ }
+ common->nluns = nluns;
+
+ for (i = 0; i < nluns; i++) {
+ common->luns[i].removable = 1;
+
+ rc = fsg_lun_open(&common->luns[i], "");
+ if (rc)
+ goto error_luns;
+ }
+ common->lun = 0;
+
+ /* Data buffers cyclic list */
+ bh = common->buffhds;
+
+ i = FSG_NUM_BUFFERS;
+ goto buffhds_first_it;
+ do {
+ bh->next = bh + 1;
+ ++bh;
+buffhds_first_it:
+ bh->inreq_busy = 0;
+ bh->outreq_busy = 0;
+ bh->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, FSG_BUFLEN);
+ if (unlikely(!bh->buf)) {
+ rc = -ENOMEM;
+ goto error_release;
+ }
+ } while (--i);
+ bh->next = common->buffhds;
+
+ snprintf(common->inquiry_string, sizeof common->inquiry_string,
+ "%-8s%-16s%04x",
+ "Linux ",
+ "File-Store Gadget",
+ 0xffff);
+
+ /* Some peripheral controllers are known not to be able to
+ * halt bulk endpoints correctly. If one of them is present,
+ * disable stalls.
+ */
+
+ /* Tell the thread to start working */
+ common->thread_task =
+ kthread_create(fsg_main_thread, common,
+ OR(cfg->thread_name, "file-storage"));
+ if (IS_ERR(common->thread_task)) {
+ rc = PTR_ERR(common->thread_task);
+ goto error_release;
+ }
+
+#undef OR
+ /* Information */
+ INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
+ INFO(common, "Number of LUNs=%d\n", common->nluns);
+
+ return common;
+
+error_luns:
+ common->nluns = i + 1;
+error_release:
+ common->state = FSG_STATE_TERMINATED; /* The thread is dead */
+ /* Call fsg_common_release() directly, ref might be not
+ * initialised */
+ fsg_common_release(&common->ref);
+ return ERR_PTR(rc);
+}
+
+static void fsg_common_release(struct kref *ref)
+{
+ struct fsg_common *common = container_of(ref, struct fsg_common, ref);
+
+ /* If the thread isn't already dead, tell it to exit now */
+ if (common->state != FSG_STATE_TERMINATED) {
+ raise_exception(common, FSG_STATE_EXIT);
+ wait_for_completion(&common->thread_notifier);
+ }
+
+ if (likely(common->luns)) {
+ struct fsg_lun *lun = common->luns;
+ unsigned i = common->nluns;
+
+ /* In error recovery common->nluns may be zero. */
+ for (; i; --i, ++lun)
+ fsg_lun_close(lun);
+
+ kfree(common->luns);
+ }
+
+ {
+ struct fsg_buffhd *bh = common->buffhds;
+ unsigned i = FSG_NUM_BUFFERS;
+ do {
+ kfree(bh->buf);
+ } while (++bh, --i);
+ }
+
+ if (common->free_storage_on_release)
+ kfree(common);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_copy_descriptors - copy a vector of USB descriptors
+ * @src: null-terminated vector to copy
+ * Context: initialization code, which may sleep
+ *
+ * This makes a copy of a vector of USB descriptors. Its primary use
+ * is to support usb_function objects which can have multiple copies,
+ * each needing different descriptors. Functions may have static
+ * tables of descriptors, which are used as templates and customized
+ * with identifiers (for interfaces, strings, endpoints, and more)
+ * as needed by a given function instance.
+ */
+struct usb_descriptor_header **
+usb_copy_descriptors(struct usb_descriptor_header **src)
+{
+ struct usb_descriptor_header **tmp;
+ unsigned bytes;
+ unsigned n_desc;
+ void *mem;
+ struct usb_descriptor_header **ret;
+
+ /* count descriptors and their sizes; then add vector size */
+ for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++)
+ bytes += (*tmp)->bLength;
+ bytes += (n_desc + 1) * sizeof(*tmp);
+
+ mem = memalign(CONFIG_SYS_CACHELINE_SIZE, bytes);
+ if (!mem)
+ return NULL;
+
+ /* fill in pointers starting at "tmp",
+ * to descriptors copied starting at "mem";
+ * and return "ret"
+ */
+ tmp = mem;
+ ret = mem;
+ mem += (n_desc + 1) * sizeof(*tmp);
+ while (*src) {
+ memcpy(mem, *src, (*src)->bLength);
+ *tmp = mem;
+ tmp++;
+ mem += (*src)->bLength;
+ src++;
+ }
+ *tmp = NULL;
+
+ return ret;
+}
+
+static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+
+ DBG(fsg, "unbind\n");
+ if (fsg->common->fsg == fsg) {
+ fsg->common->new_fsg = NULL;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+ }
+
+ free(fsg->function.descriptors);
+ free(fsg->function.hs_descriptors);
+ kfree(fsg);
+}
+
+static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct usb_gadget *gadget = c->cdev->gadget;
+ int i;
+ struct usb_ep *ep;
+ fsg->gadget = gadget;
+
+ /* New interface */
+ i = usb_interface_id(c, f);
+ if (i < 0)
+ return i;
+ fsg_intf_desc.bInterfaceNumber = i;
+ fsg->interface_number = i;
+
+ /* Find all the endpoints we will use */
+ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = fsg->common; /* claim the endpoint */
+ fsg->bulk_in = ep;
+
+ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = fsg->common; /* claim the endpoint */
+ fsg->bulk_out = ep;
+
+ /* Copy descriptors */
+ f->descriptors = usb_copy_descriptors(fsg_fs_function);
+ if (unlikely(!f->descriptors))
+ return -ENOMEM;
+
+ if (gadget_is_dualspeed(gadget)) {
+ /* Assume endpoint addresses are the same for both speeds */
+ fsg_hs_bulk_in_desc.bEndpointAddress =
+ fsg_fs_bulk_in_desc.bEndpointAddress;
+ fsg_hs_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+ f->hs_descriptors = usb_copy_descriptors(fsg_hs_function);
+ if (unlikely(!f->hs_descriptors)) {
+ free(f->descriptors);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+
+autoconf_fail:
+ ERROR(fsg, "unable to autoconfigure all endpoints\n");
+ return -ENOTSUPP;
+}
+
+
+/****************************** ADD FUNCTION ******************************/
+
+static struct usb_gadget_strings *fsg_strings_array[] = {
+ &fsg_stringtab,
+ NULL,
+};
+
+static int fsg_bind_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct fsg_common *common)
+{
+ struct fsg_dev *fsg;
+ int rc;
+
+ fsg = calloc(1, sizeof *fsg);
+ if (!fsg)
+ return -ENOMEM;
+ fsg->function.name = FSG_DRIVER_DESC;
+ fsg->function.strings = fsg_strings_array;
+ fsg->function.bind = fsg_bind;
+ fsg->function.unbind = fsg_unbind;
+ fsg->function.setup = fsg_setup;
+ fsg->function.set_alt = fsg_set_alt;
+ fsg->function.disable = fsg_disable;
+
+ fsg->common = common;
+ common->fsg = fsg;
+ /* Our caller holds a reference to common structure so we
+ * don't have to be worry about it being freed until we return
+ * from this function. So instead of incrementing counter now
+ * and decrement in error recovery we increment it only when
+ * call to usb_add_function() was successful. */
+
+ rc = usb_add_function(c, &fsg->function);
+
+ if (rc)
+ kfree(fsg);
+
+ return rc;
+}
+
+int fsg_add(struct usb_configuration *c)
+{
+ struct fsg_common *fsg_common;
+
+ fsg_common = fsg_common_init(NULL, c->cdev);
+
+ fsg_common->vendor_name = 0;
+ fsg_common->product_name = 0;
+ fsg_common->release = 0xffff;
+
+ fsg_common->ops = NULL;
+ fsg_common->private_data = NULL;
+
+ the_fsg_common = fsg_common;
+
+ return fsg_bind_config(c->cdev, c, fsg_common);
+}
+
+int fsg_init(struct ums *ums_dev)
+{
+ ums = ums_dev;
+
+ return 0;
+}
+
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_ums, fsg_add);
diff --git a/src/roms/u-boot/drivers/usb/gadget/f_thor.c b/src/roms/u-boot/drivers/usb/gadget/f_thor.c
new file mode 100644
index 0000000..feef9e4
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/f_thor.c
@@ -0,0 +1,1008 @@
+/*
+ * f_thor.c -- USB TIZEN THOR Downloader gadget function
+ *
+ * Copyright (C) 2013 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * Based on code from:
+ * git://review.tizen.org/kernel/u-boot
+ *
+ * Developed by:
+ * Copyright (C) 2009 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ * Sanghee Kim <sh0130.kim@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <errno.h>
+#include <common.h>
+#include <malloc.h>
+#include <version.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+#include <g_dnl.h>
+#include <dfu.h>
+
+#include "f_thor.h"
+
+static void thor_tx_data(unsigned char *data, int len);
+static void thor_set_dma(void *addr, int len);
+static int thor_rx_data(void);
+
+static struct f_thor *thor_func;
+static inline struct f_thor *func_to_thor(struct usb_function *f)
+{
+ return container_of(f, struct f_thor, usb_function);
+}
+
+DEFINE_CACHE_ALIGN_BUFFER(unsigned char, thor_tx_data_buf,
+ sizeof(struct rsp_box));
+DEFINE_CACHE_ALIGN_BUFFER(unsigned char, thor_rx_data_buf,
+ sizeof(struct rqt_box));
+
+/* ********************************************************** */
+/* THOR protocol - transmission handling */
+/* ********************************************************** */
+DEFINE_CACHE_ALIGN_BUFFER(char, f_name, F_NAME_BUF_SIZE);
+static unsigned long long int thor_file_size;
+static int alt_setting_num;
+
+static void send_rsp(const struct rsp_box *rsp)
+{
+ memcpy(thor_tx_data_buf, rsp, sizeof(struct rsp_box));
+ thor_tx_data(thor_tx_data_buf, sizeof(struct rsp_box));
+
+ debug("-RSP: %d, %d\n", rsp->rsp, rsp->rsp_data);
+}
+
+static void send_data_rsp(s32 ack, s32 count)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct data_rsp_box, rsp,
+ sizeof(struct data_rsp_box));
+
+ rsp->ack = ack;
+ rsp->count = count;
+
+ memcpy(thor_tx_data_buf, rsp, sizeof(struct data_rsp_box));
+ thor_tx_data(thor_tx_data_buf, sizeof(struct data_rsp_box));
+
+ debug("-DATA RSP: %d, %d\n", ack, count);
+}
+
+static int process_rqt_info(const struct rqt_box *rqt)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box));
+ memset(rsp, 0, sizeof(struct rsp_box));
+
+ rsp->rsp = rqt->rqt;
+ rsp->rsp_data = rqt->rqt_data;
+
+ switch (rqt->rqt_data) {
+ case RQT_INFO_VER_PROTOCOL:
+ rsp->int_data[0] = VER_PROTOCOL_MAJOR;
+ rsp->int_data[1] = VER_PROTOCOL_MINOR;
+ break;
+ case RQT_INIT_VER_HW:
+ snprintf(rsp->str_data[0], sizeof(rsp->str_data[0]),
+ "%x", checkboard());
+ break;
+ case RQT_INIT_VER_BOOT:
+ sprintf(rsp->str_data[0], "%s", U_BOOT_VERSION);
+ break;
+ case RQT_INIT_VER_KERNEL:
+ sprintf(rsp->str_data[0], "%s", "k unknown");
+ break;
+ case RQT_INIT_VER_PLATFORM:
+ sprintf(rsp->str_data[0], "%s", "p unknown");
+ break;
+ case RQT_INIT_VER_CSC:
+ sprintf(rsp->str_data[0], "%s", "c unknown");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ send_rsp(rsp);
+ return true;
+}
+
+static int process_rqt_cmd(const struct rqt_box *rqt)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box));
+ memset(rsp, 0, sizeof(struct rsp_box));
+
+ rsp->rsp = rqt->rqt;
+ rsp->rsp_data = rqt->rqt_data;
+
+ switch (rqt->rqt_data) {
+ case RQT_CMD_REBOOT:
+ debug("TARGET RESET\n");
+ send_rsp(rsp);
+ g_dnl_unregister();
+ dfu_free_entities();
+ run_command("reset", 0);
+ break;
+ case RQT_CMD_POWEROFF:
+ case RQT_CMD_EFSCLEAR:
+ send_rsp(rsp);
+ default:
+ printf("Command not supported -> cmd: %d\n", rqt->rqt_data);
+ return -EINVAL;
+ }
+
+ return true;
+}
+
+static long long int download_head(unsigned long long total,
+ unsigned int packet_size,
+ long long int *left,
+ int *cnt)
+{
+ long long int rcv_cnt = 0, left_to_rcv, ret_rcv;
+ void *transfer_buffer = dfu_get_buf();
+ void *buf = transfer_buffer;
+ int usb_pkt_cnt = 0, ret;
+
+ /*
+ * Files smaller than THOR_STORE_UNIT_SIZE (now 32 MiB) are stored on
+ * the medium.
+ * The packet response is sent on the purpose after successful data
+ * chunk write. There is a room for improvement when asynchronous write
+ * is performed.
+ */
+ while (total - rcv_cnt >= packet_size) {
+ thor_set_dma(buf, packet_size);
+ buf += packet_size;
+ ret_rcv = thor_rx_data();
+ if (ret_rcv < 0)
+ return ret_rcv;
+ rcv_cnt += ret_rcv;
+ debug("%d: RCV data count: %llu cnt: %d\n", usb_pkt_cnt,
+ rcv_cnt, *cnt);
+
+ if ((rcv_cnt % THOR_STORE_UNIT_SIZE) == 0) {
+ ret = dfu_write(dfu_get_entity(alt_setting_num),
+ transfer_buffer, THOR_STORE_UNIT_SIZE,
+ (*cnt)++);
+ if (ret) {
+ error("DFU write failed [%d] cnt: %d",
+ ret, *cnt);
+ return ret;
+ }
+ buf = transfer_buffer;
+ }
+ send_data_rsp(0, ++usb_pkt_cnt);
+ }
+
+ /* Calculate the amount of data to arrive from PC (in bytes) */
+ left_to_rcv = total - rcv_cnt;
+
+ /*
+ * Calculate number of data already received. but not yet stored
+ * on the medium (they are smaller than THOR_STORE_UNIT_SIZE)
+ */
+ *left = left_to_rcv + buf - transfer_buffer;
+ debug("%s: left: %llu left_to_rcv: %llu buf: 0x%p\n", __func__,
+ *left, left_to_rcv, buf);
+
+ if (left_to_rcv) {
+ thor_set_dma(buf, packet_size);
+ ret_rcv = thor_rx_data();
+ if (ret_rcv < 0)
+ return ret_rcv;
+ rcv_cnt += ret_rcv;
+ send_data_rsp(0, ++usb_pkt_cnt);
+ }
+
+ debug("%s: %llu total: %llu cnt: %d\n", __func__, rcv_cnt, total, *cnt);
+
+ return rcv_cnt;
+}
+
+static int download_tail(long long int left, int cnt)
+{
+ struct dfu_entity *dfu_entity = dfu_get_entity(alt_setting_num);
+ void *transfer_buffer = dfu_get_buf();
+ int ret;
+
+ debug("%s: left: %llu cnt: %d\n", __func__, left, cnt);
+
+ if (left) {
+ ret = dfu_write(dfu_entity, transfer_buffer, left, cnt++);
+ if (ret) {
+ error("DFU write failed [%d]: left: %llu", ret, left);
+ return ret;
+ }
+ }
+
+ /*
+ * To store last "packet" DFU storage backend requires dfu_write with
+ * size parameter equal to 0
+ *
+ * This also frees memory malloc'ed by dfu_get_buf(), so no explicit
+ * need fo call dfu_free_buf() is needed.
+ */
+ ret = dfu_write(dfu_entity, transfer_buffer, 0, cnt);
+ if (ret)
+ error("DFU write failed [%d] cnt: %d", ret, cnt);
+
+ ret = dfu_flush(dfu_entity, transfer_buffer, 0, cnt);
+ if (ret) {
+ error("DFU flush failed!");
+ return ret;
+ }
+
+ return ret;
+}
+
+static long long int process_rqt_download(const struct rqt_box *rqt)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box));
+ static long long int left, ret_head;
+ int file_type, ret = 0;
+ static int cnt;
+
+ memset(rsp, 0, sizeof(struct rsp_box));
+ rsp->rsp = rqt->rqt;
+ rsp->rsp_data = rqt->rqt_data;
+
+ switch (rqt->rqt_data) {
+ case RQT_DL_INIT:
+ thor_file_size = rqt->int_data[0];
+ debug("INIT: total %d bytes\n", rqt->int_data[0]);
+ break;
+ case RQT_DL_FILE_INFO:
+ file_type = rqt->int_data[0];
+ if (file_type == FILE_TYPE_PIT) {
+ puts("PIT table file - not supported\n");
+ rsp->ack = -ENOTSUPP;
+ ret = rsp->ack;
+ break;
+ }
+
+ thor_file_size = rqt->int_data[1];
+ memcpy(f_name, rqt->str_data[0], F_NAME_BUF_SIZE);
+
+ debug("INFO: name(%s, %d), size(%llu), type(%d)\n",
+ f_name, 0, thor_file_size, file_type);
+
+ rsp->int_data[0] = THOR_PACKET_SIZE;
+
+ alt_setting_num = dfu_get_alt(f_name);
+ if (alt_setting_num < 0) {
+ error("Alt setting [%d] to write not found!",
+ alt_setting_num);
+ rsp->ack = -ENODEV;
+ ret = rsp->ack;
+ }
+ break;
+ case RQT_DL_FILE_START:
+ send_rsp(rsp);
+ ret_head = download_head(thor_file_size, THOR_PACKET_SIZE,
+ &left, &cnt);
+ if (ret_head < 0) {
+ left = 0;
+ cnt = 0;
+ }
+ return ret_head;
+ case RQT_DL_FILE_END:
+ debug("DL FILE_END\n");
+ rsp->ack = download_tail(left, cnt);
+ ret = rsp->ack;
+ left = 0;
+ cnt = 0;
+ break;
+ case RQT_DL_EXIT:
+ debug("DL EXIT\n");
+ break;
+ default:
+ error("Operation not supported: %d", rqt->rqt_data);
+ ret = -ENOTSUPP;
+ }
+
+ send_rsp(rsp);
+ return ret;
+}
+
+static int process_data(void)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct rqt_box, rqt, sizeof(struct rqt_box));
+ int ret = -EINVAL;
+
+ memset(rqt, 0, sizeof(rqt));
+ memcpy(rqt, thor_rx_data_buf, sizeof(struct rqt_box));
+
+ debug("+RQT: %d, %d\n", rqt->rqt, rqt->rqt_data);
+
+ switch (rqt->rqt) {
+ case RQT_INFO:
+ ret = process_rqt_info(rqt);
+ break;
+ case RQT_CMD:
+ ret = process_rqt_cmd(rqt);
+ break;
+ case RQT_DL:
+ ret = (int) process_rqt_download(rqt);
+ break;
+ case RQT_UL:
+ puts("RQT: UPLOAD not supported!\n");
+ break;
+ default:
+ error("unknown request (%d)", rqt->rqt);
+ }
+
+ return ret;
+}
+
+/* ********************************************************** */
+/* THOR USB Function */
+/* ********************************************************** */
+
+static inline struct usb_endpoint_descriptor *
+ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
+ struct usb_endpoint_descriptor *fs)
+{
+ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return hs;
+ return fs;
+}
+
+static struct usb_interface_descriptor thor_downloader_intf_data = {
+ .bLength = sizeof(thor_downloader_intf_data),
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+};
+
+static struct usb_endpoint_descriptor fs_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+/* CDC configuration */
+static struct usb_interface_descriptor thor_downloader_intf_int = {
+ .bLength = sizeof(thor_downloader_intf_int),
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ /* 0x02 Abstract Line Control Model */
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
+ /* 0x01 Common AT commands */
+ .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
+};
+
+static struct usb_cdc_header_desc thor_downloader_cdc_header = {
+ .bLength = sizeof(thor_downloader_cdc_header),
+ .bDescriptorType = 0x24, /* CS_INTERFACE */
+ .bDescriptorSubType = 0x00,
+ .bcdCDC = 0x0110,
+};
+
+static struct usb_cdc_call_mgmt_descriptor thor_downloader_cdc_call = {
+ .bLength = sizeof(thor_downloader_cdc_call),
+ .bDescriptorType = 0x24, /* CS_INTERFACE */
+ .bDescriptorSubType = 0x01,
+ .bmCapabilities = 0x00,
+ .bDataInterface = 0x01,
+};
+
+static struct usb_cdc_acm_descriptor thor_downloader_cdc_abstract = {
+ .bLength = sizeof(thor_downloader_cdc_abstract),
+ .bDescriptorType = 0x24, /* CS_INTERFACE */
+ .bDescriptorSubType = 0x02,
+ .bmCapabilities = 0x00,
+};
+
+static struct usb_cdc_union_desc thor_downloader_cdc_union = {
+ .bLength = sizeof(thor_downloader_cdc_union),
+ .bDescriptorType = 0x24, /* CS_INTERFACE */
+ .bDescriptorSubType = USB_CDC_UNION_TYPE,
+};
+
+static struct usb_endpoint_descriptor fs_int_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = 3 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(16),
+
+ .bInterval = 0x9,
+};
+
+static struct usb_interface_assoc_descriptor
+thor_iad_descriptor = {
+ .bLength = sizeof(thor_iad_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+ .bFirstInterface = 0,
+ .bInterfaceCount = 2, /* control + data */
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
+ .bFunctionProtocol = USB_CDC_PROTO_NONE,
+};
+
+static struct usb_endpoint_descriptor hs_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_int_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(16),
+
+ .bInterval = 0x9,
+};
+
+static struct usb_qualifier_descriptor dev_qualifier = {
+ .bLength = sizeof(dev_qualifier),
+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+
+ .bNumConfigurations = 2,
+};
+
+/*
+ * This attribute vendor descriptor is necessary for correct operation with
+ * Windows version of THOR download program
+ *
+ * It prevents windows driver from sending zero lenght packet (ZLP) after
+ * each THOR_PACKET_SIZE. This assures consistent behaviour with libusb
+ */
+static struct usb_cdc_attribute_vendor_descriptor thor_downloader_cdc_av = {
+ .bLength = sizeof(thor_downloader_cdc_av),
+ .bDescriptorType = 0x24,
+ .bDescriptorSubType = 0x80,
+ .DAUType = 0x0002,
+ .DAULength = 0x0001,
+ .DAUValue = 0x00,
+};
+
+static const struct usb_descriptor_header *hs_thor_downloader_function[] = {
+ (struct usb_descriptor_header *)&thor_iad_descriptor,
+
+ (struct usb_descriptor_header *)&thor_downloader_intf_int,
+ (struct usb_descriptor_header *)&thor_downloader_cdc_header,
+ (struct usb_descriptor_header *)&thor_downloader_cdc_call,
+ (struct usb_descriptor_header *)&thor_downloader_cdc_abstract,
+ (struct usb_descriptor_header *)&thor_downloader_cdc_union,
+ (struct usb_descriptor_header *)&hs_int_desc,
+
+ (struct usb_descriptor_header *)&thor_downloader_intf_data,
+ (struct usb_descriptor_header *)&thor_downloader_cdc_av,
+ (struct usb_descriptor_header *)&hs_in_desc,
+ (struct usb_descriptor_header *)&hs_out_desc,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, 0);
+ if (!req)
+ return req;
+
+ req->length = length;
+ req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ req = NULL;
+ }
+
+ return req;
+}
+
+static int thor_rx_data(void)
+{
+ struct thor_dev *dev = thor_func->dev;
+ int data_to_rx, tmp, status;
+
+ data_to_rx = dev->out_req->length;
+ tmp = data_to_rx;
+ do {
+ dev->out_req->length = data_to_rx;
+ debug("dev->out_req->length:%d dev->rxdata:%d\n",
+ dev->out_req->length, dev->rxdata);
+
+ status = usb_ep_queue(dev->out_ep, dev->out_req, 0);
+ if (status) {
+ error("kill %s: resubmit %d bytes --> %d",
+ dev->out_ep->name, dev->out_req->length, status);
+ usb_ep_set_halt(dev->out_ep);
+ return -EAGAIN;
+ }
+
+ while (!dev->rxdata) {
+ usb_gadget_handle_interrupts();
+ if (ctrlc())
+ return -1;
+ }
+ dev->rxdata = 0;
+ data_to_rx -= dev->out_req->actual;
+ } while (data_to_rx);
+
+ return tmp;
+}
+
+static void thor_tx_data(unsigned char *data, int len)
+{
+ struct thor_dev *dev = thor_func->dev;
+ unsigned char *ptr = dev->in_req->buf;
+ int status;
+
+ memset(ptr, 0, len);
+ memcpy(ptr, data, len);
+
+ dev->in_req->length = len;
+
+ debug("%s: dev->in_req->length:%d to_cpy:%d\n", __func__,
+ dev->in_req->length, sizeof(data));
+
+ status = usb_ep_queue(dev->in_ep, dev->in_req, 0);
+ if (status) {
+ error("kill %s: resubmit %d bytes --> %d",
+ dev->in_ep->name, dev->in_req->length, status);
+ usb_ep_set_halt(dev->in_ep);
+ }
+
+ /* Wait until tx interrupt received */
+ while (!dev->txdata)
+ usb_gadget_handle_interrupts();
+
+ dev->txdata = 0;
+}
+
+static void thor_rx_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct thor_dev *dev = thor_func->dev;
+ int status = req->status;
+
+ debug("%s: ep_ptr:%p, req_ptr:%p\n", __func__, ep, req);
+ switch (status) {
+ case 0:
+ if (ep == dev->out_ep)
+ dev->rxdata = 1;
+ else
+ dev->txdata = 1;
+
+ break;
+
+ /* this endpoint is normally active while we're configured */
+ case -ECONNABORTED: /* hardware forced ep reset */
+ case -ECONNRESET: /* request dequeued */
+ case -ESHUTDOWN: /* disconnect from host */
+ case -EREMOTEIO: /* short read */
+ case -EOVERFLOW:
+ error("ERROR:%d", status);
+ break;
+ }
+
+ debug("%s complete --> %d, %d/%d\n", ep->name,
+ status, req->actual, req->length);
+}
+
+static struct usb_request *thor_start_ep(struct usb_ep *ep)
+{
+ struct usb_request *req;
+
+ req = alloc_ep_req(ep, THOR_PACKET_SIZE);
+ debug("%s: ep:%p req:%p\n", __func__, ep, req);
+
+ if (!req)
+ return NULL;
+
+ memset(req->buf, 0, req->length);
+ req->complete = thor_rx_tx_complete;
+
+ return req;
+}
+
+static void thor_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->status || req->actual != req->length)
+ debug("setup complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
+}
+
+static int
+thor_func_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct thor_dev *dev = thor_func->dev;
+ struct usb_request *req = dev->req;
+ struct usb_gadget *gadget = dev->gadget;
+ int value = 0;
+
+ u16 len = le16_to_cpu(ctrl->wLength);
+
+ debug("Req_Type: 0x%x Req: 0x%x wValue: 0x%x wIndex: 0x%x wLen: 0x%x\n",
+ ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex,
+ ctrl->wLength);
+
+ switch (ctrl->bRequest) {
+ case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+ value = 0;
+ break;
+ case USB_CDC_REQ_SET_LINE_CODING:
+ value = len;
+ /* Line Coding set done = configuration done */
+ thor_func->dev->configuration_done = 1;
+ break;
+
+ default:
+ error("thor_setup: unknown request: %d", ctrl->bRequest);
+ }
+
+ if (value >= 0) {
+ req->length = value;
+ req->zero = value < len;
+ value = usb_ep_queue(gadget->ep0, req, 0);
+ if (value < 0) {
+ debug("%s: ep_queue: %d\n", __func__, value);
+ req->status = 0;
+ }
+ }
+
+ return value;
+}
+
+/* Specific to the THOR protocol */
+static void thor_set_dma(void *addr, int len)
+{
+ struct thor_dev *dev = thor_func->dev;
+
+ debug("in_req:%p, out_req:%p\n", dev->in_req, dev->out_req);
+ debug("addr:%p, len:%d\n", addr, len);
+
+ dev->out_req->buf = addr;
+ dev->out_req->length = len;
+}
+
+int thor_init(void)
+{
+ struct thor_dev *dev = thor_func->dev;
+
+ /* Wait for a device enumeration and configuration settings */
+ debug("THOR enumeration/configuration setting....\n");
+ while (!dev->configuration_done)
+ usb_gadget_handle_interrupts();
+
+ thor_set_dma(thor_rx_data_buf, strlen("THOR"));
+ /* detect the download request from Host PC */
+ if (thor_rx_data() < 0) {
+ printf("%s: Data not received!\n", __func__);
+ return -1;
+ }
+
+ if (!strncmp((char *)thor_rx_data_buf, "THOR", strlen("THOR"))) {
+ puts("Download request from the Host PC\n");
+ udelay(30 * 1000); /* 30 ms */
+
+ strcpy((char *)thor_tx_data_buf, "ROHT");
+ thor_tx_data(thor_tx_data_buf, strlen("ROHT"));
+ } else {
+ puts("Wrong reply information\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int thor_handle(void)
+{
+ int ret;
+
+ /* receive the data from Host PC */
+ while (1) {
+ thor_set_dma(thor_rx_data_buf, sizeof(struct rqt_box));
+ ret = thor_rx_data();
+
+ if (ret > 0) {
+ ret = process_data();
+ if (ret < 0)
+ return ret;
+ } else {
+ printf("%s: No data received!\n", __func__);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int thor_func_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_gadget *gadget = c->cdev->gadget;
+ struct f_thor *f_thor = func_to_thor(f);
+ struct thor_dev *dev;
+ struct usb_ep *ep;
+ int status;
+
+ thor_func = f_thor;
+ dev = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*dev));
+ if (!dev)
+ return -ENOMEM;
+
+ memset(dev, 0, sizeof(*dev));
+ dev->gadget = gadget;
+ f_thor->dev = dev;
+
+ debug("%s: usb_configuration: 0x%p usb_function: 0x%p\n",
+ __func__, c, f);
+ debug("f_thor: 0x%p thor: 0x%p\n", f_thor, dev);
+
+ /* EP0 */
+ /* preallocate control response and buffer */
+ dev->req = usb_ep_alloc_request(gadget->ep0, 0);
+ if (!dev->req) {
+ status = -ENOMEM;
+ goto fail;
+ }
+ dev->req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE,
+ gadget->ep0->maxpacket);
+ if (!dev->req->buf) {
+ status = -ENOMEM;
+ goto fail;
+ }
+
+ dev->req->complete = thor_setup_complete;
+
+ /* DYNAMIC interface numbers assignments */
+ status = usb_interface_id(c, f);
+
+ if (status < 0)
+ goto fail;
+
+ thor_downloader_intf_int.bInterfaceNumber = status;
+ thor_downloader_cdc_union.bMasterInterface0 = status;
+
+ status = usb_interface_id(c, f);
+
+ if (status < 0)
+ goto fail;
+
+ thor_downloader_intf_data.bInterfaceNumber = status;
+ thor_downloader_cdc_union.bSlaveInterface0 = status;
+
+ /* allocate instance-specific endpoints */
+ ep = usb_ep_autoconfig(gadget, &fs_in_desc);
+ if (!ep) {
+ status = -ENODEV;
+ goto fail;
+ }
+
+ if (gadget_is_dualspeed(gadget)) {
+ hs_in_desc.bEndpointAddress =
+ fs_in_desc.bEndpointAddress;
+ }
+
+ dev->in_ep = ep; /* Store IN EP for enabling @ setup */
+
+ ep = usb_ep_autoconfig(gadget, &fs_out_desc);
+ if (!ep) {
+ status = -ENODEV;
+ goto fail;
+ }
+
+ if (gadget_is_dualspeed(gadget))
+ hs_out_desc.bEndpointAddress =
+ fs_out_desc.bEndpointAddress;
+
+ dev->out_ep = ep; /* Store OUT EP for enabling @ setup */
+
+ ep = usb_ep_autoconfig(gadget, &fs_int_desc);
+ if (!ep) {
+ status = -ENODEV;
+ goto fail;
+ }
+
+ dev->int_ep = ep;
+
+ if (gadget_is_dualspeed(gadget)) {
+ hs_int_desc.bEndpointAddress =
+ fs_int_desc.bEndpointAddress;
+
+ f->hs_descriptors = (struct usb_descriptor_header **)
+ &hs_thor_downloader_function;
+
+ if (!f->hs_descriptors)
+ goto fail;
+ }
+
+ debug("%s: out_ep:%p out_req:%p\n", __func__,
+ dev->out_ep, dev->out_req);
+
+ return 0;
+
+ fail:
+ free(dev);
+ return status;
+}
+
+static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+ free(req->buf);
+ usb_ep_free_request(ep, req);
+}
+
+static void thor_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_thor *f_thor = func_to_thor(f);
+ struct thor_dev *dev = f_thor->dev;
+
+ free(dev);
+ memset(thor_func, 0, sizeof(*thor_func));
+ thor_func = NULL;
+}
+
+static void thor_func_disable(struct usb_function *f)
+{
+ struct f_thor *f_thor = func_to_thor(f);
+ struct thor_dev *dev = f_thor->dev;
+
+ debug("%s:\n", __func__);
+
+ /* Avoid freeing memory when ep is still claimed */
+ if (dev->in_ep->driver_data) {
+ free_ep_req(dev->in_ep, dev->in_req);
+ usb_ep_disable(dev->in_ep);
+ dev->in_ep->driver_data = NULL;
+ }
+
+ if (dev->out_ep->driver_data) {
+ dev->out_req->buf = NULL;
+ usb_ep_free_request(dev->out_ep, dev->out_req);
+ usb_ep_disable(dev->out_ep);
+ dev->out_ep->driver_data = NULL;
+ }
+
+ if (dev->int_ep->driver_data) {
+ usb_ep_disable(dev->int_ep);
+ dev->int_ep->driver_data = NULL;
+ }
+}
+
+static int thor_eps_setup(struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_gadget *gadget = cdev->gadget;
+ struct thor_dev *dev = thor_func->dev;
+ struct usb_endpoint_descriptor *d;
+ struct usb_request *req;
+ struct usb_ep *ep;
+ int result;
+
+ ep = dev->in_ep;
+ d = ep_desc(gadget, &hs_in_desc, &fs_in_desc);
+ debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress);
+
+ result = usb_ep_enable(ep, d);
+ if (result)
+ goto exit;
+
+ ep->driver_data = cdev; /* claim */
+ req = thor_start_ep(ep);
+ if (!req) {
+ usb_ep_disable(ep);
+ result = -EIO;
+ goto exit;
+ }
+
+ dev->in_req = req;
+ ep = dev->out_ep;
+ d = ep_desc(gadget, &hs_out_desc, &fs_out_desc);
+ debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress);
+
+ result = usb_ep_enable(ep, d);
+ if (result)
+ goto exit;
+
+ ep->driver_data = cdev; /* claim */
+ req = thor_start_ep(ep);
+ if (!req) {
+ usb_ep_disable(ep);
+ result = -EIO;
+ goto exit;
+ }
+
+ dev->out_req = req;
+ /* ACM control EP */
+ ep = dev->int_ep;
+ ep->driver_data = cdev; /* claim */
+
+ exit:
+ return result;
+}
+
+static int thor_func_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct thor_dev *dev = thor_func->dev;
+ int result;
+
+ debug("%s: func: %s intf: %d alt: %d\n",
+ __func__, f->name, intf, alt);
+
+ switch (intf) {
+ case 0:
+ debug("ACM INTR interface\n");
+ break;
+ case 1:
+ debug("Communication Data interface\n");
+ result = thor_eps_setup(f);
+ if (result)
+ error("%s: EPs setup failed!", __func__);
+ dev->configuration_done = 1;
+ break;
+ }
+
+ return 0;
+}
+
+static int thor_func_init(struct usb_configuration *c)
+{
+ struct f_thor *f_thor;
+ int status;
+
+ debug("%s: cdev: 0x%p\n", __func__, c->cdev);
+
+ f_thor = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_thor));
+ if (!f_thor)
+ return -ENOMEM;
+
+ memset(f_thor, 0, sizeof(*f_thor));
+
+ f_thor->usb_function.name = "f_thor";
+ f_thor->usb_function.bind = thor_func_bind;
+ f_thor->usb_function.unbind = thor_unbind;
+ f_thor->usb_function.setup = thor_func_setup;
+ f_thor->usb_function.set_alt = thor_func_set_alt;
+ f_thor->usb_function.disable = thor_func_disable;
+
+ status = usb_add_function(c, &f_thor->usb_function);
+ if (status)
+ free(f_thor);
+
+ return status;
+}
+
+int thor_add(struct usb_configuration *c)
+{
+ debug("%s:\n", __func__);
+ return thor_func_init(c);
+}
+
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_thor, thor_add);
diff --git a/src/roms/u-boot/drivers/usb/gadget/f_thor.h b/src/roms/u-boot/drivers/usb/gadget/f_thor.h
new file mode 100644
index 0000000..833a9d2
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/f_thor.h
@@ -0,0 +1,124 @@
+/*
+ * f_thor.h - USB TIZEN THOR - internal gadget definitions
+ *
+ * Copyright (C) 2013 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef _USB_THOR_H_
+#define _USB_THOR_H_
+
+#include <linux/compiler.h>
+#include <linux/sizes.h>
+
+/* THOR Composite Gadget */
+#define STRING_MANUFACTURER_IDX 0
+#define STRING_PRODUCT_IDX 1
+#define STRING_SERIAL_IDX 2
+
+/* ********************************************************** */
+/* THOR protocol definitions */
+/* ********************************************************** */
+
+/*
+ * Attribute Vendor descriptor - necessary to prevent ZLP transmission
+ * from Windows XP HOST PC
+ */
+struct usb_cdc_attribute_vendor_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubType;
+ __u16 DAUType;
+ __u16 DAULength;
+ __u8 DAUValue;
+} __packed;
+
+#define VER_PROTOCOL_MAJOR 4
+#define VER_PROTOCOL_MINOR 0
+
+enum rqt {
+ RQT_INFO = 200,
+ RQT_CMD,
+ RQT_DL,
+ RQT_UL,
+};
+
+enum rqt_data {
+ /* RQT_INFO */
+ RQT_INFO_VER_PROTOCOL = 1,
+ RQT_INIT_VER_HW,
+ RQT_INIT_VER_BOOT,
+ RQT_INIT_VER_KERNEL,
+ RQT_INIT_VER_PLATFORM,
+ RQT_INIT_VER_CSC,
+
+ /* RQT_CMD */
+ RQT_CMD_REBOOT = 1,
+ RQT_CMD_POWEROFF,
+ RQT_CMD_EFSCLEAR,
+
+ /* RQT_DL */
+ RQT_DL_INIT = 1,
+ RQT_DL_FILE_INFO,
+ RQT_DL_FILE_START,
+ RQT_DL_FILE_END,
+ RQT_DL_EXIT,
+
+ /* RQT_UL */
+ RQT_UL_INIT = 1,
+ RQT_UL_START,
+ RQT_UL_END,
+ RQT_UL_EXIT,
+};
+
+struct rqt_box { /* total: 256B */
+ s32 rqt; /* request id */
+ s32 rqt_data; /* request data id */
+ s32 int_data[14]; /* int data */
+ char str_data[5][32]; /* string data */
+ char md5[32]; /* md5 checksum */
+} __packed;
+
+struct rsp_box { /* total: 128B */
+ s32 rsp; /* response id (= request id) */
+ s32 rsp_data; /* response data id */
+ s32 ack; /* ack */
+ s32 int_data[5]; /* int data */
+ char str_data[3][32]; /* string data */
+} __packed;
+
+struct data_rsp_box { /* total: 8B */
+ s32 ack; /* response id (= request id) */
+ s32 count; /* response data id */
+} __packed;
+
+enum {
+ FILE_TYPE_NORMAL,
+ FILE_TYPE_PIT,
+};
+
+struct thor_dev {
+ struct usb_gadget *gadget;
+ struct usb_request *req; /* EP0 -> control responses */
+
+ /* IN/OUT EP's and correspoinding requests */
+ struct usb_ep *in_ep, *out_ep, *int_ep;
+ struct usb_request *in_req, *out_req;
+
+ /* Control flow variables */
+ unsigned char configuration_done;
+ unsigned char rxdata;
+ unsigned char txdata;
+};
+
+struct f_thor {
+ struct usb_function usb_function;
+ struct thor_dev *dev;
+};
+
+#define F_NAME_BUF_SIZE 32
+#define THOR_PACKET_SIZE SZ_1M /* 1 MiB */
+#define THOR_STORE_UNIT_SIZE SZ_32M /* 32 MiB */
+#endif /* _USB_THOR_H_ */
diff --git a/src/roms/u-boot/drivers/usb/gadget/fotg210.c b/src/roms/u-boot/drivers/usb/gadget/fotg210.c
new file mode 100644
index 0000000..3acf6a1
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/fotg210.c
@@ -0,0 +1,962 @@
+/*
+ * Faraday USB 2.0 OTG Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <command.h>
+#include <config.h>
+#include <net.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+#include <linux/types.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <usb/fotg210.h>
+
+#define CFG_NUM_ENDPOINTS 4
+#define CFG_EP0_MAX_PACKET_SIZE 64
+#define CFG_EPX_MAX_PACKET_SIZE 512
+
+#define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 2) /* 250 ms */
+
+struct fotg210_chip;
+
+struct fotg210_ep {
+ struct usb_ep ep;
+
+ uint maxpacket;
+ uint id;
+ uint stopped;
+
+ struct list_head queue;
+ struct fotg210_chip *chip;
+ const struct usb_endpoint_descriptor *desc;
+};
+
+struct fotg210_request {
+ struct usb_request req;
+ struct list_head queue;
+ struct fotg210_ep *ep;
+};
+
+struct fotg210_chip {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct fotg210_regs *regs;
+ uint8_t irq;
+ uint16_t addr;
+ int pullup;
+ enum usb_device_state state;
+ struct fotg210_ep ep[1 + CFG_NUM_ENDPOINTS];
+};
+
+static struct usb_endpoint_descriptor ep0_desc = {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+};
+
+static inline int fifo_to_ep(struct fotg210_chip *chip, int id, int in)
+{
+ return (id < 0) ? 0 : ((id & 0x03) + 1);
+}
+
+static inline int ep_to_fifo(struct fotg210_chip *chip, int id)
+{
+ return (id <= 0) ? -1 : ((id - 1) & 0x03);
+}
+
+static inline int ep_reset(struct fotg210_chip *chip, uint8_t ep_addr)
+{
+ int ep = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+ struct fotg210_regs *regs = chip->regs;
+
+ if (ep_addr & USB_DIR_IN) {
+ /* reset endpoint */
+ setbits_le32(&regs->iep[ep - 1], IEP_RESET);
+ mdelay(1);
+ clrbits_le32(&regs->iep[ep - 1], IEP_RESET);
+ /* clear endpoint stall */
+ clrbits_le32(&regs->iep[ep - 1], IEP_STALL);
+ } else {
+ /* reset endpoint */
+ setbits_le32(&regs->oep[ep - 1], OEP_RESET);
+ mdelay(1);
+ clrbits_le32(&regs->oep[ep - 1], OEP_RESET);
+ /* clear endpoint stall */
+ clrbits_le32(&regs->oep[ep - 1], OEP_STALL);
+ }
+
+ return 0;
+}
+
+static int fotg210_reset(struct fotg210_chip *chip)
+{
+ struct fotg210_regs *regs = chip->regs;
+ uint32_t i;
+
+ chip->state = USB_STATE_POWERED;
+
+ /* chip enable */
+ writel(DEVCTRL_EN, &regs->dev_ctrl);
+
+ /* device address reset */
+ chip->addr = 0;
+ writel(0, &regs->dev_addr);
+
+ /* set idle counter to 7ms */
+ writel(7, &regs->idle);
+
+ /* disable all interrupts */
+ writel(IMR_MASK, &regs->imr);
+ writel(GIMR_MASK, &regs->gimr);
+ writel(GIMR0_MASK, &regs->gimr0);
+ writel(GIMR1_MASK, &regs->gimr1);
+ writel(GIMR2_MASK, &regs->gimr2);
+
+ /* clear interrupts */
+ writel(ISR_MASK, &regs->isr);
+ writel(0, &regs->gisr);
+ writel(0, &regs->gisr0);
+ writel(0, &regs->gisr1);
+ writel(0, &regs->gisr2);
+
+ /* chip reset */
+ setbits_le32(&regs->dev_ctrl, DEVCTRL_RESET);
+ mdelay(10);
+ if (readl(&regs->dev_ctrl) & DEVCTRL_RESET) {
+ printf("fotg210: chip reset failed\n");
+ return -1;
+ }
+
+ /* CX FIFO reset */
+ setbits_le32(&regs->cxfifo, CXFIFO_CXFIFOCLR);
+ mdelay(10);
+ if (readl(&regs->cxfifo) & CXFIFO_CXFIFOCLR) {
+ printf("fotg210: ep0 fifo reset failed\n");
+ return -1;
+ }
+
+ /* create static ep-fifo map (EP1 <-> FIFO0, EP2 <-> FIFO1 ...) */
+ writel(EPMAP14_DEFAULT, &regs->epmap14);
+ writel(EPMAP58_DEFAULT, &regs->epmap58);
+ writel(FIFOMAP_DEFAULT, &regs->fifomap);
+ writel(0, &regs->fifocfg);
+ for (i = 0; i < 8; ++i) {
+ writel(CFG_EPX_MAX_PACKET_SIZE, &regs->iep[i]);
+ writel(CFG_EPX_MAX_PACKET_SIZE, &regs->oep[i]);
+ }
+
+ /* FIFO reset */
+ for (i = 0; i < 4; ++i) {
+ writel(FIFOCSR_RESET, &regs->fifocsr[i]);
+ mdelay(10);
+ if (readl(&regs->fifocsr[i]) & FIFOCSR_RESET) {
+ printf("fotg210: fifo%d reset failed\n", i);
+ return -1;
+ }
+ }
+
+ /* enable only device interrupt and triggered at level-high */
+ writel(IMR_IRQLH | IMR_HOST | IMR_OTG, &regs->imr);
+ writel(ISR_MASK, &regs->isr);
+ /* disable EP0 IN/OUT interrupt */
+ writel(GIMR0_CXOUT | GIMR0_CXIN, &regs->gimr0);
+ /* disable EPX IN+SPK+OUT interrupts */
+ writel(GIMR1_MASK, &regs->gimr1);
+ /* disable wakeup+idle+dma+zlp interrupts */
+ writel(GIMR2_WAKEUP | GIMR2_IDLE | GIMR2_DMAERR | GIMR2_DMAFIN
+ | GIMR2_ZLPRX | GIMR2_ZLPTX, &regs->gimr2);
+ /* enable all group interrupt */
+ writel(0, &regs->gimr);
+
+ /* suspend delay = 3 ms */
+ writel(3, &regs->idle);
+
+ /* turn-on device interrupts */
+ setbits_le32(&regs->dev_ctrl, DEVCTRL_GIRQ_EN);
+
+ return 0;
+}
+
+static inline int fotg210_cxwait(struct fotg210_chip *chip, uint32_t mask)
+{
+ struct fotg210_regs *regs = chip->regs;
+ int ret = -1;
+ ulong ts;
+
+ for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
+ if ((readl(&regs->cxfifo) & mask) != mask)
+ continue;
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ printf("fotg210: cx/ep0 timeout\n");
+
+ return ret;
+}
+
+static int fotg210_dma(struct fotg210_ep *ep, struct fotg210_request *req)
+{
+ struct fotg210_chip *chip = ep->chip;
+ struct fotg210_regs *regs = chip->regs;
+ uint32_t tmp, ts;
+ uint8_t *buf = req->req.buf + req->req.actual;
+ uint32_t len = req->req.length - req->req.actual;
+ int fifo = ep_to_fifo(chip, ep->id);
+ int ret = -EBUSY;
+
+ /* 1. init dma buffer */
+ if (len > ep->maxpacket)
+ len = ep->maxpacket;
+
+ /* 2. wait for dma ready (hardware) */
+ for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
+ if (!(readl(&regs->dma_ctrl) & DMACTRL_START)) {
+ ret = 0;
+ break;
+ }
+ }
+ if (ret) {
+ printf("fotg210: dma busy\n");
+ req->req.status = ret;
+ return ret;
+ }
+
+ /* 3. DMA target setup */
+ if (ep->desc->bEndpointAddress & USB_DIR_IN)
+ flush_dcache_range((ulong)buf, (ulong)buf + len);
+ else
+ invalidate_dcache_range((ulong)buf, (ulong)buf + len);
+
+ writel(virt_to_phys(buf), &regs->dma_addr);
+
+ if (ep->desc->bEndpointAddress & USB_DIR_IN) {
+ if (ep->id == 0) {
+ /* Wait until cx/ep0 fifo empty */
+ fotg210_cxwait(chip, CXFIFO_CXFIFOE);
+ udelay(1);
+ writel(DMAFIFO_CX, &regs->dma_fifo);
+ } else {
+ /* Wait until epx fifo empty */
+ fotg210_cxwait(chip, CXFIFO_FIFOE(fifo));
+ writel(DMAFIFO_FIFO(fifo), &regs->dma_fifo);
+ }
+ writel(DMACTRL_LEN(len) | DMACTRL_MEM2FIFO, &regs->dma_ctrl);
+ } else {
+ uint32_t blen;
+
+ if (ep->id == 0) {
+ writel(DMAFIFO_CX, &regs->dma_fifo);
+ do {
+ blen = CXFIFO_BYTES(readl(&regs->cxfifo));
+ } while (blen < len);
+ } else {
+ writel(DMAFIFO_FIFO(fifo), &regs->dma_fifo);
+ blen = FIFOCSR_BYTES(readl(&regs->fifocsr[fifo]));
+ }
+ len = (len < blen) ? len : blen;
+ writel(DMACTRL_LEN(len) | DMACTRL_FIFO2MEM, &regs->dma_ctrl);
+ }
+
+ /* 4. DMA start */
+ setbits_le32(&regs->dma_ctrl, DMACTRL_START);
+
+ /* 5. DMA wait */
+ ret = -EBUSY;
+ for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
+ tmp = readl(&regs->gisr2);
+ /* DMA complete */
+ if (tmp & GISR2_DMAFIN) {
+ ret = 0;
+ break;
+ }
+ /* DMA error */
+ if (tmp & GISR2_DMAERR) {
+ printf("fotg210: dma error\n");
+ break;
+ }
+ /* resume, suspend, reset */
+ if (tmp & (GISR2_RESUME | GISR2_SUSPEND | GISR2_RESET)) {
+ printf("fotg210: dma reset by host\n");
+ break;
+ }
+ }
+
+ /* 7. DMA target reset */
+ if (ret)
+ writel(DMACTRL_ABORT | DMACTRL_CLRFF, &regs->dma_ctrl);
+
+ writel(0, &regs->gisr2);
+ writel(0, &regs->dma_fifo);
+
+ req->req.status = ret;
+ if (!ret)
+ req->req.actual += len;
+ else
+ printf("fotg210: ep%d dma error(code=%d)\n", ep->id, ret);
+
+ return len;
+}
+
+/*
+ * result of setup packet
+ */
+#define CX_IDLE 0
+#define CX_FINISH 1
+#define CX_STALL 2
+
+static void fotg210_setup(struct fotg210_chip *chip)
+{
+ int id, ret = CX_IDLE;
+ uint32_t tmp[2];
+ struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)tmp;
+ struct fotg210_regs *regs = chip->regs;
+
+ /*
+ * If this is the first Cx 8 byte command,
+ * we can now query USB mode (high/full speed; USB 2.0/USB 1.0)
+ */
+ if (chip->state == USB_STATE_POWERED) {
+ chip->state = USB_STATE_DEFAULT;
+ if (readl(&regs->otgcsr) & OTGCSR_DEV_B) {
+ /* Mini-B */
+ if (readl(&regs->dev_ctrl) & DEVCTRL_HS) {
+ puts("fotg210: HS\n");
+ chip->gadget.speed = USB_SPEED_HIGH;
+ /* SOF mask timer = 1100 ticks */
+ writel(SOFMTR_TMR(1100), &regs->sof_mtr);
+ } else {
+ puts("fotg210: FS\n");
+ chip->gadget.speed = USB_SPEED_FULL;
+ /* SOF mask timer = 10000 ticks */
+ writel(SOFMTR_TMR(10000), &regs->sof_mtr);
+ }
+ } else {
+ printf("fotg210: mini-A?\n");
+ }
+ }
+
+ /* switch data port to ep0 */
+ writel(DMAFIFO_CX, &regs->dma_fifo);
+ /* fetch 8 bytes setup packet */
+ tmp[0] = readl(&regs->ep0_data);
+ tmp[1] = readl(&regs->ep0_data);
+ /* release data port */
+ writel(0, &regs->dma_fifo);
+
+ if (req->bRequestType & USB_DIR_IN)
+ ep0_desc.bEndpointAddress = USB_DIR_IN;
+ else
+ ep0_desc.bEndpointAddress = USB_DIR_OUT;
+
+ ret = CX_IDLE;
+
+ if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ switch (req->bRequest) {
+ case USB_REQ_SET_CONFIGURATION:
+ debug("fotg210: set_cfg(%d)\n", req->wValue & 0x00FF);
+ if (!(req->wValue & 0x00FF)) {
+ chip->state = USB_STATE_ADDRESS;
+ writel(chip->addr, &regs->dev_addr);
+ } else {
+ chip->state = USB_STATE_CONFIGURED;
+ writel(chip->addr | DEVADDR_CONF,
+ &regs->dev_addr);
+ }
+ ret = CX_IDLE;
+ break;
+
+ case USB_REQ_SET_ADDRESS:
+ debug("fotg210: set_addr(0x%04X)\n", req->wValue);
+ chip->state = USB_STATE_ADDRESS;
+ chip->addr = req->wValue & DEVADDR_ADDR_MASK;
+ ret = CX_FINISH;
+ writel(chip->addr, &regs->dev_addr);
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ debug("fotg210: clr_feature(%d, %d)\n",
+ req->bRequestType & 0x03, req->wValue);
+ switch (req->wValue) {
+ case 0: /* [Endpoint] halt */
+ ep_reset(chip, req->wIndex);
+ ret = CX_FINISH;
+ break;
+ case 1: /* [Device] remote wake-up */
+ case 2: /* [Device] test mode */
+ default:
+ ret = CX_STALL;
+ break;
+ }
+ break;
+
+ case USB_REQ_SET_FEATURE:
+ debug("fotg210: set_feature(%d, %d)\n",
+ req->wValue, req->wIndex & 0xf);
+ switch (req->wValue) {
+ case 0: /* Endpoint Halt */
+ id = req->wIndex & 0xf;
+ setbits_le32(&regs->iep[id - 1], IEP_STALL);
+ setbits_le32(&regs->oep[id - 1], OEP_STALL);
+ ret = CX_FINISH;
+ break;
+ case 1: /* Remote Wakeup */
+ case 2: /* Test Mode */
+ default:
+ ret = CX_STALL;
+ break;
+ }
+ break;
+
+ case USB_REQ_GET_STATUS:
+ debug("fotg210: get_status\n");
+ ret = CX_STALL;
+ break;
+
+ case USB_REQ_SET_DESCRIPTOR:
+ debug("fotg210: set_descriptor\n");
+ ret = CX_STALL;
+ break;
+
+ case USB_REQ_SYNCH_FRAME:
+ debug("fotg210: sync frame\n");
+ ret = CX_STALL;
+ break;
+ }
+ } /* if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) */
+
+ if (ret == CX_IDLE && chip->driver->setup) {
+ if (chip->driver->setup(&chip->gadget, req) < 0)
+ ret = CX_STALL;
+ else
+ ret = CX_FINISH;
+ }
+
+ switch (ret) {
+ case CX_FINISH:
+ setbits_le32(&regs->cxfifo, CXFIFO_CXFIN);
+ break;
+
+ case CX_STALL:
+ setbits_le32(&regs->cxfifo, CXFIFO_CXSTALL | CXFIFO_CXFIN);
+ printf("fotg210: cx_stall!\n");
+ break;
+
+ case CX_IDLE:
+ debug("fotg210: cx_idle?\n");
+ default:
+ break;
+ }
+}
+
+/*
+ * fifo - FIFO id
+ * zlp - zero length packet
+ */
+static void fotg210_recv(struct fotg210_chip *chip, int ep_id)
+{
+ struct fotg210_regs *regs = chip->regs;
+ struct fotg210_ep *ep = chip->ep + ep_id;
+ struct fotg210_request *req;
+ int len;
+
+ if (ep->stopped || (ep->desc->bEndpointAddress & USB_DIR_IN)) {
+ printf("fotg210: ep%d recv, invalid!\n", ep->id);
+ return;
+ }
+
+ if (list_empty(&ep->queue)) {
+ printf("fotg210: ep%d recv, drop!\n", ep->id);
+ return;
+ }
+
+ req = list_first_entry(&ep->queue, struct fotg210_request, queue);
+ len = fotg210_dma(ep, req);
+ if (len < ep->ep.maxpacket || req->req.length <= req->req.actual) {
+ list_del_init(&req->queue);
+ if (req->req.complete)
+ req->req.complete(&ep->ep, &req->req);
+ }
+
+ if (ep->id > 0 && list_empty(&ep->queue)) {
+ setbits_le32(&regs->gimr1,
+ GIMR1_FIFO_RX(ep_to_fifo(chip, ep->id)));
+ }
+}
+
+/*
+ * USB Gadget Layer
+ */
+static int fotg210_ep_enable(
+ struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+ struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep);
+ struct fotg210_chip *chip = ep->chip;
+ struct fotg210_regs *regs = chip->regs;
+ int id = ep_to_fifo(chip, ep->id);
+ int in = (desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0;
+
+ if (!_ep || !desc
+ || desc->bDescriptorType != USB_DT_ENDPOINT
+ || le16_to_cpu(desc->wMaxPacketSize) == 0) {
+ printf("fotg210: bad ep or descriptor\n");
+ return -EINVAL;
+ }
+
+ ep->desc = desc;
+ ep->stopped = 0;
+
+ if (in)
+ setbits_le32(&regs->fifomap, FIFOMAP(id, FIFOMAP_IN));
+
+ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ return -EINVAL;
+
+ case USB_ENDPOINT_XFER_ISOC:
+ setbits_le32(&regs->fifocfg,
+ FIFOCFG(id, FIFOCFG_EN | FIFOCFG_ISOC));
+ break;
+
+ case USB_ENDPOINT_XFER_BULK:
+ setbits_le32(&regs->fifocfg,
+ FIFOCFG(id, FIFOCFG_EN | FIFOCFG_BULK));
+ break;
+
+ case USB_ENDPOINT_XFER_INT:
+ setbits_le32(&regs->fifocfg,
+ FIFOCFG(id, FIFOCFG_EN | FIFOCFG_INTR));
+ break;
+ }
+
+ return 0;
+}
+
+static int fotg210_ep_disable(struct usb_ep *_ep)
+{
+ struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep);
+ struct fotg210_chip *chip = ep->chip;
+ struct fotg210_regs *regs = chip->regs;
+ int id = ep_to_fifo(chip, ep->id);
+
+ ep->desc = NULL;
+ ep->stopped = 1;
+
+ clrbits_le32(&regs->fifocfg, FIFOCFG(id, FIFOCFG_CFG_MASK));
+ clrbits_le32(&regs->fifomap, FIFOMAP(id, FIFOMAP_DIR_MASK));
+
+ return 0;
+}
+
+static struct usb_request *fotg210_ep_alloc_request(
+ struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct fotg210_request *req = malloc(sizeof(*req));
+
+ if (req) {
+ memset(req, 0, sizeof(*req));
+ INIT_LIST_HEAD(&req->queue);
+ }
+ return &req->req;
+}
+
+static void fotg210_ep_free_request(
+ struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct fotg210_request *req;
+
+ req = container_of(_req, struct fotg210_request, req);
+ free(req);
+}
+
+static int fotg210_ep_queue(
+ struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep);
+ struct fotg210_chip *chip = ep->chip;
+ struct fotg210_regs *regs = chip->regs;
+ struct fotg210_request *req;
+
+ req = container_of(_req, struct fotg210_request, req);
+ if (!_req || !_req->complete || !_req->buf
+ || !list_empty(&req->queue)) {
+ printf("fotg210: invalid request to ep%d\n", ep->id);
+ return -EINVAL;
+ }
+
+ if (!chip || chip->state == USB_STATE_SUSPENDED) {
+ printf("fotg210: request while chip suspended\n");
+ return -EINVAL;
+ }
+
+ req->req.actual = 0;
+ req->req.status = -EINPROGRESS;
+
+ if (req->req.length == 0) {
+ req->req.status = 0;
+ if (req->req.complete)
+ req->req.complete(&ep->ep, &req->req);
+ return 0;
+ }
+
+ if (ep->id == 0) {
+ do {
+ int len = fotg210_dma(ep, req);
+ if (len < ep->ep.maxpacket)
+ break;
+ if (ep->desc->bEndpointAddress & USB_DIR_IN)
+ udelay(100);
+ } while (req->req.length > req->req.actual);
+ } else {
+ if (ep->desc->bEndpointAddress & USB_DIR_IN) {
+ do {
+ int len = fotg210_dma(ep, req);
+ if (len < ep->ep.maxpacket)
+ break;
+ } while (req->req.length > req->req.actual);
+ } else {
+ list_add_tail(&req->queue, &ep->queue);
+ clrbits_le32(&regs->gimr1,
+ GIMR1_FIFO_RX(ep_to_fifo(chip, ep->id)));
+ }
+ }
+
+ if (ep->id == 0 || (ep->desc->bEndpointAddress & USB_DIR_IN)) {
+ if (req->req.complete)
+ req->req.complete(&ep->ep, &req->req);
+ }
+
+ return 0;
+}
+
+static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep);
+ struct fotg210_request *req;
+
+ /* make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req)
+ return -EINVAL;
+
+ /* remove the request */
+ list_del_init(&req->queue);
+
+ /* update status & invoke complete callback */
+ if (req->req.status == -EINPROGRESS) {
+ req->req.status = -ECONNRESET;
+ if (req->req.complete)
+ req->req.complete(_ep, &req->req);
+ }
+
+ return 0;
+}
+
+static int fotg210_ep_halt(struct usb_ep *_ep, int halt)
+{
+ struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep);
+ struct fotg210_chip *chip = ep->chip;
+ struct fotg210_regs *regs = chip->regs;
+ int ret = -1;
+
+ debug("fotg210: ep%d halt=%d\n", ep->id, halt);
+
+ /* Endpoint STALL */
+ if (ep->id > 0 && ep->id <= CFG_NUM_ENDPOINTS) {
+ if (halt) {
+ /* wait until all ep fifo empty */
+ fotg210_cxwait(chip, 0xf00);
+ /* stall */
+ if (ep->desc->bEndpointAddress & USB_DIR_IN) {
+ setbits_le32(&regs->iep[ep->id - 1],
+ IEP_STALL);
+ } else {
+ setbits_le32(&regs->oep[ep->id - 1],
+ OEP_STALL);
+ }
+ } else {
+ if (ep->desc->bEndpointAddress & USB_DIR_IN) {
+ clrbits_le32(&regs->iep[ep->id - 1],
+ IEP_STALL);
+ } else {
+ clrbits_le32(&regs->oep[ep->id - 1],
+ OEP_STALL);
+ }
+ }
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * activate/deactivate link with host.
+ */
+static void pullup(struct fotg210_chip *chip, int is_on)
+{
+ struct fotg210_regs *regs = chip->regs;
+
+ if (is_on) {
+ if (!chip->pullup) {
+ chip->state = USB_STATE_POWERED;
+ chip->pullup = 1;
+ /* enable the chip */
+ setbits_le32(&regs->dev_ctrl, DEVCTRL_EN);
+ /* clear unplug bit (BIT0) */
+ clrbits_le32(&regs->phy_tmsr, PHYTMSR_UNPLUG);
+ }
+ } else {
+ chip->state = USB_STATE_NOTATTACHED;
+ chip->pullup = 0;
+ chip->addr = 0;
+ writel(chip->addr, &regs->dev_addr);
+ /* set unplug bit (BIT0) */
+ setbits_le32(&regs->phy_tmsr, PHYTMSR_UNPLUG);
+ /* disable the chip */
+ clrbits_le32(&regs->dev_ctrl, DEVCTRL_EN);
+ }
+}
+
+static int fotg210_pullup(struct usb_gadget *_gadget, int is_on)
+{
+ struct fotg210_chip *chip;
+
+ chip = container_of(_gadget, struct fotg210_chip, gadget);
+
+ debug("fotg210: pullup=%d\n", is_on);
+
+ pullup(chip, is_on);
+
+ return 0;
+}
+
+static int fotg210_get_frame(struct usb_gadget *_gadget)
+{
+ struct fotg210_chip *chip;
+ struct fotg210_regs *regs;
+
+ chip = container_of(_gadget, struct fotg210_chip, gadget);
+ regs = chip->regs;
+
+ return SOFFNR_FNR(readl(&regs->sof_fnr));
+}
+
+static struct usb_gadget_ops fotg210_gadget_ops = {
+ .get_frame = fotg210_get_frame,
+ .pullup = fotg210_pullup,
+};
+
+static struct usb_ep_ops fotg210_ep_ops = {
+ .enable = fotg210_ep_enable,
+ .disable = fotg210_ep_disable,
+ .queue = fotg210_ep_queue,
+ .dequeue = fotg210_ep_dequeue,
+ .set_halt = fotg210_ep_halt,
+ .alloc_request = fotg210_ep_alloc_request,
+ .free_request = fotg210_ep_free_request,
+};
+
+static struct fotg210_chip controller = {
+ .regs = (void __iomem *)CONFIG_FOTG210_BASE,
+ .gadget = {
+ .name = "fotg210_udc",
+ .ops = &fotg210_gadget_ops,
+ .ep0 = &controller.ep[0].ep,
+ .speed = USB_SPEED_UNKNOWN,
+ .is_dualspeed = 1,
+ .is_otg = 0,
+ .is_a_peripheral = 0,
+ .b_hnp_enable = 0,
+ .a_hnp_support = 0,
+ .a_alt_hnp_support = 0,
+ },
+ .ep[0] = {
+ .id = 0,
+ .ep = {
+ .name = "ep0",
+ .ops = &fotg210_ep_ops,
+ },
+ .desc = &ep0_desc,
+ .chip = &controller,
+ .maxpacket = CFG_EP0_MAX_PACKET_SIZE,
+ },
+ .ep[1] = {
+ .id = 1,
+ .ep = {
+ .name = "ep1",
+ .ops = &fotg210_ep_ops,
+ },
+ .chip = &controller,
+ .maxpacket = CFG_EPX_MAX_PACKET_SIZE,
+ },
+ .ep[2] = {
+ .id = 2,
+ .ep = {
+ .name = "ep2",
+ .ops = &fotg210_ep_ops,
+ },
+ .chip = &controller,
+ .maxpacket = CFG_EPX_MAX_PACKET_SIZE,
+ },
+ .ep[3] = {
+ .id = 3,
+ .ep = {
+ .name = "ep3",
+ .ops = &fotg210_ep_ops,
+ },
+ .chip = &controller,
+ .maxpacket = CFG_EPX_MAX_PACKET_SIZE,
+ },
+ .ep[4] = {
+ .id = 4,
+ .ep = {
+ .name = "ep4",
+ .ops = &fotg210_ep_ops,
+ },
+ .chip = &controller,
+ .maxpacket = CFG_EPX_MAX_PACKET_SIZE,
+ },
+};
+
+int usb_gadget_handle_interrupts(void)
+{
+ struct fotg210_chip *chip = &controller;
+ struct fotg210_regs *regs = chip->regs;
+ uint32_t id, st, isr, gisr;
+
+ isr = readl(&regs->isr) & (~readl(&regs->imr));
+ gisr = readl(&regs->gisr) & (~readl(&regs->gimr));
+ if (!(isr & ISR_DEV) || !gisr)
+ return 0;
+
+ writel(ISR_DEV, &regs->isr);
+
+ /* CX interrupts */
+ if (gisr & GISR_GRP0) {
+ st = readl(&regs->gisr0);
+ /*
+ * Write 1 and then 0 works for both W1C & RW.
+ *
+ * HW v1.11.0+: It's a W1C register (write 1 clear)
+ * HW v1.10.0-: It's a R/W register (write 0 clear)
+ */
+ writel(st & GISR0_CXABORT, &regs->gisr0);
+ writel(0, &regs->gisr0);
+
+ if (st & GISR0_CXERR)
+ printf("fotg210: cmd error\n");
+
+ if (st & GISR0_CXABORT)
+ printf("fotg210: cmd abort\n");
+
+ if (st & GISR0_CXSETUP) /* setup */
+ fotg210_setup(chip);
+ else if (st & GISR0_CXEND) /* command finish */
+ setbits_le32(&regs->cxfifo, CXFIFO_CXFIN);
+ }
+
+ /* FIFO interrupts */
+ if (gisr & GISR_GRP1) {
+ st = readl(&regs->gisr1);
+ for (id = 0; id < 4; ++id) {
+ if (st & GISR1_RX_FIFO(id))
+ fotg210_recv(chip, fifo_to_ep(chip, id, 0));
+ }
+ }
+
+ /* Device Status Interrupts */
+ if (gisr & GISR_GRP2) {
+ st = readl(&regs->gisr2);
+ /*
+ * Write 1 and then 0 works for both W1C & RW.
+ *
+ * HW v1.11.0+: It's a W1C register (write 1 clear)
+ * HW v1.10.0-: It's a R/W register (write 0 clear)
+ */
+ writel(st, &regs->gisr2);
+ writel(0, &regs->gisr2);
+
+ if (st & GISR2_RESET)
+ printf("fotg210: reset by host\n");
+ else if (st & GISR2_SUSPEND)
+ printf("fotg210: suspend/removed\n");
+ else if (st & GISR2_RESUME)
+ printf("fotg210: resume\n");
+
+ /* Errors */
+ if (st & GISR2_ISOCERR)
+ printf("fotg210: iso error\n");
+ if (st & GISR2_ISOCABT)
+ printf("fotg210: iso abort\n");
+ if (st & GISR2_DMAERR)
+ printf("fotg210: dma error\n");
+ }
+
+ return 0;
+}
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ int i, ret = 0;
+ struct fotg210_chip *chip = &controller;
+
+ if (!driver || !driver->bind || !driver->setup) {
+ puts("fotg210: bad parameter.\n");
+ return -EINVAL;
+ }
+
+ INIT_LIST_HEAD(&chip->gadget.ep_list);
+ for (i = 0; i < CFG_NUM_ENDPOINTS + 1; ++i) {
+ struct fotg210_ep *ep = chip->ep + i;
+
+ ep->ep.maxpacket = ep->maxpacket;
+ INIT_LIST_HEAD(&ep->queue);
+
+ if (ep->id == 0) {
+ ep->stopped = 0;
+ } else {
+ ep->stopped = 1;
+ list_add_tail(&ep->ep.ep_list, &chip->gadget.ep_list);
+ }
+ }
+
+ if (fotg210_reset(chip)) {
+ puts("fotg210: reset failed.\n");
+ return -EINVAL;
+ }
+
+ ret = driver->bind(&chip->gadget);
+ if (ret) {
+ debug("fotg210: driver->bind() returned %d\n", ret);
+ return ret;
+ }
+ chip->driver = driver;
+
+ return ret;
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct fotg210_chip *chip = &controller;
+
+ driver->unbind(&chip->gadget);
+ chip->driver = NULL;
+
+ pullup(chip, 0);
+
+ return 0;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/g_dnl.c b/src/roms/u-boot/drivers/usb/gadget/g_dnl.c
new file mode 100644
index 0000000..25611ac
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/g_dnl.c
@@ -0,0 +1,264 @@
+/*
+ * g_dnl.c -- USB Downloader Gadget
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+
+#include <mmc.h>
+#include <part.h>
+
+#include <g_dnl.h>
+#include <usb_mass_storage.h>
+#include <dfu.h>
+#include <thor.h>
+
+#include "gadget_chips.h"
+#include "composite.c"
+
+/*
+ * One needs to define the following:
+ * CONFIG_G_DNL_VENDOR_NUM
+ * CONFIG_G_DNL_PRODUCT_NUM
+ * CONFIG_G_DNL_MANUFACTURER
+ * at e.g. ./include/configs/<board>.h
+ */
+
+#define STRING_MANUFACTURER 25
+#define STRING_PRODUCT 2
+/* Index of String Descriptor describing this configuration */
+#define STRING_USBDOWN 2
+/* Index of String serial */
+#define STRING_SERIAL 3
+#define MAX_STRING_SERIAL 32
+/* Number of supported configurations */
+#define CONFIGURATION_NUMBER 1
+
+#define DRIVER_VERSION "usb_dnl 2.0"
+
+static const char product[] = "USB download gadget";
+static char g_dnl_serial[MAX_STRING_SERIAL];
+static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER;
+
+void g_dnl_set_serialnumber(char *s)
+{
+ memset(g_dnl_serial, 0, MAX_STRING_SERIAL);
+ if (strlen(s) < MAX_STRING_SERIAL)
+ strncpy(g_dnl_serial, s, strlen(s));
+}
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_COMM,
+ .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/
+
+ .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM),
+ .iProduct = STRING_PRODUCT,
+ .iSerialNumber = STRING_SERIAL,
+ .bNumConfigurations = 1,
+};
+
+/*
+ * static strings, in UTF-8
+ * IDs for those strings are assigned dynamically at g_dnl_bind()
+ */
+static struct usb_string g_dnl_string_defs[] = {
+ {.s = manufacturer},
+ {.s = product},
+ {.s = g_dnl_serial},
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings g_dnl_string_tab = {
+ .language = 0x0409, /* en-us */
+ .strings = g_dnl_string_defs,
+};
+
+static struct usb_gadget_strings *g_dnl_composite_strings[] = {
+ &g_dnl_string_tab,
+ NULL,
+};
+
+static int g_dnl_unbind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+
+ free(cdev->config);
+ cdev->config = NULL;
+ debug("%s: calling usb_gadget_disconnect for "
+ "controller '%s'\n", __func__, gadget->name);
+ usb_gadget_disconnect(gadget);
+
+ return 0;
+}
+
+static inline struct g_dnl_bind_callback *g_dnl_bind_callback_first(void)
+{
+ return ll_entry_start(struct g_dnl_bind_callback,
+ g_dnl_bind_callbacks);
+}
+
+static inline struct g_dnl_bind_callback *g_dnl_bind_callback_end(void)
+{
+ return ll_entry_end(struct g_dnl_bind_callback,
+ g_dnl_bind_callbacks);
+}
+
+static int g_dnl_do_config(struct usb_configuration *c)
+{
+ const char *s = c->cdev->driver->name;
+ struct g_dnl_bind_callback *callback = g_dnl_bind_callback_first();
+
+ debug("%s: configuration: 0x%p composite dev: 0x%p\n",
+ __func__, c, c->cdev);
+
+ for (; callback != g_dnl_bind_callback_end(); callback++)
+ if (!strcmp(s, callback->usb_function_name))
+ return callback->fptr(c);
+ return -ENODEV;
+}
+
+static int g_dnl_config_register(struct usb_composite_dev *cdev)
+{
+ struct usb_configuration *config;
+ const char *name = "usb_dnload";
+
+ config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config));
+ if (!config)
+ return -ENOMEM;
+
+ memset(config, 0, sizeof(*config));
+
+ config->label = name;
+ config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
+ config->bConfigurationValue = CONFIGURATION_NUMBER;
+ config->iConfiguration = STRING_USBDOWN;
+ config->bind = g_dnl_do_config;
+
+ return usb_add_config(cdev, config);
+}
+
+__weak
+int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
+{
+ return 0;
+}
+
+__weak int g_dnl_get_board_bcd_device_number(int gcnum)
+{
+ return gcnum;
+}
+
+__weak int g_dnl_board_usb_cable_connected(void)
+{
+ return -EOPNOTSUPP;
+}
+
+static int g_dnl_get_bcd_device_number(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ int gcnum;
+
+ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum > 0)
+ gcnum += 0x200;
+
+ return g_dnl_get_board_bcd_device_number(gcnum);
+}
+
+static int g_dnl_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ int id, ret;
+ int gcnum;
+
+ debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
+
+ id = usb_string_id(cdev);
+
+ if (id < 0)
+ return id;
+ g_dnl_string_defs[0].id = id;
+ device_desc.iManufacturer = id;
+
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+
+ g_dnl_string_defs[1].id = id;
+ device_desc.iProduct = id;
+
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+
+ g_dnl_string_defs[2].id = id;
+ device_desc.iSerialNumber = id;
+
+ g_dnl_bind_fixup(&device_desc, cdev->driver->name);
+ ret = g_dnl_config_register(cdev);
+ if (ret)
+ goto error;
+
+ gcnum = g_dnl_get_bcd_device_number(cdev);
+ if (gcnum >= 0)
+ device_desc.bcdDevice = cpu_to_le16(gcnum);
+ else {
+ debug("%s: controller '%s' not recognized\n",
+ __func__, gadget->name);
+ device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+ }
+
+ debug("%s: calling usb_gadget_connect for "
+ "controller '%s'\n", __func__, gadget->name);
+ usb_gadget_connect(gadget);
+
+ return 0;
+
+ error:
+ g_dnl_unbind(cdev);
+ return -ENOMEM;
+}
+
+static struct usb_composite_driver g_dnl_driver = {
+ .name = NULL,
+ .dev = &device_desc,
+ .strings = g_dnl_composite_strings,
+
+ .bind = g_dnl_bind,
+ .unbind = g_dnl_unbind,
+};
+
+/*
+ * NOTICE:
+ * Registering via USB function name won't be necessary after rewriting
+ * g_dnl to support multiple USB functions.
+ */
+int g_dnl_register(const char *name)
+{
+ int ret;
+
+ debug("%s: g_dnl_driver.name = %s\n", __func__, name);
+ g_dnl_driver.name = name;
+
+ ret = usb_composite_register(&g_dnl_driver);
+ if (ret) {
+ printf("%s: failed!, error: %d\n", __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+void g_dnl_unregister(void)
+{
+ usb_composite_unregister(&g_dnl_driver);
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/gadget_chips.h b/src/roms/u-boot/drivers/usb/gadget/gadget_chips.h
new file mode 100644
index 0000000..cc94771
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/gadget_chips.h
@@ -0,0 +1,227 @@
+/*
+ * USB device controllers have lots of quirks. Use these macros in
+ * gadget drivers or other code that needs to deal with them, and which
+ * autoconfigures instead of using early binding to the hardware.
+ *
+ * This SHOULD eventually work like the ARM mach_is_*() stuff, driven by
+ * some config file that gets updated as new hardware is supported.
+ * (And avoiding all runtime comparisons in typical one-choice configs!)
+ *
+ * NOTE: some of these controller drivers may not be available yet.
+ * Some are available on 2.4 kernels; several are available, but not
+ * yet pushed in the 2.6 mainline tree.
+ *
+ * Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and
+ * Remy Bohmer <linux@bohmer.net>
+ */
+#ifdef CONFIG_USB_GADGET_NET2280
+#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name))
+#else
+#define gadget_is_net2280(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+#define gadget_is_amd5536udc(g) (!strcmp("amd5536udc", (g)->name))
+#else
+#define gadget_is_amd5536udc(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_DUMMY_HCD
+#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name))
+#else
+#define gadget_is_dummy(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_PXA2XX
+#define gadget_is_pxa(g) (!strcmp("pxa2xx_udc", (g)->name))
+#else
+#define gadget_is_pxa(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_GOKU
+#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
+#else
+#define gadget_is_goku(g) 0
+#endif
+
+/* SH3 UDC -- not yet ported 2.4 --> 2.6 */
+#ifdef CONFIG_USB_GADGET_SUPERH
+#define gadget_is_sh(g) (!strcmp("sh_udc", (g)->name))
+#else
+#define gadget_is_sh(g) 0
+#endif
+
+/* not yet stable on 2.6 (would help "original Zaurus") */
+#ifdef CONFIG_USB_GADGET_SA1100
+#define gadget_is_sa1100(g) (!strcmp("sa1100_udc", (g)->name))
+#else
+#define gadget_is_sa1100(g) 0
+#endif
+
+/* handhelds.org tree (?) */
+#ifdef CONFIG_USB_GADGET_MQ11XX
+#define gadget_is_mq11xx(g) (!strcmp("mq11xx_udc", (g)->name))
+#else
+#define gadget_is_mq11xx(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_OMAP
+#define gadget_is_omap(g) (!strcmp("omap_udc", (g)->name))
+#else
+#define gadget_is_omap(g) 0
+#endif
+
+/* not yet ported 2.4 --> 2.6 */
+#ifdef CONFIG_USB_GADGET_N9604
+#define gadget_is_n9604(g) (!strcmp("n9604_udc", (g)->name))
+#else
+#define gadget_is_n9604(g) 0
+#endif
+
+/* various unstable versions available */
+#ifdef CONFIG_USB_GADGET_PXA27X
+#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name))
+#else
+#define gadget_is_pxa27x(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_ATMEL_USBA
+#define gadget_is_atmel_usba(g) (!strcmp("atmel_usba_udc", (g)->name))
+#else
+#define gadget_is_atmel_usba(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_S3C2410
+#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name))
+#else
+#define gadget_is_s3c2410(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_AT91
+#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name))
+#else
+#define gadget_is_at91(g) 0
+#endif
+
+/* status unclear */
+#ifdef CONFIG_USB_GADGET_IMX
+#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name))
+#else
+#define gadget_is_imx(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_FSL_USB2
+#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name))
+#else
+#define gadget_is_fsl_usb2(g) 0
+#endif
+
+/* Mentor high speed function controller */
+/* from Montavista kernel (?) */
+#ifdef CONFIG_USB_GADGET_MUSBHSFC
+#define gadget_is_musbhsfc(g) (!strcmp("musbhsfc_udc", (g)->name))
+#else
+#define gadget_is_musbhsfc(g) 0
+#endif
+
+/* Mentor high speed "dual role" controller, in peripheral role */
+#ifdef CONFIG_MUSB_GADGET
+#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
+#else
+#define gadget_is_musbhdrc(g) 0
+#endif
+
+/* from Montavista kernel (?) */
+#ifdef CONFIG_USB_GADGET_MPC8272
+#define gadget_is_mpc8272(g) (!strcmp("mpc8272_udc", (g)->name))
+#else
+#define gadget_is_mpc8272(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_M66592
+#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name))
+#else
+#define gadget_is_m66592(g) 0
+#endif
+
+#ifdef CONFIG_CI_UDC
+#define gadget_is_ci(g) (!strcmp("ci_udc", (g)->name))
+#else
+#define gadget_is_ci(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_FOTG210
+#define gadget_is_fotg210(g) (!strcmp("fotg210_udc", (g)->name))
+#else
+#define gadget_is_fotg210(g) 0
+#endif
+
+/*
+ * CONFIG_USB_GADGET_SX2
+ * CONFIG_USB_GADGET_AU1X00
+ * ...
+ */
+
+/**
+ * usb_gadget_controller_number - support bcdDevice id convention
+ * @gadget: the controller being driven
+ *
+ * Return a 2-digit BCD value associated with the peripheral controller,
+ * suitable for use as part of a bcdDevice value, or a negative error code.
+ *
+ * NOTE: this convention is purely optional, and has no meaning in terms of
+ * any USB specification. If you want to use a different convention in your
+ * gadget driver firmware -- maybe a more formal revision ID -- feel free.
+ *
+ * Hosts see these bcdDevice numbers, and are allowed (but not encouraged!)
+ * to change their behavior accordingly. For example it might help avoiding
+ * some chip bug.
+ */
+static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
+{
+ if (gadget_is_net2280(gadget))
+ return 0x01;
+ else if (gadget_is_dummy(gadget))
+ return 0x02;
+ else if (gadget_is_pxa(gadget))
+ return 0x03;
+ else if (gadget_is_sh(gadget))
+ return 0x04;
+ else if (gadget_is_sa1100(gadget))
+ return 0x05;
+ else if (gadget_is_goku(gadget))
+ return 0x06;
+ else if (gadget_is_mq11xx(gadget))
+ return 0x07;
+ else if (gadget_is_omap(gadget))
+ return 0x08;
+ else if (gadget_is_n9604(gadget))
+ return 0x09;
+ else if (gadget_is_pxa27x(gadget))
+ return 0x10;
+ else if (gadget_is_s3c2410(gadget))
+ return 0x11;
+ else if (gadget_is_at91(gadget))
+ return 0x12;
+ else if (gadget_is_imx(gadget))
+ return 0x13;
+ else if (gadget_is_musbhsfc(gadget))
+ return 0x14;
+ else if (gadget_is_musbhdrc(gadget))
+ return 0x15;
+ else if (gadget_is_mpc8272(gadget))
+ return 0x16;
+ else if (gadget_is_atmel_usba(gadget))
+ return 0x17;
+ else if (gadget_is_fsl_usb2(gadget))
+ return 0x18;
+ else if (gadget_is_amd5536udc(gadget))
+ return 0x19;
+ else if (gadget_is_m66592(gadget))
+ return 0x20;
+ else if (gadget_is_ci(gadget))
+ return 0x21;
+ else if (gadget_is_fotg210(gadget))
+ return 0x22;
+ return -ENOENT;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/mpc8xx_udc.c b/src/roms/u-boot/drivers/usb/gadget/mpc8xx_udc.c
new file mode 100644
index 0000000..7f72972
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/mpc8xx_udc.c
@@ -0,0 +1,1386 @@
+/*
+ * Copyright (C) 2006 by Bryan O'Donoghue, CodeHermit
+ * bodonoghue@CodeHermit.ie
+ *
+ * References
+ * DasUBoot/drivers/usb/gadget/omap1510_udc.c, for design and implementation
+ * ideas.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * Notes :
+ * 1. #define __SIMULATE_ERROR__ to inject a CRC error into every 2nd TX
+ * packet to force the USB re-transmit protocol.
+ *
+ * 2. #define __DEBUG_UDC__ to switch on debug tracing to serial console
+ * be careful that tracing doesn't create Hiesen-bugs with respect to
+ * response timeouts to control requests.
+ *
+ * 3. This driver should be able to support any higher level driver that
+ * that wants to do either of the two standard UDC implementations
+ * Control-Bulk-Interrupt or Bulk-IN/Bulk-Out standards. Hence
+ * gserial and cdc_acm should work with this code.
+ *
+ * 4. NAK events never actually get raised at all, the documentation
+ * is just wrong !
+ *
+ * 5. For some reason, cbd_datlen is *always* +2 the value it should be.
+ * this means that having an RX cbd of 16 bytes is not possible, since
+ * the same size is reported for 14 bytes received as 16 bytes received
+ * until we can find out why this happens, RX cbds must be limited to 8
+ * bytes. TODO: check errata for this behaviour.
+ *
+ * 6. Right now this code doesn't support properly powering up with the USB
+ * cable attached to the USB host my development board the Adder87x doesn't
+ * have a pull-up fitted to allow this, so it is necessary to power the
+ * board and *then* attached the USB cable to the host. However somebody
+ * with a different design in their board may be able to keep the cable
+ * constantly connected and simply enable/disable a pull-up re
+ * figure 31.1 in MPC885RM.pdf instead of having to power up the board and
+ * then attach the cable !
+ *
+ */
+#include <common.h>
+#include <config.h>
+#include <commproc.h>
+#include <usbdevice.h>
+#include <usb/mpc8xx_udc.h>
+#include <usb/udc.h>
+
+#include "ep0.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define ERR(fmt, args...)\
+ serial_printf("ERROR : [%s] %s:%d: "fmt,\
+ __FILE__,__FUNCTION__,__LINE__, ##args)
+#ifdef __DEBUG_UDC__
+#define DBG(fmt,args...)\
+ serial_printf("[%s] %s:%d: "fmt,\
+ __FILE__,__FUNCTION__,__LINE__, ##args)
+#else
+#define DBG(fmt,args...)
+#endif
+
+/* Static Data */
+#ifdef __SIMULATE_ERROR__
+static char err_poison_test = 0;
+#endif
+static struct mpc8xx_ep ep_ref[MAX_ENDPOINTS];
+static u32 address_base = STATE_NOT_READY;
+static mpc8xx_udc_state_t udc_state = 0;
+static struct usb_device_instance *udc_device = 0;
+static volatile usb_epb_t *endpoints[MAX_ENDPOINTS];
+static volatile cbd_t *tx_cbd[TX_RING_SIZE];
+static volatile cbd_t *rx_cbd[RX_RING_SIZE];
+static volatile immap_t *immr = 0;
+static volatile cpm8xx_t *cp = 0;
+static volatile usb_pram_t *usb_paramp = 0;
+static volatile usb_t *usbp = 0;
+static int rx_ct = 0;
+static int tx_ct = 0;
+
+/* Static Function Declarations */
+static void mpc8xx_udc_state_transition_up (usb_device_state_t initial,
+ usb_device_state_t final);
+static void mpc8xx_udc_state_transition_down (usb_device_state_t initial,
+ usb_device_state_t final);
+static void mpc8xx_udc_stall (unsigned int ep);
+static void mpc8xx_udc_flush_tx_fifo (int epid);
+static void mpc8xx_udc_flush_rx_fifo (void);
+static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi,
+ struct urb *tx_urb);
+static void mpc8xx_udc_dump_request (struct usb_device_request *request);
+static void mpc8xx_udc_clock_init (volatile immap_t * immr,
+ volatile cpm8xx_t * cp);
+static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi);
+static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_cbd_init (void);
+static void mpc8xx_udc_endpoint_init (void);
+static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size);
+static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment);
+static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_set_nak (unsigned int ep);
+static short mpc8xx_udc_handle_txerr (void);
+static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid);
+
+/******************************************************************************
+ Global Linkage
+ *****************************************************************************/
+
+/* udc_init
+ *
+ * Do initial bus gluing
+ */
+int udc_init (void)
+{
+ /* Init various pointers */
+ immr = (immap_t *) CONFIG_SYS_IMMR;
+ cp = (cpm8xx_t *) & (immr->im_cpm);
+ usb_paramp = (usb_pram_t *) & (cp->cp_dparam[PROFF_USB]);
+ usbp = (usb_t *) & (cp->cp_scc[0]);
+
+ memset (ep_ref, 0x00, (sizeof (struct mpc8xx_ep) * MAX_ENDPOINTS));
+
+ udc_device = 0;
+ udc_state = STATE_NOT_READY;
+
+ usbp->usmod = 0x00;
+ usbp->uscom = 0;
+
+ /* Set USB Frame #0, Respond at Address & Get a clock source */
+ usbp->usaddr = 0x00;
+ mpc8xx_udc_clock_init (immr, cp);
+
+ /* PA15, PA14 as perhiperal USBRXD and USBOE */
+ immr->im_ioport.iop_padir &= ~0x0003;
+ immr->im_ioport.iop_papar |= 0x0003;
+
+ /* PC11/PC10 as peripheral USBRXP USBRXN */
+ immr->im_ioport.iop_pcso |= 0x0030;
+
+ /* PC7/PC6 as perhiperal USBTXP and USBTXN */
+ immr->im_ioport.iop_pcdir |= 0x0300;
+ immr->im_ioport.iop_pcpar |= 0x0300;
+
+ /* Set the base address */
+ address_base = (u32) (cp->cp_dpmem + CPM_USB_BASE);
+
+ /* Initialise endpoints and circular buffers */
+ mpc8xx_udc_endpoint_init ();
+ mpc8xx_udc_cbd_init ();
+
+ /* Assign allocated Dual Port Endpoint descriptors */
+ usb_paramp->ep0ptr = (u32) endpoints[0];
+ usb_paramp->ep1ptr = (u32) endpoints[1];
+ usb_paramp->ep2ptr = (u32) endpoints[2];
+ usb_paramp->ep3ptr = (u32) endpoints[3];
+ usb_paramp->frame_n = 0;
+
+ DBG ("ep0ptr=0x%08x ep1ptr=0x%08x ep2ptr=0x%08x ep3ptr=0x%08x\n",
+ usb_paramp->ep0ptr, usb_paramp->ep1ptr, usb_paramp->ep2ptr,
+ usb_paramp->ep3ptr);
+
+ return 0;
+}
+
+/* udc_irq
+ *
+ * Poll for whatever events may have occured
+ */
+void udc_irq (void)
+{
+ int epid = 0;
+ volatile cbd_t *rx_cbdp = 0;
+ volatile cbd_t *rx_cbdp_base = 0;
+
+ if (udc_state != STATE_READY) {
+ return;
+ }
+
+ if (usbp->usber & USB_E_BSY) {
+ /* This shouldn't happen. If it does then it's a bug ! */
+ usbp->usber |= USB_E_BSY;
+ mpc8xx_udc_flush_rx_fifo ();
+ }
+
+ /* Scan all RX/Bidirectional Endpoints for RX data. */
+ for (epid = 0; epid < MAX_ENDPOINTS; epid++) {
+ if (!ep_ref[epid].prx) {
+ continue;
+ }
+ rx_cbdp = rx_cbdp_base = ep_ref[epid].prx;
+
+ do {
+ if (!(rx_cbdp->cbd_sc & RX_BD_E)) {
+
+ if (rx_cbdp->cbd_sc & 0x1F) {
+ /* Corrupt data discard it.
+ * Controller has NAK'd this packet.
+ */
+ mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+ } else {
+ if (!epid) {
+ mpc8xx_udc_ep0_rx (rx_cbdp);
+
+ } else {
+ /* Process data */
+ mpc8xx_udc_set_nak (epid);
+ mpc8xx_udc_epn_rx (epid, rx_cbdp);
+ mpc8xx_udc_clear_rxbd (rx_cbdp);
+ }
+ }
+
+ /* Advance RX CBD pointer */
+ mpc8xx_udc_advance_rx (&rx_cbdp, epid);
+ ep_ref[epid].prx = rx_cbdp;
+ } else {
+ /* Advance RX CBD pointer */
+ mpc8xx_udc_advance_rx (&rx_cbdp, epid);
+ }
+
+ } while (rx_cbdp != rx_cbdp_base);
+ }
+
+ /* Handle TX events as appropiate, the correct place to do this is
+ * in a tx routine. Perhaps TX on epn was pre-empted by ep0
+ */
+
+ if (usbp->usber & USB_E_TXB) {
+ usbp->usber |= USB_E_TXB;
+ }
+
+ if (usbp->usber & (USB_TX_ERRMASK)) {
+ mpc8xx_udc_handle_txerr ();
+ }
+
+ /* Switch to the default state, respond at the default address */
+ if (usbp->usber & USB_E_RESET) {
+ usbp->usber |= USB_E_RESET;
+ usbp->usaddr = 0x00;
+ udc_device->device_state = STATE_DEFAULT;
+ }
+
+ /* if(usbp->usber&USB_E_IDLE){
+ We could suspend here !
+ usbp->usber|=USB_E_IDLE;
+ DBG("idle state change\n");
+ }
+ if(usbp->usbs){
+ We could resume here when IDLE is deasserted !
+ Not worth doing, so long as we are self powered though.
+ }
+ */
+
+ return;
+}
+
+/* udc_endpoint_write
+ *
+ * Write some data to an endpoint
+ */
+int udc_endpoint_write (struct usb_endpoint_instance *epi)
+{
+ int ep = 0;
+ short epid = 1, unnak = 0, ret = 0;
+
+ if (udc_state != STATE_READY) {
+ ERR ("invalid udc_state != STATE_READY!\n");
+ return -1;
+ }
+
+ if (!udc_device || !epi) {
+ return -1;
+ }
+
+ if (udc_device->device_state != STATE_CONFIGURED) {
+ return -1;
+ }
+
+ ep = epi->endpoint_address & 0x03;
+ if (ep >= MAX_ENDPOINTS) {
+ return -1;
+ }
+
+ /* Set NAK for all RX endpoints during TX */
+ for (epid = 1; epid < MAX_ENDPOINTS; epid++) {
+
+ /* Don't set NAK on DATA IN/CONTROL endpoints */
+ if (ep_ref[epid].sc & USB_DIR_IN) {
+ continue;
+ }
+
+ if (!(usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK))) {
+ unnak |= 1 << epid;
+ }
+
+ mpc8xx_udc_set_nak (epid);
+ }
+
+ mpc8xx_udc_init_tx (&udc_device->bus->endpoint_array[ep],
+ epi->tx_urb);
+ ret = mpc8xx_udc_ep_tx (&udc_device->bus->endpoint_array[ep]);
+
+ /* Remove temporary NAK */
+ for (epid = 1; epid < MAX_ENDPOINTS; epid++) {
+ if (unnak & (1 << epid)) {
+ udc_unset_nak (epid);
+ }
+ }
+
+ return ret;
+}
+
+/* mpc8xx_udc_assign_urb
+ *
+ * Associate a given urb to an endpoint TX or RX transmit/receive buffers
+ */
+static int mpc8xx_udc_assign_urb (int ep, char direction)
+{
+ struct usb_endpoint_instance *epi = 0;
+
+ if (ep >= MAX_ENDPOINTS) {
+ goto err;
+ }
+ epi = &udc_device->bus->endpoint_array[ep];
+ if (!epi) {
+ goto err;
+ }
+
+ if (!ep_ref[ep].urb) {
+ ep_ref[ep].urb = usbd_alloc_urb (udc_device, udc_device->bus->endpoint_array);
+ if (!ep_ref[ep].urb) {
+ goto err;
+ }
+ } else {
+ ep_ref[ep].urb->actual_length = 0;
+ }
+
+ switch (direction) {
+ case USB_DIR_IN:
+ epi->tx_urb = ep_ref[ep].urb;
+ break;
+ case USB_DIR_OUT:
+ epi->rcv_urb = ep_ref[ep].urb;
+ break;
+ default:
+ goto err;
+ }
+ return 0;
+
+ err:
+ udc_state = STATE_ERROR;
+ return -1;
+}
+
+/* udc_setup_ep
+ *
+ * Associate U-Boot software endpoints to mpc8xx endpoint parameter ram
+ * Isochronous endpoints aren't yet supported!
+ */
+void udc_setup_ep (struct usb_device_instance *device, unsigned int ep,
+ struct usb_endpoint_instance *epi)
+{
+ uchar direction = 0;
+ int ep_attrib = 0;
+
+ if (epi && (ep < MAX_ENDPOINTS)) {
+
+ if (ep == 0) {
+ if (epi->rcv_attributes != USB_ENDPOINT_XFER_CONTROL
+ || epi->tx_attributes !=
+ USB_ENDPOINT_XFER_CONTROL) {
+
+ /* ep0 must be a control endpoint */
+ udc_state = STATE_ERROR;
+ return;
+
+ }
+ if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+ mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize,
+ epi->rcv_packetSize);
+ }
+ usbp->usep[ep] = 0x0000;
+ return;
+ }
+
+ if ((epi->endpoint_address & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN) {
+
+ direction = 1;
+ ep_attrib = epi->tx_attributes;
+ epi->rcv_packetSize = 0;
+ ep_ref[ep].sc |= USB_DIR_IN;
+ } else {
+
+ direction = 0;
+ ep_attrib = epi->rcv_attributes;
+ epi->tx_packetSize = 0;
+ ep_ref[ep].sc &= ~USB_DIR_IN;
+ }
+
+ if (mpc8xx_udc_assign_urb (ep, epi->endpoint_address
+ & USB_ENDPOINT_DIR_MASK)) {
+ return;
+ }
+
+ switch (ep_attrib) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+ mpc8xx_udc_cbd_attach (ep,
+ epi->tx_packetSize,
+ epi->rcv_packetSize);
+ }
+ usbp->usep[ep] = ep << 12;
+ epi->rcv_urb = epi->tx_urb = ep_ref[ep].urb;
+
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+ if (direction) {
+ mpc8xx_udc_cbd_attach (ep,
+ epi->tx_packetSize,
+ 0);
+ } else {
+ mpc8xx_udc_cbd_attach (ep,
+ 0,
+ epi->rcv_packetSize);
+ }
+ }
+ usbp->usep[ep] = (ep << 12) | ((ep_attrib) << 8);
+
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ default:
+ serial_printf ("Error endpoint attrib %d>3\n", ep_attrib);
+ udc_state = STATE_ERROR;
+ break;
+ }
+ }
+
+}
+
+/* udc_connect
+ *
+ * Move state, switch on the USB
+ */
+void udc_connect (void)
+{
+ /* Enable pull-up resistor on D+
+ * TODO: fit a pull-up resistor to drive SE0 for > 2.5us
+ */
+
+ if (udc_state != STATE_ERROR) {
+ udc_state = STATE_READY;
+ usbp->usmod |= USMOD_EN;
+ }
+}
+
+/* udc_disconnect
+ *
+ * Disconnect is not used but, is included for completeness
+ */
+void udc_disconnect (void)
+{
+ /* Disable pull-up resistor on D-
+ * TODO: fix a pullup resistor to control this
+ */
+
+ if (udc_state != STATE_ERROR) {
+ udc_state = STATE_NOT_READY;
+ }
+ usbp->usmod &= ~USMOD_EN;
+}
+
+/* udc_enable
+ *
+ * Grab an EP0 URB, register interest in a subset of USB events
+ */
+void udc_enable (struct usb_device_instance *device)
+{
+ if (udc_state == STATE_ERROR) {
+ return;
+ }
+
+ udc_device = device;
+
+ if (!ep_ref[0].urb) {
+ ep_ref[0].urb = usbd_alloc_urb (device, device->bus->endpoint_array);
+ }
+
+ /* Register interest in all events except SOF, enable transceiver */
+ usbp->usber = 0x03FF;
+ usbp->usbmr = 0x02F7;
+
+ return;
+}
+
+/* udc_disable
+ *
+ * disable the currently hooked device
+ */
+void udc_disable (void)
+{
+ int i = 0;
+
+ if (udc_state == STATE_ERROR) {
+ DBG ("Won't disable UDC. udc_state==STATE_ERROR !\n");
+ return;
+ }
+
+ udc_device = 0;
+
+ for (; i < MAX_ENDPOINTS; i++) {
+ if (ep_ref[i].urb) {
+ usbd_dealloc_urb (ep_ref[i].urb);
+ ep_ref[i].urb = 0;
+ }
+ }
+
+ usbp->usbmr = 0x00;
+ usbp->usmod = ~USMOD_EN;
+ udc_state = STATE_NOT_READY;
+}
+
+/* udc_startup_events
+ *
+ * Enable the specified device
+ */
+void udc_startup_events (struct usb_device_instance *device)
+{
+ udc_enable (device);
+ if (udc_state == STATE_READY) {
+ usbd_device_event_irq (device, DEVICE_CREATE, 0);
+ }
+}
+
+/* udc_set_nak
+ *
+ * Allow upper layers to signal lower layers should not accept more RX data
+ *
+ */
+void udc_set_nak (int epid)
+{
+ if (epid) {
+ mpc8xx_udc_set_nak (epid);
+ }
+}
+
+/* udc_unset_nak
+ *
+ * Suspend sending of NAK tokens for DATA OUT tokens on a given endpoint.
+ * Switch off NAKing on this endpoint to accept more data output from host.
+ *
+ */
+void udc_unset_nak (int epid)
+{
+ if (epid > MAX_ENDPOINTS) {
+ return;
+ }
+
+ if (usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK)) {
+ usbp->usep[epid] &= ~(USEP_THS_NAK | USEP_RHS_NAK);
+ __asm__ ("eieio");
+ }
+}
+
+/******************************************************************************
+ Static Linkage
+******************************************************************************/
+
+/* udc_state_transition_up
+ * udc_state_transition_down
+ *
+ * Helper functions to implement device state changes. The device states and
+ * the events that transition between them are:
+ *
+ * STATE_ATTACHED
+ * || /\
+ * \/ ||
+ * DEVICE_HUB_CONFIGURED DEVICE_HUB_RESET
+ * || /\
+ * \/ ||
+ * STATE_POWERED
+ * || /\
+ * \/ ||
+ * DEVICE_RESET DEVICE_POWER_INTERRUPTION
+ * || /\
+ * \/ ||
+ * STATE_DEFAULT
+ * || /\
+ * \/ ||
+ * DEVICE_ADDRESS_ASSIGNED DEVICE_RESET
+ * || /\
+ * \/ ||
+ * STATE_ADDRESSED
+ * || /\
+ * \/ ||
+ * DEVICE_CONFIGURED DEVICE_DE_CONFIGURED
+ * || /\
+ * \/ ||
+ * STATE_CONFIGURED
+ *
+ * udc_state_transition_up transitions up (in the direction from STATE_ATTACHED
+ * to STATE_CONFIGURED) from the specified initial state to the specified final
+ * state, passing through each intermediate state on the way. If the initial
+ * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then
+ * no state transitions will take place.
+ *
+ * udc_state_transition_down transitions down (in the direction from
+ * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the
+ * specified final state, passing through each intermediate state on the way.
+ * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final
+ * state, then no state transitions will take place.
+ *
+ */
+
+static void mpc8xx_udc_state_transition_up (usb_device_state_t initial,
+ usb_device_state_t final)
+{
+ if (initial < final) {
+ switch (initial) {
+ case STATE_ATTACHED:
+ usbd_device_event_irq (udc_device,
+ DEVICE_HUB_CONFIGURED, 0);
+ if (final == STATE_POWERED)
+ break;
+ case STATE_POWERED:
+ usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+ if (final == STATE_DEFAULT)
+ break;
+ case STATE_DEFAULT:
+ usbd_device_event_irq (udc_device,
+ DEVICE_ADDRESS_ASSIGNED, 0);
+ if (final == STATE_ADDRESSED)
+ break;
+ case STATE_ADDRESSED:
+ usbd_device_event_irq (udc_device, DEVICE_CONFIGURED,
+ 0);
+ case STATE_CONFIGURED:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void mpc8xx_udc_state_transition_down (usb_device_state_t initial,
+ usb_device_state_t final)
+{
+ if (initial > final) {
+ switch (initial) {
+ case STATE_CONFIGURED:
+ usbd_device_event_irq (udc_device,
+ DEVICE_DE_CONFIGURED, 0);
+ if (final == STATE_ADDRESSED)
+ break;
+ case STATE_ADDRESSED:
+ usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+ if (final == STATE_DEFAULT)
+ break;
+ case STATE_DEFAULT:
+ usbd_device_event_irq (udc_device,
+ DEVICE_POWER_INTERRUPTION, 0);
+ if (final == STATE_POWERED)
+ break;
+ case STATE_POWERED:
+ usbd_device_event_irq (udc_device, DEVICE_HUB_RESET,
+ 0);
+ case STATE_ATTACHED:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* mpc8xx_udc_stall
+ *
+ * Force returning of STALL tokens on the given endpoint. Protocol or function
+ * STALL conditions are permissable here
+ */
+static void mpc8xx_udc_stall (unsigned int ep)
+{
+ usbp->usep[ep] |= STALL_BITMASK;
+}
+
+/* mpc8xx_udc_set_nak
+ *
+ * Force returning of NAK responses for the given endpoint as a kind of very
+ * simple flow control
+ */
+static void mpc8xx_udc_set_nak (unsigned int ep)
+{
+ usbp->usep[ep] |= NAK_BITMASK;
+ __asm__ ("eieio");
+}
+
+/* mpc8xx_udc_handle_txerr
+ *
+ * Handle errors relevant to TX. Return a status code to allow calling
+ * indicative of what if anything happened
+ */
+static short mpc8xx_udc_handle_txerr ()
+{
+ short ep = 0, ret = 0;
+
+ for (; ep < TX_RING_SIZE; ep++) {
+ if (usbp->usber & (0x10 << ep)) {
+
+ /* Timeout or underrun */
+ if (tx_cbd[ep]->cbd_sc & 0x06) {
+ ret = 1;
+ mpc8xx_udc_flush_tx_fifo (ep);
+
+ } else {
+ if (usbp->usep[ep] & STALL_BITMASK) {
+ if (!ep) {
+ usbp->usep[ep] &= ~STALL_BITMASK;
+ }
+ } /* else NAK */
+ }
+ usbp->usber |= (0x10 << ep);
+ }
+ }
+ return ret;
+}
+
+/* mpc8xx_udc_advance_rx
+ *
+ * Advance cbd rx
+ */
+static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid)
+{
+ if ((*rx_cbdp)->cbd_sc & RX_BD_W) {
+ *rx_cbdp = (volatile cbd_t *) (endpoints[epid]->rbase + CONFIG_SYS_IMMR);
+
+ } else {
+ (*rx_cbdp)++;
+ }
+}
+
+
+/* mpc8xx_udc_flush_tx_fifo
+ *
+ * Flush a given TX fifo. Assumes one tx cbd per endpoint
+ */
+static void mpc8xx_udc_flush_tx_fifo (int epid)
+{
+ volatile cbd_t *tx_cbdp = 0;
+
+ if (epid > MAX_ENDPOINTS) {
+ return;
+ }
+
+ /* TX stop */
+ immr->im_cpm.cp_cpcr = ((epid << 2) | 0x1D01);
+ __asm__ ("eieio");
+ while (immr->im_cpm.cp_cpcr & 0x01);
+
+ usbp->uscom = 0x40 | 0;
+
+ /* reset ring */
+ tx_cbdp = (cbd_t *) (endpoints[epid]->tbptr + CONFIG_SYS_IMMR);
+ tx_cbdp->cbd_sc = (TX_BD_I | TX_BD_W);
+
+
+ endpoints[epid]->tptr = endpoints[epid]->tbase;
+ endpoints[epid]->tstate = 0x00;
+ endpoints[epid]->tbcnt = 0x00;
+
+ /* TX start */
+ immr->im_cpm.cp_cpcr = ((epid << 2) | 0x2D01);
+ __asm__ ("eieio");
+ while (immr->im_cpm.cp_cpcr & 0x01);
+
+ return;
+}
+
+/* mpc8xx_udc_flush_rx_fifo
+ *
+ * For the sake of completeness of the namespace, it seems like
+ * a good-design-decision (tm) to include mpc8xx_udc_flush_rx_fifo();
+ * If RX_BD_E is true => a driver bug either here or in an upper layer
+ * not polling frequently enough. If RX_BD_E is true we have told the host
+ * we have accepted data but, the CPM found it had no-where to put that data
+ * which needless to say would be a bad thing.
+ */
+static void mpc8xx_udc_flush_rx_fifo ()
+{
+ int i = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) {
+ ERR ("buf %p used rx data len = 0x%x sc=0x%x!\n",
+ rx_cbd[i], rx_cbd[i]->cbd_datlen,
+ rx_cbd[i]->cbd_sc);
+
+ }
+ }
+ ERR ("BUG : Input over-run\n");
+}
+
+/* mpc8xx_udc_clear_rxbd
+ *
+ * Release control of RX CBD to CP.
+ */
+static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp)
+{
+ rx_cbdp->cbd_datlen = 0x0000;
+ rx_cbdp->cbd_sc = ((rx_cbdp->cbd_sc & RX_BD_W) | (RX_BD_E | RX_BD_I));
+ __asm__ ("eieio");
+}
+
+/* mpc8xx_udc_tx_irq
+ *
+ * Parse for tx timeout, control RX or USB reset/busy conditions
+ * Return -1 on timeout, -2 on fatal error, else return zero
+ */
+static int mpc8xx_udc_tx_irq (int ep)
+{
+ int i = 0;
+
+ if (usbp->usber & (USB_TX_ERRMASK)) {
+ if (mpc8xx_udc_handle_txerr ()) {
+ /* Timeout, controlling function must retry send */
+ return -1;
+ }
+ }
+
+ if (usbp->usber & (USB_E_RESET | USB_E_BSY)) {
+ /* Fatal, abandon TX transaction */
+ return -2;
+ }
+
+ if (usbp->usber & USB_E_RXB) {
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) {
+ if ((rx_cbd[i] == ep_ref[0].prx) || ep) {
+ return -2;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* mpc8xx_udc_ep_tx
+ *
+ * Transmit in a re-entrant fashion outbound USB packets.
+ * Implement retry/timeout mechanism described in USB specification
+ * Toggle DATA0/DATA1 pids as necessary
+ * Introduces non-standard tx_retry. The USB standard has no scope for slave
+ * devices to give up TX, however tx_retry stops us getting stuck in an endless
+ * TX loop.
+ */
+static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi)
+{
+ struct urb *urb = epi->tx_urb;
+ volatile cbd_t *tx_cbdp = 0;
+ unsigned int ep = 0, pkt_len = 0, x = 0, tx_retry = 0;
+ int ret = 0;
+
+ if (!epi || (epi->endpoint_address & 0x03) >= MAX_ENDPOINTS || !urb) {
+ return -1;
+ }
+
+ ep = epi->endpoint_address & 0x03;
+ tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CONFIG_SYS_IMMR);
+
+ if (tx_cbdp->cbd_sc & TX_BD_R || usbp->usber & USB_E_TXB) {
+ mpc8xx_udc_flush_tx_fifo (ep);
+ usbp->usber |= USB_E_TXB;
+ };
+
+ while (tx_retry++ < 100) {
+ ret = mpc8xx_udc_tx_irq (ep);
+ if (ret == -1) {
+ /* ignore timeout here */
+ } else if (ret == -2) {
+ /* Abandon TX */
+ mpc8xx_udc_flush_tx_fifo (ep);
+ return -1;
+ }
+
+ tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CONFIG_SYS_IMMR);
+ while (tx_cbdp->cbd_sc & TX_BD_R) {
+ };
+ tx_cbdp->cbd_sc = (tx_cbdp->cbd_sc & TX_BD_W);
+
+ pkt_len = urb->actual_length - epi->sent;
+
+ if (pkt_len > epi->tx_packetSize || pkt_len > EP_MAX_PKT) {
+ pkt_len = MIN (epi->tx_packetSize, EP_MAX_PKT);
+ }
+
+ for (x = 0; x < pkt_len; x++) {
+ *((unsigned char *) (tx_cbdp->cbd_bufaddr + x)) =
+ urb->buffer[epi->sent + x];
+ }
+ tx_cbdp->cbd_datlen = pkt_len;
+ tx_cbdp->cbd_sc |= (CBD_TX_BITMASK | ep_ref[ep].pid);
+ __asm__ ("eieio");
+
+#ifdef __SIMULATE_ERROR__
+ if (++err_poison_test == 2) {
+ err_poison_test = 0;
+ tx_cbdp->cbd_sc &= ~TX_BD_TC;
+ }
+#endif
+
+ usbp->uscom = (USCOM_STR | ep);
+
+ while (!(usbp->usber & USB_E_TXB)) {
+ ret = mpc8xx_udc_tx_irq (ep);
+ if (ret == -1) {
+ /* TX timeout */
+ break;
+ } else if (ret == -2) {
+ if (usbp->usber & USB_E_TXB) {
+ usbp->usber |= USB_E_TXB;
+ }
+ mpc8xx_udc_flush_tx_fifo (ep);
+ return -1;
+ }
+ };
+
+ if (usbp->usber & USB_E_TXB) {
+ usbp->usber |= USB_E_TXB;
+ }
+
+ /* ACK must be present <= 18bit times from TX */
+ if (ret == -1) {
+ continue;
+ }
+
+ /* TX ACK : USB 2.0 8.7.2, Toggle PID, Advance TX */
+ epi->sent += pkt_len;
+ epi->last = MIN (urb->actual_length - epi->sent, epi->tx_packetSize);
+ TOGGLE_TX_PID (ep_ref[ep].pid);
+
+ if (epi->sent >= epi->tx_urb->actual_length) {
+
+ epi->tx_urb->actual_length = 0;
+ epi->sent = 0;
+
+ if (ep_ref[ep].sc & EP_SEND_ZLP) {
+ ep_ref[ep].sc &= ~EP_SEND_ZLP;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ ERR ("TX fail, endpoint 0x%x tx bytes 0x%x/0x%x\n", ep, epi->sent,
+ epi->tx_urb->actual_length);
+
+ return -1;
+}
+
+/* mpc8xx_udc_dump_request
+ *
+ * Dump a control request to console
+ */
+static void mpc8xx_udc_dump_request (struct usb_device_request *request)
+{
+ DBG ("bmRequestType:%02x bRequest:%02x wValue:%04x "
+ "wIndex:%04x wLength:%04x ?\n",
+ request->bmRequestType,
+ request->bRequest,
+ request->wValue, request->wIndex, request->wLength);
+
+ return;
+}
+
+/* mpc8xx_udc_ep0_rx_setup
+ *
+ * Decode received ep0 SETUP packet. return non-zero on error
+ */
+static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp)
+{
+ unsigned int x = 0;
+ struct urb *purb = ep_ref[0].urb;
+ struct usb_endpoint_instance *epi =
+ &udc_device->bus->endpoint_array[0];
+
+ for (; x < rx_cbdp->cbd_datlen; x++) {
+ *(((unsigned char *) &ep_ref[0].urb->device_request) + x) =
+ *((unsigned char *) (rx_cbdp->cbd_bufaddr + x));
+ }
+
+ mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+ if (ep0_recv_setup (purb)) {
+ mpc8xx_udc_dump_request (&purb->device_request);
+ return -1;
+ }
+
+ if ((purb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK)
+ == USB_REQ_HOST2DEVICE) {
+
+ switch (purb->device_request.bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ /* Send the Status OUT ZLP */
+ ep_ref[0].pid = TX_BD_PID_DATA1;
+ purb->actual_length = 0;
+ mpc8xx_udc_init_tx (epi, purb);
+ mpc8xx_udc_ep_tx (epi);
+
+ /* Move to the addressed state */
+ usbp->usaddr = udc_device->address;
+ mpc8xx_udc_state_transition_up (udc_device->device_state,
+ STATE_ADDRESSED);
+ return 0;
+
+ case USB_REQ_SET_CONFIGURATION:
+ if (!purb->device_request.wValue) {
+ /* Respond at default address */
+ usbp->usaddr = 0x00;
+ mpc8xx_udc_state_transition_down (udc_device->device_state,
+ STATE_ADDRESSED);
+ } else {
+ /* TODO: Support multiple configurations */
+ mpc8xx_udc_state_transition_up (udc_device->device_state,
+ STATE_CONFIGURED);
+ for (x = 1; x < MAX_ENDPOINTS; x++) {
+ if ((udc_device->bus->endpoint_array[x].endpoint_address & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN) {
+ ep_ref[x].pid = TX_BD_PID_DATA0;
+ } else {
+ ep_ref[x].pid = RX_BD_PID_DATA0;
+ }
+ /* Set configuration must unstall endpoints */
+ usbp->usep[x] &= ~STALL_BITMASK;
+ }
+ }
+ break;
+ default:
+ /* CDC/Vendor specific */
+ break;
+ }
+
+ /* Send ZLP as ACK in Status OUT phase */
+ ep_ref[0].pid = TX_BD_PID_DATA1;
+ purb->actual_length = 0;
+ mpc8xx_udc_init_tx (epi, purb);
+ mpc8xx_udc_ep_tx (epi);
+
+ } else {
+
+ if (purb->actual_length) {
+ ep_ref[0].pid = TX_BD_PID_DATA1;
+ mpc8xx_udc_init_tx (epi, purb);
+
+ if (!(purb->actual_length % EP0_MAX_PACKET_SIZE)) {
+ ep_ref[0].sc |= EP_SEND_ZLP;
+ }
+
+ if (purb->device_request.wValue ==
+ USB_DESCRIPTOR_TYPE_DEVICE) {
+ if (le16_to_cpu (purb->device_request.wLength)
+ > purb->actual_length) {
+ /* Send EP0_MAX_PACKET_SIZE bytes
+ * unless correct size requested.
+ */
+ if (purb->actual_length > epi->tx_packetSize) {
+ purb->actual_length = epi->tx_packetSize;
+ }
+ }
+ }
+ mpc8xx_udc_ep_tx (epi);
+
+ } else {
+ /* Corrupt SETUP packet? */
+ ERR ("Zero length data or SETUP with DATA-IN phase ?\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* mpc8xx_udc_init_tx
+ *
+ * Setup some basic parameters for a TX transaction
+ */
+static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi,
+ struct urb *tx_urb)
+{
+ epi->sent = 0;
+ epi->last = 0;
+ epi->tx_urb = tx_urb;
+}
+
+/* mpc8xx_udc_ep0_rx
+ *
+ * Receive ep0/control USB data. Parse and possibly send a response.
+ */
+static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp)
+{
+ if (rx_cbdp->cbd_sc & RX_BD_PID_SETUP) {
+
+ /* Unconditionally accept SETUP packets */
+ if (mpc8xx_udc_ep0_rx_setup (rx_cbdp)) {
+ mpc8xx_udc_stall (0);
+ }
+
+ } else {
+
+ mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+ if ((rx_cbdp->cbd_datlen - 2)) {
+ /* SETUP with a DATA phase
+ * outside of SETUP packet.
+ * Reply with STALL.
+ */
+ mpc8xx_udc_stall (0);
+ }
+ }
+}
+
+/* mpc8xx_udc_epn_rx
+ *
+ * Receive some data from cbd into USB system urb data abstraction
+ * Upper layers should NAK if there is insufficient RX data space
+ */
+static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp)
+{
+ struct usb_endpoint_instance *epi = 0;
+ struct urb *urb = 0;
+ unsigned int x = 0;
+
+ if (epid >= MAX_ENDPOINTS || !rx_cbdp->cbd_datlen) {
+ return 0;
+ }
+
+ /* USB 2.0 PDF section 8.6.4
+ * Discard data with invalid PID it is a resend.
+ */
+ if (ep_ref[epid].pid != (rx_cbdp->cbd_sc & 0xC0)) {
+ return 1;
+ }
+ TOGGLE_RX_PID (ep_ref[epid].pid);
+
+ epi = &udc_device->bus->endpoint_array[epid];
+ urb = epi->rcv_urb;
+
+ for (; x < (rx_cbdp->cbd_datlen - 2); x++) {
+ *((unsigned char *) (urb->buffer + urb->actual_length + x)) =
+ *((unsigned char *) (rx_cbdp->cbd_bufaddr + x));
+ }
+
+ if (x) {
+ usbd_rcv_complete (epi, x, 0);
+ if (ep_ref[epid].urb->status == RECV_ERROR) {
+ DBG ("RX error unset NAK\n");
+ udc_unset_nak (epid);
+ }
+ }
+ return x;
+}
+
+/* mpc8xx_udc_clock_init
+ *
+ * Obtain a clock reference for Full Speed Signaling
+ */
+static void mpc8xx_udc_clock_init (volatile immap_t * immr,
+ volatile cpm8xx_t * cp)
+{
+
+#if defined(CONFIG_SYS_USB_EXTC_CLK)
+
+ /* This has been tested with a 48MHz crystal on CLK6 */
+ switch (CONFIG_SYS_USB_EXTC_CLK) {
+ case 1:
+ immr->im_ioport.iop_papar |= 0x0100;
+ immr->im_ioport.iop_padir &= ~0x0100;
+ cp->cp_sicr |= 0x24;
+ break;
+ case 2:
+ immr->im_ioport.iop_papar |= 0x0200;
+ immr->im_ioport.iop_padir &= ~0x0200;
+ cp->cp_sicr |= 0x2D;
+ break;
+ case 3:
+ immr->im_ioport.iop_papar |= 0x0400;
+ immr->im_ioport.iop_padir &= ~0x0400;
+ cp->cp_sicr |= 0x36;
+ break;
+ case 4:
+ immr->im_ioport.iop_papar |= 0x0800;
+ immr->im_ioport.iop_padir &= ~0x0800;
+ cp->cp_sicr |= 0x3F;
+ break;
+ default:
+ udc_state = STATE_ERROR;
+ break;
+ }
+
+#elif defined(CONFIG_SYS_USB_BRGCLK)
+
+ /* This has been tested with brgclk == 50MHz */
+ int divisor = 0;
+
+ if (gd->cpu_clk < 48000000L) {
+ ERR ("brgclk is too slow for full-speed USB!\n");
+ udc_state = STATE_ERROR;
+ return;
+ }
+
+ /* Assume the brgclk is 'good enough', we want !(gd->cpu_clk%48MHz)
+ * but, can /probably/ live with close-ish alternative rates.
+ */
+ divisor = (gd->cpu_clk / 48000000L) - 1;
+ cp->cp_sicr &= ~0x0000003F;
+
+ switch (CONFIG_SYS_USB_BRGCLK) {
+ case 1:
+ cp->cp_brgc1 |= (divisor | CPM_BRG_EN);
+ cp->cp_sicr &= ~0x2F;
+ break;
+ case 2:
+ cp->cp_brgc2 |= (divisor | CPM_BRG_EN);
+ cp->cp_sicr |= 0x00000009;
+ break;
+ case 3:
+ cp->cp_brgc3 |= (divisor | CPM_BRG_EN);
+ cp->cp_sicr |= 0x00000012;
+ break;
+ case 4:
+ cp->cp_brgc4 = (divisor | CPM_BRG_EN);
+ cp->cp_sicr |= 0x0000001B;
+ break;
+ default:
+ udc_state = STATE_ERROR;
+ break;
+ }
+
+#else
+#error "CONFIG_SYS_USB_EXTC_CLK or CONFIG_SYS_USB_BRGCLK must be defined"
+#endif
+
+}
+
+/* mpc8xx_udc_cbd_attach
+ *
+ * attach a cbd to and endpoint
+ */
+static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size)
+{
+
+ if (!tx_cbd[ep] || !rx_cbd[ep] || ep >= MAX_ENDPOINTS) {
+ udc_state = STATE_ERROR;
+ return;
+ }
+
+ if (tx_size > USB_MAX_PKT || rx_size > USB_MAX_PKT ||
+ (!tx_size && !rx_size)) {
+ udc_state = STATE_ERROR;
+ return;
+ }
+
+ /* Attach CBD to appropiate Parameter RAM Endpoint data structure */
+ if (rx_size) {
+ endpoints[ep]->rbase = (u32) rx_cbd[rx_ct];
+ endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+ rx_ct++;
+
+ if (!ep) {
+
+ endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+ rx_cbd[rx_ct]->cbd_sc |= RX_BD_W;
+ rx_ct++;
+
+ } else {
+ rx_ct += 2;
+ endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+ rx_cbd[rx_ct]->cbd_sc |= RX_BD_W;
+ rx_ct++;
+ }
+
+ /* Where we expect to RX data on this endpoint */
+ ep_ref[ep].prx = rx_cbd[rx_ct - 1];
+ } else {
+
+ ep_ref[ep].prx = 0;
+ endpoints[ep]->rbase = 0;
+ endpoints[ep]->rbptr = 0;
+ }
+
+ if (tx_size) {
+ endpoints[ep]->tbase = (u32) tx_cbd[tx_ct];
+ endpoints[ep]->tbptr = (u32) tx_cbd[tx_ct];
+ tx_ct++;
+ } else {
+ endpoints[ep]->tbase = 0;
+ endpoints[ep]->tbptr = 0;
+ }
+
+ endpoints[ep]->tstate = 0;
+ endpoints[ep]->tbcnt = 0;
+ endpoints[ep]->mrblr = EP_MAX_PKT;
+ endpoints[ep]->rfcr = 0x18;
+ endpoints[ep]->tfcr = 0x18;
+ ep_ref[ep].sc |= EP_ATTACHED;
+
+ DBG ("ep %d rbase 0x%08x rbptr 0x%08x tbase 0x%08x tbptr 0x%08x prx = %p\n",
+ ep, endpoints[ep]->rbase, endpoints[ep]->rbptr,
+ endpoints[ep]->tbase, endpoints[ep]->tbptr,
+ ep_ref[ep].prx);
+
+ return;
+}
+
+/* mpc8xx_udc_cbd_init
+ *
+ * Allocate space for a cbd and allocate TX/RX data space
+ */
+static void mpc8xx_udc_cbd_init (void)
+{
+ int i = 0;
+
+ for (; i < TX_RING_SIZE; i++) {
+ tx_cbd[i] = (cbd_t *)
+ mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int));
+ }
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_cbd[i] = (cbd_t *)
+ mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int));
+ }
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ tx_cbd[i]->cbd_bufaddr =
+ mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int));
+
+ tx_cbd[i]->cbd_sc = (TX_BD_I | TX_BD_W);
+ tx_cbd[i]->cbd_datlen = 0x0000;
+ }
+
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_cbd[i]->cbd_bufaddr =
+ mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int));
+ rx_cbd[i]->cbd_sc = (RX_BD_I | RX_BD_E);
+ rx_cbd[i]->cbd_datlen = 0x0000;
+
+ }
+
+ return;
+}
+
+/* mpc8xx_udc_endpoint_init
+ *
+ * Attach an endpoint to some dpram
+ */
+static void mpc8xx_udc_endpoint_init (void)
+{
+ int i = 0;
+
+ for (; i < MAX_ENDPOINTS; i++) {
+ endpoints[i] = (usb_epb_t *)
+ mpc8xx_udc_alloc (sizeof (usb_epb_t), 32);
+ }
+}
+
+/* mpc8xx_udc_alloc
+ *
+ * Grab the address of some dpram
+ */
+static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment)
+{
+ u32 retaddr = address_base;
+
+ while (retaddr % alignment) {
+ retaddr++;
+ }
+ address_base += data_size;
+
+ return retaddr;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/ndis.h b/src/roms/u-boot/drivers/usb/gadget/ndis.h
new file mode 100644
index 0000000..753838f
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/ndis.h
@@ -0,0 +1,217 @@
+/*
+ * ndis.h
+ *
+ * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
+ *
+ * Thanks to the cygwin development team,
+ * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef _USBGADGET_NDIS_H
+#define _USBGADGET_NDIS_H
+
+
+#define NDIS_STATUS_MULTICAST_FULL 0xC0010009
+#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A
+#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B
+
+enum NDIS_DEVICE_POWER_STATE {
+ NdisDeviceStateUnspecified = 0,
+ NdisDeviceStateD0,
+ NdisDeviceStateD1,
+ NdisDeviceStateD2,
+ NdisDeviceStateD3,
+ NdisDeviceStateMaximum
+};
+
+struct NDIS_PM_WAKE_UP_CAPABILITIES {
+ enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp;
+ enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp;
+ enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp;
+};
+
+/* NDIS_PNP_CAPABILITIES.Flags constants */
+#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001
+#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002
+#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004
+
+struct NDIS_PNP_CAPABILITIES {
+ __le32 Flags;
+ struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities;
+};
+
+struct NDIS_PM_PACKET_PATTERN {
+ __le32 Priority;
+ __le32 Reserved;
+ __le32 MaskSize;
+ __le32 PatternOffset;
+ __le32 PatternSize;
+ __le32 PatternFlags;
+};
+
+
+/* Required Object IDs (OIDs) */
+#define OID_GEN_SUPPORTED_LIST 0x00010101
+#define OID_GEN_HARDWARE_STATUS 0x00010102
+#define OID_GEN_MEDIA_SUPPORTED 0x00010103
+#define OID_GEN_MEDIA_IN_USE 0x00010104
+#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105
+#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106
+#define OID_GEN_LINK_SPEED 0x00010107
+#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108
+#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109
+#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A
+#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B
+#define OID_GEN_VENDOR_ID 0x0001010C
+#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D
+#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
+#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F
+#define OID_GEN_DRIVER_VERSION 0x00010110
+#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111
+#define OID_GEN_PROTOCOL_OPTIONS 0x00010112
+#define OID_GEN_MAC_OPTIONS 0x00010113
+#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114
+#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115
+#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116
+#define OID_GEN_SUPPORTED_GUIDS 0x00010117
+#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118
+#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119
+#define OID_GEN_MACHINE_NAME 0x0001021A
+#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B
+#define OID_GEN_VLAN_ID 0x0001021C
+
+/* Optional OIDs */
+#define OID_GEN_MEDIA_CAPABILITIES 0x00010201
+#define OID_GEN_PHYSICAL_MEDIUM 0x00010202
+
+/* Required statistics OIDs */
+#define OID_GEN_XMIT_OK 0x00020101
+#define OID_GEN_RCV_OK 0x00020102
+#define OID_GEN_XMIT_ERROR 0x00020103
+#define OID_GEN_RCV_ERROR 0x00020104
+#define OID_GEN_RCV_NO_BUFFER 0x00020105
+
+/* Optional statistics OIDs */
+#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201
+#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202
+#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203
+#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204
+#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205
+#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206
+#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207
+#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208
+#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209
+#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A
+#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B
+#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C
+#define OID_GEN_RCV_CRC_ERROR 0x0002020D
+#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E
+#define OID_GEN_GET_TIME_CAPS 0x0002020F
+#define OID_GEN_GET_NETCARD_TIME 0x00020210
+#define OID_GEN_NETCARD_LOAD 0x00020211
+#define OID_GEN_DEVICE_PROFILE 0x00020212
+#define OID_GEN_INIT_TIME_MS 0x00020213
+#define OID_GEN_RESET_COUNTS 0x00020214
+#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215
+#define OID_GEN_FRIENDLY_NAME 0x00020216
+#define OID_GEN_MINIPORT_INFO 0x00020217
+#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218
+
+/* IEEE 802.3 (Ethernet) OIDs */
+#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001
+
+#define OID_802_3_PERMANENT_ADDRESS 0x01010101
+#define OID_802_3_CURRENT_ADDRESS 0x01010102
+#define OID_802_3_MULTICAST_LIST 0x01010103
+#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104
+#define OID_802_3_MAC_OPTIONS 0x01010105
+#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101
+#define OID_802_3_XMIT_ONE_COLLISION 0x01020102
+#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103
+#define OID_802_3_XMIT_DEFERRED 0x01020201
+#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202
+#define OID_802_3_RCV_OVERRUN 0x01020203
+#define OID_802_3_XMIT_UNDERRUN 0x01020204
+#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205
+#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206
+#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207
+
+/* OID_GEN_MINIPORT_INFO constants */
+#define NDIS_MINIPORT_BUS_MASTER 0x00000001
+#define NDIS_MINIPORT_WDM_DRIVER 0x00000002
+#define NDIS_MINIPORT_SG_LIST 0x00000004
+#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008
+#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010
+#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020
+#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040
+#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080
+#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100
+#define NDIS_MINIPORT_IS_NDIS_5 0x00000200
+#define NDIS_MINIPORT_IS_CO 0x00000400
+#define NDIS_MINIPORT_DESERIALIZE 0x00000800
+#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000
+#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000
+#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000
+#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000
+#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000
+#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000
+#define NDIS_MINIPORT_HIDDEN 0x00040000
+#define NDIS_MINIPORT_SWENUM 0x00080000
+#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000
+#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000
+#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000
+#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000
+#define NDIS_MINIPORT_64BITS_DMA 0x01000000
+
+#define NDIS_MEDIUM_802_3 0x00000000
+#define NDIS_MEDIUM_802_5 0x00000001
+#define NDIS_MEDIUM_FDDI 0x00000002
+#define NDIS_MEDIUM_WAN 0x00000003
+#define NDIS_MEDIUM_LOCAL_TALK 0x00000004
+#define NDIS_MEDIUM_DIX 0x00000005
+#define NDIS_MEDIUM_ARCENT_RAW 0x00000006
+#define NDIS_MEDIUM_ARCENT_878_2 0x00000007
+#define NDIS_MEDIUM_ATM 0x00000008
+#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009
+#define NDIS_MEDIUM_IRDA 0x0000000A
+#define NDIS_MEDIUM_BPC 0x0000000B
+#define NDIS_MEDIUM_CO_WAN 0x0000000C
+#define NDIS_MEDIUM_1394 0x0000000D
+
+#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
+#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
+#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
+#define NDIS_PACKET_TYPE_BROADCAST 0x00000008
+#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010
+#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020
+#define NDIS_PACKET_TYPE_SMT 0x00000040
+#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080
+#define NDIS_PACKET_TYPE_GROUP 0x00000100
+#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200
+#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
+#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
+
+#define NDIS_MEDIA_STATE_CONNECTED 0x00000000
+#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001
+
+#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001
+#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002
+#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004
+#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008
+#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010
+#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020
+#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040
+#define NDIS_MAC_OPTION_RESERVED 0x80000000
+
+#endif /* _USBGADGET_NDIS_H */
diff --git a/src/roms/u-boot/drivers/usb/gadget/omap1510_udc.c b/src/roms/u-boot/drivers/usb/gadget/omap1510_udc.c
new file mode 100644
index 0000000..bdc1b88
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/omap1510_udc.c
@@ -0,0 +1,1555 @@
+/*
+ * (C) Copyright 2003
+ * Gerry Hamel, geh@ti.com, Texas Instruments
+ *
+ * Based on
+ * linux/drivers/usb/device/bi/omap.c
+ * TI OMAP1510 USB bus interface driver
+ *
+ * Author: MontaVista Software, Inc.
+ * source@mvista.com
+ * (C) Copyright 2002
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#ifdef CONFIG_OMAP_SX1
+#include <i2c.h>
+#endif
+#include <usbdevice.h>
+#include <usb/omap1510_udc.h>
+#include <usb/udc.h>
+
+#include "ep0.h"
+
+
+#define UDC_INIT_MDELAY 80 /* Device settle delay */
+#define UDC_MAX_ENDPOINTS 31 /* Number of endpoints on this UDC */
+
+/* Some kind of debugging output... */
+#if 1
+#define UDCDBG(str)
+#define UDCDBGA(fmt,args...)
+#else /* The bugs still exists... */
+#define UDCDBG(str) serial_printf("[%s] %s:%d: " str "\n", __FILE__,__FUNCTION__,__LINE__)
+#define UDCDBGA(fmt,args...) serial_printf("[%s] %s:%d: " fmt "\n", __FILE__,__FUNCTION__,__LINE__, ##args)
+#endif
+
+#if 1
+#define UDCREG(name)
+#define UDCREGL(name)
+#else /* The bugs still exists... */
+#define UDCREG(name) serial_printf("%s():%d: %s[%08x]=%.4x\n",__FUNCTION__,__LINE__, (#name), name, inw(name)) /* For 16-bit regs */
+#define UDCREGL(name) serial_printf("%s():%d: %s[%08x]=%.8x\n",__FUNCTION__,__LINE__, (#name), name, inl(name)) /* For 32-bit regs */
+#endif
+
+
+static struct urb *ep0_urb = NULL;
+
+static struct usb_device_instance *udc_device; /* Used in interrupt handler */
+static u16 udc_devstat = 0; /* UDC status (DEVSTAT) */
+static u32 udc_interrupts = 0;
+
+static void udc_stall_ep (unsigned int ep_addr);
+
+
+static struct usb_endpoint_instance *omap1510_find_ep (int ep)
+{
+ int i;
+
+ for (i = 0; i < udc_device->bus->max_endpoints; i++) {
+ if (udc_device->bus->endpoint_array[i].endpoint_address == ep)
+ return &udc_device->bus->endpoint_array[i];
+ }
+ return NULL;
+}
+
+/* ************************************************************************** */
+/* IO
+ */
+
+/*
+ * omap1510_prepare_endpoint_for_rx
+ *
+ * This function implements TRM Figure 14-11.
+ *
+ * The endpoint to prepare for transfer is specified as a physical endpoint
+ * number. For OUT (rx) endpoints 1 through 15, the corresponding endpoint
+ * configuration register is checked to see if the endpoint is ISO or not.
+ * If the OUT endpoint is valid and is non-ISO then its FIFO is enabled.
+ * No action is taken for endpoint 0 or for IN (tx) endpoints 16 through 30.
+ */
+static void omap1510_prepare_endpoint_for_rx (int ep_addr)
+{
+ int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+
+ UDCDBGA ("omap1510_prepare_endpoint %x", ep_addr);
+ if (((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)) {
+ if ((inw (UDC_EP_RX (ep_num)) &
+ (UDC_EPn_RX_Valid | UDC_EPn_RX_Iso)) ==
+ UDC_EPn_RX_Valid) {
+ /* rx endpoint is valid, non-ISO, so enable its FIFO */
+ outw (UDC_EP_Sel | ep_num, UDC_EP_NUM);
+ outw (UDC_Set_FIFO_En, UDC_CTRL);
+ outw (0, UDC_EP_NUM);
+ }
+ }
+}
+
+/* omap1510_configure_endpoints
+ *
+ * This function implements TRM Figure 14-10.
+ */
+static void omap1510_configure_endpoints (struct usb_device_instance *device)
+{
+ int ep;
+ struct usb_bus_instance *bus;
+ struct usb_endpoint_instance *endpoint;
+ unsigned short ep_ptr;
+ unsigned short ep_size;
+ unsigned short ep_isoc;
+ unsigned short ep_doublebuffer;
+ int ep_addr;
+ int packet_size;
+ int buffer_size;
+ int attributes;
+
+ bus = device->bus;
+
+ /* There is a dedicated 2048 byte buffer for USB packets that may be
+ * arbitrarily partitioned among the endpoints on 8-byte boundaries.
+ * The first 8 bytes are reserved for receiving setup packets on
+ * endpoint 0.
+ */
+ ep_ptr = 8; /* reserve the first 8 bytes for the setup fifo */
+
+ for (ep = 0; ep < bus->max_endpoints; ep++) {
+ endpoint = bus->endpoint_array + ep;
+ ep_addr = endpoint->endpoint_address;
+ if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+ /* IN endpoint */
+ packet_size = endpoint->tx_packetSize;
+ attributes = endpoint->tx_attributes;
+ } else {
+ /* OUT endpoint */
+ packet_size = endpoint->rcv_packetSize;
+ attributes = endpoint->rcv_attributes;
+ }
+
+ switch (packet_size) {
+ case 0:
+ ep_size = 0;
+ break;
+ case 8:
+ ep_size = 0;
+ break;
+ case 16:
+ ep_size = 1;
+ break;
+ case 32:
+ ep_size = 2;
+ break;
+ case 64:
+ ep_size = 3;
+ break;
+ case 128:
+ ep_size = 4;
+ break;
+ case 256:
+ ep_size = 5;
+ break;
+ case 512:
+ ep_size = 6;
+ break;
+ default:
+ UDCDBGA ("ep 0x%02x has bad packet size %d",
+ ep_addr, packet_size);
+ packet_size = 0;
+ ep_size = 0;
+ break;
+ }
+
+ switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ default:
+ /* A non-isochronous endpoint may optionally be
+ * double-buffered. For now we disable
+ * double-buffering.
+ */
+ ep_doublebuffer = 0;
+ ep_isoc = 0;
+ if (packet_size > 64)
+ packet_size = 0;
+ if (!ep || !ep_doublebuffer)
+ buffer_size = packet_size;
+ else
+ buffer_size = packet_size * 2;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ /* Isochronous endpoints are always double-
+ * buffered, but the double-buffering bit
+ * in the endpoint configuration register
+ * becomes the msb of the endpoint size so we
+ * set the double-buffering flag to zero.
+ */
+ ep_doublebuffer = 0;
+ ep_isoc = 1;
+ buffer_size = packet_size * 2;
+ break;
+ }
+
+ /* check to see if our packet buffer RAM is exhausted */
+ if ((ep_ptr + buffer_size) > 2048) {
+ UDCDBGA ("out of packet RAM for ep 0x%02x buf size %d", ep_addr, buffer_size);
+ buffer_size = packet_size = 0;
+ }
+
+ /* force a default configuration for endpoint 0 since it is
+ * always enabled
+ */
+ if (!ep && ((packet_size < 8) || (packet_size > 64))) {
+ buffer_size = packet_size = 64;
+ ep_size = 3;
+ }
+
+ if (!ep) {
+ /* configure endpoint 0 */
+ outw ((ep_size << 12) | (ep_ptr >> 3), UDC_EP0);
+ /*UDCDBGA("ep 0 buffer offset 0x%03x packet size 0x%03x", */
+ /* ep_ptr, packet_size); */
+ } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+ /* IN endpoint */
+ if (packet_size) {
+ outw ((1 << 15) | (ep_doublebuffer << 14) |
+ (ep_size << 12) | (ep_isoc << 11) |
+ (ep_ptr >> 3),
+ UDC_EP_TX (ep_addr &
+ USB_ENDPOINT_NUMBER_MASK));
+ UDCDBGA ("IN ep %d buffer offset 0x%03x"
+ " packet size 0x%03x",
+ ep_addr & USB_ENDPOINT_NUMBER_MASK,
+ ep_ptr, packet_size);
+ } else {
+ outw (0,
+ UDC_EP_TX (ep_addr &
+ USB_ENDPOINT_NUMBER_MASK));
+ }
+ } else {
+ /* OUT endpoint */
+ if (packet_size) {
+ outw ((1 << 15) | (ep_doublebuffer << 14) |
+ (ep_size << 12) | (ep_isoc << 11) |
+ (ep_ptr >> 3),
+ UDC_EP_RX (ep_addr &
+ USB_ENDPOINT_NUMBER_MASK));
+ UDCDBGA ("OUT ep %d buffer offset 0x%03x"
+ " packet size 0x%03x",
+ ep_addr & USB_ENDPOINT_NUMBER_MASK,
+ ep_ptr, packet_size);
+ } else {
+ outw (0,
+ UDC_EP_RX (ep_addr &
+ USB_ENDPOINT_NUMBER_MASK));
+ }
+ }
+ ep_ptr += buffer_size;
+ }
+}
+
+/* omap1510_deconfigure_device
+ *
+ * This function balances omap1510_configure_device.
+ */
+static void omap1510_deconfigure_device (void)
+{
+ int epnum;
+
+ UDCDBG ("clear Cfg_Lock");
+ outw (inw (UDC_SYSCON1) & ~UDC_Cfg_Lock, UDC_SYSCON1);
+ UDCREG (UDC_SYSCON1);
+
+ /* deconfigure all endpoints */
+ for (epnum = 1; epnum <= 15; epnum++) {
+ outw (0, UDC_EP_RX (epnum));
+ outw (0, UDC_EP_TX (epnum));
+ }
+}
+
+/* omap1510_configure_device
+ *
+ * This function implements TRM Figure 14-9.
+ */
+static void omap1510_configure_device (struct usb_device_instance *device)
+{
+ omap1510_configure_endpoints (device);
+
+
+ /* Figure 14-9 indicates we should enable interrupts here, but we have
+ * other routines (udc_all_interrupts, udc_suspended_interrupts) to
+ * do that.
+ */
+
+ UDCDBG ("set Cfg_Lock");
+ outw (inw (UDC_SYSCON1) | UDC_Cfg_Lock, UDC_SYSCON1);
+ UDCREG (UDC_SYSCON1);
+}
+
+/* omap1510_write_noniso_tx_fifo
+ *
+ * This function implements TRM Figure 14-30.
+ *
+ * If the endpoint has an active tx_urb, then the next packet of data from the
+ * URB is written to the tx FIFO. The total amount of data in the urb is given
+ * by urb->actual_length. The maximum amount of data that can be sent in any
+ * one packet is given by endpoint->tx_packetSize. The number of data bytes
+ * from this URB that have already been transmitted is given by endpoint->sent.
+ * endpoint->last is updated by this routine with the number of data bytes
+ * transmitted in this packet.
+ *
+ * In accordance with Figure 14-30, the EP_NUM register must already have been
+ * written with the value to select the appropriate tx FIFO before this routine
+ * is called.
+ */
+static void omap1510_write_noniso_tx_fifo (struct usb_endpoint_instance
+ *endpoint)
+{
+ struct urb *urb = endpoint->tx_urb;
+
+ if (urb) {
+ unsigned int last, i;
+
+ UDCDBGA ("urb->buffer %p, buffer_length %d, actual_length %d",
+ urb->buffer, urb->buffer_length, urb->actual_length);
+ if ((last =
+ MIN (urb->actual_length - endpoint->sent,
+ endpoint->tx_packetSize))) {
+ u8 *cp = urb->buffer + endpoint->sent;
+
+ UDCDBGA ("endpoint->sent %d, tx_packetSize %d, last %d", endpoint->sent, endpoint->tx_packetSize, last);
+
+ if (((u32) cp & 1) == 0) { /* word aligned? */
+ outsw (UDC_DATA, cp, last >> 1);
+ } else { /* byte aligned. */
+ for (i = 0; i < (last >> 1); i++) {
+ u16 w = ((u16) cp[2 * i + 1] << 8) |
+ (u16) cp[2 * i];
+ outw (w, UDC_DATA);
+ }
+ }
+ if (last & 1) {
+ outb (*(cp + last - 1), UDC_DATA);
+ }
+ }
+ endpoint->last = last;
+ }
+}
+
+/* omap1510_read_noniso_rx_fifo
+ *
+ * This function implements TRM Figure 14-28.
+ *
+ * If the endpoint has an active rcv_urb, then the next packet of data is read
+ * from the rcv FIFO and written to rcv_urb->buffer at offset
+ * rcv_urb->actual_length to append the packet data to the data from any
+ * previous packets for this transfer. We assume that there is sufficient room
+ * left in the buffer to hold an entire packet of data.
+ *
+ * The return value is the number of bytes read from the FIFO for this packet.
+ *
+ * In accordance with Figure 14-28, the EP_NUM register must already have been
+ * written with the value to select the appropriate rcv FIFO before this routine
+ * is called.
+ */
+static int omap1510_read_noniso_rx_fifo (struct usb_endpoint_instance
+ *endpoint)
+{
+ struct urb *urb = endpoint->rcv_urb;
+ int len = 0;
+
+ if (urb) {
+ len = inw (UDC_RXFSTAT);
+
+ if (len) {
+ unsigned char *cp = urb->buffer + urb->actual_length;
+
+ insw (UDC_DATA, cp, len >> 1);
+ if (len & 1)
+ *(cp + len - 1) = inb (UDC_DATA);
+ }
+ }
+ return len;
+}
+
+/* omap1510_prepare_for_control_write_status
+ *
+ * This function implements TRM Figure 14-17.
+ *
+ * We have to deal here with non-autodecoded control writes that haven't already
+ * been dealt with by ep0_recv_setup. The non-autodecoded standard control
+ * write requests are: set/clear endpoint feature, set configuration, set
+ * interface, and set descriptor. ep0_recv_setup handles set/clear requests for
+ * ENDPOINT_HALT by halting the endpoint for a set request and resetting the
+ * endpoint for a clear request. ep0_recv_setup returns an error for
+ * SET_DESCRIPTOR requests which causes them to be terminated with a stall by
+ * the setup handler. A SET_INTERFACE request is handled by ep0_recv_setup by
+ * generating a DEVICE_SET_INTERFACE event. This leaves only the
+ * SET_CONFIGURATION event for us to deal with here.
+ *
+ */
+static void omap1510_prepare_for_control_write_status (struct urb *urb)
+{
+ struct usb_device_request *request = &urb->device_request;;
+
+ /* check for a SET_CONFIGURATION request */
+ if (request->bRequest == USB_REQ_SET_CONFIGURATION) {
+ int configuration = le16_to_cpu (request->wValue) & 0xff;
+ unsigned short devstat = inw (UDC_DEVSTAT);
+
+ if ((devstat & (UDC_ADD | UDC_CFG)) == UDC_ADD) {
+ /* device is currently in ADDRESSED state */
+ if (configuration) {
+ /* Assume the specified non-zero configuration
+ * value is valid and switch to the CONFIGURED
+ * state.
+ */
+ outw (UDC_Dev_Cfg, UDC_SYSCON2);
+ }
+ } else if ((devstat & UDC_CFG) == UDC_CFG) {
+ /* device is currently in CONFIGURED state */
+ if (!configuration) {
+ /* Switch to ADDRESSED state. */
+ outw (UDC_Clr_Cfg, UDC_SYSCON2);
+ }
+ }
+ }
+
+ /* select EP0 tx FIFO */
+ outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM);
+ /* clear endpoint (no data bytes in status stage) */
+ outw (UDC_Clr_EP, UDC_CTRL);
+ /* enable the EP0 tx FIFO */
+ outw (UDC_Set_FIFO_En, UDC_CTRL);
+ /* deselect the endpoint */
+ outw (UDC_EP_Dir, UDC_EP_NUM);
+}
+
+/* udc_state_transition_up
+ * udc_state_transition_down
+ *
+ * Helper functions to implement device state changes. The device states and
+ * the events that transition between them are:
+ *
+ * STATE_ATTACHED
+ * || /\
+ * \/ ||
+ * DEVICE_HUB_CONFIGURED DEVICE_HUB_RESET
+ * || /\
+ * \/ ||
+ * STATE_POWERED
+ * || /\
+ * \/ ||
+ * DEVICE_RESET DEVICE_POWER_INTERRUPTION
+ * || /\
+ * \/ ||
+ * STATE_DEFAULT
+ * || /\
+ * \/ ||
+ * DEVICE_ADDRESS_ASSIGNED DEVICE_RESET
+ * || /\
+ * \/ ||
+ * STATE_ADDRESSED
+ * || /\
+ * \/ ||
+ * DEVICE_CONFIGURED DEVICE_DE_CONFIGURED
+ * || /\
+ * \/ ||
+ * STATE_CONFIGURED
+ *
+ * udc_state_transition_up transitions up (in the direction from STATE_ATTACHED
+ * to STATE_CONFIGURED) from the specified initial state to the specified final
+ * state, passing through each intermediate state on the way. If the initial
+ * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then
+ * no state transitions will take place.
+ *
+ * udc_state_transition_down transitions down (in the direction from
+ * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the
+ * specified final state, passing through each intermediate state on the way.
+ * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final
+ * state, then no state transitions will take place.
+ *
+ * These functions must only be called with interrupts disabled.
+ */
+static void udc_state_transition_up (usb_device_state_t initial,
+ usb_device_state_t final)
+{
+ if (initial < final) {
+ switch (initial) {
+ case STATE_ATTACHED:
+ usbd_device_event_irq (udc_device,
+ DEVICE_HUB_CONFIGURED, 0);
+ if (final == STATE_POWERED)
+ break;
+ case STATE_POWERED:
+ usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+ if (final == STATE_DEFAULT)
+ break;
+ case STATE_DEFAULT:
+ usbd_device_event_irq (udc_device,
+ DEVICE_ADDRESS_ASSIGNED, 0);
+ if (final == STATE_ADDRESSED)
+ break;
+ case STATE_ADDRESSED:
+ usbd_device_event_irq (udc_device, DEVICE_CONFIGURED,
+ 0);
+ case STATE_CONFIGURED:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void udc_state_transition_down (usb_device_state_t initial,
+ usb_device_state_t final)
+{
+ if (initial > final) {
+ switch (initial) {
+ case STATE_CONFIGURED:
+ usbd_device_event_irq (udc_device, DEVICE_DE_CONFIGURED, 0);
+ if (final == STATE_ADDRESSED)
+ break;
+ case STATE_ADDRESSED:
+ usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+ if (final == STATE_DEFAULT)
+ break;
+ case STATE_DEFAULT:
+ usbd_device_event_irq (udc_device, DEVICE_POWER_INTERRUPTION, 0);
+ if (final == STATE_POWERED)
+ break;
+ case STATE_POWERED:
+ usbd_device_event_irq (udc_device, DEVICE_HUB_RESET, 0);
+ case STATE_ATTACHED:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* Handle all device state changes.
+ * This function implements TRM Figure 14-21.
+ */
+static void omap1510_udc_state_changed (void)
+{
+ u16 bits;
+ u16 devstat = inw (UDC_DEVSTAT);
+
+ UDCDBGA ("state changed, devstat %x, old %x", devstat, udc_devstat);
+
+ bits = devstat ^ udc_devstat;
+ if (bits) {
+ if (bits & UDC_ATT) {
+ if (devstat & UDC_ATT) {
+ UDCDBG ("device attached and powered");
+ udc_state_transition_up (udc_device->device_state, STATE_POWERED);
+ } else {
+ UDCDBG ("device detached or unpowered");
+ udc_state_transition_down (udc_device->device_state, STATE_ATTACHED);
+ }
+ }
+ if (bits & UDC_USB_Reset) {
+ if (devstat & UDC_USB_Reset) {
+ UDCDBG ("device reset in progess");
+ udc_state_transition_down (udc_device->device_state, STATE_POWERED);
+ } else {
+ UDCDBG ("device reset completed");
+ }
+ }
+ if (bits & UDC_DEF) {
+ if (devstat & UDC_DEF) {
+ UDCDBG ("device entering default state");
+ udc_state_transition_up (udc_device->device_state, STATE_DEFAULT);
+ } else {
+ UDCDBG ("device leaving default state");
+ udc_state_transition_down (udc_device->device_state, STATE_POWERED);
+ }
+ }
+ if (bits & UDC_SUS) {
+ if (devstat & UDC_SUS) {
+ UDCDBG ("entering suspended state");
+ usbd_device_event_irq (udc_device, DEVICE_BUS_INACTIVE, 0);
+ } else {
+ UDCDBG ("leaving suspended state");
+ usbd_device_event_irq (udc_device, DEVICE_BUS_ACTIVITY, 0);
+ }
+ }
+ if (bits & UDC_R_WK_OK) {
+ UDCDBGA ("remote wakeup %s", (devstat & UDC_R_WK_OK)
+ ? "enabled" : "disabled");
+ }
+ if (bits & UDC_ADD) {
+ if (devstat & UDC_ADD) {
+ UDCDBG ("default -> addressed");
+ udc_state_transition_up (udc_device->device_state, STATE_ADDRESSED);
+ } else {
+ UDCDBG ("addressed -> default");
+ udc_state_transition_down (udc_device->device_state, STATE_DEFAULT);
+ }
+ }
+ if (bits & UDC_CFG) {
+ if (devstat & UDC_CFG) {
+ UDCDBG ("device configured");
+ /* The ep0_recv_setup function generates the
+ * DEVICE_CONFIGURED event when a
+ * USB_REQ_SET_CONFIGURATION setup packet is
+ * received, so we should already be in the
+ * state STATE_CONFIGURED.
+ */
+ udc_state_transition_up (udc_device->device_state, STATE_CONFIGURED);
+ } else {
+ UDCDBG ("device deconfigured");
+ udc_state_transition_down (udc_device->device_state, STATE_ADDRESSED);
+ }
+ }
+ }
+
+ /* Clear interrupt source */
+ outw (UDC_DS_Chg, UDC_IRQ_SRC);
+
+ /* Save current DEVSTAT */
+ udc_devstat = devstat;
+}
+
+/* Handle SETUP USB interrupt.
+ * This function implements TRM Figure 14-14.
+ */
+static void omap1510_udc_setup (struct usb_endpoint_instance *endpoint)
+{
+ UDCDBG ("-> Entering device setup");
+
+ do {
+ const int setup_pktsize = 8;
+ unsigned char *datap =
+ (unsigned char *) &ep0_urb->device_request;
+
+ /* Gain access to EP 0 setup FIFO */
+ outw (UDC_Setup_Sel, UDC_EP_NUM);
+
+ /* Read control request data */
+ insb (UDC_DATA, datap, setup_pktsize);
+
+ UDCDBGA ("EP0 setup read [%x %x %x %x %x %x %x %x]",
+ *(datap + 0), *(datap + 1), *(datap + 2),
+ *(datap + 3), *(datap + 4), *(datap + 5),
+ *(datap + 6), *(datap + 7));
+
+ /* Reset EP0 setup FIFO */
+ outw (0, UDC_EP_NUM);
+ } while (inw (UDC_IRQ_SRC) & UDC_Setup);
+
+ /* Try to process setup packet */
+ if (ep0_recv_setup (ep0_urb)) {
+ /* Not a setup packet, stall next EP0 transaction */
+ udc_stall_ep (0);
+ UDCDBG ("can't parse setup packet, still waiting for setup");
+ return;
+ }
+
+ /* Check direction */
+ if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK)
+ == USB_REQ_HOST2DEVICE) {
+ UDCDBG ("control write on EP0");
+ if (le16_to_cpu (ep0_urb->device_request.wLength)) {
+ /* We don't support control write data stages.
+ * The only standard control write request with a data
+ * stage is SET_DESCRIPTOR, and ep0_recv_setup doesn't
+ * support that so we just stall those requests. A
+ * function driver might support a non-standard
+ * write request with a data stage, but it isn't
+ * obvious what we would do with the data if we read it
+ * so we'll just stall it. It seems like the API isn't
+ * quite right here.
+ */
+#if 0
+ /* Here is what we would do if we did support control
+ * write data stages.
+ */
+ ep0_urb->actual_length = 0;
+ outw (0, UDC_EP_NUM);
+ /* enable the EP0 rx FIFO */
+ outw (UDC_Set_FIFO_En, UDC_CTRL);
+#else
+ /* Stall this request */
+ UDCDBG ("Stalling unsupported EP0 control write data "
+ "stage.");
+ udc_stall_ep (0);
+#endif
+ } else {
+ omap1510_prepare_for_control_write_status (ep0_urb);
+ }
+ } else {
+ UDCDBG ("control read on EP0");
+ /* The ep0_recv_setup function has already placed our response
+ * packet data in ep0_urb->buffer and the packet length in
+ * ep0_urb->actual_length.
+ */
+ endpoint->tx_urb = ep0_urb;
+ endpoint->sent = 0;
+ /* select the EP0 tx FIFO */
+ outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM);
+ /* Write packet data to the FIFO. omap1510_write_noniso_tx_fifo
+ * will update endpoint->last with the number of bytes written
+ * to the FIFO.
+ */
+ omap1510_write_noniso_tx_fifo (endpoint);
+ /* enable the FIFO to start the packet transmission */
+ outw (UDC_Set_FIFO_En, UDC_CTRL);
+ /* deselect the EP0 tx FIFO */
+ outw (UDC_EP_Dir, UDC_EP_NUM);
+ }
+
+ UDCDBG ("<- Leaving device setup");
+}
+
+/* Handle endpoint 0 RX interrupt
+ * This routine implements TRM Figure 14-16.
+ */
+static void omap1510_udc_ep0_rx (struct usb_endpoint_instance *endpoint)
+{
+ unsigned short status;
+
+ UDCDBG ("RX on EP0");
+ /* select EP0 rx FIFO */
+ outw (UDC_EP_Sel, UDC_EP_NUM);
+
+ status = inw (UDC_STAT_FLG);
+
+ if (status & UDC_ACK) {
+ /* Check direction */
+ if ((ep0_urb->device_request.bmRequestType
+ & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) {
+ /* This rx interrupt must be for a control write data
+ * stage packet.
+ *
+ * We don't support control write data stages.
+ * We should never end up here.
+ */
+
+ /* clear the EP0 rx FIFO */
+ outw (UDC_Clr_EP, UDC_CTRL);
+
+ /* deselect the EP0 rx FIFO */
+ outw (0, UDC_EP_NUM);
+
+ UDCDBG ("Stalling unexpected EP0 control write "
+ "data stage packet");
+ udc_stall_ep (0);
+ } else {
+ /* This rx interrupt must be for a control read status
+ * stage packet.
+ */
+ UDCDBG ("ACK on EP0 control read status stage packet");
+ /* deselect EP0 rx FIFO */
+ outw (0, UDC_EP_NUM);
+ }
+ } else if (status & UDC_STALL) {
+ UDCDBG ("EP0 stall during RX");
+ /* deselect EP0 rx FIFO */
+ outw (0, UDC_EP_NUM);
+ } else {
+ /* deselect EP0 rx FIFO */
+ outw (0, UDC_EP_NUM);
+ }
+}
+
+/* Handle endpoint 0 TX interrupt
+ * This routine implements TRM Figure 14-18.
+ */
+static void omap1510_udc_ep0_tx (struct usb_endpoint_instance *endpoint)
+{
+ unsigned short status;
+ struct usb_device_request *request = &ep0_urb->device_request;
+
+ UDCDBG ("TX on EP0");
+ /* select EP0 TX FIFO */
+ outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM);
+
+ status = inw (UDC_STAT_FLG);
+ if (status & UDC_ACK) {
+ /* Check direction */
+ if ((request->bmRequestType & USB_REQ_DIRECTION_MASK) ==
+ USB_REQ_HOST2DEVICE) {
+ /* This tx interrupt must be for a control write status
+ * stage packet.
+ */
+ UDCDBG ("ACK on EP0 control write status stage packet");
+ /* deselect EP0 TX FIFO */
+ outw (UDC_EP_Dir, UDC_EP_NUM);
+ } else {
+ /* This tx interrupt must be for a control read data
+ * stage packet.
+ */
+ int wLength = le16_to_cpu (request->wLength);
+
+ /* Update our count of bytes sent so far in this
+ * transfer.
+ */
+ endpoint->sent += endpoint->last;
+
+ /* We are finished with this transfer if we have sent
+ * all of the bytes in our tx urb (urb->actual_length)
+ * unless we need a zero-length terminating packet. We
+ * need a zero-length terminating packet if we returned
+ * fewer bytes than were requested (wLength) by the host,
+ * and the number of bytes we returned is an exact
+ * multiple of the packet size endpoint->tx_packetSize.
+ */
+ if ((endpoint->sent == ep0_urb->actual_length)
+ && ((ep0_urb->actual_length == wLength)
+ || (endpoint->last !=
+ endpoint->tx_packetSize))) {
+ /* Done with control read data stage. */
+ UDCDBG ("control read data stage complete");
+ /* deselect EP0 TX FIFO */
+ outw (UDC_EP_Dir, UDC_EP_NUM);
+ /* select EP0 RX FIFO to prepare for control
+ * read status stage.
+ */
+ outw (UDC_EP_Sel, UDC_EP_NUM);
+ /* clear the EP0 RX FIFO */
+ outw (UDC_Clr_EP, UDC_CTRL);
+ /* enable the EP0 RX FIFO */
+ outw (UDC_Set_FIFO_En, UDC_CTRL);
+ /* deselect the EP0 RX FIFO */
+ outw (0, UDC_EP_NUM);
+ } else {
+ /* We still have another packet of data to send
+ * in this control read data stage or else we
+ * need a zero-length terminating packet.
+ */
+ UDCDBG ("ACK control read data stage packet");
+ omap1510_write_noniso_tx_fifo (endpoint);
+ /* enable the EP0 tx FIFO to start transmission */
+ outw (UDC_Set_FIFO_En, UDC_CTRL);
+ /* deselect EP0 TX FIFO */
+ outw (UDC_EP_Dir, UDC_EP_NUM);
+ }
+ }
+ } else if (status & UDC_STALL) {
+ UDCDBG ("EP0 stall during TX");
+ /* deselect EP0 TX FIFO */
+ outw (UDC_EP_Dir, UDC_EP_NUM);
+ } else {
+ /* deselect EP0 TX FIFO */
+ outw (UDC_EP_Dir, UDC_EP_NUM);
+ }
+}
+
+/* Handle RX transaction on non-ISO endpoint.
+ * This function implements TRM Figure 14-27.
+ * The ep argument is a physical endpoint number for a non-ISO OUT endpoint
+ * in the range 1 to 15.
+ */
+static void omap1510_udc_epn_rx (int ep)
+{
+ unsigned short status;
+
+ /* Check endpoint status */
+ status = inw (UDC_STAT_FLG);
+
+ if (status & UDC_ACK) {
+ int nbytes;
+ struct usb_endpoint_instance *endpoint =
+ omap1510_find_ep (ep);
+
+ nbytes = omap1510_read_noniso_rx_fifo (endpoint);
+ usbd_rcv_complete (endpoint, nbytes, 0);
+
+ /* enable rx FIFO to prepare for next packet */
+ outw (UDC_Set_FIFO_En, UDC_CTRL);
+ } else if (status & UDC_STALL) {
+ UDCDBGA ("STALL on RX endpoint %d", ep);
+ } else if (status & UDC_NAK) {
+ UDCDBGA ("NAK on RX ep %d", ep);
+ } else {
+ serial_printf ("omap-bi: RX on ep %d with status %x", ep,
+ status);
+ }
+}
+
+/* Handle TX transaction on non-ISO endpoint.
+ * This function implements TRM Figure 14-29.
+ * The ep argument is a physical endpoint number for a non-ISO IN endpoint
+ * in the range 16 to 30.
+ */
+static void omap1510_udc_epn_tx (int ep)
+{
+ unsigned short status;
+
+ /*serial_printf("omap1510_udc_epn_tx( %x )\n",ep); */
+
+ /* Check endpoint status */
+ status = inw (UDC_STAT_FLG);
+
+ if (status & UDC_ACK) {
+ struct usb_endpoint_instance *endpoint =
+ omap1510_find_ep (ep);
+
+ /* We need to transmit a terminating zero-length packet now if
+ * we have sent all of the data in this URB and the transfer
+ * size was an exact multiple of the packet size.
+ */
+ if (endpoint->tx_urb
+ && (endpoint->last == endpoint->tx_packetSize)
+ && (endpoint->tx_urb->actual_length - endpoint->sent -
+ endpoint->last == 0)) {
+ /* Prepare to transmit a zero-length packet. */
+ endpoint->sent += endpoint->last;
+ /* write 0 bytes of data to FIFO */
+ omap1510_write_noniso_tx_fifo (endpoint);
+ /* enable tx FIFO to start transmission */
+ outw (UDC_Set_FIFO_En, UDC_CTRL);
+ } else if (endpoint->tx_urb
+ && endpoint->tx_urb->actual_length) {
+ /* retire the data that was just sent */
+ usbd_tx_complete (endpoint);
+ /* Check to see if we have more data ready to transmit
+ * now.
+ */
+ if (endpoint->tx_urb
+ && endpoint->tx_urb->actual_length) {
+ /* write data to FIFO */
+ omap1510_write_noniso_tx_fifo (endpoint);
+ /* enable tx FIFO to start transmission */
+ outw (UDC_Set_FIFO_En, UDC_CTRL);
+ }
+ }
+ } else if (status & UDC_STALL) {
+ UDCDBGA ("STALL on TX endpoint %d", ep);
+ } else if (status & UDC_NAK) {
+ UDCDBGA ("NAK on TX endpoint %d", ep);
+ } else {
+ /*serial_printf("omap-bi: TX on ep %d with status %x\n", ep, status); */
+ }
+}
+
+
+/*
+-------------------------------------------------------------------------------
+*/
+
+/* Handle general USB interrupts and dispatch according to type.
+ * This function implements TRM Figure 14-13.
+ */
+void omap1510_udc_irq (void)
+{
+ u16 irq_src = inw (UDC_IRQ_SRC);
+ int valid_irq = 0;
+
+ if (!(irq_src & ~UDC_SOF_Flg)) /* ignore SOF interrupts ) */
+ return;
+
+ UDCDBGA ("< IRQ #%d start >- %x", udc_interrupts, irq_src);
+ /*serial_printf("< IRQ #%d start >- %x\n", udc_interrupts, irq_src); */
+
+ if (irq_src & UDC_DS_Chg) {
+ /* Device status changed */
+ omap1510_udc_state_changed ();
+ valid_irq++;
+ }
+ if (irq_src & UDC_EP0_RX) {
+ /* Endpoint 0 receive */
+ outw (UDC_EP0_RX, UDC_IRQ_SRC); /* ack interrupt */
+ omap1510_udc_ep0_rx (udc_device->bus->endpoint_array + 0);
+ valid_irq++;
+ }
+ if (irq_src & UDC_EP0_TX) {
+ /* Endpoint 0 transmit */
+ outw (UDC_EP0_TX, UDC_IRQ_SRC); /* ack interrupt */
+ omap1510_udc_ep0_tx (udc_device->bus->endpoint_array + 0);
+ valid_irq++;
+ }
+ if (irq_src & UDC_Setup) {
+ /* Device setup */
+ omap1510_udc_setup (udc_device->bus->endpoint_array + 0);
+ valid_irq++;
+ }
+ /*if (!valid_irq) */
+ /* serial_printf("unknown interrupt, IRQ_SRC %.4x\n", irq_src); */
+ UDCDBGA ("< IRQ #%d end >", udc_interrupts);
+ udc_interrupts++;
+}
+
+/* This function implements TRM Figure 14-26. */
+void omap1510_udc_noniso_irq (void)
+{
+ unsigned short epnum;
+ unsigned short irq_src = inw (UDC_IRQ_SRC);
+ int valid_irq = 0;
+
+ if (!(irq_src & (UDC_EPn_RX | UDC_EPn_TX)))
+ return;
+
+ UDCDBGA ("non-ISO IRQ, IRQ_SRC %x", inw (UDC_IRQ_SRC));
+
+ if (irq_src & UDC_EPn_RX) { /* Endpoint N OUT transaction */
+ /* Determine the endpoint number for this interrupt */
+ epnum = (inw (UDC_EPN_STAT) & 0x0f00) >> 8;
+ UDCDBGA ("RX on ep %x", epnum);
+
+ /* acknowledge interrupt */
+ outw (UDC_EPn_RX, UDC_IRQ_SRC);
+
+ if (epnum) {
+ /* select the endpoint FIFO */
+ outw (UDC_EP_Sel | epnum, UDC_EP_NUM);
+
+ omap1510_udc_epn_rx (epnum);
+
+ /* deselect the endpoint FIFO */
+ outw (epnum, UDC_EP_NUM);
+ }
+ valid_irq++;
+ }
+ if (irq_src & UDC_EPn_TX) { /* Endpoint N IN transaction */
+ /* Determine the endpoint number for this interrupt */
+ epnum = (inw (UDC_EPN_STAT) & 0x000f) | USB_DIR_IN;
+ UDCDBGA ("TX on ep %x", epnum);
+
+ /* acknowledge interrupt */
+ outw (UDC_EPn_TX, UDC_IRQ_SRC);
+
+ if (epnum) {
+ /* select the endpoint FIFO */
+ outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM);
+
+ omap1510_udc_epn_tx (epnum);
+
+ /* deselect the endpoint FIFO */
+ outw (UDC_EP_Dir | epnum, UDC_EP_NUM);
+ }
+ valid_irq++;
+ }
+ if (!valid_irq)
+ serial_printf (": unknown non-ISO interrupt, IRQ_SRC %.4x\n",
+ irq_src);
+}
+
+/*
+-------------------------------------------------------------------------------
+*/
+
+
+/*
+ * Start of public functions.
+ */
+
+/* Called to start packet transmission. */
+int udc_endpoint_write (struct usb_endpoint_instance *endpoint)
+{
+ unsigned short epnum =
+ endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK;
+
+ UDCDBGA ("Starting transmit on ep %x", epnum);
+
+ if (endpoint->tx_urb) {
+ /* select the endpoint FIFO */
+ outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM);
+ /* write data to FIFO */
+ omap1510_write_noniso_tx_fifo (endpoint);
+ /* enable tx FIFO to start transmission */
+ outw (UDC_Set_FIFO_En, UDC_CTRL);
+ /* deselect the endpoint FIFO */
+ outw (UDC_EP_Dir | epnum, UDC_EP_NUM);
+ }
+
+ return 0;
+}
+
+/* Start to initialize h/w stuff */
+int udc_init (void)
+{
+ u16 udc_rev;
+ uchar value;
+ ulong gpio;
+ int i;
+
+ /* Let the device settle down before we start */
+ for (i = 0; i < UDC_INIT_MDELAY; i++) udelay(1000);
+
+ udc_device = NULL;
+
+ UDCDBG ("starting");
+
+ /* Check peripheral reset. Must be 1 to make sure
+ MPU TIPB peripheral reset is inactive */
+ UDCREG (ARM_RSTCT2);
+
+ /* Set and check clock control.
+ * We might ought to be using the clock control API to do
+ * this instead of fiddling with the clock registers directly
+ * here.
+ */
+ outw ((1 << 4) | (1 << 5), CLOCK_CTRL);
+ UDCREG (CLOCK_CTRL);
+
+#ifdef CONFIG_OMAP1510
+ /* This code was originally implemented for OMAP1510 and
+ * therefore is only applicable for OMAP1510 boards. For
+ * OMAP5912 or OMAP16xx the register APLL_CTRL does not
+ * exist and DPLL_CTRL is already configured.
+ */
+
+ /* Set and check APLL */
+ outw (0x0008, APLL_CTRL);
+ UDCREG (APLL_CTRL);
+ /* Set and check DPLL */
+ outw (0x2210, DPLL_CTRL);
+ UDCREG (DPLL_CTRL);
+#endif
+ /* Set and check SOFT
+ * The below line of code has been changed to perform a
+ * read-modify-write instead of a simple write for
+ * configuring the SOFT_REQ register. This allows the code
+ * to be compatible with OMAP5912 and OMAP16xx devices
+ */
+ outw ((1 << 4) | (1 << 3) | 1 | (inw(SOFT_REQ)), SOFT_REQ);
+
+ /* Short delay to wait for DPLL */
+ udelay (1000);
+
+ /* Print banner with device revision */
+ udc_rev = inw (UDC_REV) & 0xff;
+#ifdef CONFIG_OMAP1510
+ printf ("USB: TI OMAP1510 USB function module rev %d.%d\n",
+ udc_rev >> 4, udc_rev & 0xf);
+#endif
+
+#ifdef CONFIG_OMAP1610
+ printf ("USB: TI OMAP5912 USB function module rev %d.%d\n",
+ udc_rev >> 4, udc_rev & 0xf);
+#endif
+
+#ifdef CONFIG_OMAP_SX1
+ i2c_read (0x32, 0x04, 1, &value, 1);
+ value |= 0x04;
+ i2c_write (0x32, 0x04, 1, &value, 1);
+
+ i2c_read (0x32, 0x03, 1, &value, 1);
+ value |= 0x01;
+ i2c_write (0x32, 0x03, 1, &value, 1);
+
+ gpio = inl(GPIO_PIN_CONTROL_REG);
+ gpio |= 0x0002; /* A_IRDA_OFF */
+ gpio |= 0x0800; /* A_SWITCH */
+ gpio |= 0x8000; /* A_USB_ON */
+ outl (gpio, GPIO_PIN_CONTROL_REG);
+
+ gpio = inl(GPIO_DIR_CONTROL_REG);
+ gpio &= ~0x0002; /* A_IRDA_OFF */
+ gpio &= ~0x0800; /* A_SWITCH */
+ gpio &= ~0x8000; /* A_USB_ON */
+ outl (gpio, GPIO_DIR_CONTROL_REG);
+
+ gpio = inl(GPIO_DATA_OUTPUT_REG);
+ gpio |= 0x0002; /* A_IRDA_OFF */
+ gpio &= ~0x0800; /* A_SWITCH */
+ gpio &= ~0x8000; /* A_USB_ON */
+ outl (gpio, GPIO_DATA_OUTPUT_REG);
+#endif
+
+ /* The VBUS_MODE bit selects whether VBUS detection is done via
+ * software (1) or hardware (0). When software detection is
+ * selected, VBUS_CTRL selects whether USB is not connected (0)
+ * or connected (1).
+ */
+ outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0);
+ outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0);
+ UDCREGL (FUNC_MUX_CTRL_0);
+
+ /*
+ * At this point, device is ready for configuration...
+ */
+
+ UDCDBG ("disable USB interrupts");
+ outw (0, UDC_IRQ_EN);
+ UDCREG (UDC_IRQ_EN);
+
+ UDCDBG ("disable USB DMA");
+ outw (0, UDC_DMA_IRQ_EN);
+ UDCREG (UDC_DMA_IRQ_EN);
+
+ UDCDBG ("initialize SYSCON1");
+ outw (UDC_Self_Pwr | UDC_Pullup_En, UDC_SYSCON1);
+ UDCREG (UDC_SYSCON1);
+
+ return 0;
+}
+
+/* Stall endpoint */
+static void udc_stall_ep (unsigned int ep_addr)
+{
+ /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */
+ int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+
+ UDCDBGA ("stall ep_addr %d", ep_addr);
+
+ /* REVISIT?
+ * The OMAP TRM section 14.2.4.2 says we must check that the FIFO
+ * is empty before halting the endpoint. The current implementation
+ * doesn't check that the FIFO is empty.
+ */
+
+ if (!ep_num) {
+ outw (UDC_Stall_Cmd, UDC_SYSCON2);
+ } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
+ if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) {
+ /* we have a valid rx endpoint, so halt it */
+ outw (UDC_EP_Sel | ep_num, UDC_EP_NUM);
+ outw (UDC_Set_Halt, UDC_CTRL);
+ outw (ep_num, UDC_EP_NUM);
+ }
+ } else {
+ if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) {
+ /* we have a valid tx endpoint, so halt it */
+ outw (UDC_EP_Sel | UDC_EP_Dir | ep_num, UDC_EP_NUM);
+ outw (UDC_Set_Halt, UDC_CTRL);
+ outw (ep_num, UDC_EP_NUM);
+ }
+ }
+}
+
+/* Reset endpoint */
+#if 0
+static void udc_reset_ep (unsigned int ep_addr)
+{
+ /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */
+ int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+
+ UDCDBGA ("reset ep_addr %d", ep_addr);
+
+ if (!ep_num) {
+ /* control endpoint 0 can't be reset */
+ } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
+ UDCDBGA ("UDC_EP_RX(%d) = 0x%04x", ep_num,
+ inw (UDC_EP_RX (ep_num)));
+ if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) {
+ /* we have a valid rx endpoint, so reset it */
+ outw (ep_num | UDC_EP_Sel, UDC_EP_NUM);
+ outw (UDC_Reset_EP, UDC_CTRL);
+ outw (ep_num, UDC_EP_NUM);
+ UDCDBGA ("OUT endpoint %d reset", ep_num);
+ }
+ } else {
+ UDCDBGA ("UDC_EP_TX(%d) = 0x%04x", ep_num,
+ inw (UDC_EP_TX (ep_num)));
+ /* Resetting of tx endpoints seems to be causing the USB function
+ * module to fail, which causes problems when the driver is
+ * uninstalled. We'll skip resetting tx endpoints for now until
+ * we figure out what the problem is.
+ */
+#if 0
+ if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) {
+ /* we have a valid tx endpoint, so reset it */
+ outw (ep_num | UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM);
+ outw (UDC_Reset_EP, UDC_CTRL);
+ outw (ep_num | UDC_EP_Dir, UDC_EP_NUM);
+ UDCDBGA ("IN endpoint %d reset", ep_num);
+ }
+#endif
+ }
+}
+#endif
+
+/* ************************************************************************** */
+
+/**
+ * udc_check_ep - check logical endpoint
+ *
+ * Return physical endpoint number to use for this logical endpoint or zero if not valid.
+ */
+#if 0
+int udc_check_ep (int logical_endpoint, int packetsize)
+{
+ if ((logical_endpoint == 0x80) ||
+ ((logical_endpoint & 0x8f) != logical_endpoint)) {
+ return 0;
+ }
+
+ switch (packetsize) {
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ case 512:
+ break;
+ default:
+ return 0;
+ }
+
+ return EP_ADDR_TO_PHYS_EP (logical_endpoint);
+}
+#endif
+
+/*
+ * udc_setup_ep - setup endpoint
+ *
+ * Associate a physical endpoint with endpoint_instance
+ */
+void udc_setup_ep (struct usb_device_instance *device,
+ unsigned int ep, struct usb_endpoint_instance *endpoint)
+{
+ UDCDBGA ("setting up endpoint addr %x", endpoint->endpoint_address);
+
+ /* This routine gets called by bi_modinit for endpoint 0 and from
+ * bi_config for all of the other endpoints. bi_config gets called
+ * during the DEVICE_CREATE, DEVICE_CONFIGURED, and
+ * DEVICE_SET_INTERFACE events. We need to reconfigure the OMAP packet
+ * RAM after bi_config scans the selected device configuration and
+ * initializes the endpoint structures, but before this routine enables
+ * the OUT endpoint FIFOs. Since bi_config calls this routine in a
+ * loop for endpoints 1 through UDC_MAX_ENDPOINTS, we reconfigure our
+ * packet RAM here when ep==1.
+ * I really hate to do this here, but it seems like the API exported
+ * by the USB bus interface controller driver to the usbd-bi module
+ * isn't quite right so there is no good place to do this.
+ */
+ if (ep == 1) {
+ omap1510_deconfigure_device ();
+ omap1510_configure_device (device);
+ }
+
+ if (endpoint && (ep < UDC_MAX_ENDPOINTS)) {
+ int ep_addr = endpoint->endpoint_address;
+
+ if (!ep_addr) {
+ /* nothing to do for endpoint 0 */
+ } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+ /* nothing to do for IN (tx) endpoints */
+ } else { /* OUT (rx) endpoint */
+ if (endpoint->rcv_packetSize) {
+ /*struct urb* urb = &(urb_out_array[ep&0xFF]); */
+ /*urb->endpoint = endpoint; */
+ /*urb->device = device; */
+ /*urb->buffer_length = sizeof(urb->buffer); */
+
+ /*endpoint->rcv_urb = urb; */
+ omap1510_prepare_endpoint_for_rx (ep_addr);
+ }
+ }
+ }
+}
+
+/**
+ * udc_disable_ep - disable endpoint
+ * @ep:
+ *
+ * Disable specified endpoint
+ */
+#if 0
+void udc_disable_ep (unsigned int ep_addr)
+{
+ /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */
+ int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+ struct usb_endpoint_instance *endpoint = omap1510_find_ep (ep_addr); /*udc_device->bus->endpoint_array + ep; */
+
+ UDCDBGA ("disable ep_addr %d", ep_addr);
+
+ if (!ep_num) {
+ /* nothing to do for endpoint 0 */ ;
+ } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+ if (endpoint->tx_packetSize) {
+ /* we have a valid tx endpoint */
+ /*usbd_flush_tx(endpoint); */
+ endpoint->tx_urb = NULL;
+ }
+ } else {
+ if (endpoint->rcv_packetSize) {
+ /* we have a valid rx endpoint */
+ /*usbd_flush_rcv(endpoint); */
+ endpoint->rcv_urb = NULL;
+ }
+ }
+}
+#endif
+
+/* ************************************************************************** */
+
+/**
+ * udc_connected - is the USB cable connected
+ *
+ * Return non-zero if cable is connected.
+ */
+#if 0
+int udc_connected (void)
+{
+ return ((inw (UDC_DEVSTAT) & UDC_ATT) == UDC_ATT);
+}
+#endif
+
+/* Turn on the USB connection by enabling the pullup resistor */
+void udc_connect (void)
+{
+ UDCDBG ("connect, enable Pullup");
+ outl (0x00000018, FUNC_MUX_CTRL_D);
+}
+
+/* Turn off the USB connection by disabling the pullup resistor */
+void udc_disconnect (void)
+{
+ UDCDBG ("disconnect, disable Pullup");
+ outl (0x00000000, FUNC_MUX_CTRL_D);
+}
+
+/* ************************************************************************** */
+
+
+/*
+ * udc_disable_interrupts - disable interrupts
+ * switch off interrupts
+ */
+#if 0
+void udc_disable_interrupts (struct usb_device_instance *device)
+{
+ UDCDBG ("disabling all interrupts");
+ outw (0, UDC_IRQ_EN);
+}
+#endif
+
+/* ************************************************************************** */
+
+/**
+ * udc_ep0_packetsize - return ep0 packetsize
+ */
+#if 0
+int udc_ep0_packetsize (void)
+{
+ return EP0_PACKETSIZE;
+}
+#endif
+
+/* Switch on the UDC */
+void udc_enable (struct usb_device_instance *device)
+{
+ UDCDBGA ("enable device %p, status %d", device, device->status);
+
+ /* initialize driver state variables */
+ udc_devstat = 0;
+
+ /* Save the device structure pointer */
+ udc_device = device;
+
+ /* Setup ep0 urb */
+ if (!ep0_urb) {
+ ep0_urb =
+ usbd_alloc_urb (udc_device,
+ udc_device->bus->endpoint_array);
+ } else {
+ serial_printf ("udc_enable: ep0_urb already allocated %p\n",
+ ep0_urb);
+ }
+
+ UDCDBG ("Check clock status");
+ UDCREG (STATUS_REQ);
+
+ /* The VBUS_MODE bit selects whether VBUS detection is done via
+ * software (1) or hardware (0). When software detection is
+ * selected, VBUS_CTRL selects whether USB is not connected (0)
+ * or connected (1).
+ */
+ outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_CTRL | UDC_VBUS_MODE,
+ FUNC_MUX_CTRL_0);
+ UDCREGL (FUNC_MUX_CTRL_0);
+
+ omap1510_configure_device (device);
+}
+
+/* Switch off the UDC */
+void udc_disable (void)
+{
+ UDCDBG ("disable UDC");
+
+ omap1510_deconfigure_device ();
+
+ /* The VBUS_MODE bit selects whether VBUS detection is done via
+ * software (1) or hardware (0). When software detection is
+ * selected, VBUS_CTRL selects whether USB is not connected (0)
+ * or connected (1).
+ */
+ outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0);
+ outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0);
+ UDCREGL (FUNC_MUX_CTRL_0);
+
+ /* Free ep0 URB */
+ if (ep0_urb) {
+ /*usbd_dealloc_urb(ep0_urb); */
+ ep0_urb = NULL;
+ }
+
+ /* Reset device pointer.
+ * We ought to do this here to balance the initialization of udc_device
+ * in udc_enable, but some of our other exported functions get called
+ * by the bus interface driver after udc_disable, so we have to hang on
+ * to the device pointer to avoid a null pointer dereference. */
+ /* udc_device = NULL; */
+}
+
+/**
+ * udc_startup - allow udc code to do any additional startup
+ */
+void udc_startup_events (struct usb_device_instance *device)
+{
+ /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */
+ usbd_device_event_irq (device, DEVICE_INIT, 0);
+
+ /* The DEVICE_CREATE event puts the USB device in the state
+ * STATE_ATTACHED.
+ */
+ usbd_device_event_irq (device, DEVICE_CREATE, 0);
+
+ /* Some USB controller driver implementations signal
+ * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here.
+ * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED,
+ * and DEVICE_RESET causes a transition to the state STATE_DEFAULT.
+ * The OMAP USB client controller has the capability to detect when the
+ * USB cable is connected to a powered USB bus via the ATT bit in the
+ * DEVSTAT register, so we will defer the DEVICE_HUB_CONFIGURED and
+ * DEVICE_RESET events until later.
+ */
+
+ udc_enable (device);
+}
+
+/**
+ * udc_irq - do pseudo interrupts
+ */
+void udc_irq(void)
+{
+ /* Loop while we have interrupts.
+ * If we don't do this, the input chain
+ * polling delay is likely to miss
+ * host requests.
+ */
+ while (inw (UDC_IRQ_SRC) & ~UDC_SOF_Flg) {
+ /* Handle any new IRQs */
+ omap1510_udc_irq ();
+ omap1510_udc_noniso_irq ();
+ }
+}
+
+/* Flow control */
+void udc_set_nak(int epid)
+{
+ /* TODO: implement this functionality in omap1510 */
+}
+
+void udc_unset_nak (int epid)
+{
+ /* TODO: implement this functionality in omap1510 */
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c b/src/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c
new file mode 100644
index 0000000..8945c5b
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c
@@ -0,0 +1,2047 @@
+/*
+ * Intel PXA25x and IXP4xx on-chip full speed USB device controllers
+ *
+ * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker)
+ * Copyright (C) 2003 Robert Schwebel, Pengutronix
+ * Copyright (C) 2003 Benedikt Spranger, Pengutronix
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2003 Joshua Wise
+ * Copyright (C) 2012 Lukasz Dalek <luk0104@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell");
+ */
+
+#define CONFIG_USB_PXA25X_SMALL
+#define DRIVER_NAME "pxa25x_udc_linux"
+#define ARCH_HAS_PREFETCH
+
+#include <common.h>
+#include <errno.h>
+#include <asm/byteorder.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <asm/unaligned.h>
+#include <linux/compat.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/arch/pxa.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <usb/lin_gadget_compat.h>
+#include <asm/arch/pxa-regs.h>
+
+#include "pxa25x_udc.h"
+
+/*
+ * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x
+ * series processors. The UDC for the IXP 4xx series is very similar.
+ * There are fifteen endpoints, in addition to ep0.
+ *
+ * Such controller drivers work with a gadget driver. The gadget driver
+ * returns descriptors, implements configuration and data protocols used
+ * by the host to interact with this device, and allocates endpoints to
+ * the different protocol interfaces. The controller driver virtualizes
+ * usb hardware so that the gadget drivers will be more portable.
+ *
+ * This UDC hardware wants to implement a bit too much USB protocol, so
+ * it constrains the sorts of USB configuration change events that work.
+ * The errata for these chips are misleading; some "fixed" bugs from
+ * pxa250 a0/a1 b0/b1/b2 sure act like they're still there.
+ *
+ * Note that the UDC hardware supports DMA (except on IXP) but that's
+ * not used here. IN-DMA (to host) is simple enough, when the data is
+ * suitably aligned (16 bytes) ... the network stack doesn't do that,
+ * other software can. OUT-DMA is buggy in most chip versions, as well
+ * as poorly designed (data toggle not automatic). So this driver won't
+ * bother using DMA. (Mostly-working IN-DMA support was available in
+ * kernels before 2.6.23, but was never enabled or well tested.)
+ */
+
+#define DRIVER_VERSION "18-August-2012"
+#define DRIVER_DESC "PXA 25x USB Device Controller driver"
+
+static const char driver_name[] = "pxa25x_udc";
+static const char ep0name[] = "ep0";
+
+/* Watchdog */
+static inline void start_watchdog(struct pxa25x_udc *udc)
+{
+ debug("Started watchdog\n");
+ udc->watchdog.base = get_timer(0);
+ udc->watchdog.running = 1;
+}
+
+static inline void stop_watchdog(struct pxa25x_udc *udc)
+{
+ udc->watchdog.running = 0;
+ debug("Stopped watchdog\n");
+}
+
+static inline void test_watchdog(struct pxa25x_udc *udc)
+{
+ if (!udc->watchdog.running)
+ return;
+
+ debug("watchdog %ld %ld\n", get_timer(udc->watchdog.base),
+ udc->watchdog.period);
+
+ if (get_timer(udc->watchdog.base) >= udc->watchdog.period) {
+ stop_watchdog(udc);
+ udc->watchdog.function(udc);
+ }
+}
+
+static void udc_watchdog(struct pxa25x_udc *dev)
+{
+ uint32_t udccs0 = readl(&dev->regs->udccs[0]);
+
+ debug("Fired up udc_watchdog\n");
+
+ local_irq_disable();
+ if (dev->ep0state == EP0_STALL
+ && (udccs0 & UDCCS0_FST) == 0
+ && (udccs0 & UDCCS0_SST) == 0) {
+ writel(UDCCS0_FST|UDCCS0_FTF, &dev->regs->udccs[0]);
+ debug("ep0 re-stall\n");
+ start_watchdog(dev);
+ }
+ local_irq_enable();
+}
+
+#ifdef DEBUG
+
+static const char * const state_name[] = {
+ "EP0_IDLE",
+ "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
+ "EP0_END_XFER", "EP0_STALL"
+};
+
+static void
+dump_udccr(const char *label)
+{
+ u32 udccr = readl(&UDC_REGS->udccr);
+ debug("%s %02X =%s%s%s%s%s%s%s%s\n",
+ label, udccr,
+ (udccr & UDCCR_REM) ? " rem" : "",
+ (udccr & UDCCR_RSTIR) ? " rstir" : "",
+ (udccr & UDCCR_SRM) ? " srm" : "",
+ (udccr & UDCCR_SUSIR) ? " susir" : "",
+ (udccr & UDCCR_RESIR) ? " resir" : "",
+ (udccr & UDCCR_RSM) ? " rsm" : "",
+ (udccr & UDCCR_UDA) ? " uda" : "",
+ (udccr & UDCCR_UDE) ? " ude" : "");
+}
+
+static void
+dump_udccs0(const char *label)
+{
+ u32 udccs0 = readl(&UDC_REGS->udccs[0]);
+
+ debug("%s %s %02X =%s%s%s%s%s%s%s%s\n",
+ label, state_name[the_controller->ep0state], udccs0,
+ (udccs0 & UDCCS0_SA) ? " sa" : "",
+ (udccs0 & UDCCS0_RNE) ? " rne" : "",
+ (udccs0 & UDCCS0_FST) ? " fst" : "",
+ (udccs0 & UDCCS0_SST) ? " sst" : "",
+ (udccs0 & UDCCS0_DRWF) ? " dwrf" : "",
+ (udccs0 & UDCCS0_FTF) ? " ftf" : "",
+ (udccs0 & UDCCS0_IPR) ? " ipr" : "",
+ (udccs0 & UDCCS0_OPR) ? " opr" : "");
+}
+
+static void
+dump_state(struct pxa25x_udc *dev)
+{
+ u32 tmp;
+ unsigned i;
+
+ debug("%s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
+ state_name[dev->ep0state],
+ readl(&UDC_REGS->uicr1), readl(&UDC_REGS->uicr0),
+ readl(&UDC_REGS->usir1), readl(&UDC_REGS->usir0),
+ readl(&UDC_REGS->ufnrh), readl(&UDC_REGS->ufnrl));
+ dump_udccr("udccr");
+ if (dev->has_cfr) {
+ tmp = readl(&UDC_REGS->udccfr);
+ debug("udccfr %02X =%s%s\n", tmp,
+ (tmp & UDCCFR_AREN) ? " aren" : "",
+ (tmp & UDCCFR_ACM) ? " acm" : "");
+ }
+
+ if (!dev->driver) {
+ debug("no gadget driver bound\n");
+ return;
+ } else
+ debug("ep0 driver '%s'\n", "ether");
+
+ dump_udccs0("udccs0");
+ debug("ep0 IN %lu/%lu, OUT %lu/%lu\n",
+ dev->stats.write.bytes, dev->stats.write.ops,
+ dev->stats.read.bytes, dev->stats.read.ops);
+
+ for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+ if (dev->ep[i].desc == NULL)
+ continue;
+ debug("udccs%d = %02x\n", i, *dev->ep->reg_udccs);
+ }
+}
+
+#else /* DEBUG */
+
+static inline void dump_udccr(const char *label) { }
+static inline void dump_udccs0(const char *label) { }
+static inline void dump_state(struct pxa25x_udc *dev) { }
+
+#endif /* DEBUG */
+
+/*
+ * ---------------------------------------------------------------------------
+ * endpoint related parts of the api to the usb controller hardware,
+ * used by gadget driver; and the inner talker-to-hardware core.
+ * ---------------------------------------------------------------------------
+ */
+
+static void pxa25x_ep_fifo_flush(struct usb_ep *ep);
+static void nuke(struct pxa25x_ep *, int status);
+
+/* one GPIO should control a D+ pullup, so host sees this device (or not) */
+static void pullup_off(void)
+{
+ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+
+ if (mach->udc_command)
+ mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+}
+
+static void pullup_on(void)
+{
+ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+
+ if (mach->udc_command)
+ mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+
+static void pio_irq_enable(int bEndpointAddress)
+{
+ bEndpointAddress &= 0xf;
+ if (bEndpointAddress < 8) {
+ clrbits_le32(&the_controller->regs->uicr0,
+ 1 << bEndpointAddress);
+ } else {
+ bEndpointAddress -= 8;
+ clrbits_le32(&the_controller->regs->uicr1,
+ 1 << bEndpointAddress);
+ }
+}
+
+static void pio_irq_disable(int bEndpointAddress)
+{
+ bEndpointAddress &= 0xf;
+ if (bEndpointAddress < 8) {
+ setbits_le32(&the_controller->regs->uicr0,
+ 1 << bEndpointAddress);
+ } else {
+ bEndpointAddress -= 8;
+ setbits_le32(&the_controller->regs->uicr1,
+ 1 << bEndpointAddress);
+ }
+}
+
+static inline void udc_set_mask_UDCCR(int mask)
+{
+ /*
+ * The UDCCR reg contains mask and interrupt status bits,
+ * so using '|=' isn't safe as it may ack an interrupt.
+ */
+ const uint32_t mask_bits = UDCCR_REM | UDCCR_SRM | UDCCR_UDE;
+
+ mask &= mask_bits;
+ clrsetbits_le32(&the_controller->regs->udccr, ~mask_bits, mask);
+}
+
+static inline void udc_clear_mask_UDCCR(int mask)
+{
+ const uint32_t mask_bits = UDCCR_REM | UDCCR_SRM | UDCCR_UDE;
+
+ mask = ~mask & mask_bits;
+ clrbits_le32(&the_controller->regs->udccr, ~mask);
+}
+
+static inline void udc_ack_int_UDCCR(int mask)
+{
+ const uint32_t mask_bits = UDCCR_REM | UDCCR_SRM | UDCCR_UDE;
+
+ mask &= ~mask_bits;
+ clrsetbits_le32(&the_controller->regs->udccr, ~mask_bits, mask);
+}
+
+/*
+ * endpoint enable/disable
+ *
+ * we need to verify the descriptors used to enable endpoints. since pxa25x
+ * endpoint configurations are fixed, and are pretty much always enabled,
+ * there's not a lot to manage here.
+ *
+ * because pxa25x can't selectively initialize bulk (or interrupt) endpoints,
+ * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except
+ * for a single interface (with only the default altsetting) and for gadget
+ * drivers that don't halt endpoints (not reset by set_interface). that also
+ * means that if you use ISO, you must violate the USB spec rule that all
+ * iso endpoints must be in non-default altsettings.
+ */
+static int pxa25x_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct pxa25x_ep *ep;
+ struct pxa25x_udc *dev;
+
+ ep = container_of(_ep, struct pxa25x_ep, ep);
+ if (!_ep || !desc || ep->desc || _ep->name == ep0name
+ || desc->bDescriptorType != USB_DT_ENDPOINT
+ || ep->bEndpointAddress != desc->bEndpointAddress
+ || ep->fifo_size <
+ le16_to_cpu(get_unaligned(&desc->wMaxPacketSize))) {
+ printf("%s, bad ep or descriptor\n", __func__);
+ return -EINVAL;
+ }
+
+ /* xfer types must match, except that interrupt ~= bulk */
+ if (ep->bmAttributes != desc->bmAttributes
+ && ep->bmAttributes != USB_ENDPOINT_XFER_BULK
+ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+ printf("%s, %s type mismatch\n", __func__, _ep->name);
+ return -EINVAL;
+ }
+
+ /* hardware _could_ do smaller, but driver doesn't */
+ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+ && le16_to_cpu(get_unaligned(&desc->wMaxPacketSize))
+ != BULK_FIFO_SIZE)
+ || !get_unaligned(&desc->wMaxPacketSize)) {
+ printf("%s, bad %s maxpacket\n", __func__, _ep->name);
+ return -ERANGE;
+ }
+
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ printf("%s, bogus device state\n", __func__);
+ return -ESHUTDOWN;
+ }
+
+ ep->desc = desc;
+ ep->stopped = 0;
+ ep->pio_irqs = 0;
+ ep->ep.maxpacket = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));
+
+ /* flush fifo (mostly for OUT buffers) */
+ pxa25x_ep_fifo_flush(_ep);
+
+ /* ... reset halt state too, if we could ... */
+
+ debug("enabled %s\n", _ep->name);
+ return 0;
+}
+
+static int pxa25x_ep_disable(struct usb_ep *_ep)
+{
+ struct pxa25x_ep *ep;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct pxa25x_ep, ep);
+ if (!_ep || !ep->desc) {
+ printf("%s, %s not enabled\n", __func__,
+ _ep ? ep->ep.name : NULL);
+ return -EINVAL;
+ }
+ local_irq_save(flags);
+
+ nuke(ep, -ESHUTDOWN);
+
+ /* flush fifo (mostly for IN buffers) */
+ pxa25x_ep_fifo_flush(_ep);
+
+ ep->desc = NULL;
+ ep->stopped = 1;
+
+ local_irq_restore(flags);
+ debug("%s disabled\n", _ep->name);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * for the pxa25x, these can just wrap kmalloc/kfree. gadget drivers
+ * must still pass correctly initialized endpoints, since other controller
+ * drivers may care about how it's currently set up (dma issues etc).
+ */
+
+/*
+ * pxa25x_ep_alloc_request - allocate a request data structure
+ */
+static struct usb_request *
+pxa25x_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct pxa25x_request *req;
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req)
+ return NULL;
+
+ INIT_LIST_HEAD(&req->queue);
+ return &req->req;
+}
+
+
+/*
+ * pxa25x_ep_free_request - deallocate a request data structure
+ */
+static void
+pxa25x_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct pxa25x_request *req;
+
+ req = container_of(_req, struct pxa25x_request, req);
+ WARN_ON(!list_empty(&req->queue));
+ kfree(req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * done - retire a request; caller blocked irqs
+ */
+static void done(struct pxa25x_ep *ep, struct pxa25x_request *req, int status)
+{
+ unsigned stopped = ep->stopped;
+
+ list_del_init(&req->queue);
+
+ if (likely(req->req.status == -EINPROGRESS))
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ if (status && status != -ESHUTDOWN)
+ debug("complete %s req %p stat %d len %u/%u\n",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+ req->req.complete(&ep->ep, &req->req);
+ ep->stopped = stopped;
+}
+
+
+static inline void ep0_idle(struct pxa25x_udc *dev)
+{
+ dev->ep0state = EP0_IDLE;
+}
+
+static int
+write_packet(u32 *uddr, struct pxa25x_request *req, unsigned max)
+{
+ u8 *buf;
+ unsigned length, count;
+
+ debug("%s(): uddr %p\n", __func__, uddr);
+
+ buf = req->req.buf + req->req.actual;
+ prefetch(buf);
+
+ /* how big will this packet be? */
+ length = min(req->req.length - req->req.actual, max);
+ req->req.actual += length;
+
+ count = length;
+ while (likely(count--))
+ writeb(*buf++, uddr);
+
+ return length;
+}
+
+/*
+ * write to an IN endpoint fifo, as many packets as possible.
+ * irqs will use this to write the rest later.
+ * caller guarantees at least one packet buffer is ready (or a zlp).
+ */
+static int
+write_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req)
+{
+ unsigned max;
+
+ max = le16_to_cpu(get_unaligned(&ep->desc->wMaxPacketSize));
+ do {
+ unsigned count;
+ int is_last, is_short;
+
+ count = write_packet(ep->reg_uddr, req, max);
+
+ /* last packet is usually short (or a zlp) */
+ if (unlikely(count != max))
+ is_last = is_short = 1;
+ else {
+ if (likely(req->req.length != req->req.actual)
+ || req->req.zero)
+ is_last = 0;
+ else
+ is_last = 1;
+ /* interrupt/iso maxpacket may not fill the fifo */
+ is_short = unlikely(max < ep->fifo_size);
+ }
+
+ debug_cond(NOISY, "wrote %s %d bytes%s%s %d left %p\n",
+ ep->ep.name, count,
+ is_last ? "/L" : "", is_short ? "/S" : "",
+ req->req.length - req->req.actual, req);
+
+ /*
+ * let loose that packet. maybe try writing another one,
+ * double buffering might work. TSP, TPC, and TFS
+ * bit values are the same for all normal IN endpoints.
+ */
+ writel(UDCCS_BI_TPC, ep->reg_udccs);
+ if (is_short)
+ writel(UDCCS_BI_TSP, ep->reg_udccs);
+
+ /* requests complete when all IN data is in the FIFO */
+ if (is_last) {
+ done(ep, req, 0);
+ if (list_empty(&ep->queue))
+ pio_irq_disable(ep->bEndpointAddress);
+ return 1;
+ }
+
+ /*
+ * TODO experiment: how robust can fifo mode tweaking be?
+ * double buffering is off in the default fifo mode, which
+ * prevents TFS from being set here.
+ */
+
+ } while (readl(ep->reg_udccs) & UDCCS_BI_TFS);
+ return 0;
+}
+
+/*
+ * caller asserts req->pending (ep0 irq status nyet cleared); starts
+ * ep0 data stage. these chips want very simple state transitions.
+ */
+static inline
+void ep0start(struct pxa25x_udc *dev, u32 flags, const char *tag)
+{
+ writel(flags|UDCCS0_SA|UDCCS0_OPR, &dev->regs->udccs[0]);
+ writel(USIR0_IR0, &dev->regs->usir0);
+ dev->req_pending = 0;
+ debug_cond(NOISY, "%s() %s, udccs0: %02x/%02x usir: %X.%X\n",
+ __func__, tag, readl(&dev->regs->udccs[0]), flags,
+ readl(&dev->regs->usir1), readl(&dev->regs->usir0));
+}
+
+static int
+write_ep0_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req)
+{
+ unsigned count;
+ int is_short;
+
+ count = write_packet(&ep->dev->regs->uddr0, req, EP0_FIFO_SIZE);
+ ep->dev->stats.write.bytes += count;
+
+ /* last packet "must be" short (or a zlp) */
+ is_short = (count != EP0_FIFO_SIZE);
+
+ debug_cond(NOISY, "ep0in %d bytes %d left %p\n", count,
+ req->req.length - req->req.actual, req);
+
+ if (unlikely(is_short)) {
+ if (ep->dev->req_pending)
+ ep0start(ep->dev, UDCCS0_IPR, "short IN");
+ else
+ writel(UDCCS0_IPR, &ep->dev->regs->udccs[0]);
+
+ count = req->req.length;
+ done(ep, req, 0);
+ ep0_idle(ep->dev);
+
+ /*
+ * This seems to get rid of lost status irqs in some cases:
+ * host responds quickly, or next request involves config
+ * change automagic, or should have been hidden, or ...
+ *
+ * FIXME get rid of all udelays possible...
+ */
+ if (count >= EP0_FIFO_SIZE) {
+ count = 100;
+ do {
+ if ((readl(&ep->dev->regs->udccs[0]) &
+ UDCCS0_OPR) != 0) {
+ /* clear OPR, generate ack */
+ writel(UDCCS0_OPR,
+ &ep->dev->regs->udccs[0]);
+ break;
+ }
+ count--;
+ udelay(1);
+ } while (count);
+ }
+ } else if (ep->dev->req_pending)
+ ep0start(ep->dev, 0, "IN");
+
+ return is_short;
+}
+
+
+/*
+ * read_fifo - unload packet(s) from the fifo we use for usb OUT
+ * transfers and put them into the request. caller should have made
+ * sure there's at least one packet ready.
+ *
+ * returns true if the request completed because of short packet or the
+ * request buffer having filled (and maybe overran till end-of-packet).
+ */
+static int
+read_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req)
+{
+ u32 udccs;
+ u8 *buf;
+ unsigned bufferspace, count, is_short;
+
+ for (;;) {
+ /*
+ * make sure there's a packet in the FIFO.
+ * UDCCS_{BO,IO}_RPC are all the same bit value.
+ * UDCCS_{BO,IO}_RNE are all the same bit value.
+ */
+ udccs = readl(ep->reg_udccs);
+ if (unlikely((udccs & UDCCS_BO_RPC) == 0))
+ break;
+ buf = req->req.buf + req->req.actual;
+ prefetchw(buf);
+ bufferspace = req->req.length - req->req.actual;
+
+ /* read all bytes from this packet */
+ if (likely(udccs & UDCCS_BO_RNE)) {
+ count = 1 + (0x0ff & readl(ep->reg_ubcr));
+ req->req.actual += min(count, bufferspace);
+ } else /* zlp */
+ count = 0;
+ is_short = (count < ep->ep.maxpacket);
+ debug_cond(NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n",
+ ep->ep.name, udccs, count,
+ is_short ? "/S" : "",
+ req, req->req.actual, req->req.length);
+ while (likely(count-- != 0)) {
+ u8 byte = readb(ep->reg_uddr);
+
+ if (unlikely(bufferspace == 0)) {
+ /*
+ * this happens when the driver's buffer
+ * is smaller than what the host sent.
+ * discard the extra data.
+ */
+ if (req->req.status != -EOVERFLOW)
+ printf("%s overflow %d\n",
+ ep->ep.name, count);
+ req->req.status = -EOVERFLOW;
+ } else {
+ *buf++ = byte;
+ bufferspace--;
+ }
+ }
+ writel(UDCCS_BO_RPC, ep->reg_udccs);
+ /* RPC/RSP/RNE could now reflect the other packet buffer */
+
+ /* iso is one request per packet */
+ if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ if (udccs & UDCCS_IO_ROF)
+ req->req.status = -EHOSTUNREACH;
+ /* more like "is_done" */
+ is_short = 1;
+ }
+
+ /* completion */
+ if (is_short || req->req.actual == req->req.length) {
+ done(ep, req, 0);
+ if (list_empty(&ep->queue))
+ pio_irq_disable(ep->bEndpointAddress);
+ return 1;
+ }
+
+ /* finished that packet. the next one may be waiting... */
+ }
+ return 0;
+}
+
+/*
+ * special ep0 version of the above. no UBCR0 or double buffering; status
+ * handshaking is magic. most device protocols don't need control-OUT.
+ * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other
+ * protocols do use them.
+ */
+static int
+read_ep0_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req)
+{
+ u8 *buf, byte;
+ unsigned bufferspace;
+
+ buf = req->req.buf + req->req.actual;
+ bufferspace = req->req.length - req->req.actual;
+
+ while (readl(&ep->dev->regs->udccs[0]) & UDCCS0_RNE) {
+ byte = (u8)readb(&ep->dev->regs->uddr0);
+
+ if (unlikely(bufferspace == 0)) {
+ /*
+ * this happens when the driver's buffer
+ * is smaller than what the host sent.
+ * discard the extra data.
+ */
+ if (req->req.status != -EOVERFLOW)
+ printf("%s overflow\n", ep->ep.name);
+ req->req.status = -EOVERFLOW;
+ } else {
+ *buf++ = byte;
+ req->req.actual++;
+ bufferspace--;
+ }
+ }
+
+ writel(UDCCS0_OPR | UDCCS0_IPR, &ep->dev->regs->udccs[0]);
+
+ /* completion */
+ if (req->req.actual >= req->req.length)
+ return 1;
+
+ /* finished that packet. the next one may be waiting... */
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct pxa25x_request *req;
+ struct pxa25x_ep *ep;
+ struct pxa25x_udc *dev;
+ unsigned long flags;
+
+ req = container_of(_req, struct pxa25x_request, req);
+ if (unlikely(!_req || !_req->complete || !_req->buf
+ || !list_empty(&req->queue))) {
+ printf("%s, bad params\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct pxa25x_ep, ep);
+ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+ printf("%s, bad ep\n", __func__);
+ return -EINVAL;
+ }
+
+ dev = ep->dev;
+ if (unlikely(!dev->driver
+ || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+ printf("%s, bogus device state\n", __func__);
+ return -ESHUTDOWN;
+ }
+
+ /*
+ * iso is always one packet per request, that's the only way
+ * we can report per-packet status. that also helps with dma.
+ */
+ if (unlikely(ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
+ && req->req.length >
+ le16_to_cpu(get_unaligned(&ep->desc->wMaxPacketSize))))
+ return -EMSGSIZE;
+
+ debug_cond(NOISY, "%s queue req %p, len %d buf %p\n",
+ _ep->name, _req, _req->length, _req->buf);
+
+ local_irq_save(flags);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ /* kickstart this i/o queue? */
+ if (list_empty(&ep->queue) && !ep->stopped) {
+ if (ep->desc == NULL/* ep0 */) {
+ unsigned length = _req->length;
+
+ switch (dev->ep0state) {
+ case EP0_IN_DATA_PHASE:
+ dev->stats.write.ops++;
+ if (write_ep0_fifo(ep, req))
+ req = NULL;
+ break;
+
+ case EP0_OUT_DATA_PHASE:
+ dev->stats.read.ops++;
+ /* messy ... */
+ if (dev->req_config) {
+ debug("ep0 config ack%s\n",
+ dev->has_cfr ? "" : " raced");
+ if (dev->has_cfr)
+ writel(UDCCFR_AREN|UDCCFR_ACM
+ |UDCCFR_MB1,
+ &ep->dev->regs->udccfr);
+ done(ep, req, 0);
+ dev->ep0state = EP0_END_XFER;
+ local_irq_restore(flags);
+ return 0;
+ }
+ if (dev->req_pending)
+ ep0start(dev, UDCCS0_IPR, "OUT");
+ if (length == 0 ||
+ ((readl(
+ &ep->dev->regs->udccs[0])
+ & UDCCS0_RNE) != 0
+ && read_ep0_fifo(ep, req))) {
+ ep0_idle(dev);
+ done(ep, req, 0);
+ req = NULL;
+ }
+ break;
+
+ default:
+ printf("ep0 i/o, odd state %d\n",
+ dev->ep0state);
+ local_irq_restore(flags);
+ return -EL2HLT;
+ }
+ /* can the FIFO can satisfy the request immediately? */
+ } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
+ if ((readl(ep->reg_udccs) & UDCCS_BI_TFS) != 0
+ && write_fifo(ep, req))
+ req = NULL;
+ } else if ((readl(ep->reg_udccs) & UDCCS_BO_RFS) != 0
+ && read_fifo(ep, req)) {
+ req = NULL;
+ }
+
+ if (likely(req && ep->desc))
+ pio_irq_enable(ep->bEndpointAddress);
+ }
+
+ /* pio or dma irq handler advances the queue. */
+ if (likely(req != NULL))
+ list_add_tail(&req->queue, &ep->queue);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+
+/*
+ * nuke - dequeue ALL requests
+ */
+static void nuke(struct pxa25x_ep *ep, int status)
+{
+ struct pxa25x_request *req;
+
+ /* called with irqs blocked */
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct pxa25x_request,
+ queue);
+ done(ep, req, status);
+ }
+ if (ep->desc)
+ pio_irq_disable(ep->bEndpointAddress);
+}
+
+
+/* dequeue JUST ONE request */
+static int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct pxa25x_ep *ep;
+ struct pxa25x_request *req;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct pxa25x_ep, ep);
+ if (!_ep || ep->ep.name == ep0name)
+ return -EINVAL;
+
+ local_irq_save(flags);
+
+ /* make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ local_irq_restore(flags);
+ return -EINVAL;
+ }
+
+ done(ep, req, -ECONNRESET);
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct pxa25x_ep *ep;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct pxa25x_ep, ep);
+ if (unlikely(!_ep
+ || (!ep->desc && ep->ep.name != ep0name))
+ || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ printf("%s, bad ep\n", __func__);
+ return -EINVAL;
+ }
+ if (value == 0) {
+ /*
+ * this path (reset toggle+halt) is needed to implement
+ * SET_INTERFACE on normal hardware. but it can't be
+ * done from software on the PXA UDC, and the hardware
+ * forgets to do it as part of SET_INTERFACE automagic.
+ */
+ printf("only host can clear %s halt\n", _ep->name);
+ return -EROFS;
+ }
+
+ local_irq_save(flags);
+
+ if ((ep->bEndpointAddress & USB_DIR_IN) != 0
+ && ((readl(ep->reg_udccs) & UDCCS_BI_TFS) == 0
+ || !list_empty(&ep->queue))) {
+ local_irq_restore(flags);
+ return -EAGAIN;
+ }
+
+ /* FST bit is the same for control, bulk in, bulk out, interrupt in */
+ writel(UDCCS_BI_FST|UDCCS_BI_FTF, ep->reg_udccs);
+
+ /* ep0 needs special care */
+ if (!ep->desc) {
+ start_watchdog(ep->dev);
+ ep->dev->req_pending = 0;
+ ep->dev->ep0state = EP0_STALL;
+
+ /* and bulk/intr endpoints like dropping stalls too */
+ } else {
+ unsigned i;
+ for (i = 0; i < 1000; i += 20) {
+ if (readl(ep->reg_udccs) & UDCCS_BI_SST)
+ break;
+ udelay(20);
+ }
+ }
+ local_irq_restore(flags);
+
+ debug("%s halt\n", _ep->name);
+ return 0;
+}
+
+static int pxa25x_ep_fifo_status(struct usb_ep *_ep)
+{
+ struct pxa25x_ep *ep;
+
+ ep = container_of(_ep, struct pxa25x_ep, ep);
+ if (!_ep) {
+ printf("%s, bad ep\n", __func__);
+ return -ENODEV;
+ }
+ /* pxa can't report unclaimed bytes from IN fifos */
+ if ((ep->bEndpointAddress & USB_DIR_IN) != 0)
+ return -EOPNOTSUPP;
+ if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN
+ || (readl(ep->reg_udccs) & UDCCS_BO_RFS) == 0)
+ return 0;
+ else
+ return (readl(ep->reg_ubcr) & 0xfff) + 1;
+}
+
+static void pxa25x_ep_fifo_flush(struct usb_ep *_ep)
+{
+ struct pxa25x_ep *ep;
+
+ ep = container_of(_ep, struct pxa25x_ep, ep);
+ if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) {
+ printf("%s, bad ep\n", __func__);
+ return;
+ }
+
+ /* toggle and halt bits stay unchanged */
+
+ /* for OUT, just read and discard the FIFO contents. */
+ if ((ep->bEndpointAddress & USB_DIR_IN) == 0) {
+ while (((readl(ep->reg_udccs)) & UDCCS_BO_RNE) != 0)
+ (void)readb(ep->reg_uddr);
+ return;
+ }
+
+ /* most IN status is the same, but ISO can't stall */
+ writel(UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR
+ | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
+ ? 0 : UDCCS_BI_SST), ep->reg_udccs);
+}
+
+
+static struct usb_ep_ops pxa25x_ep_ops = {
+ .enable = pxa25x_ep_enable,
+ .disable = pxa25x_ep_disable,
+
+ .alloc_request = pxa25x_ep_alloc_request,
+ .free_request = pxa25x_ep_free_request,
+
+ .queue = pxa25x_ep_queue,
+ .dequeue = pxa25x_ep_dequeue,
+
+ .set_halt = pxa25x_ep_set_halt,
+ .fifo_status = pxa25x_ep_fifo_status,
+ .fifo_flush = pxa25x_ep_fifo_flush,
+};
+
+
+/* ---------------------------------------------------------------------------
+ * device-scoped parts of the api to the usb controller hardware
+ * ---------------------------------------------------------------------------
+ */
+
+static int pxa25x_udc_get_frame(struct usb_gadget *_gadget)
+{
+ return ((readl(&the_controller->regs->ufnrh) & 0x07) << 8) |
+ (readl(&the_controller->regs->ufnrl) & 0xff);
+}
+
+static int pxa25x_udc_wakeup(struct usb_gadget *_gadget)
+{
+ /* host may not have enabled remote wakeup */
+ if ((readl(&the_controller->regs->udccs[0]) & UDCCS0_DRWF) == 0)
+ return -EHOSTUNREACH;
+ udc_set_mask_UDCCR(UDCCR_RSM);
+ return 0;
+}
+
+static void stop_activity(struct pxa25x_udc *, struct usb_gadget_driver *);
+static void udc_enable(struct pxa25x_udc *);
+static void udc_disable(struct pxa25x_udc *);
+
+/*
+ * We disable the UDC -- and its 48 MHz clock -- whenever it's not
+ * in active use.
+ */
+static int pullup(struct pxa25x_udc *udc)
+{
+ if (udc->pullup)
+ pullup_on();
+ else
+ pullup_off();
+
+
+ int is_active = udc->pullup;
+ if (is_active) {
+ if (!udc->active) {
+ udc->active = 1;
+ udc_enable(udc);
+ }
+ } else {
+ if (udc->active) {
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN)
+ stop_activity(udc, udc->driver);
+ udc_disable(udc);
+ udc->active = 0;
+ }
+
+ }
+ return 0;
+}
+
+/* VBUS reporting logically comes from a transceiver */
+static int pxa25x_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+ struct pxa25x_udc *udc;
+
+ udc = container_of(_gadget, struct pxa25x_udc, gadget);
+ printf("vbus %s\n", is_active ? "supplied" : "inactive");
+ pullup(udc);
+ return 0;
+}
+
+/* drivers may have software control over D+ pullup */
+static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active)
+{
+ struct pxa25x_udc *udc;
+
+ udc = container_of(_gadget, struct pxa25x_udc, gadget);
+
+ /* not all boards support pullup control */
+ if (!udc->mach->udc_command)
+ return -EOPNOTSUPP;
+
+ udc->pullup = (is_active != 0);
+ pullup(udc);
+ return 0;
+}
+
+/*
+ * boards may consume current from VBUS, up to 100-500mA based on config.
+ * the 500uA suspend ceiling means that exclusively vbus-powered PXA designs
+ * violate USB specs.
+ */
+static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
+{
+ return -EOPNOTSUPP;
+}
+
+static const struct usb_gadget_ops pxa25x_udc_ops = {
+ .get_frame = pxa25x_udc_get_frame,
+ .wakeup = pxa25x_udc_wakeup,
+ .vbus_session = pxa25x_udc_vbus_session,
+ .pullup = pxa25x_udc_pullup,
+ .vbus_draw = pxa25x_udc_vbus_draw,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * udc_disable - disable USB device controller
+ */
+static void udc_disable(struct pxa25x_udc *dev)
+{
+ /* block all irqs */
+ udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM);
+ writel(0xff, &dev->regs->uicr0);
+ writel(0xff, &dev->regs->uicr1);
+ writel(UFNRH_SIM, &dev->regs->ufnrh);
+
+ /* if hardware supports it, disconnect from usb */
+ pullup_off();
+
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+
+ ep0_idle(dev);
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
+/*
+ * udc_reinit - initialize software state
+ */
+static void udc_reinit(struct pxa25x_udc *dev)
+{
+ u32 i;
+
+ /* device/ep0 records init */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+ dev->ep0state = EP0_IDLE;
+
+ /* basic endpoint records init */
+ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+ struct pxa25x_ep *ep = &dev->ep[i];
+
+ if (i != 0)
+ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+ ep->desc = NULL;
+ ep->stopped = 0;
+ INIT_LIST_HEAD(&ep->queue);
+ ep->pio_irqs = 0;
+ }
+
+ /* the rest was statically initialized, and is read-only */
+}
+
+/*
+ * until it's enabled, this UDC should be completely invisible
+ * to any USB host.
+ */
+static void udc_enable(struct pxa25x_udc *dev)
+{
+ debug("udc: enabling udc\n");
+
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+
+ /*
+ * Try to clear these bits before we enable the udc.
+ * Do not touch reset ack bit, we would take care of it in
+ * interrupt handle routine
+ */
+ udc_ack_int_UDCCR(UDCCR_SUSIR|UDCCR_RESIR);
+
+ ep0_idle(dev);
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+ dev->stats.irqs = 0;
+
+ /*
+ * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual:
+ * - enable UDC
+ * - if RESET is already in progress, ack interrupt
+ * - unmask reset interrupt
+ */
+ udc_set_mask_UDCCR(UDCCR_UDE);
+ if (!(readl(&dev->regs->udccr) & UDCCR_UDA))
+ udc_ack_int_UDCCR(UDCCR_RSTIR);
+
+ if (dev->has_cfr /* UDC_RES2 is defined */) {
+ /*
+ * pxa255 (a0+) can avoid a set_config race that could
+ * prevent gadget drivers from configuring correctly
+ */
+ writel(UDCCFR_ACM | UDCCFR_MB1, &dev->regs->udccfr);
+ }
+
+ /* enable suspend/resume and reset irqs */
+ udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM);
+
+ /* enable ep0 irqs */
+ clrbits_le32(&dev->regs->uicr0, UICR0_IM0);
+
+ /* if hardware supports it, pullup D+ and wait for reset */
+ pullup_on();
+}
+
+static inline void clear_ep_state(struct pxa25x_udc *dev)
+{
+ unsigned i;
+
+ /*
+ * hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
+ * fifos, and pending transactions mustn't be continued in any case.
+ */
+ for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++)
+ nuke(&dev->ep[i], -ECONNABORTED);
+}
+
+static void handle_ep0(struct pxa25x_udc *dev)
+{
+ u32 udccs0 = readl(&dev->regs->udccs[0]);
+ struct pxa25x_ep *ep = &dev->ep[0];
+ struct pxa25x_request *req;
+ union {
+ struct usb_ctrlrequest r;
+ u8 raw[8];
+ u32 word[2];
+ } u;
+
+ if (list_empty(&ep->queue))
+ req = NULL;
+ else
+ req = list_entry(ep->queue.next, struct pxa25x_request, queue);
+
+ /* clear stall status */
+ if (udccs0 & UDCCS0_SST) {
+ nuke(ep, -EPIPE);
+ writel(UDCCS0_SST, &dev->regs->udccs[0]);
+ stop_watchdog(dev);
+ ep0_idle(dev);
+ }
+
+ /* previous request unfinished? non-error iff back-to-back ... */
+ if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) {
+ nuke(ep, 0);
+ stop_watchdog(dev);
+ ep0_idle(dev);
+ }
+
+ switch (dev->ep0state) {
+ case EP0_IDLE:
+ /* late-breaking status? */
+ udccs0 = readl(&dev->regs->udccs[0]);
+
+ /* start control request? */
+ if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))
+ == (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) {
+ int i;
+
+ nuke(ep, -EPROTO);
+
+ /* read SETUP packet */
+ for (i = 0; i < 8; i++) {
+ if (unlikely(!(readl(&dev->regs->udccs[0]) &
+ UDCCS0_RNE))) {
+bad_setup:
+ debug("SETUP %d!\n", i);
+ goto stall;
+ }
+ u.raw[i] = (u8)readb(&dev->regs->uddr0);
+ }
+ if (unlikely((readl(&dev->regs->udccs[0]) &
+ UDCCS0_RNE) != 0))
+ goto bad_setup;
+
+got_setup:
+ debug("SETUP %02x.%02x v%04x i%04x l%04x\n",
+ u.r.bRequestType, u.r.bRequest,
+ le16_to_cpu(u.r.wValue),
+ le16_to_cpu(u.r.wIndex),
+ le16_to_cpu(u.r.wLength));
+
+ /* cope with automagic for some standard requests. */
+ dev->req_std = (u.r.bRequestType & USB_TYPE_MASK)
+ == USB_TYPE_STANDARD;
+ dev->req_config = 0;
+ dev->req_pending = 1;
+ switch (u.r.bRequest) {
+ /* hardware restricts gadget drivers here! */
+ case USB_REQ_SET_CONFIGURATION:
+ debug("GOT SET_CONFIGURATION\n");
+ if (u.r.bRequestType == USB_RECIP_DEVICE) {
+ /*
+ * reflect hardware's automagic
+ * up to the gadget driver.
+ */
+config_change:
+ dev->req_config = 1;
+ clear_ep_state(dev);
+ /*
+ * if !has_cfr, there's no synch
+ * else use AREN (later) not SA|OPR
+ * USIR0_IR0 acts edge sensitive
+ */
+ }
+ break;
+ /* ... and here, even more ... */
+ case USB_REQ_SET_INTERFACE:
+ if (u.r.bRequestType == USB_RECIP_INTERFACE) {
+ /*
+ * udc hardware is broken by design:
+ * - altsetting may only be zero;
+ * - hw resets all interfaces' eps;
+ * - ep reset doesn't include halt(?).
+ */
+ printf("broken set_interface (%d/%d)\n",
+ le16_to_cpu(u.r.wIndex),
+ le16_to_cpu(u.r.wValue));
+ goto config_change;
+ }
+ break;
+ /* hardware was supposed to hide this */
+ case USB_REQ_SET_ADDRESS:
+ debug("GOT SET ADDRESS\n");
+ if (u.r.bRequestType == USB_RECIP_DEVICE) {
+ ep0start(dev, 0, "address");
+ return;
+ }
+ break;
+ }
+
+ if (u.r.bRequestType & USB_DIR_IN)
+ dev->ep0state = EP0_IN_DATA_PHASE;
+ else
+ dev->ep0state = EP0_OUT_DATA_PHASE;
+
+ i = dev->driver->setup(&dev->gadget, &u.r);
+ if (i < 0) {
+ /* hardware automagic preventing STALL... */
+ if (dev->req_config) {
+ /*
+ * hardware sometimes neglects to tell
+ * tell us about config change events,
+ * so later ones may fail...
+ */
+ printf("config change %02x fail %d?\n",
+ u.r.bRequest, i);
+ return;
+ /*
+ * TODO experiment: if has_cfr,
+ * hardware didn't ACK; maybe we
+ * could actually STALL!
+ */
+ }
+ if (0) {
+stall:
+ /* uninitialized when goto stall */
+ i = 0;
+ }
+ debug("protocol STALL, "
+ "%02x err %d\n",
+ readl(&dev->regs->udccs[0]), i);
+
+ /*
+ * the watchdog timer helps deal with cases
+ * where udc seems to clear FST wrongly, and
+ * then NAKs instead of STALLing.
+ */
+ ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall");
+ start_watchdog(dev);
+ dev->ep0state = EP0_STALL;
+
+ /* deferred i/o == no response yet */
+ } else if (dev->req_pending) {
+ if (likely(dev->ep0state == EP0_IN_DATA_PHASE
+ || dev->req_std || u.r.wLength))
+ ep0start(dev, 0, "defer");
+ else
+ ep0start(dev, UDCCS0_IPR, "defer/IPR");
+ }
+
+ /* expect at least one data or status stage irq */
+ return;
+
+ } else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA))
+ == (UDCCS0_OPR|UDCCS0_SA))) {
+ unsigned i;
+
+ /*
+ * pxa210/250 erratum 131 for B0/B1 says RNE lies.
+ * still observed on a pxa255 a0.
+ */
+ debug("e131\n");
+ nuke(ep, -EPROTO);
+
+ /* read SETUP data, but don't trust it too much */
+ for (i = 0; i < 8; i++)
+ u.raw[i] = (u8)readb(&dev->regs->uddr0);
+ if ((u.r.bRequestType & USB_RECIP_MASK)
+ > USB_RECIP_OTHER)
+ goto stall;
+ if (u.word[0] == 0 && u.word[1] == 0)
+ goto stall;
+ goto got_setup;
+ } else {
+ /*
+ * some random early IRQ:
+ * - we acked FST
+ * - IPR cleared
+ * - OPR got set, without SA (likely status stage)
+ */
+ debug("random IRQ %X %X\n", udccs0,
+ readl(&dev->regs->udccs[0]));
+ writel(udccs0 & (UDCCS0_SA|UDCCS0_OPR),
+ &dev->regs->udccs[0]);
+ }
+ break;
+ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
+ if (udccs0 & UDCCS0_OPR) {
+ debug("ep0in premature status\n");
+ if (req)
+ done(ep, req, 0);
+ ep0_idle(dev);
+ } else /* irq was IPR clearing */ {
+ if (req) {
+ debug("next ep0 in packet\n");
+ /* this IN packet might finish the request */
+ (void) write_ep0_fifo(ep, req);
+ } /* else IN token before response was written */
+ }
+ break;
+ case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */
+ if (udccs0 & UDCCS0_OPR) {
+ if (req) {
+ /* this OUT packet might finish the request */
+ if (read_ep0_fifo(ep, req))
+ done(ep, req, 0);
+ /* else more OUT packets expected */
+ } /* else OUT token before read was issued */
+ } else /* irq was IPR clearing */ {
+ debug("ep0out premature status\n");
+ if (req)
+ done(ep, req, 0);
+ ep0_idle(dev);
+ }
+ break;
+ case EP0_END_XFER:
+ if (req)
+ done(ep, req, 0);
+ /*
+ * ack control-IN status (maybe in-zlp was skipped)
+ * also appears after some config change events.
+ */
+ if (udccs0 & UDCCS0_OPR)
+ writel(UDCCS0_OPR, &dev->regs->udccs[0]);
+ ep0_idle(dev);
+ break;
+ case EP0_STALL:
+ writel(UDCCS0_FST, &dev->regs->udccs[0]);
+ break;
+ }
+
+ writel(USIR0_IR0, &dev->regs->usir0);
+}
+
+static void handle_ep(struct pxa25x_ep *ep)
+{
+ struct pxa25x_request *req;
+ int is_in = ep->bEndpointAddress & USB_DIR_IN;
+ int completed;
+ u32 udccs, tmp;
+
+ do {
+ completed = 0;
+ if (likely(!list_empty(&ep->queue)))
+ req = list_entry(ep->queue.next,
+ struct pxa25x_request, queue);
+ else
+ req = NULL;
+
+ /* TODO check FST handling */
+
+ udccs = readl(ep->reg_udccs);
+ if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */
+ tmp = UDCCS_BI_TUR;
+ if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))
+ tmp |= UDCCS_BI_SST;
+ tmp &= udccs;
+ if (likely(tmp))
+ writel(tmp, ep->reg_udccs);
+ if (req && likely((udccs & UDCCS_BI_TFS) != 0))
+ completed = write_fifo(ep, req);
+
+ } else { /* irq from RPC (or for ISO, ROF) */
+ if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))
+ tmp = UDCCS_BO_SST | UDCCS_BO_DME;
+ else
+ tmp = UDCCS_IO_ROF | UDCCS_IO_DME;
+ tmp &= udccs;
+ if (likely(tmp))
+ writel(tmp, ep->reg_udccs);
+
+ /* fifos can hold packets, ready for reading... */
+ if (likely(req))
+ completed = read_fifo(ep, req);
+ else
+ pio_irq_disable(ep->bEndpointAddress);
+ }
+ ep->pio_irqs++;
+ } while (completed);
+}
+
+/*
+ * pxa25x_udc_irq - interrupt handler
+ *
+ * avoid delays in ep0 processing. the control handshaking isn't always
+ * under software control (pxa250c0 and the pxa255 are better), and delays
+ * could cause usb protocol errors.
+ */
+static struct pxa25x_udc memory;
+static int
+pxa25x_udc_irq(void)
+{
+ struct pxa25x_udc *dev = &memory;
+ int handled;
+
+ test_watchdog(dev);
+
+ dev->stats.irqs++;
+ do {
+ u32 udccr = readl(&dev->regs->udccr);
+
+ handled = 0;
+
+ /* SUSpend Interrupt Request */
+ if (unlikely(udccr & UDCCR_SUSIR)) {
+ udc_ack_int_UDCCR(UDCCR_SUSIR);
+ handled = 1;
+ debug("USB suspend\n");
+
+ if (dev->gadget.speed != USB_SPEED_UNKNOWN
+ && dev->driver
+ && dev->driver->suspend)
+ dev->driver->suspend(&dev->gadget);
+ ep0_idle(dev);
+ }
+
+ /* RESume Interrupt Request */
+ if (unlikely(udccr & UDCCR_RESIR)) {
+ udc_ack_int_UDCCR(UDCCR_RESIR);
+ handled = 1;
+ debug("USB resume\n");
+
+ if (dev->gadget.speed != USB_SPEED_UNKNOWN
+ && dev->driver
+ && dev->driver->resume)
+ dev->driver->resume(&dev->gadget);
+ }
+
+ /* ReSeT Interrupt Request - USB reset */
+ if (unlikely(udccr & UDCCR_RSTIR)) {
+ udc_ack_int_UDCCR(UDCCR_RSTIR);
+ handled = 1;
+
+ if ((readl(&dev->regs->udccr) & UDCCR_UDA) == 0) {
+ debug("USB reset start\n");
+
+ /*
+ * reset driver and endpoints,
+ * in case that's not yet done
+ */
+ stop_activity(dev, dev->driver);
+
+ } else {
+ debug("USB reset end\n");
+ dev->gadget.speed = USB_SPEED_FULL;
+ memset(&dev->stats, 0, sizeof dev->stats);
+ /* driver and endpoints are still reset */
+ }
+
+ } else {
+ u32 uicr0 = readl(&dev->regs->uicr0);
+ u32 uicr1 = readl(&dev->regs->uicr1);
+ u32 usir0 = readl(&dev->regs->usir0);
+ u32 usir1 = readl(&dev->regs->usir1);
+
+ usir0 = usir0 & ~uicr0;
+ usir1 = usir1 & ~uicr1;
+ int i;
+
+ if (unlikely(!usir0 && !usir1))
+ continue;
+
+ debug_cond(NOISY, "irq %02x.%02x\n", usir1, usir0);
+
+ /* control traffic */
+ if (usir0 & USIR0_IR0) {
+ dev->ep[0].pio_irqs++;
+ handle_ep0(dev);
+ handled = 1;
+ }
+
+ /* endpoint data transfers */
+ for (i = 0; i < 8; i++) {
+ u32 tmp = 1 << i;
+
+ if (i && (usir0 & tmp)) {
+ handle_ep(&dev->ep[i]);
+ setbits_le32(&dev->regs->usir0, tmp);
+ handled = 1;
+ }
+#ifndef CONFIG_USB_PXA25X_SMALL
+ if (usir1 & tmp) {
+ handle_ep(&dev->ep[i+8]);
+ setbits_le32(&dev->regs->usir1, tmp);
+ handled = 1;
+ }
+#endif
+ }
+ }
+
+ /* we could also ask for 1 msec SOF (SIR) interrupts */
+
+ } while (handled);
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * this uses load-time allocation and initialization (instead of
+ * doing it at run-time) to save code, eliminate fault paths, and
+ * be more obviously correct.
+ */
+static struct pxa25x_udc memory = {
+ .regs = UDC_REGS,
+
+ .gadget = {
+ .ops = &pxa25x_udc_ops,
+ .ep0 = &memory.ep[0].ep,
+ .name = driver_name,
+ },
+
+ /* control endpoint */
+ .ep[0] = {
+ .ep = {
+ .name = ep0name,
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = EP0_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .reg_udccs = &UDC_REGS->udccs[0],
+ .reg_uddr = &UDC_REGS->uddr0,
+ },
+
+ /* first group of endpoints */
+ .ep[1] = {
+ .ep = {
+ .name = "ep1in-bulk",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = BULK_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = BULK_FIFO_SIZE,
+ .bEndpointAddress = USB_DIR_IN | 1,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .reg_udccs = &UDC_REGS->udccs[1],
+ .reg_uddr = &UDC_REGS->uddr1,
+ },
+ .ep[2] = {
+ .ep = {
+ .name = "ep2out-bulk",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = BULK_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = BULK_FIFO_SIZE,
+ .bEndpointAddress = 2,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .reg_udccs = &UDC_REGS->udccs[2],
+ .reg_ubcr = &UDC_REGS->ubcr2,
+ .reg_uddr = &UDC_REGS->uddr2,
+ },
+#ifndef CONFIG_USB_PXA25X_SMALL
+ .ep[3] = {
+ .ep = {
+ .name = "ep3in-iso",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = ISO_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = ISO_FIFO_SIZE,
+ .bEndpointAddress = USB_DIR_IN | 3,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+ .reg_udccs = &UDC_REGS->udccs[3],
+ .reg_uddr = &UDC_REGS->uddr3,
+ },
+ .ep[4] = {
+ .ep = {
+ .name = "ep4out-iso",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = ISO_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = ISO_FIFO_SIZE,
+ .bEndpointAddress = 4,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+ .reg_udccs = &UDC_REGS->udccs[4],
+ .reg_ubcr = &UDC_REGS->ubcr4,
+ .reg_uddr = &UDC_REGS->uddr4,
+ },
+ .ep[5] = {
+ .ep = {
+ .name = "ep5in-int",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = INT_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = INT_FIFO_SIZE,
+ .bEndpointAddress = USB_DIR_IN | 5,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .reg_udccs = &UDC_REGS->udccs[5],
+ .reg_uddr = &UDC_REGS->uddr5,
+ },
+
+ /* second group of endpoints */
+ .ep[6] = {
+ .ep = {
+ .name = "ep6in-bulk",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = BULK_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = BULK_FIFO_SIZE,
+ .bEndpointAddress = USB_DIR_IN | 6,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .reg_udccs = &UDC_REGS->udccs[6],
+ .reg_uddr = &UDC_REGS->uddr6,
+ },
+ .ep[7] = {
+ .ep = {
+ .name = "ep7out-bulk",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = BULK_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = BULK_FIFO_SIZE,
+ .bEndpointAddress = 7,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .reg_udccs = &UDC_REGS->udccs[7],
+ .reg_ubcr = &UDC_REGS->ubcr7,
+ .reg_uddr = &UDC_REGS->uddr7,
+ },
+ .ep[8] = {
+ .ep = {
+ .name = "ep8in-iso",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = ISO_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = ISO_FIFO_SIZE,
+ .bEndpointAddress = USB_DIR_IN | 8,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+ .reg_udccs = &UDC_REGS->udccs[8],
+ .reg_uddr = &UDC_REGS->uddr8,
+ },
+ .ep[9] = {
+ .ep = {
+ .name = "ep9out-iso",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = ISO_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = ISO_FIFO_SIZE,
+ .bEndpointAddress = 9,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+ .reg_udccs = &UDC_REGS->udccs[9],
+ .reg_ubcr = &UDC_REGS->ubcr9,
+ .reg_uddr = &UDC_REGS->uddr9,
+ },
+ .ep[10] = {
+ .ep = {
+ .name = "ep10in-int",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = INT_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = INT_FIFO_SIZE,
+ .bEndpointAddress = USB_DIR_IN | 10,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .reg_udccs = &UDC_REGS->udccs[10],
+ .reg_uddr = &UDC_REGS->uddr10,
+ },
+
+ /* third group of endpoints */
+ .ep[11] = {
+ .ep = {
+ .name = "ep11in-bulk",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = BULK_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = BULK_FIFO_SIZE,
+ .bEndpointAddress = USB_DIR_IN | 11,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .reg_udccs = &UDC_REGS->udccs[11],
+ .reg_uddr = &UDC_REGS->uddr11,
+ },
+ .ep[12] = {
+ .ep = {
+ .name = "ep12out-bulk",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = BULK_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = BULK_FIFO_SIZE,
+ .bEndpointAddress = 12,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .reg_udccs = &UDC_REGS->udccs[12],
+ .reg_ubcr = &UDC_REGS->ubcr12,
+ .reg_uddr = &UDC_REGS->uddr12,
+ },
+ .ep[13] = {
+ .ep = {
+ .name = "ep13in-iso",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = ISO_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = ISO_FIFO_SIZE,
+ .bEndpointAddress = USB_DIR_IN | 13,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+ .reg_udccs = &UDC_REGS->udccs[13],
+ .reg_uddr = &UDC_REGS->uddr13,
+ },
+ .ep[14] = {
+ .ep = {
+ .name = "ep14out-iso",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = ISO_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = ISO_FIFO_SIZE,
+ .bEndpointAddress = 14,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+ .reg_udccs = &UDC_REGS->udccs[14],
+ .reg_ubcr = &UDC_REGS->ubcr14,
+ .reg_uddr = &UDC_REGS->uddr14,
+ },
+ .ep[15] = {
+ .ep = {
+ .name = "ep15in-int",
+ .ops = &pxa25x_ep_ops,
+ .maxpacket = INT_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .fifo_size = INT_FIFO_SIZE,
+ .bEndpointAddress = USB_DIR_IN | 15,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .reg_udccs = &UDC_REGS->udccs[15],
+ .reg_uddr = &UDC_REGS->uddr15,
+ },
+#endif /* !CONFIG_USB_PXA25X_SMALL */
+};
+
+static void udc_command(int cmd)
+{
+ switch (cmd) {
+ case PXA2XX_UDC_CMD_CONNECT:
+ setbits_le32(GPDR(CONFIG_USB_DEV_PULLUP_GPIO),
+ GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO));
+
+ /* enable pullup */
+ writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO),
+ GPCR(CONFIG_USB_DEV_PULLUP_GPIO));
+
+ debug("Connected to USB\n");
+ break;
+
+ case PXA2XX_UDC_CMD_DISCONNECT:
+ /* disable pullup resistor */
+ writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO),
+ GPSR(CONFIG_USB_DEV_PULLUP_GPIO));
+
+ /* setup pin as input, line will float */
+ clrbits_le32(GPDR(CONFIG_USB_DEV_PULLUP_GPIO),
+ GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO));
+
+ debug("Disconnected from USB\n");
+ break;
+ }
+}
+
+static struct pxa2xx_udc_mach_info mach_info = {
+ .udc_command = udc_command,
+};
+
+/*
+ * when a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests. then usb traffic follows until a
+ * disconnect is reported. then a host may connect again, or
+ * the driver might get unbound.
+ */
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct pxa25x_udc *dev = &memory;
+ int retval;
+ uint32_t chiprev;
+
+ if (!driver
+ || driver->speed < USB_SPEED_FULL
+ || !driver->disconnect
+ || !driver->setup)
+ return -EINVAL;
+ if (!dev)
+ return -ENODEV;
+ if (dev->driver)
+ return -EBUSY;
+
+ /* Enable clock for usb controller */
+ setbits_le32(CKEN, CKEN11_USB);
+
+ /* first hook up the driver ... */
+ dev->driver = driver;
+ dev->pullup = 1;
+
+ /* trigger chiprev-specific logic */
+ switch ((chiprev = pxa_get_cpu_revision())) {
+ case PXA255_A0:
+ dev->has_cfr = 1;
+ break;
+ case PXA250_A0:
+ case PXA250_A1:
+ /* A0/A1 "not released"; ep 13, 15 unusable */
+ /* fall through */
+ case PXA250_B2: case PXA210_B2:
+ case PXA250_B1: case PXA210_B1:
+ case PXA250_B0: case PXA210_B0:
+ /* OUT-DMA is broken ... */
+ /* fall through */
+ case PXA250_C0: case PXA210_C0:
+ break;
+ default:
+ printf("%s: unrecognized processor: %08x\n",
+ DRIVER_NAME, chiprev);
+ return -ENODEV;
+ }
+
+ the_controller = dev;
+
+ /* prepare watchdog timer */
+ dev->watchdog.running = 0;
+ dev->watchdog.period = 5000 * CONFIG_SYS_HZ / 1000000; /* 5 ms */
+ dev->watchdog.function = udc_watchdog;
+
+ udc_disable(dev);
+ udc_reinit(dev);
+
+ dev->mach = &mach_info;
+
+ dev->gadget.name = "pxa2xx_udc";
+ retval = driver->bind(&dev->gadget);
+ if (retval) {
+ printf("bind to driver %s --> error %d\n",
+ DRIVER_NAME, retval);
+ dev->driver = NULL;
+ return retval;
+ }
+
+ /*
+ * ... then enable host detection and ep0; and we're ready
+ * for set_configuration as well as eventual disconnect.
+ */
+ printf("registered gadget driver '%s'\n", DRIVER_NAME);
+
+ pullup(dev);
+ dump_state(dev);
+ return 0;
+}
+
+static void
+stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
+{
+ int i;
+
+ /* don't disconnect drivers more than once */
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+ struct pxa25x_ep *ep = &dev->ep[i];
+
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
+ stop_watchdog(dev);
+
+ /* report disconnect; the driver is already quiesced */
+ if (driver)
+ driver->disconnect(&dev->gadget);
+
+ /* re-init driver-visible data structures */
+ udc_reinit(dev);
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct pxa25x_udc *dev = the_controller;
+
+ if (!dev)
+ return -ENODEV;
+ if (!driver || driver != dev->driver || !driver->unbind)
+ return -EINVAL;
+
+ local_irq_disable();
+ dev->pullup = 0;
+ pullup(dev);
+ stop_activity(dev, driver);
+ local_irq_enable();
+
+ driver->unbind(&dev->gadget);
+ dev->driver = NULL;
+
+ printf("unregistered gadget driver '%s'\n", DRIVER_NAME);
+ dump_state(dev);
+
+ the_controller = NULL;
+
+ clrbits_le32(CKEN, CKEN11_USB);
+
+ return 0;
+}
+
+extern void udc_disconnect(void)
+{
+ setbits_le32(CKEN, CKEN11_USB);
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+ udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+ clrbits_le32(CKEN, CKEN11_USB);
+}
+
+/*-------------------------------------------------------------------------*/
+
+extern int
+usb_gadget_handle_interrupts(void)
+{
+ return pxa25x_udc_irq();
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h b/src/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h
new file mode 100644
index 0000000..f543b2d
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h
@@ -0,0 +1,150 @@
+/*
+ * Intel PXA25x on-chip full speed USB device controller
+ *
+ * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2012 Lukasz Dalek <luk0104@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __LINUX_USB_GADGET_PXA25X_H
+#define __LINUX_USB_GADGET_PXA25X_H
+
+#include <linux/types.h>
+#include <asm/arch/regs-usb.h>
+
+/*
+ * Prefetching support - only ARMv5.
+ */
+
+#ifdef ARCH_HAS_PREFETCH
+static inline void prefetch(const void *ptr)
+{
+ __asm__ __volatile__(
+ "pld\t%a0"
+ :
+ : "p" (ptr)
+ : "cc");
+}
+
+#define prefetchw(ptr) prefetch(ptr)
+#endif /* ARCH_HAS_PREFETCH */
+
+/*-------------------------------------------------------------------------*/
+
+#define UDC_REGS ((struct pxa25x_udc_regs *)PXA25X_UDC_BASE)
+
+/*-------------------------------------------------------------------------*/
+
+struct pxa2xx_udc_mach_info {
+ int (*udc_is_connected)(void); /* do we see host? */
+ void (*udc_command)(int cmd);
+#define PXA2XX_UDC_CMD_CONNECT 0 /* let host see us */
+#define PXA2XX_UDC_CMD_DISCONNECT 1 /* so host won't see us */
+};
+
+struct pxa25x_udc;
+
+struct pxa25x_ep {
+ struct usb_ep ep;
+ struct pxa25x_udc *dev;
+
+ const struct usb_endpoint_descriptor *desc;
+ struct list_head queue;
+ unsigned long pio_irqs;
+
+ unsigned short fifo_size;
+ u8 bEndpointAddress;
+ u8 bmAttributes;
+
+ unsigned stopped:1;
+
+ /* UDCCS = UDC Control/Status for this EP
+ * UBCR = UDC Byte Count Remaining (contents of OUT fifo)
+ * UDDR = UDC Endpoint Data Register (the fifo)
+ * DRCM = DMA Request Channel Map
+ */
+ u32 *reg_udccs;
+ u32 *reg_ubcr;
+ u32 *reg_uddr;
+};
+
+struct pxa25x_request {
+ struct usb_request req;
+ struct list_head queue;
+};
+
+enum ep0_state {
+ EP0_IDLE,
+ EP0_IN_DATA_PHASE,
+ EP0_OUT_DATA_PHASE,
+ EP0_END_XFER,
+ EP0_STALL,
+};
+
+#define EP0_FIFO_SIZE 16U
+#define BULK_FIFO_SIZE 64U
+#define ISO_FIFO_SIZE 256U
+#define INT_FIFO_SIZE 8U
+
+struct udc_stats {
+ struct ep0stats {
+ unsigned long ops;
+ unsigned long bytes;
+ } read, write;
+ unsigned long irqs;
+};
+
+#ifdef CONFIG_USB_PXA25X_SMALL
+/* when memory's tight, SMALL config saves code+data. */
+#define PXA_UDC_NUM_ENDPOINTS 3
+#endif
+
+#ifndef PXA_UDC_NUM_ENDPOINTS
+#define PXA_UDC_NUM_ENDPOINTS 16
+#endif
+
+struct pxa25x_watchdog {
+ unsigned running:1;
+ ulong period;
+ ulong base;
+ struct pxa25x_udc *udc;
+
+ void (*function)(struct pxa25x_udc *udc);
+};
+
+struct pxa25x_udc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct pxa25x_udc_regs *regs;
+
+ enum ep0_state ep0state;
+ struct udc_stats stats;
+ unsigned got_irq:1,
+ pullup:1,
+ has_cfr:1,
+ req_pending:1,
+ req_std:1,
+ req_config:1,
+ active:1;
+
+ struct clk *clk;
+ struct pxa2xx_udc_mach_info *mach;
+ u64 dma_mask;
+ struct pxa25x_ep ep[PXA_UDC_NUM_ENDPOINTS];
+
+ struct pxa25x_watchdog watchdog;
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct pxa25x_udc *the_controller;
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef DEBUG
+# define NOISY 0
+#endif
+
+#endif /* __LINUX_USB_GADGET_PXA25X_H */
diff --git a/src/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c b/src/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c
new file mode 100644
index 0000000..733558d
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c
@@ -0,0 +1,703 @@
+/*
+ * PXA27x USB device driver for u-boot.
+ *
+ * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>
+ * Copyright (C) 2008 Vivek Kutal <vivek.kutal@azingo.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+
+#include <common.h>
+#include <config.h>
+#include <asm/byteorder.h>
+#include <usbdevice.h>
+#include <asm/arch/hardware.h>
+#include <asm/io.h>
+#include <usb/pxa27x_udc.h>
+#include <usb/udc.h>
+
+#include "ep0.h"
+
+/* number of endpoints on this UDC */
+#define UDC_MAX_ENDPOINTS 24
+
+static struct urb *ep0_urb;
+static struct usb_device_instance *udc_device;
+static int ep0state = EP0_IDLE;
+
+#ifdef USBDDBG
+static void udc_dump_buffer(char *name, u8 *buf, int len)
+{
+ usbdbg("%s - buf %p, len %d", name, buf, len);
+ print_buffer(0, buf, 1, len, 0);
+}
+#else
+#define udc_dump_buffer(name, buf, len) /* void */
+#endif
+
+static inline void udc_ack_int_UDCCR(int mask)
+{
+ writel(readl(USIR1) | mask, USIR1);
+}
+
+/*
+ * If the endpoint has an active tx_urb, then the next packet of data from the
+ * URB is written to the tx FIFO.
+ * The total amount of data in the urb is given by urb->actual_length.
+ * The maximum amount of data that can be sent in any one packet is given by
+ * endpoint->tx_packetSize.
+ * The number of data bytes from this URB that have already been transmitted
+ * is given by endpoint->sent.
+ * endpoint->last is updated by this routine with the number of data bytes
+ * transmitted in this packet.
+ */
+static int udc_write_urb(struct usb_endpoint_instance *endpoint)
+{
+ struct urb *urb = endpoint->tx_urb;
+ int ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK;
+ u32 *data32 = (u32 *) urb->buffer;
+ u8 *data8 = (u8 *) urb->buffer;
+ unsigned int i, n, w, b, is_short;
+ int timeout = 2000; /* 2ms */
+
+ if (!urb || !urb->actual_length)
+ return -1;
+
+ n = MIN(urb->actual_length - endpoint->sent, endpoint->tx_packetSize);
+ if (n <= 0)
+ return -1;
+
+ usbdbg("write urb on ep %d", ep_num);
+#if defined(USBDDBG) && defined(USBDPARANOIA)
+ usbdbg("urb: buf %p, buf_len %d, actual_len %d",
+ urb->buffer, urb->buffer_length, urb->actual_length);
+ usbdbg("endpoint: sent %d, tx_packetSize %d, last %d",
+ endpoint->sent, endpoint->tx_packetSize, endpoint->last);
+#endif
+
+ is_short = n != endpoint->tx_packetSize;
+ w = n / 4;
+ b = n % 4;
+ usbdbg("n %d%s w %d b %d", n, is_short ? "-s" : "", w, b);
+ udc_dump_buffer("urb write", data8 + endpoint->sent, n);
+
+ /* Prepare for data send */
+ if (ep_num)
+ writel(UDCCSR_PC ,UDCCSN(ep_num));
+
+ for (i = 0; i < w; i++)
+ writel(data32[endpoint->sent / 4 + i], UDCDN(ep_num));
+
+ for (i = 0; i < b; i++)
+ writeb(data8[endpoint->sent + w * 4 + i], UDCDN(ep_num));
+
+ /* Set "Packet Complete" if less data then tx_packetSize */
+ if (is_short)
+ writel(ep_num ? UDCCSR_SP : UDCCSR0_IPR, UDCCSN(ep_num));
+
+ /* Wait for data sent */
+ if (ep_num) {
+ while (!(readl(UDCCSN(ep_num)) & UDCCSR_PC)) {
+ if (timeout-- == 0)
+ return -1;
+ else
+ udelay(1);
+ }
+ }
+
+ endpoint->last = n;
+
+ if (ep_num) {
+ usbd_tx_complete(endpoint);
+ } else {
+ endpoint->sent += n;
+ endpoint->last -= n;
+ }
+
+ if (endpoint->sent >= urb->actual_length) {
+ urb->actual_length = 0;
+ endpoint->sent = 0;
+ endpoint->last = 0;
+ }
+
+ if ((endpoint->sent >= urb->actual_length) && (!ep_num)) {
+ usbdbg("ep0 IN stage done");
+ if (is_short)
+ ep0state = EP0_IDLE;
+ else
+ ep0state = EP0_XFER_COMPLETE;
+ }
+
+ return 0;
+}
+
+static int udc_read_urb(struct usb_endpoint_instance *endpoint)
+{
+ struct urb *urb = endpoint->rcv_urb;
+ int ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK;
+ u32 *data32 = (u32 *) urb->buffer;
+ unsigned int i, n;
+
+ usbdbg("read urb on ep %d", ep_num);
+#if defined(USBDDBG) && defined(USBDPARANOIA)
+ usbdbg("urb: buf %p, buf_len %d, actual_len %d",
+ urb->buffer, urb->buffer_length, urb->actual_length);
+ usbdbg("endpoint: rcv_packetSize %d",
+ endpoint->rcv_packetSize);
+#endif
+
+ if (readl(UDCCSN(ep_num)) & UDCCSR_BNE)
+ n = readl(UDCBCN(ep_num)) & 0x3ff;
+ else /* zlp */
+ n = 0;
+
+ usbdbg("n %d%s", n, n != endpoint->rcv_packetSize ? "-s" : "");
+ for (i = 0; i < n; i += 4)
+ data32[urb->actual_length / 4 + i / 4] = readl(UDCDN(ep_num));
+
+ udc_dump_buffer("urb read", (u8 *) data32, urb->actual_length + n);
+ usbd_rcv_complete(endpoint, n, 0);
+
+ return 0;
+}
+
+static int udc_read_urb_ep0(void)
+{
+ u32 *data32 = (u32 *) ep0_urb->buffer;
+ u8 *data8 = (u8 *) ep0_urb->buffer;
+ unsigned int i, n, w, b;
+
+ usbdbg("read urb on ep 0");
+#if defined(USBDDBG) && defined(USBDPARANOIA)
+ usbdbg("urb: buf %p, buf_len %d, actual_len %d",
+ ep0_urb->buffer, ep0_urb->buffer_length, ep0_urb->actual_length);
+#endif
+
+ n = readl(UDCBCR0);
+ w = n / 4;
+ b = n % 4;
+
+ for (i = 0; i < w; i++) {
+ data32[ep0_urb->actual_length / 4 + i] = readl(UDCDN(0));
+ /* ep0_urb->actual_length += 4; */
+ }
+
+ for (i = 0; i < b; i++) {
+ data8[ep0_urb->actual_length + w * 4 + i] = readb(UDCDN(0));
+ /* ep0_urb->actual_length++; */
+ }
+
+ ep0_urb->actual_length += n;
+
+ udc_dump_buffer("urb read", (u8 *) data32, ep0_urb->actual_length);
+
+ writel(UDCCSR0_OPC | UDCCSR0_IPR, UDCCSR0);
+ if (ep0_urb->actual_length == ep0_urb->device_request.wLength)
+ return 1;
+
+ return 0;
+}
+
+static void udc_handle_ep0(struct usb_endpoint_instance *endpoint)
+{
+ u32 udccsr0 = readl(UDCCSR0);
+ u32 *data = (u32 *) &ep0_urb->device_request;
+ int i;
+
+ usbdbg("udccsr0 %x", udccsr0);
+
+ /* Clear stall status */
+ if (udccsr0 & UDCCSR0_SST) {
+ usberr("clear stall status");
+ writel(UDCCSR0_SST, UDCCSR0);
+ ep0state = EP0_IDLE;
+ }
+
+ /* previous request unfinished? non-error iff back-to-back ... */
+ if ((udccsr0 & UDCCSR0_SA) != 0 && ep0state != EP0_IDLE)
+ ep0state = EP0_IDLE;
+
+ switch (ep0state) {
+
+ case EP0_IDLE:
+ udccsr0 = readl(UDCCSR0);
+ /* Start control request? */
+ if ((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE))
+ == (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)) {
+
+ /* Read SETUP packet.
+ * SETUP packet size is 8 bytes (aka 2 words)
+ */
+ usbdbg("try reading SETUP packet");
+ for (i = 0; i < 2; i++) {
+ if ((readl(UDCCSR0) & UDCCSR0_RNE) == 0) {
+ usberr("setup packet too short:%d", i);
+ goto stall;
+ }
+ data[i] = readl(UDCDR0);
+ }
+
+ writel(readl(UDCCSR0) | UDCCSR0_OPC | UDCCSR0_SA, UDCCSR0);
+ if ((readl(UDCCSR0) & UDCCSR0_RNE) != 0) {
+ usberr("setup packet too long");
+ goto stall;
+ }
+
+ udc_dump_buffer("ep0 setup read", (u8 *) data, 8);
+
+ if (ep0_urb->device_request.wLength == 0) {
+ usbdbg("Zero Data control Packet\n");
+ if (ep0_recv_setup(ep0_urb)) {
+ usberr("Invalid Setup Packet\n");
+ udc_dump_buffer("ep0 setup read",
+ (u8 *)data, 8);
+ goto stall;
+ }
+ writel(UDCCSR0_IPR, UDCCSR0);
+ ep0state = EP0_IDLE;
+ } else {
+ /* Check direction */
+ if ((ep0_urb->device_request.bmRequestType &
+ USB_REQ_DIRECTION_MASK)
+ == USB_REQ_HOST2DEVICE) {
+ ep0state = EP0_OUT_DATA;
+ ep0_urb->buffer =
+ (u8 *)ep0_urb->buffer_data;
+ ep0_urb->buffer_length =
+ sizeof(ep0_urb->buffer_data);
+ ep0_urb->actual_length = 0;
+ writel(UDCCSR0_IPR, UDCCSR0);
+ } else {
+ /* The ep0_recv_setup function has
+ * already placed our response packet
+ * data in ep0_urb->buffer and the
+ * packet length in
+ * ep0_urb->actual_length.
+ */
+ if (ep0_recv_setup(ep0_urb)) {
+stall:
+ usberr("Invalid setup packet");
+ udc_dump_buffer("ep0 setup read"
+ , (u8 *) data, 8);
+ ep0state = EP0_IDLE;
+
+ writel(UDCCSR0_SA |
+ UDCCSR0_OPC | UDCCSR0_FST |
+ UDCCS0_FTF, UDCCSR0);
+
+ return;
+ }
+
+ endpoint->tx_urb = ep0_urb;
+ endpoint->sent = 0;
+ usbdbg("EP0_IN_DATA");
+ ep0state = EP0_IN_DATA;
+ if (udc_write_urb(endpoint) < 0)
+ goto stall;
+
+ }
+ }
+ return;
+ } else if ((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA))
+ == (UDCCSR0_OPC|UDCCSR0_SA)) {
+ usberr("Setup Active but no data. Stalling ....\n");
+ goto stall;
+ } else {
+ usbdbg("random early IRQs");
+ /* Some random early IRQs:
+ * - we acked FST
+ * - IPR cleared
+ * - OPC got set, without SA (likely status stage)
+ */
+ writel(udccsr0 & (UDCCSR0_SA | UDCCSR0_OPC), UDCCSR0);
+ }
+ break;
+
+ case EP0_OUT_DATA:
+
+ if ((udccsr0 & UDCCSR0_OPC) && !(udccsr0 & UDCCSR0_SA)) {
+ if (udc_read_urb_ep0()) {
+read_complete:
+ ep0state = EP0_IDLE;
+ if (ep0_recv_setup(ep0_urb)) {
+ /* Not a setup packet, stall next
+ * EP0 transaction
+ */
+ udc_dump_buffer("ep0 setup read",
+ (u8 *) data, 8);
+ usberr("can't parse setup packet\n");
+ goto stall;
+ }
+ }
+ } else if (!(udccsr0 & UDCCSR0_OPC) &&
+ !(udccsr0 & UDCCSR0_IPR)) {
+ if (ep0_urb->device_request.wLength ==
+ ep0_urb->actual_length)
+ goto read_complete;
+
+ usberr("Premature Status\n");
+ ep0state = EP0_IDLE;
+ }
+ break;
+
+ case EP0_IN_DATA:
+ /* GET_DESCRIPTOR etc */
+ if (udccsr0 & UDCCSR0_OPC) {
+ writel(UDCCSR0_OPC | UDCCSR0_FTF, UDCCSR0);
+ usberr("ep0in premature status");
+ ep0state = EP0_IDLE;
+ } else {
+ /* irq was IPR clearing */
+ if (udc_write_urb(endpoint) < 0) {
+ usberr("ep0_write_error\n");
+ goto stall;
+ }
+ }
+ break;
+
+ case EP0_XFER_COMPLETE:
+ writel(UDCCSR0_IPR, UDCCSR0);
+ ep0state = EP0_IDLE;
+ break;
+
+ default:
+ usbdbg("Default\n");
+ }
+ writel(USIR0_IR0, USIR0);
+}
+
+static void udc_handle_ep(struct usb_endpoint_instance *endpoint)
+{
+ int ep_addr = endpoint->endpoint_address;
+ int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+ int ep_isout = (ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT;
+
+ u32 flags = readl(UDCCSN(ep_num)) & (UDCCSR_SST | UDCCSR_TRN);
+ if (flags)
+ writel(flags, UDCCSN(ep_num));
+
+ if (ep_isout)
+ udc_read_urb(endpoint);
+ else
+ udc_write_urb(endpoint);
+
+ writel(UDCCSR_PC, UDCCSN(ep_num));
+}
+
+static void udc_state_changed(void)
+{
+
+ writel(readl(UDCCR) | UDCCR_SMAC, UDCCR);
+
+ usbdbg("New UDC settings are: conf %d - inter %d - alter %d",
+ (readl(UDCCR) & UDCCR_ACN) >> UDCCR_ACN_S,
+ (readl(UDCCR) & UDCCR_AIN) >> UDCCR_AIN_S,
+ (readl(UDCCR) & UDCCR_AAISN) >> UDCCR_AAISN_S);
+
+ usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0);
+ writel(UDCISR1_IRCC, UDCISR1);
+}
+
+void udc_irq(void)
+{
+ int handled;
+ struct usb_endpoint_instance *endpoint;
+ int ep_num, i;
+ u32 udcisr0;
+
+ do {
+ handled = 0;
+ /* Suspend Interrupt Request */
+ if (readl(USIR1) & UDCCR_SUSIR) {
+ usbdbg("Suspend\n");
+ udc_ack_int_UDCCR(UDCCR_SUSIR);
+ handled = 1;
+ ep0state = EP0_IDLE;
+ }
+
+ /* Resume Interrupt Request */
+ if (readl(USIR1) & UDCCR_RESIR) {
+ udc_ack_int_UDCCR(UDCCR_RESIR);
+ handled = 1;
+ usbdbg("USB resume\n");
+ }
+
+ if (readl(USIR1) & (1<<31)) {
+ handled = 1;
+ udc_state_changed();
+ }
+
+ /* Reset Interrupt Request */
+ if (readl(USIR1) & UDCCR_RSTIR) {
+ udc_ack_int_UDCCR(UDCCR_RSTIR);
+ handled = 1;
+ usbdbg("Reset\n");
+ usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
+ } else {
+ if (readl(USIR0))
+ usbdbg("UISR0: %x \n", readl(USIR0));
+
+ if (readl(USIR0) & 0x2)
+ writel(0x2, USIR0);
+
+ /* Control traffic */
+ if (readl(USIR0) & USIR0_IR0) {
+ handled = 1;
+ writel(USIR0_IR0, USIR0);
+ udc_handle_ep0(udc_device->bus->endpoint_array);
+ }
+
+ endpoint = udc_device->bus->endpoint_array;
+ for (i = 0; i < udc_device->bus->max_endpoints; i++) {
+ ep_num = (endpoint[i].endpoint_address) &
+ USB_ENDPOINT_NUMBER_MASK;
+ if (!ep_num)
+ continue;
+ udcisr0 = readl(UDCISR0);
+ if (udcisr0 &
+ UDCISR_INT(ep_num, UDC_INT_PACKETCMP)) {
+ writel(UDCISR_INT(ep_num, UDC_INT_PACKETCMP),
+ UDCISR0);
+ udc_handle_ep(&endpoint[i]);
+ }
+ }
+ }
+
+ } while (handled);
+}
+
+/* The UDCCR reg contains mask and interrupt status bits,
+ * so using '|=' isn't safe as it may ack an interrupt.
+ */
+#define UDCCR_OEN (1 << 31) /* On-the-Go Enable */
+#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_UDE)
+
+static inline void udc_set_mask_UDCCR(int mask)
+{
+ writel((readl(UDCCR) & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS), UDCCR);
+}
+
+static inline void udc_clear_mask_UDCCR(int mask)
+{
+ writel((readl(UDCCR) & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS), UDCCR);
+}
+
+static void pio_irq_enable(int ep_num)
+{
+ if (ep_num < 16)
+ writel(readl(UDCICR0) | 3 << (ep_num * 2), UDCICR0);
+ else {
+ ep_num -= 16;
+ writel(readl(UDCICR1) | 3 << (ep_num * 2), UDCICR1);
+ }
+}
+
+/*
+ * udc_set_nak
+ *
+ * Allow upper layers to signal lower layers should not accept more RX data
+ */
+void udc_set_nak(int ep_num)
+{
+ /* TODO */
+}
+
+/*
+ * udc_unset_nak
+ *
+ * Suspend sending of NAK tokens for DATA OUT tokens on a given endpoint.
+ * Switch off NAKing on this endpoint to accept more data output from host.
+ */
+void udc_unset_nak(int ep_num)
+{
+ /* TODO */
+}
+
+int udc_endpoint_write(struct usb_endpoint_instance *endpoint)
+{
+ return udc_write_urb(endpoint);
+}
+
+/* Associate a physical endpoint with endpoint instance */
+void udc_setup_ep(struct usb_device_instance *device, unsigned int id,
+ struct usb_endpoint_instance *endpoint)
+{
+ int ep_num, ep_addr, ep_isout, ep_type, ep_size;
+ int config, interface, alternate;
+ u32 tmp;
+
+ usbdbg("setting up endpoint id %d", id);
+
+ if (!endpoint) {
+ usberr("endpoint void!");
+ return;
+ }
+
+ ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK;
+ if (ep_num >= UDC_MAX_ENDPOINTS) {
+ usberr("unable to setup ep %d!", ep_num);
+ return;
+ }
+
+ pio_irq_enable(ep_num);
+ if (ep_num == 0) {
+ /* Done for ep0 */
+ return;
+ }
+
+ config = 1;
+ interface = 0;
+ alternate = 0;
+
+ usbdbg("config %d - interface %d - alternate %d",
+ config, interface, alternate);
+
+ ep_addr = endpoint->endpoint_address;
+ ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+ ep_isout = (ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT;
+ ep_type = ep_isout ? endpoint->rcv_attributes : endpoint->tx_attributes;
+ ep_size = ep_isout ? endpoint->rcv_packetSize : endpoint->tx_packetSize;
+
+ usbdbg("addr %x, num %d, dir %s, type %s, packet size %d",
+ ep_addr, ep_num,
+ ep_isout ? "out" : "in",
+ ep_type == USB_ENDPOINT_XFER_ISOC ? "isoc" :
+ ep_type == USB_ENDPOINT_XFER_BULK ? "bulk" :
+ ep_type == USB_ENDPOINT_XFER_INT ? "int" : "???",
+ ep_size
+ );
+
+ /* Configure UDCCRx */
+ tmp = 0;
+ tmp |= (config << UDCCONR_CN_S) & UDCCONR_CN;
+ tmp |= (interface << UDCCONR_IN_S) & UDCCONR_IN;
+ tmp |= (alternate << UDCCONR_AISN_S) & UDCCONR_AISN;
+ tmp |= (ep_num << UDCCONR_EN_S) & UDCCONR_EN;
+ tmp |= (ep_type << UDCCONR_ET_S) & UDCCONR_ET;
+ tmp |= ep_isout ? 0 : UDCCONR_ED;
+ tmp |= (ep_size << UDCCONR_MPS_S) & UDCCONR_MPS;
+ tmp |= UDCCONR_EE;
+
+ writel(tmp, UDCCN(ep_num));
+
+ usbdbg("UDCCR%c = %x", 'A' + ep_num-1, readl(UDCCN(ep_num)));
+ usbdbg("UDCCSR%c = %x", 'A' + ep_num-1, readl(UDCCSN(ep_num)));
+}
+
+/* Connect the USB device to the bus */
+void udc_connect(void)
+{
+ usbdbg("UDC connect");
+
+#ifdef CONFIG_USB_DEV_PULLUP_GPIO
+ /* Turn on the USB connection by enabling the pullup resistor */
+ writel(readl(GPDR(CONFIG_USB_DEV_PULLUP_GPIO))
+ | GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO),
+ GPDR(CONFIG_USB_DEV_PULLUP_GPIO));
+ writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), GPSR(CONFIG_USB_DEV_PULLUP_GPIO));
+#else
+ /* Host port 2 transceiver D+ pull up enable */
+ writel(readl(UP2OCR) | UP2OCR_DPPUE, UP2OCR);
+#endif
+}
+
+/* Disconnect the USB device to the bus */
+void udc_disconnect(void)
+{
+ usbdbg("UDC disconnect");
+
+#ifdef CONFIG_USB_DEV_PULLUP_GPIO
+ /* Turn off the USB connection by disabling the pullup resistor */
+ writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), GPCR(CONFIG_USB_DEV_PULLUP_GPIO));
+#else
+ /* Host port 2 transceiver D+ pull up disable */
+ writel(readl(UP2OCR) & ~UP2OCR_DPPUE, UP2OCR);
+#endif
+}
+
+/* Switch on the UDC */
+void udc_enable(struct usb_device_instance *device)
+{
+
+ ep0state = EP0_IDLE;
+
+ /* enable endpoint 0, A, B's Packet Complete Interrupt. */
+ writel(0xffffffff, UDCICR0);
+ writel(0xa8000000, UDCICR1);
+
+ /* clear the interrupt status/control registers */
+ writel(0xffffffff, UDCISR0);
+ writel(0xffffffff, UDCISR1);
+
+ /* set UDC-enable */
+ udc_set_mask_UDCCR(UDCCR_UDE);
+
+ udc_device = device;
+ if (!ep0_urb)
+ ep0_urb = usbd_alloc_urb(udc_device,
+ udc_device->bus->endpoint_array);
+ else
+ usbinfo("ep0_urb %p already allocated", ep0_urb);
+
+ usbdbg("UDC Enabled\n");
+}
+
+/* Need to check this again */
+void udc_disable(void)
+{
+ usbdbg("disable UDC");
+
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+
+ /* Disable clock for USB device */
+ writel(readl(CKEN) & ~CKEN11_USB, CKEN);
+
+ /* Free ep0 URB */
+ if (ep0_urb) {
+ usbd_dealloc_urb(ep0_urb);
+ ep0_urb = NULL;
+ }
+
+ /* Reset device pointer */
+ udc_device = NULL;
+}
+
+/* Allow udc code to do any additional startup */
+void udc_startup_events(struct usb_device_instance *device)
+{
+ /* The DEVICE_INIT event puts the USB device in the state STATE_INIT */
+ usbd_device_event_irq(device, DEVICE_INIT, 0);
+
+ /* The DEVICE_CREATE event puts the USB device in the state
+ * STATE_ATTACHED */
+ usbd_device_event_irq(device, DEVICE_CREATE, 0);
+
+ /* Some USB controller driver implementations signal
+ * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here.
+ * DEVICE_HUB_CONFIGURED causes a transition to the state
+ * STATE_POWERED, and DEVICE_RESET causes a transition to
+ * the state STATE_DEFAULT.
+ */
+ udc_enable(device);
+}
+
+/* Initialize h/w stuff */
+int udc_init(void)
+{
+ udc_device = NULL;
+ usbdbg("PXA27x usbd start");
+
+ /* Enable clock for USB device */
+ writel(readl(CKEN) | CKEN11_USB, CKEN);
+
+ /* Disable the UDC */
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+
+ /* Disable IRQs: we don't use them */
+ writel(0, UDCICR0);
+ writel(0, UDCICR1);
+
+ return 0;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/regs-otg.h b/src/roms/u-boot/drivers/usb/gadget/regs-otg.h
new file mode 100644
index 0000000..ac5d112
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/regs-otg.h
@@ -0,0 +1,273 @@
+/* linux/arch/arm/plat-s3c/include/plat/regs-otg.h
+ *
+ * Copyright (C) 2004 Herbert Poetzl <herbert@13thfloor.at>
+ *
+ * Registers remapping:
+ * Lukasz Majewski <l.majewski@samsumg.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __ASM_ARCH_REGS_USB_OTG_HS_H
+#define __ASM_ARCH_REGS_USB_OTG_HS_H
+
+/* USB2.0 OTG Controller register */
+struct s3c_usbotg_phy {
+ u32 phypwr;
+ u32 phyclk;
+ u32 rstcon;
+};
+
+/* Device Logical IN Endpoint-Specific Registers */
+struct s3c_dev_in_endp {
+ u32 diepctl;
+ u8 res1[4];
+ u32 diepint;
+ u8 res2[4];
+ u32 dieptsiz;
+ u32 diepdma;
+ u8 res3[4];
+ u32 diepdmab;
+};
+
+/* Device Logical OUT Endpoint-Specific Registers */
+struct s3c_dev_out_endp {
+ u32 doepctl;
+ u8 res1[4];
+ u32 doepint;
+ u8 res2[4];
+ u32 doeptsiz;
+ u32 doepdma;
+ u8 res3[4];
+ u32 doepdmab;
+};
+
+struct ep_fifo {
+ u32 fifo;
+ u8 res[4092];
+};
+
+/* USB2.0 OTG Controller register */
+struct s3c_usbotg_reg {
+ /* Core Global Registers */
+ u32 gotgctl; /* OTG Control & Status */
+ u32 gotgint; /* OTG Interrupt */
+ u32 gahbcfg; /* Core AHB Configuration */
+ u32 gusbcfg; /* Core USB Configuration */
+ u32 grstctl; /* Core Reset */
+ u32 gintsts; /* Core Interrupt */
+ u32 gintmsk; /* Core Interrupt Mask */
+ u32 grxstsr; /* Receive Status Debug Read/Status Read */
+ u32 grxstsp; /* Receive Status Debug Pop/Status Pop */
+ u32 grxfsiz; /* Receive FIFO Size */
+ u32 gnptxfsiz; /* Non-Periodic Transmit FIFO Size */
+ u8 res1[216];
+ u32 dieptxf[15]; /* Device Periodic Transmit FIFO size register */
+ u8 res2[1728];
+ /* Device Configuration */
+ u32 dcfg; /* Device Configuration Register */
+ u32 dctl; /* Device Control */
+ u32 dsts; /* Device Status */
+ u8 res3[4];
+ u32 diepmsk; /* Device IN Endpoint Common Interrupt Mask */
+ u32 doepmsk; /* Device OUT Endpoint Common Interrupt Mask */
+ u32 daint; /* Device All Endpoints Interrupt */
+ u32 daintmsk; /* Device All Endpoints Interrupt Mask */
+ u8 res4[224];
+ struct s3c_dev_in_endp in_endp[16];
+ struct s3c_dev_out_endp out_endp[16];
+ u8 res5[768];
+ struct ep_fifo ep[16];
+};
+
+/*===================================================================== */
+/*definitions related to CSR setting */
+
+/* S3C_UDC_OTG_GOTGCTL */
+#define B_SESSION_VALID (0x1<<19)
+#define A_SESSION_VALID (0x1<<18)
+
+/* S3C_UDC_OTG_GAHBCFG */
+#define PTXFE_HALF (0<<8)
+#define PTXFE_ZERO (1<<8)
+#define NPTXFE_HALF (0<<7)
+#define NPTXFE_ZERO (1<<7)
+#define MODE_SLAVE (0<<5)
+#define MODE_DMA (1<<5)
+#define BURST_SINGLE (0<<1)
+#define BURST_INCR (1<<1)
+#define BURST_INCR4 (3<<1)
+#define BURST_INCR8 (5<<1)
+#define BURST_INCR16 (7<<1)
+#define GBL_INT_UNMASK (1<<0)
+#define GBL_INT_MASK (0<<0)
+
+/* S3C_UDC_OTG_GRSTCTL */
+#define AHB_MASTER_IDLE (1u<<31)
+#define CORE_SOFT_RESET (0x1<<0)
+
+/* S3C_UDC_OTG_GINTSTS/S3C_UDC_OTG_GINTMSK core interrupt register */
+#define INT_RESUME (1u<<31)
+#define INT_DISCONN (0x1<<29)
+#define INT_CONN_ID_STS_CNG (0x1<<28)
+#define INT_OUT_EP (0x1<<19)
+#define INT_IN_EP (0x1<<18)
+#define INT_ENUMDONE (0x1<<13)
+#define INT_RESET (0x1<<12)
+#define INT_SUSPEND (0x1<<11)
+#define INT_EARLY_SUSPEND (0x1<<10)
+#define INT_NP_TX_FIFO_EMPTY (0x1<<5)
+#define INT_RX_FIFO_NOT_EMPTY (0x1<<4)
+#define INT_SOF (0x1<<3)
+#define INT_DEV_MODE (0x0<<0)
+#define INT_HOST_MODE (0x1<<1)
+#define INT_GOUTNakEff (0x01<<7)
+#define INT_GINNakEff (0x01<<6)
+
+#define FULL_SPEED_CONTROL_PKT_SIZE 8
+#define FULL_SPEED_BULK_PKT_SIZE 64
+
+#define HIGH_SPEED_CONTROL_PKT_SIZE 64
+#define HIGH_SPEED_BULK_PKT_SIZE 512
+
+#define RX_FIFO_SIZE (1024*4)
+#define NPTX_FIFO_SIZE (1024*4)
+#define PTX_FIFO_SIZE (1536*1)
+
+#define DEPCTL_TXFNUM_0 (0x0<<22)
+#define DEPCTL_TXFNUM_1 (0x1<<22)
+#define DEPCTL_TXFNUM_2 (0x2<<22)
+#define DEPCTL_TXFNUM_3 (0x3<<22)
+#define DEPCTL_TXFNUM_4 (0x4<<22)
+
+/* Enumeration speed */
+#define USB_HIGH_30_60MHZ (0x0<<1)
+#define USB_FULL_30_60MHZ (0x1<<1)
+#define USB_LOW_6MHZ (0x2<<1)
+#define USB_FULL_48MHZ (0x3<<1)
+
+/* S3C_UDC_OTG_GRXSTSP STATUS */
+#define OUT_PKT_RECEIVED (0x2<<17)
+#define OUT_TRANSFER_COMPLELTED (0x3<<17)
+#define SETUP_TRANSACTION_COMPLETED (0x4<<17)
+#define SETUP_PKT_RECEIVED (0x6<<17)
+#define GLOBAL_OUT_NAK (0x1<<17)
+
+/* S3C_UDC_OTG_DCTL device control register */
+#define NORMAL_OPERATION (0x1<<0)
+#define SOFT_DISCONNECT (0x1<<1)
+
+/* S3C_UDC_OTG_DAINT device all endpoint interrupt register */
+#define DAINT_OUT_BIT (16)
+#define DAINT_MASK (0xFFFF)
+
+/* S3C_UDC_OTG_DIEPCTL0/DOEPCTL0 device
+ control IN/OUT endpoint 0 control register */
+#define DEPCTL_EPENA (0x1<<31)
+#define DEPCTL_EPDIS (0x1<<30)
+#define DEPCTL_SETD1PID (0x1<<29)
+#define DEPCTL_SETD0PID (0x1<<28)
+#define DEPCTL_SNAK (0x1<<27)
+#define DEPCTL_CNAK (0x1<<26)
+#define DEPCTL_STALL (0x1<<21)
+#define DEPCTL_TYPE_BIT (18)
+#define DEPCTL_TYPE_MASK (0x3<<18)
+#define DEPCTL_CTRL_TYPE (0x0<<18)
+#define DEPCTL_ISO_TYPE (0x1<<18)
+#define DEPCTL_BULK_TYPE (0x2<<18)
+#define DEPCTL_INTR_TYPE (0x3<<18)
+#define DEPCTL_USBACTEP (0x1<<15)
+#define DEPCTL_NEXT_EP_BIT (11)
+#define DEPCTL_MPS_BIT (0)
+#define DEPCTL_MPS_MASK (0x7FF)
+
+#define DEPCTL0_MPS_64 (0x0<<0)
+#define DEPCTL0_MPS_32 (0x1<<0)
+#define DEPCTL0_MPS_16 (0x2<<0)
+#define DEPCTL0_MPS_8 (0x3<<0)
+#define DEPCTL_MPS_BULK_512 (512<<0)
+#define DEPCTL_MPS_INT_MPS_16 (16<<0)
+
+#define DIEPCTL0_NEXT_EP_BIT (11)
+
+
+/* S3C_UDC_OTG_DIEPMSK/DOEPMSK device IN/OUT endpoint
+ common interrupt mask register */
+/* S3C_UDC_OTG_DIEPINTn/DOEPINTn device IN/OUT endpoint interrupt register */
+#define BACK2BACK_SETUP_RECEIVED (0x1<<6)
+#define INTKNEPMIS (0x1<<5)
+#define INTKN_TXFEMP (0x1<<4)
+#define NON_ISO_IN_EP_TIMEOUT (0x1<<3)
+#define CTRL_OUT_EP_SETUP_PHASE_DONE (0x1<<3)
+#define AHB_ERROR (0x1<<2)
+#define EPDISBLD (0x1<<1)
+#define TRANSFER_DONE (0x1<<0)
+
+#define USB_PHY_CTRL_EN0 (0x1 << 0)
+
+/* OPHYPWR */
+#define PHY_0_SLEEP (0x1 << 5)
+#define OTG_DISABLE_0 (0x1 << 4)
+#define ANALOG_PWRDOWN (0x1 << 3)
+#define FORCE_SUSPEND_0 (0x1 << 0)
+
+/* URSTCON */
+#define HOST_SW_RST (0x1 << 4)
+#define PHY_SW_RST1 (0x1 << 3)
+#define PHYLNK_SW_RST (0x1 << 2)
+#define LINK_SW_RST (0x1 << 1)
+#define PHY_SW_RST0 (0x1 << 0)
+
+/* OPHYCLK */
+#define COMMON_ON_N1 (0x1 << 7)
+#define COMMON_ON_N0 (0x1 << 4)
+#define ID_PULLUP0 (0x1 << 2)
+#define CLK_SEL_24MHZ (0x3 << 0)
+#define CLK_SEL_12MHZ (0x2 << 0)
+#define CLK_SEL_48MHZ (0x0 << 0)
+
+#define EXYNOS4X12_ID_PULLUP0 (0x01 << 3)
+#define EXYNOS4X12_COMMON_ON_N0 (0x01 << 4)
+#define EXYNOS4X12_CLK_SEL_12MHZ (0x02 << 0)
+#define EXYNOS4X12_CLK_SEL_24MHZ (0x05 << 0)
+
+/* Device Configuration Register DCFG */
+#define DEV_SPEED_HIGH_SPEED_20 (0x0 << 0)
+#define DEV_SPEED_FULL_SPEED_20 (0x1 << 0)
+#define DEV_SPEED_LOW_SPEED_11 (0x2 << 0)
+#define DEV_SPEED_FULL_SPEED_11 (0x3 << 0)
+#define EP_MISS_CNT(x) (x << 18)
+#define DEVICE_ADDRESS(x) (x << 4)
+
+/* Core Reset Register (GRSTCTL) */
+#define TX_FIFO_FLUSH (0x1 << 5)
+#define RX_FIFO_FLUSH (0x1 << 4)
+#define TX_FIFO_NUMBER(x) (x << 6)
+#define TX_FIFO_FLUSH_ALL TX_FIFO_NUMBER(0x10)
+
+/* Masks definitions */
+#define GINTMSK_INIT (INT_OUT_EP | INT_IN_EP | INT_RESUME | INT_ENUMDONE\
+ | INT_RESET | INT_SUSPEND)
+#define DOEPMSK_INIT (CTRL_OUT_EP_SETUP_PHASE_DONE | AHB_ERROR|TRANSFER_DONE)
+#define DIEPMSK_INIT (NON_ISO_IN_EP_TIMEOUT|AHB_ERROR|TRANSFER_DONE)
+#define GAHBCFG_INIT (PTXFE_HALF | NPTXFE_HALF | MODE_DMA | BURST_INCR4\
+ | GBL_INT_UNMASK)
+
+/* Device Endpoint X Transfer Size Register (DIEPTSIZX) */
+#define DIEPT_SIZ_PKT_CNT(x) (x << 19)
+#define DIEPT_SIZ_XFER_SIZE(x) (x << 0)
+
+/* Device OUT Endpoint X Transfer Size Register (DOEPTSIZX) */
+#define DOEPT_SIZ_PKT_CNT(x) (x << 19)
+#define DOEPT_SIZ_XFER_SIZE(x) (x << 0)
+#define DOEPT_SIZ_XFER_SIZE_MAX_EP0 (0x7F << 0)
+#define DOEPT_SIZ_XFER_SIZE_MAX_EP (0x7FFF << 0)
+
+/* Device Endpoint-N Control Register (DIEPCTLn/DOEPCTLn) */
+#define DIEPCTL_TX_FIFO_NUM(x) (x << 22)
+#define DIEPCTL_TX_FIFO_NUM_MASK (~DIEPCTL_TX_FIFO_NUM(0xF))
+
+/* Device ALL Endpoints Interrupt Register (DAINT) */
+#define DAINT_IN_EP_INT(x) (x << 0)
+#define DAINT_OUT_EP_INT(x) (x << 16)
+#endif
diff --git a/src/roms/u-boot/drivers/usb/gadget/rndis.c b/src/roms/u-boot/drivers/usb/gadget/rndis.c
new file mode 100644
index 0000000..404a7b9
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/rndis.c
@@ -0,0 +1,1316 @@
+/*
+ * RNDIS MSG parser
+ *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This software was originally developed in conformance with
+ * Microsoft's Remote NDIS Specification License Agreement.
+ *
+ * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ * Fixed message length bug in init_response
+ *
+ * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ * Fixed rndis_rm_hdr length bug.
+ *
+ * Copyright (C) 2004 by David Brownell
+ * updates to merge with Linux 2.6, better match RNDIS spec
+ */
+
+#include <common.h>
+#include <net.h>
+#include <malloc.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <asm/errno.h>
+
+#undef RNDIS_PM
+#undef RNDIS_WAKEUP
+#undef VERBOSE
+
+#include "rndis.h"
+
+#define ETH_ALEN 6 /* Octets in one ethernet addr */
+#define ETH_HLEN 14 /* Total octets in header. */
+#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN 1500 /* Max. octets in payload */
+#define ETH_FRAME_LEN PKTSIZE_ALIGN /* Max. octets in frame sans FCS */
+#define ETH_FCS_LEN 4 /* Octets in the FCS */
+#define ENOTSUPP 524 /* Operation is not supported */
+
+
+/*
+ * The driver for your USB chip needs to support ep0 OUT to work with
+ * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
+ *
+ * Windows hosts need an INF file like Documentation/usb/linux.inf
+ * and will be happier if you provide the host_addr module parameter.
+ */
+
+#define RNDIS_MAX_CONFIGS 1
+
+static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS];
+
+/* Driver Version */
+static const __le32 rndis_driver_version = __constant_cpu_to_le32(1);
+
+/* Function Prototypes */
+static rndis_resp_t *rndis_add_response(int configNr, u32 length);
+
+
+/* supported OIDs */
+static const u32 oid_supported_list[] = {
+ /* the general stuff */
+ OID_GEN_SUPPORTED_LIST,
+ OID_GEN_HARDWARE_STATUS,
+ OID_GEN_MEDIA_SUPPORTED,
+ OID_GEN_MEDIA_IN_USE,
+ OID_GEN_MAXIMUM_FRAME_SIZE,
+ OID_GEN_LINK_SPEED,
+ OID_GEN_TRANSMIT_BLOCK_SIZE,
+ OID_GEN_RECEIVE_BLOCK_SIZE,
+ OID_GEN_VENDOR_ID,
+ OID_GEN_VENDOR_DESCRIPTION,
+ OID_GEN_VENDOR_DRIVER_VERSION,
+ OID_GEN_CURRENT_PACKET_FILTER,
+ OID_GEN_MAXIMUM_TOTAL_SIZE,
+ OID_GEN_MEDIA_CONNECT_STATUS,
+ OID_GEN_PHYSICAL_MEDIUM,
+#if 0
+ OID_GEN_RNDIS_CONFIG_PARAMETER,
+#endif
+
+ /* the statistical stuff */
+ OID_GEN_XMIT_OK,
+ OID_GEN_RCV_OK,
+ OID_GEN_XMIT_ERROR,
+ OID_GEN_RCV_ERROR,
+ OID_GEN_RCV_NO_BUFFER,
+#ifdef RNDIS_OPTIONAL_STATS
+ OID_GEN_DIRECTED_BYTES_XMIT,
+ OID_GEN_DIRECTED_FRAMES_XMIT,
+ OID_GEN_MULTICAST_BYTES_XMIT,
+ OID_GEN_MULTICAST_FRAMES_XMIT,
+ OID_GEN_BROADCAST_BYTES_XMIT,
+ OID_GEN_BROADCAST_FRAMES_XMIT,
+ OID_GEN_DIRECTED_BYTES_RCV,
+ OID_GEN_DIRECTED_FRAMES_RCV,
+ OID_GEN_MULTICAST_BYTES_RCV,
+ OID_GEN_MULTICAST_FRAMES_RCV,
+ OID_GEN_BROADCAST_BYTES_RCV,
+ OID_GEN_BROADCAST_FRAMES_RCV,
+ OID_GEN_RCV_CRC_ERROR,
+ OID_GEN_TRANSMIT_QUEUE_LENGTH,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+ /* mandatory 802.3 */
+ /* the general stuff */
+ OID_802_3_PERMANENT_ADDRESS,
+ OID_802_3_CURRENT_ADDRESS,
+ OID_802_3_MULTICAST_LIST,
+ OID_802_3_MAC_OPTIONS,
+ OID_802_3_MAXIMUM_LIST_SIZE,
+
+ /* the statistical stuff */
+ OID_802_3_RCV_ERROR_ALIGNMENT,
+ OID_802_3_XMIT_ONE_COLLISION,
+ OID_802_3_XMIT_MORE_COLLISIONS,
+#ifdef RNDIS_OPTIONAL_STATS
+ OID_802_3_XMIT_DEFERRED,
+ OID_802_3_XMIT_MAX_COLLISIONS,
+ OID_802_3_RCV_OVERRUN,
+ OID_802_3_XMIT_UNDERRUN,
+ OID_802_3_XMIT_HEARTBEAT_FAILURE,
+ OID_802_3_XMIT_TIMES_CRS_LOST,
+ OID_802_3_XMIT_LATE_COLLISIONS,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+#ifdef RNDIS_PM
+ /* PM and wakeup are mandatory for USB: */
+
+ /* power management */
+ OID_PNP_CAPABILITIES,
+ OID_PNP_QUERY_POWER,
+ OID_PNP_SET_POWER,
+
+#ifdef RNDIS_WAKEUP
+ /* wake up host */
+ OID_PNP_ENABLE_WAKE_UP,
+ OID_PNP_ADD_WAKE_UP_PATTERN,
+ OID_PNP_REMOVE_WAKE_UP_PATTERN,
+#endif /* RNDIS_WAKEUP */
+#endif /* RNDIS_PM */
+};
+
+
+/* NDIS Functions */
+static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
+ unsigned buf_len, rndis_resp_t *r)
+{
+ int retval = -ENOTSUPP;
+ u32 length = 4; /* usually */
+ __le32 *outbuf;
+ int i, count;
+ rndis_query_cmplt_type *resp;
+ rndis_params *params;
+
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_query_cmplt_type *) r->buf;
+
+ if (!resp)
+ return -ENOMEM;
+
+#if defined(DEBUG) && defined(DEBUG_VERBOSE)
+ if (buf_len) {
+ debug("query OID %08x value, len %d:\n", OID, buf_len);
+ for (i = 0; i < buf_len; i += 16) {
+ debug("%03d: %08x %08x %08x %08x\n", i,
+ get_unaligned_le32(&buf[i]),
+ get_unaligned_le32(&buf[i + 4]),
+ get_unaligned_le32(&buf[i + 8]),
+ get_unaligned_le32(&buf[i + 12]));
+ }
+ }
+#endif
+
+ /* response goes here, right after the header */
+ outbuf = (__le32 *) &resp[1];
+ resp->InformationBufferOffset = __constant_cpu_to_le32(16);
+
+ params = &rndis_per_dev_params[configNr];
+ switch (OID) {
+
+ /* general oids (table 4-1) */
+
+ /* mandatory */
+ case OID_GEN_SUPPORTED_LIST:
+ debug("%s: OID_GEN_SUPPORTED_LIST\n", __func__);
+ length = sizeof(oid_supported_list);
+ count = length / sizeof(u32);
+ for (i = 0; i < count; i++)
+ outbuf[i] = cpu_to_le32(oid_supported_list[i]);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case OID_GEN_HARDWARE_STATUS:
+ debug("%s: OID_GEN_HARDWARE_STATUS\n", __func__);
+ /*
+ * Bogus question!
+ * Hardware must be ready to receive high level protocols.
+ * BTW:
+ * reddite ergo quae sunt Caesaris Caesari
+ * et quae sunt Dei Deo!
+ */
+ *outbuf = __constant_cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case OID_GEN_MEDIA_SUPPORTED:
+ debug("%s: OID_GEN_MEDIA_SUPPORTED\n", __func__);
+ *outbuf = cpu_to_le32(params->medium);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case OID_GEN_MEDIA_IN_USE:
+ debug("%s: OID_GEN_MEDIA_IN_USE\n", __func__);
+ /* one medium, one transport... (maybe you do it better) */
+ *outbuf = cpu_to_le32(params->medium);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case OID_GEN_MAXIMUM_FRAME_SIZE:
+ debug("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
+ if (params->dev) {
+ *outbuf = cpu_to_le32(params->mtu);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case OID_GEN_LINK_SPEED:
+#if defined(DEBUG) && defined(DEBUG_VERBOSE)
+ debug("%s: OID_GEN_LINK_SPEED\n", __func__);
+#endif
+ if (params->media_state == NDIS_MEDIA_STATE_DISCONNECTED)
+ *outbuf = __constant_cpu_to_le32(0);
+ else
+ *outbuf = cpu_to_le32(params->speed);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case OID_GEN_TRANSMIT_BLOCK_SIZE:
+ debug("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
+ if (params->dev) {
+ *outbuf = cpu_to_le32(params->mtu);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case OID_GEN_RECEIVE_BLOCK_SIZE:
+ debug("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
+ if (params->dev) {
+ *outbuf = cpu_to_le32(params->mtu);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case OID_GEN_VENDOR_ID:
+ debug("%s: OID_GEN_VENDOR_ID\n", __func__);
+ *outbuf = cpu_to_le32(params->vendorID);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case OID_GEN_VENDOR_DESCRIPTION:
+ debug("%s: OID_GEN_VENDOR_DESCRIPTION\n", __func__);
+ length = strlen(params->vendorDescr);
+ memcpy(outbuf, params->vendorDescr, length);
+ retval = 0;
+ break;
+
+ case OID_GEN_VENDOR_DRIVER_VERSION:
+ debug("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __func__);
+ /* Created as LE */
+ *outbuf = rndis_driver_version;
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ debug("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __func__);
+ *outbuf = cpu_to_le32(*params->filter);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case OID_GEN_MAXIMUM_TOTAL_SIZE:
+ debug("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__);
+ *outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case OID_GEN_MEDIA_CONNECT_STATUS:
+#if defined(DEBUG) && defined(DEBUG_VERBOSE)
+ debug("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);
+#endif
+ *outbuf = cpu_to_le32(params->media_state);
+ retval = 0;
+ break;
+
+ case OID_GEN_PHYSICAL_MEDIUM:
+ debug("%s: OID_GEN_PHYSICAL_MEDIUM\n", __func__);
+ *outbuf = __constant_cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ /*
+ * The RNDIS specification is incomplete/wrong. Some versions
+ * of MS-Windows expect OIDs that aren't specified there. Other
+ * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
+ */
+ case OID_GEN_MAC_OPTIONS: /* from WinME */
+ debug("%s: OID_GEN_MAC_OPTIONS\n", __func__);
+ *outbuf = __constant_cpu_to_le32(
+ NDIS_MAC_OPTION_RECEIVE_SERIALIZED
+ | NDIS_MAC_OPTION_FULL_DUPLEX);
+ retval = 0;
+ break;
+
+ /* statistics OIDs (table 4-2) */
+
+ /* mandatory */
+ case OID_GEN_XMIT_OK:
+#if defined(DEBUG) && defined(DEBUG_VERBOSE)
+ debug("%s: OID_GEN_XMIT_OK\n", __func__);
+#endif
+ if (params->stats) {
+ *outbuf = cpu_to_le32(
+ params->stats->tx_packets -
+ params->stats->tx_errors -
+ params->stats->tx_dropped);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case OID_GEN_RCV_OK:
+#if defined(DEBUG) && defined(DEBUG_VERBOSE)
+ debug("%s: OID_GEN_RCV_OK\n", __func__);
+#endif
+ if (params->stats) {
+ *outbuf = cpu_to_le32(
+ params->stats->rx_packets -
+ params->stats->rx_errors -
+ params->stats->rx_dropped);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case OID_GEN_XMIT_ERROR:
+#if defined(DEBUG) && defined(DEBUG_VERBOSE)
+ debug("%s: OID_GEN_XMIT_ERROR\n", __func__);
+#endif
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->tx_errors);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case OID_GEN_RCV_ERROR:
+#if defined(DEBUG) && defined(DEBUG_VERBOSE)
+ debug("%s: OID_GEN_RCV_ERROR\n", __func__);
+#endif
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->rx_errors);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case OID_GEN_RCV_NO_BUFFER:
+ debug("%s: OID_GEN_RCV_NO_BUFFER\n", __func__);
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->rx_dropped);
+ retval = 0;
+ }
+ break;
+
+#ifdef RNDIS_OPTIONAL_STATS
+ case OID_GEN_DIRECTED_BYTES_XMIT:
+ debug("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __func__);
+ /*
+ * Aunt Tilly's size of shoes
+ * minus antarctica count of penguins
+ * divided by weight of Alpha Centauri
+ */
+ if (params->stats) {
+ *outbuf = cpu_to_le32(
+ (params->stats->tx_packets -
+ params->stats->tx_errors -
+ params->stats->tx_dropped)
+ * 123);
+ retval = 0;
+ }
+ break;
+
+ case OID_GEN_DIRECTED_FRAMES_XMIT:
+ debug("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __func__);
+ /* dito */
+ if (params->stats) {
+ *outbuf = cpu_to_le32(
+ (params->stats->tx_packets -
+ params->stats->tx_errors -
+ params->stats->tx_dropped)
+ / 123);
+ retval = 0;
+ }
+ break;
+
+ case OID_GEN_MULTICAST_BYTES_XMIT:
+ debug("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __func__);
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->multicast * 1234);
+ retval = 0;
+ }
+ break;
+
+ case OID_GEN_MULTICAST_FRAMES_XMIT:
+ debug("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __func__);
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->multicast);
+ retval = 0;
+ }
+ break;
+
+ case OID_GEN_BROADCAST_BYTES_XMIT:
+ debug("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __func__);
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->tx_packets/42*255);
+ retval = 0;
+ }
+ break;
+
+ case OID_GEN_BROADCAST_FRAMES_XMIT:
+ debug("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __func__);
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->tx_packets / 42);
+ retval = 0;
+ }
+ break;
+
+ case OID_GEN_DIRECTED_BYTES_RCV:
+ debug("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __func__);
+ *outbuf = __constant_cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ case OID_GEN_DIRECTED_FRAMES_RCV:
+ debug("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __func__);
+ *outbuf = __constant_cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ case OID_GEN_MULTICAST_BYTES_RCV:
+ debug("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __func__);
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->multicast * 1111);
+ retval = 0;
+ }
+ break;
+
+ case OID_GEN_MULTICAST_FRAMES_RCV:
+ debug("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __func__);
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->multicast);
+ retval = 0;
+ }
+ break;
+
+ case OID_GEN_BROADCAST_BYTES_RCV:
+ debug("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __func__);
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->rx_packets/42*255);
+ retval = 0;
+ }
+ break;
+
+ case OID_GEN_BROADCAST_FRAMES_RCV:
+ debug("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __func__);
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->rx_packets / 42);
+ retval = 0;
+ }
+ break;
+
+ case OID_GEN_RCV_CRC_ERROR:
+ debug("%s: OID_GEN_RCV_CRC_ERROR\n", __func__);
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->rx_crc_errors);
+ retval = 0;
+ }
+ break;
+
+ case OID_GEN_TRANSMIT_QUEUE_LENGTH:
+ debug("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __func__);
+ *outbuf = __constant_cpu_to_le32(0);
+ retval = 0;
+ break;
+#endif /* RNDIS_OPTIONAL_STATS */
+
+ /* ieee802.3 OIDs (table 4-3) */
+
+ /* mandatory */
+ case OID_802_3_PERMANENT_ADDRESS:
+ debug("%s: OID_802_3_PERMANENT_ADDRESS\n", __func__);
+ if (params->dev) {
+ length = ETH_ALEN;
+ memcpy(outbuf, params->host_mac, length);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case OID_802_3_CURRENT_ADDRESS:
+ debug("%s: OID_802_3_CURRENT_ADDRESS\n", __func__);
+ if (params->dev) {
+ length = ETH_ALEN;
+ memcpy(outbuf, params->host_mac, length);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case OID_802_3_MULTICAST_LIST:
+ debug("%s: OID_802_3_MULTICAST_LIST\n", __func__);
+ /* Multicast base address only */
+ *outbuf = __constant_cpu_to_le32(0xE0000000);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case OID_802_3_MAXIMUM_LIST_SIZE:
+ debug("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __func__);
+ /* Multicast base address only */
+ *outbuf = __constant_cpu_to_le32(1);
+ retval = 0;
+ break;
+
+ case OID_802_3_MAC_OPTIONS:
+ debug("%s: OID_802_3_MAC_OPTIONS\n", __func__);
+ break;
+
+ /* ieee802.3 statistics OIDs (table 4-4) */
+
+ /* mandatory */
+ case OID_802_3_RCV_ERROR_ALIGNMENT:
+ debug("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
+ if (params->stats) {
+ *outbuf = cpu_to_le32(params->stats->rx_frame_errors);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case OID_802_3_XMIT_ONE_COLLISION:
+ debug("%s: OID_802_3_XMIT_ONE_COLLISION\n", __func__);
+ *outbuf = __constant_cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case OID_802_3_XMIT_MORE_COLLISIONS:
+ debug("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __func__);
+ *outbuf = __constant_cpu_to_le32(0);
+ retval = 0;
+ break;
+
+#ifdef RNDIS_OPTIONAL_STATS
+ case OID_802_3_XMIT_DEFERRED:
+ debug("%s: OID_802_3_XMIT_DEFERRED\n", __func__);
+ /* TODO */
+ break;
+
+ case OID_802_3_XMIT_MAX_COLLISIONS:
+ debug("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __func__);
+ /* TODO */
+ break;
+
+ case OID_802_3_RCV_OVERRUN:
+ debug("%s: OID_802_3_RCV_OVERRUN\n", __func__);
+ /* TODO */
+ break;
+
+ case OID_802_3_XMIT_UNDERRUN:
+ debug("%s: OID_802_3_XMIT_UNDERRUN\n", __func__);
+ /* TODO */
+ break;
+
+ case OID_802_3_XMIT_HEARTBEAT_FAILURE:
+ debug("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __func__);
+ /* TODO */
+ break;
+
+ case OID_802_3_XMIT_TIMES_CRS_LOST:
+ debug("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __func__);
+ /* TODO */
+ break;
+
+ case OID_802_3_XMIT_LATE_COLLISIONS:
+ debug("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __func__);
+ /* TODO */
+ break;
+#endif /* RNDIS_OPTIONAL_STATS */
+
+#ifdef RNDIS_PM
+ /* power management OIDs (table 4-5) */
+ case OID_PNP_CAPABILITIES:
+ debug("%s: OID_PNP_CAPABILITIES\n", __func__);
+
+ /* for now, no wakeup capabilities */
+ length = sizeof(struct NDIS_PNP_CAPABILITIES);
+ memset(outbuf, 0, length);
+ retval = 0;
+ break;
+ case OID_PNP_QUERY_POWER:
+ debug("%s: OID_PNP_QUERY_POWER D%d\n", __func__,
+ get_unaligned_le32(buf) - 1);
+ /*
+ * only suspend is a real power state, and
+ * it can't be entered by OID_PNP_SET_POWER...
+ */
+ length = 0;
+ retval = 0;
+ break;
+#endif
+
+ default:
+ debug("%s: query unknown OID 0x%08X\n", __func__, OID);
+ }
+ if (retval < 0)
+ length = 0;
+
+ resp->InformationBufferLength = cpu_to_le32(length);
+ r->length = length + sizeof *resp;
+ resp->MessageLength = cpu_to_le32(r->length);
+ return retval;
+}
+
+static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
+ rndis_resp_t *r)
+{
+ rndis_set_cmplt_type *resp;
+ int retval = -ENOTSUPP;
+ struct rndis_params *params;
+#if (defined(DEBUG) && defined(DEBUG_VERBOSE)) || defined(RNDIS_PM)
+ int i;
+#endif
+
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_set_cmplt_type *) r->buf;
+ if (!resp)
+ return -ENOMEM;
+
+#if defined(DEBUG) && defined(DEBUG_VERBOSE)
+ if (buf_len) {
+ debug("set OID %08x value, len %d:\n", OID, buf_len);
+ for (i = 0; i < buf_len; i += 16) {
+ debug("%03d: %08x %08x %08x %08x\n", i,
+ get_unaligned_le32(&buf[i]),
+ get_unaligned_le32(&buf[i + 4]),
+ get_unaligned_le32(&buf[i + 8]),
+ get_unaligned_le32(&buf[i + 12]));
+ }
+ }
+#endif
+
+ params = &rndis_per_dev_params[configNr];
+ switch (OID) {
+ case OID_GEN_CURRENT_PACKET_FILTER:
+
+ /*
+ * these NDIS_PACKET_TYPE_* bitflags are shared with
+ * cdc_filter; it's not RNDIS-specific
+ * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
+ * PROMISCUOUS, DIRECTED,
+ * MULTICAST, ALL_MULTICAST, BROADCAST
+ */
+ *params->filter = (u16) get_unaligned_le32(buf);
+ debug("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
+ __func__, *params->filter);
+
+ /*
+ * this call has a significant side effect: it's
+ * what makes the packet flow start and stop, like
+ * activating the CDC Ethernet altsetting.
+ */
+#ifdef RNDIS_PM
+update_linkstate:
+#endif
+ retval = 0;
+ if (*params->filter)
+ params->state = RNDIS_DATA_INITIALIZED;
+ else
+ params->state = RNDIS_INITIALIZED;
+ break;
+
+ case OID_802_3_MULTICAST_LIST:
+ /* I think we can ignore this */
+ debug("%s: OID_802_3_MULTICAST_LIST\n", __func__);
+ retval = 0;
+ break;
+#if 0
+ case OID_GEN_RNDIS_CONFIG_PARAMETER:
+ {
+ struct rndis_config_parameter *param;
+ param = (struct rndis_config_parameter *) buf;
+ debug("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n",
+ __func__,
+ min(cpu_to_le32(param->ParameterNameLength), 80),
+ buf + param->ParameterNameOffset);
+ retval = 0;
+ }
+ break;
+#endif
+
+#ifdef RNDIS_PM
+ case OID_PNP_SET_POWER:
+ /*
+ * The only real power state is USB suspend, and RNDIS requests
+ * can't enter it; this one isn't really about power. After
+ * resuming, Windows forces a reset, and then SET_POWER D0.
+ * FIXME ... then things go batty; Windows wedges itself.
+ */
+ i = get_unaligned_le32(buf);
+ debug("%s: OID_PNP_SET_POWER D%d\n", __func__, i - 1);
+ switch (i) {
+ case NdisDeviceStateD0:
+ *params->filter = params->saved_filter;
+ goto update_linkstate;
+ case NdisDeviceStateD3:
+ case NdisDeviceStateD2:
+ case NdisDeviceStateD1:
+ params->saved_filter = *params->filter;
+ retval = 0;
+ break;
+ }
+ break;
+
+#ifdef RNDIS_WAKEUP
+ /*
+ * no wakeup support advertised, so wakeup OIDs always fail:
+ * - OID_PNP_ENABLE_WAKE_UP
+ * - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN
+ */
+#endif
+
+#endif /* RNDIS_PM */
+
+ default:
+ debug("%s: set unknown OID 0x%08X, size %d\n",
+ __func__, OID, buf_len);
+ }
+
+ return retval;
+}
+
+/*
+ * Response Functions
+ */
+
+static int rndis_init_response(int configNr, rndis_init_msg_type *buf)
+{
+ rndis_init_cmplt_type *resp;
+ rndis_resp_t *r;
+
+ if (!rndis_per_dev_params[configNr].dev)
+ return -ENOTSUPP;
+
+ r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_init_cmplt_type *) r->buf;
+
+ resp->MessageType = __constant_cpu_to_le32(
+ REMOTE_NDIS_INITIALIZE_CMPLT);
+ resp->MessageLength = __constant_cpu_to_le32(52);
+ resp->RequestID = get_unaligned(&buf->RequestID); /* Still LE in msg buffer */
+ resp->Status = __constant_cpu_to_le32(RNDIS_STATUS_SUCCESS);
+ resp->MajorVersion = __constant_cpu_to_le32(RNDIS_MAJOR_VERSION);
+ resp->MinorVersion = __constant_cpu_to_le32(RNDIS_MINOR_VERSION);
+ resp->DeviceFlags = __constant_cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
+ resp->Medium = __constant_cpu_to_le32(RNDIS_MEDIUM_802_3);
+ resp->MaxPacketsPerTransfer = __constant_cpu_to_le32(1);
+ resp->MaxTransferSize = cpu_to_le32(
+ rndis_per_dev_params[configNr].mtu
+ + ETHER_HDR_SIZE
+ + sizeof(struct rndis_packet_msg_type)
+ + 22);
+ resp->PacketAlignmentFactor = __constant_cpu_to_le32(0);
+ resp->AFListOffset = __constant_cpu_to_le32(0);
+ resp->AFListSize = __constant_cpu_to_le32(0);
+
+ if (rndis_per_dev_params[configNr].ack)
+ rndis_per_dev_params[configNr].ack(
+ rndis_per_dev_params[configNr].dev);
+
+ return 0;
+}
+
+static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
+{
+ rndis_query_cmplt_type *resp;
+ rndis_resp_t *r;
+
+ debug("%s: OID = %08X\n", __func__, get_unaligned_le32(&buf->OID));
+ if (!rndis_per_dev_params[configNr].dev)
+ return -ENOTSUPP;
+
+ /*
+ * we need more memory:
+ * gen_ndis_query_resp expects enough space for
+ * rndis_query_cmplt_type followed by data.
+ * oid_supported_list is the largest data reply
+ */
+ r = rndis_add_response(configNr,
+ sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_query_cmplt_type *) r->buf;
+
+ resp->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_QUERY_CMPLT);
+ resp->RequestID = get_unaligned(&buf->RequestID); /* Still LE in msg buffer */
+
+ if (gen_ndis_query_resp(configNr, get_unaligned_le32(&buf->OID),
+ get_unaligned_le32(&buf->InformationBufferOffset)
+ + 8 + (u8 *) buf,
+ get_unaligned_le32(&buf->InformationBufferLength),
+ r)) {
+ /* OID not supported */
+ resp->Status = __constant_cpu_to_le32(
+ RNDIS_STATUS_NOT_SUPPORTED);
+ resp->MessageLength = __constant_cpu_to_le32(sizeof *resp);
+ resp->InformationBufferLength = __constant_cpu_to_le32(0);
+ resp->InformationBufferOffset = __constant_cpu_to_le32(0);
+ } else
+ resp->Status = __constant_cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+ if (rndis_per_dev_params[configNr].ack)
+ rndis_per_dev_params[configNr].ack(
+ rndis_per_dev_params[configNr].dev);
+ return 0;
+}
+
+static int rndis_set_response(int configNr, rndis_set_msg_type *buf)
+{
+ u32 BufLength, BufOffset;
+ rndis_set_cmplt_type *resp;
+ rndis_resp_t *r;
+
+ r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_set_cmplt_type *) r->buf;
+
+ BufLength = get_unaligned_le32(&buf->InformationBufferLength);
+ BufOffset = get_unaligned_le32(&buf->InformationBufferOffset);
+
+#ifdef VERBOSE
+ debug("%s: Length: %d\n", __func__, BufLength);
+ debug("%s: Offset: %d\n", __func__, BufOffset);
+ debug("%s: InfoBuffer: ", __func__);
+
+ for (i = 0; i < BufLength; i++)
+ debug("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
+
+ debug("\n");
+#endif
+
+ resp->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_SET_CMPLT);
+ resp->MessageLength = __constant_cpu_to_le32(16);
+ resp->RequestID = get_unaligned(&buf->RequestID); /* Still LE in msg buffer */
+ if (gen_ndis_set_resp(configNr, get_unaligned_le32(&buf->OID),
+ ((u8 *) buf) + 8 + BufOffset, BufLength, r))
+ resp->Status = __constant_cpu_to_le32(
+ RNDIS_STATUS_NOT_SUPPORTED);
+ else
+ resp->Status = __constant_cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+ if (rndis_per_dev_params[configNr].ack)
+ rndis_per_dev_params[configNr].ack(
+ rndis_per_dev_params[configNr].dev);
+
+ return 0;
+}
+
+static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf)
+{
+ rndis_reset_cmplt_type *resp;
+ rndis_resp_t *r;
+
+ r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_reset_cmplt_type *) r->buf;
+
+ resp->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_RESET_CMPLT);
+ resp->MessageLength = __constant_cpu_to_le32(16);
+ resp->Status = __constant_cpu_to_le32(RNDIS_STATUS_SUCCESS);
+ /* resent information */
+ resp->AddressingReset = __constant_cpu_to_le32(1);
+
+ if (rndis_per_dev_params[configNr].ack)
+ rndis_per_dev_params[configNr].ack(
+ rndis_per_dev_params[configNr].dev);
+
+ return 0;
+}
+
+static int rndis_keepalive_response(int configNr,
+ rndis_keepalive_msg_type *buf)
+{
+ rndis_keepalive_cmplt_type *resp;
+ rndis_resp_t *r;
+
+ /* host "should" check only in RNDIS_DATA_INITIALIZED state */
+
+ r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_keepalive_cmplt_type *) r->buf;
+
+ resp->MessageType = __constant_cpu_to_le32(
+ REMOTE_NDIS_KEEPALIVE_CMPLT);
+ resp->MessageLength = __constant_cpu_to_le32(16);
+ resp->RequestID = get_unaligned(&buf->RequestID); /* Still LE in msg buffer */
+ resp->Status = __constant_cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+ if (rndis_per_dev_params[configNr].ack)
+ rndis_per_dev_params[configNr].ack(
+ rndis_per_dev_params[configNr].dev);
+
+ return 0;
+}
+
+
+/*
+ * Device to Host Comunication
+ */
+static int rndis_indicate_status_msg(int configNr, u32 status)
+{
+ rndis_indicate_status_msg_type *resp;
+ rndis_resp_t *r;
+
+ if (rndis_per_dev_params[configNr].state == RNDIS_UNINITIALIZED)
+ return -ENOTSUPP;
+
+ r = rndis_add_response(configNr,
+ sizeof(rndis_indicate_status_msg_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_indicate_status_msg_type *) r->buf;
+
+ resp->MessageType = __constant_cpu_to_le32(
+ REMOTE_NDIS_INDICATE_STATUS_MSG);
+ resp->MessageLength = __constant_cpu_to_le32(20);
+ resp->Status = cpu_to_le32(status);
+ resp->StatusBufferLength = __constant_cpu_to_le32(0);
+ resp->StatusBufferOffset = __constant_cpu_to_le32(0);
+
+ if (rndis_per_dev_params[configNr].ack)
+ rndis_per_dev_params[configNr].ack(
+ rndis_per_dev_params[configNr].dev);
+ return 0;
+}
+
+int rndis_signal_connect(int configNr)
+{
+ rndis_per_dev_params[configNr].media_state
+ = NDIS_MEDIA_STATE_CONNECTED;
+ return rndis_indicate_status_msg(configNr,
+ RNDIS_STATUS_MEDIA_CONNECT);
+}
+
+int rndis_signal_disconnect(int configNr)
+{
+ rndis_per_dev_params[configNr].media_state
+ = NDIS_MEDIA_STATE_DISCONNECTED;
+
+#ifdef RNDIS_COMPLETE_SIGNAL_DISCONNECT
+ return rndis_indicate_status_msg(configNr,
+ RNDIS_STATUS_MEDIA_DISCONNECT);
+#else
+ return 0;
+#endif
+}
+
+void rndis_uninit(int configNr)
+{
+ u8 *buf;
+ u32 length;
+
+ if (configNr >= RNDIS_MAX_CONFIGS)
+ return;
+ rndis_per_dev_params[configNr].used = 0;
+ rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED;
+
+ /* drain the response queue */
+ while ((buf = rndis_get_next_response(configNr, &length)))
+ rndis_free_response(configNr, buf);
+}
+
+void rndis_set_host_mac(int configNr, const u8 *addr)
+{
+ rndis_per_dev_params[configNr].host_mac = addr;
+}
+
+enum rndis_state rndis_get_state(int configNr)
+{
+ if (configNr >= RNDIS_MAX_CONFIGS || configNr < 0)
+ return -ENOTSUPP;
+ return rndis_per_dev_params[configNr].state;
+}
+
+/*
+ * Message Parser
+ */
+int rndis_msg_parser(u8 configNr, u8 *buf)
+{
+ u32 MsgType, MsgLength;
+ __le32 *tmp;
+ struct rndis_params *params;
+
+ debug("%s: configNr = %d, %p\n", __func__, configNr, buf);
+
+ if (!buf)
+ return -ENOMEM;
+
+ tmp = (__le32 *) buf;
+ MsgType = get_unaligned_le32(tmp++);
+ MsgLength = get_unaligned_le32(tmp++);
+
+ if (configNr >= RNDIS_MAX_CONFIGS)
+ return -ENOTSUPP;
+ params = &rndis_per_dev_params[configNr];
+
+ /*
+ * NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
+ * rx/tx statistics and link status, in addition to KEEPALIVE traffic
+ * and normal HC level polling to see if there's any IN traffic.
+ */
+
+ /* For USB: responses may take up to 10 seconds */
+ switch (MsgType) {
+ case REMOTE_NDIS_INITIALIZE_MSG:
+ debug("%s: REMOTE_NDIS_INITIALIZE_MSG\n", __func__);
+ params->state = RNDIS_INITIALIZED;
+ return rndis_init_response(configNr,
+ (rndis_init_msg_type *) buf);
+
+ case REMOTE_NDIS_HALT_MSG:
+ debug("%s: REMOTE_NDIS_HALT_MSG\n", __func__);
+ params->state = RNDIS_UNINITIALIZED;
+ return 0;
+
+ case REMOTE_NDIS_QUERY_MSG:
+ return rndis_query_response(configNr,
+ (rndis_query_msg_type *) buf);
+
+ case REMOTE_NDIS_SET_MSG:
+ return rndis_set_response(configNr,
+ (rndis_set_msg_type *) buf);
+
+ case REMOTE_NDIS_RESET_MSG:
+ debug("%s: REMOTE_NDIS_RESET_MSG\n", __func__);
+ return rndis_reset_response(configNr,
+ (rndis_reset_msg_type *) buf);
+
+ case REMOTE_NDIS_KEEPALIVE_MSG:
+ /* For USB: host does this every 5 seconds */
+#if defined(DEBUG) && defined(DEBUG_VERBOSE)
+ debug("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", __func__);
+#endif
+ return rndis_keepalive_response(configNr,
+ (rndis_keepalive_msg_type *) buf);
+
+ default:
+ /*
+ * At least Windows XP emits some undefined RNDIS messages.
+ * In one case those messages seemed to relate to the host
+ * suspending itself.
+ */
+ debug("%s: unknown RNDIS message 0x%08X len %d\n",
+ __func__ , MsgType, MsgLength);
+ {
+ unsigned i;
+ for (i = 0; i < MsgLength; i += 16) {
+ debug("%03d: "
+ " %02x %02x %02x %02x"
+ " %02x %02x %02x %02x"
+ " %02x %02x %02x %02x"
+ " %02x %02x %02x %02x"
+ "\n",
+ i,
+ buf[i], buf[i+1],
+ buf[i+2], buf[i+3],
+ buf[i+4], buf[i+5],
+ buf[i+6], buf[i+7],
+ buf[i+8], buf[i+9],
+ buf[i+10], buf[i+11],
+ buf[i+12], buf[i+13],
+ buf[i+14], buf[i+15]);
+ }
+ }
+ break;
+ }
+
+ return -ENOTSUPP;
+}
+
+int rndis_register(int (*rndis_control_ack)(struct eth_device *))
+{
+ u8 i;
+
+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+ if (!rndis_per_dev_params[i].used) {
+ rndis_per_dev_params[i].used = 1;
+ rndis_per_dev_params[i].ack = rndis_control_ack;
+ debug("%s: configNr = %d\n", __func__, i);
+ return i;
+ }
+ }
+ debug("%s failed\n", __func__);
+
+ return -1;
+}
+
+void rndis_deregister(int configNr)
+{
+ debug("%s: configNr = %d\n", __func__, configNr);
+
+ if (configNr >= RNDIS_MAX_CONFIGS)
+ return;
+ rndis_per_dev_params[configNr].used = 0;
+
+ return;
+}
+
+int rndis_set_param_dev(u8 configNr, struct eth_device *dev, int mtu,
+ struct net_device_stats *stats, u16 *cdc_filter)
+{
+ debug("%s: configNr = %d\n", __func__, configNr);
+ if (!dev || !stats)
+ return -1;
+ if (configNr >= RNDIS_MAX_CONFIGS)
+ return -1;
+
+ rndis_per_dev_params[configNr].dev = dev;
+ rndis_per_dev_params[configNr].stats = stats;
+ rndis_per_dev_params[configNr].mtu = mtu;
+ rndis_per_dev_params[configNr].filter = cdc_filter;
+
+ return 0;
+}
+
+int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
+{
+ debug("%s: configNr = %d\n", __func__, configNr);
+ if (!vendorDescr)
+ return -1;
+ if (configNr >= RNDIS_MAX_CONFIGS)
+ return -1;
+
+ rndis_per_dev_params[configNr].vendorID = vendorID;
+ rndis_per_dev_params[configNr].vendorDescr = vendorDescr;
+
+ return 0;
+}
+
+int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
+{
+ debug("%s: configNr = %d, %u %u\n", __func__, configNr, medium, speed);
+ if (configNr >= RNDIS_MAX_CONFIGS)
+ return -1;
+
+ rndis_per_dev_params[configNr].medium = medium;
+ rndis_per_dev_params[configNr].speed = speed;
+
+ return 0;
+}
+
+void rndis_add_hdr(void *buf, int length)
+{
+ struct rndis_packet_msg_type *header;
+
+ header = buf;
+ memset(header, 0, sizeof *header);
+ header->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG);
+ header->MessageLength = cpu_to_le32(length + sizeof *header);
+ header->DataOffset = __constant_cpu_to_le32(36);
+ header->DataLength = cpu_to_le32(length);
+}
+
+void rndis_free_response(int configNr, u8 *buf)
+{
+ rndis_resp_t *r;
+ struct list_head *act, *tmp;
+
+ list_for_each_safe(act, tmp,
+ &(rndis_per_dev_params[configNr].resp_queue))
+ {
+ r = list_entry(act, rndis_resp_t, list);
+ if (r && r->buf == buf) {
+ list_del(&r->list);
+ free(r);
+ }
+ }
+}
+
+u8 *rndis_get_next_response(int configNr, u32 *length)
+{
+ rndis_resp_t *r;
+ struct list_head *act, *tmp;
+
+ if (!length)
+ return NULL;
+
+ list_for_each_safe(act, tmp,
+ &(rndis_per_dev_params[configNr].resp_queue))
+ {
+ r = list_entry(act, rndis_resp_t, list);
+ if (!r->send) {
+ r->send = 1;
+ *length = r->length;
+ return r->buf;
+ }
+ }
+
+ return NULL;
+}
+
+static rndis_resp_t *rndis_add_response(int configNr, u32 length)
+{
+ rndis_resp_t *r;
+
+ /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */
+ r = malloc(sizeof(rndis_resp_t) + length);
+ if (!r)
+ return NULL;
+
+ r->buf = (u8 *) (r + 1);
+ r->length = length;
+ r->send = 0;
+
+ list_add_tail(&r->list,
+ &(rndis_per_dev_params[configNr].resp_queue));
+ return r;
+}
+
+int rndis_rm_hdr(void *buf, int length)
+{
+ /* tmp points to a struct rndis_packet_msg_type */
+ __le32 *tmp = buf;
+ int offs, len;
+
+ /* MessageType, MessageLength */
+ if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG)
+ != get_unaligned(tmp++))
+ return -EINVAL;
+ tmp++;
+
+ /* DataOffset, DataLength */
+ offs = get_unaligned_le32(tmp++) + 8 /* offset of DataOffset */;
+ if (offs != sizeof(struct rndis_packet_msg_type))
+ debug("%s: unexpected DataOffset: %d\n", __func__, offs);
+ if (offs >= length)
+ return -EOVERFLOW;
+
+ len = get_unaligned_le32(tmp++);
+ if (len + sizeof(struct rndis_packet_msg_type) != length)
+ debug("%s: unexpected DataLength: %d, packet length=%d\n",
+ __func__, len, length);
+
+ memmove(buf, buf + offs, len);
+
+ return offs;
+}
+
+int rndis_init(void)
+{
+ u8 i;
+
+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+ rndis_per_dev_params[i].confignr = i;
+ rndis_per_dev_params[i].used = 0;
+ rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED;
+ rndis_per_dev_params[i].media_state
+ = NDIS_MEDIA_STATE_DISCONNECTED;
+ INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue));
+ }
+
+ return 0;
+}
+
+void rndis_exit(void)
+{
+ /* Nothing to do */
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/rndis.h b/src/roms/u-boot/drivers/usb/gadget/rndis.h
new file mode 100644
index 0000000..d9e3a75
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/rndis.h
@@ -0,0 +1,260 @@
+/*
+ * RNDIS Definitions for Remote NDIS
+ *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This software was originally developed in conformance with
+ * Microsoft's Remote NDIS Specification License Agreement.
+ */
+
+#ifndef _USBGADGET_RNDIS_H
+#define _USBGADGET_RNDIS_H
+
+#include "ndis.h"
+
+/*
+ * By default rndis_signal_disconnect does not send status message about
+ * RNDIS disconnection to USB host (indicated as cable disconnected).
+ * Define RNDIS_COMPLETE_SIGNAL_DISCONNECT to send it.
+ * However, this will cause 1 sec delay on Ethernet device halt.
+ * Usually you do not need to define it. Mostly usable for debugging.
+ */
+
+#define RNDIS_MAXIMUM_FRAME_SIZE 1518
+#define RNDIS_MAX_TOTAL_SIZE 1558
+
+/* Remote NDIS Versions */
+#define RNDIS_MAJOR_VERSION 1
+#define RNDIS_MINOR_VERSION 0
+
+/* Status Values */
+#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */
+#define RNDIS_STATUS_FAILURE 0xC0000001U /* Unspecified error */
+#define RNDIS_STATUS_INVALID_DATA 0xC0010015U /* Invalid data */
+#define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BBU /* Unsupported request */
+#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BU /* Device connected */
+#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CU /* Device disconnected */
+/*
+ * For all not specified status messages:
+ * RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx
+ */
+
+/* Message Set for Connectionless (802.3) Devices */
+#define REMOTE_NDIS_PACKET_MSG 0x00000001U
+#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */
+#define REMOTE_NDIS_HALT_MSG 0x00000003U
+#define REMOTE_NDIS_QUERY_MSG 0x00000004U
+#define REMOTE_NDIS_SET_MSG 0x00000005U
+#define REMOTE_NDIS_RESET_MSG 0x00000006U
+#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007U
+#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008U
+
+/* Message completion */
+#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002U
+#define REMOTE_NDIS_QUERY_CMPLT 0x80000004U
+#define REMOTE_NDIS_SET_CMPLT 0x80000005U
+#define REMOTE_NDIS_RESET_CMPLT 0x80000006U
+#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008U
+
+/* Device Flags */
+#define RNDIS_DF_CONNECTIONLESS 0x00000001U
+#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002U
+
+#define RNDIS_MEDIUM_802_3 0x00000000U
+
+/* from drivers/net/sk98lin/h/skgepnmi.h */
+#define OID_PNP_CAPABILITIES 0xFD010100
+#define OID_PNP_SET_POWER 0xFD010101
+#define OID_PNP_QUERY_POWER 0xFD010102
+#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103
+#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104
+#define OID_PNP_ENABLE_WAKE_UP 0xFD010106
+
+
+typedef struct rndis_init_msg_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 MajorVersion;
+ __le32 MinorVersion;
+ __le32 MaxTransferSize;
+} rndis_init_msg_type;
+
+typedef struct rndis_init_cmplt_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+ __le32 MajorVersion;
+ __le32 MinorVersion;
+ __le32 DeviceFlags;
+ __le32 Medium;
+ __le32 MaxPacketsPerTransfer;
+ __le32 MaxTransferSize;
+ __le32 PacketAlignmentFactor;
+ __le32 AFListOffset;
+ __le32 AFListSize;
+} rndis_init_cmplt_type;
+
+typedef struct rndis_halt_msg_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+} rndis_halt_msg_type;
+
+typedef struct rndis_query_msg_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 OID;
+ __le32 InformationBufferLength;
+ __le32 InformationBufferOffset;
+ __le32 DeviceVcHandle;
+} rndis_query_msg_type;
+
+typedef struct rndis_query_cmplt_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+ __le32 InformationBufferLength;
+ __le32 InformationBufferOffset;
+} rndis_query_cmplt_type;
+
+typedef struct rndis_set_msg_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 OID;
+ __le32 InformationBufferLength;
+ __le32 InformationBufferOffset;
+ __le32 DeviceVcHandle;
+} rndis_set_msg_type;
+
+typedef struct rndis_set_cmplt_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+} rndis_set_cmplt_type;
+
+typedef struct rndis_reset_msg_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 Reserved;
+} rndis_reset_msg_type;
+
+typedef struct rndis_reset_cmplt_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 Status;
+ __le32 AddressingReset;
+} rndis_reset_cmplt_type;
+
+typedef struct rndis_indicate_status_msg_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 Status;
+ __le32 StatusBufferLength;
+ __le32 StatusBufferOffset;
+} rndis_indicate_status_msg_type;
+
+typedef struct rndis_keepalive_msg_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+} rndis_keepalive_msg_type;
+
+typedef struct rndis_keepalive_cmplt_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+} rndis_keepalive_cmplt_type;
+
+struct rndis_packet_msg_type {
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 DataOffset;
+ __le32 DataLength;
+ __le32 OOBDataOffset;
+ __le32 OOBDataLength;
+ __le32 NumOOBDataElements;
+ __le32 PerPacketInfoOffset;
+ __le32 PerPacketInfoLength;
+ __le32 VcHandle;
+ __le32 Reserved;
+} __attribute__ ((packed));
+
+struct rndis_config_parameter {
+ __le32 ParameterNameOffset;
+ __le32 ParameterNameLength;
+ __le32 ParameterType;
+ __le32 ParameterValueOffset;
+ __le32 ParameterValueLength;
+};
+
+/* implementation specific */
+enum rndis_state {
+ RNDIS_UNINITIALIZED,
+ RNDIS_INITIALIZED,
+ RNDIS_DATA_INITIALIZED,
+};
+
+typedef struct rndis_resp_t {
+ struct list_head list;
+ u8 *buf;
+ u32 length;
+ int send;
+} rndis_resp_t;
+
+typedef struct rndis_params {
+ u8 confignr;
+ u8 used;
+ u16 saved_filter;
+ enum rndis_state state;
+ u32 medium;
+ u32 speed;
+ u32 media_state;
+
+ const u8 *host_mac;
+ u16 *filter;
+ struct eth_device *dev;
+ struct net_device_stats *stats;
+ int mtu;
+
+ u32 vendorID;
+ const char *vendorDescr;
+ int (*ack)(struct eth_device *);
+ struct list_head resp_queue;
+} rndis_params;
+
+/* RNDIS Message parser and other useless functions */
+int rndis_msg_parser(u8 configNr, u8 *buf);
+enum rndis_state rndis_get_state(int configNr);
+int rndis_register(int (*rndis_control_ack)(struct eth_device *));
+void rndis_deregister(int configNr);
+int rndis_set_param_dev(u8 configNr, struct eth_device *dev, int mtu,
+ struct net_device_stats *stats, u16 *cdc_filter);
+int rndis_set_param_vendor(u8 configNr, u32 vendorID,
+ const char *vendorDescr);
+int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed);
+void rndis_add_hdr(void *bug, int length);
+int rndis_rm_hdr(void *bug, int length);
+u8 *rndis_get_next_response(int configNr, u32 *length);
+void rndis_free_response(int configNr, u8 *buf);
+
+void rndis_uninit(int configNr);
+int rndis_signal_connect(int configNr);
+int rndis_signal_disconnect(int configNr);
+extern void rndis_set_host_mac(int configNr, const u8 *addr);
+
+int rndis_init(void);
+void rndis_exit(void);
+
+#endif /* _USBGADGET_RNDIS_H */
diff --git a/src/roms/u-boot/drivers/usb/gadget/s3c_udc_otg.c b/src/roms/u-boot/drivers/usb/gadget/s3c_udc_otg.c
new file mode 100644
index 0000000..63d4487
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/s3c_udc_otg.c
@@ -0,0 +1,890 @@
+/*
+ * drivers/usb/gadget/s3c_udc_otg.c
+ * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers
+ *
+ * Copyright (C) 2008 for Samsung Electronics
+ *
+ * BSP Support for Samsung's UDC driver
+ * available at:
+ * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git
+ *
+ * State machine bugfixes:
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * Ported to u-boot:
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ * Lukasz Majewski <l.majewski@samsumg.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#undef DEBUG
+#include <common.h>
+#include <asm/errno.h>
+#include <linux/list.h>
+#include <malloc.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <asm/io.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/gpio.h>
+
+#include "regs-otg.h"
+#include <usb/lin_gadget_compat.h>
+
+/***********************************************************/
+
+#define OTG_DMA_MODE 1
+
+#define DEBUG_SETUP 0
+#define DEBUG_EP0 0
+#define DEBUG_ISR 0
+#define DEBUG_OUT_EP 0
+#define DEBUG_IN_EP 0
+
+#include <usb/s3c_udc.h>
+
+#define EP0_CON 0
+#define EP_MASK 0xF
+
+static char *state_names[] = {
+ "WAIT_FOR_SETUP",
+ "DATA_STATE_XMIT",
+ "DATA_STATE_NEED_ZLP",
+ "WAIT_FOR_OUT_STATUS",
+ "DATA_STATE_RECV",
+ "WAIT_FOR_COMPLETE",
+ "WAIT_FOR_OUT_COMPLETE",
+ "WAIT_FOR_IN_COMPLETE",
+ "WAIT_FOR_NULL_COMPLETE",
+};
+
+#define DRIVER_DESC "S3C HS USB OTG Device Driver, (c) Samsung Electronics"
+#define DRIVER_VERSION "15 March 2009"
+
+struct s3c_udc *the_controller;
+
+static const char driver_name[] = "s3c-udc";
+static const char driver_desc[] = DRIVER_DESC;
+static const char ep0name[] = "ep0-control";
+
+/* Max packet size*/
+static unsigned int ep0_fifo_size = 64;
+static unsigned int ep_fifo_size = 512;
+static unsigned int ep_fifo_size2 = 1024;
+static int reset_available = 1;
+
+static struct usb_ctrlrequest *usb_ctrl;
+static dma_addr_t usb_ctrl_dma_addr;
+
+/*
+ Local declarations.
+*/
+static int s3c_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *);
+static int s3c_ep_disable(struct usb_ep *ep);
+static struct usb_request *s3c_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags);
+static void s3c_free_request(struct usb_ep *ep, struct usb_request *);
+
+static int s3c_queue(struct usb_ep *ep, struct usb_request *, gfp_t gfp_flags);
+static int s3c_dequeue(struct usb_ep *ep, struct usb_request *);
+static int s3c_fifo_status(struct usb_ep *ep);
+static void s3c_fifo_flush(struct usb_ep *ep);
+static void s3c_ep0_read(struct s3c_udc *dev);
+static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep);
+static void s3c_handle_ep0(struct s3c_udc *dev);
+static int s3c_ep0_write(struct s3c_udc *dev);
+static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req);
+static void done(struct s3c_ep *ep, struct s3c_request *req, int status);
+static void stop_activity(struct s3c_udc *dev,
+ struct usb_gadget_driver *driver);
+static int udc_enable(struct s3c_udc *dev);
+static void udc_set_address(struct s3c_udc *dev, unsigned char address);
+static void reconfig_usbd(void);
+static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed);
+static void nuke(struct s3c_ep *ep, int status);
+static int s3c_udc_set_halt(struct usb_ep *_ep, int value);
+static void s3c_udc_set_nak(struct s3c_ep *ep);
+
+void set_udc_gadget_private_data(void *p)
+{
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: the_controller: 0x%p, p: 0x%p\n", __func__,
+ the_controller, p);
+ the_controller->gadget.dev.device_data = p;
+}
+
+void *get_udc_gadget_private_data(struct usb_gadget *gadget)
+{
+ return gadget->dev.device_data;
+}
+
+static struct usb_ep_ops s3c_ep_ops = {
+ .enable = s3c_ep_enable,
+ .disable = s3c_ep_disable,
+
+ .alloc_request = s3c_alloc_request,
+ .free_request = s3c_free_request,
+
+ .queue = s3c_queue,
+ .dequeue = s3c_dequeue,
+
+ .set_halt = s3c_udc_set_halt,
+ .fifo_status = s3c_fifo_status,
+ .fifo_flush = s3c_fifo_flush,
+};
+
+#define create_proc_files() do {} while (0)
+#define remove_proc_files() do {} while (0)
+
+/***********************************************************/
+
+void __iomem *regs_otg;
+struct s3c_usbotg_reg *reg;
+struct s3c_usbotg_phy *phy;
+static unsigned int usb_phy_ctrl;
+
+void otg_phy_init(struct s3c_udc *dev)
+{
+ dev->pdata->phy_control(1);
+
+ /*USB PHY0 Enable */
+ printf("USB PHY0 Enable\n");
+
+ /* Enable PHY */
+ writel(readl(usb_phy_ctrl) | USB_PHY_CTRL_EN0, usb_phy_ctrl);
+
+ if (dev->pdata->usb_flags == PHY0_SLEEP) /* C210 Universal */
+ writel((readl(&phy->phypwr)
+ &~(PHY_0_SLEEP | OTG_DISABLE_0 | ANALOG_PWRDOWN)
+ &~FORCE_SUSPEND_0), &phy->phypwr);
+ else /* C110 GONI */
+ writel((readl(&phy->phypwr) &~(OTG_DISABLE_0 | ANALOG_PWRDOWN)
+ &~FORCE_SUSPEND_0), &phy->phypwr);
+
+ if (s5p_cpu_id == 0x4412)
+ writel((readl(&phy->phyclk) & ~(EXYNOS4X12_ID_PULLUP0 |
+ EXYNOS4X12_COMMON_ON_N0)) | EXYNOS4X12_CLK_SEL_24MHZ,
+ &phy->phyclk); /* PLL 24Mhz */
+ else
+ writel((readl(&phy->phyclk) & ~(ID_PULLUP0 | COMMON_ON_N0)) |
+ CLK_SEL_24MHZ, &phy->phyclk); /* PLL 24Mhz */
+
+ writel((readl(&phy->rstcon) &~(LINK_SW_RST | PHYLNK_SW_RST))
+ | PHY_SW_RST0, &phy->rstcon);
+ udelay(10);
+ writel(readl(&phy->rstcon)
+ &~(PHY_SW_RST0 | LINK_SW_RST | PHYLNK_SW_RST), &phy->rstcon);
+ udelay(10);
+}
+
+void otg_phy_off(struct s3c_udc *dev)
+{
+ /* reset controller just in case */
+ writel(PHY_SW_RST0, &phy->rstcon);
+ udelay(20);
+ writel(readl(&phy->phypwr) &~PHY_SW_RST0, &phy->rstcon);
+ udelay(20);
+
+ writel(readl(&phy->phypwr) | OTG_DISABLE_0 | ANALOG_PWRDOWN
+ | FORCE_SUSPEND_0, &phy->phypwr);
+
+ writel(readl(usb_phy_ctrl) &~USB_PHY_CTRL_EN0, usb_phy_ctrl);
+
+ writel((readl(&phy->phyclk) & ~(ID_PULLUP0 | COMMON_ON_N0)),
+ &phy->phyclk);
+
+ udelay(10000);
+
+ dev->pdata->phy_control(0);
+}
+
+/***********************************************************/
+
+#include "s3c_udc_otg_xfer_dma.c"
+
+/*
+ * udc_disable - disable USB device controller
+ */
+static void udc_disable(struct s3c_udc *dev)
+{
+ debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
+
+ udc_set_address(dev, 0);
+
+ dev->ep0state = WAIT_FOR_SETUP;
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+ dev->usb_address = 0;
+
+ otg_phy_off(dev);
+}
+
+/*
+ * udc_reinit - initialize software state
+ */
+static void udc_reinit(struct s3c_udc *dev)
+{
+ unsigned int i;
+
+ debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
+
+ /* device/ep0 records init */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ /* basic endpoint records init */
+ for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
+ struct s3c_ep *ep = &dev->ep[i];
+
+ if (i != 0)
+ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+ ep->desc = 0;
+ ep->stopped = 0;
+ INIT_LIST_HEAD(&ep->queue);
+ ep->pio_irqs = 0;
+ }
+
+ /* the rest was statically initialized, and is read-only */
+}
+
+#define BYTES2MAXP(x) (x / 8)
+#define MAXP2BYTES(x) (x * 8)
+
+/* until it's enabled, this UDC should be completely invisible
+ * to any USB host.
+ */
+static int udc_enable(struct s3c_udc *dev)
+{
+ debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
+
+ otg_phy_init(dev);
+ reconfig_usbd();
+
+ debug_cond(DEBUG_SETUP != 0,
+ "S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n",
+ readl(&reg->gintmsk));
+
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ return 0;
+}
+
+/*
+ Register entry point for the peripheral controller driver.
+*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct s3c_udc *dev = the_controller;
+ int retval = 0;
+ unsigned long flags;
+
+ debug_cond(DEBUG_SETUP != 0, "%s: %s\n", __func__, "no name");
+
+ if (!driver
+ || (driver->speed != USB_SPEED_FULL
+ && driver->speed != USB_SPEED_HIGH)
+ || !driver->bind || !driver->disconnect || !driver->setup)
+ return -EINVAL;
+ if (!dev)
+ return -ENODEV;
+ if (dev->driver)
+ return -EBUSY;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ /* first hook up the driver ... */
+ dev->driver = driver;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (retval) { /* TODO */
+ printf("target device_add failed, error %d\n", retval);
+ return retval;
+ }
+
+ retval = driver->bind(&dev->gadget);
+ if (retval) {
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: bind to driver --> error %d\n",
+ dev->gadget.name, retval);
+ dev->driver = 0;
+ return retval;
+ }
+
+ enable_irq(IRQ_OTG);
+
+ debug_cond(DEBUG_SETUP != 0,
+ "Registered gadget driver %s\n", dev->gadget.name);
+ udc_enable(dev);
+
+ return 0;
+}
+
+/*
+ * Unregister entry point for the peripheral controller driver.
+ */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct s3c_udc *dev = the_controller;
+ unsigned long flags;
+
+ if (!dev)
+ return -ENODEV;
+ if (!driver || driver != dev->driver)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->driver = 0;
+ stop_activity(dev, driver);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ driver->unbind(&dev->gadget);
+
+ disable_irq(IRQ_OTG);
+
+ udc_disable(dev);
+ return 0;
+}
+
+/*
+ * done - retire a request; caller blocked irqs
+ */
+static void done(struct s3c_ep *ep, struct s3c_request *req, int status)
+{
+ unsigned int stopped = ep->stopped;
+
+ debug("%s: %s %p, req = %p, stopped = %d\n",
+ __func__, ep->ep.name, ep, &req->req, stopped);
+
+ list_del_init(&req->queue);
+
+ if (likely(req->req.status == -EINPROGRESS))
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ if (status && status != -ESHUTDOWN) {
+ debug("complete %s req %p stat %d len %u/%u\n",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+ }
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+
+#ifdef DEBUG
+ printf("calling complete callback\n");
+ {
+ int i, len = req->req.length;
+
+ printf("pkt[%d] = ", req->req.length);
+ if (len > 64)
+ len = 64;
+ for (i = 0; i < len; i++) {
+ printf("%02x", ((u8 *)req->req.buf)[i]);
+ if ((i & 7) == 7)
+ printf(" ");
+ }
+ printf("\n");
+ }
+#endif
+ spin_unlock(&ep->dev->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&ep->dev->lock);
+
+ debug("callback completed\n");
+
+ ep->stopped = stopped;
+}
+
+/*
+ * nuke - dequeue ALL requests
+ */
+static void nuke(struct s3c_ep *ep, int status)
+{
+ struct s3c_request *req;
+
+ debug("%s: %s %p\n", __func__, ep->ep.name, ep);
+
+ /* called with irqs blocked */
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+ done(ep, req, status);
+ }
+}
+
+static void stop_activity(struct s3c_udc *dev,
+ struct usb_gadget_driver *driver)
+{
+ int i;
+
+ /* don't disconnect drivers more than once */
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = 0;
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
+ struct s3c_ep *ep = &dev->ep[i];
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
+
+ /* report disconnect; the driver is already quiesced */
+ if (driver) {
+ spin_unlock(&dev->lock);
+ driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
+
+ /* re-init driver-visible data structures */
+ udc_reinit(dev);
+}
+
+static void reconfig_usbd(void)
+{
+ /* 2. Soft-reset OTG Core and then unreset again. */
+ int i;
+ unsigned int uTemp = writel(CORE_SOFT_RESET, &reg->grstctl);
+
+ debug("Reseting OTG controller\n");
+
+ writel(0<<15 /* PHY Low Power Clock sel*/
+ |1<<14 /* Non-Periodic TxFIFO Rewind Enable*/
+ |0x5<<10 /* Turnaround time*/
+ |0<<9 | 0<<8 /* [0:HNP disable,1:HNP enable][ 0:SRP disable*/
+ /* 1:SRP enable] H1= 1,1*/
+ |0<<7 /* Ulpi DDR sel*/
+ |0<<6 /* 0: high speed utmi+, 1: full speed serial*/
+ |0<<4 /* 0: utmi+, 1:ulpi*/
+ |1<<3 /* phy i/f 0:8bit, 1:16bit*/
+ |0x7<<0, /* HS/FS Timeout**/
+ &reg->gusbcfg);
+
+ /* 3. Put the OTG device core in the disconnected state.*/
+ uTemp = readl(&reg->dctl);
+ uTemp |= SOFT_DISCONNECT;
+ writel(uTemp, &reg->dctl);
+
+ udelay(20);
+
+ /* 4. Make the OTG device core exit from the disconnected state.*/
+ uTemp = readl(&reg->dctl);
+ uTemp = uTemp & ~SOFT_DISCONNECT;
+ writel(uTemp, &reg->dctl);
+
+ /* 5. Configure OTG Core to initial settings of device mode.*/
+ /* [][1: full speed(30Mhz) 0:high speed]*/
+ writel(EP_MISS_CNT(1) | DEV_SPEED_HIGH_SPEED_20, &reg->dcfg);
+
+ mdelay(1);
+
+ /* 6. Unmask the core interrupts*/
+ writel(GINTMSK_INIT, &reg->gintmsk);
+
+ /* 7. Set NAK bit of EP0, EP1, EP2*/
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->out_endp[EP0_CON].doepctl);
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->in_endp[EP0_CON].diepctl);
+
+ for (i = 1; i < S3C_MAX_ENDPOINTS; i++) {
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->out_endp[i].doepctl);
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->in_endp[i].diepctl);
+ }
+
+ /* 8. Unmask EPO interrupts*/
+ writel(((1 << EP0_CON) << DAINT_OUT_BIT)
+ | (1 << EP0_CON), &reg->daintmsk);
+
+ /* 9. Unmask device OUT EP common interrupts*/
+ writel(DOEPMSK_INIT, &reg->doepmsk);
+
+ /* 10. Unmask device IN EP common interrupts*/
+ writel(DIEPMSK_INIT, &reg->diepmsk);
+
+ /* 11. Set Rx FIFO Size (in 32-bit words) */
+ writel(RX_FIFO_SIZE >> 2, &reg->grxfsiz);
+
+ /* 12. Set Non Periodic Tx FIFO Size */
+ writel((NPTX_FIFO_SIZE >> 2) << 16 | ((RX_FIFO_SIZE >> 2)) << 0,
+ &reg->gnptxfsiz);
+
+ for (i = 1; i < S3C_MAX_HW_ENDPOINTS; i++)
+ writel((PTX_FIFO_SIZE >> 2) << 16 |
+ ((RX_FIFO_SIZE + NPTX_FIFO_SIZE +
+ PTX_FIFO_SIZE*(i-1)) >> 2) << 0,
+ &reg->dieptxf[i-1]);
+
+ /* Flush the RX FIFO */
+ writel(RX_FIFO_FLUSH, &reg->grstctl);
+ while (readl(&reg->grstctl) & RX_FIFO_FLUSH)
+ debug("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__);
+
+ /* Flush all the Tx FIFO's */
+ writel(TX_FIFO_FLUSH_ALL, &reg->grstctl);
+ writel(TX_FIFO_FLUSH_ALL | TX_FIFO_FLUSH, &reg->grstctl);
+ while (readl(&reg->grstctl) & TX_FIFO_FLUSH)
+ debug("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__);
+
+ /* 13. Clear NAK bit of EP0, EP1, EP2*/
+ /* For Slave mode*/
+ /* EP0: Control OUT */
+ writel(DEPCTL_EPDIS | DEPCTL_CNAK,
+ &reg->out_endp[EP0_CON].doepctl);
+
+ /* 14. Initialize OTG Link Core.*/
+ writel(GAHBCFG_INIT, &reg->gahbcfg);
+}
+
+static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed)
+{
+ unsigned int ep_ctrl;
+ int i;
+
+ if (speed == USB_SPEED_HIGH) {
+ ep0_fifo_size = 64;
+ ep_fifo_size = 512;
+ ep_fifo_size2 = 1024;
+ dev->gadget.speed = USB_SPEED_HIGH;
+ } else {
+ ep0_fifo_size = 64;
+ ep_fifo_size = 64;
+ ep_fifo_size2 = 64;
+ dev->gadget.speed = USB_SPEED_FULL;
+ }
+
+ dev->ep[0].ep.maxpacket = ep0_fifo_size;
+ for (i = 1; i < S3C_MAX_ENDPOINTS; i++)
+ dev->ep[i].ep.maxpacket = ep_fifo_size;
+
+ /* EP0 - Control IN (64 bytes)*/
+ ep_ctrl = readl(&reg->in_endp[EP0_CON].diepctl);
+ writel(ep_ctrl|(0<<0), &reg->in_endp[EP0_CON].diepctl);
+
+ /* EP0 - Control OUT (64 bytes)*/
+ ep_ctrl = readl(&reg->out_endp[EP0_CON].doepctl);
+ writel(ep_ctrl|(0<<0), &reg->out_endp[EP0_CON].doepctl);
+}
+
+static int s3c_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct s3c_ep *ep;
+ struct s3c_udc *dev;
+ unsigned long flags;
+
+ debug("%s: %p\n", __func__, _ep);
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!_ep || !desc || ep->desc || _ep->name == ep0name
+ || desc->bDescriptorType != USB_DT_ENDPOINT
+ || ep->bEndpointAddress != desc->bEndpointAddress
+ || ep_maxpacket(ep) <
+ le16_to_cpu(get_unaligned(&desc->wMaxPacketSize))) {
+
+ debug("%s: bad ep or descriptor\n", __func__);
+ return -EINVAL;
+ }
+
+ /* xfer types must match, except that interrupt ~= bulk */
+ if (ep->bmAttributes != desc->bmAttributes
+ && ep->bmAttributes != USB_ENDPOINT_XFER_BULK
+ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+
+ debug("%s: %s type mismatch\n", __func__, _ep->name);
+ return -EINVAL;
+ }
+
+ /* hardware _could_ do smaller, but driver doesn't */
+ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+ && le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)) !=
+ ep_maxpacket(ep)) || !get_unaligned(&desc->wMaxPacketSize)) {
+
+ debug("%s: bad %s maxpacket\n", __func__, _ep->name);
+ return -ERANGE;
+ }
+
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+
+ debug("%s: bogus device state\n", __func__);
+ return -ESHUTDOWN;
+ }
+
+ ep->stopped = 0;
+ ep->desc = desc;
+ ep->pio_irqs = 0;
+ ep->ep.maxpacket = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));
+
+ /* Reset halt state */
+ s3c_udc_set_nak(ep);
+ s3c_udc_set_halt(_ep, 0);
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ s3c_udc_ep_activate(ep);
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+ debug("%s: enabled %s, stopped = %d, maxpacket = %d\n",
+ __func__, _ep->name, ep->stopped, ep->ep.maxpacket);
+ return 0;
+}
+
+/*
+ * Disable EP
+ */
+static int s3c_ep_disable(struct usb_ep *_ep)
+{
+ struct s3c_ep *ep;
+ unsigned long flags;
+
+ debug("%s: %p\n", __func__, _ep);
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!_ep || !ep->desc) {
+ debug("%s: %s not enabled\n", __func__,
+ _ep ? ep->ep.name : NULL);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ /* Nuke all pending requests */
+ nuke(ep, -ESHUTDOWN);
+
+ ep->desc = 0;
+ ep->stopped = 1;
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+ debug("%s: disabled %s\n", __func__, _ep->name);
+ return 0;
+}
+
+static struct usb_request *s3c_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct s3c_request *req;
+
+ debug("%s: %s %p\n", __func__, ep->name, ep);
+
+ req = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*req));
+ if (!req)
+ return 0;
+
+ memset(req, 0, sizeof *req);
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+static void s3c_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+ struct s3c_request *req;
+
+ debug("%s: %p\n", __func__, ep);
+
+ req = container_of(_req, struct s3c_request, req);
+ WARN_ON(!list_empty(&req->queue));
+ kfree(req);
+}
+
+/* dequeue JUST ONE request */
+static int s3c_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct s3c_ep *ep;
+ struct s3c_request *req;
+ unsigned long flags;
+
+ debug("%s: %p\n", __func__, _ep);
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!_ep || ep->ep.name == ep0name)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ /* make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return -EINVAL;
+ }
+
+ done(ep, req, -ECONNRESET);
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return 0;
+}
+
+/*
+ * Return bytes in EP FIFO
+ */
+static int s3c_fifo_status(struct usb_ep *_ep)
+{
+ int count = 0;
+ struct s3c_ep *ep;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!_ep) {
+ debug("%s: bad ep\n", __func__);
+ return -ENODEV;
+ }
+
+ debug("%s: %d\n", __func__, ep_index(ep));
+
+ /* LPD can't report unclaimed bytes from IN fifos */
+ if (ep_is_in(ep))
+ return -EOPNOTSUPP;
+
+ return count;
+}
+
+/*
+ * Flush EP FIFO
+ */
+static void s3c_fifo_flush(struct usb_ep *_ep)
+{
+ struct s3c_ep *ep;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+ debug("%s: bad ep\n", __func__);
+ return;
+ }
+
+ debug("%s: %d\n", __func__, ep_index(ep));
+}
+
+static const struct usb_gadget_ops s3c_udc_ops = {
+ /* current versions must always be self-powered */
+};
+
+static struct s3c_udc memory = {
+ .usb_address = 0,
+ .gadget = {
+ .ops = &s3c_udc_ops,
+ .ep0 = &memory.ep[0].ep,
+ .name = driver_name,
+ },
+
+ /* control endpoint */
+ .ep[0] = {
+ .ep = {
+ .name = ep0name,
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP0_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = 0,
+ .bmAttributes = 0,
+
+ .ep_type = ep_control,
+ },
+
+ /* first group of endpoints */
+ .ep[1] = {
+ .ep = {
+ .name = "ep1in-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 1,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_out,
+ .fifo_num = 1,
+ },
+
+ .ep[2] = {
+ .ep = {
+ .name = "ep2out-bulk",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_OUT | 2,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+ .ep_type = ep_bulk_in,
+ .fifo_num = 2,
+ },
+
+ .ep[3] = {
+ .ep = {
+ .name = "ep3in-int",
+ .ops = &s3c_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+
+ .bEndpointAddress = USB_DIR_IN | 3,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+
+ .ep_type = ep_interrupt,
+ .fifo_num = 3,
+ },
+};
+
+/*
+ * probe - binds to the platform device
+ */
+
+int s3c_udc_probe(struct s3c_plat_otg_data *pdata)
+{
+ struct s3c_udc *dev = &memory;
+ int retval = 0;
+
+ debug("%s: %p\n", __func__, pdata);
+
+ dev->pdata = pdata;
+
+ phy = (struct s3c_usbotg_phy *)pdata->regs_phy;
+ reg = (struct s3c_usbotg_reg *)pdata->regs_otg;
+ usb_phy_ctrl = pdata->usb_phy_ctrl;
+
+ /* regs_otg = (void *)pdata->regs_otg; */
+
+ dev->gadget.is_dualspeed = 1; /* Hack only*/
+ dev->gadget.is_otg = 0;
+ dev->gadget.is_a_peripheral = 0;
+ dev->gadget.b_hnp_enable = 0;
+ dev->gadget.a_hnp_support = 0;
+ dev->gadget.a_alt_hnp_support = 0;
+
+ the_controller = dev;
+
+ usb_ctrl = memalign(CONFIG_SYS_CACHELINE_SIZE,
+ ROUND(sizeof(struct usb_ctrlrequest),
+ CONFIG_SYS_CACHELINE_SIZE));
+ if (!usb_ctrl) {
+ error("No memory available for UDC!\n");
+ return -ENOMEM;
+ }
+
+ usb_ctrl_dma_addr = (dma_addr_t) usb_ctrl;
+
+ udc_reinit(dev);
+
+ return retval;
+}
+
+int usb_gadget_handle_interrupts()
+{
+ u32 intr_status = readl(&reg->gintsts);
+ u32 gintmsk = readl(&reg->gintmsk);
+
+ if (intr_status & gintmsk)
+ return s3c_udc_irq(1, (void *)the_controller);
+ return 0;
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/src/roms/u-boot/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
new file mode 100644
index 0000000..06dfeed
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
@@ -0,0 +1,1480 @@
+/*
+ * drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
+ * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers
+ *
+ * Copyright (C) 2009 for Samsung Electronics
+ *
+ * BSP Support for Samsung's UDC driver
+ * available at:
+ * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git
+ *
+ * State machine bugfixes:
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * Ported to u-boot:
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ * Lukasz Majewski <l.majewski@samsumg.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+static u8 clear_feature_num;
+int clear_feature_flag;
+
+/* Bulk-Only Mass Storage Reset (class-specific request) */
+#define GET_MAX_LUN_REQUEST 0xFE
+#define BOT_RESET_REQUEST 0xFF
+
+static inline void s3c_udc_ep0_zlp(struct s3c_udc *dev)
+{
+ u32 ep_ctrl;
+
+ writel(usb_ctrl_dma_addr, &reg->in_endp[EP0_CON].diepdma);
+ writel(DIEPT_SIZ_PKT_CNT(1), &reg->in_endp[EP0_CON].dieptsiz);
+
+ ep_ctrl = readl(&reg->in_endp[EP0_CON].diepctl);
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK,
+ &reg->in_endp[EP0_CON].diepctl);
+
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->in_endp[EP0_CON].diepctl));
+ dev->ep0state = WAIT_FOR_IN_COMPLETE;
+}
+
+void s3c_udc_pre_setup(void)
+{
+ u32 ep_ctrl;
+
+ debug_cond(DEBUG_IN_EP,
+ "%s : Prepare Setup packets.\n", __func__);
+
+ writel(DOEPT_SIZ_PKT_CNT(1) | sizeof(struct usb_ctrlrequest),
+ &reg->out_endp[EP0_CON].doeptsiz);
+ writel(usb_ctrl_dma_addr, &reg->out_endp[EP0_CON].doepdma);
+
+ ep_ctrl = readl(&reg->out_endp[EP0_CON].doepctl);
+ writel(ep_ctrl|DEPCTL_EPENA, &reg->out_endp[EP0_CON].doepctl);
+
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->in_endp[EP0_CON].diepctl));
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->out_endp[EP0_CON].doepctl));
+
+}
+
+static inline void s3c_ep0_complete_out(void)
+{
+ u32 ep_ctrl;
+
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->in_endp[EP0_CON].diepctl));
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->out_endp[EP0_CON].doepctl));
+
+ debug_cond(DEBUG_IN_EP,
+ "%s : Prepare Complete Out packet.\n", __func__);
+
+ writel(DOEPT_SIZ_PKT_CNT(1) | sizeof(struct usb_ctrlrequest),
+ &reg->out_endp[EP0_CON].doeptsiz);
+ writel(usb_ctrl_dma_addr, &reg->out_endp[EP0_CON].doepdma);
+
+ ep_ctrl = readl(&reg->out_endp[EP0_CON].doepctl);
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK,
+ &reg->out_endp[EP0_CON].doepctl);
+
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->in_endp[EP0_CON].diepctl));
+ debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->out_endp[EP0_CON].doepctl));
+
+}
+
+
+static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
+{
+ u32 *buf, ctrl;
+ u32 length, pktcnt;
+ u32 ep_num = ep_index(ep);
+
+ buf = req->req.buf + req->req.actual;
+ length = min(req->req.length - req->req.actual,
+ ep_num ? DMA_BUFFER_SIZE : ep->ep.maxpacket);
+
+ ep->len = length;
+ ep->dma_buf = buf;
+
+ if (ep_num == EP0_CON || length == 0)
+ pktcnt = 1;
+ else
+ pktcnt = (length - 1)/(ep->ep.maxpacket) + 1;
+
+ ctrl = readl(&reg->out_endp[ep_num].doepctl);
+
+ writel((unsigned int) ep->dma_buf, &reg->out_endp[ep_num].doepdma);
+ writel(DOEPT_SIZ_PKT_CNT(pktcnt) | DOEPT_SIZ_XFER_SIZE(length),
+ &reg->out_endp[ep_num].doeptsiz);
+ writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, &reg->out_endp[ep_num].doepctl);
+
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: EP%d RX DMA start : DOEPDMA = 0x%x,"
+ "DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n"
+ "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n",
+ __func__, ep_num,
+ readl(&reg->out_endp[ep_num].doepdma),
+ readl(&reg->out_endp[ep_num].doeptsiz),
+ readl(&reg->out_endp[ep_num].doepctl),
+ buf, pktcnt, length);
+ return 0;
+
+}
+
+int setdma_tx(struct s3c_ep *ep, struct s3c_request *req)
+{
+ u32 *buf, ctrl = 0;
+ u32 length, pktcnt;
+ u32 ep_num = ep_index(ep);
+
+ buf = req->req.buf + req->req.actual;
+ length = req->req.length - req->req.actual;
+
+ if (ep_num == EP0_CON)
+ length = min(length, (u32)ep_maxpacket(ep));
+
+ ep->len = length;
+ ep->dma_buf = buf;
+
+ flush_dcache_range((unsigned long) ep->dma_buf,
+ (unsigned long) ep->dma_buf +
+ ROUND(ep->len, CONFIG_SYS_CACHELINE_SIZE));
+
+ if (length == 0)
+ pktcnt = 1;
+ else
+ pktcnt = (length - 1)/(ep->ep.maxpacket) + 1;
+
+ /* Flush the endpoint's Tx FIFO */
+ writel(TX_FIFO_NUMBER(ep->fifo_num), &reg->grstctl);
+ writel(TX_FIFO_NUMBER(ep->fifo_num) | TX_FIFO_FLUSH, &reg->grstctl);
+ while (readl(&reg->grstctl) & TX_FIFO_FLUSH)
+ ;
+
+ writel((unsigned long) ep->dma_buf, &reg->in_endp[ep_num].diepdma);
+ writel(DIEPT_SIZ_PKT_CNT(pktcnt) | DIEPT_SIZ_XFER_SIZE(length),
+ &reg->in_endp[ep_num].dieptsiz);
+
+ ctrl = readl(&reg->in_endp[ep_num].diepctl);
+
+ /* Write the FIFO number to be used for this endpoint */
+ ctrl &= DIEPCTL_TX_FIFO_NUM_MASK;
+ ctrl |= DIEPCTL_TX_FIFO_NUM(ep->fifo_num);
+
+ /* Clear reserved (Next EP) bits */
+ ctrl = (ctrl&~(EP_MASK<<DEPCTL_NEXT_EP_BIT));
+
+ writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, &reg->in_endp[ep_num].diepctl);
+
+ debug_cond(DEBUG_IN_EP,
+ "%s:EP%d TX DMA start : DIEPDMA0 = 0x%x,"
+ "DIEPTSIZ0 = 0x%x, DIEPCTL0 = 0x%x\n"
+ "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n",
+ __func__, ep_num,
+ readl(&reg->in_endp[ep_num].diepdma),
+ readl(&reg->in_endp[ep_num].dieptsiz),
+ readl(&reg->in_endp[ep_num].diepctl),
+ buf, pktcnt, length);
+
+ return length;
+}
+
+static void complete_rx(struct s3c_udc *dev, u8 ep_num)
+{
+ struct s3c_ep *ep = &dev->ep[ep_num];
+ struct s3c_request *req = NULL;
+ u32 ep_tsr = 0, xfer_size = 0, is_short = 0;
+
+ if (list_empty(&ep->queue)) {
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: RX DMA done : NULL REQ on OUT EP-%d\n",
+ __func__, ep_num);
+ return;
+
+ }
+
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+ ep_tsr = readl(&reg->out_endp[ep_num].doeptsiz);
+
+ if (ep_num == EP0_CON)
+ xfer_size = (ep_tsr & DOEPT_SIZ_XFER_SIZE_MAX_EP0);
+ else
+ xfer_size = (ep_tsr & DOEPT_SIZ_XFER_SIZE_MAX_EP);
+
+ xfer_size = ep->len - xfer_size;
+
+ /*
+ * NOTE:
+ *
+ * Please be careful with proper buffer allocation for USB request,
+ * which needs to be aligned to CONFIG_SYS_CACHELINE_SIZE, not only
+ * with starting address, but also its size shall be a cache line
+ * multiplication.
+ *
+ * This will prevent from corruption of data allocated immediatelly
+ * before or after the buffer.
+ *
+ * For armv7, the cache_v7.c provides proper code to emit "ERROR"
+ * message to warn users.
+ */
+ invalidate_dcache_range((unsigned long) ep->dma_buf,
+ (unsigned long) ep->dma_buf +
+ ROUND(xfer_size, CONFIG_SYS_CACHELINE_SIZE));
+
+ req->req.actual += min(xfer_size, req->req.length - req->req.actual);
+ is_short = (xfer_size < ep->ep.maxpacket);
+
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: RX DMA done : ep = %d, rx bytes = %d/%d, "
+ "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n",
+ __func__, ep_num, req->req.actual, req->req.length,
+ is_short, ep_tsr, xfer_size);
+
+ if (is_short || req->req.actual == req->req.length) {
+ if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) {
+ debug_cond(DEBUG_OUT_EP != 0, " => Send ZLP\n");
+ s3c_udc_ep0_zlp(dev);
+ /* packet will be completed in complete_tx() */
+ dev->ep0state = WAIT_FOR_IN_COMPLETE;
+ } else {
+ done(ep, req, 0);
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct s3c_request, queue);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: Next Rx request start...\n",
+ __func__);
+ setdma_rx(ep, req);
+ }
+ }
+ } else
+ setdma_rx(ep, req);
+}
+
+static void complete_tx(struct s3c_udc *dev, u8 ep_num)
+{
+ struct s3c_ep *ep = &dev->ep[ep_num];
+ struct s3c_request *req;
+ u32 ep_tsr = 0, xfer_size = 0, is_short = 0;
+ u32 last;
+
+ if (dev->ep0state == WAIT_FOR_NULL_COMPLETE) {
+ dev->ep0state = WAIT_FOR_OUT_COMPLETE;
+ s3c_ep0_complete_out();
+ return;
+ }
+
+ if (list_empty(&ep->queue)) {
+ debug_cond(DEBUG_IN_EP,
+ "%s: TX DMA done : NULL REQ on IN EP-%d\n",
+ __func__, ep_num);
+ return;
+
+ }
+
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+ ep_tsr = readl(&reg->in_endp[ep_num].dieptsiz);
+
+ xfer_size = ep->len;
+ is_short = (xfer_size < ep->ep.maxpacket);
+ req->req.actual += min(xfer_size, req->req.length - req->req.actual);
+
+ debug_cond(DEBUG_IN_EP,
+ "%s: TX DMA done : ep = %d, tx bytes = %d/%d, "
+ "is_short = %d, DIEPTSIZ = 0x%x, remained bytes = %d\n",
+ __func__, ep_num, req->req.actual, req->req.length,
+ is_short, ep_tsr, xfer_size);
+
+ if (ep_num == 0) {
+ if (dev->ep0state == DATA_STATE_XMIT) {
+ debug_cond(DEBUG_IN_EP,
+ "%s: ep_num = %d, ep0stat =="
+ "DATA_STATE_XMIT\n",
+ __func__, ep_num);
+ last = write_fifo_ep0(ep, req);
+ if (last)
+ dev->ep0state = WAIT_FOR_COMPLETE;
+ } else if (dev->ep0state == WAIT_FOR_IN_COMPLETE) {
+ debug_cond(DEBUG_IN_EP,
+ "%s: ep_num = %d, completing request\n",
+ __func__, ep_num);
+ done(ep, req, 0);
+ dev->ep0state = WAIT_FOR_SETUP;
+ } else if (dev->ep0state == WAIT_FOR_COMPLETE) {
+ debug_cond(DEBUG_IN_EP,
+ "%s: ep_num = %d, completing request\n",
+ __func__, ep_num);
+ done(ep, req, 0);
+ dev->ep0state = WAIT_FOR_OUT_COMPLETE;
+ s3c_ep0_complete_out();
+ } else {
+ debug_cond(DEBUG_IN_EP,
+ "%s: ep_num = %d, invalid ep state\n",
+ __func__, ep_num);
+ }
+ return;
+ }
+
+ if (req->req.actual == req->req.length)
+ done(ep, req, 0);
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+ debug_cond(DEBUG_IN_EP,
+ "%s: Next Tx request start...\n", __func__);
+ setdma_tx(ep, req);
+ }
+}
+
+static inline void s3c_udc_check_tx_queue(struct s3c_udc *dev, u8 ep_num)
+{
+ struct s3c_ep *ep = &dev->ep[ep_num];
+ struct s3c_request *req;
+
+ debug_cond(DEBUG_IN_EP,
+ "%s: Check queue, ep_num = %d\n", __func__, ep_num);
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+ debug_cond(DEBUG_IN_EP,
+ "%s: Next Tx request(0x%p) start...\n",
+ __func__, req);
+
+ if (ep_is_in(ep))
+ setdma_tx(ep, req);
+ else
+ setdma_rx(ep, req);
+ } else {
+ debug_cond(DEBUG_IN_EP,
+ "%s: NULL REQ on IN EP-%d\n", __func__, ep_num);
+
+ return;
+ }
+
+}
+
+static void process_ep_in_intr(struct s3c_udc *dev)
+{
+ u32 ep_intr, ep_intr_status;
+ u8 ep_num = 0;
+
+ ep_intr = readl(&reg->daint);
+ debug_cond(DEBUG_IN_EP,
+ "*** %s: EP In interrupt : DAINT = 0x%x\n", __func__, ep_intr);
+
+ ep_intr &= DAINT_MASK;
+
+ while (ep_intr) {
+ if (ep_intr & DAINT_IN_EP_INT(1)) {
+ ep_intr_status = readl(&reg->in_endp[ep_num].diepint);
+ debug_cond(DEBUG_IN_EP,
+ "\tEP%d-IN : DIEPINT = 0x%x\n",
+ ep_num, ep_intr_status);
+
+ /* Interrupt Clear */
+ writel(ep_intr_status, &reg->in_endp[ep_num].diepint);
+
+ if (ep_intr_status & TRANSFER_DONE) {
+ complete_tx(dev, ep_num);
+
+ if (ep_num == 0) {
+ if (dev->ep0state ==
+ WAIT_FOR_IN_COMPLETE)
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ if (dev->ep0state == WAIT_FOR_SETUP)
+ s3c_udc_pre_setup();
+
+ /* continue transfer after
+ set_clear_halt for DMA mode */
+ if (clear_feature_flag == 1) {
+ s3c_udc_check_tx_queue(dev,
+ clear_feature_num);
+ clear_feature_flag = 0;
+ }
+ }
+ }
+ }
+ ep_num++;
+ ep_intr >>= 1;
+ }
+}
+
+static void process_ep_out_intr(struct s3c_udc *dev)
+{
+ u32 ep_intr, ep_intr_status;
+ u8 ep_num = 0;
+
+ ep_intr = readl(&reg->daint);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "*** %s: EP OUT interrupt : DAINT = 0x%x\n",
+ __func__, ep_intr);
+
+ ep_intr = (ep_intr >> DAINT_OUT_BIT) & DAINT_MASK;
+
+ while (ep_intr) {
+ if (ep_intr & 0x1) {
+ ep_intr_status = readl(&reg->out_endp[ep_num].doepint);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "\tEP%d-OUT : DOEPINT = 0x%x\n",
+ ep_num, ep_intr_status);
+
+ /* Interrupt Clear */
+ writel(ep_intr_status, &reg->out_endp[ep_num].doepint);
+
+ if (ep_num == 0) {
+ if (ep_intr_status & TRANSFER_DONE) {
+ if (dev->ep0state !=
+ WAIT_FOR_OUT_COMPLETE)
+ complete_rx(dev, ep_num);
+ else {
+ dev->ep0state = WAIT_FOR_SETUP;
+ s3c_udc_pre_setup();
+ }
+ }
+
+ if (ep_intr_status &
+ CTRL_OUT_EP_SETUP_PHASE_DONE) {
+ debug_cond(DEBUG_OUT_EP != 0,
+ "SETUP packet arrived\n");
+ s3c_handle_ep0(dev);
+ }
+ } else {
+ if (ep_intr_status & TRANSFER_DONE)
+ complete_rx(dev, ep_num);
+ }
+ }
+ ep_num++;
+ ep_intr >>= 1;
+ }
+}
+
+/*
+ * usb client interrupt handler.
+ */
+static int s3c_udc_irq(int irq, void *_dev)
+{
+ struct s3c_udc *dev = _dev;
+ u32 intr_status;
+ u32 usb_status, gintmsk;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ intr_status = readl(&reg->gintsts);
+ gintmsk = readl(&reg->gintmsk);
+
+ debug_cond(DEBUG_ISR,
+ "\n*** %s : GINTSTS=0x%x(on state %s), GINTMSK : 0x%x,"
+ "DAINT : 0x%x, DAINTMSK : 0x%x\n",
+ __func__, intr_status, state_names[dev->ep0state], gintmsk,
+ readl(&reg->daint), readl(&reg->daintmsk));
+
+ if (!intr_status) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ if (intr_status & INT_ENUMDONE) {
+ debug_cond(DEBUG_ISR, "\tSpeed Detection interrupt\n");
+
+ writel(INT_ENUMDONE, &reg->gintsts);
+ usb_status = (readl(&reg->dsts) & 0x6);
+
+ if (usb_status & (USB_FULL_30_60MHZ | USB_FULL_48MHZ)) {
+ debug_cond(DEBUG_ISR,
+ "\t\tFull Speed Detection\n");
+ set_max_pktsize(dev, USB_SPEED_FULL);
+
+ } else {
+ debug_cond(DEBUG_ISR,
+ "\t\tHigh Speed Detection : 0x%x\n",
+ usb_status);
+ set_max_pktsize(dev, USB_SPEED_HIGH);
+ }
+ }
+
+ if (intr_status & INT_EARLY_SUSPEND) {
+ debug_cond(DEBUG_ISR, "\tEarly suspend interrupt\n");
+ writel(INT_EARLY_SUSPEND, &reg->gintsts);
+ }
+
+ if (intr_status & INT_SUSPEND) {
+ usb_status = readl(&reg->dsts);
+ debug_cond(DEBUG_ISR,
+ "\tSuspend interrupt :(DSTS):0x%x\n", usb_status);
+ writel(INT_SUSPEND, &reg->gintsts);
+
+ if (dev->gadget.speed != USB_SPEED_UNKNOWN
+ && dev->driver) {
+ if (dev->driver->suspend)
+ dev->driver->suspend(&dev->gadget);
+
+ /* HACK to let gadget detect disconnected state */
+ if (dev->driver->disconnect) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ dev->driver->disconnect(&dev->gadget);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ }
+ }
+
+ if (intr_status & INT_RESUME) {
+ debug_cond(DEBUG_ISR, "\tResume interrupt\n");
+ writel(INT_RESUME, &reg->gintsts);
+
+ if (dev->gadget.speed != USB_SPEED_UNKNOWN
+ && dev->driver
+ && dev->driver->resume) {
+
+ dev->driver->resume(&dev->gadget);
+ }
+ }
+
+ if (intr_status & INT_RESET) {
+ usb_status = readl(&reg->gotgctl);
+ debug_cond(DEBUG_ISR,
+ "\tReset interrupt - (GOTGCTL):0x%x\n", usb_status);
+ writel(INT_RESET, &reg->gintsts);
+
+ if ((usb_status & 0xc0000) == (0x3 << 18)) {
+ if (reset_available) {
+ debug_cond(DEBUG_ISR,
+ "\t\tOTG core got reset (%d)!!\n",
+ reset_available);
+ reconfig_usbd();
+ dev->ep0state = WAIT_FOR_SETUP;
+ reset_available = 0;
+ s3c_udc_pre_setup();
+ } else
+ reset_available = 1;
+
+ } else {
+ reset_available = 1;
+ debug_cond(DEBUG_ISR,
+ "\t\tRESET handling skipped\n");
+ }
+ }
+
+ if (intr_status & INT_IN_EP)
+ process_ep_in_intr(dev);
+
+ if (intr_status & INT_OUT_EP)
+ process_ep_out_intr(dev);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/** Queue one request
+ * Kickstart transfer if needed
+ */
+static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct s3c_request *req;
+ struct s3c_ep *ep;
+ struct s3c_udc *dev;
+ unsigned long flags;
+ u32 ep_num, gintsts;
+
+ req = container_of(_req, struct s3c_request, req);
+ if (unlikely(!_req || !_req->complete || !_req->buf
+ || !list_empty(&req->queue))) {
+
+ debug("%s: bad params\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+
+ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+
+ debug("%s: bad ep: %s, %d, %p\n", __func__,
+ ep->ep.name, !ep->desc, _ep);
+ return -EINVAL;
+ }
+
+ ep_num = ep_index(ep);
+ dev = ep->dev;
+ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+
+ debug("%s: bogus device state %p\n", __func__, dev->driver);
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ /* kickstart this i/o queue? */
+ debug("\n*** %s: %s-%s req = %p, len = %d, buf = %p"
+ "Q empty = %d, stopped = %d\n",
+ __func__, _ep->name, ep_is_in(ep) ? "in" : "out",
+ _req, _req->length, _req->buf,
+ list_empty(&ep->queue), ep->stopped);
+
+#ifdef DEBUG
+ {
+ int i, len = _req->length;
+
+ printf("pkt = ");
+ if (len > 64)
+ len = 64;
+ for (i = 0; i < len; i++) {
+ printf("%02x", ((u8 *)_req->buf)[i]);
+ if ((i & 7) == 7)
+ printf(" ");
+ }
+ printf("\n");
+ }
+#endif
+
+ if (list_empty(&ep->queue) && !ep->stopped) {
+
+ if (ep_num == 0) {
+ /* EP0 */
+ list_add_tail(&req->queue, &ep->queue);
+ s3c_ep0_kick(dev, ep);
+ req = 0;
+
+ } else if (ep_is_in(ep)) {
+ gintsts = readl(&reg->gintsts);
+ debug_cond(DEBUG_IN_EP,
+ "%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n",
+ __func__, gintsts);
+
+ setdma_tx(ep, req);
+ } else {
+ gintsts = readl(&reg->gintsts);
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s:ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n",
+ __func__, gintsts);
+
+ setdma_rx(ep, req);
+ }
+ }
+
+ /* pio or dma irq handler advances the queue. */
+ if (likely(req != 0))
+ list_add_tail(&req->queue, &ep->queue);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+/****************************************************************/
+/* End Point 0 related functions */
+/****************************************************************/
+
+/* return: 0 = still running, 1 = completed, negative = errno */
+static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req)
+{
+ u32 max;
+ unsigned count;
+ int is_last;
+
+ max = ep_maxpacket(ep);
+
+ debug_cond(DEBUG_EP0 != 0, "%s: max = %d\n", __func__, max);
+
+ count = setdma_tx(ep, req);
+
+ /* last packet is usually short (or a zlp) */
+ if (likely(count != max))
+ is_last = 1;
+ else {
+ if (likely(req->req.length != req->req.actual + count)
+ || req->req.zero)
+ is_last = 0;
+ else
+ is_last = 1;
+ }
+
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: wrote %s %d bytes%s %d left %p\n", __func__,
+ ep->ep.name, count,
+ is_last ? "/L" : "",
+ req->req.length - req->req.actual - count, req);
+
+ /* requests complete when all IN data is in the FIFO */
+ if (is_last) {
+ ep->dev->ep0state = WAIT_FOR_SETUP;
+ return 1;
+ }
+
+ return 0;
+}
+
+int s3c_fifo_read(struct s3c_ep *ep, u32 *cp, int max)
+{
+ invalidate_dcache_range((unsigned long)cp, (unsigned long)cp +
+ ROUND(max, CONFIG_SYS_CACHELINE_SIZE));
+
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: bytes=%d, ep_index=%d 0x%p\n", __func__,
+ max, ep_index(ep), cp);
+
+ return max;
+}
+
+/**
+ * udc_set_address - set the USB address for this device
+ * @address:
+ *
+ * Called from control endpoint function
+ * after it decodes a set address setup packet.
+ */
+static void udc_set_address(struct s3c_udc *dev, unsigned char address)
+{
+ u32 ctrl = readl(&reg->dcfg);
+ writel(DEVICE_ADDRESS(address) | ctrl, &reg->dcfg);
+
+ s3c_udc_ep0_zlp(dev);
+
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n",
+ __func__, address, readl(&reg->dcfg));
+
+ dev->usb_address = address;
+}
+
+static inline void s3c_udc_ep0_set_stall(struct s3c_ep *ep)
+{
+ struct s3c_udc *dev;
+ u32 ep_ctrl = 0;
+
+ dev = ep->dev;
+ ep_ctrl = readl(&reg->in_endp[EP0_CON].diepctl);
+
+ /* set the disable and stall bits */
+ if (ep_ctrl & DEPCTL_EPENA)
+ ep_ctrl |= DEPCTL_EPDIS;
+
+ ep_ctrl |= DEPCTL_STALL;
+
+ writel(ep_ctrl, &reg->in_endp[EP0_CON].diepctl);
+
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: set ep%d stall, DIEPCTL0 = 0x%p\n",
+ __func__, ep_index(ep), &reg->in_endp[EP0_CON].diepctl);
+ /*
+ * The application can only set this bit, and the core clears it,
+ * when a SETUP token is received for this endpoint
+ */
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ s3c_udc_pre_setup();
+}
+
+static void s3c_ep0_read(struct s3c_udc *dev)
+{
+ struct s3c_request *req;
+ struct s3c_ep *ep = &dev->ep[0];
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+ } else {
+ debug("%s: ---> BUG\n", __func__);
+ BUG();
+ return;
+ }
+
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n",
+ __func__, req, req->req.length, req->req.actual);
+
+ if (req->req.length == 0) {
+ /* zlp for Set_configuration, Set_interface,
+ * or Bulk-Only mass storge reset */
+
+ ep->len = 0;
+ s3c_udc_ep0_zlp(dev);
+
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: req.length = 0, bRequest = %d\n",
+ __func__, usb_ctrl->bRequest);
+ return;
+ }
+
+ setdma_rx(ep, req);
+}
+
+/*
+ * DATA_STATE_XMIT
+ */
+static int s3c_ep0_write(struct s3c_udc *dev)
+{
+ struct s3c_request *req;
+ struct s3c_ep *ep = &dev->ep[0];
+ int ret, need_zlp = 0;
+
+ if (list_empty(&ep->queue))
+ req = 0;
+ else
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+ if (!req) {
+ debug_cond(DEBUG_EP0 != 0, "%s: NULL REQ\n", __func__);
+ return 0;
+ }
+
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n",
+ __func__, req, req->req.length, req->req.actual);
+
+ if (req->req.length - req->req.actual == ep0_fifo_size) {
+ /* Next write will end with the packet size, */
+ /* so we need Zero-length-packet */
+ need_zlp = 1;
+ }
+
+ ret = write_fifo_ep0(ep, req);
+
+ if ((ret == 1) && !need_zlp) {
+ /* Last packet */
+ dev->ep0state = WAIT_FOR_COMPLETE;
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: finished, waiting for status\n", __func__);
+
+ } else {
+ dev->ep0state = DATA_STATE_XMIT;
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: not finished\n", __func__);
+ }
+
+ return 1;
+}
+
+int s3c_udc_get_status(struct s3c_udc *dev,
+ struct usb_ctrlrequest *crq)
+{
+ u8 ep_num = crq->wIndex & 0x7F;
+ u16 g_status = 0;
+ u32 ep_ctrl;
+
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_GET_STATUS\n", __func__);
+ printf("crq->brequest:0x%x\n", crq->bRequestType & USB_RECIP_MASK);
+ switch (crq->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ g_status = 0;
+ debug_cond(DEBUG_SETUP != 0,
+ "\tGET_STATUS:USB_RECIP_INTERFACE, g_stauts = %d\n",
+ g_status);
+ break;
+
+ case USB_RECIP_DEVICE:
+ g_status = 0x1; /* Self powered */
+ debug_cond(DEBUG_SETUP != 0,
+ "\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n",
+ g_status);
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ if (crq->wLength > 2) {
+ debug_cond(DEBUG_SETUP != 0,
+ "\tGET_STATUS:Not support EP or wLength\n");
+ return 1;
+ }
+
+ g_status = dev->ep[ep_num].stopped;
+ debug_cond(DEBUG_SETUP != 0,
+ "\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n",
+ g_status);
+
+ break;
+
+ default:
+ return 1;
+ }
+
+ memcpy(usb_ctrl, &g_status, sizeof(g_status));
+
+ flush_dcache_range((unsigned long) usb_ctrl,
+ (unsigned long) usb_ctrl +
+ ROUND(sizeof(g_status), CONFIG_SYS_CACHELINE_SIZE));
+
+ writel(usb_ctrl_dma_addr, &reg->in_endp[EP0_CON].diepdma);
+ writel(DIEPT_SIZ_PKT_CNT(1) | DIEPT_SIZ_XFER_SIZE(2),
+ &reg->in_endp[EP0_CON].dieptsiz);
+
+ ep_ctrl = readl(&reg->in_endp[EP0_CON].diepctl);
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK,
+ &reg->in_endp[EP0_CON].diepctl);
+ dev->ep0state = WAIT_FOR_NULL_COMPLETE;
+
+ return 0;
+}
+
+static void s3c_udc_set_nak(struct s3c_ep *ep)
+{
+ u8 ep_num;
+ u32 ep_ctrl = 0;
+
+ ep_num = ep_index(ep);
+ debug("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type);
+
+ if (ep_is_in(ep)) {
+ ep_ctrl = readl(&reg->in_endp[ep_num].diepctl);
+ ep_ctrl |= DEPCTL_SNAK;
+ writel(ep_ctrl, &reg->in_endp[ep_num].diepctl);
+ debug("%s: set NAK, DIEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->in_endp[ep_num].diepctl));
+ } else {
+ ep_ctrl = readl(&reg->out_endp[ep_num].doepctl);
+ ep_ctrl |= DEPCTL_SNAK;
+ writel(ep_ctrl, &reg->out_endp[ep_num].doepctl);
+ debug("%s: set NAK, DOEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->out_endp[ep_num].doepctl));
+ }
+
+ return;
+}
+
+
+void s3c_udc_ep_set_stall(struct s3c_ep *ep)
+{
+ u8 ep_num;
+ u32 ep_ctrl = 0;
+
+ ep_num = ep_index(ep);
+ debug("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type);
+
+ if (ep_is_in(ep)) {
+ ep_ctrl = readl(&reg->in_endp[ep_num].diepctl);
+
+ /* set the disable and stall bits */
+ if (ep_ctrl & DEPCTL_EPENA)
+ ep_ctrl |= DEPCTL_EPDIS;
+
+ ep_ctrl |= DEPCTL_STALL;
+
+ writel(ep_ctrl, &reg->in_endp[ep_num].diepctl);
+ debug("%s: set stall, DIEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->in_endp[ep_num].diepctl));
+
+ } else {
+ ep_ctrl = readl(&reg->out_endp[ep_num].doepctl);
+
+ /* set the stall bit */
+ ep_ctrl |= DEPCTL_STALL;
+
+ writel(ep_ctrl, &reg->out_endp[ep_num].doepctl);
+ debug("%s: set stall, DOEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->out_endp[ep_num].doepctl));
+ }
+
+ return;
+}
+
+void s3c_udc_ep_clear_stall(struct s3c_ep *ep)
+{
+ u8 ep_num;
+ u32 ep_ctrl = 0;
+
+ ep_num = ep_index(ep);
+ debug("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type);
+
+ if (ep_is_in(ep)) {
+ ep_ctrl = readl(&reg->in_endp[ep_num].diepctl);
+
+ /* clear stall bit */
+ ep_ctrl &= ~DEPCTL_STALL;
+
+ /*
+ * USB Spec 9.4.5: For endpoints using data toggle, regardless
+ * of whether an endpoint has the Halt feature set, a
+ * ClearFeature(ENDPOINT_HALT) request always results in the
+ * data toggle being reinitialized to DATA0.
+ */
+ if (ep->bmAttributes == USB_ENDPOINT_XFER_INT
+ || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+ ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */
+ }
+
+ writel(ep_ctrl, &reg->in_endp[ep_num].diepctl);
+ debug("%s: cleared stall, DIEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->in_endp[ep_num].diepctl));
+
+ } else {
+ ep_ctrl = readl(&reg->out_endp[ep_num].doepctl);
+
+ /* clear stall bit */
+ ep_ctrl &= ~DEPCTL_STALL;
+
+ if (ep->bmAttributes == USB_ENDPOINT_XFER_INT
+ || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+ ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */
+ }
+
+ writel(ep_ctrl, &reg->out_endp[ep_num].doepctl);
+ debug("%s: cleared stall, DOEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->out_endp[ep_num].doepctl));
+ }
+
+ return;
+}
+
+static int s3c_udc_set_halt(struct usb_ep *_ep, int value)
+{
+ struct s3c_ep *ep;
+ struct s3c_udc *dev;
+ unsigned long flags;
+ u8 ep_num;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ ep_num = ep_index(ep);
+
+ if (unlikely(!_ep || !ep->desc || ep_num == EP0_CON ||
+ ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC)) {
+ debug("%s: %s bad ep or descriptor\n", __func__, ep->ep.name);
+ return -EINVAL;
+ }
+
+ /* Attempt to halt IN ep will fail if any transfer requests
+ * are still queue */
+ if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+ debug("%s: %s queue not empty, req = %p\n",
+ __func__, ep->ep.name,
+ list_entry(ep->queue.next, struct s3c_request, queue));
+
+ return -EAGAIN;
+ }
+
+ dev = ep->dev;
+ debug("%s: ep_num = %d, value = %d\n", __func__, ep_num, value);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (value == 0) {
+ ep->stopped = 0;
+ s3c_udc_ep_clear_stall(ep);
+ } else {
+ if (ep_num == 0)
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ ep->stopped = 1;
+ s3c_udc_ep_set_stall(ep);
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+void s3c_udc_ep_activate(struct s3c_ep *ep)
+{
+ u8 ep_num;
+ u32 ep_ctrl = 0, daintmsk = 0;
+
+ ep_num = ep_index(ep);
+
+ /* Read DEPCTLn register */
+ if (ep_is_in(ep)) {
+ ep_ctrl = readl(&reg->in_endp[ep_num].diepctl);
+ daintmsk = 1 << ep_num;
+ } else {
+ ep_ctrl = readl(&reg->out_endp[ep_num].doepctl);
+ daintmsk = (1 << ep_num) << DAINT_OUT_BIT;
+ }
+
+ debug("%s: EPCTRL%d = 0x%x, ep_is_in = %d\n",
+ __func__, ep_num, ep_ctrl, ep_is_in(ep));
+
+ /* If the EP is already active don't change the EP Control
+ * register. */
+ if (!(ep_ctrl & DEPCTL_USBACTEP)) {
+ ep_ctrl = (ep_ctrl & ~DEPCTL_TYPE_MASK) |
+ (ep->bmAttributes << DEPCTL_TYPE_BIT);
+ ep_ctrl = (ep_ctrl & ~DEPCTL_MPS_MASK) |
+ (ep->ep.maxpacket << DEPCTL_MPS_BIT);
+ ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP | DEPCTL_SNAK);
+
+ if (ep_is_in(ep)) {
+ writel(ep_ctrl, &reg->in_endp[ep_num].diepctl);
+ debug("%s: USB Ative EP%d, DIEPCTRL%d = 0x%x\n",
+ __func__, ep_num, ep_num,
+ readl(&reg->in_endp[ep_num].diepctl));
+ } else {
+ writel(ep_ctrl, &reg->out_endp[ep_num].doepctl);
+ debug("%s: USB Ative EP%d, DOEPCTRL%d = 0x%x\n",
+ __func__, ep_num, ep_num,
+ readl(&reg->out_endp[ep_num].doepctl));
+ }
+ }
+
+ /* Unmask EP Interrtupt */
+ writel(readl(&reg->daintmsk)|daintmsk, &reg->daintmsk);
+ debug("%s: DAINTMSK = 0x%x\n", __func__, readl(&reg->daintmsk));
+
+}
+
+static int s3c_udc_clear_feature(struct usb_ep *_ep)
+{
+ struct s3c_udc *dev;
+ struct s3c_ep *ep;
+ u8 ep_num;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ ep_num = ep_index(ep);
+
+ dev = ep->dev;
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n",
+ __func__, ep_num, ep_is_in(ep), clear_feature_flag);
+
+ if (usb_ctrl->wLength != 0) {
+ debug_cond(DEBUG_SETUP != 0,
+ "\tCLEAR_FEATURE: wLength is not zero.....\n");
+ return 1;
+ }
+
+ switch (usb_ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ switch (usb_ctrl->wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ debug_cond(DEBUG_SETUP != 0,
+ "\tOFF:USB_DEVICE_REMOTE_WAKEUP\n");
+ break;
+
+ case USB_DEVICE_TEST_MODE:
+ debug_cond(DEBUG_SETUP != 0,
+ "\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n");
+ /** @todo Add CLEAR_FEATURE for TEST modes. */
+ break;
+ }
+
+ s3c_udc_ep0_zlp(dev);
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ debug_cond(DEBUG_SETUP != 0,
+ "\tCLEAR_FEATURE:USB_RECIP_ENDPOINT, wValue = %d\n",
+ usb_ctrl->wValue);
+
+ if (usb_ctrl->wValue == USB_ENDPOINT_HALT) {
+ if (ep_num == 0) {
+ s3c_udc_ep0_set_stall(ep);
+ return 0;
+ }
+
+ s3c_udc_ep0_zlp(dev);
+
+ s3c_udc_ep_clear_stall(ep);
+ s3c_udc_ep_activate(ep);
+ ep->stopped = 0;
+
+ clear_feature_num = ep_num;
+ clear_feature_flag = 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int s3c_udc_set_feature(struct usb_ep *_ep)
+{
+ struct s3c_udc *dev;
+ struct s3c_ep *ep;
+ u8 ep_num;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ ep_num = ep_index(ep);
+ dev = ep->dev;
+
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n",
+ __func__, ep_num);
+
+ if (usb_ctrl->wLength != 0) {
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE: wLength is not zero.....\n");
+ return 1;
+ }
+
+ switch (usb_ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ switch (usb_ctrl->wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n");
+ break;
+ case USB_DEVICE_B_HNP_ENABLE:
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");
+ break;
+
+ case USB_DEVICE_A_HNP_SUPPORT:
+ /* RH port supports HNP */
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE:USB_DEVICE_A_HNP_SUPPORT\n");
+ break;
+
+ case USB_DEVICE_A_ALT_HNP_SUPPORT:
+ /* other RH port does */
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET: USB_DEVICE_A_ALT_HNP_SUPPORT\n");
+ break;
+ }
+
+ s3c_udc_ep0_zlp(dev);
+ return 0;
+
+ case USB_RECIP_INTERFACE:
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE: USB_RECIP_INTERFACE\n");
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ debug_cond(DEBUG_SETUP != 0,
+ "\tSET_FEATURE: USB_RECIP_ENDPOINT\n");
+ if (usb_ctrl->wValue == USB_ENDPOINT_HALT) {
+ if (ep_num == 0) {
+ s3c_udc_ep0_set_stall(ep);
+ return 0;
+ }
+ ep->stopped = 1;
+ s3c_udc_ep_set_stall(ep);
+ }
+
+ s3c_udc_ep0_zlp(dev);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * WAIT_FOR_SETUP (OUT_PKT_RDY)
+ */
+void s3c_ep0_setup(struct s3c_udc *dev)
+{
+ struct s3c_ep *ep = &dev->ep[0];
+ int i;
+ u8 ep_num;
+
+ /* Nuke all previous transfers */
+ nuke(ep, -EPROTO);
+
+ /* read control req from fifo (8 bytes) */
+ s3c_fifo_read(ep, (u32 *)usb_ctrl, 8);
+
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: bRequestType = 0x%x(%s), bRequest = 0x%x"
+ "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n",
+ __func__, usb_ctrl->bRequestType,
+ (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT",
+ usb_ctrl->bRequest,
+ usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex);
+
+#ifdef DEBUG
+ {
+ int i, len = sizeof(*usb_ctrl);
+ char *p = (char *)usb_ctrl;
+
+ printf("pkt = ");
+ for (i = 0; i < len; i++) {
+ printf("%02x", ((u8 *)p)[i]);
+ if ((i & 7) == 7)
+ printf(" ");
+ }
+ printf("\n");
+ }
+#endif
+
+ if (usb_ctrl->bRequest == GET_MAX_LUN_REQUEST &&
+ usb_ctrl->wLength != 1) {
+ debug_cond(DEBUG_SETUP != 0,
+ "\t%s:GET_MAX_LUN_REQUEST:invalid",
+ __func__);
+ debug_cond(DEBUG_SETUP != 0,
+ "wLength = %d, setup returned\n",
+ usb_ctrl->wLength);
+
+ s3c_udc_ep0_set_stall(ep);
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ return;
+ } else if (usb_ctrl->bRequest == BOT_RESET_REQUEST &&
+ usb_ctrl->wLength != 0) {
+ /* Bulk-Only *mass storge reset of class-specific request */
+ debug_cond(DEBUG_SETUP != 0,
+ "%s:BOT Rest:invalid wLength =%d, setup returned\n",
+ __func__, usb_ctrl->wLength);
+
+ s3c_udc_ep0_set_stall(ep);
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ return;
+ }
+
+ /* Set direction of EP0 */
+ if (likely(usb_ctrl->bRequestType & USB_DIR_IN)) {
+ ep->bEndpointAddress |= USB_DIR_IN;
+ } else {
+ ep->bEndpointAddress &= ~USB_DIR_IN;
+ }
+ /* cope with automagic for some standard requests. */
+ dev->req_std = (usb_ctrl->bRequestType & USB_TYPE_MASK)
+ == USB_TYPE_STANDARD;
+
+ dev->req_pending = 1;
+
+ /* Handle some SETUP packets ourselves */
+ if (dev->req_std) {
+ switch (usb_ctrl->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_SET_ADDRESS (%d)\n",
+ __func__, usb_ctrl->wValue);
+ if (usb_ctrl->bRequestType
+ != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+ break;
+
+ udc_set_address(dev, usb_ctrl->wValue);
+ return;
+
+ case USB_REQ_SET_CONFIGURATION:
+ debug_cond(DEBUG_SETUP != 0,
+ "=====================================\n");
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: USB_REQ_SET_CONFIGURATION (%d)\n",
+ __func__, usb_ctrl->wValue);
+
+ if (usb_ctrl->bRequestType == USB_RECIP_DEVICE)
+ reset_available = 1;
+
+ break;
+
+ case USB_REQ_GET_DESCRIPTOR:
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_GET_DESCRIPTOR\n",
+ __func__);
+ break;
+
+ case USB_REQ_SET_INTERFACE:
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_SET_INTERFACE (%d)\n",
+ __func__, usb_ctrl->wValue);
+
+ if (usb_ctrl->bRequestType == USB_RECIP_INTERFACE)
+ reset_available = 1;
+
+ break;
+
+ case USB_REQ_GET_CONFIGURATION:
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** USB_REQ_GET_CONFIGURATION\n",
+ __func__);
+ break;
+
+ case USB_REQ_GET_STATUS:
+ if (!s3c_udc_get_status(dev, usb_ctrl))
+ return;
+
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ ep_num = usb_ctrl->wIndex & 0x7f;
+
+ if (!s3c_udc_clear_feature(&dev->ep[ep_num].ep))
+ return;
+
+ break;
+
+ case USB_REQ_SET_FEATURE:
+ ep_num = usb_ctrl->wIndex & 0x7f;
+
+ if (!s3c_udc_set_feature(&dev->ep[ep_num].ep))
+ return;
+
+ break;
+
+ default:
+ debug_cond(DEBUG_SETUP != 0,
+ "%s: *** Default of usb_ctrl->bRequest=0x%x"
+ "happened.\n", __func__, usb_ctrl->bRequest);
+ break;
+ }
+ }
+
+
+ if (likely(dev->driver)) {
+ /* device-2-host (IN) or no data setup command,
+ * process immediately */
+ debug_cond(DEBUG_SETUP != 0,
+ "%s:usb_ctrlreq will be passed to fsg_setup()\n",
+ __func__);
+
+ spin_unlock(&dev->lock);
+ i = dev->driver->setup(&dev->gadget, usb_ctrl);
+ spin_lock(&dev->lock);
+
+ if (i < 0) {
+ /* setup processing failed, force stall */
+ s3c_udc_ep0_set_stall(ep);
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ debug_cond(DEBUG_SETUP != 0,
+ "\tdev->driver->setup failed (%d),"
+ " bRequest = %d\n",
+ i, usb_ctrl->bRequest);
+
+
+ } else if (dev->req_pending) {
+ dev->req_pending = 0;
+ debug_cond(DEBUG_SETUP != 0,
+ "\tdev->req_pending...\n");
+ }
+
+ debug_cond(DEBUG_SETUP != 0,
+ "\tep0state = %s\n", state_names[dev->ep0state]);
+
+ }
+}
+
+/*
+ * handle ep0 interrupt
+ */
+static void s3c_handle_ep0(struct s3c_udc *dev)
+{
+ if (dev->ep0state == WAIT_FOR_SETUP) {
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: WAIT_FOR_SETUP\n", __func__);
+ s3c_ep0_setup(dev);
+
+ } else {
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: strange state!!(state = %s)\n",
+ __func__, state_names[dev->ep0state]);
+ }
+}
+
+static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep)
+{
+ debug_cond(DEBUG_EP0 != 0,
+ "%s: ep_is_in = %d\n", __func__, ep_is_in(ep));
+ if (ep_is_in(ep)) {
+ dev->ep0state = DATA_STATE_XMIT;
+ s3c_ep0_write(dev);
+
+ } else {
+ dev->ep0state = DATA_STATE_RECV;
+ s3c_ep0_read(dev);
+ }
+}
diff --git a/src/roms/u-boot/drivers/usb/gadget/storage_common.c b/src/roms/u-boot/drivers/usb/gadget/storage_common.c
new file mode 100644
index 0000000..7430074
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/storage_common.c
@@ -0,0 +1,624 @@
+/*
+ * storage_common.c -- Common definitions for mass storage functionality
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyeight (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
+ *
+ * Ported to u-boot:
+ * Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * Code refactoring & cleanup:
+ * Ɓukasz Majewski <l.majewski@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+
+/*
+ * This file requires the following identifiers used in USB strings to
+ * be defined (each of type pointer to char):
+ * - fsg_string_manufacturer -- name of the manufacturer
+ * - fsg_string_product -- name of the product
+ * - fsg_string_serial -- product's serial
+ * - fsg_string_config -- name of the configuration
+ * - fsg_string_interface -- name of the interface
+ * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS
+ * macro is defined prior to including this file.
+ */
+
+/*
+ * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and
+ * fsg_hs_intr_in_desc objects as well as
+ * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES
+ * macros are not defined.
+ *
+ * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER,
+ * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not
+ * defined (as well as corresponding entries in string tables are
+ * missing) and FSG_STRING_INTERFACE has value of zero.
+ *
+ * When FSG_NO_OTG is defined fsg_otg_desc won't be defined.
+ */
+
+/*
+ * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included
+ * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN
+ * characters rather then a pointer to void.
+ */
+
+
+/* #include <asm/unaligned.h> */
+
+
+/*
+ * Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+#define FSG_VENDOR_ID 0x0525 /* NetChip */
+#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef DEBUG
+#undef VERBOSE_DEBUG
+#undef DUMP_MSGS
+#endif /* !DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VLDBG LDBG
+#else
+#define VLDBG(lun, fmt, args...) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+/*
+#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args)
+#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args)
+#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
+#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args)
+*/
+
+#define LDBG(lun, fmt, args...) do { } while (0)
+#define LERROR(lun, fmt, args...) do { } while (0)
+#define LWARN(lun, fmt, args...) do { } while (0)
+#define LINFO(lun, fmt, args...) do { } while (0)
+
+/*
+ * Keep those macros in sync with those in
+ * include/linux/usb/composite.h or else GCC will complain. If they
+ * are identical (the same names of arguments, white spaces in the
+ * same places) GCC will allow redefinition otherwise (even if some
+ * white space is removed or added) warning will be issued.
+ *
+ * Those macros are needed here because File Storage Gadget does not
+ * include the composite.h header. For composite gadgets those macros
+ * are redundant since composite.h is included any way.
+ *
+ * One could check whether those macros are already defined (which
+ * would indicate composite.h had been included) or not (which would
+ * indicate we were in FSG) but this is not done because a warning is
+ * desired if definitions here differ from the ones in composite.h.
+ *
+ * We want the definitions to match and be the same in File Storage
+ * Gadget as well as Mass Storage Function (and so composite gadgets
+ * using MSF). If someone changes them in composite.h it will produce
+ * a warning in this file when building MSF.
+ */
+
+#define DBG(d, fmt, args...) debug(fmt , ## args)
+#define VDBG(d, fmt, args...) debug(fmt , ## args)
+/* #define ERROR(d, fmt, args...) printf(fmt , ## args) */
+/* #define WARNING(d, fmt, args...) printf(fmt , ## args) */
+/* #define INFO(d, fmt, args...) printf(fmt , ## args) */
+
+/* #define DBG(d, fmt, args...) do { } while (0) */
+/* #define VDBG(d, fmt, args...) do { } while (0) */
+#define ERROR(d, fmt, args...) do { } while (0)
+#define WARNING(d, fmt, args...) do { } while (0)
+#define INFO(d, fmt, args...) do { } while (0)
+
+#ifdef DUMP_MSGS
+
+/* dump_msg(fsg, const char * label, const u8 * buf, unsigned length); */
+# define dump_msg(fsg, label, buf, length) do { \
+ if (length < 512) { \
+ DBG(fsg, "%s, length %u:\n", label, length); \
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \
+ 16, 1, buf, length, 0); \
+ } \
+} while (0)
+
+# define dump_cdb(fsg) do { } while (0)
+
+#else
+
+# define dump_msg(fsg, /* const char * */ label, \
+ /* const u8 * */ buf, /* unsigned */ length) do { } while (0)
+
+# ifdef VERBOSE_DEBUG
+
+# define dump_cdb(fsg) \
+ print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \
+ 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \
+
+# else
+
+# define dump_cdb(fsg) do { } while (0)
+
+# endif /* VERBOSE_DEBUG */
+
+#endif /* DUMP_MSGS */
+
+/*-------------------------------------------------------------------------*/
+
+/* SCSI device types */
+#define TYPE_DISK 0x00
+#define TYPE_CDROM 0x05
+
+/* USB protocol value = the transport method */
+#define USB_PR_CBI 0x00 /* Control/Bulk/Interrupt */
+#define USB_PR_CB 0x01 /* Control/Bulk w/o interrupt */
+#define USB_PR_BULK 0x50 /* Bulk-only */
+
+/* USB subclass value = the protocol encapsulation */
+#define USB_SC_RBC 0x01 /* Reduced Block Commands (flash) */
+#define USB_SC_8020 0x02 /* SFF-8020i, MMC-2, ATAPI (CD-ROM) */
+#define USB_SC_QIC 0x03 /* QIC-157 (tape) */
+#define USB_SC_UFI 0x04 /* UFI (floppy) */
+#define USB_SC_8070 0x05 /* SFF-8070i (removable) */
+#define USB_SC_SCSI 0x06 /* Transparent SCSI */
+
+/* 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
+
+/* Bulk-only class specific requests */
+#define USB_BULK_RESET_REQUEST 0xff
+#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe
+
+/* CBI Interrupt data structure */
+struct interrupt_data {
+ u8 bType;
+ u8 bValue;
+};
+
+#define CBI_INTERRUPT_DATA_LEN 2
+
+/* CBI Accept Device-Specific Command request */
+#define USB_CBI_ADSC_REQUEST 0x00
+
+/* Length of a SCSI Command Data Block */
+#define MAX_COMMAND_SIZE 16
+
+/* SCSI commands that we recognize */
+#define SC_FORMAT_UNIT 0x04
+#define SC_INQUIRY 0x12
+#define SC_MODE_SELECT_6 0x15
+#define SC_MODE_SELECT_10 0x55
+#define SC_MODE_SENSE_6 0x1a
+#define SC_MODE_SENSE_10 0x5a
+#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define SC_READ_6 0x08
+#define SC_READ_10 0x28
+#define SC_READ_12 0xa8
+#define SC_READ_CAPACITY 0x25
+#define SC_READ_FORMAT_CAPACITIES 0x23
+#define SC_READ_HEADER 0x44
+#define SC_READ_TOC 0x43
+#define SC_RELEASE 0x17
+#define SC_REQUEST_SENSE 0x03
+#define SC_RESERVE 0x16
+#define SC_SEND_DIAGNOSTIC 0x1d
+#define SC_START_STOP_UNIT 0x1b
+#define SC_SYNCHRONIZE_CACHE 0x35
+#define SC_TEST_UNIT_READY 0x00
+#define SC_VERIFY 0x2f
+#define SC_WRITE_6 0x0a
+#define SC_WRITE_10 0x2a
+#define SC_WRITE_12 0xaa
+
+/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
+#define SS_NO_SENSE 0
+#define SS_COMMUNICATION_FAILURE 0x040800
+#define SS_INVALID_COMMAND 0x052000
+#define SS_INVALID_FIELD_IN_CDB 0x052400
+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
+#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
+#define SS_MEDIUM_NOT_PRESENT 0x023a00
+#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
+#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
+#define SS_RESET_OCCURRED 0x062900
+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
+#define SS_UNRECOVERED_READ_ERROR 0x031100
+#define SS_WRITE_ERROR 0x030c02
+#define SS_WRITE_PROTECTED 0x072700
+
+#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */
+#define ASC(x) ((u8) ((x) >> 8))
+#define ASCQ(x) ((u8) (x))
+
+struct device_attribute { int i; };
+struct rw_semaphore { int i; };
+#define down_write(...) do { } while (0)
+#define up_write(...) do { } while (0)
+#define down_read(...) do { } while (0)
+#define up_read(...) do { } while (0)
+#define ETOOSMALL 525
+
+#include <usb_mass_storage.h>
+
+/*-------------------------------------------------------------------------*/
+
+struct fsg_lun {
+ loff_t file_length;
+ loff_t num_sectors;
+
+ unsigned int initially_ro:1;
+ unsigned int ro:1;
+ unsigned int removable:1;
+ unsigned int cdrom:1;
+ unsigned int prevent_medium_removal:1;
+ unsigned int registered:1;
+ unsigned int info_valid:1;
+ unsigned int nofua:1;
+
+ u32 sense_data;
+ u32 sense_data_info;
+ u32 unit_attention_data;
+
+ struct device dev;
+};
+
+#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL)
+#if 0
+static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
+{
+ return container_of(dev, struct fsg_lun, dev);
+}
+#endif
+
+/* Big enough to hold our biggest descriptor */
+#define EP0_BUFSIZE 256
+#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
+
+/* Number of buffers we will use. 2 is enough for double-buffering */
+#ifndef CONFIG_CI_UDC
+#define FSG_NUM_BUFFERS 2
+#else
+#define FSG_NUM_BUFFERS 1 /* ci_udc only allows 1 req per ep at present */
+#endif
+
+/* Default size of buffer length. */
+#define FSG_BUFLEN ((u32)16384)
+
+/* Maximal number of LUNs supported in mass storage function */
+#define FSG_MAX_LUNS 8
+
+enum fsg_buffer_state {
+ BUF_STATE_EMPTY = 0,
+ BUF_STATE_FULL,
+ BUF_STATE_BUSY
+};
+
+struct fsg_buffhd {
+#ifdef FSG_BUFFHD_STATIC_BUFFER
+ char buf[FSG_BUFLEN];
+#else
+ void *buf;
+#endif
+ enum fsg_buffer_state state;
+ struct fsg_buffhd *next;
+
+ /*
+ * The NetChip 2280 is faster, and handles some protocol faults
+ * better, if we don't submit any short bulk-out read requests.
+ * So we will record the intended request length here.
+ */
+ unsigned int bulk_out_intended_length;
+
+ struct usb_request *inreq;
+ int inreq_busy;
+ struct usb_request *outreq;
+ int outreq_busy;
+};
+
+enum fsg_state {
+ /* This one isn't used anywhere */
+ FSG_STATE_COMMAND_PHASE = -10,
+ FSG_STATE_DATA_PHASE,
+ FSG_STATE_STATUS_PHASE,
+
+ FSG_STATE_IDLE = 0,
+ FSG_STATE_ABORT_BULK_OUT,
+ FSG_STATE_RESET,
+ FSG_STATE_INTERFACE_CHANGE,
+ FSG_STATE_CONFIG_CHANGE,
+ FSG_STATE_DISCONNECT,
+ FSG_STATE_EXIT,
+ FSG_STATE_TERMINATED
+};
+
+enum data_direction {
+ DATA_DIR_UNKNOWN = 0,
+ DATA_DIR_FROM_HOST,
+ DATA_DIR_TO_HOST,
+ DATA_DIR_NONE
+};
+
+/*-------------------------------------------------------------------------*/
+
+static inline u32 get_unaligned_be24(u8 *buf)
+{
+ return 0xffffff & (u32) get_unaligned_be32(buf - 1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+enum {
+#ifndef FSG_NO_DEVICE_STRINGS
+ FSG_STRING_MANUFACTURER = 1,
+ FSG_STRING_PRODUCT,
+ FSG_STRING_SERIAL,
+ FSG_STRING_CONFIG,
+#endif
+ FSG_STRING_INTERFACE
+};
+
+#ifndef FSG_NO_OTG
+static struct usb_otg_descriptor
+fsg_otg_desc = {
+ .bLength = sizeof fsg_otg_desc,
+ .bDescriptorType = USB_DT_OTG,
+
+ .bmAttributes = USB_OTG_SRP,
+};
+#endif
+
+/* There is only one interface. */
+
+static struct usb_interface_descriptor
+fsg_intf_desc = {
+ .bLength = sizeof fsg_intf_desc,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bNumEndpoints = 2, /* Adjusted during fsg_bind() */
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */
+ .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */
+ .iInterface = FSG_STRING_INTERFACE,
+};
+
+/*
+ * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
+ * interrupt-in.
+ */
+
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* wMaxPacketSize set by autoconfiguration */
+};
+
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* wMaxPacketSize set by autoconfiguration */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_fs_intr_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 32, /* frames -> 32 ms */
+};
+
+#ifndef FSG_NO_OTG
+# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2
+#else
+# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 1
+#endif
+
+#endif
+
+static struct usb_descriptor_header *fsg_fs_function[] = {
+#ifndef FSG_NO_OTG
+ (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
+#ifndef FSG_NO_INTR_EP
+ (struct usb_descriptor_header *) &fsg_fs_intr_in_desc,
+#endif
+ NULL,
+};
+
+/*
+ * USB 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ *
+ * That means alternate endpoint descriptors (bigger packets)
+ * and a "device qualifier" ... plus more construction options
+ * for the configuration descriptor.
+ */
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+ .bInterval = 1, /* NAK every 1 uframe */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_hs_intr_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */
+};
+
+#ifndef FSG_NO_OTG
+# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2
+#else
+# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 1
+#endif
+
+#endif
+
+static struct usb_descriptor_header *fsg_hs_function[] = {
+#ifndef FSG_NO_OTG
+ (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
+#ifndef FSG_NO_INTR_EP
+ (struct usb_descriptor_header *) &fsg_hs_intr_in_desc,
+#endif
+ NULL,
+};
+
+/* Maxpacket and other transfer characteristics vary by speed. */
+static struct usb_endpoint_descriptor *
+fsg_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 (for simplicity we use only ASCII characters) */
+static struct usb_string fsg_strings[] = {
+#ifndef FSG_NO_DEVICE_STRINGS
+ {FSG_STRING_MANUFACTURER, fsg_string_manufacturer},
+ {FSG_STRING_PRODUCT, fsg_string_product},
+ {FSG_STRING_SERIAL, fsg_string_serial},
+ {FSG_STRING_CONFIG, fsg_string_config},
+#endif
+ {FSG_STRING_INTERFACE, fsg_string_interface},
+ {}
+};
+
+static struct usb_gadget_strings fsg_stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = fsg_strings,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * If the next two routines are called while the gadget is registered,
+ * the caller must own fsg->filesem for writing.
+ */
+
+static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
+{
+ int ro;
+
+ /* R/W if we can, R/O if we must */
+ ro = curlun->initially_ro;
+
+ curlun->ro = ro;
+ curlun->file_length = ums->num_sectors << 9;
+ curlun->num_sectors = ums->num_sectors;
+ debug("open backing file: %s\n", filename);
+
+ return 0;
+}
+
+static void fsg_lun_close(struct fsg_lun *curlun)
+{
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Sync the file data, don't bother with the metadata.
+ * This code was copied from fs/buffer.c:sys_fdatasync().
+ */
+static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
+{
+ return 0;
+}
+
+static void store_cdrom_address(u8 *dest, int msf, u32 addr)
+{
+ if (msf) {
+ /* Convert to Minutes-Seconds-Frames */
+ addr >>= 2; /* Convert to 2048-byte frames */
+ addr += 2*75; /* Lead-in occupies 2 seconds */
+ dest[3] = addr % 75; /* Frames */
+ addr /= 75;
+ dest[2] = addr % 60; /* Seconds */
+ addr /= 60;
+ dest[1] = addr; /* Minutes */
+ dest[0] = 0; /* Reserved */
+ } else {
+ /* Absolute sector */
+ put_unaligned_be32(addr, dest);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
diff --git a/src/roms/u-boot/drivers/usb/gadget/usbstring.c b/src/roms/u-boot/drivers/usb/gadget/usbstring.c
new file mode 100644
index 0000000..8c3ff64
--- /dev/null
+++ b/src/roms/u-boot/drivers/usb/gadget/usbstring.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2003 David Brownell
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ * Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and
+ * Remy Bohmer <linux@bohmer.net>
+ */
+
+#include <common.h>
+#include <asm/errno.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <asm/unaligned.h>
+
+
+static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
+{
+ int count = 0;
+ u8 c;
+ u16 uchar;
+
+ /*
+ * this insists on correct encodings, though not minimal ones.
+ * BUT it currently rejects legit 4-byte UTF-8 code points,
+ * which need surrogate pairs. (Unicode 3.1 can use them.)
+ */
+ while (len != 0 && (c = (u8) *s++) != 0) {
+ if ((c & 0x80)) {
+ /*
+ * 2-byte sequence:
+ * 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
+ */
+ if ((c & 0xe0) == 0xc0) {
+ uchar = (c & 0x1f) << 6;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0x80)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c;
+
+ /*
+ * 3-byte sequence (most CJKV characters):
+ * zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
+ */
+ } else if ((c & 0xf0) == 0xe0) {
+ uchar = (c & 0x0f) << 12;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0x80)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c << 6;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0x80)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c;
+
+ /* no bogus surrogates */
+ if (0xd800 <= uchar && uchar <= 0xdfff)
+ goto fail;
+
+ /*
+ * 4-byte sequence (surrogate pairs, currently rare):
+ * 11101110wwwwzzzzyy + 110111yyyyxxxxxx
+ * = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
+ * (uuuuu = wwww + 1)
+ * FIXME accept the surrogate code points (only)
+ */
+ } else
+ goto fail;
+ } else
+ uchar = c;
+ put_unaligned_le16(uchar, cp++);
+ count++;
+ len--;
+ }
+ return count;
+fail:
+ return -1;
+}
+
+
+/**
+ * usb_gadget_get_string - fill out a string descriptor
+ * @table: of c strings encoded using UTF-8
+ * @id: string id, from low byte of wValue in get string descriptor
+ * @buf: at least 256 bytes
+ *
+ * Finds the UTF-8 string matching the ID, and converts it into a
+ * string descriptor in utf16-le.
+ * Returns length of descriptor (always even) or negative errno
+ *
+ * If your driver needs stings in multiple languages, you'll probably
+ * "switch (wIndex) { ... }" in your ep0 string descriptor logic,
+ * using this routine after choosing which set of UTF-8 strings to use.
+ * Note that US-ASCII is a strict subset of UTF-8; any string bytes with
+ * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
+ * characters (which are also widely used in C strings).
+ */
+int
+usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf)
+{
+ struct usb_string *s;
+ int len;
+
+ if (!table)
+ return -EINVAL;
+
+ /* descriptor 0 has the language id */
+ if (id == 0) {
+ buf[0] = 4;
+ buf[1] = USB_DT_STRING;
+ buf[2] = (u8) table->language;
+ buf[3] = (u8) (table->language >> 8);
+ return 4;
+ }
+ for (s = table->strings; s && s->s; s++)
+ if (s->id == id)
+ break;
+
+ /* unrecognized: stall. */
+ if (!s || !s->s)
+ return -EINVAL;
+
+ /* string descriptors have length, tag, then UTF16-LE text */
+ len = min((size_t) 126, strlen(s->s));
+ memset(buf + 2, 0, 2 * len); /* zero all the bytes */
+ len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len);
+ if (len < 0)
+ return -EINVAL;
+ buf[0] = (len + 1) * 2;
+ buf[1] = USB_DT_STRING;
+ return buf[0];
+}
OpenPOWER on IntegriCloud