summaryrefslogtreecommitdiff
path: root/chip/stm32/usb_dwc.c
diff options
context:
space:
mode:
authorJack Rosenthal <jrosenth@chromium.org>2021-11-04 12:11:58 -0600
committerCommit Bot <commit-bot@chromium.org>2021-11-05 04:22:34 +0000
commit252457d4b21f46889eebad61d4c0a65331919cec (patch)
tree01856c4d31d710b20e85a74c8d7b5836e35c3b98 /chip/stm32/usb_dwc.c
parent08f5a1e6fc2c9467230444ac9b582dcf4d9f0068 (diff)
downloadchrome-ec-stabilize-14469.9.B-ish.tar.gz
In the interest of making long-term branch maintenance incur as little technical debt on us as possible, we should not maintain any files on the branch we are not actually using. This has the added effect of making it extremely clear when merging CLs from the main branch when changes have the possibility to affect us. The follow-on CL adds a convenience script to actually pull updates from the main branch and generate a CL for the update. BUG=b:204206272 BRANCH=ish TEST=make BOARD=arcada_ish && make BOARD=drallion_ish Signed-off-by: Jack Rosenthal <jrosenth@chromium.org> Change-Id: I17e4694c38219b5a0823e0a3e55a28d1348f4b18 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3262038 Reviewed-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Tom Hughes <tomhughes@chromium.org>
Diffstat (limited to 'chip/stm32/usb_dwc.c')
-rw-r--r--chip/stm32/usb_dwc.c1423
1 files changed, 0 insertions, 1423 deletions
diff --git a/chip/stm32/usb_dwc.c b/chip/stm32/usb_dwc.c
deleted file mode 100644
index f4ee89f1f0..0000000000
--- a/chip/stm32/usb_dwc.c
+++ /dev/null
@@ -1,1423 +0,0 @@
-/* Copyright 2016 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "clock.h"
-#include "common.h"
-#include "config.h"
-#include "console.h"
-#include "flash.h"
-#include "gpio.h"
-#include "hooks.h"
-#include "link_defs.h"
-#include "registers.h"
-#include "usb_hw.h"
-#include "system.h"
-#include "task.h"
-#include "timer.h"
-#include "util.h"
-#include "usb_descriptor.h"
-#include "watchdog.h"
-
-
-/****************************************************************************/
-/* Debug output */
-
-/* Console output macro */
-#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
-#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
-
-/* TODO: Something unexpected happened. Figure out how to report & fix it. */
-#define report_error(val) \
- CPRINTS("Unhandled USB event at %s line %d: 0x%x", \
- __FILE__, __LINE__, val)
-
-
-/****************************************************************************/
-/* Standard USB stuff */
-
-#ifdef CONFIG_USB_BOS
-/* v2.10 (vs 2.00) BOS Descriptor provided */
-#define USB_DEV_BCDUSB 0x0210
-#else
-#define USB_DEV_BCDUSB 0x0200
-#endif
-
-#ifndef USB_DEV_CLASS
-#define USB_DEV_CLASS USB_CLASS_PER_INTERFACE
-#endif
-
-#ifndef CONFIG_USB_BCD_DEV
-#define CONFIG_USB_BCD_DEV 0x0100 /* 1.00 */
-#endif
-
-#ifndef CONFIG_USB_SERIALNO
-#define USB_STR_SERIALNO 0
-#else
-static int usb_load_serial(void);
-#endif
-
-
-/* USB Standard Device Descriptor */
-static const struct usb_device_descriptor dev_desc = {
- .bLength = USB_DT_DEVICE_SIZE,
- .bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = USB_DEV_BCDUSB,
- .bDeviceClass = USB_DEV_CLASS,
- .bDeviceSubClass = 0x00,
- .bDeviceProtocol = 0x00,
- .bMaxPacketSize0 = USB_MAX_PACKET_SIZE,
- .idVendor = USB_VID_GOOGLE,
- .idProduct = CONFIG_USB_PID,
- .bcdDevice = CONFIG_USB_BCD_DEV,
- .iManufacturer = USB_STR_VENDOR,
- .iProduct = USB_STR_PRODUCT,
- .iSerialNumber = USB_STR_SERIALNO,
- .bNumConfigurations = 1
-};
-
-/* USB Configuration Descriptor */
-const struct usb_config_descriptor USB_CONF_DESC(conf) = {
- .bLength = USB_DT_CONFIG_SIZE,
- .bDescriptorType = USB_DT_CONFIGURATION,
- .wTotalLength = 0x0BAD, /* number of returned bytes, set at runtime */
- .bNumInterfaces = USB_IFACE_COUNT,
- .bConfigurationValue = 1, /* Caution: hard-coded value */
- .iConfiguration = USB_STR_VERSION,
- .bmAttributes = 0x80 /* Reserved bit */
-#ifdef CONFIG_USB_SELF_POWERED /* bus or self powered */
- | 0x40
-#endif
-#ifdef CONFIG_USB_REMOTE_WAKEUP
- | 0x20
-#endif
- ,
- .bMaxPower = (CONFIG_USB_MAXPOWER_MA / 2),
-};
-
-const uint8_t usb_string_desc[] = {
- 4, /* Descriptor size */
- USB_DT_STRING,
- 0x09, 0x04 /* LangID = 0x0409: U.S. English */
-};
-
-/****************************************************************************/
-/* Packet-handling stuff, specific to this SoC */
-
-/* Some internal state to keep track of what's going on */
-static enum {
- WAITING_FOR_SETUP_PACKET,
- DATA_STAGE_IN,
- NO_DATA_STAGE,
-} what_am_i_doing;
-
-#ifdef DEBUG_ME
-static const char * const wat[3] = {
- [WAITING_FOR_SETUP_PACKET] = "wait_for_setup",
- [DATA_STAGE_IN] = "data_in",
- [NO_DATA_STAGE] = "no_data",
-};
-#endif
-
-/* Programmer's Guide, Table 10-7 */
-enum table_case {
- BAD_0,
- TABLE_CASE_COMPLETE,
- TABLE_CASE_SETUP,
- TABLE_CASE_WTF,
- TABLE_CASE_D,
- TABLE_CASE_E,
- BAD_6,
- BAD_7,
-};
-
-static enum table_case decode_table_10_7(uint32_t doepint)
-{
- enum table_case val = BAD_0;
-
- /* Bits: SI, SPD, IOC */
- if (doepint & DOEPINT_XFERCOMPL)
- val += 1;
- if (doepint & DOEPINT_SETUP)
- val += 2;
- return val;
-}
-
-/* For STATUS/OUT: Use two DMA descriptors, each with one-packet buffers */
-#define NUM_OUT_BUFFERS 2
-static uint8_t __aligned(4) ep0_setup_buf[USB_MAX_PACKET_SIZE];
-
-/* For IN: Several DMA descriptors, all pointing into one large buffer, so that
- * we can return the configuration descriptor as one big blob.
- */
-#define NUM_IN_PACKETS_AT_ONCE 4
-#define IN_BUF_SIZE (NUM_IN_PACKETS_AT_ONCE * USB_MAX_PACKET_SIZE)
-static uint8_t __aligned(4) ep0_in_buf[IN_BUF_SIZE];
-
-struct dwc_usb_ep ep0_ctl = {
- .max_packet = USB_MAX_PACKET_SIZE,
- .tx_fifo = 0,
- .out_pending = 0,
- .out_expected = 0,
- .out_data = 0,
- .out_databuffer = ep0_setup_buf,
- .out_databuffer_max = sizeof(ep0_setup_buf),
- .rx_deferred = 0,
- .in_packets = 0,
- .in_pending = 0,
- .in_data = 0,
- .in_databuffer = ep0_in_buf,
- .in_databuffer_max = sizeof(ep0_in_buf),
- .tx_deferred = 0,
-};
-
-/* Overall device state (USB 2.0 spec, section 9.1.1).
- * We only need a few, though.
- */
-static enum {
- DS_DEFAULT,
- DS_ADDRESS,
- DS_CONFIGURED,
-} device_state;
-static uint8_t configuration_value;
-
-
-/* True if the HW Rx/OUT FIFO is currently listening. */
-int rx_ep_is_active(uint32_t ep_num)
-{
- return (GR_USB_DOEPCTL(ep_num) & DXEPCTL_EPENA) ? 1 : 0;
-}
-
-/* Number of bytes the HW Rx/OUT FIFO has for us.
- *
- * @param ep_num USB endpoint
- *
- * @returns number of bytes ready, zero if none.
- */
-int rx_ep_pending(uint32_t ep_num)
-{
- struct dwc_usb_ep *ep = usb_ctl.ep[ep_num];
-
- return ep->out_pending;
-}
-
-/* True if the Tx/IN FIFO can take some bytes from us. */
-int tx_ep_is_ready(uint32_t ep_num)
-{
- struct dwc_usb_ep *ep = usb_ctl.ep[ep_num];
- int ready;
-
- /* Is the tx hw idle? */
- ready = !(GR_USB_DIEPCTL(ep_num) & DXEPCTL_EPENA);
-
- /* Is there no pending data? */
- ready &= (ep->in_pending == 0);
- return ready;
-}
-
-/* Write packets of data IN to the host.
- *
- * This function uses DMA, so the *data write buffer
- * must persist until the write completion event.
- *
- * @param ep_num USB endpoint to write
- * @param len number of bytes to write
- * @param data pointer of data to write
- *
- * @return bytes written
- */
-int usb_write_ep(uint32_t ep_num, int len, void *data)
-{
- struct dwc_usb_ep *ep = usb_ctl.ep[ep_num];
-
- if (GR_USB_DIEPCTL(ep_num) & DXEPCTL_EPENA) {
- CPRINTS("usb_write_ep ep%d: FAIL: tx already in progress!",
- ep_num);
- return 0;
- }
-
- /* We will send as many packets as necessary, including a final
- * packet of < USB_MAX_PACKET_SIZE (maybe zero length)
- */
- ep->in_packets = (len + USB_MAX_PACKET_SIZE - 1) / USB_MAX_PACKET_SIZE;
- ep->in_pending = len;
- ep->in_data = data;
-
- GR_USB_DIEPTSIZ(ep_num) = 0;
-
- GR_USB_DIEPTSIZ(ep_num) |= DXEPTSIZ_PKTCNT(ep->in_packets);
- GR_USB_DIEPTSIZ(ep_num) |= DXEPTSIZ_XFERSIZE(len);
- GR_USB_DIEPDMA(ep_num) = (uint32_t)(ep->in_data);
-
- /* We could support longer multi-dma transfers here. */
- ep->in_pending -= len;
- ep->in_packets -= ep->in_packets;
- ep->in_data += len;
-
- /* We are ready to enable this endpoint to start transferring data. */
- GR_USB_DIEPCTL(ep_num) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
- return len;
-}
-
-/* Tx/IN interrupt handler */
-void usb_epN_tx(uint32_t ep_num)
-{
- struct dwc_usb_ep *ep = usb_ctl.ep[ep_num];
- uint32_t dieptsiz = GR_USB_DIEPTSIZ(ep_num);
-
- if (GR_USB_DIEPCTL(ep_num) & DXEPCTL_EPENA) {
- CPRINTS("usb_epN_tx ep%d: tx still active.", ep_num);
- return;
- }
-
- /* clear the Tx/IN interrupts */
- GR_USB_DIEPINT(ep_num) = 0xffffffff;
-
- /*
- * Let's assume this is actually true.
- * We could support multi-dma transfers here.
- */
- ep->in_packets = 0;
- ep->in_pending = dieptsiz & GC_USB_DIEPTSIZ1_XFERSIZE_MASK;
-
- if (ep->tx_deferred)
- hook_call_deferred(ep->tx_deferred, 0);
-}
-
-/* Read a packet of data OUT from the host.
- *
- * This function uses DMA, so the *data write buffer
- * must persist until the read completion event.
- *
- * @param ep_num USB endpoint to read
- * @param len number of bytes to read
- * @param data pointer of data to read
- *
- * @return EC_SUCCESS on success
- */
-int usb_read_ep(uint32_t ep_num, int len, void *data)
-{
- struct dwc_usb_ep *ep = usb_ctl.ep[ep_num];
- int packets = (len + USB_MAX_PACKET_SIZE - 1) / USB_MAX_PACKET_SIZE;
-
- ep->out_data = data;
- ep->out_pending = 0;
- ep->out_expected = len;
-
- GR_USB_DOEPTSIZ(ep_num) = 0;
- GR_USB_DOEPTSIZ(ep_num) |= DXEPTSIZ_PKTCNT(packets);
- GR_USB_DOEPTSIZ(ep_num) |= DXEPTSIZ_XFERSIZE(len);
- GR_USB_DOEPDMA(ep_num) = (uint32_t)ep->out_data;
-
- GR_USB_DOEPCTL(ep_num) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
- return EC_SUCCESS;
-}
-
-/* Rx/OUT endpoint interrupt handler */
-void usb_epN_rx(uint32_t ep_num)
-{
- struct dwc_usb_ep *ep = usb_ctl.ep[ep_num];
-
- /* Still receiving data. Let's wait. */
- if (rx_ep_is_active(ep_num))
- return;
-
- /* Bytes received decrement DOEPTSIZ XFERSIZE */
- if (GR_USB_DOEPINT(ep_num) & DOEPINT_XFERCOMPL) {
- if (ep->out_expected > 0) {
- ep->out_pending =
- ep->out_expected -
- (GR_USB_DOEPTSIZ(ep_num) &
- GC_USB_DOEPTSIZ1_XFERSIZE_MASK);
- } else {
- CPRINTF("usb_ep%d_rx: unexpected RX DOEPTSIZ %08x\n",
- ep_num, GR_USB_DOEPTSIZ(ep_num));
- ep->out_pending = 0;
- }
- ep->out_expected = 0;
- GR_USB_DOEPTSIZ(ep_num) = 0;
- }
-
- /* clear the RX/OUT interrupts */
- GR_USB_DOEPINT(ep_num) = 0xffffffff;
-
- if (ep->rx_deferred)
- hook_call_deferred(ep->rx_deferred, 0);
-}
-
-/* Reset endpoint HW block. */
-void epN_reset(uint32_t ep_num)
-{
- GR_USB_DOEPCTL(ep_num) = DXEPCTL_MPS(USB_MAX_PACKET_SIZE) |
- DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_BULK;
- GR_USB_DIEPCTL(ep_num) = DXEPCTL_MPS(USB_MAX_PACKET_SIZE) |
- DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_BULK |
- DXEPCTL_TXFNUM(ep_num);
- GR_USB_DAINTMSK |= DAINT_INEP(ep_num) |
- DAINT_OUTEP(ep_num);
-}
-
-
-/******************************************************************************
- * Internal and EP0 functions.
- */
-
-
-static void flush_all_fifos(void)
-{
- /* Flush all FIFOs according to Section 2.1.1.2 */
- GR_USB_GRSTCTL = GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH
- | GRSTCTL_RXFFLSH;
- while (GR_USB_GRSTCTL & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH))
- ;
-}
-
-int send_in_packet(uint32_t ep_num)
-{
- struct dwc_usb *usb = &usb_ctl;
- struct dwc_usb_ep *ep = usb->ep[ep_num];
- int len = MIN(USB_MAX_PACKET_SIZE, ep->in_pending);
-
- if (ep->in_packets == 0) {
- report_error(ep_num);
- return -1;
- }
-
- GR_USB_DIEPTSIZ(ep_num) = 0;
-
- GR_USB_DIEPTSIZ(ep_num) |= DXEPTSIZ_PKTCNT(1);
- GR_USB_DIEPTSIZ(0) |= DXEPTSIZ_XFERSIZE(len);
- GR_USB_DIEPDMA(0) = (uint32_t)ep->in_data;
-
-
- /* We're sending this much. */
- ep->in_pending -= len;
- ep->in_packets -= 1;
- ep->in_data += len;
-
- /* We are ready to enable this endpoint to start transferring data. */
- return len;
-}
-
-
-/* Load the EP0 IN FIFO buffer with some data (zero-length works too). Returns
- * len, or negative on error.
- */
-int initialize_in_transfer(const void *source, uint32_t len)
-{
- struct dwc_usb *usb = &usb_ctl;
- struct dwc_usb_ep *ep = usb->ep[0];
-
-#ifdef CONFIG_USB_DWC_FS
- /* FS OTG port does not support DMA or external phy */
- ASSERT(!(usb->dma_en));
- ASSERT(usb->phy_type == USB_PHY_INTERNAL);
- ASSERT(usb->speed == USB_SPEED_FS);
- ASSERT(usb->irq == STM32_IRQ_OTG_FS);
-#else
- /* HS OTG port requires an external phy to support HS */
- ASSERT(!((usb->phy_type == USB_PHY_INTERNAL) &&
- (usb->speed == USB_SPEED_HS)));
- ASSERT(usb->irq == STM32_IRQ_OTG_HS);
-#endif
-
- /* Copy the data into our FIFO buffer */
- if (len >= IN_BUF_SIZE) {
- report_error(len);
- return -1;
- }
-
- /* Stage data in DMA buffer. */
- memcpy(ep->in_databuffer, source, len);
- ep->in_data = ep->in_databuffer;
-
- /* We will send as many packets as necessary, including a final
- * packet of < USB_MAX_PACKET_SIZE (maybe zero length)
- */
- ep->in_packets = (len + USB_MAX_PACKET_SIZE)/USB_MAX_PACKET_SIZE;
- ep->in_pending = len;
-
- send_in_packet(0);
- return len;
-}
-
-/* Prepare the EP0 OUT FIFO buffer to accept some data. Returns len, or
- * negative on error.
- */
-int accept_out_fifo(uint32_t len)
-{
- /* TODO: This is not yet implemented */
- report_error(len);
- return -1;
-}
-
-/* The next packet from the host should be a Setup packet. Get ready for it. */
-static void expect_setup_packet(void)
-{
- struct dwc_usb *usb = &usb_ctl;
- struct dwc_usb_ep *ep = usb->ep[0];
-
- what_am_i_doing = WAITING_FOR_SETUP_PACKET;
- ep->out_data = ep->out_databuffer;
-
- /* We don't care about IN packets right now, only OUT. */
- GR_USB_DAINTMSK |= DAINT_OUTEP(0);
- GR_USB_DAINTMSK &= ~DAINT_INEP(0);
-
- GR_USB_DOEPTSIZ(0) = 0;
- GR_USB_DOEPTSIZ(0) |= DXEPTSIZ_PKTCNT(1);
- GR_USB_DOEPTSIZ(0) |= DXEPTSIZ_XFERSIZE(0x18);
- GR_USB_DOEPTSIZ(0) |= DXEPTSIZ_SUPCNT(1);
- GR_USB_DOEPCTL(0) = DXEPCTL_USBACTEP | DXEPCTL_EPENA;
- GR_USB_DOEPDMA(0) = (uint32_t)ep->out_data;
-}
-
-/* We're complaining about something by stalling both IN and OUT packets,
- * but a SETUP packet will get through anyway, so prepare for it.
- */
-static void stall_both_fifos(void)
-{
- what_am_i_doing = WAITING_FOR_SETUP_PACKET;
- /* We don't care about IN packets right now, only OUT. */
- GR_USB_DAINTMSK |= DAINT_OUTEP(0);
- GR_USB_DAINTMSK &= ~DAINT_INEP(0);
-
- GR_USB_DOEPCTL(0) |= DXEPCTL_STALL;
- GR_USB_DIEPCTL(0) |= DXEPCTL_STALL;
- expect_setup_packet();
-}
-
-/* The TX FIFO buffer is loaded. Start the Data phase. */
-static void expect_data_phase_in(enum table_case tc)
-{
- what_am_i_doing = DATA_STAGE_IN;
-
- /* Send the reply (data phase in) */
- if (tc == TABLE_CASE_SETUP)
- GR_USB_DIEPCTL(0) |= DXEPCTL_USBACTEP |
- DXEPCTL_CNAK | DXEPCTL_EPENA;
- else
- GR_USB_DIEPCTL(0) |= DXEPCTL_EPENA;
-
- /* We'll receive an empty packet back as a ack, I guess. */
- if (tc == TABLE_CASE_SETUP)
- GR_USB_DOEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
- else
- GR_USB_DOEPCTL(0) |= DXEPCTL_EPENA;
-
- /* Get an interrupt when either IN or OUT arrives */
- GR_USB_DAINTMSK |= (DAINT_OUTEP(0) | DAINT_INEP(0));
-
-}
-
-static void expect_data_phase_out(enum table_case tc)
-{
- /* TODO: This is not yet supported */
- report_error(tc);
- expect_setup_packet();
-}
-
-/* No Data phase, just Status phase (which is IN, since Setup is OUT) */
-static void expect_status_phase_in(enum table_case tc)
-{
- what_am_i_doing = NO_DATA_STAGE;
-
- /* Expect a zero-length IN for the Status phase */
- (void) initialize_in_transfer(0, 0);
-
- /* Blindly following instructions here, too. */
- if (tc == TABLE_CASE_SETUP)
- GR_USB_DIEPCTL(0) |= DXEPCTL_USBACTEP
- | DXEPCTL_CNAK | DXEPCTL_EPENA;
- else
- GR_USB_DIEPCTL(0) |= DXEPCTL_EPENA;
-
- /* Get an interrupt when either IN or OUT arrives */
- GR_USB_DAINTMSK |= (DAINT_OUTEP(0) | DAINT_INEP(0));
-}
-
-/* Handle a Setup packet that expects us to send back data in reply. Return the
- * length of the data we're returning, or negative to indicate an error.
- */
-static int handle_setup_with_in_stage(enum table_case tc,
- struct usb_setup_packet *req)
-{
- struct dwc_usb *usb = &usb_ctl;
- struct dwc_usb_ep *ep = usb->ep[0];
-
- const void *data = 0;
- uint32_t len = 0;
- int ugly_hack = 0;
- static const uint16_t zero; /* == 0 */
-
- switch (req->bRequest) {
- case USB_REQ_GET_DESCRIPTOR: {
- uint8_t type = req->wValue >> 8;
- uint8_t idx = req->wValue & 0xff;
-
- switch (type) {
- case USB_DT_DEVICE:
- data = &dev_desc;
- len = sizeof(dev_desc);
- break;
- case USB_DT_CONFIGURATION:
- data = __usb_desc;
- len = USB_DESC_SIZE;
- ugly_hack = 1; /* see below */
- break;
-#ifdef CONFIG_USB_BOS
- case USB_DT_BOS:
- data = bos_ctx.descp;
- len = bos_ctx.size;
- break;
-#endif
- case USB_DT_STRING:
- if (idx >= USB_STR_COUNT)
- return -1;
-#ifdef CONFIG_USB_SERIALNO
- if (idx == USB_STR_SERIALNO)
- data = (uint8_t *)usb_serialno_desc;
- else
-#endif
- data = usb_strings[idx];
- len = *(uint8_t *)data;
- break;
- case USB_DT_DEVICE_QUALIFIER:
- /* We're not high speed */
- return -1;
- case USB_DT_DEBUG:
- /* Not supported */
- return -1;
- default:
- report_error(type);
- return -1;
- }
- break;
- }
- case USB_REQ_GET_STATUS: {
- /* TODO: Device Status: Remote Wakeup? Self Powered? */
- data = &zero;
- len = sizeof(zero);
- break;
- }
- case USB_REQ_GET_CONFIGURATION:
- data = &configuration_value;
- len = sizeof(configuration_value);
- break;
-
- case USB_REQ_SYNCH_FRAME:
- /* Unimplemented */
- return -1;
-
- default:
- report_error(req->bRequest);
- return -1;
- }
-
- /* Don't send back more than we were asked for. */
- len = MIN(req->wLength, len);
-
- /* Prepare the TX FIFO. If we haven't preallocated enough room in the
- * TX FIFO for the largest reply, we'll have to stall. This is a bug in
- * our code, but detecting it easily at compile time is related to the
- * ugly_hack directly below.
- */
- if (initialize_in_transfer(data, len) < 0)
- return -1;
-
- if (ugly_hack) {
- /*
- * TODO: Somebody figure out how to fix this, please.
- *
- * The USB configuration descriptor request is unique in that
- * it not only returns the configuration descriptor, but also
- * all the interface descriptors and all their endpoint
- * descriptors as one enormous blob. We've set up some macros
- * so we can declare and implement separate interfaces in
- * separate files just by compiling them, and all the relevant
- * descriptors are sorted and bundled up by the linker. But the
- * total length of the entire blob needs to appear in the first
- * configuration descriptor struct and because we don't know
- * that value until after linking, it can't be initialized as a
- * constant. So we have to compute it at run-time and shove it
- * in here, which also means that we have to copy the whole
- * blob into our TX FIFO buffer so that it's mutable. Otherwise
- * we could just point at it (or pretty much any other constant
- * struct that we wanted to send to the host). Bah.
- */
- struct usb_config_descriptor *cfg =
- (struct usb_config_descriptor *)ep->in_databuffer;
- /* set the real descriptor size */
- cfg->wTotalLength = USB_DESC_SIZE;
- }
-
- return len;
-}
-
-/* Handle a Setup that comes with additional data for us. */
-static int handle_setup_with_out_stage(enum table_case tc,
- struct usb_setup_packet *req)
-{
- /* TODO: We don't support any of these. We should. */
- report_error(-1);
- return -1;
-}
-
-/* Some Setup packets don't have a data stage at all. */
-static int handle_setup_with_no_data_stage(enum table_case tc,
- struct usb_setup_packet *req)
-{
- uint8_t set_addr;
-
- switch (req->bRequest) {
- case USB_REQ_SET_ADDRESS:
- /*
- * Set the address after the IN packet handshake.
- *
- * From the USB 2.0 spec, section 9.4.6:
- *
- * As noted elsewhere, requests actually may result in
- * up to three stages. In the first stage, the Setup
- * packet is sent to the device. In the optional second
- * stage, data is transferred between the host and the
- * device. In the final stage, status is transferred
- * between the host and the device. The direction of
- * data and status transfer depends on whether the host
- * is sending data to the device or the device is
- * sending data to the host. The Status stage transfer
- * is always in the opposite direction of the Data
- * stage. If there is no Data stage, the Status stage
- * is from the device to the host.
- *
- * Stages after the initial Setup packet assume the
- * same device address as the Setup packet. The USB
- * device does not change its device address until
- * after the Status stage of this request is completed
- * successfully. Note that this is a difference between
- * this request and all other requests. For all other
- * requests, the operation indicated must be completed
- * before the Status stage
- */
- set_addr = req->wValue & 0xff;
- /*
- * NOTE: Now that we've said that, we don't do it. The
- * hardware for this SoC knows that an IN packet will
- * be following the SET ADDRESS, so it waits until it
- * sees that happen before the address change takes
- * effect. If we wait until after the IN packet to
- * change the register, the hardware gets confused and
- * doesn't respond to anything.
- */
- GWRITE_FIELD(USB, DCFG, DEVADDR, set_addr);
- CPRINTS("SETAD 0x%02x (%d)", set_addr, set_addr);
- device_state = DS_ADDRESS;
- break;
-
- case USB_REQ_SET_CONFIGURATION:
- switch (req->wValue) {
- case 0:
- configuration_value = req->wValue;
- device_state = DS_ADDRESS;
- break;
- case 1: /* Caution: Only one config descriptor TODAY */
- /* TODO: All endpoints set to DATA0 toggle state */
- configuration_value = req->wValue;
- device_state = DS_CONFIGURED;
- break;
- default:
- /* Nope. That's a paddlin. */
- report_error(-1);
- return -1;
- }
- break;
-
- case USB_REQ_CLEAR_FEATURE:
- case USB_REQ_SET_FEATURE:
- /* TODO: Handle DEVICE_REMOTE_WAKEUP, ENDPOINT_HALT? */
- break;
-
- default:
- /* Anything else is unsupported */
- report_error(-1);
- return -1;
- }
-
- /* No data to transfer, go straight to the Status phase. */
- return 0;
-}
-
-/* Dispatch an incoming Setup packet according to its type */
-static void handle_setup(enum table_case tc)
-{
- struct dwc_usb *usb = &usb_ctl;
- struct dwc_usb_ep *ep = usb->ep[0];
- struct usb_setup_packet *req =
- (struct usb_setup_packet *)ep->out_databuffer;
- int data_phase_in = req->bmRequestType & USB_DIR_IN;
- int data_phase_out = !data_phase_in && req->wLength;
- int bytes = -1; /* default is to stall */
-
- if (0 == (req->bmRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))) {
- /* Standard Device requests */
- if (data_phase_in)
- bytes = handle_setup_with_in_stage(tc, req);
- else if (data_phase_out)
- bytes = handle_setup_with_out_stage(tc, req);
- else
- bytes = handle_setup_with_no_data_stage(tc, req);
- } else if (USB_RECIP_INTERFACE ==
- (req->bmRequestType & USB_RECIP_MASK)) {
- /* Interface-specific requests */
- uint8_t iface = req->wIndex & 0xff;
-
- if (iface < USB_IFACE_COUNT)
- bytes = usb_iface_request[iface](req);
- } else {
- /* Something we need to add support for? */
- report_error(-1);
- }
-
- /* We say "no" to unsupported and intentionally unhandled requests by
- * stalling the Data and/or Status stage.
- */
- if (bytes < 0) {
- /* Stall both IN and OUT. SETUP will come through anyway. */
- stall_both_fifos();
- } else {
- if (data_phase_in)
- expect_data_phase_in(tc);
- else if (data_phase_out)
- expect_data_phase_out(tc);
- else
- expect_status_phase_in(tc);
- }
-}
-
-/* This handles both IN and OUT interrupts for EP0 */
-static void ep0_interrupt(uint32_t intr_on_out, uint32_t intr_on_in)
-{
- struct dwc_usb *usb = &usb_ctl;
- struct dwc_usb_ep *ep = usb->ep[0];
- uint32_t doepint, diepint;
- enum table_case tc;
- int out_complete, out_setup, in_complete;
-
- /* Determine the interrupt cause and clear the bits quickly, but only
- * if they really apply. I don't think they're trustworthy if we didn't
- * actually get an interrupt.
- */
- doepint = GR_USB_DOEPINT(0) & GR_USB_DOEPMSK;
- if (intr_on_out)
- GR_USB_DOEPINT(0) = doepint;
- diepint = GR_USB_DIEPINT(0) & GR_USB_DIEPMSK;
- if (intr_on_in)
- GR_USB_DIEPINT(0) = diepint;
-
- out_complete = doepint & DOEPINT_XFERCOMPL;
- out_setup = doepint & DOEPINT_SETUP;
- in_complete = diepint & DIEPINT_XFERCOMPL;
-
- /* Decode the situation according to Table 10-7 */
- tc = decode_table_10_7(doepint);
-
- switch (what_am_i_doing) {
- case WAITING_FOR_SETUP_PACKET:
- if (out_setup)
- handle_setup(tc);
- else
- report_error(-1);
- break;
-
- case DATA_STAGE_IN:
- if (intr_on_in && in_complete) {
- /* A packet is sent. Should we send another? */
- if (ep->in_packets > 0) {
- /* Send another packet. */
- send_in_packet(0);
- expect_data_phase_in(tc);
- }
- }
-
- /* But we should ignore the OUT endpoint if we didn't actually
- * get an OUT interrupt.
- */
- if (!intr_on_out)
- break;
-
- if (out_setup) {
- /* The first IN packet has been seen. Keep going. */
- break;
- }
- if (out_complete) {
- /* We've handled the Status phase. All done. */
- expect_setup_packet();
- break;
- }
-
- /* Anything else should be ignorable. Right? */
- break;
-
- case NO_DATA_STAGE:
- if (intr_on_in && in_complete) {
- /* We are not expecting an empty packet in
- * return for our empty packet.
- */
- expect_setup_packet();
- }
-
- /* Done unless we got an OUT interrupt */
- if (!intr_on_out)
- break;
-
- if (out_setup) {
- report_error(-1);
- break;
- }
-
- /* Anything else means get ready for a Setup packet */
- report_error(-1);
- expect_setup_packet();
- break;
- }
-}
-
-/****************************************************************************/
-/* USB device initialization and shutdown routines */
-
-/*
- * DATA FIFO Setup. There is an internal SPRAM used to buffer the IN/OUT
- * packets and track related state without hammering the AHB and system RAM
- * during USB transactions. We have to specify where and how much of that SPRAM
- * to use for what.
- *
- * See Programmer's Guide chapter 2, "Calculating FIFO Size".
- * We're using Dedicated TxFIFO Operation, without enabling thresholding.
- *
- * Section 2.1.1.2, page 30: RXFIFO size is the same as for Shared FIFO, which
- * is Section 2.1.1.1, page 28. This is also the same as Method 2 on page 45.
- *
- * We support up to 3 control EPs, no periodic IN EPs, up to 16 TX EPs. Max
- * data packet size is 64 bytes. Total SPRAM available is 1024 slots.
- */
-#define MAX_CONTROL_EPS 3
-#define MAX_NORMAL_EPS 16
-#define FIFO_RAM_DEPTH 1024
-/*
- * Device RX FIFO size is thus:
- * (4 * 3 + 6) + 2 * ((64 / 4) + 1) + (2 * 16) + 1 == 85
- */
-#define RXFIFO_SIZE ((4 * MAX_CONTROL_EPS + 6) + \
- 2 * ((USB_MAX_PACKET_SIZE / 4) + 1) + \
- (2 * MAX_NORMAL_EPS) + 1)
-/*
- * Device TX FIFO size is 2 * (64 / 4) == 32 for each IN EP (Page 46).
- */
-#define TXFIFO_SIZE (2 * (USB_MAX_PACKET_SIZE / 4))
-/*
- * We need 4 slots per endpoint direction for endpoint status stuff (Table 2-1,
- * unconfigurable).
- */
-#define EP_STATUS_SIZE (4 * MAX_NORMAL_EPS * 2)
-/*
- * Make sure all that fits.
- */
-BUILD_ASSERT(RXFIFO_SIZE + TXFIFO_SIZE * MAX_NORMAL_EPS + EP_STATUS_SIZE <
- FIFO_RAM_DEPTH);
-
-
-/* Now put those constants into the correct registers */
-static void setup_data_fifos(void)
-{
- int i;
-
- /* Programmer's Guide, p31 */
- GR_USB_GRXFSIZ = RXFIFO_SIZE; /* RXFIFO */
- GR_USB_GNPTXFSIZ = (TXFIFO_SIZE << 16) | RXFIFO_SIZE; /* TXFIFO 0 */
-
- /* TXFIFO 1..15 */
- for (i = 1; i < MAX_NORMAL_EPS; i++)
- GR_USB_DIEPTXF(i) = ((TXFIFO_SIZE << 16) |
- (RXFIFO_SIZE + i * TXFIFO_SIZE));
-
- /*
- * TODO: The Programmer's Guide is confusing about when or whether to
- * flush the FIFOs. Section 2.1.1.2 (p31) just says to flush. Section
- * 2.2.2 (p55) says to stop all the FIFOs first, then flush. Section
- * 7.5.4 (p162) says that flushing the RXFIFO at reset is not
- * recommended at all.
- *
- * I'm also unclear on whether or not the individual EPs are expected
- * to be disabled already (DIEPCTLn/DOEPCTLn.EPENA == 0), and if so,
- * whether by firmware or hardware.
- */
-
- /* Flush all FIFOs according to Section 2.1.1.2 */
- GR_USB_GRSTCTL = GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH
- | GRSTCTL_RXFFLSH;
- while (GR_USB_GRSTCTL & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH))
- ; /* TODO: timeout 100ms */
-}
-
-static void usb_init_endpoints(void)
-{
- int ep;
-
- /* Prepare to receive packets on EP0 */
- expect_setup_packet();
-
- /* Reset the other endpoints */
- for (ep = 1; ep < USB_EP_COUNT; ep++)
- usb_ep_event[ep](USB_EVENT_RESET);
-}
-
-static void usb_reset(void)
-{
- /* Clear our internal state */
- device_state = DS_DEFAULT;
- configuration_value = 0;
-
- /* Clear the device address */
- GWRITE_FIELD(USB, DCFG, DEVADDR, 0);
-
- /* Reinitialize all the endpoints */
- usb_init_endpoints();
-}
-
-static void usb_resetdet(void)
-{
- /* TODO: Same as normal reset, right? I think we only get this if we're
- * suspended (sleeping) and the host resets us. Try it and see.
- */
- usb_reset();
-}
-
-static void usb_enumdone(void)
-{
- /* We can change to HS here. We will not go to HS today */
- GR_USB_DCTL |= DCTL_CGOUTNAK;
-}
-
-
-void usb_interrupt(void)
-{
- uint32_t status = GR_USB_GINTSTS & GR_USB_GINTMSK;
- uint32_t oepint = status & GINTSTS(OEPINT);
- uint32_t iepint = status & GINTSTS(IEPINT);
- int ep;
-
- if (status & GINTSTS(ENUMDONE))
- usb_enumdone();
-
- if (status & GINTSTS(RESETDET))
- usb_resetdet();
-
- if (status & GINTSTS(USBRST))
- usb_reset();
-
- /* Endpoint interrupts */
- if (oepint || iepint) {
- /* Note: It seems that the DAINT bits are only trustworthy for
- * identifying interrupts when selected by the corresponding
- * OEPINT and IEPINT bits from GINTSTS.
- */
- uint32_t daint = GR_USB_DAINT;
-
- /* EP0 has a combined IN/OUT handler. Only call it once, but
- * let it know which direction(s) had an interrupt.
- */
- if (daint & (DAINT_OUTEP(0) | DAINT_INEP(0))) {
- uint32_t intr_on_out = (oepint &&
- (daint & DAINT_OUTEP(0)));
- uint32_t intr_on_in = (iepint &&
- (daint & DAINT_INEP(0)));
- ep0_interrupt(intr_on_out, intr_on_in);
- }
-
- /* Invoke the unidirectional IN and OUT functions for the other
- * endpoints. Each handler must clear their own bits in
- * DIEPINTn/DOEPINTn.
- */
- for (ep = 1; ep < USB_EP_COUNT; ep++) {
- if (oepint && (daint & DAINT_OUTEP(ep)))
- usb_ep_rx[ep]();
- if (iepint && (daint & DAINT_INEP(ep)))
- usb_ep_tx[ep]();
- }
- }
-
- GR_USB_GINTSTS = status;
-}
-DECLARE_IRQ(STM32_IRQ_OTG_FS, usb_interrupt, 1);
-DECLARE_IRQ(STM32_IRQ_OTG_HS, usb_interrupt, 1);
-
-static void usb_softreset(void)
-{
- int timeout;
-
- CPRINTS("%s", __func__);
-
- /* Wait for bus idle */
- timeout = 10000;
- while (!(GR_USB_GRSTCTL & GRSTCTL_AHBIDLE) && timeout-- > 0)
- ;
-
- /* Reset and wait for clear */
- GR_USB_GRSTCTL = GRSTCTL_CSFTRST;
- timeout = 10000;
- while ((GR_USB_GRSTCTL & GRSTCTL_CSFTRST) && timeout-- > 0)
- ;
- if (GR_USB_GRSTCTL & GRSTCTL_CSFTRST) {
- CPRINTF("USB: reset failed\n");
- return;
- }
-
- /* Some more idle? */
- timeout = 10000;
- while (!(GR_USB_GRSTCTL & GRSTCTL_AHBIDLE) && timeout-- > 0)
- ;
-
- if (!timeout) {
- CPRINTF("USB: reset timeout\n");
- return;
- }
- /* TODO: Wait 3 PHY clocks before returning */
-}
-
-void usb_connect(void)
-{
- GR_USB_DCTL &= ~DCTL_SFTDISCON;
-}
-
-void usb_disconnect(void)
-{
- GR_USB_DCTL |= DCTL_SFTDISCON;
-
- device_state = DS_DEFAULT;
- configuration_value = 0;
-}
-
-void usb_reset_init_phy(void)
-{
- struct dwc_usb *usb = &usb_ctl;
-
- if (usb->phy_type == USB_PHY_ULPI) {
- GR_USB_GCCFG &= ~GCCFG_PWRDWN;
- GR_USB_GUSBCFG &= ~(GUSBCFG_TSDPS |
- GUSBCFG_ULPIFSLS | GUSBCFG_PHYSEL);
- GR_USB_GUSBCFG &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI);
- /* No suspend */
- GR_USB_GUSBCFG |= GUSBCFG_ULPICSM | GUSBCFG_ULPIAR;
-
- usb_softreset();
- } else {
- GR_USB_GUSBCFG |= GUSBCFG_PHYSEL;
- usb_softreset();
- GR_USB_GCCFG |= GCCFG_PWRDWN;
- }
-}
-
-void usb_init(void)
-{
- int i;
- struct dwc_usb *usb = &usb_ctl;
-
- CPRINTS("%s", __func__);
-
-#ifdef CONFIG_USB_SERIALNO
- usb_load_serial();
-#endif
-
- /* USB is in use */
- disable_sleep(SLEEP_MASK_USB_DEVICE);
-
- /* Enable clocks */
- clock_enable_module(MODULE_USB, 0);
- clock_enable_module(MODULE_USB, 1);
-
- /* TODO(crbug.com/496888): set up pinmux */
- gpio_config_module(MODULE_USB, 1);
-
- /* Make sure interrupts are disabled */
- GR_USB_GINTMSK = 0;
- GR_USB_DAINTMSK = 0;
- GR_USB_DIEPMSK = 0;
- GR_USB_DOEPMSK = 0;
-
- /* Full-Speed Serial PHY */
- usb_reset_init_phy();
-
- /* Global + DMA configuration */
- GR_USB_GAHBCFG = GAHBCFG_GLB_INTR_EN;
- GR_USB_GAHBCFG |= GAHBCFG_HBSTLEN_INCR4;
- if (usb->dma_en)
- GR_USB_GAHBCFG |= GAHBCFG_DMA_EN;
-
- /* Device only, no SRP */
- GR_USB_GUSBCFG |= GUSBCFG_FDMOD;
- GR_USB_GUSBCFG |= GUSBCFG_SRPCAP | GUSBCFG_HNPCAP;
-
- GR_USB_GCCFG &= ~GCCFG_VBDEN;
- GR_USB_GOTGCTL |= GOTGCTL_BVALOEN;
- GR_USB_GOTGCTL |= GOTGCTL_BVALOVAL;
-
- GR_USB_PCGCCTL = 0;
-
- if (usb->phy_type == USB_PHY_ULPI) {
- /* TODO(nsanders): add HS support like so.
- * GR_USB_DCFG = (GR_USB_DCFG & ~GC_USB_DCFG_DEVSPD_MASK)
- * | DCFG_DEVSPD_HSULPI;
- */
- GR_USB_DCFG = (GR_USB_DCFG & ~GC_USB_DCFG_DEVSPD_MASK)
- | DCFG_DEVSPD_FSULPI;
- } else {
- GR_USB_DCFG = (GR_USB_DCFG & ~GC_USB_DCFG_DEVSPD_MASK)
- | DCFG_DEVSPD_FS48;
- }
-
- GR_USB_DCFG |= DCFG_NZLSOHSK;
-
- flush_all_fifos();
-
- /* Clear pending interrupts again */
- GR_USB_GINTMSK = 0;
- GR_USB_DIEPMSK = 0;
- GR_USB_DOEPMSK = 0;
- GR_USB_DAINT = 0xffffffff;
- GR_USB_DAINTMSK = 0;
-
- /* TODO: What about the AHB Burst Length Field? It's 0 now. */
- GR_USB_GAHBCFG |= GAHBCFG_TXFELVL | GAHBCFG_PTXFELVL;
-
- /* Device only, no SRP */
- GR_USB_GUSBCFG |= GUSBCFG_FDMOD
- | GUSBCFG_TOUTCAL(7)
- /* FIXME: Magic number! 14 is for 15MHz! Use 9 for 30MHz */
- | GUSBCFG_USBTRDTIM(14);
-
- /* Be in disconnected state until we are ready */
- usb_disconnect();
-
- /* If we've restored a nonzero device address, update our state. */
- if (GR_USB_DCFG & GC_USB_DCFG_DEVADDR_MASK) {
- /* Caution: We only have one config TODAY, so there's no real
- * difference between DS_CONFIGURED and DS_ADDRESS.
- */
- device_state = DS_CONFIGURED;
- configuration_value = 1;
- } else {
- device_state = DS_DEFAULT;
- configuration_value = 0;
- }
-
- /* Now that DCFG.DesDMA is accurate, prepare the FIFOs */
- setup_data_fifos();
-
- usb_init_endpoints();
-
- /* Clear any pending interrupts */
- for (i = 0; i < 16; i++) {
- GR_USB_DIEPINT(i) = 0xffffffff;
- GR_USB_DIEPTSIZ(i) = 0;
- GR_USB_DOEPINT(i) = 0xffffffff;
- GR_USB_DOEPTSIZ(i) = 0;
- }
-
- if (usb->dma_en) {
- GR_USB_DTHRCTL = DTHRCTL_TXTHRLEN_6 | DTHRCTL_RXTHRLEN_6;
- GR_USB_DTHRCTL |= DTHRCTL_RXTHREN | DTHRCTL_ISOTHREN
- | DTHRCTL_NONISOTHREN;
- i = GR_USB_DTHRCTL;
- }
-
- GR_USB_GINTSTS = 0xFFFFFFFF;
-
- GR_USB_GAHBCFG |= GAHBCFG_GLB_INTR_EN | GAHBCFG_TXFELVL
- | GAHBCFG_PTXFELVL;
-
- if (!(usb->dma_en))
- GR_USB_GINTMSK |= GINTMSK(RXFLVL);
-
- /* Unmask some endpoint interrupt causes */
- GR_USB_DIEPMSK = DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK;
- GR_USB_DOEPMSK = DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK |
- DOEPMSK_SETUPMSK;
-
- /* Enable interrupt handlers */
- task_enable_irq(usb->irq);
-
- /* Allow USB interrupts to come in */
- GR_USB_GINTMSK |=
- /* NAK bits that must be cleared by the DCTL register */
- GINTMSK(GOUTNAKEFF) | GINTMSK(GINNAKEFF) |
- /* Initialization events */
- GINTMSK(USBRST) | GINTMSK(ENUMDONE) |
- /* Reset detected while suspended. Need to wake up. */
- GINTMSK(RESETDET) | /* TODO: Do we need this? */
- /* Idle, Suspend detected. Should go to sleep. */
- GINTMSK(ERLYSUSP) | GINTMSK(USBSUSP);
-
- GR_USB_GINTMSK |=
- /* Endpoint activity, cleared by the DOEPINT/DIEPINT regs */
- GINTMSK(OEPINT) | GINTMSK(IEPINT);
-
- /* Device registers have been setup */
- GR_USB_DCTL |= DCTL_PWRONPRGDONE;
- udelay(10);
- GR_USB_DCTL &= ~DCTL_PWRONPRGDONE;
-
- /* Clear global NAKs */
- GR_USB_DCTL |= DCTL_CGOUTNAK | DCTL_CGNPINNAK;
-
-#ifndef CONFIG_USB_INHIBIT_CONNECT
- /* Indicate our presence to the USB host */
- usb_connect();
-#endif
-}
-#ifndef CONFIG_USB_INHIBIT_INIT
-DECLARE_HOOK(HOOK_INIT, usb_init, HOOK_PRIO_DEFAULT);
-#endif
-
-void usb_release(void)
-{
- struct dwc_usb *usb = &usb_ctl;
-
- /* signal disconnect to host */
- usb_disconnect();
-
- /* disable interrupt handlers */
- task_disable_irq(usb->irq);
-
- /* disable clocks */
- clock_enable_module(MODULE_USB, 0);
- /* TODO: pin-mux */
-
- /* USB is off, so sleep whenever */
- enable_sleep(SLEEP_MASK_USB_DEVICE);
-}
-
-/* Print USB info and stats */
-static void usb_info(void)
-{
- struct dwc_usb *usb = &usb_ctl;
- int i;
-
- CPRINTF("USB settings: %s%s%s\n",
- usb->speed == USB_SPEED_FS ? "FS " : "HS ",
- usb->phy_type == USB_PHY_INTERNAL ? "Internal Phy " : "ULPI ",
- usb->dma_en ? "DMA " : "");
-
- for (i = 0; i < USB_EP_COUNT; i++) {
- CPRINTF("Endpoint %d activity: %s%s\n", i,
- rx_ep_is_active(i) ? "RX " : "",
- tx_ep_is_ready(i) ? "" : "TX ");
- }
-}
-
-static int command_usb(int argc, char **argv)
-{
- if (argc > 1) {
- if (!strcasecmp("on", argv[1]))
- usb_init();
- else if (!strcasecmp("off", argv[1]))
- usb_release();
- else if (!strcasecmp("info", argv[1]))
- usb_info();
- return EC_SUCCESS;
- }
-
- return EC_ERROR_PARAM1;
-}
-DECLARE_CONSOLE_COMMAND(usb, command_usb,
- "[on|off|info]",
- "Get/set the USB connection state and PHY selection");
-
-#ifdef CONFIG_USB_SERIALNO
-/* This will be subbed into USB_STR_SERIALNO. */
-struct usb_string_desc *usb_serialno_desc =
- USB_WR_STRING_DESC(DEFAULT_SERIALNO);
-
-/* Update serial number */
-static int usb_set_serial(const char *serialno)
-{
- struct usb_string_desc *sd = usb_serialno_desc;
- int i;
-
- if (!serialno)
- return EC_ERROR_INVAL;
-
- /* Convert into unicode usb string desc. */
- for (i = 0; i < CONFIG_SERIALNO_LEN; i++) {
- sd->_data[i] = serialno[i];
- if (serialno[i] == 0)
- break;
- }
- /* Count wchars (w/o null terminator) plus size & type bytes. */
- sd->_len = (i * 2) + 2;
- sd->_type = USB_DT_STRING;
-
- return EC_SUCCESS;
-}
-
-/* Retrieve serial number from pstate flash. */
-static int usb_load_serial(void)
-{
- const char *serialno;
- int rv;
-
- serialno = board_read_serial();
- if (!serialno)
- return EC_ERROR_ACCESS_DENIED;
-
- rv = usb_set_serial(serialno);
- return rv;
-}
-
-/* Save serial number into pstate region. */
-static int usb_save_serial(const char *serialno)
-{
- int rv;
-
- if (!serialno)
- return EC_ERROR_INVAL;
-
- /* Save this new serial number to flash. */
- rv = board_write_serial(serialno);
- if (rv)
- return rv;
-
- /* Load this new serial number to memory. */
- rv = usb_load_serial();
- return rv;
-}
-
-static int command_serialno(int argc, char **argv)
-{
- struct usb_string_desc *sd = usb_serialno_desc;
- char buf[CONFIG_SERIALNO_LEN];
- int rv = EC_SUCCESS;
- int i;
-
- if (argc != 1) {
- if ((strcasecmp(argv[1], "set") == 0) &&
- (argc == 3)) {
- ccprintf("Saving serial number\n");
- rv = usb_save_serial(argv[2]);
- } else if ((strcasecmp(argv[1], "load") == 0) &&
- (argc == 2)) {
- ccprintf("Loading serial number\n");
- rv = usb_load_serial();
- } else
- return EC_ERROR_INVAL;
- }
-
- for (i = 0; i < CONFIG_SERIALNO_LEN; i++)
- buf[i] = sd->_data[i];
- ccprintf("Serial number: %s\n", buf);
- return rv;
-}
-
-DECLARE_CONSOLE_COMMAND(serialno, command_serialno,
- "load/set [value]",
- "Read and write USB serial number");
-#endif /* CONFIG_USB_SERIALNO */