aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/drivers/usb/gadget
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/drivers/usb/gadget
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/drivers/usb/gadget')
-rw-r--r--roms/u-boot/drivers/usb/gadget/Kconfig243
-rw-r--r--roms/u-boot/drivers/usb/gadget/Makefile48
-rw-r--r--roms/u-boot/drivers/usb/gadget/at91_udc.c1546
-rw-r--r--roms/u-boot/drivers/usb/gadget/at91_udc.h167
-rw-r--r--roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c1304
-rw-r--r--roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h320
-rw-r--r--roms/u-boot/drivers/usb/gadget/bcm_udc_otg.h21
-rw-r--r--roms/u-boot/drivers/usb/gadget/bcm_udc_otg_phy.c55
-rw-r--r--roms/u-boot/drivers/usb/gadget/ci_udc.c1072
-rw-r--r--roms/u-boot/drivers/usb/gadget/ci_udc.h153
-rw-r--r--roms/u-boot/drivers/usb/gadget/composite.c1531
-rw-r--r--roms/u-boot/drivers/usb/gadget/config.c107
-rw-r--r--roms/u-boot/drivers/usb/gadget/core.c633
-rw-r--r--roms/u-boot/drivers/usb/gadget/designware_udc.c1021
-rw-r--r--roms/u-boot/drivers/usb/gadget/dwc2_udc_otg.c1199
-rw-r--r--roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_phy.c101
-rw-r--r--roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_priv.h95
-rw-r--r--roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_regs.h296
-rw-r--r--roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c1508
-rw-r--r--roms/u-boot/drivers/usb/gadget/ep0.c643
-rw-r--r--roms/u-boot/drivers/usb/gadget/ep0.h25
-rw-r--r--roms/u-boot/drivers/usb/gadget/epautoconf.c324
-rw-r--r--roms/u-boot/drivers/usb/gadget/ether.c2706
-rw-r--r--roms/u-boot/drivers/usb/gadget/f_dfu.c844
-rw-r--r--roms/u-boot/drivers/usb/gadget/f_dfu.h85
-rw-r--r--roms/u-boot/drivers/usb/gadget/f_fastboot.c560
-rw-r--r--roms/u-boot/drivers/usb/gadget/f_mass_storage.c2777
-rw-r--r--roms/u-boot/drivers/usb/gadget/f_rockusb.c934
-rw-r--r--roms/u-boot/drivers/usb/gadget/f_sdp.c956
-rw-r--r--roms/u-boot/drivers/usb/gadget/f_thor.c1034
-rw-r--r--roms/u-boot/drivers/usb/gadget/f_thor.h126
-rw-r--r--roms/u-boot/drivers/usb/gadget/fotg210.c964
-rw-r--r--roms/u-boot/drivers/usb/gadget/g_dnl.c318
-rw-r--r--roms/u-boot/drivers/usb/gadget/gadget_chips.h236
-rw-r--r--roms/u-boot/drivers/usb/gadget/max3420_udc.c875
-rw-r--r--roms/u-boot/drivers/usb/gadget/ndis.h217
-rw-r--r--roms/u-boot/drivers/usb/gadget/pxa25x_udc.c2049
-rw-r--r--roms/u-boot/drivers/usb/gadget/pxa25x_udc.h149
-rw-r--r--roms/u-boot/drivers/usb/gadget/pxa27x_udc.c703
-rw-r--r--roms/u-boot/drivers/usb/gadget/rndis.c1314
-rw-r--r--roms/u-boot/drivers/usb/gadget/rndis.h268
-rw-r--r--roms/u-boot/drivers/usb/gadget/storage_common.c617
-rw-r--r--roms/u-boot/drivers/usb/gadget/u_os_desc.h123
-rw-r--r--roms/u-boot/drivers/usb/gadget/udc/Makefile10
-rw-r--r--roms/u-boot/drivers/usb/gadget/udc/udc-core.c379
-rw-r--r--roms/u-boot/drivers/usb/gadget/udc/udc-uclass.c64
-rw-r--r--roms/u-boot/drivers/usb/gadget/usbstring.c66
47 files changed, 30786 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/usb/gadget/Kconfig b/roms/u-boot/drivers/usb/gadget/Kconfig
new file mode 100644
index 000000000..4a3b22e6d
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/Kconfig
@@ -0,0 +1,243 @@
+#
+# USB Gadget support on a system involves
+# (a) a peripheral controller, and
+# (b) the gadget driver using it.
+#
+# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
+#
+# - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
+# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
+# - Some systems have both kinds of controllers.
+#
+# With help from a special transceiver and a "Mini-AB" jack, systems with
+# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
+#
+
+menuconfig USB_GADGET
+ bool "USB Gadget Support"
+ help
+ USB is a master/slave protocol, organized with one master
+ host (such as a PC) controlling up to 127 peripheral devices.
+ The USB hardware is asymmetric, which makes it easier to set up:
+ you can't connect a "to-the-host" connector to a peripheral.
+
+ U-Boot can run in the host, or in the peripheral. In both cases
+ you need a low level bus controller driver, and some software
+ talking to it. Peripheral controllers are often discrete silicon,
+ or are integrated with the CPU in a microcontroller. The more
+ familiar host side controllers have names like "EHCI", "OHCI",
+ or "UHCI", and are usually integrated into southbridges on PC
+ motherboards.
+
+ Enable this configuration option if you want to run U-Boot inside
+ a USB peripheral device. Configure one hardware driver for your
+ peripheral/device side bus controller, and a "gadget driver" for
+ your peripheral protocol.
+
+if USB_GADGET
+
+config USB_GADGET_MANUFACTURER
+ string "Vendor name of the USB device"
+ default "Allwinner Technology" if ARCH_SUNXI
+ default "Rockchip" if ARCH_ROCKCHIP
+ default "U-Boot"
+ help
+ Vendor name of the USB device emulated, reported to the host device.
+ This is usually either the manufacturer of the device or the SoC.
+
+config USB_GADGET_VENDOR_NUM
+ hex "Vendor ID of the USB device"
+ default 0x1f3a if ARCH_SUNXI
+ default 0x2207 if ARCH_ROCKCHIP
+ default 0x0
+ help
+ Vendor ID of the USB device emulated, reported to the host device.
+ This is usually the board or SoC vendor's, unless you've registered
+ for one.
+
+config USB_GADGET_PRODUCT_NUM
+ hex "Product ID of the USB device"
+ default 0x1010 if ARCH_SUNXI
+ default 0x310a if ROCKCHIP_RK3036
+ default 0x310c if ROCKCHIP_RK3128
+ default 0x320a if ROCKCHIP_RK3229 || ROCKCHIP_RK3288
+ default 0x330a if ROCKCHIP_RK3328
+ default 0x330c if ROCKCHIP_RK3399
+ default 0x0
+ help
+ Product ID of the USB device emulated, reported to the host device.
+
+config USB_GADGET_ATMEL_USBA
+ bool "Atmel USBA"
+ select USB_GADGET_DUALSPEED
+ help
+ USBA is the integrated high-speed USB Device controller on
+ the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
+
+config USB_GADGET_BCM_UDC_OTG_PHY
+ bool "Broadcom UDC OTG PHY"
+ help
+ Enable the Broadcom UDC OTG physical device interface.
+
+config USB_GADGET_DWC2_OTG
+ bool "DesignWare USB2.0 HS OTG controller (gadget mode)"
+ select USB_GADGET_DUALSPEED
+ help
+ The Designware USB2.0 high-speed gadget controller
+ integrated into many SoCs. Select this option if you want the
+ driver to operate in Peripheral mode. This option requires
+ USB_GADGET to be enabled.
+
+if USB_GADGET_DWC2_OTG
+
+config USB_GADGET_DWC2_OTG_PHY_BUS_WIDTH_8
+ bool "DesignWare USB2.0 HS OTG controller 8-bit PHY bus width"
+ help
+ Set the Designware USB2.0 high-speed OTG controller
+ PHY interface width to 8 bits, rather than the default (16 bits).
+
+endif # USB_GADGET_DWC2_OTG
+
+config USB_GADGET_OS_DESCRIPTORS
+ bool "USB OS Feature Descriptors support"
+ help
+ This is a porting patch from linux kernel: 37a3a533429e
+ ("usb: gadget: OS Feature Descriptors support"), the original commit
+ log see below:
+ There is a custom (non-USB IF) extension to the USB standard:
+ http://msdn.microsoft.com/library/windows/hardware/gg463182
+
+config CI_UDC
+ bool "ChipIdea device controller"
+ select USB_GADGET_DUALSPEED
+ help
+ Say Y here to enable device controller functionality of the
+ ChipIdea driver.
+
+config USB_GADGET_MAX3420
+ bool "MAX3420 USB Over SPI"
+ depends on DM_SPI
+ help
+ MAX3420, from MAXIM, implements USB-over-SPI Full-Speed device controller.
+
+config USB_GADGET_VBUS_DRAW
+ int "Maximum VBUS Power usage (2-500 mA)"
+ range 2 500
+ default 2
+ help
+ Some devices need to draw power from USB when they are
+ configured, perhaps to operate circuitry or to recharge
+ batteries. This is in addition to any local power supply,
+ such as an AC adapter or batteries.
+
+ Enter the maximum power your device draws through USB, in
+ milliAmperes. The permitted range of values is 2 - 500 mA;
+ 0 mA would be legal, but can make some hosts misbehave.
+
+ This value will be used except for system-specific gadget
+ drivers that have more specific information.
+
+config SDP_LOADADDR
+ hex "Default load address at SDP_WRITE and SDP_JUMP"
+ default 0
+
+# Selected by UDC drivers that support high-speed operation.
+config USB_GADGET_DUALSPEED
+ bool
+
+config USB_GADGET_DOWNLOAD
+ bool "Enable USB download gadget"
+ help
+ Composite USB download gadget support (g_dnl) for download functions.
+ This code works on top of composite gadget.
+
+if USB_GADGET_DOWNLOAD
+
+config USB_FUNCTION_MASS_STORAGE
+ bool "Enable USB mass storage gadget"
+ help
+ Enable mass storage protocol support in U-Boot. It allows exporting
+ the eMMC/SD card content to HOST PC so it can be mounted.
+
+config USB_FUNCTION_ROCKUSB
+ bool "Enable USB rockusb gadget"
+ help
+ Rockusb protocol is widely used by Rockchip SoC based devices. It can
+ read/write info, image to/from devices. This enables the USB part of
+ the rockusb gadget.for more detail about Rockusb protocol, please see
+ doc/README.rockusb
+
+config USB_FUNCTION_SDP
+ bool "Enable USB SDP (Serial Download Protocol)"
+ help
+ Enable Serial Download Protocol (SDP) device support in U-Boot. This
+ allows to download images into memory and execute (jump to) them
+ using the same protocol as implemented by the i.MX family's boot ROM.
+
+config USB_FUNCTION_THOR
+ bool "Enable USB THOR gadget"
+ help
+ Enable Tizen's THOR download protocol support in U-Boot. It
+ allows downloading images into memory and flash them to target device.
+
+endif # USB_GADGET_DOWNLOAD
+
+config USB_ETHER
+ bool "USB Ethernet Gadget"
+ depends on NET
+ default y if ARCH_SUNXI && USB_MUSB_GADGET
+ help
+ Creates an Ethernet network device through a USB peripheral
+ controller. This will create a network interface on both the device
+ (U-Boot) and the host (remote device) that can be used just like any
+ other nework interface.
+ It will bind on the peripheral USB controller, ignoring the USB hosts
+ controllers in the system.
+
+if USB_ETHER
+
+choice
+ prompt "USB Ethernet Gadget Model"
+ default USB_ETH_RNDIS
+ help
+ There is several models (protocols) to implement Ethernet over USB
+ devices. The main ones are Microsoft's RNDIS and USB's CDC-Ethernet
+ (also called CDC-ECM). RNDIS is obviously compatible with Windows,
+ while CDC-ECM is not. Most other operating systems support both, so
+ if inter-operability is a concern, RNDIS is to be preferred.
+
+config USB_ETH_CDC
+ bool "CDC-ECM Protocol"
+ help
+ CDC (Communications Device Class) is the standard for Ethernet over
+ USB devices. While there's several alternatives, the most widely used
+ protocol is ECM (Ethernet Control Model). However, compatibility with
+ Windows is not that great.
+
+config USB_ETH_RNDIS
+ bool "RNDIS Protocol"
+ help
+ The RNDIS (Remote Network Driver Interface Specification) is a
+ Microsoft proprietary protocol to create an Ethernet device over USB.
+ Windows obviously supports it, as well as all the major operating
+ systems, so it's the best option for compatibility.
+
+endchoice
+
+config USBNET_DEVADDR
+ string "USB Gadget Ethernet device mac address"
+ default "de:ad:be:ef:00:01"
+ help
+ Ethernet MAC address of the device-side (ie. local board's) MAC
+ address of the usb_ether interface
+
+config USBNET_HOST_ADDR
+ string "USB Gadget Ethernet host mac address"
+ default "de:ad:be:ef:00:00"
+ help
+ Ethernet MAC address of the host-side (ie. remote device's) MAC
+ address of the usb_ether interface
+
+endif # USB_ETHER
+
+endif # USB_GADGET
diff --git a/roms/u-boot/drivers/usb/gadget/Makefile b/roms/u-boot/drivers/usb/gadget/Makefile
new file mode 100644
index 000000000..f560068b4
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/Makefile
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2000-2007
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+
+obj-$(CONFIG_USB_GADGET) += epautoconf.o config.o usbstring.o
+obj-$(CONFIG_USB_ETHER) += epautoconf.o config.o usbstring.o
+
+ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_SPL_USB_GADGET) += g_dnl.o
+obj-$(CONFIG_SPL_DFU) += f_dfu.o
+obj-$(CONFIG_SPL_USB_SDP_SUPPORT) += f_sdp.o
+endif
+
+# new USB gadget layer dependencies
+ifdef CONFIG_USB_GADGET
+obj-$(CONFIG_USB_GADGET_AT91) += at91_udc.o
+obj-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o
+obj-$(CONFIG_USB_GADGET_BCM_UDC_OTG_PHY) += bcm_udc_otg_phy.o
+obj-$(CONFIG_USB_GADGET_DWC2_OTG) += dwc2_udc_otg.o
+obj-$(CONFIG_USB_GADGET_DWC2_OTG_PHY) += dwc2_udc_otg_phy.o
+obj-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o
+obj-$(CONFIG_USB_GADGET_MAX3420) += max3420_udc.o
+obj-$(CONFIG_CI_UDC) += ci_udc.o
+ifndef CONFIG_SPL_BUILD
+obj-$(CONFIG_USB_GADGET_DOWNLOAD) += g_dnl.o
+obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o
+obj-$(CONFIG_DFU_OVER_USB) += f_dfu.o
+obj-$(CONFIG_USB_FUNCTION_MASS_STORAGE) += f_mass_storage.o
+obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o
+obj-$(CONFIG_USB_FUNCTION_SDP) += f_sdp.o
+obj-$(CONFIG_USB_FUNCTION_ROCKUSB) += f_rockusb.o
+endif
+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_CPU_PXA27X) += pxa27x_udc.o
+endif
+endif
diff --git a/roms/u-boot/drivers/usb/gadget/at91_udc.c b/roms/u-boot/drivers/usb/gadget/at91_udc.c
new file mode 100644
index 000000000..1feed417d
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/at91_udc.c
@@ -0,0 +1,1546 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * from linux:
+ * c94e289f195e: usb: gadget: remove incorrect __init/__exit annotations
+ *
+ * at91_udc -- driver for at91-series USB peripheral controller
+ *
+ * Copyright (C) 2004 by Thomas Rathbone
+ * Copyright (C) 2005 by HP Labs
+ * Copyright (C) 2005 by David Brownell
+ */
+
+#undef VERBOSE_DEBUG
+#undef PACKET_TRACE
+
+#include <common.h>
+#include <dm/devres.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <asm/hardware.h>
+#include <mach/at91_matrix.h>
+#include <linux/list.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/at91_udc.h>
+#include <malloc.h>
+
+#include "at91_udc.h"
+
+/*
+ * This controller is simple and PIO-only. It's used in many AT91-series
+ * full speed USB controllers, including the at91rm9200 (arm920T, with MMU),
+ * at91sam926x (arm926ejs, with MMU), and several no-mmu versions.
+ *
+ * This driver expects the board has been wired with two GPIOs supporting
+ * a VBUS sensing IRQ, and a D+ pullup. (They may be omitted, but the
+ * testing hasn't covered such cases.)
+ *
+ * The pullup is most important (so it's integrated on sam926x parts). It
+ * provides software control over whether the host enumerates the device.
+ *
+ * The VBUS sensing helps during enumeration, and allows both USB clocks
+ * (and the transceiver) to stay gated off until they're necessary, saving
+ * power. During USB suspend, the 48 MHz clock is gated off in hardware;
+ * it may also be gated off by software during some Linux sleep states.
+ */
+
+#define DRIVER_VERSION "3 May 2006"
+
+static const char driver_name [] = "at91_udc";
+static const char * const ep_names[] = {
+ "ep0",
+ "ep1",
+ "ep2",
+ "ep3-int",
+ "ep4",
+ "ep5",
+};
+#define ep0name ep_names[0]
+
+#define at91_udp_read(udc, reg) \
+ __raw_readl((udc)->udp_baseaddr + (reg))
+#define at91_udp_write(udc, reg, val) \
+ __raw_writel((val), (udc)->udp_baseaddr + (reg))
+
+static struct at91_udc *controller;
+
+/*-------------------------------------------------------------------------*/
+
+static void done(struct at91_ep *ep, struct at91_request *req, int status)
+{
+ unsigned stopped = ep->stopped;
+ struct at91_udc *udc = ep->udc;
+
+ list_del_init(&req->queue);
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+ if (status && status != -ESHUTDOWN)
+ VDBG("%s done %p, status %d\n", ep->ep.name, req, status);
+
+ ep->stopped = 1;
+ spin_unlock(&udc->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&udc->lock);
+ ep->stopped = stopped;
+
+ /* ep0 is always ready; other endpoints need a non-empty queue */
+ if (list_empty(&ep->queue) && ep->int_mask != (1 << 0))
+ at91_udp_write(udc, AT91_UDP_IDR, ep->int_mask);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* bits indicating OUT fifo has data ready */
+#define RX_DATA_READY (AT91_UDP_RX_DATA_BK0 | AT91_UDP_RX_DATA_BK1)
+
+/*
+ * Endpoint FIFO CSR bits have a mix of bits, making it unsafe to just write
+ * back most of the value you just read (because of side effects, including
+ * bits that may change after reading and before writing).
+ *
+ * Except when changing a specific bit, always write values which:
+ * - clear SET_FX bits (setting them could change something)
+ * - set CLR_FX bits (clearing them could change something)
+ *
+ * There are also state bits like FORCESTALL, EPEDS, DIR, and EPTYPE
+ * that shouldn't normally be changed.
+ *
+ * NOTE at91sam9260 docs mention synch between UDPCK and MCK clock domains,
+ * implying a need to wait for one write to complete (test relevant bits)
+ * before starting the next write. This shouldn't be an issue given how
+ * infrequently we write, except maybe for write-then-read idioms.
+ */
+#define SET_FX (AT91_UDP_TXPKTRDY)
+#define CLR_FX (RX_DATA_READY | AT91_UDP_RXSETUP \
+ | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)
+
+/* pull OUT packet data from the endpoint's fifo */
+static int read_fifo (struct at91_ep *ep, struct at91_request *req)
+{
+ u32 __iomem *creg = ep->creg;
+ u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0));
+ u32 csr;
+ u8 *buf;
+ unsigned int count, bufferspace, is_done;
+
+ buf = req->req.buf + req->req.actual;
+ bufferspace = req->req.length - req->req.actual;
+
+ /*
+ * there might be nothing to read if ep_queue() calls us,
+ * or if we already emptied both pingpong buffers
+ */
+rescan:
+ csr = __raw_readl(creg);
+ if ((csr & RX_DATA_READY) == 0)
+ return 0;
+
+ count = (csr & AT91_UDP_RXBYTECNT) >> 16;
+ if (count > ep->ep.maxpacket)
+ count = ep->ep.maxpacket;
+ if (count > bufferspace) {
+ DBG("%s buffer overflow\n", ep->ep.name);
+ req->req.status = -EOVERFLOW;
+ count = bufferspace;
+ }
+ __raw_readsb((unsigned long)dreg, buf, count);
+
+ /* release and swap pingpong mem bank */
+ csr |= CLR_FX;
+ if (ep->is_pingpong) {
+ if (ep->fifo_bank == 0) {
+ csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0);
+ ep->fifo_bank = 1;
+ } else {
+ csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK1);
+ ep->fifo_bank = 0;
+ }
+ } else
+ csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0);
+ __raw_writel(csr, creg);
+
+ req->req.actual += count;
+ is_done = (count < ep->ep.maxpacket);
+ if (count == bufferspace)
+ is_done = 1;
+
+ PACKET("%s %p out/%d%s\n", ep->ep.name, &req->req, count,
+ is_done ? " (done)" : "");
+
+ /*
+ * avoid extra trips through IRQ logic for packets already in
+ * the fifo ... maybe preventing an extra (expensive) OUT-NAK
+ */
+ if (is_done)
+ done(ep, req, 0);
+ else if (ep->is_pingpong) {
+ /*
+ * One dummy read to delay the code because of a HW glitch:
+ * CSR returns bad RXCOUNT when read too soon after updating
+ * RX_DATA_BK flags.
+ */
+ csr = __raw_readl(creg);
+
+ bufferspace -= count;
+ buf += count;
+ goto rescan;
+ }
+
+ return is_done;
+}
+
+/* load fifo for an IN packet */
+static int write_fifo(struct at91_ep *ep, struct at91_request *req)
+{
+ u32 __iomem *creg = ep->creg;
+ u32 csr = __raw_readl(creg);
+ u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0));
+ unsigned total, count, is_last;
+ u8 *buf;
+
+ /*
+ * TODO: allow for writing two packets to the fifo ... that'll
+ * reduce the amount of IN-NAKing, but probably won't affect
+ * throughput much. (Unlike preventing OUT-NAKing!)
+ */
+
+ /*
+ * If ep_queue() calls us, the queue is empty and possibly in
+ * odd states like TXCOMP not yet cleared (we do it, saving at
+ * least one IRQ) or the fifo not yet being free. Those aren't
+ * issues normally (IRQ handler fast path).
+ */
+ if (unlikely(csr & (AT91_UDP_TXCOMP | AT91_UDP_TXPKTRDY))) {
+ if (csr & AT91_UDP_TXCOMP) {
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_TXCOMP);
+ __raw_writel(csr, creg);
+ csr = __raw_readl(creg);
+ }
+ if (csr & AT91_UDP_TXPKTRDY)
+ return 0;
+ }
+
+ buf = req->req.buf + req->req.actual;
+ prefetch(buf);
+ total = req->req.length - req->req.actual;
+ if (ep->ep.maxpacket < total) {
+ count = ep->ep.maxpacket;
+ is_last = 0;
+ } else {
+ count = total;
+ is_last = (count < ep->ep.maxpacket) || !req->req.zero;
+ }
+
+ /*
+ * Write the packet, maybe it's a ZLP.
+ *
+ * NOTE: incrementing req->actual before we receive the ACK means
+ * gadget driver IN bytecounts can be wrong in fault cases. That's
+ * fixable with PIO drivers like this one (save "count" here, and
+ * do the increment later on TX irq), but not for most DMA hardware.
+ *
+ * So all gadget drivers must accept that potential error. Some
+ * hardware supports precise fifo status reporting, letting them
+ * recover when the actual bytecount matters (e.g. for USB Test
+ * and Measurement Class devices).
+ */
+ __raw_writesb((unsigned long)dreg, buf, count);
+ csr &= ~SET_FX;
+ csr |= CLR_FX | AT91_UDP_TXPKTRDY;
+ __raw_writel(csr, creg);
+ req->req.actual += count;
+
+ PACKET("%s %p in/%d%s\n", ep->ep.name, &req->req, count,
+ is_last ? " (done)" : "");
+ if (is_last)
+ done(ep, req, 0);
+ return is_last;
+}
+
+static void nuke(struct at91_ep *ep, int status)
+{
+ struct at91_request *req;
+
+ /* terminate any request in the queue */
+ ep->stopped = 1;
+ if (list_empty(&ep->queue))
+ return;
+
+ VDBG("%s %s\n", __func__, ep->ep.name);
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct at91_request, queue);
+ done(ep, req, status);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int at91_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct at91_ep *ep = container_of(_ep, struct at91_ep, ep);
+ struct at91_udc *udc;
+ u16 maxpacket;
+ u32 tmp;
+ unsigned long flags;
+
+ if (!_ep || !ep
+ || !desc || _ep->name == ep0name
+ || desc->bDescriptorType != USB_DT_ENDPOINT
+ || (maxpacket = usb_endpoint_maxp(desc)) == 0
+ || maxpacket > ep->maxpacket) {
+ DBG("bad ep or descriptor\n");
+ return -EINVAL;
+ }
+
+ udc = ep->udc;
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+ DBG("bogus device state\n");
+ return -ESHUTDOWN;
+ }
+
+ tmp = usb_endpoint_type(desc);
+ switch (tmp) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ DBG("only one control endpoint\n");
+ return -EINVAL;
+ case USB_ENDPOINT_XFER_INT:
+ if (maxpacket > 64)
+ goto bogus_max;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ switch (maxpacket) {
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ goto ok;
+ }
+bogus_max:
+ DBG("bogus maxpacket %d\n", maxpacket);
+ return -EINVAL;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (!ep->is_pingpong) {
+ DBG("iso requires double buffering\n");
+ return -EINVAL;
+ }
+ break;
+ }
+
+ok:
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* initialize endpoint to match this descriptor */
+ ep->is_in = usb_endpoint_dir_in(desc);
+ ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
+ ep->stopped = 0;
+ if (ep->is_in)
+ tmp |= 0x04;
+ tmp <<= 8;
+ tmp |= AT91_UDP_EPEDS;
+ __raw_writel(tmp, ep->creg);
+
+ ep->ep.maxpacket = maxpacket;
+
+ /*
+ * reset/init endpoint fifo. NOTE: leaves fifo_bank alone,
+ * since endpoint resets don't reset hw pingpong state.
+ */
+ at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask);
+ at91_udp_write(udc, AT91_UDP_RST_EP, 0);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+static int at91_ep_disable (struct usb_ep * _ep)
+{
+ struct at91_ep *ep = container_of(_ep, struct at91_ep, ep);
+ struct at91_udc *udc = ep->udc;
+ unsigned long flags;
+
+ if (ep == &ep->udc->ep[0])
+ return -EINVAL;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ nuke(ep, -ESHUTDOWN);
+
+ /* restore the endpoint's pristine config */
+ ep->ep.desc = NULL;
+ ep->ep.maxpacket = ep->maxpacket;
+
+ /* reset fifos and endpoint */
+ if (ep->udc->clocked) {
+ at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask);
+ at91_udp_write(udc, AT91_UDP_RST_EP, 0);
+ __raw_writel(0, ep->creg);
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/*
+ * this is a PIO-only driver, so there's nothing
+ * interesting for request or buffer allocation.
+ */
+
+static struct usb_request *
+at91_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct at91_request *req;
+
+ req = kzalloc(sizeof (struct at91_request), gfp_flags);
+ if (!req)
+ return NULL;
+
+ INIT_LIST_HEAD(&req->queue);
+ return &req->req;
+}
+
+static void at91_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct at91_request *req;
+
+ req = container_of(_req, struct at91_request, req);
+ BUG_ON(!list_empty(&req->queue));
+ kfree(req);
+}
+
+static int at91_ep_queue(struct usb_ep *_ep,
+ struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct at91_request *req;
+ struct at91_ep *ep;
+ struct at91_udc *udc;
+ int status;
+ unsigned long flags;
+
+ req = container_of(_req, struct at91_request, req);
+ ep = container_of(_ep, struct at91_ep, ep);
+
+ if (!_req || !_req->complete
+ || !_req->buf || !list_empty(&req->queue)) {
+ DBG("invalid request\n");
+ return -EINVAL;
+ }
+
+ if (!_ep || (!ep->ep.desc && ep->ep.name != ep0name)) {
+ DBG("invalid ep\n");
+ return -EINVAL;
+ }
+
+ udc = ep->udc;
+
+ if (!udc || !udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+ DBG("invalid device\n");
+ return -EINVAL;
+ }
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* try to kickstart any empty and idle queue */
+ if (list_empty(&ep->queue) && !ep->stopped) {
+ int is_ep0;
+
+ /*
+ * If this control request has a non-empty DATA stage, this
+ * will start that stage. It works just like a non-control
+ * request (until the status stage starts, maybe early).
+ *
+ * If the data stage is empty, then this starts a successful
+ * IN/STATUS stage. (Unsuccessful ones use set_halt.)
+ */
+ is_ep0 = (ep->ep.name == ep0name);
+ if (is_ep0) {
+ u32 tmp;
+
+ if (!udc->req_pending) {
+ status = -EINVAL;
+ goto done;
+ }
+
+ /*
+ * defer changing CONFG until after the gadget driver
+ * reconfigures the endpoints.
+ */
+ if (udc->wait_for_config_ack) {
+ tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);
+ tmp ^= AT91_UDP_CONFG;
+ VDBG("toggle config\n");
+ at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);
+ }
+ if (req->req.length == 0) {
+ep0_in_status:
+ PACKET("ep0 in/status\n");
+ status = 0;
+ tmp = __raw_readl(ep->creg);
+ tmp &= ~SET_FX;
+ tmp |= CLR_FX | AT91_UDP_TXPKTRDY;
+ __raw_writel(tmp, ep->creg);
+ udc->req_pending = 0;
+ goto done;
+ }
+ }
+
+ if (ep->is_in)
+ status = write_fifo(ep, req);
+ else {
+ status = read_fifo(ep, req);
+
+ /* IN/STATUS stage is otherwise triggered by irq */
+ if (status && is_ep0)
+ goto ep0_in_status;
+ }
+ } else
+ status = 0;
+
+ if (req && !status) {
+ list_add_tail (&req->queue, &ep->queue);
+ at91_udp_write(udc, AT91_UDP_IER, ep->int_mask);
+ }
+done:
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return (status < 0) ? status : 0;
+}
+
+static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct at91_ep *ep;
+ struct at91_request *req;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct at91_ep, ep);
+ if (!_ep || ep->ep.name == ep0name)
+ return -EINVAL;
+
+ spin_lock_irqsave(&udc->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(&udc->lock, flags);
+ return -EINVAL;
+ }
+
+ done(ep, req, -ECONNRESET);
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+static int at91_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct at91_ep *ep = container_of(_ep, struct at91_ep, ep);
+ struct at91_udc *udc = ep->udc;
+ u32 __iomem *creg;
+ u32 csr;
+ unsigned long flags;
+ int status = 0;
+
+ if (!_ep || ep->is_iso || !ep->udc->clocked)
+ return -EINVAL;
+
+ creg = ep->creg;
+ spin_lock_irqsave(&udc->lock, flags);
+
+ csr = __raw_readl(creg);
+
+ /*
+ * fail with still-busy IN endpoints, ensuring correct sequencing
+ * of data tx then stall. note that the fifo rx bytecount isn't
+ * completely accurate as a tx bytecount.
+ */
+ if (ep->is_in && (!list_empty(&ep->queue) || (csr >> 16) != 0))
+ status = -EAGAIN;
+ else {
+ csr |= CLR_FX;
+ csr &= ~SET_FX;
+ if (value) {
+ csr |= AT91_UDP_FORCESTALL;
+ VDBG("halt %s\n", ep->ep.name);
+ } else {
+ at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask);
+ at91_udp_write(udc, AT91_UDP_RST_EP, 0);
+ csr &= ~AT91_UDP_FORCESTALL;
+ }
+ __raw_writel(csr, creg);
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return status;
+}
+
+static const struct usb_ep_ops at91_ep_ops = {
+ .enable = at91_ep_enable,
+ .disable = at91_ep_disable,
+ .alloc_request = at91_ep_alloc_request,
+ .free_request = at91_ep_free_request,
+ .queue = at91_ep_queue,
+ .dequeue = at91_ep_dequeue,
+ .set_halt = at91_ep_set_halt,
+ /* there's only imprecise fifo status reporting */
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int at91_get_frame(struct usb_gadget *gadget)
+{
+ struct at91_udc *udc = to_udc(gadget);
+
+ if (!to_udc(gadget)->clocked)
+ return -EINVAL;
+ return at91_udp_read(udc, AT91_UDP_FRM_NUM) & AT91_UDP_NUM;
+}
+
+static int at91_wakeup(struct usb_gadget *gadget)
+{
+ struct at91_udc *udc = to_udc(gadget);
+ u32 glbstate;
+ int status = -EINVAL;
+ unsigned long flags;
+
+ DBG("%s\n", __func__ );
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (!udc->clocked || !udc->suspended)
+ goto done;
+
+ /* NOTE: some "early versions" handle ESR differently ... */
+
+ glbstate = at91_udp_read(udc, AT91_UDP_GLB_STAT);
+ if (!(glbstate & AT91_UDP_ESR))
+ goto done;
+ glbstate |= AT91_UDP_ESR;
+ at91_udp_write(udc, AT91_UDP_GLB_STAT, glbstate);
+
+done:
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return status;
+}
+
+/* reinit == restore initial software state */
+static void udc_reinit(struct at91_udc *udc)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+ INIT_LIST_HEAD(&udc->gadget.ep0->ep_list);
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ struct at91_ep *ep = &udc->ep[i];
+
+ if (i != 0)
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+ ep->ep.desc = NULL;
+ ep->stopped = 0;
+ ep->fifo_bank = 0;
+ usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket);
+ ep->creg = (void __iomem *) udc->udp_baseaddr + AT91_UDP_CSR(i);
+ /* initialize one queue per endpoint */
+ INIT_LIST_HEAD(&ep->queue);
+ }
+}
+
+static void reset_gadget(struct at91_udc *udc)
+{
+ struct usb_gadget_driver *driver = udc->driver;
+ int i;
+
+ if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->suspended = 0;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ struct at91_ep *ep = &udc->ep[i];
+
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
+ if (driver) {
+ spin_unlock(&udc->lock);
+ udc->driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+
+ udc_reinit(udc);
+}
+
+static void stop_activity(struct at91_udc *udc)
+{
+ struct usb_gadget_driver *driver = udc->driver;
+ int i;
+
+ if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->suspended = 0;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ struct at91_ep *ep = &udc->ep[i];
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
+ if (driver) {
+ spin_unlock(&udc->lock);
+ driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+
+ udc_reinit(udc);
+}
+
+static void clk_on(struct at91_udc *udc)
+{
+ if (udc->clocked)
+ return;
+ udc->clocked = 1;
+}
+
+static void clk_off(struct at91_udc *udc)
+{
+ if (!udc->clocked)
+ return;
+ udc->clocked = 0;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
+/*
+ * activate/deactivate link with host; minimize power usage for
+ * inactive links by cutting clocks and transceiver power.
+ */
+static void pullup(struct at91_udc *udc, int is_on)
+{
+ if (!udc->enabled || !udc->vbus)
+ is_on = 0;
+ DBG("%sactive\n", is_on ? "" : "in");
+
+ if (is_on) {
+ clk_on(udc);
+ at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM);
+ at91_udp_write(udc, AT91_UDP_TXVC, 0);
+ } else {
+ stop_activity(udc);
+ at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM);
+ at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
+ clk_off(udc);
+ }
+
+ if (udc->caps && udc->caps->pullup)
+ udc->caps->pullup(udc, is_on);
+}
+
+/* vbus is here! turn everything on that's ready */
+static int at91_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+ struct at91_udc *udc = to_udc(gadget);
+ unsigned long flags;
+
+ /* VDBG("vbus %s\n", is_active ? "on" : "off"); */
+ spin_lock_irqsave(&udc->lock, flags);
+ udc->vbus = (is_active != 0);
+ if (udc->driver)
+ pullup(udc, is_active);
+ else
+ pullup(udc, 0);
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+static int at91_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct at91_udc *udc = to_udc(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ udc->enabled = is_on = !!is_on;
+ pullup(udc, is_on);
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
+{
+ struct at91_udc *udc = to_udc(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ udc->selfpowered = (is_on != 0);
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+static int at91_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver);
+static int at91_stop(struct usb_gadget *gadget);
+
+static const struct usb_gadget_ops at91_udc_ops = {
+ .get_frame = at91_get_frame,
+ .wakeup = at91_wakeup,
+ .set_selfpowered = at91_set_selfpowered,
+ .vbus_session = at91_vbus_session,
+ .pullup = at91_pullup,
+ .udc_start = at91_start,
+ .udc_stop = at91_stop,
+
+ /*
+ * VBUS-powered devices may also also want to support bigger
+ * power budgets after an appropriate SET_CONFIGURATION.
+ */
+ /* .vbus_power = at91_vbus_power, */
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int handle_ep(struct at91_ep *ep)
+{
+ struct at91_request *req;
+ u32 __iomem *creg = ep->creg;
+ u32 csr = __raw_readl(creg);
+
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next,
+ struct at91_request, queue);
+ else
+ req = NULL;
+
+ if (ep->is_in) {
+ if (csr & (AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)) {
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP);
+ __raw_writel(csr, creg);
+ }
+ if (req)
+ return write_fifo(ep, req);
+
+ } else {
+ if (csr & AT91_UDP_STALLSENT) {
+ /* STALLSENT bit == ISOERR */
+ if (ep->is_iso && req)
+ req->req.status = -EILSEQ;
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_STALLSENT);
+ __raw_writel(csr, creg);
+ csr = __raw_readl(creg);
+ }
+ if (req && (csr & RX_DATA_READY))
+ return read_fifo(ep, req);
+ }
+ return 0;
+}
+
+union setup {
+ u8 raw[8];
+ struct usb_ctrlrequest r;
+};
+
+static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr)
+{
+ u32 __iomem *creg = ep->creg;
+ u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0));
+ unsigned rxcount, i = 0;
+ u32 tmp;
+ union setup pkt;
+ int status = 0;
+
+ /* read and ack SETUP; hard-fail for bogus packets */
+ rxcount = (csr & AT91_UDP_RXBYTECNT) >> 16;
+ if (likely(rxcount == 8)) {
+ while (rxcount--)
+ pkt.raw[i++] = __raw_readb(dreg);
+ if (pkt.r.bRequestType & USB_DIR_IN) {
+ csr |= AT91_UDP_DIR;
+ ep->is_in = 1;
+ } else {
+ csr &= ~AT91_UDP_DIR;
+ ep->is_in = 0;
+ }
+ } else {
+ /* REVISIT this happens sometimes under load; why?? */
+ ERR("SETUP len %d, csr %08x\n", rxcount, csr);
+ status = -EINVAL;
+ }
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_RXSETUP);
+ __raw_writel(csr, creg);
+ udc->wait_for_addr_ack = 0;
+ udc->wait_for_config_ack = 0;
+ ep->stopped = 0;
+ if (unlikely(status != 0))
+ goto stall;
+
+#define w_index le16_to_cpu(pkt.r.wIndex)
+#define w_value le16_to_cpu(pkt.r.wValue)
+#define w_length le16_to_cpu(pkt.r.wLength)
+
+ VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n",
+ pkt.r.bRequestType, pkt.r.bRequest,
+ w_value, w_index, w_length);
+
+ /*
+ * A few standard requests get handled here, ones that touch
+ * hardware ... notably for device and endpoint features.
+ */
+ udc->req_pending = 1;
+ csr = __raw_readl(creg);
+ csr |= CLR_FX;
+ csr &= ~SET_FX;
+ switch ((pkt.r.bRequestType << 8) | pkt.r.bRequest) {
+
+ case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+ | USB_REQ_SET_ADDRESS:
+ __raw_writel(csr | AT91_UDP_TXPKTRDY, creg);
+ udc->addr = w_value;
+ udc->wait_for_addr_ack = 1;
+ udc->req_pending = 0;
+ /* FADDR is set later, when we ack host STATUS */
+ return;
+
+ case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+ | USB_REQ_SET_CONFIGURATION:
+ tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT) & AT91_UDP_CONFG;
+ if (pkt.r.wValue)
+ udc->wait_for_config_ack = (tmp == 0);
+ else
+ udc->wait_for_config_ack = (tmp != 0);
+ if (udc->wait_for_config_ack)
+ VDBG("wait for config\n");
+ /* CONFG is toggled later, if gadget driver succeeds */
+ break;
+
+ /*
+ * Hosts may set or clear remote wakeup status, and
+ * devices may report they're VBUS powered.
+ */
+ case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+ | USB_REQ_GET_STATUS:
+ tmp = (udc->selfpowered << USB_DEVICE_SELF_POWERED);
+ if (at91_udp_read(udc, AT91_UDP_GLB_STAT) & AT91_UDP_ESR)
+ tmp |= (1 << USB_DEVICE_REMOTE_WAKEUP);
+ PACKET("get device status\n");
+ __raw_writeb(tmp, dreg);
+ __raw_writeb(0, dreg);
+ goto write_in;
+ /* then STATUS starts later, automatically */
+ case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+ | USB_REQ_SET_FEATURE:
+ if (w_value != USB_DEVICE_REMOTE_WAKEUP)
+ goto stall;
+ tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);
+ tmp |= AT91_UDP_ESR;
+ at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);
+ goto succeed;
+ case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+ | USB_REQ_CLEAR_FEATURE:
+ if (w_value != USB_DEVICE_REMOTE_WAKEUP)
+ goto stall;
+ tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);
+ tmp &= ~AT91_UDP_ESR;
+ at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);
+ goto succeed;
+
+ /*
+ * Interfaces have no feature settings; this is pretty useless.
+ * we won't even insist the interface exists...
+ */
+ case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)
+ | USB_REQ_GET_STATUS:
+ PACKET("get interface status\n");
+ __raw_writeb(0, dreg);
+ __raw_writeb(0, dreg);
+ goto write_in;
+ /* then STATUS starts later, automatically */
+ case ((USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)
+ | USB_REQ_SET_FEATURE:
+ case ((USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)
+ | USB_REQ_CLEAR_FEATURE:
+ goto stall;
+
+ /*
+ * Hosts may clear bulk/intr endpoint halt after the gadget
+ * driver sets it (not widely used); or set it (for testing)
+ */
+ case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)
+ | USB_REQ_GET_STATUS:
+ tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
+ ep = &udc->ep[tmp];
+ if (tmp >= NUM_ENDPOINTS || (tmp && !ep->ep.desc))
+ goto stall;
+
+ if (tmp) {
+ if ((w_index & USB_DIR_IN)) {
+ if (!ep->is_in)
+ goto stall;
+ } else if (ep->is_in)
+ goto stall;
+ }
+ PACKET("get %s status\n", ep->ep.name);
+ if (__raw_readl(ep->creg) & AT91_UDP_FORCESTALL)
+ tmp = (1 << USB_ENDPOINT_HALT);
+ else
+ tmp = 0;
+ __raw_writeb(tmp, dreg);
+ __raw_writeb(0, dreg);
+ goto write_in;
+ /* then STATUS starts later, automatically */
+ case ((USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)
+ | USB_REQ_SET_FEATURE:
+ tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
+ ep = &udc->ep[tmp];
+ if (w_value != USB_ENDPOINT_HALT || tmp >= NUM_ENDPOINTS)
+ goto stall;
+ if (!ep->ep.desc || ep->is_iso)
+ goto stall;
+ if ((w_index & USB_DIR_IN)) {
+ if (!ep->is_in)
+ goto stall;
+ } else if (ep->is_in)
+ goto stall;
+
+ tmp = __raw_readl(ep->creg);
+ tmp &= ~SET_FX;
+ tmp |= CLR_FX | AT91_UDP_FORCESTALL;
+ __raw_writel(tmp, ep->creg);
+ goto succeed;
+ case ((USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)
+ | USB_REQ_CLEAR_FEATURE:
+ tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
+ ep = &udc->ep[tmp];
+ if (w_value != USB_ENDPOINT_HALT || tmp >= NUM_ENDPOINTS)
+ goto stall;
+ if (tmp == 0)
+ goto succeed;
+ if (!ep->ep.desc || ep->is_iso)
+ goto stall;
+ if ((w_index & USB_DIR_IN)) {
+ if (!ep->is_in)
+ goto stall;
+ } else if (ep->is_in)
+ goto stall;
+
+ at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask);
+ at91_udp_write(udc, AT91_UDP_RST_EP, 0);
+ tmp = __raw_readl(ep->creg);
+ tmp |= CLR_FX;
+ tmp &= ~(SET_FX | AT91_UDP_FORCESTALL);
+ __raw_writel(tmp, ep->creg);
+ if (!list_empty(&ep->queue))
+ handle_ep(ep);
+ goto succeed;
+ }
+
+#undef w_value
+#undef w_index
+#undef w_length
+
+ /* pass request up to the gadget driver */
+ if (udc->driver) {
+ spin_unlock(&udc->lock);
+ status = udc->driver->setup(&udc->gadget, &pkt.r);
+ spin_lock(&udc->lock);
+ }
+ else
+ status = -ENODEV;
+ if (status < 0) {
+stall:
+ VDBG("req %02x.%02x protocol STALL; stat %d\n",
+ pkt.r.bRequestType, pkt.r.bRequest, status);
+ csr |= AT91_UDP_FORCESTALL;
+ __raw_writel(csr, creg);
+ udc->req_pending = 0;
+ }
+ return;
+
+succeed:
+ /* immediate successful (IN) STATUS after zero length DATA */
+ PACKET("ep0 in/status\n");
+write_in:
+ csr |= AT91_UDP_TXPKTRDY;
+ __raw_writel(csr, creg);
+ udc->req_pending = 0;
+}
+
+static void handle_ep0(struct at91_udc *udc)
+{
+ struct at91_ep *ep0 = &udc->ep[0];
+ u32 __iomem *creg = ep0->creg;
+ u32 csr = __raw_readl(creg);
+ struct at91_request *req;
+
+ if (unlikely(csr & AT91_UDP_STALLSENT)) {
+ nuke(ep0, -EPROTO);
+ udc->req_pending = 0;
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_FORCESTALL);
+ __raw_writel(csr, creg);
+ VDBG("ep0 stalled\n");
+ csr = __raw_readl(creg);
+ }
+ if (csr & AT91_UDP_RXSETUP) {
+ nuke(ep0, 0);
+ udc->req_pending = 0;
+ handle_setup(udc, ep0, csr);
+ return;
+ }
+
+ if (list_empty(&ep0->queue))
+ req = NULL;
+ else
+ req = list_entry(ep0->queue.next, struct at91_request, queue);
+
+ /* host ACKed an IN packet that we sent */
+ if (csr & AT91_UDP_TXCOMP) {
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_TXCOMP);
+
+ /* write more IN DATA? */
+ if (req && ep0->is_in) {
+ if (handle_ep(ep0))
+ udc->req_pending = 0;
+
+ /*
+ * Ack after:
+ * - last IN DATA packet (including GET_STATUS)
+ * - IN/STATUS for OUT DATA
+ * - IN/STATUS for any zero-length DATA stage
+ * except for the IN DATA case, the host should send
+ * an OUT status later, which we'll ack.
+ */
+ } else {
+ udc->req_pending = 0;
+ __raw_writel(csr, creg);
+
+ /*
+ * SET_ADDRESS takes effect only after the STATUS
+ * (to the original address) gets acked.
+ */
+ if (udc->wait_for_addr_ack) {
+ u32 tmp;
+
+ at91_udp_write(udc, AT91_UDP_FADDR,
+ AT91_UDP_FEN | udc->addr);
+ tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);
+ tmp &= ~AT91_UDP_FADDEN;
+ if (udc->addr)
+ tmp |= AT91_UDP_FADDEN;
+ at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);
+
+ udc->wait_for_addr_ack = 0;
+ VDBG("address %d\n", udc->addr);
+ }
+ }
+ }
+
+ /* OUT packet arrived ... */
+ else if (csr & AT91_UDP_RX_DATA_BK0) {
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0);
+
+ /* OUT DATA stage */
+ if (!ep0->is_in) {
+ if (req) {
+ if (handle_ep(ep0)) {
+ /* send IN/STATUS */
+ PACKET("ep0 in/status\n");
+ csr = __raw_readl(creg);
+ csr &= ~SET_FX;
+ csr |= CLR_FX | AT91_UDP_TXPKTRDY;
+ __raw_writel(csr, creg);
+ udc->req_pending = 0;
+ }
+ } else if (udc->req_pending) {
+ /*
+ * AT91 hardware has a hard time with this
+ * "deferred response" mode for control-OUT
+ * transfers. (For control-IN it's fine.)
+ *
+ * The normal solution leaves OUT data in the
+ * fifo until the gadget driver is ready.
+ * We couldn't do that here without disabling
+ * the IRQ that tells about SETUP packets,
+ * e.g. when the host gets impatient...
+ *
+ * Working around it by copying into a buffer
+ * would almost be a non-deferred response,
+ * except that it wouldn't permit reliable
+ * stalling of the request. Instead, demand
+ * that gadget drivers not use this mode.
+ */
+ DBG("no control-OUT deferred responses!\n");
+ __raw_writel(csr | AT91_UDP_FORCESTALL, creg);
+ udc->req_pending = 0;
+ }
+
+ /* STATUS stage for control-IN; ack. */
+ } else {
+ PACKET("ep0 out/status ACK\n");
+ __raw_writel(csr, creg);
+
+ /* "early" status stage */
+ if (req)
+ done(ep0, req, 0);
+ }
+ }
+}
+
+static irqreturn_t at91_udc_irq(struct at91_udc *udc)
+{
+ u32 rescans = 5;
+ int disable_clock = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (!udc->clocked) {
+ clk_on(udc);
+ disable_clock = 1;
+ }
+
+ while (rescans--) {
+ u32 status;
+
+ status = at91_udp_read(udc, AT91_UDP_ISR)
+ & at91_udp_read(udc, AT91_UDP_IMR);
+ if (!status)
+ break;
+
+ /* USB reset irq: not maskable */
+ if (status & AT91_UDP_ENDBUSRES) {
+ at91_udp_write(udc, AT91_UDP_IDR, ~MINIMUS_INTERRUPTUS);
+ at91_udp_write(udc, AT91_UDP_IER, MINIMUS_INTERRUPTUS);
+ /* Atmel code clears this irq twice */
+ at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_ENDBUSRES);
+ at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_ENDBUSRES);
+ VDBG("end bus reset\n");
+ udc->addr = 0;
+ reset_gadget(udc);
+
+ /* enable ep0 */
+ at91_udp_write(udc, AT91_UDP_CSR(0),
+ AT91_UDP_EPEDS | AT91_UDP_EPTYPE_CTRL);
+ udc->gadget.speed = USB_SPEED_FULL;
+ udc->suspended = 0;
+ at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_EP(0));
+
+ /*
+ * NOTE: this driver keeps clocks off unless the
+ * USB host is present. That saves power, but for
+ * boards that don't support VBUS detection, both
+ * clocks need to be active most of the time.
+ */
+
+ /* host initiated suspend (3+ms bus idle) */
+ } else if (status & AT91_UDP_RXSUSP) {
+ at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXSUSP);
+ at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXRSM);
+ at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXSUSP);
+ /* VDBG("bus suspend\n"); */
+ if (udc->suspended)
+ continue;
+ udc->suspended = 1;
+
+ /*
+ * NOTE: when suspending a VBUS-powered device, the
+ * gadget driver should switch into slow clock mode
+ * and then into standby to avoid drawing more than
+ * 500uA power (2500uA for some high-power configs).
+ */
+ if (udc->driver && udc->driver->suspend) {
+ spin_unlock(&udc->lock);
+ udc->driver->suspend(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+
+ /* host initiated resume */
+ } else if (status & AT91_UDP_RXRSM) {
+ at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM);
+ at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXSUSP);
+ at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM);
+ /* VDBG("bus resume\n"); */
+ if (!udc->suspended)
+ continue;
+ udc->suspended = 0;
+
+ /*
+ * NOTE: for a VBUS-powered device, the gadget driver
+ * would normally want to switch out of slow clock
+ * mode into normal mode.
+ */
+ if (udc->driver && udc->driver->resume) {
+ spin_unlock(&udc->lock);
+ udc->driver->resume(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+
+ /* endpoint IRQs are cleared by handling them */
+ } else {
+ int i;
+ unsigned mask = 1;
+ struct at91_ep *ep = &udc->ep[1];
+
+ if (status & mask)
+ handle_ep0(udc);
+ for (i = 1; i < NUM_ENDPOINTS; i++) {
+ mask <<= 1;
+ if (status & mask)
+ handle_ep(ep);
+ ep++;
+ }
+ }
+ }
+
+ if (disable_clock)
+ clk_off(udc);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int at91_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct at91_udc *udc = controller;
+
+ udc->driver = driver;
+ udc->enabled = 1;
+ udc->selfpowered = 1;
+
+ return 0;
+}
+
+static int at91_stop(struct usb_gadget *gadget)
+{
+ struct at91_udc *udc = controller;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ udc->enabled = 0;
+ at91_udp_write(udc, AT91_UDP_IDR, ~0);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ udc->driver = NULL;
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#if defined(CONFIG_AT91SAM9260) || defined(CONFIG_AT91SAM9G20)
+static int at91sam9260_udc_init(struct at91_udc *udc)
+{
+ struct at91_ep *ep;
+ int i;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+
+ switch (i) {
+ case 0 ... 3:
+ ep->maxpacket = 64;
+ break;
+ case 4 ... 5:
+ ep->maxpacket = 512;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void at91sam9260_udc_pullup(struct at91_udc *udc, int is_on)
+{
+ u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC);
+
+ if (is_on)
+ txvc |= AT91_UDP_TXVC_PUON;
+ else
+ txvc &= ~AT91_UDP_TXVC_PUON;
+
+ at91_udp_write(udc, AT91_UDP_TXVC, txvc);
+}
+
+static const struct at91_udc_caps at91sam9260_udc_caps = {
+ .init = at91sam9260_udc_init,
+ .pullup = at91sam9260_udc_pullup,
+};
+#endif
+
+#if defined(CONFIG_AT91SAM9261)
+static int at91sam9261_udc_init(struct at91_udc *udc)
+{
+ struct at91_ep *ep;
+ int i;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+
+ switch (i) {
+ case 0:
+ ep->maxpacket = 8;
+ break;
+ case 1 ... 3:
+ ep->maxpacket = 64;
+ break;
+ case 4 ... 5:
+ ep->maxpacket = 256;
+ break;
+ }
+ }
+
+ udc->matrix = (struct at91_matrix *)ATMEL_BASE_MATRIX;
+
+ if (IS_ERR(udc->matrix))
+ return PTR_ERR(udc->matrix);
+
+ return 0;
+}
+
+static void at91sam9261_udc_pullup(struct at91_udc *udc, int is_on)
+{
+ u32 usbpucr = 0;
+
+ usbpucr = readl(&udc->matrix->pucr);
+ if (is_on)
+ usbpucr |= AT91_MATRIX_USBPUCR_PUON;
+
+ writel(usbpucr, &udc->matrix->pucr);
+}
+
+static const struct at91_udc_caps at91sam9261_udc_caps = {
+ .init = at91sam9261_udc_init,
+ .pullup = at91sam9261_udc_pullup,
+};
+#endif
+
+int usb_gadget_handle_interrupts(int index)
+{
+ struct at91_udc *udc = controller;
+
+ return at91_udc_irq(udc);
+}
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct at91_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;
+ }
+
+ at91_start(&udc->gadget, driver);
+
+ udc->driver = driver;
+
+ ret = driver->bind(&udc->gadget);
+ if (ret) {
+ pr_err("driver->bind() returned %d\n", ret);
+ udc->driver = NULL;
+ }
+
+ return ret;
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct at91_udc *udc = controller;
+
+ if (!driver || !driver->unbind || !driver->disconnect) {
+ pr_err("bad paramter\n");
+ return -EINVAL;
+ }
+
+ driver->disconnect(&udc->gadget);
+ driver->unbind(&udc->gadget);
+ udc->driver = NULL;
+
+ at91_stop(&udc->gadget);
+
+ return 0;
+}
+
+int at91_udc_probe(struct at91_udc_data *pdata)
+{
+ struct at91_udc *udc;
+ int retval;
+ struct at91_ep *ep;
+ int i;
+
+ udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ return -ENOMEM;
+
+ controller = udc;
+ memcpy(&udc->board, pdata, sizeof(struct at91_udc_data));
+ if (udc->board.vbus_pin) {
+ printf("%s: gpio vbus pin not supported yet.\n", __func__);
+ return -ENXIO;
+ } else {
+ DBG("no VBUS detection, assuming always-on\n");
+ udc->vbus = 1;
+ }
+
+#if defined(CONFIG_AT91SAM9260) || defined(CONFIG_AT91SAM9G20)
+ udc->caps = &at91sam9260_udc_caps;
+#endif
+
+ udc->enabled = 0;
+ spin_lock_init(&udc->lock);
+
+ udc->gadget.ops = &at91_udc_ops;
+ udc->gadget.ep0 = &udc->ep[0].ep;
+ udc->gadget.name = driver_name;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+ ep->ep.name = ep_names[i];
+ ep->ep.ops = &at91_ep_ops;
+ ep->udc = udc;
+ ep->int_mask = (1 << i);
+ if (i != 0 && i != 3)
+ ep->is_pingpong = 1;
+ }
+
+ udc->udp_baseaddr = (void *)udc->board.baseaddr;
+ if (IS_ERR(udc->udp_baseaddr))
+ return PTR_ERR(udc->udp_baseaddr);
+
+ if (udc->caps && udc->caps->init) {
+ retval = udc->caps->init(udc);
+ if (retval)
+ return retval;
+ }
+
+ udc_reinit(udc);
+
+ at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
+ at91_udp_write(udc, AT91_UDP_IDR, 0xffffffff);
+ /* Clear all pending interrupts - UDP may be used by bootloader. */
+ at91_udp_write(udc, AT91_UDP_ICR, 0xffffffff);
+
+ INFO("%s version %s\n", driver_name, DRIVER_VERSION);
+ return 0;
+}
diff --git a/roms/u-boot/drivers/usb/gadget/at91_udc.h b/roms/u-boot/drivers/usb/gadget/at91_udc.h
new file mode 100644
index 000000000..668b8c4a7
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/at91_udc.h
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2004 by Thomas Rathbone, HP Labs
+ * Copyright (C) 2005 by Ivan Kokshaysky
+ * Copyright (C) 2006 by SAN People
+ */
+
+#ifndef AT91_UDC_H
+#define AT91_UDC_H
+
+/*
+ * USB Device Port (UDP) registers.
+ * Based on AT91RM9200 datasheet revision E.
+ */
+
+#define AT91_UDP_FRM_NUM 0x00 /* Frame Number Register */
+#define AT91_UDP_NUM (0x7ff << 0) /* Frame Number */
+#define AT91_UDP_FRM_ERR (1 << 16) /* Frame Error */
+#define AT91_UDP_FRM_OK (1 << 17) /* Frame OK */
+
+#define AT91_UDP_GLB_STAT 0x04 /* Global State Register */
+#define AT91_UDP_FADDEN (1 << 0) /* Function Address Enable */
+#define AT91_UDP_CONFG (1 << 1) /* Configured */
+#define AT91_UDP_ESR (1 << 2) /* Enable Send Resume */
+#define AT91_UDP_RSMINPR (1 << 3) /* Resume has been sent */
+#define AT91_UDP_RMWUPE (1 << 4) /* Remote Wake Up Enable */
+
+#define AT91_UDP_FADDR 0x08 /* Function Address Register */
+#define AT91_UDP_FADD (0x7f << 0) /* Function Address Value */
+#define AT91_UDP_FEN (1 << 8) /* Function Enable */
+
+#define AT91_UDP_IER 0x10 /* Interrupt Enable Register */
+#define AT91_UDP_IDR 0x14 /* Interrupt Disable Register */
+#define AT91_UDP_IMR 0x18 /* Interrupt Mask Register */
+
+#define AT91_UDP_ISR 0x1c /* Interrupt Status Register */
+#define AT91_UDP_EP(n) (1 << (n)) /* Endpoint Interrupt Status */
+#define AT91_UDP_RXSUSP (1 << 8) /* USB Suspend Interrupt Status */
+#define AT91_UDP_RXRSM (1 << 9) /* USB Resume Interrupt Status */
+#define AT91_UDP_EXTRSM (1 << 10) /* External Resume Interrupt Status [AT91RM9200 only] */
+#define AT91_UDP_SOFINT (1 << 11) /* Start of Frame Interrupt Status */
+#define AT91_UDP_ENDBUSRES (1 << 12) /* End of Bus Reset Interrupt Status */
+#define AT91_UDP_WAKEUP (1 << 13) /* USB Wakeup Interrupt Status [AT91RM9200 only] */
+
+#define AT91_UDP_ICR 0x20 /* Interrupt Clear Register */
+#define AT91_UDP_RST_EP 0x28 /* Reset Endpoint Register */
+
+#define AT91_UDP_CSR(n) (0x30+((n)*4)) /* Endpoint Control/Status Registers 0-7 */
+#define AT91_UDP_TXCOMP (1 << 0) /* Generates IN packet with data previously written in DPR */
+#define AT91_UDP_RX_DATA_BK0 (1 << 1) /* Receive Data Bank 0 */
+#define AT91_UDP_RXSETUP (1 << 2) /* Send STALL to the host */
+#define AT91_UDP_STALLSENT (1 << 3) /* Stall Sent / Isochronous error (Isochronous endpoints) */
+#define AT91_UDP_TXPKTRDY (1 << 4) /* Transmit Packet Ready */
+#define AT91_UDP_FORCESTALL (1 << 5) /* Force Stall */
+#define AT91_UDP_RX_DATA_BK1 (1 << 6) /* Receive Data Bank 1 */
+#define AT91_UDP_DIR (1 << 7) /* Transfer Direction */
+#define AT91_UDP_EPTYPE (7 << 8) /* Endpoint Type */
+#define AT91_UDP_EPTYPE_CTRL (0 << 8)
+#define AT91_UDP_EPTYPE_ISO_OUT (1 << 8)
+#define AT91_UDP_EPTYPE_BULK_OUT (2 << 8)
+#define AT91_UDP_EPTYPE_INT_OUT (3 << 8)
+#define AT91_UDP_EPTYPE_ISO_IN (5 << 8)
+#define AT91_UDP_EPTYPE_BULK_IN (6 << 8)
+#define AT91_UDP_EPTYPE_INT_IN (7 << 8)
+#define AT91_UDP_DTGLE (1 << 11) /* Data Toggle */
+#define AT91_UDP_EPEDS (1 << 15) /* Endpoint Enable/Disable */
+#define AT91_UDP_RXBYTECNT (0x7ff << 16) /* Number of bytes in FIFO */
+
+#define AT91_UDP_FDR(n) (0x50+((n)*4)) /* Endpoint FIFO Data Registers 0-7 */
+
+#define AT91_UDP_TXVC 0x74 /* Transceiver Control Register */
+#define AT91_UDP_TXVC_TXVDIS (1 << 8) /* Transceiver Disable */
+#define AT91_UDP_TXVC_PUON (1 << 9) /* PullUp On [AT91SAM9260 only] */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * controller driver data structures
+ */
+
+#define NUM_ENDPOINTS 6
+
+/*
+ * hardware won't disable bus reset, or resume while the controller
+ * is suspended ... watching suspend helps keep the logic symmetric.
+ */
+#define MINIMUS_INTERRUPTUS \
+ (AT91_UDP_ENDBUSRES | AT91_UDP_RXRSM | AT91_UDP_RXSUSP)
+
+struct at91_ep {
+ struct usb_ep ep;
+ struct list_head queue;
+ struct at91_udc *udc;
+ void __iomem *creg;
+
+ unsigned maxpacket:16;
+ u8 int_mask;
+ unsigned is_pingpong:1;
+
+ unsigned stopped:1;
+ unsigned is_in:1;
+ unsigned is_iso:1;
+ unsigned fifo_bank:1;
+};
+
+struct at91_udc_caps {
+ int (*init)(struct at91_udc *udc);
+ void (*pullup)(struct at91_udc *udc, int is_on);
+};
+
+/*
+ * driver is non-SMP, and just blocks IRQs whenever it needs
+ * access protection for chip registers or driver state
+ */
+struct at91_udc {
+ struct usb_gadget gadget;
+ struct at91_ep ep[NUM_ENDPOINTS];
+ struct usb_gadget_driver *driver;
+ const struct at91_udc_caps *caps;
+ unsigned vbus:1;
+ unsigned enabled:1;
+ unsigned clocked:1;
+ unsigned suspended:1;
+ unsigned req_pending:1;
+ unsigned wait_for_addr_ack:1;
+ unsigned wait_for_config_ack:1;
+ unsigned selfpowered:1;
+ unsigned active_suspend:1;
+ u8 addr;
+ struct at91_udc_data board;
+ void __iomem *udp_baseaddr;
+ int udp_irq;
+ spinlock_t lock;
+ struct at91_matrix *matrix;
+};
+
+static inline struct at91_udc *to_udc(struct usb_gadget *g)
+{
+ return container_of(g, struct at91_udc, gadget);
+}
+
+struct at91_request {
+ struct usb_request req;
+ struct list_head queue;
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef VERBOSE_DEBUG
+# define VDBG DBG
+#else
+# define VDBG(stuff...) do{}while(0)
+#endif
+
+#ifdef PACKET_TRACE
+# define PACKET VDBG
+#else
+# define PACKET(stuff...) do{}while(0)
+#endif
+
+#define ERR(stuff...) debug("udc: " stuff)
+#define WARNING(stuff...) debug("udc: " stuff)
+#define INFO(stuff...) debug("udc: " stuff)
+#define DBG(stuff...) debug("udc: " stuff)
+
+#endif
+
diff --git a/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c b/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c
new file mode 100644
index 000000000..7d5182149
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c
@@ -0,0 +1,1304 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 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>
+ */
+
+#include <common.h>
+#include <linux/bitops.h>
+#include <linux/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 "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 = 0, 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 = 0;
+
+ 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 = calloc(1, 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 = 0;
+ 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 = 0;
+ 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 = 0;
+ 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 = 0;
+
+ 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);
+ }
+}
+
+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(int index)
+{
+ 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) {
+ pr_err("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) {
+ pr_err("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) {
+ pr_err("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/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h b/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h
new file mode 100644
index 000000000..f6cb48c1c
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h
@@ -0,0 +1,320 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * 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>
+ */
+
+#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)
+
+#define USBA_ENABLE_MASK (USBA_EN_USBA | USBA_PULLD_DIS)
+#define USBA_DISABLE_MASK USBA_DETACH
+
+/* 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/roms/u-boot/drivers/usb/gadget/bcm_udc_otg.h b/roms/u-boot/drivers/usb/gadget/bcm_udc_otg.h
new file mode 100644
index 000000000..24cc936c6
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/bcm_udc_otg.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2015 Broadcom Corporation.
+ */
+
+#ifndef __BCM_UDC_OTG_H
+#define __BCM_UDC_OTG_H
+
+#include <common.h>
+
+static inline void wfld_set(uintptr_t addr, uint32_t fld_val, uint32_t fld_mask)
+{
+ writel(((readl(addr) & ~(fld_mask)) | (fld_val)), (addr));
+}
+
+static inline void wfld_clear(uintptr_t addr, uint32_t fld_mask)
+{
+ writel((readl(addr) & ~(fld_mask)), (addr));
+}
+
+#endif
diff --git a/roms/u-boot/drivers/usb/gadget/bcm_udc_otg_phy.c b/roms/u-boot/drivers/usb/gadget/bcm_udc_otg_phy.c
new file mode 100644
index 000000000..c89cd57c2
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/bcm_udc_otg_phy.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2015 Broadcom Corporation.
+ */
+
+#include <config.h>
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/sysmap.h>
+#include <asm/kona-common/clk.h>
+#include <linux/delay.h>
+
+#include "dwc2_udc_otg_priv.h"
+#include "bcm_udc_otg.h"
+
+void otg_phy_init(struct dwc2_udc *dev)
+{
+ /* turn on the USB OTG clocks */
+ clk_usb_otg_enable((void *)HSOTG_BASE_ADDR);
+
+ /* set Phy to driving mode */
+ wfld_clear(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET,
+ HSOTG_CTRL_PHY_P1CTL_NON_DRIVING_MASK);
+
+ udelay(100);
+
+ /* clear Soft Disconnect */
+ wfld_clear(HSOTG_BASE_ADDR + HSOTG_DCTL_OFFSET,
+ HSOTG_DCTL_SFTDISCON_MASK);
+
+ /* invoke Reset (active low) */
+ wfld_clear(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET,
+ HSOTG_CTRL_PHY_P1CTL_SOFT_RESET_MASK);
+
+ /* Reset needs to be asserted for 2ms */
+ udelay(2000);
+
+ /* release Reset */
+ wfld_set(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET,
+ HSOTG_CTRL_PHY_P1CTL_SOFT_RESET_MASK,
+ HSOTG_CTRL_PHY_P1CTL_SOFT_RESET_MASK);
+}
+
+void otg_phy_off(struct dwc2_udc *dev)
+{
+ /* Soft Disconnect */
+ wfld_set(HSOTG_BASE_ADDR + HSOTG_DCTL_OFFSET,
+ HSOTG_DCTL_SFTDISCON_MASK,
+ HSOTG_DCTL_SFTDISCON_MASK);
+
+ /* set Phy to non-driving (reset) mode */
+ wfld_set(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET,
+ HSOTG_CTRL_PHY_P1CTL_NON_DRIVING_MASK,
+ HSOTG_CTRL_PHY_P1CTL_NON_DRIVING_MASK);
+}
diff --git a/roms/u-boot/drivers/usb/gadget/ci_udc.c b/roms/u-boot/drivers/usb/gadget/ci_udc.c
new file mode 100644
index 000000000..226a9e6d6
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/ci_udc.c
@@ -0,0 +1,1072 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ * Lei Wen <leiwen@marvell.com>
+ *
+ * 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 <cpu_func.h>
+#include <net.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <asm/cache.h>
+#include <linux/delay.h>
+#include <linux/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
+
+/*
+ * Every QTD must be individually aligned, since we can program any
+ * QTD's address into HW. Cache flushing requires ARCH_DMA_MINALIGN,
+ * and the USB HW requires 32-byte alignment. Align to both:
+ */
+#define ILIST_ALIGN roundup(ARCH_DMA_MINALIGN, 32)
+/* Each QTD is this size */
+#define ILIST_ENT_RAW_SZ sizeof(struct ept_queue_item)
+/*
+ * Align the size of the QTD too, so we can add this value to each
+ * QTD's address to get another aligned address.
+ */
+#define ILIST_ENT_SZ roundup(ILIST_ENT_RAW_SZ, ILIST_ALIGN)
+/* For each endpoint, we need 2 QTDs, one for each of IN and OUT */
+#define ILIST_SZ (NUM_ENDPOINTS * 2 * ILIST_ENT_SZ)
+
+#define EP_MAX_LENGTH_TRANSFER 0x4000
+
+#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_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 int ci_ep_dequeue(struct usb_ep *ep, struct usb_request *req);
+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,
+ .dequeue = ci_ep_dequeue,
+ .alloc_request = ci_ep_alloc_request,
+ .free_request = ci_ep_free_request,
+};
+
+__weak void ci_init_after_reset(struct ehci_ctrl *ctrl)
+{
+}
+
+/* Init values for USB endpoints. */
+static const struct usb_ep ci_ep_init[5] = {
+ [0] = { /* EP 0 */
+ .maxpacket = 64,
+ .name = "ep0",
+ .ops = &ci_ep_ops,
+ },
+ [1] = {
+ .maxpacket = 512,
+ .name = "ep1in-bulk",
+ .ops = &ci_ep_ops,
+ },
+ [2] = {
+ .maxpacket = 512,
+ .name = "ep2out-bulk",
+ .ops = &ci_ep_ops,
+ },
+ [3] = {
+ .maxpacket = 512,
+ .name = "ep3in-int",
+ .ops = &ci_ep_ops,
+ },
+ [4] = {
+ .maxpacket = 512,
+ .name = "ep-",
+ .ops = &ci_ep_ops,
+ },
+};
+
+static struct ci_drv controller = {
+ .gadget = {
+ .name = "ci_udc",
+ .ops = &ci_udc_ops,
+ .is_dualspeed = 1,
+ .max_speed = USB_SPEED_HIGH,
+ },
+};
+
+/**
+ * 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)
+{
+ int index = (ep_num * 2) + dir_in;
+ uint8_t *imem = controller.items_mem + (index * ILIST_ENT_SZ);
+ return (struct ept_queue_item *)imem;
+}
+
+/**
+ * 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 unsigned long start = (unsigned long)head;
+ const unsigned long 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);
+ unsigned long start = (unsigned long)head;
+ unsigned long 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 unsigned long start = (unsigned long)item;
+ const unsigned long end = start + 2 * ILIST_ENT_SZ;
+
+ flush_dcache_range(start, end);
+}
+
+/**
+ * ci_flush_td - flush cache over queue item
+ * @td: td pointer
+ *
+ * This function flushes cache for particular transfer descriptor.
+ */
+static void ci_flush_td(struct ept_queue_item *td)
+{
+ const unsigned long start = (unsigned long)td;
+ const unsigned long end = (unsigned long)td + ILIST_ENT_SZ;
+ 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 unsigned long start = (unsigned long)item;
+ const unsigned long end = start + 2 * ILIST_ENT_SZ;
+
+ invalidate_dcache_range(start, end);
+}
+
+/**
+ * ci_invalidate_td - invalidate cache over queue item
+ * @td: td pointer
+ *
+ * This function invalidates cache for particular transfer descriptor.
+ */
+static void ci_invalidate_td(struct ept_queue_item *td)
+{
+ const unsigned long start = (unsigned long)td;
+ const unsigned long end = start + ILIST_ENT_SZ;
+ 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);
+ int num = -1;
+ struct ci_req *ci_req;
+
+ if (ci_ep->desc)
+ num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+ if (num == 0 && controller.ep0_req)
+ return &controller.ep0_req->req;
+
+ ci_req = calloc(1, sizeof(*ci_req));
+ if (!ci_req)
+ return NULL;
+
+ INIT_LIST_HEAD(&ci_req->queue);
+
+ if (num == 0)
+ controller.ep0_req = ci_req;
+
+ return &ci_req->req;
+}
+
+static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *req)
+{
+ struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
+ struct ci_req *ci_req = container_of(req, struct ci_req, req);
+ int num = -1;
+
+ if (ci_ep->desc)
+ num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+ if (num == 0) {
+ if (!controller.ep0_req)
+ return;
+ controller.ep0_req = 0;
+ }
+
+ if (ci_req->b_buf)
+ free(ci_req->b_buf);
+ free(ci_req);
+}
+
+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;
+ 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;
+ ep->desc = NULL;
+ return 0;
+}
+
+static int ci_bounce(struct ci_req *ci_req, int in)
+{
+ struct usb_request *req = &ci_req->req;
+ unsigned long addr = (unsigned long)req->buf;
+ unsigned long hwaddr;
+ uint32_t aligned_used_len;
+
+ /* Input buffer address is not aligned. */
+ if (addr & (ARCH_DMA_MINALIGN - 1))
+ goto align;
+
+ /* Input buffer length is not aligned. */
+ if (req->length & (ARCH_DMA_MINALIGN - 1))
+ goto align;
+
+ /* The buffer is well aligned, only flush cache. */
+ ci_req->hw_len = req->length;
+ ci_req->hw_buf = req->buf;
+ goto flush;
+
+align:
+ if (ci_req->b_buf && req->length > ci_req->b_len) {
+ free(ci_req->b_buf);
+ ci_req->b_buf = 0;
+ }
+ if (!ci_req->b_buf) {
+ ci_req->b_len = roundup(req->length, ARCH_DMA_MINALIGN);
+ ci_req->b_buf = memalign(ARCH_DMA_MINALIGN, ci_req->b_len);
+ if (!ci_req->b_buf)
+ return -ENOMEM;
+ }
+ ci_req->hw_len = ci_req->b_len;
+ ci_req->hw_buf = ci_req->b_buf;
+
+ if (in)
+ memcpy(ci_req->hw_buf, req->buf, req->length);
+
+flush:
+ hwaddr = (unsigned long)ci_req->hw_buf;
+ aligned_used_len = roundup(req->length, ARCH_DMA_MINALIGN);
+ flush_dcache_range(hwaddr, hwaddr + aligned_used_len);
+
+ return 0;
+}
+
+static void ci_debounce(struct ci_req *ci_req, int in)
+{
+ struct usb_request *req = &ci_req->req;
+ unsigned long addr = (unsigned long)req->buf;
+ unsigned long hwaddr = (unsigned long)ci_req->hw_buf;
+ uint32_t aligned_used_len;
+
+ if (in)
+ return;
+
+ aligned_used_len = roundup(req->actual, ARCH_DMA_MINALIGN);
+ invalidate_dcache_range(hwaddr, hwaddr + aligned_used_len);
+
+ if (addr == hwaddr)
+ return; /* not a bounce */
+
+ memcpy(req->buf, ci_req->hw_buf, req->actual);
+}
+
+static void ci_ep_submit_next_request(struct ci_ep *ci_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;
+ struct ci_req *ci_req;
+ u8 *buf;
+ uint32_t len_left, len_this_dtd;
+ struct ept_queue_item *dtd, *qtd;
+
+ ci_ep->req_primed = true;
+
+ 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);
+
+ ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue);
+ len = ci_req->req.length;
+
+ head->next = (unsigned long)item;
+ head->info = 0;
+
+ ci_req->dtd_count = 0;
+ buf = ci_req->hw_buf;
+ len_left = len;
+ dtd = item;
+
+ do {
+ len_this_dtd = min(len_left, (unsigned)EP_MAX_LENGTH_TRANSFER);
+
+ dtd->info = INFO_BYTES(len_this_dtd) | INFO_ACTIVE;
+ dtd->page0 = (unsigned long)buf;
+ dtd->page1 = ((unsigned long)buf & 0xfffff000) + 0x1000;
+ dtd->page2 = ((unsigned long)buf & 0xfffff000) + 0x2000;
+ dtd->page3 = ((unsigned long)buf & 0xfffff000) + 0x3000;
+ dtd->page4 = ((unsigned long)buf & 0xfffff000) + 0x4000;
+
+ len_left -= len_this_dtd;
+ buf += len_this_dtd;
+
+ if (len_left) {
+ qtd = (struct ept_queue_item *)
+ memalign(ILIST_ALIGN, ILIST_ENT_SZ);
+ dtd->next = (unsigned long)qtd;
+ dtd = qtd;
+ memset(dtd, 0, ILIST_ENT_SZ);
+ }
+
+ ci_req->dtd_count++;
+ } while (len_left);
+
+ item = dtd;
+ /*
+ * When sending the data for an IN transaction, the attached host
+ * knows that all data for the IN is sent when one of the following
+ * occurs:
+ * a) A zero-length packet is transmitted.
+ * b) A packet with length that isn't an exact multiple of the ep's
+ * maxpacket is transmitted.
+ * c) Enough data is sent to exactly fill the host's maximum expected
+ * IN transaction size.
+ *
+ * One of these conditions MUST apply at the end of an IN transaction,
+ * or the transaction will not be considered complete by the host. If
+ * none of (a)..(c) already applies, then we must force (a) to apply
+ * by explicitly sending an extra zero-length packet.
+ */
+ /* IN !a !b !c */
+ if (in && len && !(len % ci_ep->ep.maxpacket) && ci_req->req.zero) {
+ /*
+ * Each endpoint has 2 items allocated, even though typically
+ * only 1 is used at a time since either an IN or an OUT but
+ * not both is queued. For an IN transaction, item currently
+ * points at the second of these items, so we know that we
+ * can use the other to transmit the extra zero-length packet.
+ */
+ struct ept_queue_item *other_item = ci_get_qtd(num, 0);
+ item->next = (unsigned long)other_item;
+ item = other_item;
+ item->info = INFO_ACTIVE;
+ }
+
+ item->next = TERMINATE;
+ item->info |= INFO_IOC;
+
+ ci_flush_qtd(num);
+
+ item = (struct ept_queue_item *)(unsigned long)head->next;
+ while (item->next != TERMINATE) {
+ ci_flush_td((struct ept_queue_item *)(unsigned long)item->next);
+ item = (struct ept_queue_item *)(unsigned long)item->next;
+ }
+
+ DBG("ept%d %s queue len %x, req %p, buffer %p\n",
+ num, in ? "in" : "out", len, ci_req, ci_req->hw_buf);
+ ci_flush_qh(num);
+
+ if (in)
+ bit = EPT_TX(num);
+ else
+ bit = EPT_RX(num);
+
+ writel(bit, &udc->epprime);
+}
+
+static int ci_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct ci_ep *ci_ep = container_of(_ep, struct ci_ep, ep);
+ struct ci_req *ci_req;
+
+ list_for_each_entry(ci_req, &ci_ep->queue, queue) {
+ if (&ci_req->req == _req)
+ break;
+ }
+
+ if (&ci_req->req != _req)
+ return -EINVAL;
+
+ list_del_init(&ci_req->queue);
+
+ if (ci_req->req.status == -EINPROGRESS) {
+ ci_req->req.status = -ECONNRESET;
+ if (ci_req->req.complete)
+ ci_req->req.complete(_ep, _req);
+ }
+
+ return 0;
+}
+
+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_req *ci_req = container_of(req, struct ci_req, req);
+ int in, ret;
+ int __maybe_unused num;
+
+ num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
+
+ if (!num && ci_ep->req_primed) {
+ /*
+ * The flipping of ep0 between IN and OUT relies on
+ * ci_ep_queue consuming the current IN/OUT setting
+ * immediately. If this is deferred to a later point when the
+ * req is pulled out of ci_req->queue, then the IN/OUT setting
+ * may have been changed since the req was queued, and state
+ * will get out of sync. This condition doesn't occur today,
+ * but could if bugs were introduced later, and this error
+ * check will save a lot of debugging time.
+ */
+ printf("%s: ep0 transaction already in progress\n", __func__);
+ return -EPROTO;
+ }
+
+ ret = ci_bounce(ci_req, in);
+ if (ret)
+ return ret;
+
+ DBG("ept%d %s pre-queue req %p, buffer %p\n",
+ num, in ? "in" : "out", ci_req, ci_req->hw_buf);
+ list_add_tail(&ci_req->queue, &ci_ep->queue);
+
+ if (!ci_ep->req_primed)
+ ci_ep_submit_next_request(ci_ep);
+
+ return 0;
+}
+
+static void flip_ep0_direction(void)
+{
+ if (ep0_desc.bEndpointAddress == USB_DIR_IN) {
+ DBG("%s: Flipping ep0 to OUT\n", __func__);
+ ep0_desc.bEndpointAddress = 0;
+ } else {
+ DBG("%s: Flipping ep0 to IN\n", __func__);
+ ep0_desc.bEndpointAddress = USB_DIR_IN;
+ }
+}
+
+static void handle_ep_complete(struct ci_ep *ci_ep)
+{
+ struct ept_queue_item *item, *next_td;
+ int num, in, len, j;
+ struct ci_req *ci_req;
+
+ num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
+ item = ci_get_qtd(num, in);
+ ci_invalidate_qtd(num);
+ ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue);
+
+ next_td = item;
+ len = 0;
+ for (j = 0; j < ci_req->dtd_count; j++) {
+ ci_invalidate_td(next_td);
+ item = next_td;
+ len += (item->info >> 16) & 0x7fff;
+ if (item->info & 0xff)
+ printf("EP%d/%s FAIL info=%x pg0=%x\n",
+ num, in ? "in" : "out", item->info, item->page0);
+ if (j != ci_req->dtd_count - 1)
+ next_td = (struct ept_queue_item *)(unsigned long)
+ item->next;
+ if (j != 0)
+ free(item);
+ }
+
+ list_del_init(&ci_req->queue);
+ ci_ep->req_primed = false;
+
+ if (!list_empty(&ci_ep->queue))
+ ci_ep_submit_next_request(ci_ep);
+
+ ci_req->req.actual = ci_req->req.length - len;
+ ci_debounce(ci_req, in);
+
+ DBG("ept%d %s req %p, complete %x\n",
+ num, in ? "in" : "out", ci_req, len);
+ if (num != 0 || controller.ep0_data_phase)
+ ci_req->req.complete(&ci_ep->ep, &ci_req->req);
+ if (num == 0 && controller.ep0_data_phase) {
+ /*
+ * Data Stage is complete, so flip ep0 dir for Status Stage,
+ * which always transfers a packet in the opposite direction.
+ */
+ DBG("%s: flip ep0 dir for Status Stage\n", __func__);
+ flip_ep0_direction();
+ controller.ep0_data_phase = false;
+ ci_req->req.length = 0;
+ usb_ep_queue(&ci_ep->ep, &ci_req->req, 0);
+ }
+}
+
+#define SETUP(type, request) (((type) << 8) | (request))
+
+static void handle_setup(void)
+{
+ struct ci_ep *ci_ep = &controller.ep[0];
+ struct ci_req *ci_req;
+ struct usb_request *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;
+
+ ci_req = controller.ep0_req;
+ req = &ci_req->req;
+ 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 length %x\n",
+ reqname(r.bRequest), r.bRequestType, r.bRequest, r.wIndex,
+ r.wValue, r.wLength);
+
+ /* Set EP0 dir for Data Stage based on Setup Stage data */
+ if (r.bRequestType & USB_DIR_IN) {
+ DBG("%s: Set ep0 to IN for Data Stage\n", __func__);
+ ep0_desc.bEndpointAddress = USB_DIR_IN;
+ } else {
+ DBG("%s: Set ep0 to OUT for Data Stage\n", __func__);
+ ep0_desc.bEndpointAddress = 0;
+ }
+ if (r.wLength) {
+ controller.ep0_data_phase = true;
+ } else {
+ /* 0 length -> no Data Stage. Flip dir for Status Stage */
+ DBG("%s: 0 length: flip ep0 dir for Status Stage\n", __func__);
+ flip_ep0_direction();
+ controller.ep0_data_phase = false;
+ }
+
+ list_del_init(&ci_req->queue);
+ ci_ep->req_primed = false;
+
+ 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(int index)
+{
+ u32 value;
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+
+ value = readl(&udc->usbsts);
+ if (value)
+ udc_irq();
+
+ return value;
+}
+
+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_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);
+
+ ci_init_after_reset(controller.ctrl);
+
+ writel((unsigned long)controller.epts, &udc->epinitaddr);
+
+ /* select DEVICE mode */
+ writel(USBMODE_DEVICE, &udc->usbmode);
+
+#if !defined(CONFIG_USB_GADGET_DUALSPEED)
+ /* Port force Full-Speed Connect */
+ setbits_le32(&udc->portsc, PFSC);
+#endif
+
+ writel(0xffffffff, &udc->epflush);
+
+ /* Turn on the USB connection by enabling the pullup resistor */
+ setbits_le32(&udc->usbcmd, USBCMD_ITC(MICRO_8FRAME) |
+ USBCMD_RUN);
+ } else {
+ udc_disconnect();
+ }
+
+ return 0;
+}
+
+static int ci_udc_probe(void)
+{
+ struct ept_queue_head *head;
+ 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);
+
+ /* 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);
+
+ 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;
+
+ if (i & 1) {
+ ci_flush_qh(i / 2);
+ ci_flush_qtd(i / 2);
+ }
+ }
+
+ 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_desc;
+ INIT_LIST_HEAD(&controller.ep[0].queue);
+ controller.ep[0].req_primed = false;
+ controller.gadget.ep0 = &controller.ep[0].ep;
+ INIT_LIST_HEAD(&controller.gadget.ep0->ep_list);
+
+ /* Init EP 1..3 */
+ for (i = 1; i < 4; i++) {
+ memcpy(&controller.ep[i].ep, &ci_ep_init[i],
+ sizeof(*ci_ep_init));
+ INIT_LIST_HEAD(&controller.ep[i].queue);
+ controller.ep[i].req_primed = false;
+ list_add_tail(&controller.ep[i].ep.ep_list,
+ &controller.gadget.ep_list);
+ }
+
+ /* Init EP 4..n */
+ for (i = 4; i < NUM_ENDPOINTS; i++) {
+ memcpy(&controller.ep[i].ep, &ci_ep_init[4],
+ sizeof(*ci_ep_init));
+ INIT_LIST_HEAD(&controller.ep[i].queue);
+ controller.ep[i].req_primed = false;
+ list_add_tail(&controller.ep[i].ep.ep_list,
+ &controller.gadget.ep_list);
+ }
+
+ ci_ep_alloc_request(&controller.ep[0].ep, 0);
+ if (!controller.ep0_req) {
+ free(controller.items_mem);
+ free(controller.epts);
+ return -ENOMEM;
+ }
+
+ 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 CONFIG_IS_ENABLED(DM_USB)
+ ret = usb_setup_ehci_gadget(&controller.ctrl);
+#else
+ ret = usb_lowlevel_init(0, USB_INIT_DEVICE, (void **)&controller.ctrl);
+#endif
+ if (ret)
+ return ret;
+
+ ret = ci_udc_probe();
+ if (ret) {
+ DBG("udc probe failed, returned %d\n", ret);
+ return ret;
+ }
+
+ 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)
+{
+ udc_disconnect();
+
+ driver->unbind(&controller.gadget);
+ controller.driver = NULL;
+
+ ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req);
+ free(controller.items_mem);
+ free(controller.epts);
+
+#if CONFIG_IS_ENABLED(DM_USB)
+ usb_remove_ehci_gadget(&controller.ctrl);
+#else
+ usb_lowlevel_stop(0);
+ controller.ctrl = NULL;
+#endif
+
+ return 0;
+}
+
+bool dfu_usb_get_reset(void)
+{
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+
+ return !!(readl(&udc->usbsts) & STS_URI);
+}
diff --git a/roms/u-boot/drivers/usb/gadget/ci_udc.h b/roms/u-boot/drivers/usb/gadget/ci_udc.h
new file mode 100644
index 000000000..95cc07992
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/ci_udc.h
@@ -0,0 +1,153 @@
+/*
+ * 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_req {
+ struct usb_request req;
+ struct list_head queue;
+ /* Bounce buffer allocated if needed to align the transfer */
+ uint8_t *b_buf;
+ uint32_t b_len;
+ /* Buffer for the current transfer. Either req.buf/len or b_buf/len */
+ uint8_t *hw_buf;
+ uint32_t hw_len;
+ uint32_t dtd_count;
+};
+
+struct ci_ep {
+ struct usb_ep ep;
+ struct list_head queue;
+ bool req_primed;
+ const struct usb_endpoint_descriptor *desc;
+};
+
+struct ci_drv {
+ struct usb_gadget gadget;
+ struct ci_req *ep0_req;
+ bool ep0_data_phase;
+ struct usb_gadget_driver *driver;
+ struct ehci_ctrl *ctrl;
+ struct ept_queue_head *epts;
+ 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/roms/u-boot/drivers/usb/gadget/composite.c b/roms/u-boot/drivers/usb/gadget/composite.c
new file mode 100644
index 000000000..2a309e624
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/composite.c
@@ -0,0 +1,1531 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * composite.c - infrastructure for Composite USB Gadgets
+ *
+ * Copyright (C) 2006-2008 David Brownell
+ * U-Boot porting: Lukasz Majewski <l.majewski@samsung.com>
+ */
+#undef DEBUG
+
+#include <log.h>
+#include <dm/devres.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/usb/composite.h>
+#include "u_os_desc.h"
+
+#define USB_BUFSIZ 4096
+
+/* Helper type for accessing packed u16 pointers */
+typedef struct { __le16 val; } __packed __le16_packed;
+
+static struct usb_composite_driver *composite;
+static struct usb_configuration *os_desc_config;
+
+/* Microsoft OS String Descriptor */
+static char qw_sign_buf[OS_STRING_QW_SIGN_LEN / 2] = {'M', 'S', 'F', 'T', '1', '0', '0'};
+
+static inline void le16_add_cpu_packed(__le16_packed *var, u16 val)
+{
+ var->val = cpu_to_le16(le16_to_cpu(var->val) + val);
+}
+
+/**
+ * struct usb_os_string - represents OS String to be reported by a gadget
+ * @bLength: total length of the entire descritor, always 0x12
+ * @bDescriptorType: USB_DT_STRING
+ * @qwSignature: the OS String proper
+ * @bMS_VendorCode: code used by the host for subsequent requests
+ * @bPad: not used, must be zero
+ */
+struct usb_os_string {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 qwSignature[OS_STRING_QW_SIGN_LEN];
+ __u8 bMS_VendorCode;
+ __u8 bPad;
+} __packed;
+
+/**
+ * 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;
+ if (!config->superspeed && function->ss_descriptors)
+ config->superspeed = 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;
+ 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_SUPER)
+ descriptors = f->ss_descriptors;
+ else 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;
+ struct list_head *pos;
+
+ if (gadget_is_superspeed(gadget)) {
+ speed = gadget->speed;
+ } else 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;
+
+ pos = &cdev->configs;
+ c = cdev->os_desc_config;
+ if (c)
+ goto check_config;
+
+ while ((pos = pos->next) != &cdev->configs) {
+ c = list_entry(pos, typeof(*c), list);
+
+ /* skip OS Descriptors config which is handled separately */
+ if (c == cdev->os_desc_config)
+ continue;
+
+check_config:
+ if (speed == USB_SPEED_SUPER) {
+ if (!c->superspeed)
+ continue;
+ } else 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;
+ int ss = 0;
+ struct usb_configuration *c;
+
+ if (gadget->speed == USB_SPEED_SUPER)
+ ss = 1;
+
+ 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 (ss) {
+ if (!c->superspeed)
+ continue;
+ } else 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->gadget->ep0->maxpacket;
+ 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;
+ case USB_SPEED_SUPER:
+ speed = "super";
+ 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_SUPER)
+ descriptors = f->ss_descriptors;
+ else 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);
+ generic_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%s\n",
+ config->bConfigurationValue, config,
+ config->superspeed ? " super" : "",
+ 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);
+ }
+ }
+
+ /*
+ * If one function of config is not super speed capable,
+ * force the gadget to be high speed so controller driver
+ * can init HW to be USB 2.0
+ */
+ if (gadget_is_superspeed(cdev->gadget)) {
+ list_for_each_entry(f, &config->functions, list) {
+ if (!f->ss_descriptors)
+ cdev->gadget->max_speed =
+ USB_SPEED_HIGH;
+ }
+ }
+
+ usb_ep_autoconfig_reset(cdev->gadget);
+
+ os_desc_config = config;
+ cdev->os_desc_config = os_desc_config;
+
+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, void *buf)
+{
+ const struct usb_gadget_strings *s;
+ u16 language;
+ __le16_packed *tmp;
+ __le16_packed *end = (buf + 252);
+
+ while (*sp) {
+ s = *sp;
+ language = cpu_to_le16(s->language);
+ for (tmp = buf; tmp->val && tmp < end; tmp++) {
+ if (tmp->val == language)
+ goto repeat;
+ }
+ tmp->val = 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;
+ }
+
+ if (cdev->use_os_string && language == 0 && id == OS_STRING_IDX) {
+ struct usb_os_string *b = buf;
+ b->bLength = sizeof(*b);
+ b->bDescriptorType = USB_DT_STRING;
+ memcpy(&b->qwSignature, cdev->qw_sign, sizeof(b->qwSignature));
+ b->bMS_VendorCode = cdev->b_vendor_code;
+ b->bPad = 0;
+ return sizeof(*b);
+ }
+
+ /*
+ * 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);
+}
+
+static int bos_desc(struct usb_composite_dev *cdev)
+{
+ struct usb_ext_cap_descriptor *usb_ext;
+ struct usb_dcd_config_params dcd_config_params;
+ struct usb_bos_descriptor *bos = cdev->req->buf;
+
+ bos->bLength = USB_DT_BOS_SIZE;
+ bos->bDescriptorType = USB_DT_BOS;
+
+ bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE);
+ bos->bNumDeviceCaps = 0;
+
+ /*
+ * A SuperSpeed device shall include the USB2.0 extension descriptor
+ * and shall support LPM when operating in USB2.0 HS mode.
+ */
+ usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu_packed((__le16_packed *)&bos->wTotalLength,
+ USB_DT_USB_EXT_CAP_SIZE);
+ usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
+ usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
+ usb_ext->bmAttributes =
+ cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT);
+
+ /*
+ * The Superspeed USB Capability descriptor shall be implemented
+ * by all SuperSpeed devices.
+ */
+ if (gadget_is_superspeed(cdev->gadget)) {
+ struct usb_ss_cap_descriptor *ss_cap;
+
+ ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu_packed((__le16_packed *)&bos->wTotalLength,
+ USB_DT_USB_SS_CAP_SIZE);
+ ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
+ ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
+ ss_cap->bmAttributes = 0; /* LTM is not supported yet */
+ ss_cap->wSpeedSupported =
+ cpu_to_le16(USB_LOW_SPEED_OPERATION |
+ USB_FULL_SPEED_OPERATION |
+ USB_HIGH_SPEED_OPERATION |
+ USB_5GBPS_OPERATION);
+ ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
+
+ /* Get Controller configuration */
+ if (cdev->gadget->ops->get_config_params) {
+ cdev->gadget->ops->get_config_params(
+ &dcd_config_params);
+ } else {
+ dcd_config_params.bU1devExitLat =
+ USB_DEFAULT_U1_DEV_EXIT_LAT;
+ dcd_config_params.bU2DevExitLat =
+ cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ }
+ ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+ ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
+ }
+ return le16_to_cpu(bos->wTotalLength);
+}
+
+static int count_ext_compat(struct usb_configuration *c)
+{
+ int i, res;
+
+ res = 0;
+ for (i = 0; i < c->next_interface_id; ++i) {
+ struct usb_function *f;
+ int j;
+
+ f = c->interface[i];
+ for (j = 0; j < f->os_desc_n; ++j) {
+ struct usb_os_desc *d;
+
+ if (i != f->os_desc_table[j].if_id)
+ continue;
+ d = f->os_desc_table[j].os_desc;
+ if (d && d->ext_compat_id)
+ ++res;
+ }
+ }
+ BUG_ON(res > 255);
+ return res;
+}
+
+static void fill_ext_compat(struct usb_configuration *c, u8 *buf)
+{
+ int i, count;
+
+ count = 16;
+ for (i = 0; i < c->next_interface_id; ++i) {
+ struct usb_function *f;
+ int j;
+
+ f = c->interface[i];
+ for (j = 0; j < f->os_desc_n; ++j) {
+ struct usb_os_desc *d;
+
+ if (i != f->os_desc_table[j].if_id)
+ continue;
+ d = f->os_desc_table[j].os_desc;
+ if (d && d->ext_compat_id) {
+ *buf++ = i;
+ *buf++ = 0x01;
+ memcpy(buf, d->ext_compat_id, 16);
+ buf += 22;
+ } else {
+ ++buf;
+ *buf = 0x01;
+ buf += 23;
+ }
+ count += 24;
+ if (count >= 4096)
+ return;
+ }
+ }
+}
+
+static int count_ext_prop(struct usb_configuration *c, int interface)
+{
+ struct usb_function *f;
+ int j;
+
+ f = c->interface[interface];
+ for (j = 0; j < f->os_desc_n; ++j) {
+ struct usb_os_desc *d;
+
+ if (interface != f->os_desc_table[j].if_id)
+ continue;
+ d = f->os_desc_table[j].os_desc;
+ if (d && d->ext_compat_id)
+ return d->ext_prop_count;
+ }
+ return 0;
+}
+
+static int len_ext_prop(struct usb_configuration *c, int interface)
+{
+ struct usb_function *f;
+ struct usb_os_desc *d;
+ int j, res;
+
+ res = 10; /* header length */
+ f = c->interface[interface];
+ for (j = 0; j < f->os_desc_n; ++j) {
+ if (interface != f->os_desc_table[j].if_id)
+ continue;
+ d = f->os_desc_table[j].os_desc;
+ if (d)
+ return min(res + d->ext_prop_len, 4096);
+ }
+ return res;
+}
+
+static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
+{
+ struct usb_function *f;
+ struct usb_os_desc *d;
+ struct usb_os_desc_ext_prop *ext_prop;
+ int j, count, n, ret;
+ u8 *start = buf;
+
+ f = c->interface[interface];
+ for (j = 0; j < f->os_desc_n; ++j) {
+ if (interface != f->os_desc_table[j].if_id)
+ continue;
+ d = f->os_desc_table[j].os_desc;
+ if (d)
+ list_for_each_entry(ext_prop, &d->ext_prop, entry) {
+ /* 4kB minus header length */
+ n = buf - start;
+ if (n >= 4086)
+ return 0;
+
+ count = ext_prop->data_len +
+ ext_prop->name_len + 14;
+ if (count > 4086 - n)
+ return -EINVAL;
+ usb_ext_prop_put_size(buf, count);
+ usb_ext_prop_put_type(buf, ext_prop->type);
+ ret = usb_ext_prop_put_name(buf, ext_prop->name,
+ ext_prop->name_len);
+ if (ret < 0)
+ return ret;
+ switch (ext_prop->type) {
+ case USB_EXT_PROP_UNICODE:
+ case USB_EXT_PROP_UNICODE_ENV:
+ case USB_EXT_PROP_UNICODE_LINK:
+ usb_ext_prop_put_unicode(buf, ret,
+ ext_prop->data,
+ ext_prop->data_len);
+ break;
+ case USB_EXT_PROP_BINARY:
+ usb_ext_prop_put_binary(buf, ret,
+ ext_prop->data,
+ ext_prop->data_len);
+ break;
+ case USB_EXT_PROP_LE32:
+ /* not implemented */
+ case USB_EXT_PROP_BE32:
+ /* not implemented */
+ default:
+ return -EINVAL;
+ }
+ buf += count;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * 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);
+
+ cdev->desc.bMaxPacketSize0 =
+ cdev->gadget->ep0->maxpacket;
+ if (gadget->speed >= USB_SPEED_SUPER) {
+ cdev->desc.bcdUSB = cpu_to_le16(0x0310);
+ cdev->desc.bMaxPacketSize0 = 9;
+ } else {
+ cdev->desc.bcdUSB = cpu_to_le16(0x0200);
+ }
+ 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) ||
+ gadget->speed >= USB_SPEED_SUPER)
+ break;
+ device_qual(cdev);
+ value = min_t(int, w_length,
+ sizeof(struct usb_qualifier_descriptor));
+ break;
+ case USB_DT_OTHER_SPEED_CONFIG:
+ if (!gadget_is_dualspeed(gadget) ||
+ gadget->speed >= USB_SPEED_SUPER)
+ 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;
+ case USB_DT_BOS:
+ /*
+ * Super speed connection should support BOS, and
+ * USB compliance test (USB 2.0 Command Verifier)
+ * also issues this request, return for now for
+ * USB 2.0 connection.
+ */
+ if (gadget->speed >= USB_SPEED_SUPER) {
+ value = bos_desc(cdev);
+ 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:
+ /*
+ * OS descriptors handling
+ */
+ if (CONFIG_IS_ENABLED(USB_GADGET_OS_DESCRIPTORS) && cdev->use_os_string &&
+ cdev->os_desc_config && (ctrl->bRequestType & USB_TYPE_VENDOR) &&
+ ctrl->bRequest == cdev->b_vendor_code) {
+ struct usb_configuration *os_desc_cfg;
+ u8 *buf;
+ int interface;
+ int count = 0;
+
+ buf = req->buf;
+ os_desc_cfg = cdev->os_desc_config;
+ memset(buf, 0, w_length);
+ buf[5] = 0x01;
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ if (w_index != 0x4 || (w_value >> 8))
+ break;
+ buf[6] = w_index;
+ if (w_length == 0x10) {
+ /* Number of ext compat interfaces */
+ count = count_ext_compat(os_desc_cfg);
+ buf[8] = count;
+ count *= 24; /* 24 B/ext compat desc */
+ count += 16; /* header */
+ put_unaligned_le32(count, buf);
+ value = w_length;
+ } else {
+ /* "extended compatibility ID"s */
+ count = count_ext_compat(os_desc_cfg);
+ buf[8] = count;
+ count *= 24; /* 24 B/ext compat desc */
+ count += 16; /* header */
+ put_unaligned_le32(count, buf);
+ buf += 16;
+ fill_ext_compat(os_desc_cfg, buf);
+ value = w_length;
+ }
+ break;
+ case USB_RECIP_INTERFACE:
+ if (w_index != 0x5 || (w_value >> 8))
+ break;
+ interface = w_value & 0xFF;
+ buf[6] = w_index;
+ if (w_length == 0x0A) {
+ count = count_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le16(count, buf + 8);
+ count = len_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le32(count, buf);
+
+ value = w_length;
+ } else {
+ count = count_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le16(count, buf + 8);
+ count = len_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le32(count, buf);
+ buf += 10;
+ value = fill_ext_prop(os_desc_cfg,
+ interface, buf);
+ if (value < 0)
+ return value;
+
+ value = w_length;
+ }
+ break;
+ }
+
+ 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);
+ }
+ }
+ return value;
+ }
+
+ debug("non-core control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+
+ if (!cdev->config)
+ goto done;
+
+ /*
+ * 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->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.
+ */
+#ifdef __UBOOT__
+ assert_noisy(!cdev->config);
+#else
+ BUG_ON(cdev->config);
+#endif
+
+ 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);
+ }
+ free(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;
+
+ if (cdev->use_os_string) {
+ /* TODO: Do we want to pass this via platform? */
+ cdev->b_vendor_code = 0x40;
+
+ /* Microsoft OS String Descriptor */
+ utf8_to_utf16le(qw_sign_buf, (__le16 *)cdev->qw_sign,
+ OS_STRING_QW_SIGN_LEN / 2);
+ }
+
+ 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_SUPER,
+
+ .bind = composite_bind,
+ .unbind = composite_unbind,
+
+ .setup = composite_setup,
+ .reset = composite_disconnect,
+ .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)
+{
+ int res;
+
+ if (!driver || !driver->dev || !driver->bind || composite)
+ return -EINVAL;
+
+ if (!driver->name)
+ driver->name = "composite";
+ composite = driver;
+
+ res = usb_gadget_register_driver(&composite_driver);
+ if (res != 0)
+ composite = NULL;
+
+ return res;
+}
+
+/**
+ * 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/roms/u-boot/drivers/usb/gadget/config.c b/roms/u-boot/drivers/usb/gadget/config.c
new file mode 100644
index 000000000..e96782644
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/config.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * usb/gadget/config.c -- simplify building config descriptors
+ *
+ * Copyright (C) 2003 David Brownell
+ *
+ * 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 <linux/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/roms/u-boot/drivers/usb/gadget/core.c b/roms/u-boot/drivers/usb/gadget/core.c
new file mode 100644
index 000000000..888f0cfea
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/core.c
@@ -0,0 +1,633 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (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>
+ */
+
+#include <log.h>
+#include <malloc.h>
+#include <serial.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;
+
+__maybe_unused static 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",
+};
+
+__maybe_unused static char *usbd_device_status[] = {
+ "USBD_OPENING",
+ "USBD_OK",
+ "USBD_SUSPENDED",
+ "USBD_CLOSING",
+};
+
+#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN")
+
+
+/* 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/roms/u-boot/drivers/usb/gadget/designware_udc.c b/roms/u-boot/drivers/usb/gadget/designware_udc.c
new file mode 100644
index 000000000..7fc5d27d4
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/designware_udc.c
@@ -0,0 +1,1021 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 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.
+ */
+
+#include <common.h>
+#include <serial.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+
+#include <env.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_t(u32, 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 = env_get("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/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg.c b/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg.c
new file mode 100644
index 000000000..2f3181444
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg.c
@@ -0,0 +1,1199 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * drivers/usb/gadget/dwc2_udc_otg.c
+ * Designware DWC2 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>
+ */
+#undef DEBUG
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <log.h>
+#include <malloc.h>
+#include <reset.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+
+#include <linux/errno.h>
+#include <linux/list.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+
+#include <phys2bus.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <asm/io.h>
+
+#include <asm/mach-types.h>
+
+#include <power/regulator.h>
+
+#include "dwc2_udc_otg_regs.h"
+#include "dwc2_udc_otg_priv.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/dwc2_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_VERSION "15 March 2009"
+
+struct dwc2_udc *the_controller;
+
+static const char driver_name[] = "dwc2-udc";
+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 dwc2_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *);
+static int dwc2_ep_disable(struct usb_ep *ep);
+static struct usb_request *dwc2_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags);
+static void dwc2_free_request(struct usb_ep *ep, struct usb_request *);
+
+static int dwc2_queue(struct usb_ep *ep, struct usb_request *, gfp_t gfp_flags);
+static int dwc2_dequeue(struct usb_ep *ep, struct usb_request *);
+static int dwc2_fifo_status(struct usb_ep *ep);
+static void dwc2_fifo_flush(struct usb_ep *ep);
+static void dwc2_ep0_read(struct dwc2_udc *dev);
+static void dwc2_ep0_kick(struct dwc2_udc *dev, struct dwc2_ep *ep);
+static void dwc2_handle_ep0(struct dwc2_udc *dev);
+static int dwc2_ep0_write(struct dwc2_udc *dev);
+static int write_fifo_ep0(struct dwc2_ep *ep, struct dwc2_request *req);
+static void done(struct dwc2_ep *ep, struct dwc2_request *req, int status);
+static void stop_activity(struct dwc2_udc *dev,
+ struct usb_gadget_driver *driver);
+static int udc_enable(struct dwc2_udc *dev);
+static void udc_set_address(struct dwc2_udc *dev, unsigned char address);
+static void reconfig_usbd(struct dwc2_udc *dev);
+static void set_max_pktsize(struct dwc2_udc *dev, enum usb_device_speed speed);
+static void nuke(struct dwc2_ep *ep, int status);
+static int dwc2_udc_set_halt(struct usb_ep *_ep, int value);
+static void dwc2_udc_set_nak(struct dwc2_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 dwc2_ep_ops = {
+ .enable = dwc2_ep_enable,
+ .disable = dwc2_ep_disable,
+
+ .alloc_request = dwc2_alloc_request,
+ .free_request = dwc2_free_request,
+
+ .queue = dwc2_queue,
+ .dequeue = dwc2_dequeue,
+
+ .set_halt = dwc2_udc_set_halt,
+ .fifo_status = dwc2_fifo_status,
+ .fifo_flush = dwc2_fifo_flush,
+};
+
+#define create_proc_files() do {} while (0)
+#define remove_proc_files() do {} while (0)
+
+/***********************************************************/
+
+struct dwc2_usbotg_reg *reg;
+
+bool dfu_usb_get_reset(void)
+{
+ return !!(readl(&reg->gintsts) & INT_RESET);
+}
+
+__weak void otg_phy_init(struct dwc2_udc *dev) {}
+__weak void otg_phy_off(struct dwc2_udc *dev) {}
+
+/***********************************************************/
+
+#include "dwc2_udc_otg_xfer_dma.c"
+
+/*
+ * udc_disable - disable USB device controller
+ */
+static void udc_disable(struct dwc2_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 dwc2_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 < DWC2_MAX_ENDPOINTS; i++) {
+ struct dwc2_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 dwc2_udc *dev)
+{
+ debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
+
+ otg_phy_init(dev);
+ reconfig_usbd(dev);
+
+ debug_cond(DEBUG_SETUP != 0,
+ "DWC2 USB 2.0 OTG Controller Core Initialized : 0x%x\n",
+ readl(&reg->gintmsk));
+
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ return 0;
+}
+
+#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
+/*
+ Register entry point for the peripheral controller driver.
+*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct dwc2_udc *dev = the_controller;
+ int retval = 0;
+ unsigned long flags = 0;
+
+ debug_cond(DEBUG_SETUP != 0, "%s: %s\n", __func__, "no name");
+
+ if (!driver || driver->speed < USB_SPEED_FULL
+ || !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 dwc2_udc *dev = the_controller;
+ unsigned long flags = 0;
+
+ 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;
+}
+#else /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */
+
+static int dwc2_gadget_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+ struct dwc2_udc *dev = the_controller;
+
+ debug_cond(DEBUG_SETUP != 0, "%s: %s\n", __func__, "no name");
+
+ if (!driver || driver->speed < USB_SPEED_FULL ||
+ !driver->bind || !driver->disconnect || !driver->setup)
+ return -EINVAL;
+
+ if (!dev)
+ return -ENODEV;
+
+ if (dev->driver)
+ return -EBUSY;
+
+ /* first hook up the driver ... */
+ dev->driver = driver;
+
+ debug_cond(DEBUG_SETUP != 0,
+ "Registered gadget driver %s\n", dev->gadget.name);
+ return udc_enable(dev);
+}
+
+static int dwc2_gadget_stop(struct usb_gadget *g)
+{
+ struct dwc2_udc *dev = the_controller;
+
+ if (!dev)
+ return -ENODEV;
+
+ if (!dev->driver)
+ return -EINVAL;
+
+ dev->driver = 0;
+ stop_activity(dev, dev->driver);
+
+ udc_disable(dev);
+
+ return 0;
+}
+
+#endif /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */
+
+/*
+ * done - retire a request; caller blocked irqs
+ */
+static void done(struct dwc2_ep *ep, struct dwc2_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 dwc2_ep *ep, int status)
+{
+ struct dwc2_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 dwc2_request, queue);
+ done(ep, req, status);
+ }
+}
+
+static void stop_activity(struct dwc2_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 < DWC2_MAX_ENDPOINTS; i++) {
+ struct dwc2_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(struct dwc2_udc *dev)
+{
+ /* 2. Soft-reset OTG Core and then unreset again. */
+ int i;
+ unsigned int uTemp = writel(CORE_SOFT_RESET, &reg->grstctl);
+ uint32_t dflt_gusbcfg;
+ uint32_t rx_fifo_sz, tx_fifo_sz, np_tx_fifo_sz;
+ u32 max_hw_ep;
+ int pdata_hw_ep;
+
+ debug("Reseting OTG controller\n");
+
+ dflt_gusbcfg =
+ 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*/
+#ifdef CONFIG_USB_GADGET_DWC2_OTG_PHY_BUS_WIDTH_8
+ |0<<3 /* phy i/f 0:8bit, 1:16bit*/
+#else
+ |1<<3 /* phy i/f 0:8bit, 1:16bit*/
+#endif
+ |0x7<<0; /* HS/FS Timeout**/
+
+ if (dev->pdata->usb_gusbcfg)
+ dflt_gusbcfg = dev->pdata->usb_gusbcfg;
+
+ writel(dflt_gusbcfg, &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 < DWC2_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);
+
+ rx_fifo_sz = RX_FIFO_SIZE;
+ np_tx_fifo_sz = NPTX_FIFO_SIZE;
+ tx_fifo_sz = PTX_FIFO_SIZE;
+
+ if (dev->pdata->rx_fifo_sz)
+ rx_fifo_sz = dev->pdata->rx_fifo_sz;
+ if (dev->pdata->np_tx_fifo_sz)
+ np_tx_fifo_sz = dev->pdata->np_tx_fifo_sz;
+ if (dev->pdata->tx_fifo_sz)
+ tx_fifo_sz = dev->pdata->tx_fifo_sz;
+
+ /* 11. Set Rx FIFO Size (in 32-bit words) */
+ writel(rx_fifo_sz, &reg->grxfsiz);
+
+ /* 12. Set Non Periodic Tx FIFO Size */
+ writel((np_tx_fifo_sz << 16) | rx_fifo_sz,
+ &reg->gnptxfsiz);
+
+ /* retrieve the number of IN Endpoints (excluding ep0) */
+ max_hw_ep = (readl(&reg->ghwcfg4) & GHWCFG4_NUM_IN_EPS_MASK) >>
+ GHWCFG4_NUM_IN_EPS_SHIFT;
+ pdata_hw_ep = dev->pdata->tx_fifo_sz_nb;
+
+ /* tx_fifo_sz_nb should equal to number of IN Endpoint */
+ if (pdata_hw_ep && max_hw_ep != pdata_hw_ep)
+ pr_warn("Got %d hw endpoint but %d tx-fifo-size in array !!\n",
+ max_hw_ep, pdata_hw_ep);
+
+ for (i = 0; i < max_hw_ep; i++) {
+ if (pdata_hw_ep)
+ tx_fifo_sz = dev->pdata->tx_fifo_sz_array[i];
+
+ writel((rx_fifo_sz + np_tx_fifo_sz + (tx_fifo_sz * i)) |
+ tx_fifo_sz << 16, &reg->dieptxf[i]);
+ }
+ /* Flush the RX FIFO */
+ writel(RX_FIFO_FLUSH, &reg->grstctl);
+ while (readl(&reg->grstctl) & RX_FIFO_FLUSH)
+ debug("%s: waiting for DWC2_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 DWC2_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 dwc2_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 < DWC2_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 dwc2_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct dwc2_ep *ep;
+ struct dwc2_udc *dev;
+ unsigned long flags = 0;
+
+ debug("%s: %p\n", __func__, _ep);
+
+ ep = container_of(_ep, struct dwc2_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 */
+ dwc2_udc_set_nak(ep);
+ dwc2_udc_set_halt(_ep, 0);
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ dwc2_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 dwc2_ep_disable(struct usb_ep *_ep)
+{
+ struct dwc2_ep *ep;
+ unsigned long flags = 0;
+
+ debug("%s: %p\n", __func__, _ep);
+
+ ep = container_of(_ep, struct dwc2_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 *dwc2_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct dwc2_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 dwc2_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+ struct dwc2_request *req;
+
+ debug("%s: %p\n", __func__, ep);
+
+ req = container_of(_req, struct dwc2_request, req);
+ WARN_ON(!list_empty(&req->queue));
+ kfree(req);
+}
+
+/* dequeue JUST ONE request */
+static int dwc2_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct dwc2_ep *ep;
+ struct dwc2_request *req;
+ unsigned long flags = 0;
+
+ debug("%s: %p\n", __func__, _ep);
+
+ ep = container_of(_ep, struct dwc2_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 dwc2_fifo_status(struct usb_ep *_ep)
+{
+ int count = 0;
+ struct dwc2_ep *ep;
+
+ ep = container_of(_ep, struct dwc2_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 dwc2_fifo_flush(struct usb_ep *_ep)
+{
+ struct dwc2_ep *ep;
+
+ ep = container_of(_ep, struct dwc2_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 dwc2_udc_ops = {
+ /* current versions must always be self-powered */
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+ .udc_start = dwc2_gadget_start,
+ .udc_stop = dwc2_gadget_stop,
+#endif
+};
+
+static struct dwc2_udc memory = {
+ .usb_address = 0,
+ .gadget = {
+ .ops = &dwc2_udc_ops,
+ .ep0 = &memory.ep[0].ep,
+ .name = driver_name,
+ },
+
+ /* control endpoint */
+ .ep[0] = {
+ .ep = {
+ .name = ep0name,
+ .ops = &dwc2_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 = &dwc2_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 = &dwc2_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 = &dwc2_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 dwc2_udc_probe(struct dwc2_plat_otg_data *pdata)
+{
+ struct dwc2_udc *dev = &memory;
+ int retval = 0;
+
+ debug("%s: %p\n", __func__, pdata);
+
+ dev->pdata = pdata;
+
+ reg = (struct dwc2_usbotg_reg *)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) {
+ pr_err("No memory available for UDC!\n");
+ return -ENOMEM;
+ }
+
+ usb_ctrl_dma_addr = (dma_addr_t) usb_ctrl;
+
+ udc_reinit(dev);
+
+ return retval;
+}
+
+int dwc2_udc_handle_interrupt(void)
+{
+ u32 intr_status = readl(&reg->gintsts);
+ u32 gintmsk = readl(&reg->gintmsk);
+
+ if (intr_status & gintmsk)
+ return dwc2_udc_irq(1, (void *)the_controller);
+
+ return 0;
+}
+
+#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
+
+int usb_gadget_handle_interrupts(int index)
+{
+ return dwc2_udc_handle_interrupt();
+}
+
+#else /* CONFIG_IS_ENABLED(DM_USB_GADGET) */
+
+struct dwc2_priv_data {
+ struct clk_bulk clks;
+ struct reset_ctl_bulk resets;
+ struct phy_bulk phys;
+ struct udevice *usb33d_supply;
+};
+
+int dm_usb_gadget_handle_interrupts(struct udevice *dev)
+{
+ return dwc2_udc_handle_interrupt();
+}
+
+static int dwc2_phy_setup(struct udevice *dev, struct phy_bulk *phys)
+{
+ int ret;
+
+ ret = generic_phy_get_bulk(dev, phys);
+ if (ret)
+ return ret;
+
+ ret = generic_phy_init_bulk(phys);
+ if (ret)
+ return ret;
+
+ ret = generic_phy_power_on_bulk(phys);
+ if (ret)
+ generic_phy_exit_bulk(phys);
+
+ return ret;
+}
+
+static void dwc2_phy_shutdown(struct udevice *dev, struct phy_bulk *phys)
+{
+ generic_phy_power_off_bulk(phys);
+ generic_phy_exit_bulk(phys);
+}
+
+static int dwc2_udc_otg_of_to_plat(struct udevice *dev)
+{
+ struct dwc2_plat_otg_data *plat = dev_get_plat(dev);
+ ulong drvdata;
+ void (*set_params)(struct dwc2_plat_otg_data *data);
+ int ret;
+
+ if (usb_get_dr_mode(dev_ofnode(dev)) != USB_DR_MODE_PERIPHERAL &&
+ usb_get_dr_mode(dev_ofnode(dev)) != USB_DR_MODE_OTG) {
+ dev_dbg(dev, "Invalid mode\n");
+ return -ENODEV;
+ }
+
+ plat->regs_otg = dev_read_addr(dev);
+
+ plat->rx_fifo_sz = dev_read_u32_default(dev, "g-rx-fifo-size", 0);
+ plat->np_tx_fifo_sz = dev_read_u32_default(dev, "g-np-tx-fifo-size", 0);
+
+ plat->tx_fifo_sz_nb =
+ dev_read_size(dev, "g-tx-fifo-size") / sizeof(u32);
+ if (plat->tx_fifo_sz_nb > DWC2_MAX_HW_ENDPOINTS)
+ plat->tx_fifo_sz_nb = DWC2_MAX_HW_ENDPOINTS;
+ if (plat->tx_fifo_sz_nb) {
+ ret = dev_read_u32_array(dev, "g-tx-fifo-size",
+ plat->tx_fifo_sz_array,
+ plat->tx_fifo_sz_nb);
+ if (ret)
+ return ret;
+ }
+
+ plat->force_b_session_valid =
+ dev_read_bool(dev, "u-boot,force-b-session-valid");
+
+ plat->force_vbus_detection =
+ dev_read_bool(dev, "u-boot,force-vbus-detection");
+
+ /* force plat according compatible */
+ drvdata = dev_get_driver_data(dev);
+ if (drvdata) {
+ set_params = (void *)drvdata;
+ set_params(plat);
+ }
+
+ return 0;
+}
+
+static void dwc2_set_stm32mp1_hsotg_params(struct dwc2_plat_otg_data *p)
+{
+ p->activate_stm_id_vb_detection = true;
+ p->usb_gusbcfg =
+ 0 << 15 /* PHY Low Power Clock sel*/
+ | 0x9 << 10 /* USB Turnaround time (0x9 for HS phy) */
+ | 0 << 9 /* [0:HNP disable,1:HNP enable]*/
+ | 0 << 8 /* [0:SRP disable 1:SRP enable]*/
+ | 0 << 6 /* 0: high speed utmi+, 1: full speed serial*/
+ | 0x7 << 0; /* FS timeout calibration**/
+
+ if (p->force_b_session_valid)
+ p->usb_gusbcfg |= 1 << 30; /* FDMOD: Force device mode */
+}
+
+static int dwc2_udc_otg_reset_init(struct udevice *dev,
+ struct reset_ctl_bulk *resets)
+{
+ int ret;
+
+ ret = reset_get_bulk(dev, resets);
+ if (ret == -ENOTSUPP || ret == -ENOENT)
+ return 0;
+
+ if (ret)
+ return ret;
+
+ ret = reset_assert_bulk(resets);
+
+ if (!ret) {
+ udelay(2);
+ ret = reset_deassert_bulk(resets);
+ }
+ if (ret) {
+ reset_release_bulk(resets);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dwc2_udc_otg_clk_init(struct udevice *dev,
+ struct clk_bulk *clks)
+{
+ int ret;
+
+ ret = clk_get_bulk(dev, clks);
+ if (ret == -ENOSYS)
+ return 0;
+
+ if (ret)
+ return ret;
+
+ ret = clk_enable_bulk(clks);
+ if (ret) {
+ clk_release_bulk(clks);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dwc2_udc_otg_probe(struct udevice *dev)
+{
+ struct dwc2_plat_otg_data *plat = dev_get_plat(dev);
+ struct dwc2_priv_data *priv = dev_get_priv(dev);
+ struct dwc2_usbotg_reg *usbotg_reg =
+ (struct dwc2_usbotg_reg *)plat->regs_otg;
+ int ret;
+
+ ret = dwc2_udc_otg_clk_init(dev, &priv->clks);
+ if (ret)
+ return ret;
+
+ ret = dwc2_udc_otg_reset_init(dev, &priv->resets);
+ if (ret)
+ return ret;
+
+ ret = dwc2_phy_setup(dev, &priv->phys);
+ if (ret)
+ return ret;
+
+ if (plat->activate_stm_id_vb_detection) {
+ if (CONFIG_IS_ENABLED(DM_REGULATOR) &&
+ (!plat->force_b_session_valid ||
+ plat->force_vbus_detection)) {
+ ret = device_get_supply_regulator(dev, "usb33d-supply",
+ &priv->usb33d_supply);
+ if (ret) {
+ dev_err(dev, "can't get voltage level detector supply\n");
+ return ret;
+ }
+ ret = regulator_set_enable(priv->usb33d_supply, true);
+ if (ret) {
+ dev_err(dev, "can't enable voltage level detector supply\n");
+ return ret;
+ }
+ }
+
+ if (plat->force_b_session_valid &&
+ !plat->force_vbus_detection) {
+ /* Override VBUS detection: enable then value*/
+ setbits_le32(&usbotg_reg->gotgctl, VB_VALOEN);
+ setbits_le32(&usbotg_reg->gotgctl, VB_VALOVAL);
+ } else {
+ /* Enable VBUS sensing */
+ setbits_le32(&usbotg_reg->ggpio,
+ GGPIO_STM32_OTG_GCCFG_VBDEN);
+ }
+ if (plat->force_b_session_valid) {
+ /* Override B session bits: enable then value */
+ setbits_le32(&usbotg_reg->gotgctl, A_VALOEN | B_VALOEN);
+ setbits_le32(&usbotg_reg->gotgctl,
+ A_VALOVAL | B_VALOVAL);
+ } else {
+ /* Enable ID detection */
+ setbits_le32(&usbotg_reg->ggpio,
+ GGPIO_STM32_OTG_GCCFG_IDEN);
+ }
+ }
+
+ ret = dwc2_udc_probe(plat);
+ if (ret)
+ return ret;
+
+ the_controller->driver = 0;
+
+ ret = usb_add_gadget_udc((struct device *)dev, &the_controller->gadget);
+
+ return ret;
+}
+
+static int dwc2_udc_otg_remove(struct udevice *dev)
+{
+ struct dwc2_priv_data *priv = dev_get_priv(dev);
+
+ usb_del_gadget_udc(&the_controller->gadget);
+
+ reset_release_bulk(&priv->resets);
+
+ clk_release_bulk(&priv->clks);
+
+ dwc2_phy_shutdown(dev, &priv->phys);
+
+ return dm_scan_fdt_dev(dev);
+}
+
+static const struct udevice_id dwc2_udc_otg_ids[] = {
+ { .compatible = "snps,dwc2" },
+ { .compatible = "brcm,bcm2835-usb" },
+ { .compatible = "st,stm32mp15-hsotg",
+ .data = (ulong)dwc2_set_stm32mp1_hsotg_params },
+ {},
+};
+
+U_BOOT_DRIVER(dwc2_udc_otg) = {
+ .name = "dwc2-udc-otg",
+ .id = UCLASS_USB_GADGET_GENERIC,
+ .of_match = dwc2_udc_otg_ids,
+ .of_to_plat = dwc2_udc_otg_of_to_plat,
+ .probe = dwc2_udc_otg_probe,
+ .remove = dwc2_udc_otg_remove,
+ .plat_auto = sizeof(struct dwc2_plat_otg_data),
+ .priv_auto = sizeof(struct dwc2_priv_data),
+};
+
+int dwc2_udc_B_session_valid(struct udevice *dev)
+{
+ struct dwc2_plat_otg_data *plat = dev_get_plat(dev);
+ struct dwc2_usbotg_reg *usbotg_reg =
+ (struct dwc2_usbotg_reg *)plat->regs_otg;
+
+ return readl(&usbotg_reg->gotgctl) & B_SESSION_VALID;
+}
+#endif /* CONFIG_IS_ENABLED(DM_USB_GADGET) */
diff --git a/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_phy.c b/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_phy.c
new file mode 100644
index 000000000..7f8e9564b
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_phy.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * drivers/usb/gadget/dwc2_udc_otg.c
+ * Designware DWC2 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>
+ */
+
+#include <common.h>
+#include <linux/delay.h>
+#include <linux/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 "dwc2_udc_otg_regs.h"
+#include "dwc2_udc_otg_priv.h"
+
+#include <usb/dwc2_udc.h>
+
+void otg_phy_init(struct dwc2_udc *dev)
+{
+ unsigned int usb_phy_ctrl = dev->pdata->usb_phy_ctrl;
+ struct dwc2_usbotg_phy *phy =
+ (struct dwc2_usbotg_phy *)dev->pdata->regs_phy;
+
+ 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 dwc2_udc *dev)
+{
+ unsigned int usb_phy_ctrl = dev->pdata->usb_phy_ctrl;
+ struct dwc2_usbotg_phy *phy =
+ (struct dwc2_usbotg_phy *)dev->pdata->regs_phy;
+
+ /* 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);
+}
diff --git a/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_priv.h b/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_priv.h
new file mode 100644
index 000000000..e72b22ac6
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_priv.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Designware DWC2 on-chip full/high speed USB device controllers
+ * Copyright (C) 2005 for Samsung Electronics
+ */
+
+#ifndef __DWC2_UDC_OTG_PRIV__
+#define __DWC2_UDC_OTG_PRIV__
+
+#include <linux/errno.h>
+#include <linux/sizes.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/list.h>
+#include <usb/dwc2_udc.h>
+
+/*-------------------------------------------------------------------------*/
+/* DMA bounce buffer size, 16K is enough even for mass storage */
+#define DMA_BUFFER_SIZE (16*SZ_1K)
+
+#define EP0_FIFO_SIZE 64
+#define EP_FIFO_SIZE 512
+#define EP_FIFO_SIZE2 1024
+/* ep0-control, ep1in-bulk, ep2out-bulk, ep3in-int */
+#define DWC2_MAX_ENDPOINTS 4
+
+#define WAIT_FOR_SETUP 0
+#define DATA_STATE_XMIT 1
+#define DATA_STATE_NEED_ZLP 2
+#define WAIT_FOR_OUT_STATUS 3
+#define DATA_STATE_RECV 4
+#define WAIT_FOR_COMPLETE 5
+#define WAIT_FOR_OUT_COMPLETE 6
+#define WAIT_FOR_IN_COMPLETE 7
+#define WAIT_FOR_NULL_COMPLETE 8
+
+#define TEST_J_SEL 0x1
+#define TEST_K_SEL 0x2
+#define TEST_SE0_NAK_SEL 0x3
+#define TEST_PACKET_SEL 0x4
+#define TEST_FORCE_ENABLE_SEL 0x5
+
+/* ************************************************************************* */
+/* IO
+ */
+
+enum ep_type {
+ ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt
+};
+
+struct dwc2_ep {
+ struct usb_ep ep;
+ struct dwc2_udc *dev;
+
+ const struct usb_endpoint_descriptor *desc;
+ struct list_head queue;
+ unsigned long pio_irqs;
+ int len;
+ void *dma_buf;
+
+ u8 stopped;
+ u8 bEndpointAddress;
+ u8 bmAttributes;
+
+ enum ep_type ep_type;
+ int fifo_num;
+};
+
+struct dwc2_request {
+ struct usb_request req;
+ struct list_head queue;
+};
+
+struct dwc2_udc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+
+ struct dwc2_plat_otg_data *pdata;
+
+ int ep0state;
+ struct dwc2_ep ep[DWC2_MAX_ENDPOINTS];
+
+ unsigned char usb_address;
+
+ unsigned req_pending:1, req_std:1;
+};
+
+#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN) == USB_DIR_IN)
+#define ep_index(EP) ((EP)->bEndpointAddress&0xF)
+#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
+
+void otg_phy_init(struct dwc2_udc *dev);
+void otg_phy_off(struct dwc2_udc *dev);
+
+#endif /* __DWC2_UDC_OTG_PRIV__ */
diff --git a/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_regs.h b/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_regs.h
new file mode 100644
index 000000000..9ca6f4237
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_regs.h
@@ -0,0 +1,296 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* 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>
+ */
+
+#ifndef __ASM_ARCH_REGS_USB_OTG_HS_H
+#define __ASM_ARCH_REGS_USB_OTG_HS_H
+
+/* USB2.0 OTG Controller register */
+#include <linux/bitops.h>
+struct dwc2_usbotg_phy {
+ u32 phypwr;
+ u32 phyclk;
+ u32 rstcon;
+};
+
+/* Device Logical IN Endpoint-Specific Registers */
+struct dwc2_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 dwc2_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 dwc2_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 res0[12];
+ u32 ggpio; /* 0x038 */
+ u8 res1[20];
+ u32 ghwcfg4; /* User HW Config4 */
+ u8 res2[176];
+ u32 dieptxf[15]; /* Device Periodic Transmit FIFO size register */
+ u8 res3[1728];
+ /* Device Configuration */
+ u32 dcfg; /* Device Configuration Register */
+ u32 dctl; /* Device Control */
+ u32 dsts; /* Device Status */
+ u8 res4[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 res5[224];
+ struct dwc2_dev_in_endp in_endp[16];
+ struct dwc2_dev_out_endp out_endp[16];
+ u8 res6[768];
+ struct ep_fifo ep[16];
+};
+
+/*===================================================================== */
+/*definitions related to CSR setting */
+
+/* DWC2_UDC_OTG_GOTGCTL */
+#define B_SESSION_VALID BIT(19)
+#define A_SESSION_VALID BIT(18)
+#define B_VALOVAL BIT(7)
+#define B_VALOEN BIT(6)
+#define A_VALOVAL BIT(5)
+#define A_VALOEN BIT(4)
+#define VB_VALOVAL BIT(3)
+#define VB_VALOEN BIT(2)
+
+/* DWC2_UDC_OTG_GOTINT */
+#define GOTGINT_SES_END_DET (1<<2)
+
+/* DWC2_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)
+
+/* DWC2_UDC_OTG_GRSTCTL */
+#define AHB_MASTER_IDLE (1u<<31)
+#define CORE_SOFT_RESET (0x1<<0)
+
+/* DWC2_UDC_OTG_GINTSTS/DWC2_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_OTG (0x1<<2)
+#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)
+#define NPTX_FIFO_SIZE (1024)
+#define PTX_FIFO_SIZE (384)
+
+#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)
+
+/* DWC2_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)
+
+/* DWC2_UDC_OTG_DCTL device control register */
+#define NORMAL_OPERATION (0x1<<0)
+#define SOFT_DISCONNECT (0x1<<1)
+
+/* DWC2_UDC_OTG_DAINT device all endpoint interrupt register */
+#define DAINT_OUT_BIT (16)
+#define DAINT_MASK (0xFFFF)
+
+/* DWC2_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)
+
+
+/* DWC2_UDC_OTG_DIEPMSK/DOEPMSK device IN/OUT endpoint
+ common interrupt mask register */
+/* DWC2_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 | INT_OTG)
+#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)
+
+/* User HW Config4 */
+#define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26)
+#define GHWCFG4_NUM_IN_EPS_SHIFT 26
+
+/* OTG general core configuration register (OTG_GCCFG:0x38) for STM32MP1 */
+#define GGPIO_STM32_OTG_GCCFG_VBDEN BIT(21)
+#define GGPIO_STM32_OTG_GCCFG_IDEN BIT(22)
+
+#endif
diff --git a/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c b/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c
new file mode 100644
index 000000000..f17009a29
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c
@@ -0,0 +1,1508 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c
+ * Designware DWC2 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>
+ */
+
+#include <common.h>
+#include <cpu_func.h>
+#include <log.h>
+#include <linux/bug.h>
+
+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 dwc2_udc_ep0_zlp(struct dwc2_udc *dev)
+{
+ u32 ep_ctrl;
+
+ writel(phys_to_bus((unsigned long)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;
+}
+
+static void dwc2_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(phys_to_bus((unsigned long)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 dwc2_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(phys_to_bus((unsigned long)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 dwc2_ep *ep, struct dwc2_request *req)
+{
+ u32 *buf, ctrl;
+ u32 length, pktcnt;
+ u32 ep_num = ep_index(ep);
+
+ buf = req->req.buf + req->req.actual;
+ length = min_t(u32, 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);
+
+ invalidate_dcache_range((unsigned long) ep->dma_buf,
+ (unsigned long) ep->dma_buf +
+ ROUND(ep->len, CONFIG_SYS_CACHELINE_SIZE));
+
+ writel(phys_to_bus((unsigned long)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;
+
+}
+
+static int setdma_tx(struct dwc2_ep *ep, struct dwc2_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(phys_to_bus((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 dwc2_udc *dev, u8 ep_num)
+{
+ struct dwc2_ep *ep = &dev->ep[ep_num];
+ struct dwc2_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 dwc2_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, req->req.length - req->req.actual);
+
+ 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");
+ dwc2_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 dwc2_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 dwc2_udc *dev, u8 ep_num)
+{
+ struct dwc2_ep *ep = &dev->ep[ep_num];
+ struct dwc2_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;
+ dwc2_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 dwc2_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, req->req.length - req->req.actual);
+
+ 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;
+ dwc2_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 dwc2_request, queue);
+ debug_cond(DEBUG_IN_EP,
+ "%s: Next Tx request start...\n", __func__);
+ setdma_tx(ep, req);
+ }
+}
+
+static inline void dwc2_udc_check_tx_queue(struct dwc2_udc *dev, u8 ep_num)
+{
+ struct dwc2_ep *ep = &dev->ep[ep_num];
+ struct dwc2_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 dwc2_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 dwc2_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)
+ dwc2_udc_pre_setup();
+
+ /* continue transfer after
+ set_clear_halt for DMA mode */
+ if (clear_feature_flag == 1) {
+ dwc2_udc_check_tx_queue(dev,
+ clear_feature_num);
+ clear_feature_flag = 0;
+ }
+ }
+ }
+ }
+ ep_num++;
+ ep_intr >>= 1;
+ }
+}
+
+static void process_ep_out_intr(struct dwc2_udc *dev)
+{
+ u32 ep_intr, ep_intr_status;
+ u8 ep_num = 0;
+ u32 ep_tsr = 0, xfer_size = 0;
+ u32 epsiz_reg = reg->out_endp[ep_num].doeptsiz;
+ u32 req_size = sizeof(struct usb_ctrlrequest);
+
+ 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) {
+ ep_tsr = readl(&epsiz_reg);
+ xfer_size = ep_tsr &
+ DOEPT_SIZ_XFER_SIZE_MAX_EP0;
+
+ if (xfer_size == req_size &&
+ dev->ep0state == WAIT_FOR_SETUP) {
+ dwc2_udc_pre_setup();
+ } else if (dev->ep0state !=
+ WAIT_FOR_OUT_COMPLETE) {
+ complete_rx(dev, ep_num);
+ } else {
+ dev->ep0state = WAIT_FOR_SETUP;
+ dwc2_udc_pre_setup();
+ }
+ }
+
+ if (ep_intr_status &
+ CTRL_OUT_EP_SETUP_PHASE_DONE) {
+ debug_cond(DEBUG_OUT_EP != 0,
+ "SETUP packet arrived\n");
+ dwc2_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 dwc2_udc_irq(int irq, void *_dev)
+{
+ struct dwc2_udc *dev = _dev;
+ u32 intr_status, gotgint;
+ u32 usb_status, gintmsk;
+ unsigned long flags = 0;
+
+ 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);
+ }
+ }
+
+ if (intr_status & INT_OTG) {
+ gotgint = readl(&reg->gotgint);
+ debug_cond(DEBUG_ISR,
+ "\tOTG interrupt: (GOTGINT):0x%x\n", gotgint);
+
+ if (gotgint & GOTGINT_SES_END_DET) {
+ debug_cond(DEBUG_ISR, "\t\tSession End Detected\n");
+ /* 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);
+ }
+ }
+ writel(gotgint, &reg->gotgint);
+ }
+
+ 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);
+ dev->ep0state = WAIT_FOR_SETUP;
+ reset_available = 0;
+ dwc2_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 dwc2_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct dwc2_request *req;
+ struct dwc2_ep *ep;
+ struct dwc2_udc *dev;
+ unsigned long flags = 0;
+ u32 ep_num, gintsts;
+
+ req = container_of(_req, struct dwc2_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 dwc2_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);
+ dwc2_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, DWC2_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, DWC2_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 dwc2_ep *ep, struct dwc2_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;
+}
+
+static int dwc2_fifo_read(struct dwc2_ep *ep, void *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 dwc2_udc *dev, unsigned char address)
+{
+ u32 ctrl = readl(&reg->dcfg);
+ writel(DEVICE_ADDRESS(address) | ctrl, &reg->dcfg);
+
+ dwc2_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 dwc2_udc_ep0_set_stall(struct dwc2_ep *ep)
+{
+ struct dwc2_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;
+
+ dwc2_udc_pre_setup();
+}
+
+static void dwc2_ep0_read(struct dwc2_udc *dev)
+{
+ struct dwc2_request *req;
+ struct dwc2_ep *ep = &dev->ep[0];
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct dwc2_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;
+ dwc2_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 dwc2_ep0_write(struct dwc2_udc *dev)
+{
+ struct dwc2_request *req;
+ struct dwc2_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 dwc2_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;
+}
+
+static int dwc2_udc_get_status(struct dwc2_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(phys_to_bus(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 dwc2_udc_set_nak(struct dwc2_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;
+}
+
+
+static void dwc2_udc_ep_set_stall(struct dwc2_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;
+}
+
+static void dwc2_udc_ep_clear_stall(struct dwc2_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 dwc2_udc_set_halt(struct usb_ep *_ep, int value)
+{
+ struct dwc2_ep *ep;
+ struct dwc2_udc *dev;
+ unsigned long flags = 0;
+ u8 ep_num;
+
+ ep = container_of(_ep, struct dwc2_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 dwc2_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;
+ dwc2_udc_ep_clear_stall(ep);
+ } else {
+ if (ep_num == 0)
+ dev->ep0state = WAIT_FOR_SETUP;
+
+ ep->stopped = 1;
+ dwc2_udc_ep_set_stall(ep);
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+static void dwc2_udc_ep_activate(struct dwc2_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 dwc2_udc_clear_feature(struct usb_ep *_ep)
+{
+ struct dwc2_udc *dev;
+ struct dwc2_ep *ep;
+ u8 ep_num;
+
+ ep = container_of(_ep, struct dwc2_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;
+ }
+
+ dwc2_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) {
+ dwc2_udc_ep0_set_stall(ep);
+ return 0;
+ }
+
+ dwc2_udc_ep0_zlp(dev);
+
+ dwc2_udc_ep_clear_stall(ep);
+ dwc2_udc_ep_activate(ep);
+ ep->stopped = 0;
+
+ clear_feature_num = ep_num;
+ clear_feature_flag = 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int dwc2_udc_set_feature(struct usb_ep *_ep)
+{
+ struct dwc2_udc *dev;
+ struct dwc2_ep *ep;
+ u8 ep_num;
+
+ ep = container_of(_ep, struct dwc2_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;
+ }
+
+ dwc2_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) {
+ dwc2_udc_ep0_set_stall(ep);
+ return 0;
+ }
+ ep->stopped = 1;
+ dwc2_udc_ep_set_stall(ep);
+ }
+
+ dwc2_udc_ep0_zlp(dev);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * WAIT_FOR_SETUP (OUT_PKT_RDY)
+ */
+static void dwc2_ep0_setup(struct dwc2_udc *dev)
+{
+ struct dwc2_ep *ep = &dev->ep[0];
+ int i;
+ u8 ep_num;
+
+ /* Nuke all previous transfers */
+ nuke(ep, -EPROTO);
+
+ /* read control req from fifo (8 bytes) */
+ dwc2_fifo_read(ep, 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);
+
+ dwc2_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);
+
+ dwc2_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 (!dwc2_udc_get_status(dev, usb_ctrl))
+ return;
+
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ ep_num = usb_ctrl->wIndex & 0x7f;
+
+ if (!dwc2_udc_clear_feature(&dev->ep[ep_num].ep))
+ return;
+
+ break;
+
+ case USB_REQ_SET_FEATURE:
+ ep_num = usb_ctrl->wIndex & 0x7f;
+
+ if (!dwc2_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 */
+ dwc2_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 dwc2_handle_ep0(struct dwc2_udc *dev)
+{
+ if (dev->ep0state == WAIT_FOR_SETUP) {
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: WAIT_FOR_SETUP\n", __func__);
+ dwc2_ep0_setup(dev);
+
+ } else {
+ debug_cond(DEBUG_OUT_EP != 0,
+ "%s: strange state!!(state = %s)\n",
+ __func__, state_names[dev->ep0state]);
+ }
+}
+
+static void dwc2_ep0_kick(struct dwc2_udc *dev, struct dwc2_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;
+ dwc2_ep0_write(dev);
+
+ } else {
+ dev->ep0state = DATA_STATE_RECV;
+ dwc2_ep0_read(dev);
+ }
+}
diff --git a/roms/u-boot/drivers/usb/gadget/ep0.c b/roms/u-boot/drivers/usb/gadget/ep0.c
new file mode 100644
index 000000000..6624f61b7
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/ep0.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (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>
+ */
+
+/*
+ * 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 <serial.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
+
+__maybe_unused static 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 */
+};
+
+#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_INTERFACE_POWER) ? \
+ usbd_device_descriptors[x] : "UNKNOWN")
+
+__maybe_unused static char *usbd_device_states[] = {
+ "STATE_INIT",
+ "STATE_CREATED",
+ "STATE_ATTACHED",
+ "STATE_POWERED",
+ "STATE_DEFAULT",
+ "STATE_ADDRESSED",
+ "STATE_CONFIGURED",
+ "STATE_UNKNOWN",
+};
+
+#define USBD_DEVICE_STATE(x) (((unsigned int)x <= STATE_UNKNOWN) ? usbd_device_states[x] : "UNKNOWN")
+
+__maybe_unused static 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 */
+};
+
+#define USBD_DEVICE_REQUESTS(x) (((unsigned int)x <= USB_REQ_SYNCH_FRAME) ? usbd_device_requests[x] : "UNKNOWN")
+
+/* 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))) {
+ dbg_ep0(0, "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:
+ dbg_ep0(2, "USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n");
+ return -1;
+ case USB_DESCRIPTOR_TYPE_ENDPOINT:
+ dbg_ep0(2, "USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n");
+ return -1;
+ case USB_DESCRIPTOR_TYPE_HID:
+ {
+ dbg_ep0(2, "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:
+ {
+ dbg_ep0(2, "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:
+ dbg_ep0(2, "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); */
+ /*dbg_ep0(2, "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); */
+ dbg_ep0(2, "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/roms/u-boot/drivers/usb/gadget/ep0.h b/roms/u-boot/drivers/usb/gadget/ep0.h
new file mode 100644
index 000000000..976825ab9
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/ep0.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (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>
+ */
+
+#ifndef __USBDCORE_EP0_H__
+#define __USBDCORE_EP0_H__
+
+
+int ep0_recv_setup (struct urb *urb);
+
+
+#endif
diff --git a/roms/u-boot/drivers/usb/gadget/epautoconf.c b/roms/u-boot/drivers/usb/gadget/epautoconf.c
new file mode 100644
index 000000000..7da334f5d
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/epautoconf.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
+ *
+ * Copyright (C) 2004 David Brownell
+ *
+ * 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 <linux/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);
+ }
+
+ if (gadget->ops->ep_conf)
+ return gadget->ops->ep_conf(gadget, ep, desc);
+
+ 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 = NULL;
+ 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;
+#ifndef CONFIG_SPL_BUILD
+ } else if (gadget_is_dwc3(gadget)) {
+ const char *name = NULL;
+ /*
+ * First try standard, common configuration: ep1in-bulk,
+ * ep2out-bulk, ep3in-int to match other udc drivers to avoid
+ * confusion in already deployed software (endpoint numbers
+ * hardcoded in userspace software/drivers)
+ */
+ if ((desc->bEndpointAddress & USB_DIR_IN) &&
+ type == USB_ENDPOINT_XFER_BULK)
+ name = "ep1in";
+ else if ((desc->bEndpointAddress & USB_DIR_IN) == 0 &&
+ type == USB_ENDPOINT_XFER_BULK)
+ name = "ep2out";
+ else if ((desc->bEndpointAddress & USB_DIR_IN) &&
+ type == USB_ENDPOINT_XFER_INT)
+ name = "ep3in";
+
+ if (name)
+ ep = find_ep(gadget, name);
+ if (ep && ep_matches(gadget, ep, desc))
+ return ep;
+#endif
+ }
+
+ if (gadget->ops->match_ep)
+ ep = gadget->ops->match_ep(gadget, desc, NULL);
+
+ /* 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/roms/u-boot/drivers/usb/gadget/ether.c b/roms/u-boot/drivers/usb/gadget/ether.c
new file mode 100644
index 000000000..16922ff15
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/ether.c
@@ -0,0 +1,2706 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 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
+ */
+
+#include <common.h>
+#include <console.h>
+#include <env.h>
+#include <log.h>
+#include <part.h>
+#include <linux/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 <usb.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <linux/ctype.h>
+
+#include "gadget_chips.h"
+#include "rndis.h"
+
+#include <dm.h>
+#include <dm/lists.h>
+#include <dm/uclass-internal.h>
+#include <dm/device-internal.h>
+
+#define USB_NET_NAME "usb_ether"
+
+extern struct platform_data brd;
+
+
+unsigned packet_received, packet_sent;
+
+/*
+ * 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 DRIVER_DESC "Ethernet Gadget"
+/* Based on linux 2.6.27 version */
+#define DRIVER_VERSION "May Day 2005"
+
+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;
+
+#ifndef CONFIG_DM_ETH
+ struct eth_device *net;
+#else
+ struct udevice *net;
+#endif
+ 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.
+ */
+
+/*-------------------------------------------------------------------------*/
+struct ether_priv {
+ struct eth_dev ethdev;
+#ifndef CONFIG_DM_ETH
+ struct eth_device netdev;
+#else
+ struct udevice *netdev;
+#endif
+ struct usb_gadget_driver eth_driver;
+};
+
+struct ether_priv eth_priv;
+struct ether_priv *l_priv = &eth_priv;
+
+/*-------------------------------------------------------------------------*/
+
+/* "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_USB_GADGET_MANUFACTURER)
+static char *iManufacturer = CONFIG_USB_GADGET_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.
+ */
+#ifdef CONFIG_USB_ETH_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
+
+#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(PKTSIZE_ALIGN),
+ .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
+
+/*============================================================================*/
+
+/*
+ * 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...*/
+ pr_err("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) {
+ dev->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)
+ pr_err("%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:
+ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+ 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.
+ */
+ dev->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 *)net_rx_packets[0];
+ req->length = size;
+ req->complete = rx_complete;
+
+ retval = usb_ep_queue(dev->out_ep, req, gfp_flags);
+
+ if (retval)
+ pr_err("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 || PKTSIZE_ALIGN < 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:
+ pr_err("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_ethaddr(dest)) {
+ u16 type;
+
+ /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
+ * SET_ETHERNET_MULTICAST_FILTERS requests
+ */
+ if (is_broadcast_ethaddr(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 (!dev->network_started) {
+ if (rndis_get_state(dev->rndis_config)
+ == RNDIS_DATA_INITIALIZED) {
+ dev->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))));
+
+#ifndef CONFIG_DM_ETH
+static int rndis_control_ack(struct eth_device *net)
+#else
+static int rndis_control_ack(struct udevice *net)
+#endif
+{
+ struct ether_priv *priv;
+ struct eth_dev *dev;
+ int length;
+ struct usb_request *resp;
+
+#ifndef CONFIG_DM_ETH
+ priv = (struct ether_priv *)net->priv;
+#else
+ priv = dev_get_priv(net);
+#endif
+ dev = &priv->ethdev;
+ 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(0);
+#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_ethaddr(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_ethaddr(dev_addr))
+ return 0;
+ }
+ return 1;
+}
+
+static int eth_bind(struct usb_gadget *gadget)
+{
+ struct eth_dev *dev = &l_priv->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];
+#ifdef CONFIG_DM_ETH
+ struct eth_pdata *pdata = dev_get_plat(l_priv->netdev);
+#endif
+
+ /* 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.
+ */
+ pr_err("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_GADGET_VENDOR_NUM) && defined(CONFIG_USB_GADGET_PRODUCT_NUM)
+ device_desc.idVendor =
+ __constant_cpu_to_le16(CONFIG_USB_GADGET_VENDOR_NUM);
+ device_desc.idProduct =
+ __constant_cpu_to_le16(CONFIG_USB_GADGET_PRODUCT_NUM);
+#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_GADGET_VENDOR_NUM) && defined(CONFIG_USB_GADGET_PRODUCT_NUM)
+ device_desc.idVendor = cpu_to_le16(CONFIG_USB_GADGET_VENDOR_NUM);
+ device_desc.idProduct = cpu_to_le16(CONFIG_USB_GADGET_PRODUCT_NUM);
+#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:
+ pr_err("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) {
+ pr_err("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();
+ }
+
+ 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 */
+#ifndef CONFIG_DM_ETH
+ dev->net = &l_priv->netdev;
+#else
+ dev->net = l_priv->netdev;
+#endif
+
+ dev->cdc = cdc;
+ dev->zlp = zlp;
+
+ dev->in_ep = in_ep;
+ dev->out_ep = out_ep;
+ dev->status_ep = status_ep;
+
+ memset(tmp, 0, sizeof(tmp));
+ /*
+ * 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.
+ */
+#ifndef CONFIG_DM_ETH
+ get_ether_addr(dev_addr, dev->net->enetaddr);
+ memcpy(tmp, dev->net->enetaddr, sizeof(dev->net->enetaddr));
+#else
+ get_ether_addr(dev_addr, pdata->enetaddr);
+ memcpy(tmp, pdata->enetaddr, sizeof(pdata->enetaddr));
+#endif
+
+ 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) {
+ pr_err("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 : ""
+ );
+#ifndef CONFIG_DM_ETH
+ printf("MAC %pM\n", dev->net->enetaddr);
+#else
+ printf("MAC %pM\n", pdata->enetaddr);
+#endif
+
+ 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:
+ pr_err("%s failed, status = %d", __func__, status);
+ eth_unbind(gadget);
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+static void _usb_eth_halt(struct ether_priv *priv);
+
+static int _usb_eth_init(struct ether_priv *priv)
+{
+ struct eth_dev *dev = &priv->ethdev;
+ struct usb_gadget *gadget;
+ unsigned long ts;
+ int ret;
+ unsigned long timeout = USB_CONNECT_TIMEOUT;
+
+ ret = usb_gadget_initialize(0);
+ if (ret)
+ return ret;
+
+ /* 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 (env_get("usbnet_devaddr"))
+ strlcpy(dev_addr, env_get("usbnet_devaddr"),
+ sizeof(dev_addr));
+
+ if (env_get("usbnet_hostaddr"))
+ strlcpy(host_addr, env_get("usbnet_hostaddr"),
+ sizeof(host_addr));
+
+ if (!is_eth_addr_valid(dev_addr)) {
+ pr_err("Need valid 'usbnet_devaddr' to be set");
+ goto fail;
+ }
+ if (!is_eth_addr_valid(host_addr)) {
+ pr_err("Need valid 'usbnet_hostaddr' to be set");
+ goto fail;
+ }
+
+ priv->eth_driver.speed = DEVSPEED;
+ priv->eth_driver.bind = eth_bind;
+ priv->eth_driver.unbind = eth_unbind;
+ priv->eth_driver.setup = eth_setup;
+ priv->eth_driver.reset = eth_disconnect;
+ priv->eth_driver.disconnect = eth_disconnect;
+ priv->eth_driver.suspend = eth_suspend;
+ priv->eth_driver.resume = eth_resume;
+ if (usb_gadget_register_driver(&priv->eth_driver) < 0)
+ goto fail;
+
+ dev->network_started = 0;
+
+ packet_received = 0;
+ packet_sent = 0;
+
+ gadget = dev->gadget;
+ usb_gadget_connect(gadget);
+
+ if (env_get("cdc_connect_timeout"))
+ timeout = simple_strtoul(env_get("cdc_connect_timeout"),
+ NULL, 10) * CONFIG_SYS_HZ;
+ ts = get_timer(0);
+ while (!dev->network_started) {
+ /* Handle control-c and timeouts */
+ if (ctrlc() || (get_timer(ts) > timeout)) {
+ pr_err("The remote end did not respond in time.");
+ goto fail;
+ }
+ usb_gadget_handle_interrupts(0);
+ }
+
+ packet_received = 0;
+ rx_submit(dev, dev->rx_req, 0);
+ return 0;
+fail:
+ _usb_eth_halt(priv);
+ return -1;
+}
+
+static int _usb_eth_send(struct ether_priv *priv, void *packet, int length)
+{
+ int retval;
+ void *rndis_pkt = NULL;
+ struct eth_dev *dev = &priv->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) {
+ pr_err("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(0);
+ }
+ free(rndis_pkt);
+
+ return 0;
+drop:
+ dev->stats.tx_dropped++;
+ return -ENOMEM;
+}
+
+static int _usb_eth_recv(struct ether_priv *priv)
+{
+ usb_gadget_handle_interrupts(0);
+
+ return 0;
+}
+
+static void _usb_eth_halt(struct ether_priv *priv)
+{
+ struct eth_dev *dev = &priv->ethdev;
+
+ /* 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(0);
+ dev->network_started = 0;
+ }
+
+ usb_gadget_unregister_driver(&priv->eth_driver);
+ usb_gadget_release(0);
+}
+
+#ifndef CONFIG_DM_ETH
+static int usb_eth_init(struct eth_device *netdev, struct bd_info *bd)
+{
+ struct ether_priv *priv = (struct ether_priv *)netdev->priv;
+
+ return _usb_eth_init(priv);
+}
+
+static int usb_eth_send(struct eth_device *netdev, void *packet, int length)
+{
+ struct ether_priv *priv = (struct ether_priv *)netdev->priv;
+
+ return _usb_eth_send(priv, packet, length);
+}
+
+static int usb_eth_recv(struct eth_device *netdev)
+{
+ struct ether_priv *priv = (struct ether_priv *)netdev->priv;
+ struct eth_dev *dev = &priv->ethdev;
+ int ret;
+
+ ret = _usb_eth_recv(priv);
+ if (ret) {
+ pr_err("error packet receive\n");
+ return ret;
+ }
+
+ if (!packet_received)
+ return 0;
+
+ if (dev->rx_req) {
+ net_process_received_packet(net_rx_packets[0],
+ dev->rx_req->length);
+ } else {
+ pr_err("dev->rx_req invalid");
+ }
+ packet_received = 0;
+ rx_submit(dev, dev->rx_req, 0);
+
+ return 0;
+}
+
+void usb_eth_halt(struct eth_device *netdev)
+{
+ struct ether_priv *priv = (struct ether_priv *)netdev->priv;
+
+ _usb_eth_halt(priv);
+}
+
+int usb_eth_initialize(struct bd_info *bi)
+{
+ struct eth_device *netdev = &l_priv->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;
+ netdev->priv = l_priv;
+
+ eth_register(netdev);
+ return 0;
+}
+#else
+static int usb_eth_start(struct udevice *dev)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+
+ return _usb_eth_init(priv);
+}
+
+static int usb_eth_send(struct udevice *dev, void *packet, int length)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+
+ return _usb_eth_send(priv, packet, length);
+}
+
+static int usb_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+ struct eth_dev *ethdev = &priv->ethdev;
+ int ret;
+
+ ret = _usb_eth_recv(priv);
+ if (ret) {
+ pr_err("error packet receive\n");
+ return ret;
+ }
+
+ if (packet_received) {
+ if (ethdev->rx_req) {
+ *packetp = (uchar *)net_rx_packets[0];
+ return ethdev->rx_req->length;
+ } else {
+ pr_err("dev->rx_req invalid");
+ return -EFAULT;
+ }
+ }
+
+ return -EAGAIN;
+}
+
+static int usb_eth_free_pkt(struct udevice *dev, uchar *packet,
+ int length)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+ struct eth_dev *ethdev = &priv->ethdev;
+
+ packet_received = 0;
+
+ return rx_submit(ethdev, ethdev->rx_req, 0);
+}
+
+static void usb_eth_stop(struct udevice *dev)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+
+ _usb_eth_halt(priv);
+}
+
+static int usb_eth_probe(struct udevice *dev)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+ struct eth_pdata *pdata = dev_get_plat(dev);
+
+ priv->netdev = dev;
+ l_priv = priv;
+
+ get_ether_addr(CONFIG_USBNET_DEVADDR, pdata->enetaddr);
+ eth_env_set_enetaddr("usbnet_devaddr", pdata->enetaddr);
+
+ return 0;
+}
+
+static const struct eth_ops usb_eth_ops = {
+ .start = usb_eth_start,
+ .send = usb_eth_send,
+ .recv = usb_eth_recv,
+ .free_pkt = usb_eth_free_pkt,
+ .stop = usb_eth_stop,
+};
+
+int usb_ether_init(void)
+{
+ struct udevice *dev;
+ struct udevice *usb_dev;
+ int ret;
+
+ ret = uclass_first_device(UCLASS_USB_GADGET_GENERIC, &usb_dev);
+ if (!usb_dev || ret) {
+ pr_err("No USB device found\n");
+ return ret;
+ }
+
+ ret = device_bind_driver(usb_dev, "usb_ether", "usb_ether", &dev);
+ if (!dev || ret) {
+ pr_err("usb - not able to bind usb_ether device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(eth_usb) = {
+ .name = "usb_ether",
+ .id = UCLASS_ETH,
+ .probe = usb_eth_probe,
+ .ops = &usb_eth_ops,
+ .priv_auto = sizeof(struct ether_priv),
+ .plat_auto = sizeof(struct eth_pdata),
+ .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
+#endif /* CONFIG_DM_ETH */
diff --git a/roms/u-boot/drivers/usb/gadget/f_dfu.c b/roms/u-boot/drivers/usb/gadget/f_dfu.c
new file mode 100644
index 000000000..4bedc7d3a
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/f_dfu.c
@@ -0,0 +1,844 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 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>
+ */
+
+#include <env.h>
+#include <errno.h>
+#include <common.h>
+#include <log.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;
+};
+
+struct dfu_entity *dfu_defer_flush;
+
+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 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;
+ int ret;
+
+ ret = dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf,
+ req->actual, f_dfu->blk_seq_num);
+ if (ret) {
+ f_dfu->dfu_status = DFU_STATUS_errUNKNOWN;
+ f_dfu->dfu_state = DFU_STATE_dfuERROR;
+ }
+}
+
+static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_dfu *f_dfu = req->context;
+ dfu_set_defer_flush(dfu_get_entity(f_dfu->altsetting));
+}
+
+static inline int dfu_get_manifest_timeout(struct dfu_entity *dfu)
+{
+ return dfu->poll_timeout ? dfu->poll_timeout(dfu) :
+ DFU_MANIFEST_POLL_TIMEOUT;
+}
+
+static int handle_getstatus(struct usb_request *req)
+{
+ struct dfu_status *dstat = (struct dfu_status *)req->buf;
+ struct f_dfu *f_dfu = req->context;
+ struct dfu_entity *dfu = dfu_get_entity(f_dfu->altsetting);
+
+ 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_get_manifest_timeout(dfu));
+ break;
+ 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;
+
+ return sizeof(struct dfu_status);
+}
+
+static int handle_getstate(struct usb_request *req)
+{
+ struct f_dfu *f_dfu = req->context;
+
+ ((u8 *)req->buf)[0] = f_dfu->dfu_state;
+ return 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->usb_function.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;
+ f_dfu->usb_function.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:
+ value = handle_getstatus(req);
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ value = 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:
+ value = handle_getstatus(req);
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ value = 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:
+ value = handle_getstatus(req);
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ value = 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;
+
+ g_dnl_trigger_detach();
+ 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:
+ value = handle_getstatus(req);
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ value = 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:
+ value = handle_getstatus(req);
+ 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:
+ value = handle_getstatus(req);
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ value = 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;
+ value = handle_getstatus(req);
+ f_dfu->blk_seq_num = 0;
+ req->complete = dnload_request_flush;
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ value = 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;
+ value = handle_getstatus(req);
+ f_dfu->blk_seq_num = 0;
+ puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n");
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ value = 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:
+ value = handle_getstatus(req);
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ value = 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:
+ value = handle_getstatus(req);
+ break;
+ case USB_REQ_DFU_GETSTATE:
+ value = 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);
+
+#ifdef CONFIG_DFU_TIMEOUT
+ /* Forbid aborting by timeout. Next dfu command may update this */
+ dfu_set_timeout(0);
+#endif
+
+ 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)
+ return -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;
+}
+
+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 + 2);
+ 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;
+ }
+
+ /* add DFU Functional Descriptor */
+ f_dfu->function[i] = calloc(sizeof(dfu_func), 1);
+ if (!f_dfu->function[i])
+ goto enomem;
+ memcpy(f_dfu->function[i], &dfu_func, sizeof(dfu_func));
+
+ i++;
+ 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);
+ const char *s;
+ 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;
+
+ s = env_get("serial#");
+ if (s)
+ g_dnl_set_serialnumber((char *)s);
+
+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;
+ i++; /* free DFU Functional Descriptor */
+ 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;
+ f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+ f_dfu->dfu_status = DFU_STATUS_OK;
+
+ return 0;
+}
+
+static int __dfu_get_alt(struct usb_function *f, unsigned intf)
+{
+ struct f_dfu *f_dfu = func_to_dfu(f);
+
+ return f_dfu->altsetting;
+}
+
+/* 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.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.get_alt = __dfu_get_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/roms/u-boot/drivers/usb/gadget/f_dfu.h b/roms/u-boot/drivers/usb/gadget/f_dfu.h
new file mode 100644
index 000000000..a175e3f13
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/f_dfu.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * f_dfu.h -- Device Firmware Update gadget
+ *
+ * Copyright (C) 2011-2012 Samsung Electronics
+ * author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#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
+
+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/roms/u-boot/drivers/usb/gadget/f_fastboot.c b/roms/u-boot/drivers/usb/gadget/f_fastboot.c
new file mode 100644
index 000000000..8ba55aab9
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/f_fastboot.c
@@ -0,0 +1,560 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Copyright 2014 Linaro, Ltd.
+ * Rob Herring <robh@kernel.org>
+ */
+#include <command.h>
+#include <config.h>
+#include <common.h>
+#include <env.h>
+#include <errno.h>
+#include <fastboot.h>
+#include <log.h>
+#include <malloc.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include <linux/compiler.h>
+#include <g_dnl.h>
+
+#define FASTBOOT_INTERFACE_CLASS 0xff
+#define FASTBOOT_INTERFACE_SUB_CLASS 0x42
+#define FASTBOOT_INTERFACE_PROTOCOL 0x03
+
+#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200)
+#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040)
+#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040)
+
+#define EP_BUFFER_SIZE 4096
+/*
+ * EP_BUFFER_SIZE must always be an integral multiple of maxpacket size
+ * (64 or 512 or 1024), else we break on certain controllers like DWC3
+ * that expect bulk OUT requests to be divisible by maxpacket size.
+ */
+
+struct f_fastboot {
+ struct usb_function usb_function;
+
+ /* IN/OUT EP's and corresponding requests */
+ struct usb_ep *in_ep, *out_ep;
+ struct usb_request *in_req, *out_req;
+};
+
+static char fb_ext_prop_name[] = "DeviceInterfaceGUID";
+static char fb_ext_prop_data[] = "{4866319A-F4D6-4374-93B9-DC2DEB361BA9}";
+
+static struct usb_os_desc_ext_prop fb_ext_prop = {
+ .type = 1, /* NUL-terminated Unicode String (REG_SZ) */
+ .name = fb_ext_prop_name,
+ .data = fb_ext_prop_data,
+};
+
+/* 16 bytes of "Compatible ID" and "Subcompatible ID" */
+static char fb_cid[16] = {'W', 'I', 'N', 'U', 'S', 'B'};
+static struct usb_os_desc fb_os_desc = {
+ .ext_compat_id = fb_cid,
+};
+
+static struct usb_os_desc_table fb_os_desc_table = {
+ .os_desc = &fb_os_desc,
+};
+
+static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
+{
+ return container_of(f, struct f_fastboot, usb_function);
+}
+
+static struct f_fastboot *fastboot_func;
+
+static struct usb_endpoint_descriptor fs_ep_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor fs_ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor hs_ep_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_interface_descriptor interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0x00,
+ .bAlternateSetting = 0x00,
+ .bNumEndpoints = 0x02,
+ .bInterfaceClass = FASTBOOT_INTERFACE_CLASS,
+ .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS,
+ .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL,
+};
+
+static struct usb_descriptor_header *fb_fs_function[] = {
+ (struct usb_descriptor_header *)&interface_desc,
+ (struct usb_descriptor_header *)&fs_ep_in,
+ (struct usb_descriptor_header *)&fs_ep_out,
+};
+
+static struct usb_descriptor_header *fb_hs_function[] = {
+ (struct usb_descriptor_header *)&interface_desc,
+ (struct usb_descriptor_header *)&hs_ep_in,
+ (struct usb_descriptor_header *)&hs_ep_out,
+ NULL,
+};
+
+/* Super speed */
+static struct usb_endpoint_descriptor ss_ep_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fb_ss_bulk_comp_desc = {
+ .bLength = sizeof(fb_ss_bulk_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *fb_ss_function[] = {
+ (struct usb_descriptor_header *)&interface_desc,
+ (struct usb_descriptor_header *)&ss_ep_in,
+ (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc,
+ (struct usb_descriptor_header *)&ss_ep_out,
+ (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc,
+ NULL,
+};
+
+static struct usb_endpoint_descriptor *
+fb_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
+ struct usb_endpoint_descriptor *hs,
+ struct usb_endpoint_descriptor *ss)
+{
+ if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER)
+ return ss;
+
+ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return hs;
+ return fs;
+}
+
+/*
+ * static strings, in UTF-8
+ */
+static const char fastboot_name[] = "Android Fastboot";
+
+static struct usb_string fastboot_string_defs[] = {
+ [0].s = fastboot_name,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_fastboot = {
+ .language = 0x0409, /* en-us */
+ .strings = fastboot_string_defs,
+};
+
+static struct usb_gadget_strings *fastboot_strings[] = {
+ &stringtab_fastboot,
+ NULL,
+};
+
+static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
+
+static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int status = req->status;
+ if (!status)
+ return;
+ printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual);
+}
+
+static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ int id;
+ struct usb_gadget *gadget = c->cdev->gadget;
+ struct f_fastboot *f_fb = func_to_fastboot(f);
+ const char *s;
+
+ /* DYNAMIC interface numbers assignments */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ interface_desc.bInterfaceNumber = id;
+
+ /* Enable OS and Extended Properties Feature Descriptor */
+ c->cdev->use_os_string = 1;
+ f->os_desc_table = &fb_os_desc_table;
+ f->os_desc_n = 1;
+ f->os_desc_table->if_id = id;
+ INIT_LIST_HEAD(&fb_os_desc.ext_prop);
+ fb_ext_prop.name_len = strlen(fb_ext_prop.name) * 2 + 2;
+ fb_os_desc.ext_prop_len = 10 + fb_ext_prop.name_len;
+ fb_os_desc.ext_prop_count = 1;
+ fb_ext_prop.data_len = strlen(fb_ext_prop.data) * 2 + 2;
+ fb_os_desc.ext_prop_len += fb_ext_prop.data_len + 4;
+ list_add_tail(&fb_ext_prop.entry, &fb_os_desc.ext_prop);
+
+ id = usb_string_id(c->cdev);
+ if (id < 0)
+ return id;
+ fastboot_string_defs[0].id = id;
+ interface_desc.iInterface = id;
+
+ f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in);
+ if (!f_fb->in_ep)
+ return -ENODEV;
+ f_fb->in_ep->driver_data = c->cdev;
+
+ f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out);
+ if (!f_fb->out_ep)
+ return -ENODEV;
+ f_fb->out_ep->driver_data = c->cdev;
+
+ f->descriptors = fb_fs_function;
+
+ if (gadget_is_dualspeed(gadget)) {
+ /* Assume endpoint addresses are the same for both speeds */
+ hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
+ hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
+ /* copy HS descriptors */
+ f->hs_descriptors = fb_hs_function;
+ }
+
+ if (gadget_is_superspeed(gadget)) {
+ ss_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
+ ss_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
+ f->ss_descriptors = fb_ss_function;
+ }
+
+ s = env_get("serial#");
+ if (s)
+ g_dnl_set_serialnumber((char *)s);
+
+ return 0;
+}
+
+static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ f->os_desc_table = NULL;
+ list_del(&fb_os_desc.ext_prop);
+ memset(fastboot_func, 0, sizeof(*fastboot_func));
+}
+
+static void fastboot_disable(struct usb_function *f)
+{
+ struct f_fastboot *f_fb = func_to_fastboot(f);
+
+ usb_ep_disable(f_fb->out_ep);
+ usb_ep_disable(f_fb->in_ep);
+
+ if (f_fb->out_req) {
+ free(f_fb->out_req->buf);
+ usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
+ f_fb->out_req = NULL;
+ }
+ if (f_fb->in_req) {
+ free(f_fb->in_req->buf);
+ usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
+ f_fb->in_req = NULL;
+ }
+}
+
+static struct usb_request *fastboot_start_ep(struct usb_ep *ep)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, 0);
+ if (!req)
+ return NULL;
+
+ req->length = EP_BUFFER_SIZE;
+ req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+
+ memset(req->buf, 0, req->length);
+ return req;
+}
+
+static int fastboot_set_alt(struct usb_function *f,
+ unsigned interface, unsigned alt)
+{
+ int ret;
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_gadget *gadget = cdev->gadget;
+ struct f_fastboot *f_fb = func_to_fastboot(f);
+ const struct usb_endpoint_descriptor *d;
+
+ debug("%s: func: %s intf: %d alt: %d\n",
+ __func__, f->name, interface, alt);
+
+ d = fb_ep_desc(gadget, &fs_ep_out, &hs_ep_out, &ss_ep_out);
+ ret = usb_ep_enable(f_fb->out_ep, d);
+ if (ret) {
+ puts("failed to enable out ep\n");
+ return ret;
+ }
+
+ f_fb->out_req = fastboot_start_ep(f_fb->out_ep);
+ if (!f_fb->out_req) {
+ puts("failed to alloc out req\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ f_fb->out_req->complete = rx_handler_command;
+
+ d = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in, &ss_ep_in);
+ ret = usb_ep_enable(f_fb->in_ep, d);
+ if (ret) {
+ puts("failed to enable in ep\n");
+ goto err;
+ }
+
+ f_fb->in_req = fastboot_start_ep(f_fb->in_ep);
+ if (!f_fb->in_req) {
+ puts("failed alloc req in\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ f_fb->in_req->complete = fastboot_complete;
+
+ ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ fastboot_disable(f);
+ return ret;
+}
+
+static int fastboot_add(struct usb_configuration *c)
+{
+ struct f_fastboot *f_fb = fastboot_func;
+ int status;
+
+ debug("%s: cdev: 0x%p\n", __func__, c->cdev);
+
+ if (!f_fb) {
+ f_fb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_fb));
+ if (!f_fb)
+ return -ENOMEM;
+
+ fastboot_func = f_fb;
+ memset(f_fb, 0, sizeof(*f_fb));
+ }
+
+ f_fb->usb_function.name = "f_fastboot";
+ f_fb->usb_function.bind = fastboot_bind;
+ f_fb->usb_function.unbind = fastboot_unbind;
+ f_fb->usb_function.set_alt = fastboot_set_alt;
+ f_fb->usb_function.disable = fastboot_disable;
+ f_fb->usb_function.strings = fastboot_strings;
+
+ status = usb_add_function(c, &f_fb->usb_function);
+ if (status) {
+ free(f_fb);
+ fastboot_func = NULL;
+ }
+
+ return status;
+}
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_fastboot, fastboot_add);
+
+static int fastboot_tx_write(const char *buffer, unsigned int buffer_size)
+{
+ struct usb_request *in_req = fastboot_func->in_req;
+ int ret;
+
+ memcpy(in_req->buf, buffer, buffer_size);
+ in_req->length = buffer_size;
+
+ usb_ep_dequeue(fastboot_func->in_ep, in_req);
+
+ ret = usb_ep_queue(fastboot_func->in_ep, in_req, 0);
+ if (ret)
+ printf("Error %d on queue\n", ret);
+ return 0;
+}
+
+static int fastboot_tx_write_str(const char *buffer)
+{
+ return fastboot_tx_write(buffer, strlen(buffer));
+}
+
+static void compl_do_reset(struct usb_ep *ep, struct usb_request *req)
+{
+ do_reset(NULL, 0, 0, NULL);
+}
+
+static unsigned int rx_bytes_expected(struct usb_ep *ep)
+{
+ int rx_remain = fastboot_data_remaining();
+ unsigned int rem;
+ unsigned int maxpacket = usb_endpoint_maxp(ep->desc);
+
+ if (rx_remain <= 0)
+ return 0;
+ else if (rx_remain > EP_BUFFER_SIZE)
+ return EP_BUFFER_SIZE;
+
+ /*
+ * Some controllers e.g. DWC3 don't like OUT transfers to be
+ * not ending in maxpacket boundary. So just make them happy by
+ * always requesting for integral multiple of maxpackets.
+ * This shouldn't bother controllers that don't care about it.
+ */
+ rem = rx_remain % maxpacket;
+ if (rem > 0)
+ rx_remain = rx_remain + (maxpacket - rem);
+
+ return rx_remain;
+}
+
+static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
+{
+ char response[FASTBOOT_RESPONSE_LEN] = {0};
+ unsigned int transfer_size = fastboot_data_remaining();
+ const unsigned char *buffer = req->buf;
+ unsigned int buffer_size = req->actual;
+
+ if (req->status != 0) {
+ printf("Bad status: %d\n", req->status);
+ return;
+ }
+
+ if (buffer_size < transfer_size)
+ transfer_size = buffer_size;
+
+ fastboot_data_download(buffer, transfer_size, response);
+ if (response[0]) {
+ fastboot_tx_write_str(response);
+ } else if (!fastboot_data_remaining()) {
+ fastboot_data_complete(response);
+
+ /*
+ * Reset global transfer variable
+ */
+ req->complete = rx_handler_command;
+ req->length = EP_BUFFER_SIZE;
+
+ fastboot_tx_write_str(response);
+ } else {
+ req->length = rx_bytes_expected(ep);
+ }
+
+ req->actual = 0;
+ usb_ep_queue(ep, req, 0);
+}
+
+static void do_exit_on_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ g_dnl_trigger_detach();
+}
+
+static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ fastboot_boot();
+ do_exit_on_complete(ep, req);
+}
+
+#if CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT)
+static void do_acmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ /* When usb dequeue complete will be called
+ * Need status value before call run_command.
+ * otherwise, host can't get last message.
+ */
+ if (req->status == 0)
+ fastboot_acmd_complete();
+}
+#endif
+
+static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
+{
+ char *cmdbuf = req->buf;
+ char response[FASTBOOT_RESPONSE_LEN] = {0};
+ int cmd = -1;
+
+ if (req->status != 0 || req->length == 0)
+ return;
+
+ if (req->actual < req->length) {
+ cmdbuf[req->actual] = '\0';
+ cmd = fastboot_handle_command(cmdbuf, response);
+ } else {
+ pr_err("buffer overflow");
+ fastboot_fail("buffer overflow", response);
+ }
+
+ if (!strncmp("DATA", response, 4)) {
+ req->complete = rx_handler_dl_image;
+ req->length = rx_bytes_expected(ep);
+ }
+
+ if (!strncmp("OKAY", response, 4)) {
+ switch (cmd) {
+ case FASTBOOT_COMMAND_BOOT:
+ fastboot_func->in_req->complete = do_bootm_on_complete;
+ break;
+
+ case FASTBOOT_COMMAND_CONTINUE:
+ fastboot_func->in_req->complete = do_exit_on_complete;
+ break;
+
+ case FASTBOOT_COMMAND_REBOOT:
+ case FASTBOOT_COMMAND_REBOOT_BOOTLOADER:
+ case FASTBOOT_COMMAND_REBOOT_FASTBOOTD:
+ case FASTBOOT_COMMAND_REBOOT_RECOVERY:
+ fastboot_func->in_req->complete = compl_do_reset;
+ break;
+#if CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT)
+ case FASTBOOT_COMMAND_ACMD:
+ fastboot_func->in_req->complete = do_acmd_complete;
+ break;
+#endif
+ }
+ }
+
+ fastboot_tx_write_str(response);
+
+ *cmdbuf = '\0';
+ req->actual = 0;
+ usb_ep_queue(ep, req, 0);
+}
diff --git a/roms/u-boot/drivers/usb/gadget/f_mass_storage.c b/roms/u-boot/drivers/usb/gadget/f_mass_storage.c
new file mode 100644
index 000000000..45f0504b6
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/f_mass_storage.c
@@ -0,0 +1,2777 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * 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.
+ */
+
+/*
+ * 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 <hexdump.h>
+#include <log.h>
+#include <malloc.h>
+#include <common.h>
+#include <console.h>
+#include <g_dnl.h>
+#include <dm/devres.h>
+#include <linux/bug.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/bitops.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include <linux/bitmap.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; };
+
+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);
+#ifdef __UBOOT__
+ assert_noisy(false);
+#else
+ WARN_ON(1);
+#endif
+ 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;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct ums *ums;
+static int ums_count;
+static struct fsg_common *the_fsg_common;
+static unsigned int controller_index;
+
+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 == 20000) {
+ 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(controller_index);
+ }
+ 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[common->lun].read_sector(&ums[common->lun],
+ 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[common->lun].write_sector(&ums[common->lun],
+ 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:%zd amount:%u\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[common->lun].read_sector(&ums[common->lun],
+ 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[1] = curlun->removable ? 0x80 : 0;
+ 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[common->lun].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 < 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);
+ generic_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));
+ generic_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 = ums_count;
+ 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], ums[i].num_sectors, "");
+ 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_devs, int count, unsigned int controller_idx)
+{
+ ums = ums_devs;
+ ums_count = count;
+ controller_index = controller_idx;
+
+ return 0;
+}
+
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_ums, fsg_add);
diff --git a/roms/u-boot/drivers/usb/gadget/f_rockusb.c b/roms/u-boot/drivers/usb/gadget/f_rockusb.c
new file mode 100644
index 000000000..bd846ce9a
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/f_rockusb.c
@@ -0,0 +1,934 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2017
+ *
+ * Eddie Cai <eddie.cai.linux@gmail.com>
+ */
+#include <command.h>
+#include <config.h>
+#include <common.h>
+#include <env.h>
+#include <errno.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <part.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include <linux/compiler.h>
+#include <version.h>
+#include <g_dnl.h>
+#include <asm/arch-rockchip/f_rockusb.h>
+
+static inline struct f_rockusb *func_to_rockusb(struct usb_function *f)
+{
+ return container_of(f, struct f_rockusb, usb_function);
+}
+
+static struct usb_endpoint_descriptor fs_ep_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor fs_ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor hs_ep_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_interface_descriptor interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0x00,
+ .bAlternateSetting = 0x00,
+ .bNumEndpoints = 0x02,
+ .bInterfaceClass = ROCKUSB_INTERFACE_CLASS,
+ .bInterfaceSubClass = ROCKUSB_INTERFACE_SUB_CLASS,
+ .bInterfaceProtocol = ROCKUSB_INTERFACE_PROTOCOL,
+};
+
+static struct usb_descriptor_header *rkusb_fs_function[] = {
+ (struct usb_descriptor_header *)&interface_desc,
+ (struct usb_descriptor_header *)&fs_ep_in,
+ (struct usb_descriptor_header *)&fs_ep_out,
+};
+
+static struct usb_descriptor_header *rkusb_hs_function[] = {
+ (struct usb_descriptor_header *)&interface_desc,
+ (struct usb_descriptor_header *)&hs_ep_in,
+ (struct usb_descriptor_header *)&hs_ep_out,
+ NULL,
+};
+
+static const char rkusb_name[] = "Rockchip Rockusb";
+
+static struct usb_string rkusb_string_defs[] = {
+ [0].s = rkusb_name,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_rkusb = {
+ .language = 0x0409, /* en-us */
+ .strings = rkusb_string_defs,
+};
+
+static struct usb_gadget_strings *rkusb_strings[] = {
+ &stringtab_rkusb,
+ NULL,
+};
+
+static struct f_rockusb *rockusb_func;
+static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
+static int rockusb_tx_write_csw(u32 tag, int residue, u8 status, int size);
+
+struct f_rockusb *get_rkusb(void)
+{
+ struct f_rockusb *f_rkusb = rockusb_func;
+
+ if (!f_rkusb) {
+ f_rkusb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_rkusb));
+ if (!f_rkusb)
+ return NULL;
+
+ rockusb_func = f_rkusb;
+ memset(f_rkusb, 0, sizeof(*f_rkusb));
+ }
+
+ if (!f_rkusb->buf_head) {
+ f_rkusb->buf_head = memalign(CONFIG_SYS_CACHELINE_SIZE,
+ RKUSB_BUF_SIZE);
+ if (!f_rkusb->buf_head)
+ return NULL;
+
+ f_rkusb->buf = f_rkusb->buf_head;
+ memset(f_rkusb->buf_head, 0, RKUSB_BUF_SIZE);
+ }
+ return f_rkusb;
+}
+
+static struct usb_endpoint_descriptor *rkusb_ep_desc(
+struct usb_gadget *g,
+struct usb_endpoint_descriptor *fs,
+struct usb_endpoint_descriptor *hs)
+{
+ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return hs;
+ return fs;
+}
+
+static void rockusb_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int status = req->status;
+
+ if (!status)
+ return;
+ debug("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual);
+}
+
+/* config the rockusb device*/
+static int rockusb_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ int id;
+ struct usb_gadget *gadget = c->cdev->gadget;
+ struct f_rockusb *f_rkusb = func_to_rockusb(f);
+ const char *s;
+
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ interface_desc.bInterfaceNumber = id;
+
+ id = usb_string_id(c->cdev);
+ if (id < 0)
+ return id;
+
+ rkusb_string_defs[0].id = id;
+ interface_desc.iInterface = id;
+
+ f_rkusb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in);
+ if (!f_rkusb->in_ep)
+ return -ENODEV;
+ f_rkusb->in_ep->driver_data = c->cdev;
+
+ f_rkusb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out);
+ if (!f_rkusb->out_ep)
+ return -ENODEV;
+ f_rkusb->out_ep->driver_data = c->cdev;
+
+ f->descriptors = rkusb_fs_function;
+
+ if (gadget_is_dualspeed(gadget)) {
+ hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
+ hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
+ f->hs_descriptors = rkusb_hs_function;
+ }
+
+ s = env_get("serial#");
+ if (s)
+ g_dnl_set_serialnumber((char *)s);
+
+ return 0;
+}
+
+static void rockusb_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ /* clear the configuration*/
+ memset(rockusb_func, 0, sizeof(*rockusb_func));
+}
+
+static void rockusb_disable(struct usb_function *f)
+{
+ struct f_rockusb *f_rkusb = func_to_rockusb(f);
+
+ usb_ep_disable(f_rkusb->out_ep);
+ usb_ep_disable(f_rkusb->in_ep);
+
+ if (f_rkusb->out_req) {
+ free(f_rkusb->out_req->buf);
+ usb_ep_free_request(f_rkusb->out_ep, f_rkusb->out_req);
+ f_rkusb->out_req = NULL;
+ }
+ if (f_rkusb->in_req) {
+ free(f_rkusb->in_req->buf);
+ usb_ep_free_request(f_rkusb->in_ep, f_rkusb->in_req);
+ f_rkusb->in_req = NULL;
+ }
+ if (f_rkusb->buf_head) {
+ free(f_rkusb->buf_head);
+ f_rkusb->buf_head = NULL;
+ f_rkusb->buf = NULL;
+ }
+}
+
+static struct usb_request *rockusb_start_ep(struct usb_ep *ep)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, 0);
+ if (!req)
+ return NULL;
+
+ req->length = EP_BUFFER_SIZE;
+ req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+ memset(req->buf, 0, req->length);
+
+ return req;
+}
+
+static int rockusb_set_alt(struct usb_function *f, unsigned int interface,
+ unsigned int alt)
+{
+ int ret;
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_gadget *gadget = cdev->gadget;
+ struct f_rockusb *f_rkusb = func_to_rockusb(f);
+ const struct usb_endpoint_descriptor *d;
+
+ debug("%s: func: %s intf: %d alt: %d\n",
+ __func__, f->name, interface, alt);
+
+ d = rkusb_ep_desc(gadget, &fs_ep_out, &hs_ep_out);
+ ret = usb_ep_enable(f_rkusb->out_ep, d);
+ if (ret) {
+ printf("failed to enable out ep\n");
+ return ret;
+ }
+
+ f_rkusb->out_req = rockusb_start_ep(f_rkusb->out_ep);
+ if (!f_rkusb->out_req) {
+ printf("failed to alloc out req\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ f_rkusb->out_req->complete = rx_handler_command;
+
+ d = rkusb_ep_desc(gadget, &fs_ep_in, &hs_ep_in);
+ ret = usb_ep_enable(f_rkusb->in_ep, d);
+ if (ret) {
+ printf("failed to enable in ep\n");
+ goto err;
+ }
+
+ f_rkusb->in_req = rockusb_start_ep(f_rkusb->in_ep);
+ if (!f_rkusb->in_req) {
+ printf("failed alloc req in\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ f_rkusb->in_req->complete = rockusb_complete;
+
+ ret = usb_ep_queue(f_rkusb->out_ep, f_rkusb->out_req, 0);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ rockusb_disable(f);
+ return ret;
+}
+
+static int rockusb_add(struct usb_configuration *c)
+{
+ struct f_rockusb *f_rkusb = get_rkusb();
+ int status;
+
+ debug("%s: cdev: 0x%p\n", __func__, c->cdev);
+
+ f_rkusb->usb_function.name = "f_rockusb";
+ f_rkusb->usb_function.bind = rockusb_bind;
+ f_rkusb->usb_function.unbind = rockusb_unbind;
+ f_rkusb->usb_function.set_alt = rockusb_set_alt;
+ f_rkusb->usb_function.disable = rockusb_disable;
+ f_rkusb->usb_function.strings = rkusb_strings;
+
+ status = usb_add_function(c, &f_rkusb->usb_function);
+ if (status) {
+ free(f_rkusb->buf_head);
+ free(f_rkusb);
+ rockusb_func = NULL;
+ }
+ return status;
+}
+
+void rockusb_dev_init(char *dev_type, int dev_index)
+{
+ struct f_rockusb *f_rkusb = get_rkusb();
+
+ f_rkusb->dev_type = dev_type;
+ f_rkusb->dev_index = dev_index;
+}
+
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_rockusb, rockusb_add);
+
+static int rockusb_tx_write(const char *buffer, unsigned int buffer_size)
+{
+ struct usb_request *in_req = rockusb_func->in_req;
+ int ret;
+
+ memcpy(in_req->buf, buffer, buffer_size);
+ in_req->length = buffer_size;
+ debug("Transferring 0x%x bytes\n", buffer_size);
+ usb_ep_dequeue(rockusb_func->in_ep, in_req);
+ ret = usb_ep_queue(rockusb_func->in_ep, in_req, 0);
+ if (ret)
+ printf("Error %d on queue\n", ret);
+ return 0;
+}
+
+static int rockusb_tx_write_str(const char *buffer)
+{
+ return rockusb_tx_write(buffer, strlen(buffer));
+}
+
+#ifdef DEBUG
+static void printcbw(char *buf)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw,
+ sizeof(struct fsg_bulk_cb_wrap));
+
+ memcpy((char *)cbw, buf, USB_BULK_CB_WRAP_LEN);
+
+ debug("cbw: signature:%x\n", cbw->signature);
+ debug("cbw: tag=%x\n", cbw->tag);
+ debug("cbw: data_transfer_length=%d\n", cbw->data_transfer_length);
+ debug("cbw: flags=%x\n", cbw->flags);
+ debug("cbw: lun=%d\n", cbw->lun);
+ debug("cbw: length=%d\n", cbw->length);
+ debug("cbw: ucOperCode=%x\n", cbw->CDB[0]);
+ debug("cbw: ucReserved=%x\n", cbw->CDB[1]);
+ debug("cbw: dwAddress:%x %x %x %x\n", cbw->CDB[5], cbw->CDB[4],
+ cbw->CDB[3], cbw->CDB[2]);
+ debug("cbw: ucReserved2=%x\n", cbw->CDB[6]);
+ debug("cbw: uslength:%x %x\n", cbw->CDB[8], cbw->CDB[7]);
+}
+
+static void printcsw(char *buf)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct bulk_cs_wrap, csw,
+ sizeof(struct bulk_cs_wrap));
+ memcpy((char *)csw, buf, USB_BULK_CS_WRAP_LEN);
+ debug("csw: signature:%x\n", csw->signature);
+ debug("csw: tag:%x\n", csw->tag);
+ debug("csw: residue:%x\n", csw->residue);
+ debug("csw: status:%x\n", csw->status);
+}
+#endif
+
+static int rockusb_tx_write_csw(u32 tag, int residue, u8 status, int size)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct bulk_cs_wrap, csw,
+ sizeof(struct bulk_cs_wrap));
+ csw->signature = cpu_to_le32(USB_BULK_CS_SIG);
+ csw->tag = tag;
+ csw->residue = cpu_to_be32(residue);
+ csw->status = status;
+#ifdef DEBUG
+ printcsw((char *)csw);
+#endif
+ return rockusb_tx_write((char *)csw, size);
+}
+
+static void tx_handler_send_csw(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_rockusb *f_rkusb = get_rkusb();
+ int status = req->status;
+
+ if (status)
+ debug("status: %d ep '%s' trans: %d\n",
+ status, ep->name, req->actual);
+
+ /* Return back to default in_req complete function after sending CSW */
+ req->complete = rockusb_complete;
+ rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_GOOD, USB_BULK_CS_WRAP_LEN);
+}
+
+static unsigned int rx_bytes_expected(struct usb_ep *ep)
+{
+ struct f_rockusb *f_rkusb = get_rkusb();
+ int rx_remain = f_rkusb->dl_size - f_rkusb->dl_bytes;
+ unsigned int rem;
+ unsigned int maxpacket = ep->maxpacket;
+
+ if (rx_remain <= 0)
+ return 0;
+ else if (rx_remain > EP_BUFFER_SIZE)
+ return EP_BUFFER_SIZE;
+
+ rem = rx_remain % maxpacket;
+ if (rem > 0)
+ rx_remain = rx_remain + (maxpacket - rem);
+
+ return rx_remain;
+}
+
+/* usb_request complete call back to handle upload image */
+static void tx_handler_ul_image(struct usb_ep *ep, struct usb_request *req)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(char, rbuffer, RKBLOCK_BUF_SIZE);
+ struct f_rockusb *f_rkusb = get_rkusb();
+ struct usb_request *in_req = rockusb_func->in_req;
+ int ret;
+
+ /* Print error status of previous transfer */
+ if (req->status)
+ debug("status: %d ep '%s' trans: %d len %d\n", req->status,
+ ep->name, req->actual, req->length);
+
+ /* On transfer complete reset in_req and feedback host with CSW_GOOD */
+ if (f_rkusb->ul_bytes >= f_rkusb->ul_size) {
+ in_req->length = 0;
+ in_req->complete = rockusb_complete;
+
+ rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_GOOD,
+ USB_BULK_CS_WRAP_LEN);
+ return;
+ }
+
+ /* Proceed with current chunk */
+ unsigned int transfer_size = f_rkusb->ul_size - f_rkusb->ul_bytes;
+
+ if (transfer_size > RKBLOCK_BUF_SIZE)
+ transfer_size = RKBLOCK_BUF_SIZE;
+ /* Read at least one block */
+ unsigned int blkcount = (transfer_size + f_rkusb->desc->blksz - 1) /
+ f_rkusb->desc->blksz;
+
+ debug("ul %x bytes, %x blks, read lba %x, ul_size:%x, ul_bytes:%x, ",
+ transfer_size, blkcount, f_rkusb->lba,
+ f_rkusb->ul_size, f_rkusb->ul_bytes);
+
+ int blks = blk_dread(f_rkusb->desc, f_rkusb->lba, blkcount, rbuffer);
+
+ if (blks != blkcount) {
+ printf("failed reading from device %s: %d\n",
+ f_rkusb->dev_type, f_rkusb->dev_index);
+ rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL,
+ USB_BULK_CS_WRAP_LEN);
+ return;
+ }
+ f_rkusb->lba += blkcount;
+ f_rkusb->ul_bytes += transfer_size;
+
+ /* Proceed with USB request */
+ memcpy(in_req->buf, rbuffer, transfer_size);
+ in_req->length = transfer_size;
+ in_req->complete = tx_handler_ul_image;
+ debug("Uploading 0x%x bytes\n", transfer_size);
+ usb_ep_dequeue(rockusb_func->in_ep, in_req);
+ ret = usb_ep_queue(rockusb_func->in_ep, in_req, 0);
+ if (ret)
+ printf("Error %d on queue\n", ret);
+}
+
+/* usb_request complete call back to handle down load image */
+static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_rockusb *f_rkusb = get_rkusb();
+ unsigned int transfer_size = 0;
+ const unsigned char *buffer = req->buf;
+ unsigned int buffer_size = req->actual;
+
+ transfer_size = f_rkusb->dl_size - f_rkusb->dl_bytes;
+
+ if (req->status != 0) {
+ printf("Bad status: %d\n", req->status);
+ rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL,
+ USB_BULK_CS_WRAP_LEN);
+ return;
+ }
+
+ if (buffer_size < transfer_size)
+ transfer_size = buffer_size;
+
+ memcpy((void *)f_rkusb->buf, buffer, transfer_size);
+ f_rkusb->dl_bytes += transfer_size;
+ int blks = 0, blkcnt = transfer_size / f_rkusb->desc->blksz;
+
+ debug("dl %x bytes, %x blks, write lba %x, dl_size:%x, dl_bytes:%x, ",
+ transfer_size, blkcnt, f_rkusb->lba, f_rkusb->dl_size,
+ f_rkusb->dl_bytes);
+ blks = blk_dwrite(f_rkusb->desc, f_rkusb->lba, blkcnt, f_rkusb->buf);
+ if (blks != blkcnt) {
+ printf("failed writing to device %s: %d\n", f_rkusb->dev_type,
+ f_rkusb->dev_index);
+ rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL,
+ USB_BULK_CS_WRAP_LEN);
+ return;
+ }
+ f_rkusb->lba += blkcnt;
+
+ /* Check if transfer is done */
+ if (f_rkusb->dl_bytes >= f_rkusb->dl_size) {
+ req->complete = rx_handler_command;
+ req->length = EP_BUFFER_SIZE;
+ f_rkusb->buf = f_rkusb->buf_head;
+ debug("transfer 0x%x bytes done\n", f_rkusb->dl_size);
+ f_rkusb->dl_size = 0;
+ rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_GOOD,
+ USB_BULK_CS_WRAP_LEN);
+ } else {
+ req->length = rx_bytes_expected(ep);
+ if (f_rkusb->buf == f_rkusb->buf_head)
+ f_rkusb->buf = f_rkusb->buf_head + EP_BUFFER_SIZE;
+ else
+ f_rkusb->buf = f_rkusb->buf_head;
+
+ debug("remain %x bytes, %lx sectors\n", req->length,
+ req->length / f_rkusb->desc->blksz);
+ }
+
+ req->actual = 0;
+ usb_ep_queue(ep, req, 0);
+}
+
+static void cb_test_unit_ready(struct usb_ep *ep, struct usb_request *req)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw,
+ sizeof(struct fsg_bulk_cb_wrap));
+
+ memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN);
+
+ rockusb_tx_write_csw(cbw->tag, cbw->data_transfer_length,
+ CSW_GOOD, USB_BULK_CS_WRAP_LEN);
+}
+
+static void cb_read_storage_id(struct usb_ep *ep, struct usb_request *req)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw,
+ sizeof(struct fsg_bulk_cb_wrap));
+ struct f_rockusb *f_rkusb = get_rkusb();
+ char emmc_id[] = "EMMC ";
+
+ printf("read storage id\n");
+ memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN);
+
+ /* Prepare for sending subsequent CSW_GOOD */
+ f_rkusb->tag = cbw->tag;
+ f_rkusb->in_req->complete = tx_handler_send_csw;
+
+ rockusb_tx_write_str(emmc_id);
+}
+
+int __weak rk_get_bootrom_chip_version(unsigned int *chip_info, int size)
+{
+ return 0;
+}
+
+static void cb_get_chip_version(struct usb_ep *ep, struct usb_request *req)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw,
+ sizeof(struct fsg_bulk_cb_wrap));
+ struct f_rockusb *f_rkusb = get_rkusb();
+ unsigned int chip_info[4], i;
+
+ memset(chip_info, 0, sizeof(chip_info));
+ rk_get_bootrom_chip_version(chip_info, 4);
+
+ /*
+ * Chip Version is a string saved in BOOTROM address space Little Endian
+ *
+ * Ex for rk3288: 0x33323041 0x32303134 0x30383133 0x56323030
+ * which brings: 320A20140813V200
+ *
+ * Note that memory version do invert MSB/LSB so printing the char
+ * buffer will show: A02341023180002V
+ */
+ printf("read chip version: ");
+ for (i = 0; i < 4; i++) {
+ printf("%c%c%c%c",
+ (chip_info[i] >> 24) & 0xFF,
+ (chip_info[i] >> 16) & 0xFF,
+ (chip_info[i] >> 8) & 0xFF,
+ (chip_info[i] >> 0) & 0xFF);
+ }
+ printf("\n");
+ memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN);
+
+ /* Prepare for sending subsequent CSW_GOOD */
+ f_rkusb->tag = cbw->tag;
+ f_rkusb->in_req->complete = tx_handler_send_csw;
+
+ rockusb_tx_write((char *)chip_info, sizeof(chip_info));
+}
+
+static void cb_read_lba(struct usb_ep *ep, struct usb_request *req)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw,
+ sizeof(struct fsg_bulk_cb_wrap));
+ struct f_rockusb *f_rkusb = get_rkusb();
+ int sector_count;
+
+ memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN);
+ sector_count = (int)get_unaligned_be16(&cbw->CDB[7]);
+ f_rkusb->tag = cbw->tag;
+
+ if (!f_rkusb->desc) {
+ char *type = f_rkusb->dev_type;
+ int index = f_rkusb->dev_index;
+
+ f_rkusb->desc = blk_get_dev(type, index);
+ if (!f_rkusb->desc ||
+ f_rkusb->desc->type == DEV_TYPE_UNKNOWN) {
+ printf("invalid device \"%s\", %d\n", type, index);
+ rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL,
+ USB_BULK_CS_WRAP_LEN);
+ return;
+ }
+ }
+
+ f_rkusb->lba = get_unaligned_be32(&cbw->CDB[2]);
+ f_rkusb->ul_size = sector_count * f_rkusb->desc->blksz;
+ f_rkusb->ul_bytes = 0;
+
+ debug("require read %x bytes, %x sectors from lba %x\n",
+ f_rkusb->ul_size, sector_count, f_rkusb->lba);
+
+ if (f_rkusb->ul_size == 0) {
+ rockusb_tx_write_csw(cbw->tag, cbw->data_transfer_length,
+ CSW_FAIL, USB_BULK_CS_WRAP_LEN);
+ return;
+ }
+
+ /* Start right now sending first chunk */
+ tx_handler_ul_image(ep, req);
+}
+
+static void cb_write_lba(struct usb_ep *ep, struct usb_request *req)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw,
+ sizeof(struct fsg_bulk_cb_wrap));
+ struct f_rockusb *f_rkusb = get_rkusb();
+ int sector_count;
+
+ memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN);
+ sector_count = (int)get_unaligned_be16(&cbw->CDB[7]);
+ f_rkusb->tag = cbw->tag;
+
+ if (!f_rkusb->desc) {
+ char *type = f_rkusb->dev_type;
+ int index = f_rkusb->dev_index;
+
+ f_rkusb->desc = blk_get_dev(type, index);
+ if (!f_rkusb->desc ||
+ f_rkusb->desc->type == DEV_TYPE_UNKNOWN) {
+ printf("invalid device \"%s\", %d\n", type, index);
+ rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL,
+ USB_BULK_CS_WRAP_LEN);
+ return;
+ }
+ }
+
+ f_rkusb->lba = get_unaligned_be32(&cbw->CDB[2]);
+ f_rkusb->dl_size = sector_count * f_rkusb->desc->blksz;
+ f_rkusb->dl_bytes = 0;
+
+ debug("require write %x bytes, %x sectors to lba %x\n",
+ f_rkusb->dl_size, sector_count, f_rkusb->lba);
+
+ if (f_rkusb->dl_size == 0) {
+ rockusb_tx_write_csw(cbw->tag, cbw->data_transfer_length,
+ CSW_FAIL, USB_BULK_CS_WRAP_LEN);
+ } else {
+ req->complete = rx_handler_dl_image;
+ req->length = rx_bytes_expected(ep);
+ }
+}
+
+static void cb_erase_lba(struct usb_ep *ep, struct usb_request *req)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw,
+ sizeof(struct fsg_bulk_cb_wrap));
+ struct f_rockusb *f_rkusb = get_rkusb();
+ int sector_count, lba, blks;
+
+ memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN);
+ sector_count = (int)get_unaligned_be16(&cbw->CDB[7]);
+ f_rkusb->tag = cbw->tag;
+
+ if (!f_rkusb->desc) {
+ char *type = f_rkusb->dev_type;
+ int index = f_rkusb->dev_index;
+
+ f_rkusb->desc = blk_get_dev(type, index);
+ if (!f_rkusb->desc ||
+ f_rkusb->desc->type == DEV_TYPE_UNKNOWN) {
+ printf("invalid device \"%s\", %d\n", type, index);
+ rockusb_tx_write_csw(f_rkusb->tag, 0, CSW_FAIL,
+ USB_BULK_CS_WRAP_LEN);
+ return;
+ }
+ }
+
+ lba = get_unaligned_be32(&cbw->CDB[2]);
+
+ debug("require erase %x sectors from lba %x\n",
+ sector_count, lba);
+
+ blks = blk_derase(f_rkusb->desc, lba, sector_count);
+ if (blks != sector_count) {
+ printf("failed erasing device %s: %d\n", f_rkusb->dev_type,
+ f_rkusb->dev_index);
+ rockusb_tx_write_csw(f_rkusb->tag,
+ cbw->data_transfer_length, CSW_FAIL,
+ USB_BULK_CS_WRAP_LEN);
+ return;
+ }
+
+ rockusb_tx_write_csw(cbw->tag, cbw->data_transfer_length, CSW_GOOD,
+ USB_BULK_CS_WRAP_LEN);
+}
+
+void __weak rkusb_set_reboot_flag(int flag)
+{
+ struct f_rockusb *f_rkusb = get_rkusb();
+
+ printf("rockkusb set reboot flag: %d\n", f_rkusb->reboot_flag);
+}
+
+static void compl_do_reset(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_rockusb *f_rkusb = get_rkusb();
+
+ rkusb_set_reboot_flag(f_rkusb->reboot_flag);
+ do_reset(NULL, 0, 0, NULL);
+}
+
+static void cb_reboot(struct usb_ep *ep, struct usb_request *req)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw,
+ sizeof(struct fsg_bulk_cb_wrap));
+ struct f_rockusb *f_rkusb = get_rkusb();
+
+ memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN);
+ f_rkusb->reboot_flag = cbw->CDB[1];
+ rockusb_func->in_req->complete = compl_do_reset;
+ rockusb_tx_write_csw(cbw->tag, cbw->data_transfer_length, CSW_GOOD,
+ USB_BULK_CS_WRAP_LEN);
+}
+
+static void cb_not_support(struct usb_ep *ep, struct usb_request *req)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw,
+ sizeof(struct fsg_bulk_cb_wrap));
+
+ memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN);
+ printf("Rockusb command %x not support yet\n", cbw->CDB[0]);
+ rockusb_tx_write_csw(cbw->tag, 0, CSW_FAIL, USB_BULK_CS_WRAP_LEN);
+}
+
+static const struct cmd_dispatch_info cmd_dispatch_info[] = {
+ {
+ .cmd = K_FW_TEST_UNIT_READY,
+ .cb = cb_test_unit_ready,
+ },
+ {
+ .cmd = K_FW_READ_FLASH_ID,
+ .cb = cb_read_storage_id,
+ },
+ {
+ .cmd = K_FW_SET_DEVICE_ID,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_TEST_BAD_BLOCK,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_READ_10,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_WRITE_10,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_ERASE_10,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_WRITE_SPARE,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_READ_SPARE,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_ERASE_10_FORCE,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_GET_VERSION,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_LBA_READ_10,
+ .cb = cb_read_lba,
+ },
+ {
+ .cmd = K_FW_LBA_WRITE_10,
+ .cb = cb_write_lba,
+ },
+ {
+ .cmd = K_FW_ERASE_SYS_DISK,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_SDRAM_READ_10,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_SDRAM_WRITE_10,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_SDRAM_EXECUTE,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_READ_FLASH_INFO,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_GET_CHIP_VER,
+ .cb = cb_get_chip_version,
+ },
+ {
+ .cmd = K_FW_LOW_FORMAT,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_SET_RESET_FLAG,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_SPI_READ_10,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_SPI_WRITE_10,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_LBA_ERASE_10,
+ .cb = cb_erase_lba,
+ },
+ {
+ .cmd = K_FW_SESSION,
+ .cb = cb_not_support,
+ },
+ {
+ .cmd = K_FW_RESET,
+ .cb = cb_reboot,
+ },
+};
+
+static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
+{
+ void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL;
+
+ ALLOC_CACHE_ALIGN_BUFFER(struct fsg_bulk_cb_wrap, cbw,
+ sizeof(struct fsg_bulk_cb_wrap));
+ char *cmdbuf = req->buf;
+ int i;
+
+ if (req->status || req->length == 0)
+ return;
+
+ memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN);
+#ifdef DEBUG
+ printcbw(req->buf);
+#endif
+
+ for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) {
+ if (cmd_dispatch_info[i].cmd == cbw->CDB[0]) {
+ func_cb = cmd_dispatch_info[i].cb;
+ break;
+ }
+ }
+
+ if (!func_cb) {
+ printf("unknown command: %s\n", (char *)req->buf);
+ rockusb_tx_write_str("FAILunknown command");
+ } else {
+ if (req->actual < req->length) {
+ u8 *buf = (u8 *)req->buf;
+
+ buf[req->actual] = 0;
+ func_cb(ep, req);
+ } else {
+ puts("buffer overflow\n");
+ rockusb_tx_write_str("FAILbuffer overflow");
+ }
+ }
+
+ *cmdbuf = '\0';
+ req->actual = 0;
+ usb_ep_queue(ep, req, 0);
+}
diff --git a/roms/u-boot/drivers/usb/gadget/f_sdp.c b/roms/u-boot/drivers/usb/gadget/f_sdp.c
new file mode 100644
index 000000000..e48aa2f90
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/f_sdp.c
@@ -0,0 +1,956 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_sdp.c -- USB HID Serial Download Protocol
+ *
+ * Copyright (C) 2017 Toradex
+ * Author: Stefan Agner <stefan.agner@toradex.com>
+ *
+ * This file implements the Serial Download Protocol (SDP) as specified in
+ * the i.MX 6 Reference Manual. The SDP is a USB HID based protocol and
+ * allows to download images directly to memory. The implementation
+ * works with the imx_loader (imx_usb) USB client software on host side.
+ *
+ * Not all commands are implemented, e.g. WRITE_REGISTER, DCD_WRITE and
+ * SKIP_DCD_HEADER are only stubs.
+ *
+ * Parts of the implementation are based on f_dfu and f_thor.
+ */
+
+#include <errno.h>
+#include <common.h>
+#include <console.h>
+#include <env.h>
+#include <log.h>
+#include <malloc.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+#include <asm/io.h>
+#include <g_dnl.h>
+#include <sdp.h>
+#include <spl.h>
+#include <image.h>
+#include <imximage.h>
+#include <watchdog.h>
+
+#define HID_REPORT_ID_MASK 0x000000ff
+
+/*
+ * HID class requests
+ */
+#define HID_REQ_GET_REPORT 0x01
+#define HID_REQ_GET_IDLE 0x02
+#define HID_REQ_GET_PROTOCOL 0x03
+#define HID_REQ_SET_REPORT 0x09
+#define HID_REQ_SET_IDLE 0x0A
+#define HID_REQ_SET_PROTOCOL 0x0B
+
+#define HID_USAGE_PAGE_LEN 76
+
+struct hid_report {
+ u8 usage_page[HID_USAGE_PAGE_LEN];
+} __packed;
+
+#define SDP_READ_REGISTER 0x0101
+#define SDP_WRITE_REGISTER 0x0202
+#define SDP_WRITE_FILE 0x0404
+#define SDP_ERROR_STATUS 0x0505
+#define SDP_DCD_WRITE 0x0a0a
+#define SDP_JUMP_ADDRESS 0x0b0b
+#define SDP_SKIP_DCD_HEADER 0x0c0c
+
+#define SDP_SECURITY_CLOSED 0x12343412
+#define SDP_SECURITY_OPEN 0x56787856
+
+#define SDP_WRITE_FILE_COMPLETE 0x88888888
+#define SDP_WRITE_REGISTER_COMPLETE 0x128A8A12
+#define SDP_SKIP_DCD_HEADER_COMPLETE 0x900DD009
+#define SDP_ERROR_IMXHEADER 0x000a0533
+
+#define SDP_COMMAND_LEN 16
+
+#define SDP_HID_PACKET_SIZE_EP1 1024
+
+#define SDP_EXIT 1
+
+struct sdp_command {
+ u16 cmd;
+ u32 addr;
+ u8 format;
+ u32 cnt;
+ u32 data;
+ u8 rsvd;
+} __packed;
+
+enum sdp_state {
+ SDP_STATE_IDLE,
+ SDP_STATE_RX_CMD,
+ SDP_STATE_RX_DCD_DATA,
+ SDP_STATE_RX_FILE_DATA,
+ SDP_STATE_RX_FILE_DATA_BUSY,
+ SDP_STATE_TX_SEC_CONF,
+ SDP_STATE_TX_SEC_CONF_BUSY,
+ SDP_STATE_TX_REGISTER,
+ SDP_STATE_TX_REGISTER_BUSY,
+ SDP_STATE_TX_STATUS,
+ SDP_STATE_TX_STATUS_BUSY,
+ SDP_STATE_JUMP,
+};
+
+struct f_sdp {
+ struct usb_function usb_function;
+
+ struct usb_descriptor_header **function;
+
+ u8 altsetting;
+ enum sdp_state state;
+ enum sdp_state next_state;
+ u32 dnl_address;
+ u32 dnl_bytes;
+ u32 dnl_bytes_remaining;
+ u32 jmp_address;
+ bool always_send_status;
+ u32 error_status;
+
+ /* EP0 request */
+ struct usb_request *req;
+
+ /* EP1 IN */
+ struct usb_ep *in_ep;
+ struct usb_request *in_req;
+ /* EP1 OUT */
+ struct usb_ep *out_ep;
+ struct usb_request *out_req;
+
+ bool configuration_done;
+ bool ep_int_enable;
+};
+
+static struct f_sdp *sdp_func;
+
+static inline struct f_sdp *func_to_sdp(struct usb_function *f)
+{
+ return container_of(f, struct f_sdp, usb_function);
+}
+
+static struct usb_interface_descriptor sdp_intf_runtime = {
+ .bLength = sizeof(sdp_intf_runtime),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ /* .iInterface = DYNAMIC */
+};
+
+/* HID configuration */
+static struct usb_class_hid_descriptor sdp_hid_desc = {
+ .bLength = sizeof(sdp_hid_desc),
+ .bDescriptorType = USB_DT_CS_DEVICE,
+
+ .bcdCDC = __constant_cpu_to_le16(0x0110),
+ .bCountryCode = 0,
+ .bNumDescriptors = 1,
+
+ .bDescriptorType0 = USB_DT_HID_REPORT,
+ .wDescriptorLength0 = HID_USAGE_PAGE_LEN,
+};
+
+static struct usb_endpoint_descriptor in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/
+
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 64,
+ .bInterval = 1,
+};
+
+static struct usb_endpoint_descriptor out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/
+
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 64,
+ .bInterval = 1,
+};
+
+static struct usb_endpoint_descriptor in_hs_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/
+
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 512,
+ .bInterval = 3,
+};
+
+static struct usb_endpoint_descriptor out_hs_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/
+
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = SDP_HID_PACKET_SIZE_EP1,
+ .bInterval = 3,
+};
+
+static struct usb_descriptor_header *sdp_runtime_descs[] = {
+ (struct usb_descriptor_header *)&sdp_intf_runtime,
+ (struct usb_descriptor_header *)&sdp_hid_desc,
+ (struct usb_descriptor_header *)&in_desc,
+ (struct usb_descriptor_header *)&out_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *sdp_runtime_hs_descs[] = {
+ (struct usb_descriptor_header *)&sdp_intf_runtime,
+ (struct usb_descriptor_header *)&sdp_hid_desc,
+ (struct usb_descriptor_header *)&in_hs_desc,
+ (struct usb_descriptor_header *)&out_hs_desc,
+ NULL,
+};
+
+/* This is synchronized with what the SoC implementation reports */
+static struct hid_report sdp_hid_report = {
+ .usage_page = {
+ 0x06, 0x00, 0xff, /* Usage Page */
+ 0x09, 0x01, /* Usage (Pointer?) */
+ 0xa1, 0x01, /* Collection */
+
+ 0x85, 0x01, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x08, /* Report Size */
+ 0x95, 0x10, /* Report Count */
+ 0x91, 0x02, /* Output Data */
+
+ 0x85, 0x02, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x80, /* Report Size 128 */
+ 0x95, 0x40, /* Report Count */
+ 0x91, 0x02, /* Output Data */
+
+ 0x85, 0x03, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x08, /* Report Size 8 */
+ 0x95, 0x04, /* Report Count */
+ 0x81, 0x02, /* Input Data */
+
+ 0x85, 0x04, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x08, /* Report Size 8 */
+ 0x95, 0x40, /* Report Count */
+ 0x81, 0x02, /* Input Data */
+ 0xc0
+ },
+};
+
+static const char sdp_name[] = "Serial Downloader Protocol";
+
+/*
+ * static strings, in UTF-8
+ */
+static struct usb_string strings_sdp_generic[] = {
+ [0].s = sdp_name,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_sdp_generic = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_sdp_generic,
+};
+
+static struct usb_gadget_strings *sdp_generic_strings[] = {
+ &stringtab_sdp_generic,
+ NULL,
+};
+
+static inline void *sdp_ptr(u32 val)
+{
+ return (void *)(uintptr_t)val;
+}
+
+static void sdp_rx_command_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_sdp *sdp = req->context;
+ int status = req->status;
+ u8 *data = req->buf;
+ u8 report = data[0];
+
+ if (status != 0) {
+ pr_err("Status: %d\n", status);
+ return;
+ }
+
+ if (report != 1) {
+ pr_err("Unexpected report %d\n", report);
+ return;
+ }
+
+ struct sdp_command *cmd = req->buf + 1;
+
+ debug("%s: command: %04x, addr: %08x, cnt: %u\n",
+ __func__, be16_to_cpu(cmd->cmd),
+ be32_to_cpu(cmd->addr), be32_to_cpu(cmd->cnt));
+
+ switch (be16_to_cpu(cmd->cmd)) {
+ case SDP_READ_REGISTER:
+ sdp->always_send_status = false;
+ sdp->error_status = 0x0;
+
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->dnl_address = be32_to_cpu(cmd->addr);
+ sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
+ sdp->next_state = SDP_STATE_TX_REGISTER;
+ printf("Reading %d registers at 0x%08x... ",
+ sdp->dnl_bytes_remaining, sdp->dnl_address);
+ break;
+ case SDP_WRITE_FILE:
+ sdp->always_send_status = true;
+ sdp->error_status = SDP_WRITE_FILE_COMPLETE;
+
+ sdp->state = SDP_STATE_RX_FILE_DATA;
+ sdp->dnl_address = cmd->addr ? be32_to_cpu(cmd->addr) : CONFIG_SDP_LOADADDR;
+ sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
+ sdp->dnl_bytes = sdp->dnl_bytes_remaining;
+ sdp->next_state = SDP_STATE_IDLE;
+
+ printf("Downloading file of size %d to 0x%08x... ",
+ sdp->dnl_bytes_remaining, sdp->dnl_address);
+
+ break;
+ case SDP_ERROR_STATUS:
+ sdp->always_send_status = true;
+ sdp->error_status = 0;
+
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->next_state = SDP_STATE_IDLE;
+ break;
+ case SDP_DCD_WRITE:
+ sdp->always_send_status = true;
+ sdp->error_status = SDP_WRITE_REGISTER_COMPLETE;
+
+ sdp->state = SDP_STATE_RX_DCD_DATA;
+ sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
+ sdp->next_state = SDP_STATE_IDLE;
+ break;
+ case SDP_JUMP_ADDRESS:
+ sdp->always_send_status = false;
+ sdp->error_status = 0;
+
+ sdp->jmp_address = cmd->addr ? be32_to_cpu(cmd->addr) : CONFIG_SDP_LOADADDR;
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->next_state = SDP_STATE_JUMP;
+ break;
+ case SDP_SKIP_DCD_HEADER:
+ sdp->always_send_status = true;
+ sdp->error_status = SDP_SKIP_DCD_HEADER_COMPLETE;
+
+ /* Ignore command, DCD not supported anyway */
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->next_state = SDP_STATE_IDLE;
+ break;
+ default:
+ pr_err("Unknown command: %04x\n", be16_to_cpu(cmd->cmd));
+ }
+}
+
+static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_sdp *sdp = req->context;
+ int status = req->status;
+ u8 *data = req->buf;
+ u8 report = data[0];
+ int datalen = req->actual - 1;
+
+ if (status != 0) {
+ pr_err("Status: %d\n", status);
+ return;
+ }
+
+ if (report != 2) {
+ pr_err("Unexpected report %d\n", report);
+ return;
+ }
+
+ if (sdp->dnl_bytes_remaining < datalen) {
+ /*
+ * Some USB stacks require to send a complete buffer as
+ * specified in the HID descriptor. This leads to longer
+ * transfers than the file length, no problem for us.
+ */
+ sdp->dnl_bytes_remaining = 0;
+ } else {
+ sdp->dnl_bytes_remaining -= datalen;
+ }
+
+ if (sdp->state == SDP_STATE_RX_FILE_DATA_BUSY) {
+ memcpy(sdp_ptr(sdp->dnl_address), req->buf + 1, datalen);
+ sdp->dnl_address += datalen;
+ }
+
+ if (sdp->dnl_bytes_remaining) {
+ sdp->state = SDP_STATE_RX_FILE_DATA;
+ return;
+ }
+
+#ifndef CONFIG_SPL_BUILD
+ env_set_hex("filesize", sdp->dnl_bytes);
+#endif
+ printf("done\n");
+
+ switch (sdp->state) {
+ case SDP_STATE_RX_FILE_DATA_BUSY:
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ break;
+ case SDP_STATE_RX_DCD_DATA:
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ break;
+ default:
+ pr_err("Invalid state: %d\n", sdp->state);
+ }
+}
+
+static void sdp_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_sdp *sdp = req->context;
+ int status = req->status;
+
+ if (status != 0) {
+ pr_err("Status: %d\n", status);
+ return;
+ }
+
+ switch (sdp->state) {
+ case SDP_STATE_TX_SEC_CONF_BUSY:
+ /* Not all commands require status report */
+ if (sdp->always_send_status || sdp->error_status)
+ sdp->state = SDP_STATE_TX_STATUS;
+ else
+ sdp->state = sdp->next_state;
+
+ break;
+ case SDP_STATE_TX_STATUS_BUSY:
+ sdp->state = sdp->next_state;
+ break;
+ case SDP_STATE_TX_REGISTER_BUSY:
+ if (sdp->dnl_bytes_remaining)
+ sdp->state = SDP_STATE_TX_REGISTER;
+ else
+ sdp->state = SDP_STATE_IDLE;
+ break;
+ default:
+ pr_err("Wrong State: %d\n", sdp->state);
+ sdp->state = SDP_STATE_IDLE;
+ break;
+ }
+ debug("%s complete --> %d, %d/%d\n", ep->name,
+ status, req->actual, req->length);
+}
+
+static int sdp_setup(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_sdp *sdp = 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%04x len: 0x%04x\n", w_value, len);
+ debug("req_type: 0x%02x ctrl->bRequest: 0x%02x sdp->state: %d\n",
+ req_type, ctrl->bRequest, sdp->state);
+
+ if (req_type == USB_TYPE_STANDARD) {
+ if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) {
+ /* Send HID report descriptor */
+ value = min(len, (u16) sizeof(sdp_hid_report));
+ memcpy(req->buf, &sdp_hid_report, value);
+ sdp->configuration_done = true;
+ }
+ }
+
+ if (req_type == USB_TYPE_CLASS) {
+ int report = w_value & HID_REPORT_ID_MASK;
+
+ /* HID (SDP) request */
+ switch (ctrl->bRequest) {
+ case HID_REQ_SET_REPORT:
+ switch (report) {
+ case 1:
+ value = SDP_COMMAND_LEN + 1;
+ req->complete = sdp_rx_command_complete;
+ sdp_func->ep_int_enable = false;
+ break;
+ case 2:
+ value = len;
+ req->complete = sdp_rx_data_complete;
+ sdp_func->state = SDP_STATE_RX_FILE_DATA_BUSY;
+ break;
+ }
+ }
+ }
+
+ 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 sdp_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_gadget *gadget = c->cdev->gadget;
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_sdp *sdp = func_to_sdp(f);
+ int rv = 0, id;
+
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ sdp_intf_runtime.bInterfaceNumber = id;
+
+ struct usb_ep *ep_in, *ep_out;
+
+ /* allocate instance-specific endpoints */
+ ep_in = usb_ep_autoconfig(gadget, &in_desc);
+ if (!ep_in) {
+ rv = -ENODEV;
+ goto error;
+ }
+
+ ep_out = usb_ep_autoconfig(gadget, &out_desc);
+ if (!ep_out) {
+ rv = -ENODEV;
+ goto error;
+ }
+
+ if (gadget_is_dualspeed(gadget)) {
+ /* Assume endpoint addresses are the same for both speeds */
+ in_hs_desc.bEndpointAddress = in_desc.bEndpointAddress;
+ out_hs_desc.bEndpointAddress = out_desc.bEndpointAddress;
+ }
+
+ sdp->in_ep = ep_in; /* Store IN EP for enabling @ setup */
+ sdp->out_ep = ep_out;
+
+ cdev->req->context = sdp;
+
+error:
+ return rv;
+}
+
+static void sdp_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ free(sdp_func);
+ sdp_func = 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 struct usb_request *sdp_start_ep(struct usb_ep *ep, bool in)
+{
+ struct usb_request *req;
+
+ if (in)
+ req = alloc_ep_req(ep, 65);
+ else
+ req = alloc_ep_req(ep, 2048);
+/*
+ * OUT endpoint request length should be an integral multiple of
+ * maxpacket size 1024, else we break on certain controllers like
+ * DWC3 that expect bulk OUT requests to be divisible by maxpacket size.
+ */
+ debug("%s: ep:%p req:%p\n", __func__, ep, req);
+
+ if (!req)
+ return NULL;
+
+ memset(req->buf, 0, req->length);
+ if (in)
+ req->complete = sdp_tx_complete;
+ else
+ req->complete = sdp_rx_command_complete;
+
+ return req;
+}
+static int sdp_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_sdp *sdp = func_to_sdp(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_gadget *gadget = cdev->gadget;
+ int result;
+
+ debug("%s: intf: %d alt: %d\n", __func__, intf, alt);
+
+ if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) {
+ result = usb_ep_enable(sdp->in_ep, &in_hs_desc);
+ result |= usb_ep_enable(sdp->out_ep, &out_hs_desc);
+ } else {
+ result = usb_ep_enable(sdp->in_ep, &in_desc);
+ result |= usb_ep_enable(sdp->out_ep, &out_desc);
+ }
+ if (result)
+ return result;
+
+ sdp->in_req = sdp_start_ep(sdp->in_ep, true);
+ sdp->in_req->context = sdp;
+ sdp->out_req = sdp_start_ep(sdp->out_ep, false);
+ sdp->out_req->context = sdp;
+
+ sdp->in_ep->driver_data = cdev; /* claim */
+ sdp->out_ep->driver_data = cdev; /* claim */
+
+ sdp->altsetting = alt;
+ sdp->state = SDP_STATE_IDLE;
+ sdp->ep_int_enable = true;
+
+ return 0;
+}
+
+static int sdp_get_alt(struct usb_function *f, unsigned intf)
+{
+ struct f_sdp *sdp = func_to_sdp(f);
+
+ return sdp->altsetting;
+}
+
+static void sdp_disable(struct usb_function *f)
+{
+ struct f_sdp *sdp = func_to_sdp(f);
+
+ usb_ep_disable(sdp->in_ep);
+ usb_ep_disable(sdp->out_ep);
+
+ if (sdp->in_req) {
+ free(sdp->in_req->buf);
+ usb_ep_free_request(sdp->in_ep, sdp->in_req);
+ sdp->in_req = NULL;
+ }
+ if (sdp->out_req) {
+ free(sdp->out_req->buf);
+ usb_ep_free_request(sdp->out_ep, sdp->out_req);
+ sdp->out_req = NULL;
+ }
+}
+
+static int sdp_bind_config(struct usb_configuration *c)
+{
+ int status;
+
+ if (!sdp_func) {
+ sdp_func = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*sdp_func));
+ if (!sdp_func)
+ return -ENOMEM;
+ }
+
+ memset(sdp_func, 0, sizeof(*sdp_func));
+
+ sdp_func->usb_function.name = "sdp";
+ sdp_func->usb_function.hs_descriptors = sdp_runtime_hs_descs;
+ sdp_func->usb_function.descriptors = sdp_runtime_descs;
+ sdp_func->usb_function.bind = sdp_bind;
+ sdp_func->usb_function.unbind = sdp_unbind;
+ sdp_func->usb_function.set_alt = sdp_set_alt;
+ sdp_func->usb_function.get_alt = sdp_get_alt;
+ sdp_func->usb_function.disable = sdp_disable;
+ sdp_func->usb_function.strings = sdp_generic_strings;
+ sdp_func->usb_function.setup = sdp_setup;
+
+ status = usb_add_function(c, &sdp_func->usb_function);
+
+ return status;
+}
+
+int sdp_init(int controller_index)
+{
+ printf("SDP: initialize...\n");
+ while (!sdp_func->configuration_done) {
+ if (ctrlc()) {
+ puts("\rCTRL+C - Operation aborted.\n");
+ return 1;
+ }
+
+ WATCHDOG_RESET();
+ usb_gadget_handle_interrupts(controller_index);
+ }
+
+ return 0;
+}
+
+static u32 sdp_jump_imxheader(void *address)
+{
+ flash_header_v2_t *headerv2 = address;
+ ulong (*entry)(void);
+
+ if (headerv2->header.tag != IVT_HEADER_TAG) {
+ printf("Header Tag is not an IMX image\n");
+ return SDP_ERROR_IMXHEADER;
+ }
+
+ printf("Jumping to 0x%08x\n", headerv2->entry);
+ entry = sdp_ptr(headerv2->entry);
+ entry();
+
+ /* The image probably never returns hence we won't reach that point */
+ return 0;
+}
+
+#ifdef CONFIG_SPL_BUILD
+static ulong sdp_load_read(struct spl_load_info *load, ulong sector,
+ ulong count, void *buf)
+{
+ debug("%s: sector %lx, count %lx, buf %lx\n",
+ __func__, sector, count, (ulong)buf);
+ memcpy(buf, (void *)(load->dev + sector), count);
+ return count;
+}
+
+static ulong search_fit_header(ulong p, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i += 4) {
+ if (genimg_get_format((const void *)(p + i)) == IMAGE_FORMAT_FIT)
+ return p + i;
+ }
+
+ return 0;
+}
+
+static ulong search_container_header(ulong p, int size)
+{
+ int i;
+ u8 *hdr;
+
+ for (i = 0; i < size; i += 4) {
+ hdr = (u8 *)(p + i);
+ if (*(hdr + 3) == 0x87 && *hdr == 0)
+ if (*(hdr + 1) != 0 || *(hdr + 2) != 0)
+ return p + i;
+ }
+ return 0;
+}
+#endif
+
+static int sdp_handle_in_ep(struct spl_image_info *spl_image)
+{
+ u8 *data = sdp_func->in_req->buf;
+ u32 status;
+ int datalen;
+
+ switch (sdp_func->state) {
+ case SDP_STATE_TX_SEC_CONF:
+ debug("Report 3: HAB security\n");
+ data[0] = 3;
+
+ status = SDP_SECURITY_OPEN;
+ memcpy(&data[1], &status, 4);
+ sdp_func->in_req->length = 5;
+ usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
+ sdp_func->state = SDP_STATE_TX_SEC_CONF_BUSY;
+ break;
+
+ case SDP_STATE_TX_STATUS:
+ debug("Report 4: Status\n");
+ data[0] = 4;
+
+ memcpy(&data[1], &sdp_func->error_status, 4);
+ sdp_func->in_req->length = 65;
+ usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
+ sdp_func->state = SDP_STATE_TX_STATUS_BUSY;
+ break;
+ case SDP_STATE_TX_REGISTER:
+ debug("Report 4: Register Values\n");
+ data[0] = 4;
+
+ datalen = sdp_func->dnl_bytes_remaining;
+
+ if (datalen > 64)
+ datalen = 64;
+
+ memcpy(&data[1], sdp_ptr(sdp_func->dnl_address), datalen);
+ sdp_func->in_req->length = 65;
+
+ sdp_func->dnl_bytes_remaining -= datalen;
+ sdp_func->dnl_address += datalen;
+
+ usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
+ sdp_func->state = SDP_STATE_TX_REGISTER_BUSY;
+ break;
+ case SDP_STATE_JUMP:
+ printf("Jumping to header at 0x%08x\n", sdp_func->jmp_address);
+ status = sdp_jump_imxheader(sdp_ptr(sdp_func->jmp_address));
+
+ /* If imx header fails, try some U-Boot specific headers */
+ if (status) {
+#ifdef CONFIG_SPL_BUILD
+ if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER))
+ sdp_func->jmp_address = (u32)search_container_header((ulong)sdp_func->jmp_address, sdp_func->dnl_bytes);
+ else if (IS_ENABLED(CONFIG_SPL_LOAD_FIT))
+ sdp_func->jmp_address = (u32)search_fit_header((ulong)sdp_func->jmp_address, sdp_func->dnl_bytes);
+ if (sdp_func->jmp_address == 0)
+ panic("Error in search header, failed to jump\n");
+
+ printf("Found header at 0x%08x\n", sdp_func->jmp_address);
+
+ image_header_t *header =
+ sdp_ptr(sdp_func->jmp_address);
+#ifdef CONFIG_SPL_LOAD_FIT
+ if (image_get_magic(header) == FDT_MAGIC) {
+ struct spl_load_info load;
+
+ debug("Found FIT\n");
+ load.dev = header;
+ load.bl_len = 1;
+ load.read = sdp_load_read;
+ spl_load_simple_fit(spl_image, &load, 0,
+ header);
+
+ return SDP_EXIT;
+ }
+#endif
+ if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) {
+ struct spl_load_info load;
+
+ load.dev = header;
+ load.bl_len = 1;
+ load.read = sdp_load_read;
+ spl_load_imx_container(spl_image, &load, 0);
+ return SDP_EXIT;
+ }
+
+ /* In SPL, allow jumps to U-Boot images */
+ struct spl_image_info spl_image = {};
+ spl_parse_image_header(&spl_image, header);
+ jump_to_image_no_args(&spl_image);
+#else
+ /* In U-Boot, allow jumps to scripts */
+ image_source_script(sdp_func->jmp_address, "script@1");
+#endif
+ }
+
+ sdp_func->next_state = SDP_STATE_IDLE;
+ sdp_func->error_status = status;
+
+ /* Only send Report 4 if there was an error */
+ if (status)
+ sdp_func->state = SDP_STATE_TX_STATUS;
+ else
+ sdp_func->state = SDP_STATE_IDLE;
+ break;
+ default:
+ break;
+ };
+
+ return 0;
+}
+
+static void sdp_handle_out_ep(void)
+{
+ int rc;
+
+ if (sdp_func->state == SDP_STATE_IDLE) {
+ sdp_func->out_req->complete = sdp_rx_command_complete;
+ rc = usb_ep_queue(sdp_func->out_ep, sdp_func->out_req, 0);
+ if (rc)
+ printf("error in submission: %s\n",
+ sdp_func->out_ep->name);
+ sdp_func->state = SDP_STATE_RX_CMD;
+ } else if (sdp_func->state == SDP_STATE_RX_FILE_DATA) {
+ sdp_func->out_req->complete = sdp_rx_data_complete;
+ rc = usb_ep_queue(sdp_func->out_ep, sdp_func->out_req, 0);
+ if (rc)
+ printf("error in submission: %s\n",
+ sdp_func->out_ep->name);
+ sdp_func->state = SDP_STATE_RX_FILE_DATA_BUSY;
+ }
+}
+
+#ifndef CONFIG_SPL_BUILD
+int sdp_handle(int controller_index)
+#else
+int spl_sdp_handle(int controller_index, struct spl_image_info *spl_image)
+#endif
+{
+ int flag = 0;
+ printf("SDP: handle requests...\n");
+ while (1) {
+ if (ctrlc()) {
+ puts("\rCTRL+C - Operation aborted.\n");
+ return -EINVAL;
+ }
+
+ if (flag == SDP_EXIT)
+ return 0;
+
+ WATCHDOG_RESET();
+ usb_gadget_handle_interrupts(controller_index);
+
+#ifdef CONFIG_SPL_BUILD
+ flag = sdp_handle_in_ep(spl_image);
+#else
+ flag = sdp_handle_in_ep(NULL);
+#endif
+ if (sdp_func->ep_int_enable)
+ sdp_handle_out_ep();
+ }
+}
+
+int sdp_add(struct usb_configuration *c)
+{
+ int id;
+
+ id = usb_string_id(c->cdev);
+ if (id < 0)
+ return id;
+ strings_sdp_generic[0].id = id;
+ sdp_intf_runtime.iInterface = id;
+
+ debug("%s: cdev: %p gadget: %p gadget->ep0: %p\n", __func__,
+ c->cdev, c->cdev->gadget, c->cdev->gadget->ep0);
+
+ return sdp_bind_config(c);
+}
+
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_sdp, sdp_add);
diff --git a/roms/u-boot/drivers/usb/gadget/f_thor.c b/roms/u-boot/drivers/usb/gadget/f_thor.c
new file mode 100644
index 000000000..47ef55b2f
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/f_thor.c
@@ -0,0 +1,1034 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 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>
+ */
+
+#include <command.h>
+#include <errno.h>
+#include <common.h>
+#include <console.h>
+#include <init.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <version.h>
+#include <linux/delay.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 <thor.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 + 1);
+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();
+#ifdef CONFIG_THOR_RESET_OFF
+ return RESET_DONE;
+#endif
+ 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;
+ struct dfu_entity *dfu_entity = dfu_get_entity(alt_setting_num);
+ void *transfer_buffer = dfu_get_buf(dfu_entity);
+ 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) {
+ pr_err("DFU write failed [%d] cnt: %d\n",
+ 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;
+ void *transfer_buffer;
+ int ret;
+
+ debug("%s: left: %llu cnt: %d\n", __func__, left, cnt);
+
+ dfu_entity = dfu_get_entity(alt_setting_num);
+ if (!dfu_entity) {
+ pr_err("Alt setting: %d entity not found!\n", alt_setting_num);
+ return -ENOENT;
+ }
+
+ transfer_buffer = dfu_get_buf(dfu_entity);
+ if (!transfer_buffer) {
+ pr_err("Transfer buffer not allocated!\n");
+ return -ENXIO;
+ }
+
+ if (left) {
+ ret = dfu_write(dfu_entity, transfer_buffer, left, cnt++);
+ if (ret) {
+ pr_err("DFU write failed[%d]: left: %llu\n", ret, left);
+ return ret;
+ }
+ }
+
+ /*
+ * To store last "packet" or write file from buffer to filesystem
+ * DFU storage backend requires dfu_flush
+ *
+ * This also frees memory malloc'ed by dfu_get_buf(), so no explicit
+ * need fo call dfu_free_buf() is needed.
+ */
+ ret = dfu_flush(dfu_entity, transfer_buffer, 0, cnt);
+ if (ret)
+ pr_err("DFU flush failed!\n");
+
+ 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 = (uint64_t)(uint32_t)rqt->int_data[0] +
+ (((uint64_t)(uint32_t)rqt->int_data[1])
+ << 32);
+ debug("INIT: total %llu bytes\n", thor_file_size);
+ 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 = (uint64_t)(uint32_t)rqt->int_data[1] +
+ (((uint64_t)(uint32_t)rqt->int_data[2])
+ << 32);
+ memcpy(f_name, rqt->str_data[0], F_NAME_BUF_SIZE);
+ f_name[F_NAME_BUF_SIZE] = '\0';
+
+ 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) {
+ pr_err("Alt setting [%d] to write not found!\n",
+ 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:
+ pr_err("Operation not supported: %d\n", 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;
+
+ 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:
+ pr_err("unknown request (%d)\n", 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,
+};
+
+/*
+ * 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) {
+ pr_err("kill %s: resubmit %d bytes --> %d\n",
+ 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(0);
+ 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:%zd\n", __func__,
+ dev->in_req->length, sizeof(data));
+
+ status = usb_ep_queue(dev->in_ep, dev->in_req, 0);
+ if (status) {
+ pr_err("kill %s: resubmit %d bytes --> %d\n",
+ 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(0);
+
+ 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:
+ pr_err("ERROR:%d\n", status);
+ break;
+ }
+
+ debug("%s complete --> %d, %d/%d\n", ep->name,
+ status, req->actual, req->length);
+}
+
+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:
+ pr_err("thor_setup: unknown request: %d\n", 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(0);
+
+ 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();
+#ifdef CONFIG_THOR_RESET_OFF
+ if (ret == RESET_DONE)
+ break;
+#endif
+ if (ret < 0)
+ return ret;
+ } else {
+ printf("%s: No data received!\n", __func__);
+ break;
+ }
+ if (dfu_reinit_needed)
+ return THOR_DFU_REINIT_NEEDED;
+ }
+
+ return 0;
+}
+
+static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->buf)
+ free(req->buf);
+ usb_ep_free_request(ep, req);
+}
+
+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,
+ THOR_PACKET_SIZE);
+ 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->driver_data = dev;
+
+ 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->driver_data = dev;
+
+ ep = usb_ep_autoconfig(gadget, &fs_int_desc);
+ if (!ep) {
+ status = -ENODEV;
+ goto fail;
+ }
+
+ dev->int_ep = ep;
+ ep->driver_data = dev;
+
+ 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:
+ if (dev->req)
+ free_ep_req(gadget->ep0, dev->req);
+ free(dev);
+ return status;
+}
+
+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_ep_req(dev->gadget->ep0, dev->req);
+ 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) {
+ usb_ep_disable(dev->in_ep);
+ free_ep_req(dev->in_ep, dev->in_req);
+ dev->in_ep->driver_data = NULL;
+ }
+
+ if (dev->out_ep->driver_data) {
+ usb_ep_disable(dev->out_ep);
+ usb_ep_free_request(dev->out_ep, dev->out_req);
+ 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 err;
+
+ ep->driver_data = cdev; /* claim */
+ req = alloc_ep_req(ep, THOR_PACKET_SIZE);
+ if (!req) {
+ result = -EIO;
+ goto err_disable_in_ep;
+ }
+
+ memset(req->buf, 0, req->length);
+ req->complete = thor_rx_tx_complete;
+ 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 err_free_in_req;
+
+ ep->driver_data = cdev; /* claim */
+ req = usb_ep_alloc_request(ep, 0);
+ if (!req) {
+ result = -EIO;
+ goto err_disable_out_ep;
+ }
+
+ req->complete = thor_rx_tx_complete;
+ dev->out_req = req;
+ /* ACM control EP */
+ ep = dev->int_ep;
+ d = ep_desc(gadget, &hs_int_desc, &fs_int_desc);
+ debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress);
+
+ result = usb_ep_enable(ep, d);
+ if (result)
+ goto err;
+
+ ep->driver_data = cdev; /* claim */
+
+ return 0;
+
+ err_disable_out_ep:
+ usb_ep_disable(dev->out_ep);
+
+ err_free_in_req:
+ free_ep_req(dev->in_ep, dev->in_req);
+ dev->in_req = NULL;
+
+ err_disable_in_ep:
+ usb_ep_disable(dev->in_ep);
+
+ err:
+ 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)
+ pr_err("%s: EPs setup failed!\n", __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/roms/u-boot/drivers/usb/gadget/f_thor.h b/roms/u-boot/drivers/usb/gadget/f_thor.h
new file mode 100644
index 000000000..8ba3fa21b
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/f_thor.h
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * f_thor.h - USB TIZEN THOR - internal gadget definitions
+ *
+ * Copyright (C) 2013 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ */
+
+#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 5
+#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 */
+#ifdef CONFIG_THOR_RESET_OFF
+#define RESET_DONE 0xFFFFFFFF
+#endif
+#endif /* _USB_THOR_H_ */
diff --git a/roms/u-boot/drivers/usb/gadget/fotg210.c b/roms/u-boot/drivers/usb/gadget/fotg210.c
new file mode 100644
index 000000000..af43433d8
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/fotg210.c
@@ -0,0 +1,964 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Faraday USB 2.0 OTG Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ */
+
+#include <common.h>
+#include <command.h>
+#include <config.h>
+#include <cpu_func.h>
+#include <log.h>
+#include <net.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/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(int index)
+{
+ 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/roms/u-boot/drivers/usb/gadget/g_dnl.c b/roms/u-boot/drivers/usb/gadget/g_dnl.c
new file mode 100644
index 000000000..afb7b74f3
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/g_dnl.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * g_dnl.c -- USB Downloader Gadget
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ */
+
+#include <common.h>
+#include <log.h>
+#include <malloc.h>
+
+#include <mmc.h>
+#include <part.h>
+#include <usb.h>
+
+#include <g_dnl.h>
+#include <usb_mass_storage.h>
+#include <dfu.h>
+#include <thor.h>
+
+#include <env_callback.h>
+
+#include "gadget_chips.h"
+#include "composite.c"
+
+/*
+ * One needs to define the following:
+ * CONFIG_USB_GADGET_VENDOR_NUM
+ * CONFIG_USB_GADGET_PRODUCT_NUM
+ * CONFIG_USB_GADGET_MANUFACTURER
+ * at e.g. ./configs/<board>_defconfig
+ */
+
+#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 256
+/* 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_USB_GADGET_MANUFACTURER;
+
+void g_dnl_set_serialnumber(char *s)
+{
+ memset(g_dnl_serial, 0, MAX_STRING_SERIAL);
+ strncpy(g_dnl_serial, s, MAX_STRING_SERIAL - 1);
+}
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .bDeviceSubClass = 0, /*0x02:CDC-modem , 0x00:CDC-serial*/
+
+ .idVendor = __constant_cpu_to_le16(CONFIG_USB_GADGET_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(CONFIG_USB_GADGET_PRODUCT_NUM),
+ /* .iProduct = DYNAMIC */
+ /* .iSerialNumber = DYNAMIC */
+ .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,
+};
+
+void g_dnl_set_product(const char *s)
+{
+ if (s)
+ g_dnl_string_defs[1].s = s;
+ else
+ g_dnl_string_defs[1].s = product;
+}
+
+static int g_dnl_unbind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+
+ 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 board_usb_init(int index, enum usb_init_type init)
+{
+ return 0;
+}
+
+__weak
+int board_usb_cleanup(int index, enum usb_init_type init)
+{
+ return 0;
+}
+
+__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 bool g_dnl_detach_request;
+
+bool g_dnl_detach(void)
+{
+ return g_dnl_detach_request;
+}
+
+void g_dnl_trigger_detach(void)
+{
+ g_dnl_detach_request = true;
+}
+
+void g_dnl_clear_detach(void)
+{
+ g_dnl_detach_request = false;
+}
+
+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);
+}
+
+/**
+ * Update internal serial number variable when the "serial#" env var changes.
+ *
+ * Handle all cases, even when flags == H_PROGRAMMATIC or op == env_op_delete.
+ */
+static int on_serialno(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ g_dnl_set_serialnumber((char *)value);
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(serialno, on_serialno);
+
+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;
+
+ g_dnl_bind_fixup(&device_desc, cdev->driver->name);
+
+ if (strlen(g_dnl_serial)) {
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+
+ g_dnl_string_defs[2].id = id;
+ device_desc.iSerialNumber = id;
+ }
+
+ 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,
+ .max_speed = USB_SPEED_SUPER,
+
+ .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/roms/u-boot/drivers/usb/gadget/gadget_chips.h b/roms/u-boot/drivers/usb/gadget/gadget_chips.h
new file mode 100644
index 000000000..0cdf47c2d
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/gadget_chips.h
@@ -0,0 +1,236 @@
+/*
+ * 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_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_USB_MUSB_GADGET
+#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
+#else
+#define gadget_is_musbhdrc(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
+
+#ifdef CONFIG_USB_DWC3_GADGET
+#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name))
+#else
+#define gadget_is_dwc3(g) 0
+#endif
+
+#ifdef CONFIG_USB_CDNS3_GADGET
+#define gadget_is_cdns3(g) (!strcmp("cdns3-gadget", (g)->name))
+#else
+#define gadget_is_cdns3(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_MAX3420
+#define gadget_is_max3420(g) (!strcmp("max3420-udc", (g)->name))
+#else
+#define gadget_is_max3420(g) 0
+#endif
+
+#ifdef CONFIG_USB_MTU3_GADGET
+#define gadget_is_mtu3(g) (!strcmp("mtu3-gadget", (g)->name))
+#else
+#define gadget_is_mtu3(g) 0
+#endif
+
+/**
+ * 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_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_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;
+ else if (gadget_is_dwc3(gadget))
+ return 0x23;
+ else if (gadget_is_cdns3(gadget))
+ return 0x24;
+ else if (gadget_is_max3420(gadget))
+ return 0x25;
+ else if (gadget_is_mtu3(gadget))
+ return 0x26;
+ return -ENOENT;
+}
diff --git a/roms/u-boot/drivers/usb/gadget/max3420_udc.c b/roms/u-boot/drivers/usb/gadget/max3420_udc.c
new file mode 100644
index 000000000..a16095f89
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/max3420_udc.c
@@ -0,0 +1,875 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <common.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <asm/gpio.h>
+#include <linux/list.h>
+#include <linux/bitfield.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <malloc.h>
+#include <spi.h>
+#include <dm.h>
+#include <g_dnl.h>
+
+#define MAX3420_MAX_EPS 4
+#define EP_MAX_PACKET 64 /* Same for all Endpoints */
+#define EPNAME_SIZE 16 /* Buffer size for endpoint name */
+
+#define MAX3420_SPI_DIR_RD 0 /* read register from MAX3420 */
+#define MAX3420_SPI_DIR_WR 1 /* write register to MAX3420 */
+
+/* SPI commands: */
+#define MAX3420_SPI_ACK_MASK BIT(0)
+#define MAX3420_SPI_DIR_MASK BIT(1)
+#define MAX3420_SPI_REG_MASK GENMASK(7, 3)
+
+#define MAX3420_REG_EP0FIFO 0
+#define MAX3420_REG_EP1FIFO 1
+#define MAX3420_REG_EP2FIFO 2
+#define MAX3420_REG_EP3FIFO 3
+#define MAX3420_REG_SUDFIFO 4
+#define MAX3420_REG_EP0BC 5
+#define MAX3420_REG_EP1BC 6
+#define MAX3420_REG_EP2BC 7
+#define MAX3420_REG_EP3BC 8
+
+#define MAX3420_REG_EPSTALLS 9
+ #define bACKSTAT BIT(6)
+ #define bSTLSTAT BIT(5)
+ #define bSTLEP3IN BIT(4)
+ #define bSTLEP2IN BIT(3)
+ #define bSTLEP1OUT BIT(2)
+ #define bSTLEP0OUT BIT(1)
+ #define bSTLEP0IN BIT(0)
+
+#define MAX3420_REG_CLRTOGS 10
+ #define bEP3DISAB BIT(7)
+ #define bEP2DISAB BIT(6)
+ #define bEP1DISAB BIT(5)
+ #define bCTGEP3IN BIT(4)
+ #define bCTGEP2IN BIT(3)
+ #define bCTGEP1OUT BIT(2)
+
+#define MAX3420_REG_EPIRQ 11
+#define MAX3420_REG_EPIEN 12
+ #define bSUDAVIRQ BIT(5)
+ #define bIN3BAVIRQ BIT(4)
+ #define bIN2BAVIRQ BIT(3)
+ #define bOUT1DAVIRQ BIT(2)
+ #define bOUT0DAVIRQ BIT(1)
+ #define bIN0BAVIRQ BIT(0)
+
+#define MAX3420_REG_USBIRQ 13
+#define MAX3420_REG_USBIEN 14
+ #define bOSCOKIRQ BIT(0)
+ #define bRWUDNIRQ BIT(1)
+ #define bBUSACTIRQ BIT(2)
+ #define bURESIRQ BIT(3)
+ #define bSUSPIRQ BIT(4)
+ #define bNOVBUSIRQ BIT(5)
+ #define bVBUSIRQ BIT(6)
+ #define bURESDNIRQ BIT(7)
+
+#define MAX3420_REG_USBCTL 15
+ #define bHOSCSTEN BIT(7)
+ #define bVBGATE BIT(6)
+ #define bCHIPRES BIT(5)
+ #define bPWRDOWN BIT(4)
+ #define bCONNECT BIT(3)
+ #define bSIGRWU BIT(2)
+
+#define MAX3420_REG_CPUCTL 16
+ #define bIE BIT(0)
+
+#define MAX3420_REG_PINCTL 17
+ #define bEP3INAK BIT(7)
+ #define bEP2INAK BIT(6)
+ #define bEP0INAK BIT(5)
+ #define bFDUPSPI BIT(4)
+ #define bINTLEVEL BIT(3)
+ #define bPOSINT BIT(2)
+ #define bGPXB BIT(1)
+ #define bGPXA BIT(0)
+
+#define MAX3420_REG_REVISION 18
+
+#define MAX3420_REG_FNADDR 19
+ #define FNADDR_MASK 0x7f
+
+#define MAX3420_REG_IOPINS 20
+#define MAX3420_REG_IOPINS2 21
+#define MAX3420_REG_GPINIRQ 22
+#define MAX3420_REG_GPINIEN 23
+#define MAX3420_REG_GPINPOL 24
+#define MAX3420_REG_HIRQ 25
+#define MAX3420_REG_HIEN 26
+#define MAX3420_REG_MODE 27
+#define MAX3420_REG_PERADDR 28
+#define MAX3420_REG_HCTL 29
+#define MAX3420_REG_HXFR 30
+#define MAX3420_REG_HRSL 31
+
+struct max3420_req {
+ struct usb_request usb_req;
+ struct list_head queue;
+ struct max3420_ep *ep;
+};
+
+struct max3420_ep {
+ struct max3420_udc *udc;
+ struct list_head queue;
+ char name[EPNAME_SIZE];
+ unsigned int maxpacket;
+ struct usb_ep ep_usb;
+ int halted;
+ int id;
+};
+
+struct max3420_udc {
+ struct max3420_ep ep[MAX3420_MAX_EPS];
+ struct usb_gadget_driver *driver;
+ bool softconnect;
+ struct usb_ctrlrequest setup;
+ struct max3420_req ep0req;
+ struct usb_gadget gadget;
+ struct spi_slave *slave;
+ struct udevice *dev;
+ u8 ep0buf[64];
+ int remote_wkp;
+ bool suspended;
+};
+
+#define to_max3420_req(r) container_of((r), struct max3420_req, usb_req)
+#define to_max3420_ep(e) container_of((e), struct max3420_ep, ep_usb)
+#define to_udc(g) container_of((g), struct max3420_udc, gadget)
+
+static void spi_ack_ctrl(struct max3420_udc *udc)
+{
+ struct spi_slave *slave = udc->slave;
+ u8 txdata[1];
+
+ txdata[0] = FIELD_PREP(MAX3420_SPI_ACK_MASK, 1);
+ spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_ONCE);
+}
+
+static u8 spi_rd8_ack(struct max3420_udc *udc, u8 reg, int ackstat)
+{
+ struct spi_slave *slave = udc->slave;
+ u8 txdata[2], rxdata[2];
+
+ txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) |
+ FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_RD) |
+ FIELD_PREP(MAX3420_SPI_ACK_MASK, ackstat ? 1 : 0);
+
+ rxdata[0] = 0;
+ rxdata[1] = 0;
+ spi_xfer(slave, sizeof(txdata), txdata, rxdata, SPI_XFER_ONCE);
+
+ return rxdata[1];
+}
+
+static u8 spi_rd8(struct max3420_udc *udc, u8 reg)
+{
+ return spi_rd8_ack(udc, reg, 0);
+}
+
+static void spi_wr8_ack(struct max3420_udc *udc, u8 reg, u8 val, int ackstat)
+{
+ struct spi_slave *slave = udc->slave;
+ u8 txdata[2];
+
+ txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) |
+ FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_WR) |
+ FIELD_PREP(MAX3420_SPI_ACK_MASK, ackstat ? 1 : 0);
+ txdata[1] = val;
+
+ spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_ONCE);
+}
+
+static void spi_wr8(struct max3420_udc *udc, u8 reg, u8 val)
+{
+ spi_wr8_ack(udc, reg, val, 0);
+}
+
+static void spi_rd_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len)
+{
+ struct spi_slave *slave = udc->slave;
+ u8 txdata[1];
+
+ txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) |
+ FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_RD);
+
+ spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_BEGIN);
+ spi_xfer(slave, len * 8, NULL, buf, SPI_XFER_END);
+}
+
+static void spi_wr_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len)
+{
+ struct spi_slave *slave = udc->slave;
+ u8 txdata[1];
+
+ txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) |
+ FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_WR);
+
+ spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_BEGIN);
+ spi_xfer(slave, len * 8, buf, NULL, SPI_XFER_END);
+}
+
+/* 0 if not-connected */
+int g_dnl_board_usb_cable_connected(void)
+{
+ return 1;
+}
+
+static void spi_max3420_enable(struct max3420_ep *ep, int enable)
+{
+ struct max3420_udc *udc = ep->udc;
+ u8 epdis, epien;
+
+ if (ep->id == 0)
+ return;
+
+ epien = spi_rd8(udc, MAX3420_REG_EPIEN);
+ epdis = spi_rd8(udc, MAX3420_REG_CLRTOGS);
+
+ if (enable) {
+ epdis &= ~BIT(ep->id + 4);
+ epien |= BIT(ep->id + 1);
+ } else {
+ epdis |= BIT(ep->id + 4);
+ epien &= ~BIT(ep->id + 1);
+ }
+
+ spi_wr8(udc, MAX3420_REG_CLRTOGS, epdis);
+ spi_wr8(udc, MAX3420_REG_EPIEN, epien);
+}
+
+static int
+max3420_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct max3420_ep *ep = to_max3420_ep(_ep);
+
+ _ep->desc = desc;
+ _ep->maxpacket = usb_endpoint_maxp(desc) & 0x7ff;
+
+ spi_max3420_enable(ep, 1);
+
+ return 0;
+}
+
+static void max3420_req_done(struct max3420_req *req, int status)
+{
+ struct max3420_ep *ep = req->ep;
+
+ if (req->usb_req.status == -EINPROGRESS)
+ req->usb_req.status = status;
+ else
+ status = req->usb_req.status;
+
+ if (status && status != -ESHUTDOWN)
+ dev_err(ep->udc->dev, "%s done %p, status %d\n",
+ ep->ep_usb.name, req, status);
+
+ if (req->usb_req.complete)
+ req->usb_req.complete(&ep->ep_usb, &req->usb_req);
+}
+
+static void max3420_ep_nuke(struct max3420_ep *ep, int status)
+{
+ struct max3420_req *req, *r;
+
+ list_for_each_entry_safe(req, r, &ep->queue, queue) {
+ list_del_init(&req->queue);
+ max3420_req_done(req, status);
+ }
+}
+
+static int max3420_ep_disable(struct usb_ep *_ep)
+{
+ struct max3420_ep *ep = to_max3420_ep(_ep);
+
+ _ep->desc = NULL;
+ max3420_ep_nuke(ep, -ESHUTDOWN);
+ spi_max3420_enable(ep, 0);
+
+ return 0;
+}
+
+static struct usb_request *
+max3420_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct max3420_ep *ep = to_max3420_ep(_ep);
+ struct max3420_req *req = kzalloc(sizeof(*req), gfp_flags);
+
+ if (!req)
+ return NULL;
+
+ req->ep = ep;
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->usb_req;
+}
+
+static void
+max3420_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ kfree(to_max3420_req(_req));
+}
+
+static int
+max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct max3420_req *req = to_max3420_req(_req);
+ struct max3420_ep *ep = to_max3420_ep(_ep);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+ list_add_tail(&req->queue, &ep->queue);
+
+ return 0;
+}
+
+static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct max3420_req *req = to_max3420_req(_req);
+
+ list_del_init(&req->queue);
+ max3420_req_done(req, -ECONNRESET);
+
+ return 0;
+}
+
+static int max3420_ep_set_halt(struct usb_ep *_ep, int halt)
+{
+ struct max3420_ep *ep = to_max3420_ep(_ep);
+ struct max3420_udc *udc = ep->udc;
+ u8 epstalls;
+
+ if (ep->id == 0) /* can't stall EP0 */
+ return 0;
+
+ epstalls = spi_rd8(udc, MAX3420_REG_EPSTALLS);
+ if (halt) {
+ ep->halted = 1;
+ epstalls |= BIT(ep->id + 1);
+ } else {
+ u8 clrtogs;
+
+ ep->halted = 0;
+ epstalls &= ~BIT(ep->id + 1);
+ clrtogs = spi_rd8(udc, MAX3420_REG_CLRTOGS);
+ clrtogs |= BIT(ep->id + 1);
+ spi_wr8(udc, MAX3420_REG_CLRTOGS, clrtogs);
+ }
+ spi_wr8(udc, MAX3420_REG_EPSTALLS, epstalls | bACKSTAT);
+
+ return 0;
+}
+
+static const struct usb_ep_ops max3420_ep_ops = {
+ .enable = max3420_ep_enable,
+ .disable = max3420_ep_disable,
+ .alloc_request = max3420_ep_alloc_request,
+ .free_request = max3420_ep_free_request,
+ .queue = max3420_ep_queue,
+ .dequeue = max3420_ep_dequeue,
+ .set_halt = max3420_ep_set_halt,
+};
+
+static void __max3420_stop(struct max3420_udc *udc)
+{
+ u8 val;
+
+ /* Disable IRQ to CPU */
+ spi_wr8(udc, MAX3420_REG_CPUCTL, 0);
+
+ val = spi_rd8(udc, MAX3420_REG_USBCTL);
+ val |= bPWRDOWN;
+ val |= bHOSCSTEN;
+ spi_wr8(udc, MAX3420_REG_USBCTL, val);
+}
+
+static void __max3420_start(struct max3420_udc *udc)
+{
+ u8 val;
+
+ /* configure SPI */
+ spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI);
+
+ /* Chip Reset */
+ spi_wr8(udc, MAX3420_REG_USBCTL, bCHIPRES);
+ mdelay(5);
+ spi_wr8(udc, MAX3420_REG_USBCTL, 0);
+
+ /* Poll for OSC to stabilize */
+ while (1) {
+ val = spi_rd8(udc, MAX3420_REG_USBIRQ);
+ if (val & bOSCOKIRQ)
+ break;
+ cond_resched();
+ }
+
+ /* Enable PULL-UP only when Vbus detected */
+ val = spi_rd8(udc, MAX3420_REG_USBCTL);
+ val |= bVBGATE | bCONNECT;
+ spi_wr8(udc, MAX3420_REG_USBCTL, val);
+
+ val = bURESDNIRQ | bURESIRQ;
+ spi_wr8(udc, MAX3420_REG_USBIEN, val);
+
+ /* Enable only EP0 interrupts */
+ val = bIN0BAVIRQ | bOUT0DAVIRQ | bSUDAVIRQ;
+ spi_wr8(udc, MAX3420_REG_EPIEN, val);
+
+ /* Enable IRQ to CPU */
+ spi_wr8(udc, MAX3420_REG_CPUCTL, bIE);
+}
+
+static int max3420_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct max3420_udc *udc = to_udc(gadget);
+
+ udc->driver = driver;
+ udc->remote_wkp = 0;
+ udc->softconnect = true;
+
+ __max3420_start(udc);
+
+ return 0;
+}
+
+static int max3420_udc_stop(struct usb_gadget *gadget)
+{
+ struct max3420_udc *udc = to_udc(gadget);
+
+ udc->driver = NULL;
+ udc->softconnect = false;
+
+ __max3420_stop(udc);
+
+ return 0;
+}
+
+static int max3420_wakeup(struct usb_gadget *gadget)
+{
+ struct max3420_udc *udc = to_udc(gadget);
+ u8 usbctl;
+
+ /* Only if wakeup allowed by host */
+ if (!udc->remote_wkp || !udc->suspended)
+ return 0;
+
+ /* Set Remote-Wakeup Signal*/
+ usbctl = spi_rd8(udc, MAX3420_REG_USBCTL);
+ usbctl |= bSIGRWU;
+ spi_wr8(udc, MAX3420_REG_USBCTL, usbctl);
+
+ mdelay(5);
+
+ /* Clear Remote-WkUp Signal*/
+ usbctl = spi_rd8(udc, MAX3420_REG_USBCTL);
+ usbctl &= ~bSIGRWU;
+ spi_wr8(udc, MAX3420_REG_USBCTL, usbctl);
+
+ udc->suspended = false;
+
+ return 0;
+}
+
+static const struct usb_gadget_ops max3420_udc_ops = {
+ .udc_start = max3420_udc_start,
+ .udc_stop = max3420_udc_stop,
+ .wakeup = max3420_wakeup,
+};
+
+static struct usb_endpoint_descriptor ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET),
+};
+
+static void max3420_getstatus(struct max3420_udc *udc)
+{
+ struct max3420_ep *ep;
+ u16 status = 0;
+
+ switch (udc->setup.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ /* Get device status */
+ status = 0 << USB_DEVICE_SELF_POWERED;
+ status |= (udc->remote_wkp << USB_DEVICE_REMOTE_WAKEUP);
+ break;
+ case USB_RECIP_INTERFACE:
+ if (udc->driver->setup(&udc->gadget, &udc->setup) < 0)
+ goto stall;
+ break;
+ case USB_RECIP_ENDPOINT:
+ ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK];
+ if (ep->halted)
+ status = 1 << USB_ENDPOINT_HALT;
+ break;
+ default:
+ goto stall;
+ }
+
+ status = cpu_to_le16(status);
+ spi_wr_buf(udc, MAX3420_REG_EP0FIFO, &status, 2);
+ spi_wr8_ack(udc, MAX3420_REG_EP0BC, 2, 1);
+ return;
+stall:
+ dev_err(udc->dev, "Can't respond to getstatus request\n");
+ spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | bSTLSTAT);
+}
+
+static void max3420_set_clear_feature(struct max3420_udc *udc)
+{
+ int set = udc->setup.bRequest == USB_REQ_SET_FEATURE;
+ struct max3420_ep *ep;
+ int id;
+
+ switch (udc->setup.bRequestType) {
+ case USB_RECIP_DEVICE:
+ if (udc->setup.wValue != USB_DEVICE_REMOTE_WAKEUP)
+ break;
+
+ if (udc->setup.bRequest == USB_REQ_SET_FEATURE)
+ udc->remote_wkp = 1;
+ else
+ udc->remote_wkp = 0;
+
+ return spi_ack_ctrl(udc);
+
+ case USB_RECIP_ENDPOINT:
+ if (udc->setup.wValue != USB_ENDPOINT_HALT)
+ break;
+
+ id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ ep = &udc->ep[id];
+
+ max3420_ep_set_halt(&ep->ep_usb, set);
+ return;
+ default:
+ break;
+ }
+
+ dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n");
+ spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | bSTLSTAT);
+}
+
+static void max3420_handle_setup(struct max3420_udc *udc)
+{
+ struct usb_ctrlrequest setup;
+ u8 addr;
+
+ spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8);
+
+ udc->setup = setup;
+ udc->setup.wValue = cpu_to_le16(setup.wValue);
+ udc->setup.wIndex = cpu_to_le16(setup.wIndex);
+ udc->setup.wLength = cpu_to_le16(setup.wLength);
+
+ switch (udc->setup.bRequest) {
+ case USB_REQ_GET_STATUS:
+ /* Data+Status phase form udc */
+ if ((udc->setup.bRequestType &
+ (USB_DIR_IN | USB_TYPE_MASK)) !=
+ (USB_DIR_IN | USB_TYPE_STANDARD)) {
+ break;
+ }
+ return max3420_getstatus(udc);
+ case USB_REQ_SET_ADDRESS:
+ /* Status phase from udc */
+ if (udc->setup.bRequestType != (USB_DIR_OUT |
+ USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+ break;
+ addr = spi_rd8_ack(udc, MAX3420_REG_FNADDR, 1);
+ dev_dbg(udc->dev, "Assigned Address=%d/%d\n",
+ udc->setup.wValue, addr);
+ return;
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ /* Requests with no data phase, status phase from udc */
+ if ((udc->setup.bRequestType & USB_TYPE_MASK)
+ != USB_TYPE_STANDARD)
+ break;
+ return max3420_set_clear_feature(udc);
+ default:
+ break;
+ }
+
+ if (udc->driver->setup(&udc->gadget, &setup) < 0) {
+ /* Stall EP0 */
+ spi_wr8(udc, MAX3420_REG_EPSTALLS,
+ bSTLEP0IN | bSTLEP0OUT | bSTLSTAT);
+ }
+}
+
+static int do_data(struct max3420_udc *udc, int ep_id, int in)
+{
+ struct max3420_ep *ep = &udc->ep[ep_id];
+ struct max3420_req *req;
+ int done, length, psz;
+ void *buf;
+
+ if (list_empty(&ep->queue))
+ return 0;
+
+ req = list_first_entry(&ep->queue, struct max3420_req, queue);
+ buf = req->usb_req.buf + req->usb_req.actual;
+
+ psz = ep->ep_usb.maxpacket;
+ length = req->usb_req.length - req->usb_req.actual;
+ length = min(length, psz);
+
+ if (length == 0) {
+ done = 1;
+ goto xfer_done;
+ }
+
+ done = 0;
+ if (in) {
+ spi_wr_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length);
+ spi_wr8(udc, MAX3420_REG_EP0BC + ep_id, length);
+ if (length < psz)
+ done = 1;
+ } else {
+ psz = spi_rd8(udc, MAX3420_REG_EP0BC + ep_id);
+ length = min(length, psz);
+ spi_rd_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length);
+ if (length < ep->ep_usb.maxpacket)
+ done = 1;
+ }
+
+ req->usb_req.actual += length;
+
+ if (req->usb_req.actual == req->usb_req.length)
+ done = 1;
+
+xfer_done:
+ if (done) {
+ list_del_init(&req->queue);
+
+ if (ep_id == 0)
+ spi_ack_ctrl(udc);
+
+ max3420_req_done(req, 0);
+ }
+
+ return 1;
+}
+
+static int max3420_handle_irqs(struct max3420_udc *udc)
+{
+ u8 epien, epirq, usbirq, usbien, reg[4];
+ int ret = 0;
+
+ spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 4);
+ epirq = reg[0];
+ epien = reg[1];
+ usbirq = reg[2];
+ usbien = reg[3];
+
+ usbirq &= usbien;
+ epirq &= epien;
+
+ if (epirq & bSUDAVIRQ) {
+ spi_wr8(udc, MAX3420_REG_EPIRQ, bSUDAVIRQ);
+ max3420_handle_setup(udc);
+ return 1;
+ }
+
+ if (usbirq & bVBUSIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, bVBUSIRQ);
+ dev_dbg(udc->dev, "Cable plugged in\n");
+ g_dnl_clear_detach();
+ return 1;
+ }
+
+ if (usbirq & bNOVBUSIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, bNOVBUSIRQ);
+ dev_dbg(udc->dev, "Cable pulled out\n");
+ g_dnl_trigger_detach();
+ return 1;
+ }
+
+ if (usbirq & bURESIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, bURESIRQ);
+ return 1;
+ }
+
+ if (usbirq & bURESDNIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, bURESDNIRQ);
+ spi_wr8(udc, MAX3420_REG_USBIEN, bURESDNIRQ | bURESIRQ);
+ spi_wr8(udc, MAX3420_REG_EPIEN, bSUDAVIRQ
+ | bIN0BAVIRQ | bOUT0DAVIRQ);
+ return 1;
+ }
+
+ if (usbirq & bSUSPIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, bSUSPIRQ);
+ dev_dbg(udc->dev, "USB Suspend - Enter\n");
+ udc->suspended = true;
+ return 1;
+ }
+
+ if (usbirq & bBUSACTIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, bBUSACTIRQ);
+ dev_dbg(udc->dev, "USB Suspend - Exit\n");
+ udc->suspended = false;
+ return 1;
+ }
+
+ if (usbirq & bRWUDNIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, bRWUDNIRQ);
+ dev_dbg(udc->dev, "Asked Host to wakeup\n");
+ return 1;
+ }
+
+ if (usbirq & bOSCOKIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, bOSCOKIRQ);
+ dev_dbg(udc->dev, "Osc stabilized, start work\n");
+ return 1;
+ }
+
+ if (epirq & bOUT0DAVIRQ && do_data(udc, 0, 0)) {
+ spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT0DAVIRQ, 1);
+ ret = 1;
+ }
+
+ if (epirq & bIN0BAVIRQ && do_data(udc, 0, 1))
+ ret = 1;
+
+ if (epirq & bOUT1DAVIRQ && do_data(udc, 1, 0)) {
+ spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT1DAVIRQ, 1);
+ ret = 1;
+ }
+
+ if (epirq & bIN2BAVIRQ && do_data(udc, 2, 1))
+ ret = 1;
+
+ if (epirq & bIN3BAVIRQ && do_data(udc, 3, 1))
+ ret = 1;
+
+ return ret;
+}
+
+static int max3420_irq(struct max3420_udc *udc)
+{
+ do_data(udc, 0, 1); /* get done with the EP0 ZLP */
+
+ return max3420_handle_irqs(udc);
+}
+
+static void max3420_setup_eps(struct max3420_udc *udc)
+{
+ int i;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+ INIT_LIST_HEAD(&udc->ep[0].ep_usb.ep_list);
+
+ for (i = 0; i < MAX3420_MAX_EPS; i++) {
+ struct max3420_ep *ep = &udc->ep[i];
+
+ INIT_LIST_HEAD(&ep->queue);
+
+ ep->id = i;
+ ep->udc = udc;
+ ep->ep_usb.ops = &max3420_ep_ops;
+ ep->ep_usb.name = ep->name;
+ ep->ep_usb.maxpacket = EP_MAX_PACKET;
+
+ if (i == 0) {
+ ep->ep_usb.desc = &ep0_desc;
+ snprintf(ep->name, EPNAME_SIZE, "ep0");
+ continue;
+ }
+
+ list_add_tail(&ep->ep_usb.ep_list, &udc->gadget.ep_list);
+
+ if (i == 1)
+ snprintf(ep->name, EPNAME_SIZE, "ep1out-bulk");
+ else
+ snprintf(ep->name, EPNAME_SIZE, "ep%din-bulk", i);
+ };
+}
+
+static void max3420_setup_spi(struct max3420_udc *udc)
+{
+ u8 reg[8];
+
+ spi_claim_bus(udc->slave);
+ spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8);
+ /* configure SPI */
+ spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI);
+}
+
+int dm_usb_gadget_handle_interrupts(struct udevice *dev)
+{
+ struct max3420_udc *udc = dev_get_priv(dev);
+
+ return max3420_irq(udc);
+}
+
+static int max3420_udc_probe(struct udevice *dev)
+{
+ struct max3420_udc *udc = dev_get_priv(dev);
+ struct dm_spi_slave_plat *slave_pdata;
+ struct udevice *bus = dev->parent;
+ int busnum = dev_seq(bus);
+ unsigned int cs;
+ uint speed, mode;
+ struct udevice *spid;
+
+ slave_pdata = dev_get_parent_plat(dev);
+ cs = slave_pdata->cs;
+ speed = slave_pdata->max_hz;
+ mode = slave_pdata->mode;
+ spi_get_bus_and_cs(busnum, cs, speed, mode, "spi_generic_drv",
+ NULL, &spid, &udc->slave);
+
+ udc->dev = dev;
+ udc->gadget.ep0 = &udc->ep[0].ep_usb;
+ udc->gadget.max_speed = USB_SPEED_FULL;
+ udc->gadget.speed = USB_SPEED_FULL;
+ udc->gadget.is_dualspeed = 0;
+ udc->gadget.ops = &max3420_udc_ops;
+ udc->gadget.name = "max3420-udc";
+
+ max3420_setup_eps(udc);
+ max3420_setup_spi(udc);
+
+ usb_add_gadget_udc((struct device *)dev, &udc->gadget);
+
+ return 0;
+}
+
+static int max3420_udc_remove(struct udevice *dev)
+{
+ struct max3420_udc *udc = dev_get_priv(dev);
+
+ usb_del_gadget_udc(&udc->gadget);
+
+ spi_release_bus(udc->slave);
+
+ return 0;
+}
+
+static const struct udevice_id max3420_ids[] = {
+ { .compatible = "maxim,max3421-udc" },
+ { }
+};
+
+U_BOOT_DRIVER(max3420_generic_udc) = {
+ .name = "max3420-udc",
+ .id = UCLASS_USB_GADGET_GENERIC,
+ .of_match = max3420_ids,
+ .probe = max3420_udc_probe,
+ .remove = max3420_udc_remove,
+ .priv_auto = sizeof(struct max3420_udc),
+};
diff --git a/roms/u-boot/drivers/usb/gadget/ndis.h b/roms/u-boot/drivers/usb/gadget/ndis.h
new file mode 100644
index 000000000..753838f79
--- /dev/null
+++ b/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/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c b/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c
new file mode 100644
index 000000000..d19ac1d03
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c
@@ -0,0 +1,2049 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 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>
+ *
+ * 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 <log.h>
+#include <asm/byteorder.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <asm/unaligned.h>
+#include <dm/devres.h>
+#include <linux/bug.h>
+#include <linux/compat.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/arch/pxa.h>
+#include <linux/delay.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.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;
+
+ dev->mach = &mach_info;
+
+ udc_disable(dev);
+ udc_reinit(dev);
+
+ 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(int index)
+{
+ return pxa25x_udc_irq();
+}
diff --git a/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h b/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h
new file mode 100644
index 000000000..7c3882aa1
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * 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>
+ */
+
+#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/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c b/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c
new file mode 100644
index 000000000..ba362b8f2
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c
@@ -0,0 +1,703 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 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>
+ */
+
+
+#include <common.h>
+#include <asm/arch/hardware.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <usbdevice.h>
+#include <linux/delay.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_t(unsigned int, 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/roms/u-boot/drivers/usb/gadget/rndis.c b/roms/u-boot/drivers/usb/gadget/rndis.c
new file mode 100644
index 000000000..13c327ea3
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/rndis.c
@@ -0,0 +1,1314 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RNDIS MSG parser
+ *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, Pengutronix
+ *
+ * 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 <log.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 <linux/errno.h>
+
+#undef RNDIS_PM
+#undef RNDIS_WAKEUP
+#undef VERBOSE
+
+#include "rndis.h"
+
+/*
+ * 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;
+}
+
+#ifndef CONFIG_DM_ETH
+int rndis_register(int (*rndis_control_ack)(struct eth_device *))
+#else
+int rndis_register(int (*rndis_control_ack)(struct udevice *))
+#endif
+{
+ 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;
+}
+
+#ifndef CONFIG_DM_ETH
+int rndis_set_param_dev(u8 configNr, struct eth_device *dev, int mtu,
+ struct net_device_stats *stats, u16 *cdc_filter)
+#else
+int rndis_set_param_dev(u8 configNr, struct udevice *dev, int mtu,
+ struct net_device_stats *stats, u16 *cdc_filter)
+#endif
+{
+ 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/roms/u-boot/drivers/usb/gadget/rndis.h b/roms/u-boot/drivers/usb/gadget/rndis.h
new file mode 100644
index 000000000..e827af0be
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/rndis.h
@@ -0,0 +1,268 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * RNDIS Definitions for Remote NDIS
+ *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, Pengutronix
+ *
+ * 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 net_device_stats *stats;
+ int mtu;
+
+ u32 vendorID;
+ const char *vendorDescr;
+#ifndef CONFIG_DM_ETH
+ struct eth_device *dev;
+ int (*ack)(struct eth_device *);
+#else
+ struct udevice *dev;
+ int (*ack)(struct udevice *);
+#endif
+ 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);
+void rndis_deregister(int configNr);
+#ifndef CONFIG_DM_ETH
+int rndis_register(int (*rndis_control_ack)(struct eth_device *));
+int rndis_set_param_dev(u8 configNr, struct eth_device *dev, int mtu,
+ struct net_device_stats *stats, u16 *cdc_filter);
+#else
+int rndis_register(int (*rndis_control_ack)(struct udevice *));
+int rndis_set_param_dev(u8 configNr, struct udevice *dev, int mtu,
+ struct net_device_stats *stats, u16 *cdc_filter);
+#endif
+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/roms/u-boot/drivers/usb/gadget/storage_common.c b/roms/u-boot/drivers/usb/gadget/storage_common.c
new file mode 100644
index 000000000..5674e8fe4
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/storage_common.c
@@ -0,0 +1,617 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 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>
+ */
+
+
+/*
+ * 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("", 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("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; };
+#define ETOOSMALL 525
+
+#include <log.h>
+#include <usb_mass_storage.h>
+#include <dm/device_compat.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 */
+#define FSG_NUM_BUFFERS 2
+
+/* Default size of buffer length. */
+#define FSG_BUFLEN ((u32)131072)
+
+/* 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, unsigned int num_sectors,
+ 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 = num_sectors << 9;
+ curlun->num_sectors = 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/roms/u-boot/drivers/usb/gadget/u_os_desc.h b/roms/u-boot/drivers/usb/gadget/u_os_desc.h
new file mode 100644
index 000000000..4dab4814a
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/u_os_desc.h
@@ -0,0 +1,123 @@
+/*
+ * u_os_desc.h
+ *
+ * Utility definitions for "OS Descriptors" support
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * 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.
+ */
+
+#ifndef __U_OS_DESC_H__
+#define __U_OS_DESC_H__
+
+#include <linux/utf.h>
+
+#define USB_EXT_PROP_DW_SIZE 0
+#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE 4
+#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH 8
+#define USB_EXT_PROP_B_PROPERTY_NAME 10
+#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH 10
+#define USB_EXT_PROP_B_PROPERTY_DATA 14
+
+#define USB_EXT_PROP_RESERVED 0
+#define USB_EXT_PROP_UNICODE 1
+#define USB_EXT_PROP_UNICODE_ENV 2
+#define USB_EXT_PROP_BINARY 3
+#define USB_EXT_PROP_LE32 4
+#define USB_EXT_PROP_BE32 5
+#define USB_EXT_PROP_UNICODE_LINK 6
+#define USB_EXT_PROP_UNICODE_MULTI 7
+
+static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset)
+{
+ return buf + offset;
+}
+
+static inline u8 *usb_ext_prop_size_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE);
+}
+
+static inline u8 *usb_ext_prop_type_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE);
+}
+
+static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH);
+}
+
+static inline u8 *usb_ext_prop_name_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME);
+}
+
+static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off)
+{
+ return __usb_ext_prop_ptr(buf,
+ USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off);
+}
+
+static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off);
+}
+
+static inline void usb_ext_prop_put_size(u8 *buf, int dw_size)
+{
+ put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf));
+}
+
+static inline void usb_ext_prop_put_type(u8 *buf, int type)
+{
+ put_unaligned_le32(type, usb_ext_prop_type_ptr(buf));
+}
+
+static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl)
+{
+ int result;
+
+ put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf));
+ memset(usb_ext_prop_name_ptr(buf), 0, 2 * strlen(name));
+ result = utf8_to_utf16le(name, (__le16 *)usb_ext_prop_name_ptr(buf),
+ strlen(name));
+ if (result < 0)
+ return result;
+
+ put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]);
+
+ return pnl;
+}
+
+static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const char *data,
+ int data_len)
+{
+ put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
+ memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len);
+}
+
+static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string,
+ int data_len)
+{
+ int result;
+ put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
+ memset(usb_ext_prop_data_ptr(buf, pnl), 0, 2 * (data_len >> 1));
+ result = utf8_to_utf16le(string, (__le16 *) usb_ext_prop_data_ptr(buf, pnl),
+ data_len >> 1);
+ if (result < 0)
+ return result;
+
+ put_unaligned_le16(0,
+ &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]);
+
+ return data_len;
+}
+
+#endif /* __U_OS_DESC_H__ */
diff --git a/roms/u-boot/drivers/usb/gadget/udc/Makefile b/roms/u-boot/drivers/usb/gadget/udc/Makefile
new file mode 100644
index 000000000..95dbf0c82
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/udc/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# USB peripheral controller drivers
+
+ifndef CONFIG_$(SPL_)DM_USB_GADGET
+obj-$(CONFIG_USB_DWC3_GADGET) += udc-core.o
+endif
+
+obj-$(CONFIG_$(SPL_)DM_USB_GADGET) += udc-core.o
+obj-$(CONFIG_$(SPL_)DM) += udc-uclass.o
diff --git a/roms/u-boot/drivers/usb/gadget/udc/udc-core.c b/roms/u-boot/drivers/usb/gadget/udc/udc-core.c
new file mode 100644
index 000000000..7f73926cb
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/udc/udc-core.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * udc-core.c - Core UDC Framework
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * Taken from Linux Kernel v3.19-rc1 (drivers/usb/gadget/udc-core.c) and ported
+ * to uboot.
+ *
+ * commit 02e8c96627 : usb: gadget: udc: core: prepend udc_attach_driver with
+ * usb_
+ */
+
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/compat.h>
+#include <malloc.h>
+#include <asm/cache.h>
+#include <linux/dma-mapping.h>
+#include <common.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/**
+ * struct usb_udc - describes one usb device controller
+ * @driver - the gadget driver pointer. For use by the class code
+ * @dev - the child device to the actual controller
+ * @gadget - the gadget. For use by the class code
+ * @list - for use by the udc class driver
+ *
+ * This represents the internal data structure which is used by the UDC-class
+ * to hold information about udc driver and gadget together.
+ */
+struct usb_udc {
+ struct usb_gadget_driver *driver;
+ struct usb_gadget *gadget;
+ struct device dev;
+ struct list_head list;
+};
+
+static struct class *udc_class;
+static LIST_HEAD(udc_list);
+DEFINE_MUTEX(udc_lock);
+
+/* ------------------------------------------------------------------------- */
+
+int usb_gadget_map_request(struct usb_gadget *gadget,
+ struct usb_request *req, int is_in)
+{
+ if (req->length == 0)
+ return 0;
+
+ req->dma = dma_map_single(req->buf, req->length,
+ is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_map_request);
+
+void usb_gadget_unmap_request(struct usb_gadget *gadget,
+ struct usb_request *req, int is_in)
+{
+ if (req->length == 0)
+ return;
+
+ dma_unmap_single(req->dma, req->length,
+ is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * usb_gadget_giveback_request - give the request back to the gadget layer
+ * Context: in_interrupt()
+ *
+ * This is called by device controller drivers in order to return the
+ * completed request back to the gadget layer.
+ */
+void usb_gadget_giveback_request(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ req->complete(ep, req);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
+
+/* ------------------------------------------------------------------------- */
+
+void usb_gadget_set_state(struct usb_gadget *gadget,
+ enum usb_device_state state)
+{
+ gadget->state = state;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_set_state);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * usb_gadget_udc_reset - notifies the udc core that bus reset occurs
+ * @gadget: The gadget which bus reset occurs
+ * @driver: The gadget driver we want to notify
+ *
+ * If the udc driver has bus reset handler, it needs to call this when the bus
+ * reset occurs, it notifies the gadget driver that the bus reset occurs as
+ * well as updates gadget state.
+ */
+void usb_gadget_udc_reset(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ driver->reset(gadget);
+ usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
+
+/**
+ * usb_gadget_udc_start - tells usb device controller to start up
+ * @udc: The UDC to be started
+ *
+ * This call is issued by the UDC Class driver when it's about
+ * to register a gadget driver to the device controller, before
+ * calling gadget driver's bind() method.
+ *
+ * It allows the controller to be powered off until strictly
+ * necessary to have it powered on.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static inline int usb_gadget_udc_start(struct usb_udc *udc)
+{
+ return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
+}
+
+/**
+ * usb_gadget_udc_stop - tells usb device controller we don't need it anymore
+ * @gadget: The device we want to stop activity
+ * @driver: The driver to unbind from @gadget
+ *
+ * This call is issued by the UDC Class driver after calling
+ * gadget driver's unbind() method.
+ *
+ * The details are implementation specific, but it can go as
+ * far as powering off UDC completely and disable its data
+ * line pullups.
+ */
+static inline void usb_gadget_udc_stop(struct usb_udc *udc)
+{
+ udc->gadget->ops->udc_stop(udc->gadget);
+}
+
+/**
+ * usb_udc_release - release the usb_udc struct
+ * @dev: the dev member within usb_udc
+ *
+ * This is called by driver's core in order to free memory once the last
+ * reference is released.
+ */
+static void usb_udc_release(struct device *dev)
+{
+ struct usb_udc *udc;
+
+ udc = container_of(dev, struct usb_udc, dev);
+ kfree(udc);
+}
+
+/**
+ * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
+ * @parent: the parent device to this udc. Usually the controller driver's
+ * device.
+ * @gadget: the gadget to be added to the list.
+ * @release: a gadget release function.
+ *
+ * Returns zero on success, negative errno otherwise.
+ */
+int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
+ void (*release)(struct device *dev))
+{
+ struct usb_udc *udc;
+ int ret = -ENOMEM;
+
+ udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ goto err1;
+
+ dev_set_name(&gadget->dev, "gadget");
+ gadget->dev.parent = parent;
+
+ udc->dev.release = usb_udc_release;
+ udc->dev.class = udc_class;
+ udc->dev.parent = parent;
+
+ udc->gadget = gadget;
+
+ mutex_lock(&udc_lock);
+ list_add_tail(&udc->list, &udc_list);
+
+ usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
+
+ mutex_unlock(&udc_lock);
+
+ return 0;
+
+err1:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
+
+/**
+ * usb_add_gadget_udc - adds a new gadget to the udc class driver list
+ * @parent: the parent device to this udc. Usually the controller
+ * driver's device.
+ * @gadget: the gadget to be added to the list
+ *
+ * Returns zero on success, negative errno otherwise.
+ */
+int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
+{
+ return usb_add_gadget_udc_release(parent, gadget, NULL);
+}
+EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
+
+static void usb_gadget_remove_driver(struct usb_udc *udc)
+{
+ dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
+ udc->driver->function);
+
+ usb_gadget_disconnect(udc->gadget);
+ udc->driver->disconnect(udc->gadget);
+ udc->driver->unbind(udc->gadget);
+ usb_gadget_udc_stop(udc);
+
+ udc->driver = NULL;
+}
+
+/**
+ * usb_del_gadget_udc - deletes @udc from udc_list
+ * @gadget: the gadget to be removed.
+ *
+ * This, will call usb_gadget_unregister_driver() if
+ * the @udc is still busy.
+ */
+void usb_del_gadget_udc(struct usb_gadget *gadget)
+{
+ struct usb_udc *udc = NULL;
+
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list)
+ if (udc->gadget == gadget)
+ goto found;
+
+ dev_err(gadget->dev.parent, "gadget not registered.\n");
+ mutex_unlock(&udc_lock);
+
+ return;
+
+found:
+ dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
+
+ list_del(&udc->list);
+ mutex_unlock(&udc_lock);
+
+ if (udc->driver)
+ usb_gadget_remove_driver(udc);
+}
+EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * usb_gadget_udc_set_speed - tells usb device controller speed supported by
+ * current driver
+ * @udc: The device we want to set maximum speed
+ * @speed: The maximum speed to allowed to run
+ *
+ * This call is issued by the UDC Class driver before calling
+ * usb_gadget_udc_start() in order to make sure that we don't try to
+ * connect on speeds the gadget driver doesn't support.
+ */
+static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
+ enum usb_device_speed speed)
+{
+ if (udc->gadget->ops->udc_set_speed) {
+ enum usb_device_speed s;
+
+ s = min(speed, udc->gadget->max_speed);
+ udc->gadget->ops->udc_set_speed(udc->gadget, s);
+ }
+}
+
+static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
+{
+ int ret;
+
+ dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
+ driver->function);
+
+ udc->driver = driver;
+
+ usb_gadget_udc_set_speed(udc, driver->speed);
+
+ ret = driver->bind(udc->gadget);
+ if (ret)
+ goto err1;
+ ret = usb_gadget_udc_start(udc);
+ if (ret) {
+ driver->unbind(udc->gadget);
+ goto err1;
+ }
+ usb_gadget_connect(udc->gadget);
+
+ return 0;
+err1:
+ if (ret != -EISNAM)
+ dev_err(&udc->dev, "failed to start %s: %d\n",
+ udc->driver->function, ret);
+ udc->driver = NULL;
+ return ret;
+}
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
+{
+ struct usb_udc *udc = NULL;
+ int ret;
+
+ if (!driver || !driver->bind || !driver->setup)
+ return -EINVAL;
+
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list) {
+ /* For now we take the first one */
+ if (!udc->driver)
+ goto found;
+ }
+
+ printf("couldn't find an available UDC\n");
+ mutex_unlock(&udc_lock);
+ return -ENODEV;
+found:
+ ret = udc_bind_to_driver(udc, driver);
+ mutex_unlock(&udc_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ return usb_gadget_probe_driver(driver);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct usb_udc *udc = NULL;
+ int ret = -ENODEV;
+
+ if (!driver || !driver->unbind)
+ return -EINVAL;
+
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list)
+ if (udc->driver == driver) {
+ usb_gadget_remove_driver(udc);
+ usb_gadget_set_state(udc->gadget,
+ USB_STATE_NOTATTACHED);
+ ret = 0;
+ break;
+ }
+
+ mutex_unlock(&udc_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
+
+MODULE_DESCRIPTION("UDC Framework");
+MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/roms/u-boot/drivers/usb/gadget/udc/udc-uclass.c b/roms/u-boot/drivers/usb/gadget/udc/udc-uclass.c
new file mode 100644
index 000000000..dbc354e84
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/udc/udc-uclass.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
+ * Written by Jean-Jacques Hiblot <jjhiblot@ti.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <linux/usb/gadget.h>
+
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+#define MAX_UDC_DEVICES 4
+static struct udevice *dev_array[MAX_UDC_DEVICES];
+int usb_gadget_initialize(int index)
+{
+ int ret;
+ struct udevice *dev = NULL;
+
+ if (index < 0 || index >= ARRAY_SIZE(dev_array))
+ return -EINVAL;
+ if (dev_array[index])
+ return 0;
+ ret = uclass_get_device_by_seq(UCLASS_USB_GADGET_GENERIC, index, &dev);
+ if (!dev || ret) {
+ ret = uclass_get_device(UCLASS_USB_GADGET_GENERIC, index, &dev);
+ if (!dev || ret) {
+ pr_err("No USB device found\n");
+ return -ENODEV;
+ }
+ }
+ dev_array[index] = dev;
+ return 0;
+}
+
+int usb_gadget_release(int index)
+{
+#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)
+ int ret;
+ if (index < 0 || index >= ARRAY_SIZE(dev_array))
+ return -EINVAL;
+
+ ret = device_remove(dev_array[index], DM_REMOVE_NORMAL);
+ if (!ret)
+ dev_array[index] = NULL;
+ return ret;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int usb_gadget_handle_interrupts(int index)
+{
+ if (index < 0 || index >= ARRAY_SIZE(dev_array))
+ return -EINVAL;
+ return dm_usb_gadget_handle_interrupts(dev_array[index]);
+}
+#endif
+
+UCLASS_DRIVER(usb_gadget_generic) = {
+ .id = UCLASS_USB_GADGET_GENERIC,
+ .name = "usb",
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+};
diff --git a/roms/u-boot/drivers/usb/gadget/usbstring.c b/roms/u-boot/drivers/usb/gadget/usbstring.c
new file mode 100644
index 000000000..e2464ad92
--- /dev/null
+++ b/roms/u-boot/drivers/usb/gadget/usbstring.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2003 David Brownell
+ *
+ * Ported to U-Boot by: Thomas Smits <ts.smits@gmail.com> and
+ * Remy Bohmer <linux@bohmer.net>
+ */
+
+#include <common.h>
+#include <linux/errno.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/utf.h>
+
+/**
+ * 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];
+}