/* * This file is part of the libopencm3 project. * * Copyright (C) 2013 Alexandru Gagniuc * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ /** * @defgroup usb_file USB * * @ingroup LM4Fxx * * @author @htmlonly © @endhtmlonly 2013 * Alexandru Gagniuc * * \brief libopencm3 LM4F Universal Serial Bus controller * * The LM4F USB driver is integrated with the libopencm3 USB stack. You should * use the generic stack. * * To use this driver, tell the linker to look for it: * @code{.c} * extern usbd_driver lm4f_usb_driver; * @endcode * * And pass this driver as an argument when initializing the USB stack: * @code{.c} * usbd_device *usbd_dev; * usbd_dev = usbd_init(&lm4f_usb_driver, ...); * @endcode * * Polling or interrupt-driven? * * The LM4F USB driver will work fine regardless of whether it is called from an * interrupt service routine, or from the main program loop. * * Polling USB from the main loop requires calling @ref usbd_poll() from the * main program loop. * For example: * @code{.c} * // Main program loop * while(1) { * usbd_poll(usb_dev); * do_other_stuff(); * ... * @endcode * * Running @ref usbd_poll() from an interrupt has the advantage that it is only * called when needed, saving CPU cycles for the main program. * * RESET, DISCON, RESUME, and SUSPEND interrupts must be enabled, along with the * interrupts for any endpoint that is used. The EP0_TX interrupt must be * enabled for the control endpoint to function correctly. * For example, if EP1IN and EP2OUT are used, then the EP0_TX, EP1_TX, and * EP2_RX interrupts should be enabled: * @code{.c} * // Enable USB interrupts for EP0, EP1IN, and EP2OUT * ints = USB_INT_RESET | USB_INT_DISCON | USB_INT_RESUME | * USB_INT_SUSPEND; * usb_enable_interrupts(ints, USB_EP2_INT, USB_EP0_INT | USB_EP1_INT); * // Route the interrupts through the NVIC * nvic_enable_irq(NVIC_USB0_IRQ); * @endcode * * The USB ISR only has to call @ref usbd_poll(). * * @code{.c} * void usb0_isr(void) * { * usbd_poll(usb_dev); * } * @endcode * @{ */ /* * TODO list: * * 1) Driver works by reading and writing to the FIFOs one byte at a time. It * has no knowledge of DMA. * 2) Double-buffering is supported. How can we take advantage of it to speed * up endpoint transfers. * 3) No benchmarks as to the endpoint's performance has been done. */ /* * The following are resources referenced in comments: * [1] http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/238784.aspx */ #include #include #include #include #include "../../lib/usb/usb_private.h" #include #define MAX_FIFO_RAM (4 * 1024) const struct _usbd_driver lm4f_usb_driver; /** * \brief Enable Specific USB Interrupts * * Enable any combination of interrupts. Interrupts may be OR'ed together to * enable them with one call. For example, to enable both the RESUME and RESET * interrupts, pass (USB_INT_RESUME | USB_INT_RESET) * * Note that the NVIC must be enabled and properly configured for the interrupt * to be routed to the CPU. * * @param[in] ints Interrupts which to enable. Any combination of interrupts may * be specified by OR'ing then together * @param[in] rx_ints Endpoints for which to generate an interrupt when a packet * packet is received. * @param[in] tx_ints Endpoints for which to generate an interrupt when a packet * packet is finished transmitting. */ void usb_enable_interrupts(enum usb_interrupt ints, enum usb_ep_interrupt rx_ints, enum usb_ep_interrupt tx_ints) { USB_IE |= ints; USB_RXIE |= rx_ints; USB_TXIE |= tx_ints; } /** * \brief Disable Specific USB Interrupts * * Disable any combination of interrupts. Interrupts may be OR'ed together to * enable them with one call. For example, to disable both the RESUME and RESET * interrupts, pass (USB_INT_RESUME | USB_INT_RESET) * * Note that the NVIC must be enabled and properly configured for the interrupt * to be routed to the CPU. * * @param[in] ints Interrupts which to disable. Any combination of interrupts * may be specified by OR'ing then together * @param[in] rx_ints Endpoints for which to stop generating an interrupt when a * packet packet is received. * @param[in] tx_ints Endpoints for which to stop generating an interrupt when a * packet packet is finished transmitting. */ void usb_disable_interrupts(enum usb_interrupt ints, enum usb_ep_interrupt rx_ints, enum usb_ep_interrupt tx_ints) { USB_IE &= ~ints; USB_RXIE &= ~rx_ints; USB_TXIE &= ~tx_ints; } /** * @cond private */ static inline void lm4f_usb_soft_disconnect(void) { USB_POWER &= ~USB_POWER_SOFTCONN; } static inline void lm4f_usb_soft_connect(void) { USB_POWER |= USB_POWER_SOFTCONN; } static void lm4f_set_address(usbd_device *usbd_dev, uint8_t addr) { (void)usbd_dev; USB_FADDR = addr & USB_FADDR_FUNCADDR_MASK; } static void lm4f_ep_setup(usbd_device *usbd_dev, uint8_t addr, uint8_t type, uint16_t max_size, void (*callback) (usbd_device *usbd_dev, uint8_t ep)) { (void)usbd_dev; (void)type; uint8_t reg8; uint16_t fifo_size; const bool dir_tx = addr & 0x80; const uint8_t ep = addr & 0x0f; /* * We do not mess with the maximum packet size, but we can only allocate * the FIFO in power-of-two increments. */ if (max_size > 1024) { fifo_size = 2048; reg8 = USB_FIFOSZ_SIZE_2048; } else if (max_size > 512) { fifo_size = 1024; reg8 = USB_FIFOSZ_SIZE_1024; } else if (max_size > 256) { fifo_size = 512; reg8 = USB_FIFOSZ_SIZE_512; } else if (max_size > 128) { fifo_size = 256; reg8 = USB_FIFOSZ_SIZE_256; } else if (max_size > 64) { fifo_size = 128; reg8 = USB_FIFOSZ_SIZE_128; } else if (max_size > 32) { fifo_size = 64; reg8 = USB_FIFOSZ_SIZE_64; } else if (max_size > 16) { fifo_size = 32; reg8 = USB_FIFOSZ_SIZE_32; } else if (max_size > 8) { fifo_size = 16; reg8 = USB_FIFOSZ_SIZE_16; } else { fifo_size = 8; reg8 = USB_FIFOSZ_SIZE_8; } /* Endpoint 0 is more special */ if (addr == 0) { USB_EPIDX = 0; if (reg8 > USB_FIFOSZ_SIZE_64) { reg8 = USB_FIFOSZ_SIZE_64; } /* The RX and TX FIFOs are shared for EP0 */ USB_RXFIFOSZ = reg8; USB_TXFIFOSZ = reg8; /* * Regardless of how much we allocate, the first 64 bytes * are always reserved for EP0. */ usbd_dev->fifo_mem_top_ep0 = 64; return; } /* Are we out of FIFO space? */ if (usbd_dev->fifo_mem_top + fifo_size > MAX_FIFO_RAM) { return; } USB_EPIDX = addr & USB_EPIDX_MASK; /* FIXME: What about double buffering? */ if (dir_tx) { USB_TXMAXP(ep) = max_size; USB_TXFIFOSZ = reg8; USB_TXFIFOADD = ((usbd_dev->fifo_mem_top) >> 3); if (callback) { usbd_dev->user_callback_ctr[ep][USB_TRANSACTION_IN] = (void *)callback; } if (type == USB_ENDPOINT_ATTR_ISOCHRONOUS) { USB_TXCSRH(ep) |= USB_TXCSRH_ISO; } else { USB_TXCSRH(ep) &= ~USB_TXCSRH_ISO; } } else { USB_RXMAXP(ep) = max_size; USB_RXFIFOSZ = reg8; USB_RXFIFOADD = ((usbd_dev->fifo_mem_top) >> 3); if (callback) { usbd_dev->user_callback_ctr[ep][USB_TRANSACTION_OUT] = (void *)callback; } if (type == USB_ENDPOINT_ATTR_ISOCHRONOUS) { USB_RXCSRH(ep) |= USB_RXCSRH_ISO; } else { USB_RXCSRH(ep) &= ~USB_RXCSRH_ISO; } } usbd_dev->fifo_mem_top += fifo_size; } static void lm4f_endpoints_reset(usbd_device *usbd_dev) { /* * The core resets the endpoints automatically on reset. * The first 64 bytes are always reserved for EP0 */ usbd_dev->fifo_mem_top = 64; } static void lm4f_ep_stall_set(usbd_device *usbd_dev, uint8_t addr, uint8_t stall) { (void)usbd_dev; const uint8_t ep = addr & 0x0f; const bool dir_tx = addr & 0x80; if (ep == 0) { if (stall) { USB_CSRL0 |= USB_CSRL0_STALL; } else { USB_CSRL0 &= ~USB_CSRL0_STALL; } return; } if (dir_tx) { if (stall) { (USB_TXCSRL(ep)) |= USB_TXCSRL_STALL; } else { (USB_TXCSRL(ep)) &= ~USB_TXCSRL_STALL; } } else { if (stall) { (USB_RXCSRL(ep)) |= USB_RXCSRL_STALL; } else { (USB_RXCSRL(ep)) &= ~USB_RXCSRL_STALL; } } } static uint8_t lm4f_ep_stall_get(usbd_device *usbd_dev, uint8_t addr) { (void)usbd_dev; const uint8_t ep = addr & 0x0f; const bool dir_tx = addr & 0x80; if (ep == 0) { return USB_CSRL0 & USB_CSRL0_STALLED; } if (dir_tx) { return USB_TXCSRL(ep) & USB_TXCSRL_STALLED; } else { return USB_RXCSRL(ep) & USB_RXCSRL_STALLED; } } static void lm4f_ep_nak_set(usbd_device *usbd_dev, uint8_t addr, uint8_t nak) { (void)usbd_dev; (void)addr; (void)nak; /* NAK's are handled automatically by hardware. Move along. */ } static uint16_t lm4f_ep_write_packet(usbd_device *usbd_dev, uint8_t addr, const void *buf, uint16_t len) { const uint8_t ep = addr & 0xf; uint16_t i; (void)usbd_dev; /* Don't touch the FIFO if there is still a packet being transmitted */ if (ep == 0 && (USB_CSRL0 & USB_CSRL0_TXRDY)) { return 0; } else if (USB_TXCSRL(ep) & USB_TXCSRL_TXRDY) { return 0; } /* * We don't need to worry about buf not being aligned. If it's not, * the reads are downgraded to 8-bit in hardware. We lose a bit of * performance, but we don't crash. */ for (i = 0; i < (len & ~0x3); i += 4) { USB_FIFO32(ep) = *((uint32_t *)(buf + i)); } if (len & 0x2) { USB_FIFO16(ep) = *((uint16_t *)(buf + i)); i += 2; } if (len & 0x1) { USB_FIFO8(ep) = *((uint8_t *)(buf + i)); i += 1; } if (ep == 0) { /* * EP0 is very special. We should only set DATAEND when we * transmit the last packet in the transaction. A transaction * that is a multiple of 64 bytes will end with a zero-length * packet, so our check is sane. */ if (len != 64) { USB_CSRL0 |= USB_CSRL0_TXRDY | USB_CSRL0_DATAEND; } else { USB_CSRL0 |= USB_CSRL0_TXRDY; } } else { USB_TXCSRL(ep) |= USB_TXCSRL_TXRDY; } return i; } static uint16_t lm4f_ep_read_packet(usbd_device *usbd_dev, uint8_t addr, void *buf, uint16_t len) { (void)usbd_dev; uint16_t rlen; uint8_t ep = addr & 0xf; uint16_t fifoin = USB_RXCOUNT(ep); rlen = (fifoin > len) ? len : fifoin; /* * We don't need to worry about buf not being aligned. If it's not, * the writes are downgraded to 8-bit in hardware. We lose a bit of * performance, but we don't crash. */ for (len = 0; len < (rlen & ~0x3); len += 4) { *((uint32_t *)(buf + len)) = USB_FIFO32(ep); } if (rlen & 0x2) { *((uint16_t *)(buf + len)) = USB_FIFO16(ep); len += 2; } if (rlen & 0x1) { *((uint8_t *)(buf + len)) = USB_FIFO8(ep); } if (ep == 0) { /* * Clear RXRDY * Datasheet says that DATAEND must also be set when clearing * RXRDY. We don't do that. If did this when transmitting a * packet larger than 64 bytes, only the first 64 bytes would * be transmitted, followed by a handshake. The host would only * get 64 bytes, seeing it as a malformed packet. Usually, we * would not get past enumeration. */ USB_CSRL0 |= USB_CSRL0_RXRDYC; } else { USB_RXCSRL(ep) &= ~USB_RXCSRL_RXRDY; } return rlen; } static void lm4f_poll(usbd_device *usbd_dev) { void (*tx_cb)(usbd_device *usbd_dev, uint8_t ea); void (*rx_cb)(usbd_device *usbd_dev, uint8_t ea); int i; /* * The initial state of these registers might change, as we process the * interrupt, but we need the initial state in order to decide how to * handle events. */ const uint8_t usb_is = USB_IS; const uint8_t usb_rxis = USB_RXIS; const uint8_t usb_txis = USB_TXIS; const uint8_t usb_csrl0 = USB_CSRL0; if ((usb_is & USB_IM_SUSPEND) && (usbd_dev->user_callback_suspend)) { usbd_dev->user_callback_suspend(); } if ((usb_is & USB_IM_RESUME) && (usbd_dev->user_callback_resume)) { usbd_dev->user_callback_resume(); } if (usb_is & USB_IM_RESET) { _usbd_reset(usbd_dev); } if ((usb_is & USB_IM_SOF) && (usbd_dev->user_callback_sof)) { usbd_dev->user_callback_sof(); } if (usb_txis & USB_EP0) { /* * The EP0 bit in USB_TXIS is special. It tells us that * something happened on EP0, but does not tell us what. This * bit does not necessarily tell us that a packet was * transmitted, so we have to go through all the possibilities * to figure out exactly what did. Only after we've exhausted * all other possibilities, can we assume this is a EPO * "transmit complete" interrupt. */ if (usb_csrl0 & USB_CSRL0_RXRDY) { enum _usbd_transaction type; type = (usbd_dev->control_state.state != DATA_OUT && usbd_dev->control_state.state != LAST_DATA_OUT) ? USB_TRANSACTION_SETUP : USB_TRANSACTION_OUT; if (usbd_dev->user_callback_ctr[0][type]) { usbd_dev-> user_callback_ctr[0][type](usbd_dev, 0); } } else { tx_cb = usbd_dev->user_callback_ctr[0] [USB_TRANSACTION_IN]; /* * EP0 bit in TXIS is set not only when a packet is * finished transmitting, but also when RXRDY is set, or * when we set TXRDY to transmit a packet. If any of * those are the case, then we do not want to call our * IN callback, since the state machine will be in the * wrong state, and we'll just stall our control * endpoint. * In fact, the only way to know if it's time to call * our TX callback is to know what to expect. The * hardware does not tell us what sort of transaction * this is. We need to work with the state machine to * figure it all out. See [1] for details. */ if ((usbd_dev->control_state.state != DATA_IN) && (usbd_dev->control_state.state != LAST_DATA_IN) && (usbd_dev->control_state.state != STATUS_IN)) { return; } if (tx_cb) { tx_cb(usbd_dev, 0); } } } /* See which interrupt occurred */ for (i = 1; i < 8; i++) { tx_cb = usbd_dev->user_callback_ctr[i][USB_TRANSACTION_IN]; rx_cb = usbd_dev->user_callback_ctr[i][USB_TRANSACTION_OUT]; if ((usb_txis & (1 << i)) && tx_cb) { tx_cb(usbd_dev, i); } if ((usb_rxis & (1 << i)) && rx_cb) { rx_cb(usbd_dev, i); } } } static void lm4f_disconnect(usbd_device *usbd_dev, bool disconnected) { (void)usbd_dev; /* * This is all it takes: * usbd_disconnect(dev, 1) followed by usbd_disconnect(dev, 0) * causes the device to re-enumerate and re-configure properly. */ if (disconnected) { lm4f_usb_soft_disconnect(); } else { lm4f_usb_soft_connect(); } } /* * A static struct works as long as we have only one USB peripheral. If we * meet LM4Fs with more than one USB, then we need to rework this approach. */ static struct _usbd_device usbd_dev; /** Initialize the USB device controller hardware of the LM4F. */ static usbd_device *lm4f_usbd_init(void) { int i; /* Start the USB clock */ periph_clock_enable(RCC_USB0); /* Enable the USB PLL interrupts - used to assert PLL lock */ SYSCTL_IMC |= (SYSCTL_IMC_USBPLLLIM | SYSCTL_IMC_PLLLIM); rcc_usb_pll_on(); /* Make sure we're disconnected. We'll reconnect later */ lm4f_usb_soft_disconnect(); /* Software reset USB */ SYSCTL_SRUSB = 1; for (i = 0; i < 1000; i++) { __asm__("nop"); } SYSCTL_SRUSB = 0; /* * Wait for the PLL to lock before soft connecting * This will result in a deadlock if the system clock is not setup * correctly (clock from main oscillator). */ /* Wait for it */ i = 0; while ((SYSCTL_RIS & SYSCTL_RIS_USBPLLLRIS) == 0) { i++; if (i > 0xffff) { return 0; } } /* Now connect to USB */ lm4f_usb_soft_connect(); /* No FIFO allocated yet, but the first 64 bytes are still reserved */ usbd_dev.fifo_mem_top = 64; return &usbd_dev; } /* What is this thing even good for */ #define RX_FIFO_SIZE 512 const struct _usbd_driver lm4f_usb_driver = { .init = lm4f_usbd_init, .set_address = lm4f_set_address, .ep_setup = lm4f_ep_setup, .ep_reset = lm4f_endpoints_reset, .ep_stall_set = lm4f_ep_stall_set, .ep_stall_get = lm4f_ep_stall_get, .ep_nak_set = lm4f_ep_nak_set, .ep_write_packet = lm4f_ep_write_packet, .ep_read_packet = lm4f_ep_read_packet, .poll = lm4f_poll, .disconnect = lm4f_disconnect, .base_address = USB_BASE, .set_address_before_status = false, .rx_fifo_size = RX_FIFO_SIZE, }; /** * @endcond */ /** * @} */