From 1dc7d758f96dd2b9bd7b03f01ca032d68b696cf0 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 2 Nov 2014 10:14:39 +0000 Subject: fish --- libopencm3/lib/usb/usb_f103.c | 346 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 libopencm3/lib/usb/usb_f103.c (limited to 'libopencm3/lib/usb/usb_f103.c') diff --git a/libopencm3/lib/usb/usb_f103.c b/libopencm3/lib/usb/usb_f103.c new file mode 100644 index 0000000..2985650 --- /dev/null +++ b/libopencm3/lib/usb/usb_f103.c @@ -0,0 +1,346 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2010 Gareth McMullin + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include "usb_private.h" + +static usbd_device *stm32f103_usbd_init(void); +static void stm32f103_set_address(usbd_device *usbd_dev, uint8_t addr); +static void stm32f103_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)); +static void stm32f103_endpoints_reset(usbd_device *usbd_dev); +static void stm32f103_ep_stall_set(usbd_device *usbd_dev, uint8_t addr, + uint8_t stall); +static uint8_t stm32f103_ep_stall_get(usbd_device *usbd_dev, uint8_t addr); +static void stm32f103_ep_nak_set(usbd_device *usbd_dev, uint8_t addr, + uint8_t nak); +static uint16_t stm32f103_ep_write_packet(usbd_device *usbd_dev, uint8_t addr, + const void *buf, uint16_t len); +static uint16_t stm32f103_ep_read_packet(usbd_device *usbd_dev, uint8_t addr, + void *buf, uint16_t len); +static void stm32f103_poll(usbd_device *usbd_dev); + +static uint8_t force_nak[8]; +static struct _usbd_device usbd_dev; + +const struct _usbd_driver stm32f103_usb_driver = { + .init = stm32f103_usbd_init, + .set_address = stm32f103_set_address, + .ep_setup = stm32f103_ep_setup, + .ep_reset = stm32f103_endpoints_reset, + .ep_stall_set = stm32f103_ep_stall_set, + .ep_stall_get = stm32f103_ep_stall_get, + .ep_nak_set = stm32f103_ep_nak_set, + .ep_write_packet = stm32f103_ep_write_packet, + .ep_read_packet = stm32f103_ep_read_packet, + .poll = stm32f103_poll, +}; + +/** Initialize the USB device controller hardware of the STM32. */ +static usbd_device *stm32f103_usbd_init(void) +{ + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN); + SET_REG(USB_CNTR_REG, 0); + SET_REG(USB_BTABLE_REG, 0); + SET_REG(USB_ISTR_REG, 0); + + /* Enable RESET, SUSPEND, RESUME and CTR interrupts. */ + SET_REG(USB_CNTR_REG, USB_CNTR_RESETM | USB_CNTR_CTRM | + USB_CNTR_SUSPM | USB_CNTR_WKUPM); + return &usbd_dev; +} + +static void stm32f103_set_address(usbd_device *dev, uint8_t addr) +{ + (void)dev; + /* Set device address and enable. */ + SET_REG(USB_DADDR_REG, (addr & USB_DADDR_ADDR) | USB_DADDR_ENABLE); +} + +/** + * Set the receive buffer size for a given USB endpoint. + * + * @param ep Index of endpoint to configure. + * @param size Size in bytes of the RX buffer. + */ +static void usb_set_ep_rx_bufsize(usbd_device *dev, uint8_t ep, uint32_t size) +{ + (void)dev; + if (size > 62) { + if (size & 0x1f) { + size -= 32; + } + USB_SET_EP_RX_COUNT(ep, (size << 5) | 0x8000); + } else { + if (size & 1) { + size++; + } + USB_SET_EP_RX_COUNT(ep, size << 10); + } +} + +static void stm32f103_ep_setup(usbd_device *dev, uint8_t addr, uint8_t type, + uint16_t max_size, + void (*callback) (usbd_device *usbd_dev, + uint8_t ep)) +{ + /* Translate USB standard type codes to STM32. */ + const uint16_t typelookup[] = { + [USB_ENDPOINT_ATTR_CONTROL] = USB_EP_TYPE_CONTROL, + [USB_ENDPOINT_ATTR_ISOCHRONOUS] = USB_EP_TYPE_ISO, + [USB_ENDPOINT_ATTR_BULK] = USB_EP_TYPE_BULK, + [USB_ENDPOINT_ATTR_INTERRUPT] = USB_EP_TYPE_INTERRUPT, + }; + uint8_t dir = addr & 0x80; + addr &= 0x7f; + + /* Assign address. */ + USB_SET_EP_ADDR(addr, addr); + USB_SET_EP_TYPE(addr, typelookup[type]); + + if (dir || (addr == 0)) { + USB_SET_EP_TX_ADDR(addr, dev->pm_top); + if (callback) { + dev->user_callback_ctr[addr][USB_TRANSACTION_IN] = + (void *)callback; + } + USB_CLR_EP_TX_DTOG(addr); + USB_SET_EP_TX_STAT(addr, USB_EP_TX_STAT_NAK); + dev->pm_top += max_size; + } + + if (!dir) { + USB_SET_EP_RX_ADDR(addr, dev->pm_top); + usb_set_ep_rx_bufsize(dev, addr, max_size); + if (callback) { + dev->user_callback_ctr[addr][USB_TRANSACTION_OUT] = + (void *)callback; + } + USB_CLR_EP_RX_DTOG(addr); + USB_SET_EP_RX_STAT(addr, USB_EP_RX_STAT_VALID); + dev->pm_top += max_size; + } +} + +static void stm32f103_endpoints_reset(usbd_device *dev) +{ + int i; + + /* Reset all endpoints. */ + for (i = 1; i < 8; i++) { + USB_SET_EP_TX_STAT(i, USB_EP_TX_STAT_DISABLED); + USB_SET_EP_RX_STAT(i, USB_EP_RX_STAT_DISABLED); + } + dev->pm_top = 0x40 + (2 * dev->desc->bMaxPacketSize0); +} + +static void stm32f103_ep_stall_set(usbd_device *dev, uint8_t addr, + uint8_t stall) +{ + (void)dev; + if (addr == 0) { + USB_SET_EP_TX_STAT(addr, stall ? USB_EP_TX_STAT_STALL : + USB_EP_TX_STAT_NAK); + } + + if (addr & 0x80) { + addr &= 0x7F; + + USB_SET_EP_TX_STAT(addr, stall ? USB_EP_TX_STAT_STALL : + USB_EP_TX_STAT_NAK); + + /* Reset to DATA0 if clearing stall condition. */ + if (!stall) { + USB_CLR_EP_TX_DTOG(addr); + } + } else { + /* Reset to DATA0 if clearing stall condition. */ + if (!stall) { + USB_CLR_EP_RX_DTOG(addr); + } + + USB_SET_EP_RX_STAT(addr, stall ? USB_EP_RX_STAT_STALL : + USB_EP_RX_STAT_VALID); + } +} + +static uint8_t stm32f103_ep_stall_get(usbd_device *dev, uint8_t addr) +{ + (void)dev; + if (addr & 0x80) { + if ((*USB_EP_REG(addr & 0x7F) & USB_EP_TX_STAT) == + USB_EP_TX_STAT_STALL) { + return 1; + } + } else { + if ((*USB_EP_REG(addr) & USB_EP_RX_STAT) == + USB_EP_RX_STAT_STALL) { + return 1; + } + } + return 0; +} + +static void stm32f103_ep_nak_set(usbd_device *dev, uint8_t addr, uint8_t nak) +{ + (void)dev; + /* It does not make sence to force NAK on IN endpoints. */ + if (addr & 0x80) { + return; + } + + force_nak[addr] = nak; + + if (nak) { + USB_SET_EP_RX_STAT(addr, USB_EP_RX_STAT_NAK); + } else { + USB_SET_EP_RX_STAT(addr, USB_EP_RX_STAT_VALID); + } +} + +/** + * Copy a data buffer to packet memory. + * + * @param vPM Destination pointer into packet memory. + * @param buf Source pointer to data buffer. + * @param len Number of bytes to copy. + */ +static void usb_copy_to_pm(volatile void *vPM, const void *buf, uint16_t len) +{ + const uint16_t *lbuf = buf; + volatile uint16_t *PM = vPM; + + for (len = (len + 1) >> 1; len; PM += 2, lbuf++, len--) { + *PM = *lbuf; + } +} + +static uint16_t stm32f103_ep_write_packet(usbd_device *dev, uint8_t addr, + const void *buf, uint16_t len) +{ + (void)dev; + addr &= 0x7F; + + if ((*USB_EP_REG(addr) & USB_EP_TX_STAT) == USB_EP_TX_STAT_VALID) { + return 0; + } + + usb_copy_to_pm(USB_GET_EP_TX_BUFF(addr), buf, len); + USB_SET_EP_TX_COUNT(addr, len); + USB_SET_EP_TX_STAT(addr, USB_EP_TX_STAT_VALID); + + return len; +} + +/** + * Copy a data buffer from packet memory. + * + * @param buf Source pointer to data buffer. + * @param vPM Destination pointer into packet memory. + * @param len Number of bytes to copy. + */ +static void usb_copy_from_pm(void *buf, const volatile void *vPM, uint16_t len) +{ + uint16_t *lbuf = buf; + const volatile uint16_t *PM = vPM; + uint8_t odd = len & 1; + + for (len >>= 1; len; PM += 2, lbuf++, len--) { + *lbuf = *PM; + } + + if (odd) { + *(uint8_t *) lbuf = *(uint8_t *) PM; + } +} + +static uint16_t stm32f103_ep_read_packet(usbd_device *dev, uint8_t addr, + void *buf, uint16_t len) +{ + (void)dev; + if ((*USB_EP_REG(addr) & USB_EP_RX_STAT) == USB_EP_RX_STAT_VALID) { + return 0; + } + + len = MIN(USB_GET_EP_RX_COUNT(addr) & 0x3ff, len); + usb_copy_from_pm(buf, USB_GET_EP_RX_BUFF(addr), len); + USB_CLR_EP_RX_CTR(addr); + + if (!force_nak[addr]) { + USB_SET_EP_RX_STAT(addr, USB_EP_RX_STAT_VALID); + } + + return len; +} + +static void stm32f103_poll(usbd_device *dev) +{ + uint16_t istr = *USB_ISTR_REG; + + if (istr & USB_ISTR_RESET) { + dev->pm_top = 0x40; + _usbd_reset(dev); + USB_CLR_ISTR_RESET(); + return; + } + + if (istr & USB_ISTR_CTR) { + uint8_t ep = istr & USB_ISTR_EP_ID; + uint8_t type = (istr & USB_ISTR_DIR) ? 1 : 0; + + if (type) { /* OUT or SETUP transaction */ + type += (*USB_EP_REG(ep) & USB_EP_SETUP) ? 1 : 0; + } else { /* IN transaction */ + USB_CLR_EP_TX_CTR(ep); + } + + if (dev->user_callback_ctr[ep][type]) { + dev->user_callback_ctr[ep][type] (dev, ep); + } else { + USB_CLR_EP_RX_CTR(ep); + } + } + + if (istr & USB_ISTR_SUSP) { + USB_CLR_ISTR_SUSP(); + if (dev->user_callback_suspend) { + dev->user_callback_suspend(); + } + } + + if (istr & USB_ISTR_WKUP) { + USB_CLR_ISTR_WKUP(); + if (dev->user_callback_resume) { + dev->user_callback_resume(); + } + } + + if (istr & USB_ISTR_SOF) { + if (dev->user_callback_sof) { + dev->user_callback_sof(); + } + USB_CLR_ISTR_SOF(); + } +} -- cgit v1.2.3