GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
#
|
||||
# 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 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_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.
|
||||
|
||||
# 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
|
||||
@@ -0,0 +1,47 @@
|
||||
# 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_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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
@@ -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
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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 "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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,669 @@
|
||||
// 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 <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;
|
||||
|
||||
char *usbd_device_events[] = {
|
||||
"DEVICE_UNKNOWN",
|
||||
"DEVICE_INIT",
|
||||
"DEVICE_CREATE",
|
||||
"DEVICE_HUB_CONFIGURED",
|
||||
"DEVICE_RESET",
|
||||
"DEVICE_ADDRESS_ASSIGNED",
|
||||
"DEVICE_CONFIGURED",
|
||||
"DEVICE_SET_INTERFACE",
|
||||
"DEVICE_SET_FEATURE",
|
||||
"DEVICE_CLEAR_FEATURE",
|
||||
"DEVICE_DE_CONFIGURED",
|
||||
"DEVICE_BUS_INACTIVE",
|
||||
"DEVICE_BUS_ACTIVITY",
|
||||
"DEVICE_POWER_INTERRUPTION",
|
||||
"DEVICE_HUB_RESET",
|
||||
"DEVICE_DESTROY",
|
||||
"DEVICE_FUNCTION_PRIVATE",
|
||||
};
|
||||
|
||||
char *usbd_device_states[] = {
|
||||
"STATE_INIT",
|
||||
"STATE_CREATED",
|
||||
"STATE_ATTACHED",
|
||||
"STATE_POWERED",
|
||||
"STATE_DEFAULT",
|
||||
"STATE_ADDRESSED",
|
||||
"STATE_CONFIGURED",
|
||||
"STATE_UNKNOWN",
|
||||
};
|
||||
|
||||
char *usbd_device_requests[] = {
|
||||
"GET STATUS", /* 0 */
|
||||
"CLEAR FEATURE", /* 1 */
|
||||
"RESERVED", /* 2 */
|
||||
"SET FEATURE", /* 3 */
|
||||
"RESERVED", /* 4 */
|
||||
"SET ADDRESS", /* 5 */
|
||||
"GET DESCRIPTOR", /* 6 */
|
||||
"SET DESCRIPTOR", /* 7 */
|
||||
"GET CONFIGURATION", /* 8 */
|
||||
"SET CONFIGURATION", /* 9 */
|
||||
"GET INTERFACE", /* 10 */
|
||||
"SET INTERFACE", /* 11 */
|
||||
"SYNC FRAME", /* 12 */
|
||||
};
|
||||
|
||||
char *usbd_device_descriptors[] = {
|
||||
"UNKNOWN", /* 0 */
|
||||
"DEVICE", /* 1 */
|
||||
"CONFIG", /* 2 */
|
||||
"STRING", /* 3 */
|
||||
"INTERFACE", /* 4 */
|
||||
"ENDPOINT", /* 5 */
|
||||
"DEVICE QUALIFIER", /* 6 */
|
||||
"OTHER SPEED", /* 7 */
|
||||
"INTERFACE POWER", /* 8 */
|
||||
};
|
||||
|
||||
char *usbd_device_status[] = {
|
||||
"USBD_OPENING",
|
||||
"USBD_OK",
|
||||
"USBD_SUSPENDED",
|
||||
"USBD_CLOSING",
|
||||
};
|
||||
|
||||
|
||||
/* Descriptor support functions ************************************************************** */
|
||||
|
||||
|
||||
/**
|
||||
* usbd_get_string - find and return a string descriptor
|
||||
* @index: string index to return
|
||||
*
|
||||
* Find an indexed string and return a pointer to a it.
|
||||
*/
|
||||
struct usb_string_descriptor *usbd_get_string (__u8 index)
|
||||
{
|
||||
if (index >= maxstrings) {
|
||||
return NULL;
|
||||
}
|
||||
return usb_strings[index];
|
||||
}
|
||||
|
||||
|
||||
/* Access to device descriptor functions ***************************************************** */
|
||||
|
||||
|
||||
/* *
|
||||
* usbd_device_configuration_instance - find a configuration instance for this device
|
||||
* @device:
|
||||
* @configuration: index to configuration, 0 - N-1
|
||||
*
|
||||
* Get specifed device configuration. Index should be bConfigurationValue-1.
|
||||
*/
|
||||
static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device,
|
||||
unsigned int port, unsigned int configuration)
|
||||
{
|
||||
if (configuration >= device->configurations)
|
||||
return NULL;
|
||||
|
||||
return device->configuration_instance_array + configuration;
|
||||
}
|
||||
|
||||
|
||||
/* *
|
||||
* usbd_device_interface_instance
|
||||
* @device:
|
||||
* @configuration: index to configuration, 0 - N-1
|
||||
* @interface: index to interface
|
||||
*
|
||||
* Return the specified interface descriptor for the specified device.
|
||||
*/
|
||||
struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface)
|
||||
{
|
||||
struct usb_configuration_instance *configuration_instance;
|
||||
|
||||
if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (interface >= configuration_instance->interfaces) {
|
||||
return NULL;
|
||||
}
|
||||
return configuration_instance->interface_instance_array + interface;
|
||||
}
|
||||
|
||||
/* *
|
||||
* usbd_device_alternate_descriptor_list
|
||||
* @device:
|
||||
* @configuration: index to configuration, 0 - N-1
|
||||
* @interface: index to interface
|
||||
* @alternate: alternate setting
|
||||
*
|
||||
* Return the specified alternate descriptor for the specified device.
|
||||
*/
|
||||
struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate)
|
||||
{
|
||||
struct usb_interface_instance *interface_instance;
|
||||
|
||||
if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (alternate >= interface_instance->alternates) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return interface_instance->alternates_instance_array + alternate;
|
||||
}
|
||||
|
||||
|
||||
/* *
|
||||
* usbd_device_device_descriptor
|
||||
* @device: which device
|
||||
* @configuration: index to configuration, 0 - N-1
|
||||
* @port: which port
|
||||
*
|
||||
* Return the specified configuration descriptor for the specified device.
|
||||
*/
|
||||
struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port)
|
||||
{
|
||||
return (device->device_descriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* usbd_device_configuration_descriptor
|
||||
* @device: which device
|
||||
* @port: which port
|
||||
* @configuration: index to configuration, 0 - N-1
|
||||
*
|
||||
* Return the specified configuration descriptor for the specified device.
|
||||
*/
|
||||
struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct
|
||||
usb_device_instance
|
||||
*device, int port, int configuration)
|
||||
{
|
||||
struct usb_configuration_instance *configuration_instance;
|
||||
if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) {
|
||||
return NULL;
|
||||
}
|
||||
return (configuration_instance->configuration_descriptor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usbd_device_interface_descriptor
|
||||
* @device: which device
|
||||
* @port: which port
|
||||
* @configuration: index to configuration, 0 - N-1
|
||||
* @interface: index to interface
|
||||
* @alternate: alternate setting
|
||||
*
|
||||
* Return the specified interface descriptor for the specified device.
|
||||
*/
|
||||
struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance
|
||||
*device, int port, int configuration, int interface, int alternate)
|
||||
{
|
||||
struct usb_interface_instance *interface_instance;
|
||||
if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) {
|
||||
return NULL;
|
||||
}
|
||||
if ((alternate < 0) || (alternate >= interface_instance->alternates)) {
|
||||
return NULL;
|
||||
}
|
||||
return (interface_instance->alternates_instance_array[alternate].interface_descriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* usbd_device_endpoint_descriptor_index
|
||||
* @device: which device
|
||||
* @port: which port
|
||||
* @configuration: index to configuration, 0 - N-1
|
||||
* @interface: index to interface
|
||||
* @alternate: index setting
|
||||
* @index: which index
|
||||
*
|
||||
* Return the specified endpoint descriptor for the specified device.
|
||||
*/
|
||||
struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance
|
||||
*device, int port, int configuration, int interface, int alternate, int index)
|
||||
{
|
||||
struct usb_alternate_instance *alternate_instance;
|
||||
|
||||
if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) {
|
||||
return NULL;
|
||||
}
|
||||
if (index >= alternate_instance->endpoints) {
|
||||
return NULL;
|
||||
}
|
||||
return *(alternate_instance->endpoints_descriptor_array + index);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usbd_device_endpoint_transfersize
|
||||
* @device: which device
|
||||
* @port: which port
|
||||
* @configuration: index to configuration, 0 - N-1
|
||||
* @interface: index to interface
|
||||
* @index: which index
|
||||
*
|
||||
* Return the specified endpoint transfer size;
|
||||
*/
|
||||
int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index)
|
||||
{
|
||||
struct usb_alternate_instance *alternate_instance;
|
||||
|
||||
if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) {
|
||||
return 0;
|
||||
}
|
||||
if (index >= alternate_instance->endpoints) {
|
||||
return 0;
|
||||
}
|
||||
return *(alternate_instance->endpoint_transfersize_array + index);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usbd_device_endpoint_descriptor
|
||||
* @device: which device
|
||||
* @port: which port
|
||||
* @configuration: index to configuration, 0 - N-1
|
||||
* @interface: index to interface
|
||||
* @alternate: alternate setting
|
||||
* @endpoint: which endpoint
|
||||
*
|
||||
* Return the specified endpoint descriptor for the specified device.
|
||||
*/
|
||||
struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint)
|
||||
{
|
||||
struct usb_endpoint_descriptor *endpoint_descriptor;
|
||||
int i;
|
||||
|
||||
for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) {
|
||||
if (endpoint_descriptor->bEndpointAddress == endpoint) {
|
||||
return endpoint_descriptor;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* usbd_endpoint_halted
|
||||
* @device: point to struct usb_device_instance
|
||||
* @endpoint: endpoint to check
|
||||
*
|
||||
* Return non-zero if endpoint is halted.
|
||||
*/
|
||||
int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint)
|
||||
{
|
||||
return (device->status == USB_STATUS_HALT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usbd_rcv_complete - complete a receive
|
||||
* @endpoint:
|
||||
* @len:
|
||||
* @urb_bad:
|
||||
*
|
||||
* Called from rcv interrupt to complete.
|
||||
*/
|
||||
void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad)
|
||||
{
|
||||
if (endpoint) {
|
||||
struct urb *rcv_urb;
|
||||
|
||||
/*usbdbg("len: %d urb: %p\n", len, endpoint->rcv_urb); */
|
||||
|
||||
/* if we had an urb then update actual_length, dispatch if neccessary */
|
||||
if ((rcv_urb = endpoint->rcv_urb)) {
|
||||
|
||||
/*usbdbg("actual: %d buffer: %d\n", */
|
||||
/*rcv_urb->actual_length, rcv_urb->buffer_length); */
|
||||
|
||||
/* check the urb is ok, are we adding data less than the packetsize */
|
||||
if (!urb_bad && (len <= endpoint->rcv_packetSize)) {
|
||||
/*usbdbg("updating actual_length by %d\n",len); */
|
||||
|
||||
/* increment the received data size */
|
||||
rcv_urb->actual_length += len;
|
||||
|
||||
} else {
|
||||
usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n",
|
||||
rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad);
|
||||
|
||||
rcv_urb->actual_length = 0;
|
||||
rcv_urb->status = RECV_ERROR;
|
||||
}
|
||||
} else {
|
||||
usberr("no rcv_urb!");
|
||||
}
|
||||
} else {
|
||||
usberr("no endpoint!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* usbd_tx_complete - complete a transmit
|
||||
* @endpoint:
|
||||
* @resetart:
|
||||
*
|
||||
* Called from tx interrupt to complete.
|
||||
*/
|
||||
void usbd_tx_complete (struct usb_endpoint_instance *endpoint)
|
||||
{
|
||||
if (endpoint) {
|
||||
struct urb *tx_urb;
|
||||
|
||||
/* if we have a tx_urb advance or reset, finish if complete */
|
||||
if ((tx_urb = endpoint->tx_urb)) {
|
||||
int sent = endpoint->last;
|
||||
endpoint->sent += sent;
|
||||
endpoint->last -= sent;
|
||||
|
||||
if( (endpoint->tx_urb->actual_length - endpoint->sent) <= 0 ) {
|
||||
tx_urb->actual_length = 0;
|
||||
endpoint->sent = 0;
|
||||
endpoint->last = 0;
|
||||
|
||||
/* Remove from active, save for re-use */
|
||||
urb_detach(tx_urb);
|
||||
urb_append(&endpoint->done, tx_urb);
|
||||
/*usbdbg("done->next %p, tx_urb %p, done %p", */
|
||||
/* endpoint->done.next, tx_urb, &endpoint->done); */
|
||||
|
||||
endpoint->tx_urb = first_urb_detached(&endpoint->tx);
|
||||
if( endpoint->tx_urb ) {
|
||||
endpoint->tx_queue--;
|
||||
usbdbg("got urb from tx list");
|
||||
}
|
||||
if( !endpoint->tx_urb ) {
|
||||
/*usbdbg("taking urb from done list"); */
|
||||
endpoint->tx_urb = first_urb_detached(&endpoint->done);
|
||||
}
|
||||
if( !endpoint->tx_urb ) {
|
||||
usbdbg("allocating new urb for tx_urb");
|
||||
endpoint->tx_urb = usbd_alloc_urb(tx_urb->device, endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* URB linked list functions ***************************************************** */
|
||||
|
||||
/*
|
||||
* Initialize an urb_link to be a single element list.
|
||||
* If the urb_link is being used as a distinguished list head
|
||||
* the list is empty when the head is the only link in the list.
|
||||
*/
|
||||
void urb_link_init (urb_link * ul)
|
||||
{
|
||||
if (ul) {
|
||||
ul->prev = ul->next = ul;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Detach an urb_link from a list, and set it
|
||||
* up as a single element list, so no dangling
|
||||
* pointers can be followed, and so it can be
|
||||
* joined to another list if so desired.
|
||||
*/
|
||||
void urb_detach (struct urb *urb)
|
||||
{
|
||||
if (urb) {
|
||||
urb_link *ul = &urb->link;
|
||||
ul->next->prev = ul->prev;
|
||||
ul->prev->next = ul->next;
|
||||
urb_link_init (ul);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the first urb_link in a list with a distinguished
|
||||
* head "hd", or NULL if the list is empty. This will also
|
||||
* work as a predicate, returning NULL if empty, and non-NULL
|
||||
* otherwise.
|
||||
*/
|
||||
urb_link *first_urb_link (urb_link * hd)
|
||||
{
|
||||
urb_link *nx;
|
||||
if (NULL != hd && NULL != (nx = hd->next) && nx != hd) {
|
||||
/* There is at least one element in the list */
|
||||
/* (besides the distinguished head). */
|
||||
return (nx);
|
||||
}
|
||||
/* The list is empty */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the first urb in a list with a distinguished
|
||||
* head "hd", or NULL if the list is empty.
|
||||
*/
|
||||
struct urb *first_urb (urb_link * hd)
|
||||
{
|
||||
urb_link *nx;
|
||||
if (NULL == (nx = first_urb_link (hd))) {
|
||||
/* The list is empty */
|
||||
return (NULL);
|
||||
}
|
||||
return (p2surround (struct urb, link, nx));
|
||||
}
|
||||
|
||||
/*
|
||||
* Detach and return the first urb in a list with a distinguished
|
||||
* head "hd", or NULL if the list is empty.
|
||||
*
|
||||
*/
|
||||
struct urb *first_urb_detached (urb_link * hd)
|
||||
{
|
||||
struct urb *urb;
|
||||
if ((urb = first_urb (hd))) {
|
||||
urb_detach (urb);
|
||||
}
|
||||
return urb;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Append an urb_link (or a whole list of
|
||||
* urb_links) to the tail of another list
|
||||
* of urb_links.
|
||||
*/
|
||||
void urb_append (urb_link * hd, struct urb *urb)
|
||||
{
|
||||
if (hd && urb) {
|
||||
urb_link *new = &urb->link;
|
||||
|
||||
/* This allows the new urb to be a list of urbs, */
|
||||
/* with new pointing at the first, but the link */
|
||||
/* must be initialized. */
|
||||
/* Order is important here... */
|
||||
urb_link *pul = hd->prev;
|
||||
new->prev->next = hd;
|
||||
hd->prev = new->prev;
|
||||
new->prev = pul;
|
||||
pul->next = new;
|
||||
}
|
||||
}
|
||||
|
||||
/* URB create/destroy functions ***************************************************** */
|
||||
|
||||
/**
|
||||
* usbd_alloc_urb - allocate an URB appropriate for specified endpoint
|
||||
* @device: device instance
|
||||
* @endpoint: endpoint
|
||||
*
|
||||
* Allocate an urb structure. The usb device urb structure is used to
|
||||
* contain all data associated with a transfer, including a setup packet for
|
||||
* control transfers.
|
||||
*
|
||||
* NOTE: endpoint_address MUST contain a direction flag.
|
||||
*/
|
||||
struct urb *usbd_alloc_urb (struct usb_device_instance *device,
|
||||
struct usb_endpoint_instance *endpoint)
|
||||
{
|
||||
struct urb *urb;
|
||||
|
||||
if (!(urb = (struct urb *) malloc (sizeof (struct urb)))) {
|
||||
usberr (" F A T A L: malloc(%zu) FAILED!!!!",
|
||||
sizeof (struct urb));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Fill in known fields */
|
||||
memset (urb, 0, sizeof (struct urb));
|
||||
urb->endpoint = endpoint;
|
||||
urb->device = device;
|
||||
urb->buffer = (u8 *) urb->buffer_data;
|
||||
urb->buffer_length = sizeof (urb->buffer_data);
|
||||
|
||||
urb_link_init (&urb->link);
|
||||
|
||||
return urb;
|
||||
}
|
||||
|
||||
/**
|
||||
* usbd_dealloc_urb - deallocate an URB and associated buffer
|
||||
* @urb: pointer to an urb structure
|
||||
*
|
||||
* Deallocate an urb structure and associated data.
|
||||
*/
|
||||
void usbd_dealloc_urb (struct urb *urb)
|
||||
{
|
||||
if (urb) {
|
||||
free (urb);
|
||||
}
|
||||
}
|
||||
|
||||
/* Event signaling functions ***************************************************** */
|
||||
|
||||
/**
|
||||
* usbd_device_event - called to respond to various usb events
|
||||
* @device: pointer to struct device
|
||||
* @event: event to respond to
|
||||
*
|
||||
* Used by a Bus driver to indicate an event.
|
||||
*/
|
||||
void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data)
|
||||
{
|
||||
usb_device_state_t state;
|
||||
|
||||
if (!device || !device->bus) {
|
||||
usberr("(%p,%d) NULL device or device->bus", device, event);
|
||||
return;
|
||||
}
|
||||
|
||||
state = device->device_state;
|
||||
|
||||
usbinfo("%s", usbd_device_events[event]);
|
||||
|
||||
switch (event) {
|
||||
case DEVICE_UNKNOWN:
|
||||
break;
|
||||
case DEVICE_INIT:
|
||||
device->device_state = STATE_INIT;
|
||||
break;
|
||||
|
||||
case DEVICE_CREATE:
|
||||
device->device_state = STATE_ATTACHED;
|
||||
break;
|
||||
|
||||
case DEVICE_HUB_CONFIGURED:
|
||||
device->device_state = STATE_POWERED;
|
||||
break;
|
||||
|
||||
case DEVICE_RESET:
|
||||
device->device_state = STATE_DEFAULT;
|
||||
device->address = 0;
|
||||
break;
|
||||
|
||||
case DEVICE_ADDRESS_ASSIGNED:
|
||||
device->device_state = STATE_ADDRESSED;
|
||||
break;
|
||||
|
||||
case DEVICE_CONFIGURED:
|
||||
device->device_state = STATE_CONFIGURED;
|
||||
break;
|
||||
|
||||
case DEVICE_DE_CONFIGURED:
|
||||
device->device_state = STATE_ADDRESSED;
|
||||
break;
|
||||
|
||||
case DEVICE_BUS_INACTIVE:
|
||||
if (device->status != USBD_CLOSING) {
|
||||
device->status = USBD_SUSPENDED;
|
||||
}
|
||||
break;
|
||||
case DEVICE_BUS_ACTIVITY:
|
||||
if (device->status != USBD_CLOSING) {
|
||||
device->status = USBD_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
case DEVICE_SET_INTERFACE:
|
||||
break;
|
||||
case DEVICE_SET_FEATURE:
|
||||
break;
|
||||
case DEVICE_CLEAR_FEATURE:
|
||||
break;
|
||||
|
||||
case DEVICE_POWER_INTERRUPTION:
|
||||
device->device_state = STATE_POWERED;
|
||||
break;
|
||||
case DEVICE_HUB_RESET:
|
||||
device->device_state = STATE_ATTACHED;
|
||||
break;
|
||||
case DEVICE_DESTROY:
|
||||
device->device_state = STATE_UNKNOWN;
|
||||
break;
|
||||
|
||||
case DEVICE_FUNCTION_PRIVATE:
|
||||
break;
|
||||
|
||||
default:
|
||||
usbdbg("event %d - not handled",event);
|
||||
break;
|
||||
}
|
||||
debug("%s event: %d oldstate: %d newstate: %d status: %d address: %d",
|
||||
device->name, event, state,
|
||||
device->device_state, device->status, device->address);
|
||||
|
||||
/* tell the bus interface driver */
|
||||
if( device->event ) {
|
||||
/* usbdbg("calling device->event"); */
|
||||
device->event(device, event, data);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,100 @@
|
||||
// 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/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);
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -0,0 +1,293 @@
|
||||
/* 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 */
|
||||
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)
|
||||
|
||||
/* 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,597 @@
|
||||
// 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
|
||||
|
||||
/* EP0 Configuration Set ********************************************************************* */
|
||||
|
||||
|
||||
/**
|
||||
* ep0_get_status - fill in URB data with appropriate status
|
||||
* @device:
|
||||
* @urb:
|
||||
* @index:
|
||||
* @requesttype:
|
||||
*
|
||||
*/
|
||||
static int ep0_get_status (struct usb_device_instance *device,
|
||||
struct urb *urb, int index, int requesttype)
|
||||
{
|
||||
char *cp;
|
||||
|
||||
urb->actual_length = 2;
|
||||
cp = (char*)urb->buffer;
|
||||
cp[0] = cp[1] = 0;
|
||||
|
||||
switch (requesttype) {
|
||||
case USB_REQ_RECIPIENT_DEVICE:
|
||||
cp[0] = USB_STATUS_SELFPOWERED;
|
||||
break;
|
||||
case USB_REQ_RECIPIENT_INTERFACE:
|
||||
break;
|
||||
case USB_REQ_RECIPIENT_ENDPOINT:
|
||||
cp[0] = usbd_endpoint_halted (device, index);
|
||||
break;
|
||||
case USB_REQ_RECIPIENT_OTHER:
|
||||
urb->actual_length = 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dbg_ep0 (2, "%02x %02x", cp[0], cp[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ep0_get_one
|
||||
* @device:
|
||||
* @urb:
|
||||
* @result:
|
||||
*
|
||||
* Set a single byte value in the urb send buffer. Return non-zero to signal
|
||||
* a request error.
|
||||
*/
|
||||
static int ep0_get_one (struct usb_device_instance *device, struct urb *urb,
|
||||
__u8 result)
|
||||
{
|
||||
urb->actual_length = 1; /* XXX 2? */
|
||||
((char *) urb->buffer)[0] = result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* copy_config
|
||||
* @urb: pointer to urb
|
||||
* @data: pointer to configuration data
|
||||
* @length: length of data
|
||||
*
|
||||
* Copy configuration data to urb transfer buffer if there is room for it.
|
||||
*/
|
||||
void copy_config (struct urb *urb, void *data, int max_length,
|
||||
int max_buf)
|
||||
{
|
||||
int available;
|
||||
int length;
|
||||
|
||||
/*dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", */
|
||||
/* urb->actual_length, urb->buffer_length, max_buf, max_length, data); */
|
||||
|
||||
if (!data) {
|
||||
dbg_ep0 (1, "data is NULL");
|
||||
return;
|
||||
}
|
||||
length = max_length;
|
||||
|
||||
if (length > max_length) {
|
||||
dbg_ep0 (1, "length: %d >= max_length: %d", length,
|
||||
max_length);
|
||||
return;
|
||||
}
|
||||
/*dbg_ep0(1, " actual: %d buf: %d max_buf: %d max_length: %d length: %d", */
|
||||
/* urb->actual_length, urb->buffer_length, max_buf, max_length, length); */
|
||||
|
||||
if ((available =
|
||||
/*urb->buffer_length */ max_buf - urb->actual_length) <= 0) {
|
||||
return;
|
||||
}
|
||||
/*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */
|
||||
/* urb->actual_length, urb->buffer_length, max_buf, length, available); */
|
||||
|
||||
if (length > available) {
|
||||
length = available;
|
||||
}
|
||||
/*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */
|
||||
/* urb->actual_length, urb->buffer_length, max_buf, length, available); */
|
||||
|
||||
memcpy (urb->buffer + urb->actual_length, data, length);
|
||||
urb->actual_length += length;
|
||||
|
||||
dbg_ep0 (3,
|
||||
"copy_config: <- actual: %d buf: %d max_buf: %d max_length: %d available: %d",
|
||||
urb->actual_length, urb->buffer_length, max_buf, max_length,
|
||||
available);
|
||||
}
|
||||
|
||||
/**
|
||||
* ep0_get_descriptor
|
||||
* @device:
|
||||
* @urb:
|
||||
* @max:
|
||||
* @descriptor_type:
|
||||
* @index:
|
||||
*
|
||||
* Called by ep0_rx_process for a get descriptor device command. Determine what
|
||||
* descriptor is being requested, copy to send buffer. Return zero if ok to send,
|
||||
* return non-zero to signal a request error.
|
||||
*/
|
||||
static int ep0_get_descriptor (struct usb_device_instance *device,
|
||||
struct urb *urb, int max, int descriptor_type,
|
||||
int index)
|
||||
{
|
||||
int port = 0; /* XXX compound device */
|
||||
|
||||
/*dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index); */
|
||||
|
||||
if (!urb || !urb->buffer || !urb->buffer_length
|
||||
|| (urb->buffer_length < 255)) {
|
||||
dbg_ep0 (2, "invalid urb %p", urb);
|
||||
return -1L;
|
||||
}
|
||||
|
||||
/* setup tx urb */
|
||||
urb->actual_length = 0;
|
||||
|
||||
dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type));
|
||||
|
||||
switch (descriptor_type) {
|
||||
case USB_DESCRIPTOR_TYPE_DEVICE:
|
||||
{
|
||||
struct usb_device_descriptor *device_descriptor;
|
||||
if (!
|
||||
(device_descriptor =
|
||||
usbd_device_device_descriptor (device, port))) {
|
||||
return -1;
|
||||
}
|
||||
/* copy descriptor for this device */
|
||||
copy_config (urb, device_descriptor,
|
||||
sizeof (struct usb_device_descriptor),
|
||||
max);
|
||||
|
||||
/* correct the correct control endpoint 0 max packet size into the descriptor */
|
||||
device_descriptor =
|
||||
(struct usb_device_descriptor *) urb->buffer;
|
||||
|
||||
}
|
||||
dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length);
|
||||
break;
|
||||
|
||||
case USB_DESCRIPTOR_TYPE_CONFIGURATION:
|
||||
{
|
||||
struct usb_configuration_descriptor
|
||||
*configuration_descriptor;
|
||||
struct usb_device_descriptor *device_descriptor;
|
||||
if (!
|
||||
(device_descriptor =
|
||||
usbd_device_device_descriptor (device, port))) {
|
||||
return -1;
|
||||
}
|
||||
/*dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations); */
|
||||
if (index >= device_descriptor->bNumConfigurations) {
|
||||
dbg_ep0 (0, "index too large: %d >= %d", index,
|
||||
device_descriptor->
|
||||
bNumConfigurations);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!
|
||||
(configuration_descriptor =
|
||||
usbd_device_configuration_descriptor (device,
|
||||
port,
|
||||
index))) {
|
||||
dbg_ep0 (0,
|
||||
"usbd_device_configuration_descriptor failed: %d",
|
||||
index);
|
||||
return -1;
|
||||
}
|
||||
dbg_ep0(0, "attempt to copy %d bytes to urb\n",cpu_to_le16(configuration_descriptor->wTotalLength));
|
||||
copy_config (urb, configuration_descriptor,
|
||||
|
||||
cpu_to_le16(configuration_descriptor->wTotalLength),
|
||||
max);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case USB_DESCRIPTOR_TYPE_STRING:
|
||||
{
|
||||
struct usb_string_descriptor *string_descriptor;
|
||||
if (!(string_descriptor = usbd_get_string (index))) {
|
||||
serial_printf("Invalid string index %d\n", index);
|
||||
return -1;
|
||||
}
|
||||
dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength);
|
||||
copy_config (urb, string_descriptor, string_descriptor->bLength, max);
|
||||
}
|
||||
break;
|
||||
case USB_DESCRIPTOR_TYPE_INTERFACE:
|
||||
serial_printf("USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n");
|
||||
return -1;
|
||||
case USB_DESCRIPTOR_TYPE_ENDPOINT:
|
||||
serial_printf("USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n");
|
||||
return -1;
|
||||
case USB_DESCRIPTOR_TYPE_HID:
|
||||
{
|
||||
serial_printf("USB_DESCRIPTOR_TYPE_HID - error not implemented\n");
|
||||
return -1; /* unsupported at this time */
|
||||
#if 0
|
||||
int bNumInterface =
|
||||
le16_to_cpu (urb->device_request.wIndex);
|
||||
int bAlternateSetting = 0;
|
||||
int class = 0;
|
||||
struct usb_class_descriptor *class_descriptor;
|
||||
|
||||
if (!(class_descriptor =
|
||||
usbd_device_class_descriptor_index (device,
|
||||
port, 0,
|
||||
bNumInterface,
|
||||
bAlternateSetting,
|
||||
class))
|
||||
|| class_descriptor->descriptor.hid.bDescriptorType != USB_DT_HID) {
|
||||
dbg_ep0 (3, "[%d] interface is not HID",
|
||||
bNumInterface);
|
||||
return -1;
|
||||
}
|
||||
/* copy descriptor for this class */
|
||||
copy_config (urb, class_descriptor,
|
||||
class_descriptor->descriptor.hid.bLength,
|
||||
max);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case USB_DESCRIPTOR_TYPE_REPORT:
|
||||
{
|
||||
serial_printf("USB_DESCRIPTOR_TYPE_REPORT - error not implemented\n");
|
||||
return -1; /* unsupported at this time */
|
||||
#if 0
|
||||
int bNumInterface =
|
||||
le16_to_cpu (urb->device_request.wIndex);
|
||||
int bAlternateSetting = 0;
|
||||
int class = 0;
|
||||
struct usb_class_report_descriptor *report_descriptor;
|
||||
|
||||
if (!(report_descriptor =
|
||||
usbd_device_class_report_descriptor_index
|
||||
(device, port, 0, bNumInterface,
|
||||
bAlternateSetting, class))
|
||||
|| report_descriptor->bDescriptorType !=
|
||||
USB_DT_REPORT) {
|
||||
dbg_ep0 (3, "[%d] descriptor is not REPORT",
|
||||
bNumInterface);
|
||||
return -1;
|
||||
}
|
||||
/* copy report descriptor for this class */
|
||||
/*copy_config(urb, &report_descriptor->bData[0], report_descriptor->wLength, max); */
|
||||
if (max - urb->actual_length > 0) {
|
||||
int length =
|
||||
min(report_descriptor->wLength,
|
||||
max - urb->actual_length);
|
||||
memcpy (urb->buffer + urb->actual_length,
|
||||
&report_descriptor->bData[0], length);
|
||||
urb->actual_length += length;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
|
||||
#if defined(CONFIG_USBD_HS)
|
||||
{
|
||||
struct usb_qualifier_descriptor *qualifier_descriptor =
|
||||
device->qualifier_descriptor;
|
||||
|
||||
if (!qualifier_descriptor)
|
||||
return -1;
|
||||
|
||||
/* copy descriptor for this device */
|
||||
copy_config(urb, qualifier_descriptor,
|
||||
sizeof(struct usb_qualifier_descriptor),
|
||||
max);
|
||||
|
||||
}
|
||||
dbg_ep0(3, "copied qualifier descriptor, actual_length: 0x%x",
|
||||
urb->actual_length);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d tx_packetSize: %2d",
|
||||
urb->buffer, urb->buffer_length, urb->actual_length,
|
||||
device->bus->endpoint_array[0].tx_packetSize);
|
||||
/*
|
||||
if ((urb->actual_length < max) && !(urb->actual_length % device->bus->endpoint_array[0].tx_packetSize)) {
|
||||
dbg_ep0(0, "adding null byte");
|
||||
urb->buffer[urb->actual_length++] = 0;
|
||||
dbg_ep0(0, "urb: buffer_length: %2d actual_length: %2d packet size: %2d",
|
||||
urb->buffer_length, urb->actual_length device->bus->endpoint_array[0].tx_packetSize);
|
||||
}
|
||||
*/
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* ep0_recv_setup - called to indicate URB has been received
|
||||
* @urb: pointer to struct urb
|
||||
*
|
||||
* Check if this is a setup packet, process the device request, put results
|
||||
* back into the urb and return zero or non-zero to indicate success (DATA)
|
||||
* or failure (STALL).
|
||||
*
|
||||
*/
|
||||
int ep0_recv_setup (struct urb *urb)
|
||||
{
|
||||
/*struct usb_device_request *request = urb->buffer; */
|
||||
/*struct usb_device_instance *device = urb->device; */
|
||||
|
||||
struct usb_device_request *request;
|
||||
struct usb_device_instance *device;
|
||||
int address;
|
||||
|
||||
dbg_ep0 (0, "entering ep0_recv_setup()");
|
||||
if (!urb || !urb->device) {
|
||||
dbg_ep0 (3, "invalid URB %p", urb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
request = &urb->device_request;
|
||||
device = urb->device;
|
||||
|
||||
dbg_ep0 (3, "urb: %p device: %p", urb, urb->device);
|
||||
|
||||
|
||||
/*dbg_ep0(2, "- - - - - - - - - -"); */
|
||||
|
||||
dbg_ep0 (2,
|
||||
"bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %s",
|
||||
request->bmRequestType, request->bRequest,
|
||||
le16_to_cpu (request->wValue), le16_to_cpu (request->wIndex),
|
||||
le16_to_cpu (request->wLength),
|
||||
USBD_DEVICE_REQUESTS (request->bRequest));
|
||||
|
||||
/* handle USB Standard Request (c.f. USB Spec table 9-2) */
|
||||
if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) {
|
||||
if(device->device_state <= STATE_CONFIGURED){
|
||||
/* Attempt to handle a CDC specific request if we are
|
||||
* in the configured state.
|
||||
*/
|
||||
return device->cdc_recv_setup(request,urb);
|
||||
}
|
||||
dbg_ep0 (1, "non standard request: %x",
|
||||
request->bmRequestType & USB_REQ_TYPE_MASK);
|
||||
return -1; /* Stall here */
|
||||
}
|
||||
|
||||
switch (device->device_state) {
|
||||
case STATE_CREATED:
|
||||
case STATE_ATTACHED:
|
||||
case STATE_POWERED:
|
||||
/* It actually is important to allow requests in these states,
|
||||
* Windows will request descriptors before assigning an
|
||||
* address to the client.
|
||||
*/
|
||||
|
||||
/*dbg_ep0 (1, "request %s not allowed in this state: %s", */
|
||||
/* USBD_DEVICE_REQUESTS(request->bRequest), */
|
||||
/* usbd_device_states[device->device_state]); */
|
||||
/*return -1; */
|
||||
break;
|
||||
|
||||
case STATE_INIT:
|
||||
case STATE_DEFAULT:
|
||||
switch (request->bRequest) {
|
||||
case USB_REQ_GET_STATUS:
|
||||
case USB_REQ_GET_INTERFACE:
|
||||
case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
case USB_REQ_SET_FEATURE:
|
||||
case USB_REQ_SET_DESCRIPTOR:
|
||||
/* case USB_REQ_SET_CONFIGURATION: */
|
||||
case USB_REQ_SET_INTERFACE:
|
||||
dbg_ep0 (1,
|
||||
"request %s not allowed in DEFAULT state: %s",
|
||||
USBD_DEVICE_REQUESTS (request->bRequest),
|
||||
usbd_device_states[device->device_state]);
|
||||
return -1;
|
||||
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
case USB_REQ_GET_CONFIGURATION:
|
||||
break;
|
||||
}
|
||||
case STATE_ADDRESSED:
|
||||
case STATE_CONFIGURED:
|
||||
break;
|
||||
case STATE_UNKNOWN:
|
||||
dbg_ep0 (1, "request %s not allowed in UNKNOWN state: %s",
|
||||
USBD_DEVICE_REQUESTS (request->bRequest),
|
||||
usbd_device_states[device->device_state]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* handle all requests that return data (direction bit set on bm RequestType) */
|
||||
if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) {
|
||||
|
||||
dbg_ep0 (3, "Device-to-Host");
|
||||
|
||||
switch (request->bRequest) {
|
||||
|
||||
case USB_REQ_GET_STATUS:
|
||||
return ep0_get_status (device, urb, request->wIndex,
|
||||
request->bmRequestType &
|
||||
USB_REQ_RECIPIENT_MASK);
|
||||
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
return ep0_get_descriptor (device, urb,
|
||||
le16_to_cpu (request->wLength),
|
||||
le16_to_cpu (request->wValue) >> 8,
|
||||
le16_to_cpu (request->wValue) & 0xff);
|
||||
|
||||
case USB_REQ_GET_CONFIGURATION:
|
||||
serial_printf("get config %d\n", device->configuration);
|
||||
return ep0_get_one (device, urb,
|
||||
device->configuration);
|
||||
|
||||
case USB_REQ_GET_INTERFACE:
|
||||
return ep0_get_one (device, urb, device->alternate);
|
||||
|
||||
case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
|
||||
return -1;
|
||||
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
case USB_REQ_SET_FEATURE:
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
case USB_REQ_SET_DESCRIPTOR:
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
case USB_REQ_SET_INTERFACE:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* handle the requests that do not return data */
|
||||
else {
|
||||
|
||||
|
||||
/*dbg_ep0(3, "Host-to-Device"); */
|
||||
switch (request->bRequest) {
|
||||
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
case USB_REQ_SET_FEATURE:
|
||||
dbg_ep0 (0, "Host-to-Device");
|
||||
switch (request->
|
||||
bmRequestType & USB_REQ_RECIPIENT_MASK) {
|
||||
case USB_REQ_RECIPIENT_DEVICE:
|
||||
/* XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here */
|
||||
/* XXX fall through for now as we do not support either */
|
||||
case USB_REQ_RECIPIENT_INTERFACE:
|
||||
case USB_REQ_RECIPIENT_OTHER:
|
||||
dbg_ep0 (0, "request %s not",
|
||||
USBD_DEVICE_REQUESTS (request->bRequest));
|
||||
default:
|
||||
return -1;
|
||||
|
||||
case USB_REQ_RECIPIENT_ENDPOINT:
|
||||
dbg_ep0 (0, "ENDPOINT: %x", le16_to_cpu (request->wValue));
|
||||
if (le16_to_cpu (request->wValue) == USB_ENDPOINT_HALT) {
|
||||
/*return usbd_device_feature (device, le16_to_cpu (request->wIndex), */
|
||||
/* request->bRequest == USB_REQ_SET_FEATURE); */
|
||||
/* NEED TO IMPLEMENT THIS!!! */
|
||||
return -1;
|
||||
} else {
|
||||
dbg_ep0 (1, "request %s bad wValue: %04x",
|
||||
USBD_DEVICE_REQUESTS
|
||||
(request->bRequest),
|
||||
le16_to_cpu (request->wValue));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
/* check if this is a re-address, reset first if it is (this shouldn't be possible) */
|
||||
if (device->device_state != STATE_DEFAULT) {
|
||||
dbg_ep0 (1, "set_address: %02x state: %s",
|
||||
le16_to_cpu (request->wValue),
|
||||
usbd_device_states[device->device_state]);
|
||||
return -1;
|
||||
}
|
||||
address = le16_to_cpu (request->wValue);
|
||||
if ((address & 0x7f) != address) {
|
||||
dbg_ep0 (1, "invalid address %04x %04x",
|
||||
address, address & 0x7f);
|
||||
return -1;
|
||||
}
|
||||
device->address = address;
|
||||
|
||||
/*dbg_ep0(2, "address: %d %d %d", */
|
||||
/* request->wValue, le16_to_cpu(request->wValue), device->address); */
|
||||
|
||||
return 0;
|
||||
|
||||
case USB_REQ_SET_DESCRIPTOR: /* XXX should we support this? */
|
||||
dbg_ep0 (0, "set descriptor: NOT SUPPORTED");
|
||||
return -1;
|
||||
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
/* c.f. 9.4.7 - the top half of wValue is reserved */
|
||||
device->configuration = le16_to_cpu(request->wValue) & 0xff;
|
||||
|
||||
/* reset interface and alternate settings */
|
||||
device->interface = device->alternate = 0;
|
||||
|
||||
/*dbg_ep0(2, "set configuration: %d", device->configuration); */
|
||||
/*serial_printf("DEVICE_CONFIGURED.. event?\n"); */
|
||||
return 0;
|
||||
|
||||
case USB_REQ_SET_INTERFACE:
|
||||
device->interface = le16_to_cpu (request->wIndex);
|
||||
device->alternate = le16_to_cpu (request->wValue);
|
||||
/*dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); */
|
||||
serial_printf("DEVICE_SET_INTERFACE.. event?\n");
|
||||
return 0;
|
||||
|
||||
case USB_REQ_GET_STATUS:
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
case USB_REQ_GET_CONFIGURATION:
|
||||
case USB_REQ_GET_INTERFACE:
|
||||
case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,318 @@
|
||||
// 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);
|
||||
}
|
||||
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;
|
||||
} 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,838 @@
|
||||
// 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 <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);
|
||||
|
||||
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);
|
||||
@@ -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_ */
|
||||
@@ -0,0 +1,464 @@
|
||||
// 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 <config.h>
|
||||
#include <common.h>
|
||||
#include <env.h>
|
||||
#include <errno.h>
|
||||
#include <fastboot.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 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,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor *
|
||||
fb_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
|
||||
struct usb_endpoint_descriptor *hs)
|
||||
{
|
||||
if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
|
||||
return hs;
|
||||
return fs;
|
||||
}
|
||||
|
||||
/*
|
||||
* static strings, in UTF-8
|
||||
*/
|
||||
static const char 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
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 = f_fb;
|
||||
}
|
||||
|
||||
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 = ep->maxpacket;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
fastboot_tx_write_str(response);
|
||||
|
||||
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:
|
||||
fastboot_func->in_req->complete = compl_do_reset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*cmdbuf = '\0';
|
||||
req->actual = 0;
|
||||
usb_ep_queue(ep, req, 0);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,930 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2017
|
||||
*
|
||||
* Eddie Cai <eddie.cai.linux@gmail.com>
|
||||
*/
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <env.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <memalign.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 0;
|
||||
|
||||
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 0;
|
||||
|
||||
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);
|
||||
rockusb_func = f_rkusb;
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,792 @@
|
||||
// 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 <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
|
||||
|
||||
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_DCD_DATA,
|
||||
SDP_STATE_RX_FILE_DATA,
|
||||
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;
|
||||
|
||||
bool configuration_done;
|
||||
};
|
||||
|
||||
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 = 1,
|
||||
.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_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,
|
||||
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 = be32_to_cpu(cmd->addr);
|
||||
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 = be32_to_cpu(cmd->addr);
|
||||
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->length - 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) {
|
||||
memcpy(sdp_ptr(sdp->dnl_address), req->buf + 1, datalen);
|
||||
sdp->dnl_address += datalen;
|
||||
}
|
||||
|
||||
if (sdp->dnl_bytes_remaining)
|
||||
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:
|
||||
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;
|
||||
break;
|
||||
case 2:
|
||||
value = len;
|
||||
req->complete = sdp_rx_data_complete;
|
||||
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;
|
||||
|
||||
/* allocate instance-specific endpoints */
|
||||
ep = usb_ep_autoconfig(gadget, &in_desc);
|
||||
if (!ep) {
|
||||
rv = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
|
||||
sdp->in_ep = ep; /* Store IN EP for enabling @ setup */
|
||||
|
||||
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)
|
||||
{
|
||||
struct usb_request *req;
|
||||
|
||||
req = alloc_ep_req(ep, 64);
|
||||
debug("%s: ep:%p req:%p\n", __func__, ep, req);
|
||||
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
memset(req->buf, 0, req->length);
|
||||
req->complete = sdp_tx_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;
|
||||
int result;
|
||||
|
||||
debug("%s: intf: %d alt: %d\n", __func__, intf, alt);
|
||||
|
||||
result = usb_ep_enable(sdp->in_ep, &in_desc);
|
||||
if (result)
|
||||
return result;
|
||||
sdp->in_req = sdp_start_ep(sdp->in_ep);
|
||||
sdp->in_req->context = sdp;
|
||||
|
||||
sdp->in_ep->driver_data = cdev; /* claim */
|
||||
|
||||
sdp->altsetting = alt;
|
||||
sdp->state = SDP_STATE_IDLE;
|
||||
|
||||
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);
|
||||
|
||||
if (sdp->in_req) {
|
||||
free(sdp->in_req);
|
||||
sdp->in_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_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
|
||||
#ifdef CONFIG_SPL_LOAD_FIT
|
||||
static ulong sdp_fit_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;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void 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
|
||||
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_fit_read;
|
||||
spl_load_simple_fit(spl_image, &load, 0,
|
||||
header);
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/* 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 */
|
||||
source(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;
|
||||
};
|
||||
}
|
||||
|
||||
#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
|
||||
{
|
||||
printf("SDP: handle requests...\n");
|
||||
while (1) {
|
||||
if (ctrlc()) {
|
||||
puts("\rCTRL+C - Operation aborted.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
if (spl_image->flags & SPL_FIT_FOUND)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
WATCHDOG_RESET();
|
||||
usb_gadget_handle_interrupts(controller_index);
|
||||
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
sdp_handle_in_ep(spl_image);
|
||||
#else
|
||||
sdp_handle_in_ep(NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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_ */
|
||||
@@ -0,0 +1,962 @@
|
||||
// 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 <net.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/io.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(®s->iep[ep - 1], IEP_RESET);
|
||||
mdelay(1);
|
||||
clrbits_le32(®s->iep[ep - 1], IEP_RESET);
|
||||
/* clear endpoint stall */
|
||||
clrbits_le32(®s->iep[ep - 1], IEP_STALL);
|
||||
} else {
|
||||
/* reset endpoint */
|
||||
setbits_le32(®s->oep[ep - 1], OEP_RESET);
|
||||
mdelay(1);
|
||||
clrbits_le32(®s->oep[ep - 1], OEP_RESET);
|
||||
/* clear endpoint stall */
|
||||
clrbits_le32(®s->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, ®s->dev_ctrl);
|
||||
|
||||
/* device address reset */
|
||||
chip->addr = 0;
|
||||
writel(0, ®s->dev_addr);
|
||||
|
||||
/* set idle counter to 7ms */
|
||||
writel(7, ®s->idle);
|
||||
|
||||
/* disable all interrupts */
|
||||
writel(IMR_MASK, ®s->imr);
|
||||
writel(GIMR_MASK, ®s->gimr);
|
||||
writel(GIMR0_MASK, ®s->gimr0);
|
||||
writel(GIMR1_MASK, ®s->gimr1);
|
||||
writel(GIMR2_MASK, ®s->gimr2);
|
||||
|
||||
/* clear interrupts */
|
||||
writel(ISR_MASK, ®s->isr);
|
||||
writel(0, ®s->gisr);
|
||||
writel(0, ®s->gisr0);
|
||||
writel(0, ®s->gisr1);
|
||||
writel(0, ®s->gisr2);
|
||||
|
||||
/* chip reset */
|
||||
setbits_le32(®s->dev_ctrl, DEVCTRL_RESET);
|
||||
mdelay(10);
|
||||
if (readl(®s->dev_ctrl) & DEVCTRL_RESET) {
|
||||
printf("fotg210: chip reset failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* CX FIFO reset */
|
||||
setbits_le32(®s->cxfifo, CXFIFO_CXFIFOCLR);
|
||||
mdelay(10);
|
||||
if (readl(®s->cxfifo) & CXFIFO_CXFIFOCLR) {
|
||||
printf("fotg210: ep0 fifo reset failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* create static ep-fifo map (EP1 <-> FIFO0, EP2 <-> FIFO1 ...) */
|
||||
writel(EPMAP14_DEFAULT, ®s->epmap14);
|
||||
writel(EPMAP58_DEFAULT, ®s->epmap58);
|
||||
writel(FIFOMAP_DEFAULT, ®s->fifomap);
|
||||
writel(0, ®s->fifocfg);
|
||||
for (i = 0; i < 8; ++i) {
|
||||
writel(CFG_EPX_MAX_PACKET_SIZE, ®s->iep[i]);
|
||||
writel(CFG_EPX_MAX_PACKET_SIZE, ®s->oep[i]);
|
||||
}
|
||||
|
||||
/* FIFO reset */
|
||||
for (i = 0; i < 4; ++i) {
|
||||
writel(FIFOCSR_RESET, ®s->fifocsr[i]);
|
||||
mdelay(10);
|
||||
if (readl(®s->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, ®s->imr);
|
||||
writel(ISR_MASK, ®s->isr);
|
||||
/* disable EP0 IN/OUT interrupt */
|
||||
writel(GIMR0_CXOUT | GIMR0_CXIN, ®s->gimr0);
|
||||
/* disable EPX IN+SPK+OUT interrupts */
|
||||
writel(GIMR1_MASK, ®s->gimr1);
|
||||
/* disable wakeup+idle+dma+zlp interrupts */
|
||||
writel(GIMR2_WAKEUP | GIMR2_IDLE | GIMR2_DMAERR | GIMR2_DMAFIN
|
||||
| GIMR2_ZLPRX | GIMR2_ZLPTX, ®s->gimr2);
|
||||
/* enable all group interrupt */
|
||||
writel(0, ®s->gimr);
|
||||
|
||||
/* suspend delay = 3 ms */
|
||||
writel(3, ®s->idle);
|
||||
|
||||
/* turn-on device interrupts */
|
||||
setbits_le32(®s->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(®s->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(®s->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), ®s->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, ®s->dma_fifo);
|
||||
} else {
|
||||
/* Wait until epx fifo empty */
|
||||
fotg210_cxwait(chip, CXFIFO_FIFOE(fifo));
|
||||
writel(DMAFIFO_FIFO(fifo), ®s->dma_fifo);
|
||||
}
|
||||
writel(DMACTRL_LEN(len) | DMACTRL_MEM2FIFO, ®s->dma_ctrl);
|
||||
} else {
|
||||
uint32_t blen;
|
||||
|
||||
if (ep->id == 0) {
|
||||
writel(DMAFIFO_CX, ®s->dma_fifo);
|
||||
do {
|
||||
blen = CXFIFO_BYTES(readl(®s->cxfifo));
|
||||
} while (blen < len);
|
||||
} else {
|
||||
writel(DMAFIFO_FIFO(fifo), ®s->dma_fifo);
|
||||
blen = FIFOCSR_BYTES(readl(®s->fifocsr[fifo]));
|
||||
}
|
||||
len = (len < blen) ? len : blen;
|
||||
writel(DMACTRL_LEN(len) | DMACTRL_FIFO2MEM, ®s->dma_ctrl);
|
||||
}
|
||||
|
||||
/* 4. DMA start */
|
||||
setbits_le32(®s->dma_ctrl, DMACTRL_START);
|
||||
|
||||
/* 5. DMA wait */
|
||||
ret = -EBUSY;
|
||||
for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
|
||||
tmp = readl(®s->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, ®s->dma_ctrl);
|
||||
|
||||
writel(0, ®s->gisr2);
|
||||
writel(0, ®s->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(®s->otgcsr) & OTGCSR_DEV_B) {
|
||||
/* Mini-B */
|
||||
if (readl(®s->dev_ctrl) & DEVCTRL_HS) {
|
||||
puts("fotg210: HS\n");
|
||||
chip->gadget.speed = USB_SPEED_HIGH;
|
||||
/* SOF mask timer = 1100 ticks */
|
||||
writel(SOFMTR_TMR(1100), ®s->sof_mtr);
|
||||
} else {
|
||||
puts("fotg210: FS\n");
|
||||
chip->gadget.speed = USB_SPEED_FULL;
|
||||
/* SOF mask timer = 10000 ticks */
|
||||
writel(SOFMTR_TMR(10000), ®s->sof_mtr);
|
||||
}
|
||||
} else {
|
||||
printf("fotg210: mini-A?\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* switch data port to ep0 */
|
||||
writel(DMAFIFO_CX, ®s->dma_fifo);
|
||||
/* fetch 8 bytes setup packet */
|
||||
tmp[0] = readl(®s->ep0_data);
|
||||
tmp[1] = readl(®s->ep0_data);
|
||||
/* release data port */
|
||||
writel(0, ®s->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, ®s->dev_addr);
|
||||
} else {
|
||||
chip->state = USB_STATE_CONFIGURED;
|
||||
writel(chip->addr | DEVADDR_CONF,
|
||||
®s->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, ®s->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(®s->iep[id - 1], IEP_STALL);
|
||||
setbits_le32(®s->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(®s->cxfifo, CXFIFO_CXFIN);
|
||||
break;
|
||||
|
||||
case CX_STALL:
|
||||
setbits_le32(®s->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(®s->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(®s->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(®s->fifocfg,
|
||||
FIFOCFG(id, FIFOCFG_EN | FIFOCFG_ISOC));
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
setbits_le32(®s->fifocfg,
|
||||
FIFOCFG(id, FIFOCFG_EN | FIFOCFG_BULK));
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
setbits_le32(®s->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(®s->fifocfg, FIFOCFG(id, FIFOCFG_CFG_MASK));
|
||||
clrbits_le32(®s->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(®s->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(®s->iep[ep->id - 1],
|
||||
IEP_STALL);
|
||||
} else {
|
||||
setbits_le32(®s->oep[ep->id - 1],
|
||||
OEP_STALL);
|
||||
}
|
||||
} else {
|
||||
if (ep->desc->bEndpointAddress & USB_DIR_IN) {
|
||||
clrbits_le32(®s->iep[ep->id - 1],
|
||||
IEP_STALL);
|
||||
} else {
|
||||
clrbits_le32(®s->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(®s->dev_ctrl, DEVCTRL_EN);
|
||||
/* clear unplug bit (BIT0) */
|
||||
clrbits_le32(®s->phy_tmsr, PHYTMSR_UNPLUG);
|
||||
}
|
||||
} else {
|
||||
chip->state = USB_STATE_NOTATTACHED;
|
||||
chip->pullup = 0;
|
||||
chip->addr = 0;
|
||||
writel(chip->addr, ®s->dev_addr);
|
||||
/* set unplug bit (BIT0) */
|
||||
setbits_le32(®s->phy_tmsr, PHYTMSR_UNPLUG);
|
||||
/* disable the chip */
|
||||
clrbits_le32(®s->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(®s->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(®s->isr) & (~readl(®s->imr));
|
||||
gisr = readl(®s->gisr) & (~readl(®s->gimr));
|
||||
if (!(isr & ISR_DEV) || !gisr)
|
||||
return 0;
|
||||
|
||||
writel(ISR_DEV, ®s->isr);
|
||||
|
||||
/* CX interrupts */
|
||||
if (gisr & GISR_GRP0) {
|
||||
st = readl(®s->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, ®s->gisr0);
|
||||
writel(0, ®s->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(®s->cxfifo, CXFIFO_CXFIN);
|
||||
}
|
||||
|
||||
/* FIFO interrupts */
|
||||
if (gisr & GISR_GRP1) {
|
||||
st = readl(®s->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(®s->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, ®s->gisr2);
|
||||
writel(0, ®s->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;
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
// 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 <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,
|
||||
};
|
||||
|
||||
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,
|
||||
|
||||
.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);
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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;
|
||||
return -ENOENT;
|
||||
}
|
||||
@@ -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 */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
@@ -0,0 +1,702 @@
|
||||
// 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 <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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
@@ -0,0 +1,615 @@
|
||||
// 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 <usb_mass_storage.h>
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
struct fsg_lun {
|
||||
loff_t file_length;
|
||||
loff_t num_sectors;
|
||||
|
||||
unsigned int initially_ro:1;
|
||||
unsigned int ro:1;
|
||||
unsigned int removable:1;
|
||||
unsigned int cdrom:1;
|
||||
unsigned int prevent_medium_removal:1;
|
||||
unsigned int registered:1;
|
||||
unsigned int info_valid:1;
|
||||
unsigned int nofua:1;
|
||||
|
||||
u32 sense_data;
|
||||
u32 sense_data_info;
|
||||
u32 unit_attention_data;
|
||||
|
||||
struct device dev;
|
||||
};
|
||||
|
||||
#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL)
|
||||
#if 0
|
||||
static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
|
||||
{
|
||||
return container_of(dev, struct fsg_lun, dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Big enough to hold our biggest descriptor */
|
||||
#define EP0_BUFSIZE 256
|
||||
#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
|
||||
|
||||
/* Number of buffers we will use. 2 is enough for double-buffering */
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@@ -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
|
||||
@@ -0,0 +1,377 @@
|
||||
// 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 <linux/compat.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/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((void *)(uintptr_t)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");
|
||||
@@ -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 -ENOTSUPP;
|
||||
#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,
|
||||
};
|
||||
@@ -0,0 +1,138 @@
|
||||
// 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 <asm/unaligned.h>
|
||||
|
||||
|
||||
static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
|
||||
{
|
||||
int count = 0;
|
||||
u8 c;
|
||||
u16 uchar;
|
||||
|
||||
/*
|
||||
* this insists on correct encodings, though not minimal ones.
|
||||
* BUT it currently rejects legit 4-byte UTF-8 code points,
|
||||
* which need surrogate pairs. (Unicode 3.1 can use them.)
|
||||
*/
|
||||
while (len != 0 && (c = (u8) *s++) != 0) {
|
||||
if ((c & 0x80)) {
|
||||
/*
|
||||
* 2-byte sequence:
|
||||
* 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
|
||||
*/
|
||||
if ((c & 0xe0) == 0xc0) {
|
||||
uchar = (c & 0x1f) << 6;
|
||||
|
||||
c = (u8) *s++;
|
||||
if ((c & 0xc0) != 0x80)
|
||||
goto fail;
|
||||
c &= 0x3f;
|
||||
uchar |= c;
|
||||
|
||||
/*
|
||||
* 3-byte sequence (most CJKV characters):
|
||||
* zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
|
||||
*/
|
||||
} else if ((c & 0xf0) == 0xe0) {
|
||||
uchar = (c & 0x0f) << 12;
|
||||
|
||||
c = (u8) *s++;
|
||||
if ((c & 0xc0) != 0x80)
|
||||
goto fail;
|
||||
c &= 0x3f;
|
||||
uchar |= c << 6;
|
||||
|
||||
c = (u8) *s++;
|
||||
if ((c & 0xc0) != 0x80)
|
||||
goto fail;
|
||||
c &= 0x3f;
|
||||
uchar |= c;
|
||||
|
||||
/* no bogus surrogates */
|
||||
if (0xd800 <= uchar && uchar <= 0xdfff)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* 4-byte sequence (surrogate pairs, currently rare):
|
||||
* 11101110wwwwzzzzyy + 110111yyyyxxxxxx
|
||||
* = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
|
||||
* (uuuuu = wwww + 1)
|
||||
* FIXME accept the surrogate code points (only)
|
||||
*/
|
||||
} else
|
||||
goto fail;
|
||||
} else
|
||||
uchar = c;
|
||||
put_unaligned_le16(uchar, cp++);
|
||||
count++;
|
||||
len--;
|
||||
}
|
||||
return count;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usb_gadget_get_string - fill out a string descriptor
|
||||
* @table: of c strings encoded using UTF-8
|
||||
* @id: string id, from low byte of wValue in get string descriptor
|
||||
* @buf: at least 256 bytes
|
||||
*
|
||||
* Finds the UTF-8 string matching the ID, and converts it into a
|
||||
* string descriptor in utf16-le.
|
||||
* Returns length of descriptor (always even) or negative errno
|
||||
*
|
||||
* If your driver needs stings in multiple languages, you'll probably
|
||||
* "switch (wIndex) { ... }" in your ep0 string descriptor logic,
|
||||
* using this routine after choosing which set of UTF-8 strings to use.
|
||||
* Note that US-ASCII is a strict subset of UTF-8; any string bytes with
|
||||
* the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
|
||||
* characters (which are also widely used in C strings).
|
||||
*/
|
||||
int
|
||||
usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf)
|
||||
{
|
||||
struct usb_string *s;
|
||||
int len;
|
||||
|
||||
if (!table)
|
||||
return -EINVAL;
|
||||
|
||||
/* descriptor 0 has the language id */
|
||||
if (id == 0) {
|
||||
buf[0] = 4;
|
||||
buf[1] = USB_DT_STRING;
|
||||
buf[2] = (u8) table->language;
|
||||
buf[3] = (u8) (table->language >> 8);
|
||||
return 4;
|
||||
}
|
||||
for (s = table->strings; s && s->s; s++)
|
||||
if (s->id == id)
|
||||
break;
|
||||
|
||||
/* unrecognized: stall. */
|
||||
if (!s || !s->s)
|
||||
return -EINVAL;
|
||||
|
||||
/* string descriptors have length, tag, then UTF16-LE text */
|
||||
len = min((size_t) 126, strlen(s->s));
|
||||
memset(buf + 2, 0, 2 * len); /* zero all the bytes */
|
||||
len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len);
|
||||
if (len < 0)
|
||||
return -EINVAL;
|
||||
buf[0] = (len + 1) * 2;
|
||||
buf[1] = USB_DT_STRING;
|
||||
return buf[0];
|
||||
}
|
||||
Reference in New Issue
Block a user