summaryrefslogtreecommitdiffstats
path: root/libopencm3/lib/usb
diff options
context:
space:
mode:
Diffstat (limited to 'libopencm3/lib/usb')
-rw-r--r--libopencm3/lib/usb/usb.c175
-rw-r--r--libopencm3/lib/usb/usb_control.c288
-rw-r--r--libopencm3/lib/usb/usb_f103.c346
-rw-r--r--libopencm3/lib/usb/usb_f107.c91
-rw-r--r--libopencm3/lib/usb/usb_f207.c91
-rw-r--r--libopencm3/lib/usb/usb_fx07_common.c338
-rw-r--r--libopencm3/lib/usb/usb_fx07_common.h39
-rw-r--r--libopencm3/lib/usb/usb_msc.c814
-rw-r--r--libopencm3/lib/usb/usb_private.h163
-rw-r--r--libopencm3/lib/usb/usb_standard.c532
10 files changed, 2877 insertions, 0 deletions
diff --git a/libopencm3/lib/usb/usb.c b/libopencm3/lib/usb/usb.c
new file mode 100644
index 0000000..7863736
--- /dev/null
+++ b/libopencm3/lib/usb/usb.c
@@ -0,0 +1,175 @@
+/** @defgroup usb_drivers_file Generic USB Drivers
+
+@ingroup USB
+
+@brief <b>Generic USB Drivers</b>
+
+@version 1.0.0
+
+@author @htmlonly &copy; @endhtmlonly 2010
+Gareth McMullin <gareth@blacksphere.co.nz>
+
+@date 10 March 2013
+
+LGPL License Terms @ref lgpl_license
+*/
+
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/**@{*/
+
+#include <string.h>
+#include <libopencm3/usb/usbd.h>
+#include "usb_private.h"
+
+/**
+ * Main initialization entry point.
+ *
+ * Initialize the USB firmware library to implement the USB device described
+ * by the descriptors provided.
+ *
+ * It is required that the 48MHz USB clock is already available.
+ *
+ * @param driver TODO
+ * @param dev Pointer to USB device descriptor. This must not be changed while
+ * the device is in use.
+ * @param conf Pointer to array of USB configuration descriptors. These must
+ * not be changed while the device is in use. The length of this
+ * array is determined by the bNumConfigurations field in the
+ * device descriptor.
+ * @param strings TODO
+ * @param control_buffer Pointer to array that would hold the data
+ * received during control requests with DATA
+ * stage
+ * @param control_buffer_size Size of control_buffer
+ * @return Zero on success (currently cannot fail).
+ */
+usbd_device *usbd_init(const usbd_driver *driver,
+ const struct usb_device_descriptor *dev,
+ const struct usb_config_descriptor *conf,
+ const char **strings, int num_strings,
+ uint8_t *control_buffer, uint16_t control_buffer_size)
+{
+ usbd_device *usbd_dev;
+
+ usbd_dev = driver->init();
+
+ usbd_dev->driver = driver;
+ usbd_dev->desc = dev;
+ usbd_dev->config = conf;
+ usbd_dev->strings = strings;
+ usbd_dev->num_strings = num_strings;
+ usbd_dev->ctrl_buf = control_buffer;
+ usbd_dev->ctrl_buf_len = control_buffer_size;
+
+ usbd_dev->user_callback_ctr[0][USB_TRANSACTION_SETUP] =
+ _usbd_control_setup;
+ usbd_dev->user_callback_ctr[0][USB_TRANSACTION_OUT] =
+ _usbd_control_out;
+ usbd_dev->user_callback_ctr[0][USB_TRANSACTION_IN] =
+ _usbd_control_in;
+
+ return usbd_dev;
+}
+
+void usbd_register_reset_callback(usbd_device *usbd_dev, void (*callback)(void))
+{
+ usbd_dev->user_callback_reset = callback;
+}
+
+void usbd_register_suspend_callback(usbd_device *usbd_dev,
+ void (*callback)(void))
+{
+ usbd_dev->user_callback_suspend = callback;
+}
+
+void usbd_register_resume_callback(usbd_device *usbd_dev,
+ void (*callback)(void))
+{
+ usbd_dev->user_callback_resume = callback;
+}
+
+void usbd_register_sof_callback(usbd_device *usbd_dev, void (*callback)(void))
+{
+ usbd_dev->user_callback_sof = callback;
+}
+
+void _usbd_reset(usbd_device *usbd_dev)
+{
+ usbd_dev->current_address = 0;
+ usbd_dev->current_config = 0;
+ usbd_ep_setup(usbd_dev, 0, USB_ENDPOINT_ATTR_CONTROL, 64, NULL);
+ usbd_dev->driver->set_address(usbd_dev, 0);
+
+ if (usbd_dev->user_callback_reset) {
+ usbd_dev->user_callback_reset();
+ }
+}
+
+/* Functions to wrap the low-level driver */
+void usbd_poll(usbd_device *usbd_dev)
+{
+ usbd_dev->driver->poll(usbd_dev);
+}
+
+void usbd_disconnect(usbd_device *usbd_dev, bool disconnected)
+{
+ /* not all drivers support disconnection */
+ if (usbd_dev->driver->disconnect) {
+ usbd_dev->driver->disconnect(usbd_dev, disconnected);
+ }
+}
+
+void usbd_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))
+{
+ usbd_dev->driver->ep_setup(usbd_dev, addr, type, max_size, callback);
+}
+
+uint16_t usbd_ep_write_packet(usbd_device *usbd_dev, uint8_t addr,
+ const void *buf, uint16_t len)
+{
+ return usbd_dev->driver->ep_write_packet(usbd_dev, addr, buf, len);
+}
+
+uint16_t usbd_ep_read_packet(usbd_device *usbd_dev, uint8_t addr, void *buf,
+ uint16_t len)
+{
+ return usbd_dev->driver->ep_read_packet(usbd_dev, addr, buf, len);
+}
+
+void usbd_ep_stall_set(usbd_device *usbd_dev, uint8_t addr, uint8_t stall)
+{
+ usbd_dev->driver->ep_stall_set(usbd_dev, addr, stall);
+}
+
+uint8_t usbd_ep_stall_get(usbd_device *usbd_dev, uint8_t addr)
+{
+ return usbd_dev->driver->ep_stall_get(usbd_dev, addr);
+}
+
+void usbd_ep_nak_set(usbd_device *usbd_dev, uint8_t addr, uint8_t nak)
+{
+ usbd_dev->driver->ep_nak_set(usbd_dev, addr, nak);
+}
+
+/**@}*/
+
diff --git a/libopencm3/lib/usb/usb_control.c b/libopencm3/lib/usb/usb_control.c
new file mode 100644
index 0000000..1b87057
--- /dev/null
+++ b/libopencm3/lib/usb/usb_control.c
@@ -0,0 +1,288 @@
+/** @defgroup usb_control_file Generic USB Control Requests
+
+@ingroup USB
+
+@brief <b>Generic USB Control Requests</b>
+
+@version 1.0.0
+
+@author @htmlonly &copy; @endhtmlonly 2010
+Gareth McMullin <gareth@blacksphere.co.nz>
+
+@date 10 March 2013
+
+LGPL License Terms @ref lgpl_license
+*/
+
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/**@{*/
+
+#include <stdlib.h>
+#include <libopencm3/usb/usbd.h>
+#include "usb_private.h"
+
+/*
+ * According to the USB 2.0 specification, section 8.5.3, when a control
+ * transfer is stalled, the pipe becomes idle. We provide one utility to stall
+ * a transaction to reduce boilerplate code.
+ */
+static void stall_transaction(usbd_device *usbd_dev)
+{
+ usbd_ep_stall_set(usbd_dev, 0, 1);
+ usbd_dev->control_state.state = IDLE;
+}
+
+/* Register application callback function for handling USB control requests. */
+int usbd_register_control_callback(usbd_device *usbd_dev, uint8_t type,
+ uint8_t type_mask,
+ usbd_control_callback callback)
+{
+ int i;
+
+ for (i = 0; i < MAX_USER_CONTROL_CALLBACK; i++) {
+ if (usbd_dev->user_control_callback[i].cb) {
+ continue;
+ }
+
+ usbd_dev->user_control_callback[i].type = type;
+ usbd_dev->user_control_callback[i].type_mask = type_mask;
+ usbd_dev->user_control_callback[i].cb = callback;
+ return 0;
+ }
+
+ return -1;
+}
+
+static void usb_control_send_chunk(usbd_device *usbd_dev)
+{
+ if (usbd_dev->desc->bMaxPacketSize0 <
+ usbd_dev->control_state.ctrl_len) {
+ /* Data stage, normal transmission */
+ usbd_ep_write_packet(usbd_dev, 0,
+ usbd_dev->control_state.ctrl_buf,
+ usbd_dev->desc->bMaxPacketSize0);
+ usbd_dev->control_state.state = DATA_IN;
+ usbd_dev->control_state.ctrl_buf +=
+ usbd_dev->desc->bMaxPacketSize0;
+ usbd_dev->control_state.ctrl_len -=
+ usbd_dev->desc->bMaxPacketSize0;
+ } else {
+ /* Data stage, end of transmission */
+ usbd_ep_write_packet(usbd_dev, 0,
+ usbd_dev->control_state.ctrl_buf,
+ usbd_dev->control_state.ctrl_len);
+ usbd_dev->control_state.state = LAST_DATA_IN;
+ usbd_dev->control_state.ctrl_len = 0;
+ usbd_dev->control_state.ctrl_buf = NULL;
+ }
+}
+
+static int usb_control_recv_chunk(usbd_device *usbd_dev)
+{
+ uint16_t packetsize = MIN(usbd_dev->desc->bMaxPacketSize0,
+ usbd_dev->control_state.req.wLength -
+ usbd_dev->control_state.ctrl_len);
+ uint16_t size = usbd_ep_read_packet(usbd_dev, 0,
+ usbd_dev->control_state.ctrl_buf +
+ usbd_dev->control_state.ctrl_len,
+ packetsize);
+
+ if (size != packetsize) {
+ stall_transaction(usbd_dev);
+ return -1;
+ }
+
+ usbd_dev->control_state.ctrl_len += size;
+
+ return packetsize;
+}
+
+static int usb_control_request_dispatch(usbd_device *usbd_dev,
+ struct usb_setup_data *req)
+{
+ int i, result = 0;
+ struct user_control_callback *cb = usbd_dev->user_control_callback;
+
+ /* Call user command hook function. */
+ for (i = 0; i < MAX_USER_CONTROL_CALLBACK; i++) {
+ if (cb[i].cb == NULL) {
+ break;
+ }
+
+ if ((req->bmRequestType & cb[i].type_mask) == cb[i].type) {
+ result = cb[i].cb(usbd_dev, req,
+ &(usbd_dev->control_state.ctrl_buf),
+ &(usbd_dev->control_state.ctrl_len),
+ &(usbd_dev->control_state.complete));
+ if (result == USBD_REQ_HANDLED ||
+ result == USBD_REQ_NOTSUPP) {
+ return result;
+ }
+ }
+ }
+
+ /* Try standard request if not already handled. */
+ return _usbd_standard_request(usbd_dev, req,
+ &(usbd_dev->control_state.ctrl_buf),
+ &(usbd_dev->control_state.ctrl_len));
+}
+
+/* Handle commands and read requests. */
+static void usb_control_setup_read(usbd_device *usbd_dev,
+ struct usb_setup_data *req)
+{
+ usbd_dev->control_state.ctrl_buf = usbd_dev->ctrl_buf;
+ usbd_dev->control_state.ctrl_len = req->wLength;
+
+ if (usb_control_request_dispatch(usbd_dev, req)) {
+ if (usbd_dev->control_state.ctrl_len) {
+ /* Go to data out stage if handled. */
+ usb_control_send_chunk(usbd_dev);
+ } else {
+ /* Go to status stage if handled. */
+ usbd_ep_write_packet(usbd_dev, 0, NULL, 0);
+ usbd_dev->control_state.state = STATUS_IN;
+ }
+ } else {
+ /* Stall endpoint on failure. */
+ stall_transaction(usbd_dev);
+ }
+}
+
+static void usb_control_setup_write(usbd_device *usbd_dev,
+ struct usb_setup_data *req)
+{
+ if (req->wLength > usbd_dev->ctrl_buf_len) {
+ stall_transaction(usbd_dev);
+ return;
+ }
+
+ /* Buffer into which to write received data. */
+ usbd_dev->control_state.ctrl_buf = usbd_dev->ctrl_buf;
+ usbd_dev->control_state.ctrl_len = 0;
+ /* Wait for DATA OUT stage. */
+ if (req->wLength > usbd_dev->desc->bMaxPacketSize0) {
+ usbd_dev->control_state.state = DATA_OUT;
+ } else {
+ usbd_dev->control_state.state = LAST_DATA_OUT;
+ }
+}
+
+/* Do not appear to belong to the API, so are omitted from docs */
+/**@}*/
+
+void _usbd_control_setup(usbd_device *usbd_dev, uint8_t ea)
+{
+ struct usb_setup_data *req = &usbd_dev->control_state.req;
+ (void)ea;
+
+ usbd_dev->control_state.complete = NULL;
+
+ if (usbd_ep_read_packet(usbd_dev, 0, req, 8) != 8) {
+ stall_transaction(usbd_dev);
+ return;
+ }
+
+ if (req->wLength == 0) {
+ usb_control_setup_read(usbd_dev, req);
+ } else if (req->bmRequestType & 0x80) {
+ usb_control_setup_read(usbd_dev, req);
+ } else {
+ usb_control_setup_write(usbd_dev, req);
+ }
+}
+
+void _usbd_control_out(usbd_device *usbd_dev, uint8_t ea)
+{
+ (void)ea;
+
+ switch (usbd_dev->control_state.state) {
+ case DATA_OUT:
+ if (usb_control_recv_chunk(usbd_dev) < 0) {
+ break;
+ }
+ if ((usbd_dev->control_state.req.wLength -
+ usbd_dev->control_state.ctrl_len) <=
+ usbd_dev->desc->bMaxPacketSize0) {
+ usbd_dev->control_state.state = LAST_DATA_OUT;
+ }
+ break;
+ case LAST_DATA_OUT:
+ if (usb_control_recv_chunk(usbd_dev) < 0) {
+ break;
+ }
+ /*
+ * We have now received the full data payload.
+ * Invoke callback to process.
+ */
+ if (usb_control_request_dispatch(usbd_dev,
+ &(usbd_dev->control_state.req))) {
+ /* Got to status stage on success. */
+ usbd_ep_write_packet(usbd_dev, 0, NULL, 0);
+ usbd_dev->control_state.state = STATUS_IN;
+ } else {
+ stall_transaction(usbd_dev);
+ }
+ break;
+ case STATUS_OUT:
+ usbd_ep_read_packet(usbd_dev, 0, NULL, 0);
+ usbd_dev->control_state.state = IDLE;
+ if (usbd_dev->control_state.complete) {
+ usbd_dev->control_state.complete(usbd_dev,
+ &(usbd_dev->control_state.req));
+ }
+ usbd_dev->control_state.complete = NULL;
+ break;
+ default:
+ stall_transaction(usbd_dev);
+ }
+}
+
+void _usbd_control_in(usbd_device *usbd_dev, uint8_t ea)
+{
+ (void)ea;
+ struct usb_setup_data *req = &(usbd_dev->control_state.req);
+
+ switch (usbd_dev->control_state.state) {
+ case DATA_IN:
+ usb_control_send_chunk(usbd_dev);
+ break;
+ case LAST_DATA_IN:
+ usbd_dev->control_state.state = STATUS_OUT;
+ break;
+ case STATUS_IN:
+ if (usbd_dev->control_state.complete) {
+ usbd_dev->control_state.complete(usbd_dev,
+ &(usbd_dev->control_state.req));
+ }
+
+ /* Exception: Handle SET ADDRESS function here... */
+ if ((req->bmRequestType == 0) &&
+ (req->bRequest == USB_REQ_SET_ADDRESS)) {
+ usbd_dev->driver->set_address(usbd_dev, req->wValue);
+ }
+ usbd_dev->control_state.state = IDLE;
+ break;
+ default:
+ stall_transaction(usbd_dev);
+ }
+}
+
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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopencm3/cm3/common.h>
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/tools.h>
+#include <libopencm3/stm32/usb.h>
+#include <libopencm3/usb/usbd.h>
+#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();
+ }
+}
diff --git a/libopencm3/lib/usb/usb_f107.c b/libopencm3/lib/usb/usb_f107.c
new file mode 100644
index 0000000..32aecab
--- /dev/null
+++ b/libopencm3/lib/usb/usb_f107.c
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <libopencm3/cm3/common.h>
+#include <libopencm3/stm32/tools.h>
+#include <libopencm3/stm32/otg_fs.h>
+#include <libopencm3/usb/usbd.h>
+#include "usb_private.h"
+#include "usb_fx07_common.h"
+
+/* Receive FIFO size in 32-bit words. */
+#define RX_FIFO_SIZE 128
+
+static usbd_device *stm32f107_usbd_init(void);
+
+static struct _usbd_device usbd_dev;
+
+const struct _usbd_driver stm32f107_usb_driver = {
+ .init = stm32f107_usbd_init,
+ .set_address = stm32fx07_set_address,
+ .ep_setup = stm32fx07_ep_setup,
+ .ep_reset = stm32fx07_endpoints_reset,
+ .ep_stall_set = stm32fx07_ep_stall_set,
+ .ep_stall_get = stm32fx07_ep_stall_get,
+ .ep_nak_set = stm32fx07_ep_nak_set,
+ .ep_write_packet = stm32fx07_ep_write_packet,
+ .ep_read_packet = stm32fx07_ep_read_packet,
+ .poll = stm32fx07_poll,
+ .disconnect = stm32fx07_disconnect,
+ .base_address = USB_OTG_FS_BASE,
+ .set_address_before_status = 1,
+ .rx_fifo_size = RX_FIFO_SIZE,
+};
+
+/** Initialize the USB device controller hardware of the STM32. */
+static usbd_device *stm32f107_usbd_init(void)
+{
+ OTG_FS_GINTSTS = OTG_FS_GINTSTS_MMIS;
+
+ OTG_FS_GUSBCFG |= OTG_FS_GUSBCFG_PHYSEL;
+ /* Enable VBUS sensing in device mode and power down the PHY. */
+ OTG_FS_GCCFG |= OTG_FS_GCCFG_VBUSBSEN | OTG_FS_GCCFG_PWRDWN;
+
+ /* Wait for AHB idle. */
+ while (!(OTG_FS_GRSTCTL & OTG_FS_GRSTCTL_AHBIDL));
+ /* Do core soft reset. */
+ OTG_FS_GRSTCTL |= OTG_FS_GRSTCTL_CSRST;
+ while (OTG_FS_GRSTCTL & OTG_FS_GRSTCTL_CSRST);
+
+ /* Force peripheral only mode. */
+ OTG_FS_GUSBCFG |= OTG_FS_GUSBCFG_FDMOD | OTG_FS_GUSBCFG_TRDT_MASK;
+
+ /* Full speed device. */
+ OTG_FS_DCFG |= OTG_FS_DCFG_DSPD;
+
+ /* Restart the PHY clock. */
+ OTG_FS_PCGCCTL = 0;
+
+ OTG_FS_GRXFSIZ = stm32f107_usb_driver.rx_fifo_size;
+ usbd_dev.fifo_mem_top = stm32f107_usb_driver.rx_fifo_size;
+
+ /* Unmask interrupts for TX and RX. */
+ OTG_FS_GAHBCFG |= OTG_FS_GAHBCFG_GINT;
+ OTG_FS_GINTMSK = OTG_FS_GINTMSK_ENUMDNEM |
+ OTG_FS_GINTMSK_RXFLVLM |
+ OTG_FS_GINTMSK_IEPINT |
+ OTG_FS_GINTMSK_USBSUSPM |
+ OTG_FS_GINTMSK_WUIM |
+ OTG_FS_GINTMSK_SOFM;
+ OTG_FS_DAINTMSK = 0xF;
+ OTG_FS_DIEPMSK = OTG_FS_DIEPMSK_XFRCM;
+
+ return &usbd_dev;
+}
diff --git a/libopencm3/lib/usb/usb_f207.c b/libopencm3/lib/usb/usb_f207.c
new file mode 100644
index 0000000..b27d3cd
--- /dev/null
+++ b/libopencm3/lib/usb/usb_f207.c
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <libopencm3/cm3/common.h>
+#include <libopencm3/stm32/tools.h>
+#include <libopencm3/stm32/otg_hs.h>
+#include <libopencm3/usb/usbd.h>
+#include "usb_private.h"
+#include "usb_fx07_common.h"
+
+/* Receive FIFO size in 32-bit words. */
+#define RX_FIFO_SIZE 512
+
+static usbd_device *stm32f207_usbd_init(void);
+
+static struct _usbd_device usbd_dev;
+
+const struct _usbd_driver stm32f207_usb_driver = {
+ .init = stm32f207_usbd_init,
+ .set_address = stm32fx07_set_address,
+ .ep_setup = stm32fx07_ep_setup,
+ .ep_reset = stm32fx07_endpoints_reset,
+ .ep_stall_set = stm32fx07_ep_stall_set,
+ .ep_stall_get = stm32fx07_ep_stall_get,
+ .ep_nak_set = stm32fx07_ep_nak_set,
+ .ep_write_packet = stm32fx07_ep_write_packet,
+ .ep_read_packet = stm32fx07_ep_read_packet,
+ .poll = stm32fx07_poll,
+ .disconnect = stm32fx07_disconnect,
+ .base_address = USB_OTG_HS_BASE,
+ .set_address_before_status = 1,
+ .rx_fifo_size = RX_FIFO_SIZE,
+};
+
+/** Initialize the USB device controller hardware of the STM32. */
+static usbd_device *stm32f207_usbd_init(void)
+{
+ OTG_HS_GINTSTS = OTG_HS_GINTSTS_MMIS;
+
+ OTG_HS_GUSBCFG |= OTG_HS_GUSBCFG_PHYSEL;
+ /* Enable VBUS sensing in device mode and power down the PHY. */
+ OTG_HS_GCCFG |= OTG_HS_GCCFG_VBUSBSEN | OTG_HS_GCCFG_PWRDWN;
+
+ /* Wait for AHB idle. */
+ while (!(OTG_HS_GRSTCTL & OTG_HS_GRSTCTL_AHBIDL));
+ /* Do core soft reset. */
+ OTG_HS_GRSTCTL |= OTG_HS_GRSTCTL_CSRST;
+ while (OTG_HS_GRSTCTL & OTG_HS_GRSTCTL_CSRST);
+
+ /* Force peripheral only mode. */
+ OTG_HS_GUSBCFG |= OTG_HS_GUSBCFG_FDMOD | OTG_HS_GUSBCFG_TRDT_MASK;
+
+ /* Full speed device. */
+ OTG_HS_DCFG |= OTG_HS_DCFG_DSPD;
+
+ /* Restart the PHY clock. */
+ OTG_HS_PCGCCTL = 0;
+
+ OTG_HS_GRXFSIZ = stm32f207_usb_driver.rx_fifo_size;
+ usbd_dev.fifo_mem_top = stm32f207_usb_driver.rx_fifo_size;
+
+ /* Unmask interrupts for TX and RX. */
+ OTG_HS_GAHBCFG |= OTG_HS_GAHBCFG_GINT;
+ OTG_HS_GINTMSK = OTG_HS_GINTMSK_ENUMDNEM |
+ OTG_HS_GINTMSK_RXFLVLM |
+ OTG_HS_GINTMSK_IEPINT |
+ OTG_HS_GINTMSK_USBSUSPM |
+ OTG_HS_GINTMSK_WUIM |
+ OTG_HS_GINTMSK_SOFM;
+ OTG_HS_DAINTMSK = 0xF;
+ OTG_HS_DIEPMSK = OTG_HS_DIEPMSK_XFRCM;
+
+ return &usbd_dev;
+}
diff --git a/libopencm3/lib/usb/usb_fx07_common.c b/libopencm3/lib/usb/usb_fx07_common.c
new file mode 100644
index 0000000..c54c7a3
--- /dev/null
+++ b/libopencm3/lib/usb/usb_fx07_common.c
@@ -0,0 +1,338 @@
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <libopencm3/cm3/common.h>
+#include <libopencm3/stm32/tools.h>
+#include <libopencm3/stm32/otg_fs.h>
+#include <libopencm3/stm32/otg_hs.h>
+#include <libopencm3/usb/usbd.h>
+#include "usb_private.h"
+#include "usb_fx07_common.h"
+
+/* The FS core and the HS core have the same register layout.
+ * As the code can be used on both cores, the registers offset is modified
+ * according to the selected cores base address. */
+#define dev_base_address (usbd_dev->driver->base_address)
+#define REBASE(x) MMIO32((x) + (dev_base_address))
+#define REBASE_FIFO(x) (&MMIO32((dev_base_address) + (OTG_FIFO(x))))
+
+void stm32fx07_set_address(usbd_device *usbd_dev, uint8_t addr)
+{
+ REBASE(OTG_DCFG) = (REBASE(OTG_DCFG) & ~OTG_FS_DCFG_DAD) | (addr << 4);
+}
+
+void stm32fx07_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))
+{
+ /*
+ * Configure endpoint address and type. Allocate FIFO memory for
+ * endpoint. Install callback funciton.
+ */
+ uint8_t dir = addr & 0x80;
+ addr &= 0x7f;
+
+ if (addr == 0) { /* For the default control endpoint */
+ /* Configure IN part. */
+ if (max_size >= 64) {
+ REBASE(OTG_DIEPCTL0) = OTG_FS_DIEPCTL0_MPSIZ_64;
+ } else if (max_size >= 32) {
+ REBASE(OTG_DIEPCTL0) = OTG_FS_DIEPCTL0_MPSIZ_32;
+ } else if (max_size >= 16) {
+ REBASE(OTG_DIEPCTL0) = OTG_FS_DIEPCTL0_MPSIZ_16;
+ } else {
+ REBASE(OTG_DIEPCTL0) = OTG_FS_DIEPCTL0_MPSIZ_8;
+ }
+
+ REBASE(OTG_DIEPTSIZ0) =
+ (max_size & OTG_FS_DIEPSIZ0_XFRSIZ_MASK);
+ REBASE(OTG_DIEPCTL0) |=
+ OTG_FS_DIEPCTL0_EPENA | OTG_FS_DIEPCTL0_SNAK;
+
+ /* Configure OUT part. */
+ usbd_dev->doeptsiz[0] = OTG_FS_DIEPSIZ0_STUPCNT_1 |
+ OTG_FS_DIEPSIZ0_PKTCNT |
+ (max_size & OTG_FS_DIEPSIZ0_XFRSIZ_MASK);
+ REBASE(OTG_DOEPTSIZ(0)) = usbd_dev->doeptsiz[0];
+ REBASE(OTG_DOEPCTL(0)) |=
+ OTG_FS_DOEPCTL0_EPENA | OTG_FS_DIEPCTL0_SNAK;
+
+ REBASE(OTG_GNPTXFSIZ) = ((max_size / 4) << 16) |
+ usbd_dev->driver->rx_fifo_size;
+ usbd_dev->fifo_mem_top += max_size / 4;
+ usbd_dev->fifo_mem_top_ep0 = usbd_dev->fifo_mem_top;
+
+ return;
+ }
+
+ if (dir) {
+ REBASE(OTG_DIEPTXF(addr)) = ((max_size / 4) << 16) |
+ usbd_dev->fifo_mem_top;
+ usbd_dev->fifo_mem_top += max_size / 4;
+
+ REBASE(OTG_DIEPTSIZ(addr)) =
+ (max_size & OTG_FS_DIEPSIZ0_XFRSIZ_MASK);
+ REBASE(OTG_DIEPCTL(addr)) |=
+ OTG_FS_DIEPCTL0_EPENA | OTG_FS_DIEPCTL0_SNAK | (type << 18)
+ | OTG_FS_DIEPCTL0_USBAEP | OTG_FS_DIEPCTLX_SD0PID
+ | (addr << 22) | max_size;
+
+ if (callback) {
+ usbd_dev->user_callback_ctr[addr][USB_TRANSACTION_IN] =
+ (void *)callback;
+ }
+ }
+
+ if (!dir) {
+ usbd_dev->doeptsiz[addr] = OTG_FS_DIEPSIZ0_PKTCNT |
+ (max_size & OTG_FS_DIEPSIZ0_XFRSIZ_MASK);
+ REBASE(OTG_DOEPTSIZ(addr)) = usbd_dev->doeptsiz[addr];
+ REBASE(OTG_DOEPCTL(addr)) |= OTG_FS_DOEPCTL0_EPENA |
+ OTG_FS_DOEPCTL0_USBAEP | OTG_FS_DIEPCTL0_CNAK |
+ OTG_FS_DOEPCTLX_SD0PID | (type << 18) | max_size;
+
+ if (callback) {
+ usbd_dev->user_callback_ctr[addr][USB_TRANSACTION_OUT] =
+ (void *)callback;
+ }
+ }
+}
+
+void stm32fx07_endpoints_reset(usbd_device *usbd_dev)
+{
+ /* The core resets the endpoints automatically on reset. */
+ usbd_dev->fifo_mem_top = usbd_dev->fifo_mem_top_ep0;
+}
+
+void stm32fx07_ep_stall_set(usbd_device *usbd_dev, uint8_t addr, uint8_t stall)
+{
+ if (addr == 0) {
+ if (stall) {
+ REBASE(OTG_DIEPCTL(addr)) |= OTG_FS_DIEPCTL0_STALL;
+ } else {
+ REBASE(OTG_DIEPCTL(addr)) &= ~OTG_FS_DIEPCTL0_STALL;
+ }
+ }
+
+ if (addr & 0x80) {
+ addr &= 0x7F;
+
+ if (stall) {
+ REBASE(OTG_DIEPCTL(addr)) |= OTG_FS_DIEPCTL0_STALL;
+ } else {
+ REBASE(OTG_DIEPCTL(addr)) &= ~OTG_FS_DIEPCTL0_STALL;
+ REBASE(OTG_DIEPCTL(addr)) |= OTG_FS_DIEPCTLX_SD0PID;
+ }
+ } else {
+ if (stall) {
+ REBASE(OTG_DOEPCTL(addr)) |= OTG_FS_DOEPCTL0_STALL;
+ } else {
+ REBASE(OTG_DOEPCTL(addr)) &= ~OTG_FS_DOEPCTL0_STALL;
+ REBASE(OTG_DOEPCTL(addr)) |= OTG_FS_DOEPCTLX_SD0PID;
+ }
+ }
+}
+
+uint8_t stm32fx07_ep_stall_get(usbd_device *usbd_dev, uint8_t addr)
+{
+ /* Return non-zero if STALL set. */
+ if (addr & 0x80) {
+ return (REBASE(OTG_DIEPCTL(addr & 0x7f)) &
+ OTG_FS_DIEPCTL0_STALL) ? 1 : 0;
+ } else {
+ return (REBASE(OTG_DOEPCTL(addr)) &
+ OTG_FS_DOEPCTL0_STALL) ? 1 : 0;
+ }
+}
+
+void stm32fx07_ep_nak_set(usbd_device *usbd_dev, uint8_t addr, uint8_t nak)
+{
+ /* It does not make sence to force NAK on IN endpoints. */
+ if (addr & 0x80) {
+ return;
+ }
+
+ usbd_dev->force_nak[addr] = nak;
+
+ if (nak) {
+ REBASE(OTG_DOEPCTL(addr)) |= OTG_FS_DOEPCTL0_SNAK;
+ } else {
+ REBASE(OTG_DOEPCTL(addr)) |= OTG_FS_DOEPCTL0_CNAK;
+ }
+}
+
+uint16_t stm32fx07_ep_write_packet(usbd_device *usbd_dev, uint8_t addr,
+ const void *buf, uint16_t len)
+{
+ const uint32_t *buf32 = buf;
+ int i;
+
+ addr &= 0x7F;
+
+ /* Return if endpoint is already enabled. */
+ if (REBASE(OTG_DIEPTSIZ(addr)) & OTG_FS_DIEPSIZ0_PKTCNT) {
+ return 0;
+ }
+
+ /* Enable endpoint for transmission. */
+ REBASE(OTG_DIEPTSIZ(addr)) = OTG_FS_DIEPSIZ0_PKTCNT | len;
+ REBASE(OTG_DIEPCTL(addr)) |= OTG_FS_DIEPCTL0_EPENA |
+ OTG_FS_DIEPCTL0_CNAK;
+ volatile uint32_t *fifo = REBASE_FIFO(addr);
+
+ /* Copy buffer to endpoint FIFO, note - memcpy does not work */
+ for (i = len; i > 0; i -= 4) {
+ *fifo++ = *buf32++;
+ }
+
+ return len;
+}
+
+uint16_t stm32fx07_ep_read_packet(usbd_device *usbd_dev, uint8_t addr,
+ void *buf, uint16_t len)
+{
+ int i;
+ uint32_t *buf32 = buf;
+ uint32_t extra;
+
+ len = MIN(len, usbd_dev->rxbcnt);
+ usbd_dev->rxbcnt -= len;
+
+ volatile uint32_t *fifo = REBASE_FIFO(addr);
+ for (i = len; i >= 4; i -= 4) {
+ *buf32++ = *fifo++;
+ }
+
+ if (i) {
+ extra = *fifo++;
+ memcpy(buf32, &extra, i);
+ }
+
+ REBASE(OTG_DOEPTSIZ(addr)) = usbd_dev->doeptsiz[addr];
+ REBASE(OTG_DOEPCTL(addr)) |= OTG_FS_DOEPCTL0_EPENA |
+ (usbd_dev->force_nak[addr] ?
+ OTG_FS_DOEPCTL0_SNAK : OTG_FS_DOEPCTL0_CNAK);
+
+ return len;
+}
+
+void stm32fx07_poll(usbd_device *usbd_dev)
+{
+ /* Read interrupt status register. */
+ uint32_t intsts = REBASE(OTG_GINTSTS);
+ int i;
+
+ if (intsts & OTG_FS_GINTSTS_ENUMDNE) {
+ /* Handle USB RESET condition. */
+ REBASE(OTG_GINTSTS) = OTG_FS_GINTSTS_ENUMDNE;
+ usbd_dev->fifo_mem_top = usbd_dev->driver->rx_fifo_size;
+ _usbd_reset(usbd_dev);
+ return;
+ }
+
+ /* Note: RX and TX handled differently in this device. */
+ if (intsts & OTG_FS_GINTSTS_RXFLVL) {
+ /* Receive FIFO non-empty. */
+ uint32_t rxstsp = REBASE(OTG_GRXSTSP);
+ uint32_t pktsts = rxstsp & OTG_FS_GRXSTSP_PKTSTS_MASK;
+ if ((pktsts != OTG_FS_GRXSTSP_PKTSTS_OUT) &&
+ (pktsts != OTG_FS_GRXSTSP_PKTSTS_SETUP)) {
+ return;
+ }
+
+ uint8_t ep = rxstsp & OTG_FS_GRXSTSP_EPNUM_MASK;
+ uint8_t type;
+ if (pktsts == OTG_FS_GRXSTSP_PKTSTS_SETUP) {
+ type = USB_TRANSACTION_SETUP;
+ } else {
+ type = USB_TRANSACTION_OUT;
+ }
+
+ /* Save packet size for stm32f107_ep_read_packet(). */
+ usbd_dev->rxbcnt = (rxstsp & OTG_FS_GRXSTSP_BCNT_MASK) >> 4;
+
+ /*
+ * FIXME: Why is a delay needed here?
+ * This appears to fix a problem where the first 4 bytes
+ * of the DATA OUT stage of a control transaction are lost.
+ */
+ for (i = 0; i < 1000; i++) {
+ __asm__("nop");
+ }
+
+ if (usbd_dev->user_callback_ctr[ep][type]) {
+ usbd_dev->user_callback_ctr[ep][type] (usbd_dev, ep);
+ }
+
+ /* Discard unread packet data. */
+ for (i = 0; i < usbd_dev->rxbcnt; i += 4) {
+ (void)*REBASE_FIFO(ep);
+ }
+
+ usbd_dev->rxbcnt = 0;
+ }
+
+ /*
+ * There is no global interrupt flag for transmit complete.
+ * The XFRC bit must be checked in each OTG_FS_DIEPINT(x).
+ */
+ for (i = 0; i < 4; i++) { /* Iterate over endpoints. */
+ if (REBASE(OTG_DIEPINT(i)) & OTG_FS_DIEPINTX_XFRC) {
+ /* Transfer complete. */
+ if (usbd_dev->user_callback_ctr[i]
+ [USB_TRANSACTION_IN]) {
+ usbd_dev->user_callback_ctr[i]
+ [USB_TRANSACTION_IN](usbd_dev, i);
+ }
+
+ REBASE(OTG_DIEPINT(i)) = OTG_FS_DIEPINTX_XFRC;
+ }
+ }
+
+ if (intsts & OTG_FS_GINTSTS_USBSUSP) {
+ if (usbd_dev->user_callback_suspend) {
+ usbd_dev->user_callback_suspend();
+ }
+ REBASE(OTG_GINTSTS) = OTG_FS_GINTSTS_USBSUSP;
+ }
+
+ if (intsts & OTG_FS_GINTSTS_WKUPINT) {
+ if (usbd_dev->user_callback_resume) {
+ usbd_dev->user_callback_resume();
+ }
+ REBASE(OTG_GINTSTS) = OTG_FS_GINTSTS_WKUPINT;
+ }
+
+ if (intsts & OTG_FS_GINTSTS_SOF) {
+ if (usbd_dev->user_callback_sof) {
+ usbd_dev->user_callback_sof();
+ }
+ REBASE(OTG_GINTSTS) = OTG_FS_GINTSTS_SOF;
+ }
+}
+
+void stm32fx07_disconnect(usbd_device *usbd_dev, bool disconnected)
+{
+ if (disconnected) {
+ REBASE(OTG_DCTL) |= OTG_FS_DCTL_SDIS;
+ } else {
+ REBASE(OTG_DCTL) &= ~OTG_FS_DCTL_SDIS;
+ }
+}
diff --git a/libopencm3/lib/usb/usb_fx07_common.h b/libopencm3/lib/usb/usb_fx07_common.h
new file mode 100644
index 0000000..31c4030
--- /dev/null
+++ b/libopencm3/lib/usb/usb_fx07_common.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __USB_FX07_COMMON_H_
+#define __USB_FX07_COMMON_H_
+
+void stm32fx07_set_address(usbd_device *usbd_dev, uint8_t addr);
+void stm32fx07_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 stm32fx07_endpoints_reset(usbd_device *usbd_dev);
+void stm32fx07_ep_stall_set(usbd_device *usbd_dev, uint8_t addr, uint8_t stall);
+uint8_t stm32fx07_ep_stall_get(usbd_device *usbd_dev, uint8_t addr);
+void stm32fx07_ep_nak_set(usbd_device *usbd_dev, uint8_t addr, uint8_t nak);
+uint16_t stm32fx07_ep_write_packet(usbd_device *usbd_dev, uint8_t addr,
+ const void *buf, uint16_t len);
+uint16_t stm32fx07_ep_read_packet(usbd_device *usbd_dev, uint8_t addr,
+ void *buf, uint16_t len);
+void stm32fx07_poll(usbd_device *usbd_dev);
+void stm32fx07_disconnect(usbd_device *usbd_dev, bool disconnected);
+
+
+#endif /* __USB_FX07_COMMON_H_ */
diff --git a/libopencm3/lib/usb/usb_msc.c b/libopencm3/lib/usb/usb_msc.c
new file mode 100644
index 0000000..573590d
--- /dev/null
+++ b/libopencm3/lib/usb/usb_msc.c
@@ -0,0 +1,814 @@
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2013 Weston Schmidt <weston_schmidt@alumni.purdue.edu>
+ * Copyright (C) 2013 Pavol Rusnak <stick@gk2.sk>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libopencm3/cm3/common.h>
+#include <libopencm3/usb/usbd.h>
+#include <libopencm3/usb/msc.h>
+#include "usb_private.h"
+
+/* Definitions of Mass Storage Class from:
+ *
+ * (A) "Universal Serial Bus Mass Storage Class Bulk-Only Transport
+ * Revision 1.0"
+ *
+ * (B) "Universal Serial Bus Mass Storage Class Specification Overview
+ * Revision 1.0"
+ */
+
+/* Command Block Wrapper */
+#define CBW_SIGNATURE 0x43425355
+#define CBW_STATUS_SUCCESS 0
+#define CBW_STATUS_FAILED 1
+#define CBW_STATUS_PHASE_ERROR 2
+
+/* Command Status Wrapper */
+#define CSW_SIGNATURE 0x53425355
+#define CSW_STATUS_SUCCESS 0
+#define CSW_STATUS_FAILED 1
+#define CSW_STATUS_PHASE_ERROR 2
+
+/* Implemented SCSI Commands */
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_FORMAT_UNIT 0x04
+#define SCSI_READ_6 0x08
+#define SCSI_WRITE_6 0x0A
+#define SCSI_INQUIRY 0x12
+#define SCSI_MODE_SENSE_6 0x1A
+#define SCSI_SEND_DIAGNOSTIC 0x1D
+#define SCSI_READ_CAPACITY 0x25
+#define SCSI_READ_10 0x28
+
+
+/* Required SCSI Commands */
+
+/* Optional SCSI Commands */
+#define SCSI_REPORT_LUNS 0xA0
+#define SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E
+#define SCSI_MODE_SELECT_6 0x15
+#define SCSI_MODE_SELECT_10 0x55
+#define SCSI_MODE_SENSE_10 0x5A
+#define SCSI_READ_12 0xA8
+#define SCSI_READ_FORMAT_CAPACITIES 0x23
+#define SCSI_READ_TOC_PMA_ATIP 0x43
+#define SCSI_START_STOP_UNIT 0x1B
+#define SCSI_SYNCHRONIZE_CACHE 0x35
+#define SCSI_VERIFY 0x2F
+#define SCSI_WRITE_10 0x2A
+#define SCSI_WRITE_12 0xAA
+
+/* The sense codes */
+enum sbc_sense_key {
+ SBC_SENSE_KEY_NO_SENSE = 0x00,
+ SBC_SENSE_KEY_RECOVERED_ERROR = 0x01,
+ SBC_SENSE_KEY_NOT_READY = 0x02,
+ SBC_SENSE_KEY_MEDIUM_ERROR = 0x03,
+ SBC_SENSE_KEY_HARDWARE_ERROR = 0x04,
+ SBC_SENSE_KEY_ILLEGAL_REQUEST = 0x05,
+ SBC_SENSE_KEY_UNIT_ATTENTION = 0x06,
+ SBC_SENSE_KEY_DATA_PROTECT = 0x07,
+ SBC_SENSE_KEY_BLANK_CHECK = 0x08,
+ SBC_SENSE_KEY_VENDOR_SPECIFIC = 0x09,
+ SBC_SENSE_KEY_COPY_ABORTED = 0x0A,
+ SBC_SENSE_KEY_ABORTED_COMMAND = 0x0B,
+ SBC_SENSE_KEY_VOLUME_OVERFLOW = 0x0D,
+ SBC_SENSE_KEY_MISCOMPARE = 0x0E
+};
+
+enum sbc_asc {
+ SBC_ASC_NO_ADDITIONAL_SENSE_INFORMATION = 0x00,
+ SBC_ASC_PERIPHERAL_DEVICE_WRITE_FAULT = 0x03,
+ SBC_ASC_LOGICAL_UNIT_NOT_READY = 0x04,
+ SBC_ASC_UNRECOVERED_READ_ERROR = 0x11,
+ SBC_ASC_INVALID_COMMAND_OPERATION_CODE = 0x20,
+ SBC_ASC_LBA_OUT_OF_RANGE = 0x21,
+ SBC_ASC_INVALID_FIELD_IN_CDB = 0x24,
+ SBC_ASC_WRITE_PROTECTED = 0x27,
+ SBC_ASC_NOT_READY_TO_READY_CHANGE = 0x28,
+ SBC_ASC_FORMAT_ERROR = 0x31,
+ SBC_ASC_MEDIUM_NOT_PRESENT = 0x3A
+};
+
+enum sbc_ascq {
+ SBC_ASCQ_NA = 0x00,
+ SBC_ASCQ_FORMAT_COMMAND_FAILED = 0x01,
+ SBC_ASCQ_INITIALIZING_COMMAND_REQUIRED = 0x02,
+ SBC_ASCQ_OPERATION_IN_PROGRESS = 0x07
+};
+
+enum trans_event {
+ EVENT_CBW_VALID,
+ EVENT_NEED_STATUS
+};
+
+struct usb_msc_cbw {
+ uint32_t dCBWSignature;
+ uint32_t dCBWTag;
+ uint32_t dCBWDataTransferLength;
+ uint8_t bmCBWFlags;
+ uint8_t bCBWLUN;
+ uint8_t bCBWCBLength;
+ uint8_t CBWCB[16];
+} __attribute__((packed));
+
+struct usb_msc_csw {
+ uint32_t dCSWSignature;
+ uint32_t dCSWTag;
+ uint32_t dCSWDataResidue;
+ uint8_t bCSWStatus;
+} __attribute__((packed));
+
+struct sbc_sense_info {
+ uint8_t key;
+ uint8_t asc;
+ uint8_t ascq;
+};
+
+struct usb_msc_trans {
+ uint8_t cbw_cnt; /* Read until 31 bytes */
+ union {
+ struct usb_msc_cbw cbw;
+ uint8_t buf[1];
+ } cbw;
+
+ uint32_t bytes_to_read;
+ uint32_t bytes_to_write;
+ uint32_t byte_count; /* Either read until equal to bytes_to_read or
+ write until equal to bytes_to_write. */
+ uint32_t lba_start;
+ uint32_t block_count;
+ uint32_t current_block;
+
+ uint8_t msd_buf[512];
+
+ bool csw_valid;
+ uint8_t csw_sent; /* Write until 13 bytes */
+ union {
+ struct usb_msc_csw csw;
+ uint8_t buf[1];
+ } csw;
+};
+
+struct _usbd_mass_storage {
+ usbd_device *usbd_dev;
+ uint8_t ep_in;
+ uint8_t ep_in_size;
+ uint8_t ep_out;
+ uint8_t ep_out_size;
+
+ const char *vendor_id;
+ const char *product_id;
+ const char *product_revision_level;
+ uint32_t block_count;
+
+ int (*read_block)(uint32_t lba, uint8_t *copy_to);
+ int (*write_block)(uint32_t lba, const uint8_t *copy_from);
+
+ void (*lock)(void);
+ void (*unlock)(void);
+
+ struct usb_msc_trans trans;
+ struct sbc_sense_info sense;
+};
+
+static usbd_mass_storage _mass_storage;
+
+/*-- SCSI Base Responses -----------------------------------------------------*/
+
+static const uint8_t _spc3_inquiry_response[36] = {
+ 0x00, /* Byte 0: Peripheral Qualifier = 0, Peripheral Device Type = 0 */
+ 0x80, /* Byte 1: RMB = 1, Reserved = 0 */
+ 0x04, /* Byte 2: Version = 0 */
+ 0x02, /* Byte 3: Obsolete = 0, NormACA = 0, HiSup = 0, Response Data Format = 2 */
+ 0x20, /* Byte 4: Additional Length (n-4) = 31 + 4 */
+ 0x00, /* Byte 5: SCCS = 0, ACC = 0, TPGS = 0, 3PC = 0, Reserved = 0, Protect = 0 */
+ 0x00, /* Byte 6: BQue = 0, EncServ = 0, VS = 0, MultiP = 0, MChngr = 0, Obsolete = 0, Addr16 = 0 */
+ 0x00, /* Byte 7: Obsolete = 0, Wbus16 = 0, Sync = 0, Linked = 0, CmdQue = 0, VS = 0 */
+ /* Byte 8 - Byte 15: Vendor Identification */
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ /* Byte 16 - Byte 31: Product Identification */
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ /* Byte 32 - Byte 35: Product Revision Level */
+ 0x20, 0x20, 0x20, 0x20
+};
+
+static const uint8_t _spc3_request_sense[18] = {
+ 0x70, /* Byte 0: VALID = 0, Response Code = 112 */
+ 0x00, /* Byte 1: Obsolete = 0 */
+ 0x00, /* Byte 2: Filemark = 0, EOM = 0, ILI = 0, Reserved = 0, Sense Key = 0 */
+ /* Byte 3 - Byte 6: Information = 0 */
+ 0, 0, 0, 0,
+ 0x0a, /* Byte 7: Additional Sense Length = 10 */
+ /* Byte 8 - Byte 11: Command Specific Info = 0 */
+ 0, 0, 0, 0,
+ 0x00, /* Byte 12: Additional Sense Code (ASC) = 0 */
+ 0x00, /* Byte 13: Additional Sense Code Qualifier (ASCQ) = 0 */
+ 0x00, /* Byte 14: Field Replaceable Unit Code (FRUC) = 0 */
+ 0x00, /* Byte 15: SKSV = 0, SenseKeySpecific[0] = 0 */
+ 0x00, /* Byte 16: SenseKeySpecific[0] = 0 */
+ 0x00 /* Byte 17: SenseKeySpecific[0] = 0 */
+};
+
+/*-- SCSI Layer --------------------------------------------------------------*/
+
+static void set_sbc_status(usbd_mass_storage *ms,
+ enum sbc_sense_key key,
+ enum sbc_asc asc,
+ enum sbc_ascq ascq)
+{
+ ms->sense.key = (uint8_t) key;
+ ms->sense.asc = (uint8_t) asc;
+ ms->sense.ascq = (uint8_t) ascq;
+}
+
+static void set_sbc_status_good(usbd_mass_storage *ms)
+{
+ set_sbc_status(ms,
+ SBC_SENSE_KEY_NO_SENSE,
+ SBC_ASC_NO_ADDITIONAL_SENSE_INFORMATION,
+ SBC_ASCQ_NA);
+}
+
+static uint8_t *get_cbw_buf(struct usb_msc_trans *trans)
+{
+ return &trans->cbw.cbw.CBWCB[0];
+}
+
+static void scsi_read_6(usbd_mass_storage *ms,
+ struct usb_msc_trans *trans,
+ enum trans_event event)
+{
+ if (EVENT_CBW_VALID == event) {
+ uint8_t *buf;
+
+ buf = get_cbw_buf(trans);
+
+ trans->lba_start = (buf[2] << 8) | buf[3];
+ trans->block_count = buf[4];
+ trans->current_block = 0;
+
+ /* TODO: Check the lba & block_count for range. */
+
+ /* both are in terms of 512 byte blocks, so shift by 9 */
+ trans->bytes_to_write = trans->block_count << 9;
+
+ set_sbc_status_good(ms);
+ }
+}
+
+static void scsi_write_6(usbd_mass_storage *ms,
+ struct usb_msc_trans *trans,
+ enum trans_event event)
+{
+ (void) ms;
+
+ if (EVENT_CBW_VALID == event) {
+ uint8_t *buf;
+
+ buf = get_cbw_buf(trans);
+
+ trans->lba_start = ((0x1f & buf[1]) << 16) | (buf[2] << 8) | buf[3];
+ trans->block_count = buf[4];
+ trans->current_block = 0;
+
+ trans->bytes_to_read = trans->block_count << 9;
+ }
+}
+
+static void scsi_write_10(usbd_mass_storage *ms,
+ struct usb_msc_trans *trans,
+ enum trans_event event)
+{
+ (void) ms;
+
+ if (EVENT_CBW_VALID == event) {
+ uint8_t *buf;
+
+ buf = get_cbw_buf(trans);
+
+ trans->lba_start = (buf[2] << 24) | (buf[3] << 16) |
+ (buf[4] << 8) | buf[5];
+ trans->block_count = (buf[7] << 8) | buf[8];
+ trans->current_block = 0;
+
+ trans->bytes_to_read = trans->block_count << 9;
+ }
+}
+
+static void scsi_read_10(usbd_mass_storage *ms,
+ struct usb_msc_trans *trans,
+ enum trans_event event)
+{
+ if (EVENT_CBW_VALID == event) {
+ uint8_t *buf;
+
+ buf = get_cbw_buf(trans);
+
+ trans->lba_start = (buf[2] << 24) | (buf[3] << 16) | (buf[4] << 8) | buf[5];
+ trans->block_count = (buf[7] << 8) | buf[8];
+
+ /* TODO: Check the lba & block_count for range. */
+
+ /* both are in terms of 512 byte blocks, so shift by 9 */
+ trans->bytes_to_write = trans->block_count << 9;
+
+ set_sbc_status_good(ms);
+ }
+}
+
+static void scsi_read_capacity(usbd_mass_storage *ms,
+ struct usb_msc_trans *trans,
+ enum trans_event event)
+{
+ if (EVENT_CBW_VALID == event) {
+ trans->msd_buf[0] = ms->block_count >> 24;
+ trans->msd_buf[1] = 0xff & (ms->block_count >> 16);
+ trans->msd_buf[2] = 0xff & (ms->block_count >> 8);
+ trans->msd_buf[3] = 0xff & ms->block_count;
+
+ /* Block size: 512 */
+ trans->msd_buf[4] = 0;
+ trans->msd_buf[5] = 0;
+ trans->msd_buf[6] = 2;
+ trans->msd_buf[7] = 0;
+ trans->bytes_to_write = 8;
+ set_sbc_status_good(ms);
+ }
+}
+
+static void scsi_format_unit(usbd_mass_storage *ms,
+ struct usb_msc_trans *trans,
+ enum trans_event event)
+{
+ if (EVENT_CBW_VALID == event) {
+ uint32_t i;
+
+ memset(trans->msd_buf, 0, 512);
+
+ for (i = 0; i < ms->block_count; i++) {
+ (*ms->write_block)(i, trans->msd_buf);
+ }
+
+ set_sbc_status_good(ms);
+ }
+}
+
+static void scsi_request_sense(usbd_mass_storage *ms,
+ struct usb_msc_trans *trans,
+ enum trans_event event)
+{
+ if (EVENT_CBW_VALID == event) {
+ uint8_t *buf;
+
+ buf = &trans->cbw.cbw.CBWCB[0];
+
+ trans->bytes_to_write = buf[4]; /* allocation length */
+ memcpy(trans->msd_buf, _spc3_request_sense, sizeof(_spc3_request_sense));
+
+ trans->msd_buf[2] = ms->sense.key;
+ trans->msd_buf[12] = ms->sense.asc;
+ trans->msd_buf[13] = ms->sense.ascq;
+ }
+}
+
+static void scsi_mode_sense_6(usbd_mass_storage *ms,
+ struct usb_msc_trans *trans,
+ enum trans_event event)
+{
+ (void) ms;
+
+ if (EVENT_CBW_VALID == event) {
+#if 0
+ uint8_t *buf;
+ uint8_t page_code;
+ uint8_t allocation_length;
+
+ buf = &trans->cbw.cbw.CBWCB[0];
+ page_code = buf[2];
+ allocation_length = buf[4];
+
+ if (0x1C == page_code) { /* Informational Exceptions */
+#endif
+ trans->bytes_to_write = 4;
+
+ trans->msd_buf[0] = 3; /* Num bytes that follow */
+ trans->msd_buf[1] = 0; /* Medium Type */
+ trans->msd_buf[2] = 0; /* Device specific param */
+ trans->csw.csw.dCSWDataResidue = 4;
+#if 0
+ } else if (0x01 == page_code) { /* Error recovery */
+ } else if (0x3F == page_code) { /* All */
+ } else {
+ /* Error */
+ trans->csw.csw.bCSWStatus = CSW_STATUS_FAILED;
+ set_sbc_status(ms,
+ SBC_SENSE_KEY_ILLEGAL_REQUEST,
+ SBC_ASC_INVALID_FIELD_IN_CDB,
+ SBC_ASCQ_NA);
+ }
+#endif
+ }
+}
+
+static void scsi_inquiry(usbd_mass_storage *ms,
+ struct usb_msc_trans *trans,
+ enum trans_event event)
+{
+ if (EVENT_CBW_VALID == event) {
+ uint8_t evpd;
+ uint8_t *buf;
+
+ buf = get_cbw_buf(trans);
+ evpd = 1 & buf[1];
+
+ if (0 == evpd) {
+ size_t len;
+ trans->bytes_to_write = sizeof(_spc3_inquiry_response);
+ memcpy(trans->msd_buf, _spc3_inquiry_response, sizeof(_spc3_inquiry_response));
+
+ len = strlen(ms->vendor_id);
+ len = MIN(len, 8);
+ memcpy(&trans->msd_buf[8], ms->vendor_id, len);
+
+ len = strlen(ms->product_id);
+ len = MIN(len, 16);
+ memcpy(&trans->msd_buf[16], ms->product_id, len);
+
+ len = strlen(ms->product_revision_level);
+ len = MIN(len, 4);
+ memcpy(&trans->msd_buf[32], ms->product_revision_level, len);
+
+ trans->csw.csw.dCSWDataResidue = sizeof(_spc3_inquiry_response);
+
+ set_sbc_status_good(ms);
+ } else {
+ /* TODO: Add VPD 0x83 support */
+ /* TODO: Add VPD 0x00 support */
+ }
+ }
+}
+
+static void scsi_command(usbd_mass_storage *ms,
+ struct usb_msc_trans *trans,
+ enum trans_event event)
+{
+ if (EVENT_CBW_VALID == event) {
+ /* Setup the default success */
+ trans->csw_sent = 0;
+ trans->csw.csw.dCSWSignature = CSW_SIGNATURE;
+ trans->csw.csw.dCSWTag = trans->cbw.cbw.dCBWTag;
+ trans->csw.csw.dCSWDataResidue = 0;
+ trans->csw.csw.bCSWStatus = CSW_STATUS_SUCCESS;
+
+ trans->bytes_to_write = 0;
+ trans->bytes_to_read = 0;
+ trans->byte_count = 0;
+ }
+
+ switch (trans->cbw.cbw.CBWCB[0]) {
+ case SCSI_TEST_UNIT_READY:
+ case SCSI_SEND_DIAGNOSTIC:
+ /* Do nothing, just send the success. */
+ set_sbc_status_good(ms);
+ break;
+ case SCSI_FORMAT_UNIT:
+ scsi_format_unit(ms, trans, event);
+ break;
+ case SCSI_REQUEST_SENSE:
+ scsi_request_sense(ms, trans, event);
+ break;
+ case SCSI_MODE_SENSE_6:
+ scsi_mode_sense_6(ms, trans, event);
+ break;
+ case SCSI_READ_6:
+ scsi_read_6(ms, trans, event);
+ break;
+ case SCSI_INQUIRY:
+ scsi_inquiry(ms, trans, event);
+ break;
+ case SCSI_READ_CAPACITY:
+ scsi_read_capacity(ms, trans, event);
+ break;
+ case SCSI_READ_10:
+ scsi_read_10(ms, trans, event);
+ break;
+ case SCSI_WRITE_6:
+ scsi_write_6(ms, trans, event);
+ break;
+ case SCSI_WRITE_10:
+ scsi_write_10(ms, trans, event);
+ break;
+ default:
+ set_sbc_status(ms, SBC_SENSE_KEY_ILLEGAL_REQUEST,
+ SBC_ASC_INVALID_COMMAND_OPERATION_CODE,
+ SBC_ASCQ_NA);
+
+ trans->bytes_to_write = 0;
+ trans->bytes_to_read = 0;
+ trans->csw.csw.bCSWStatus = CSW_STATUS_FAILED;
+ break;
+ }
+}
+
+/*-- USB Mass Storage Layer --------------------------------------------------*/
+
+/** @brief Handle the USB 'OUT' requests. */
+static void msc_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
+{
+ usbd_mass_storage *ms;
+ struct usb_msc_trans *trans;
+ int len, max_len, left;
+ void *p;
+
+ ms = &_mass_storage;
+ trans = &ms->trans;
+
+ /* RX only */
+ left = sizeof(struct usb_msc_cbw) - trans->cbw_cnt;
+ if (0 < left) {
+ max_len = MIN(ms->ep_out_size, left);
+ p = &trans->cbw.buf[0x1ff & trans->cbw_cnt];
+ len = usbd_ep_read_packet(usbd_dev, ep, p, max_len);
+ trans->cbw_cnt += len;
+
+ if (sizeof(struct usb_msc_cbw) == trans->cbw_cnt) {
+ scsi_command(ms, trans, EVENT_CBW_VALID);
+ if (trans->byte_count < trans->bytes_to_read) {
+ /* We must wait until there is something to
+ * read again. */
+ return;
+ }
+ }
+ }
+
+ if (trans->byte_count < trans->bytes_to_read) {
+ if (0 < trans->block_count) {
+ if ((0 == trans->byte_count) && (NULL != ms->lock)){
+ (*ms->lock)();
+ }
+ }
+
+ left = trans->bytes_to_read - trans->byte_count;
+ max_len = MIN(ms->ep_out_size, left);
+ p = &trans->msd_buf[0x1ff & trans->byte_count];
+ len = usbd_ep_read_packet(usbd_dev, ep, p, max_len);
+ trans->byte_count += len;
+
+ if (0 < trans->block_count) {
+ if (0 == (0x1ff & trans->byte_count)) {
+ uint32_t lba;
+
+ lba = trans->lba_start + trans->current_block;
+ if (0 != (*ms->write_block)(lba, trans->msd_buf)) {
+ /* Error */
+ }
+ trans->current_block++;
+ }
+ }
+ } else if (trans->byte_count < trans->bytes_to_write) {
+ if (0 < trans->block_count) {
+ if ((0 == trans->byte_count) && (NULL != ms->lock)) {
+ (*ms->lock)();
+ }
+
+ if (0 == (0x1ff & trans->byte_count)) {
+ uint32_t lba;
+
+ lba = trans->lba_start + trans->current_block;
+ if (0 != (*ms->read_block)(lba, trans->msd_buf)) {
+ /* Error */
+ }
+ trans->current_block++;
+ }
+ }
+
+ left = trans->bytes_to_write - trans->byte_count;
+ max_len = MIN(ms->ep_out_size, left);
+ p = &trans->msd_buf[0x1ff & trans->byte_count];
+ len = usbd_ep_write_packet(usbd_dev, ms->ep_in, p, max_len);
+ trans->byte_count += len;
+ } else {
+ if (0 < trans->block_count) {
+ if (trans->current_block == trans->block_count) {
+ uint32_t lba;
+
+ lba = trans->lba_start + trans->current_block;
+ if (0 != (*ms->write_block)(lba, trans->msd_buf)) {
+ /* Error */
+ }
+
+ trans->current_block = 0;
+ if (NULL != ms->unlock){
+ (*ms->unlock)();
+ }
+ }
+ }
+ if (false == trans->csw_valid) {
+ scsi_command(ms, trans, EVENT_NEED_STATUS);
+ trans->csw_valid = true;
+ }
+
+ left = sizeof(struct usb_msc_csw) - trans->csw_sent;
+ if (0 < left) {
+ max_len = MIN(ms->ep_out_size, left);
+ p = &trans->csw.buf[trans->csw_sent];
+ len = usbd_ep_write_packet(usbd_dev, ms->ep_in, p, max_len);
+ trans->csw_sent += len;
+ }
+ }
+}
+
+/** @brief Handle the USB 'IN' requests. */
+static void msc_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
+{
+ usbd_mass_storage *ms;
+ struct usb_msc_trans *trans;
+ int len, max_len, left;
+ void *p;
+
+ ms = &_mass_storage;
+ trans = &ms->trans;
+
+ if (trans->byte_count < trans->bytes_to_write) {
+ if (0 < trans->block_count) {
+ if (0 == (0x1ff & trans->byte_count)) {
+ uint32_t lba;
+
+ lba = trans->lba_start + trans->current_block;
+ if (0 != (*ms->read_block)(lba, trans->msd_buf)) {
+ /* Error */
+ }
+ trans->current_block++;
+ }
+ }
+
+ left = trans->bytes_to_write - trans->byte_count;
+ max_len = MIN(ms->ep_out_size, left);
+ p = &trans->msd_buf[0x1ff & trans->byte_count];
+ len = usbd_ep_write_packet(usbd_dev, ep, p, max_len);
+ trans->byte_count += len;
+ } else {
+ if (0 < trans->block_count) {
+ if (trans->current_block == trans->block_count) {
+ trans->current_block = 0;
+ if (NULL != ms->unlock){
+ (*ms->unlock)();
+ }
+ }
+ }
+ if (false == trans->csw_valid) {
+ scsi_command(ms, trans, EVENT_NEED_STATUS);
+ trans->csw_valid = true;
+ }
+
+ left = sizeof(struct usb_msc_csw) - trans->csw_sent;
+ if (0 < left) {
+ max_len = MIN(ms->ep_out_size, left);
+ p = &trans->csw.buf[trans->csw_sent];
+ len = usbd_ep_write_packet(usbd_dev, ep, p, max_len);
+ trans->csw_sent += len;
+ } else if (sizeof(struct usb_msc_csw) == trans->csw_sent) {
+ /* End of transaction */
+ trans->lba_start = 0xffffffff;
+ trans->block_count = 0;
+ trans->current_block = 0;
+ trans->cbw_cnt = 0;
+ trans->bytes_to_read = 0;
+ trans->bytes_to_write = 0;
+ trans->byte_count = 0;
+ trans->csw_sent = 0;
+ trans->csw_valid = false;
+ }
+ }
+}
+
+/** @brief Handle various control requests related to the msc storage
+ * interface.
+ */
+static int msc_control_request(usbd_device *usbd_dev,
+ struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
+ void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
+{
+ (void)complete;
+ (void)usbd_dev;
+
+ switch (req->bRequest) {
+ case USB_MSC_REQ_BULK_ONLY_RESET:
+ /* Do any special reset code here. */
+ return USBD_REQ_HANDLED;
+ case USB_MSC_REQ_GET_MAX_LUN:
+ /* Return the number of LUNs. We use 0. */
+ *buf[0] = 0;
+ *len = 1;
+ return USBD_REQ_HANDLED;
+ }
+
+ return USBD_REQ_NOTSUPP;
+}
+
+/** @brief Setup the endpoints to be bulk & register the callbacks. */
+static void msc_set_config(usbd_device *usbd_dev, uint16_t wValue)
+{
+ usbd_mass_storage *ms = &_mass_storage;
+
+ (void)wValue;
+
+ usbd_ep_setup(usbd_dev, ms->ep_in, USB_ENDPOINT_ATTR_BULK,
+ ms->ep_in_size, msc_data_tx_cb);
+ usbd_ep_setup(usbd_dev, ms->ep_out, USB_ENDPOINT_ATTR_BULK,
+ ms->ep_out_size, msc_data_rx_cb);
+
+ usbd_register_control_callback(
+ usbd_dev,
+ USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
+ USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
+ msc_control_request);
+}
+
+/** @addtogroup usb_msc */
+/** @{ */
+
+/** @brief Initializes the USB Mass Storage subsystem.
+
+@note Currently you can only have this profile active.
+
+@param[in] usbd_dev The USB device to associate the Mass Storage with.
+@param[in] ep_in The USB 'IN' endpoint.
+@param[in] ep_in_size The maximum endpoint size. Valid values: 8, 16, 32 or 64
+@param[in] ep_out The USB 'OUT' endpoint.
+@param[in] ep_out_size The maximum endpoint size. Valid values: 8, 16, 32 or 64
+@param[in] vendor_id The SCSI vendor ID to return. Maximum used length is 8.
+@param[in] product_id The SCSI product ID to return. Maximum used length is 16.
+@param[in] product_revision_level The SCSI product revision level to return.
+ Maximum used length is 4.
+@param[in] block_count The number of 512-byte blocks available.
+@param[in] read_block The function called when the host requests to read a LBA
+ block. Must _NOT_ be NULL.
+@param[in] write_block The function called when the host requests to write a
+ LBA block. Must _NOT_ be NULL.
+
+@return Pointer to the usbd_mass_storage struct.
+*/
+usbd_mass_storage *usb_msc_init(usbd_device *usbd_dev,
+ uint8_t ep_in, uint8_t ep_in_size,
+ uint8_t ep_out, uint8_t ep_out_size,
+ const char *vendor_id,
+ const char *product_id,
+ const char *product_revision_level,
+ const uint32_t block_count,
+ int (*read_block)(uint32_t lba, uint8_t *copy_to),
+ int (*write_block)(uint32_t lba, const uint8_t *copy_from))
+{
+ _mass_storage.usbd_dev = usbd_dev;
+ _mass_storage.ep_in = ep_in;
+ _mass_storage.ep_in_size = ep_in_size;
+ _mass_storage.ep_out = ep_out;
+ _mass_storage.ep_out_size = ep_out_size;
+ _mass_storage.vendor_id = vendor_id;
+ _mass_storage.product_id = product_id;
+ _mass_storage.product_revision_level = product_revision_level;
+ _mass_storage.block_count = block_count - 1;
+ _mass_storage.read_block = read_block;
+ _mass_storage.write_block = write_block;
+ _mass_storage.lock = NULL;
+ _mass_storage.unlock = NULL;
+
+ _mass_storage.trans.lba_start = 0xffffffff;
+ _mass_storage.trans.block_count = 0;
+ _mass_storage.trans.current_block = 0;
+ _mass_storage.trans.cbw_cnt = 0;
+ _mass_storage.trans.bytes_to_read = 0;
+ _mass_storage.trans.bytes_to_write = 0;
+ _mass_storage.trans.byte_count = 0;
+ _mass_storage.trans.csw_valid = false;
+ _mass_storage.trans.csw_sent = 0;
+
+ set_sbc_status_good(&_mass_storage);
+
+ usbd_register_set_config_callback(usbd_dev, msc_set_config);
+
+ return &_mass_storage;
+}
+
+/** @} */
diff --git a/libopencm3/lib/usb/usb_private.h b/libopencm3/lib/usb/usb_private.h
new file mode 100644
index 0000000..81466df
--- /dev/null
+++ b/libopencm3/lib/usb/usb_private.h
@@ -0,0 +1,163 @@
+/** @defgroup usb_private_defines USB Private Structures
+
+@brief <b>Defined Constants and Types for the USB Private Structures</b>
+
+@ingroup USB_defines
+
+@version 1.0.0
+
+@author @htmlonly &copy; @endhtmlonly 2010
+Gareth McMullin <gareth@blacksphere.co.nz>
+
+@date 10 March 2013
+
+LGPL License Terms @ref lgpl_license
+*/
+
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/**@{*/
+
+#ifndef __USB_PRIVATE_H
+#define __USB_PRIVATE_H
+
+#define MAX_USER_CONTROL_CALLBACK 4
+#define MAX_USER_SET_CONFIG_CALLBACK 4
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+/** Internal collection of device information. */
+struct _usbd_device {
+ const struct usb_device_descriptor *desc;
+ const struct usb_config_descriptor *config;
+ const char **strings;
+ int num_strings;
+
+ uint8_t *ctrl_buf; /**< Internal buffer used for control transfers */
+ uint16_t ctrl_buf_len;
+
+ uint8_t current_address;
+ uint8_t current_config;
+
+ uint16_t pm_top; /**< Top of allocated endpoint buffer memory */
+
+ /* User callback functions for various USB events */
+ void (*user_callback_reset)(void);
+ void (*user_callback_suspend)(void);
+ void (*user_callback_resume)(void);
+ void (*user_callback_sof)(void);
+
+ struct usb_control_state {
+ enum {
+ IDLE, STALLED,
+ DATA_IN, LAST_DATA_IN, STATUS_IN,
+ DATA_OUT, LAST_DATA_OUT, STATUS_OUT,
+ } state;
+ struct usb_setup_data req __attribute__((aligned(4)));
+ uint8_t *ctrl_buf;
+ uint16_t ctrl_len;
+ void (*complete)(usbd_device *usbd_dev,
+ struct usb_setup_data *req);
+ } control_state;
+
+ struct user_control_callback {
+ usbd_control_callback cb;
+ uint8_t type;
+ uint8_t type_mask;
+ } user_control_callback[MAX_USER_CONTROL_CALLBACK];
+
+ void (*user_callback_ctr[8][3])(usbd_device *usbd_dev, uint8_t ea);
+
+ /* User callback function for some standard USB function hooks */
+ void (*user_callback_set_config[MAX_USER_SET_CONFIG_CALLBACK])
+ (usbd_device *usbd_dev, uint16_t wValue);
+
+ const struct _usbd_driver *driver;
+
+ /* private driver data */
+
+ uint16_t fifo_mem_top;
+ uint16_t fifo_mem_top_ep0;
+ uint8_t force_nak[4];
+ /*
+ * We keep a backup copy of the out endpoint size registers to restore
+ * them after a transaction.
+ */
+ uint32_t doeptsiz[4];
+ /*
+ * Received packet size for each endpoint. This is assigned in
+ * stm32f107_poll() which reads the packet status push register GRXSTSP
+ * for use in stm32f107_ep_read_packet().
+ */
+ uint16_t rxbcnt;
+};
+
+enum _usbd_transaction {
+ USB_TRANSACTION_IN,
+ USB_TRANSACTION_OUT,
+ USB_TRANSACTION_SETUP,
+};
+
+/* Do not appear to belong to the API, so are omitted from docs */
+/**@}*/
+
+void _usbd_control_in(usbd_device *usbd_dev, uint8_t ea);
+void _usbd_control_out(usbd_device *usbd_dev, uint8_t ea);
+void _usbd_control_setup(usbd_device *usbd_dev, uint8_t ea);
+
+int _usbd_standard_request_device(usbd_device *usbd_dev,
+ struct usb_setup_data *req, uint8_t **buf,
+ uint16_t *len);
+int _usbd_standard_request_interface(usbd_device *usbd_dev,
+ struct usb_setup_data *req, uint8_t **buf,
+ uint16_t *len);
+int _usbd_standard_request_endpoint(usbd_device *usbd_dev,
+ struct usb_setup_data *req, uint8_t **buf,
+ uint16_t *len);
+int _usbd_standard_request(usbd_device *usbd_dev, struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len);
+
+void _usbd_reset(usbd_device *usbd_dev);
+
+/* Functions provided by the hardware abstraction. */
+struct _usbd_driver {
+ usbd_device *(*init)(void);
+ void (*set_address)(usbd_device *usbd_dev, uint8_t addr);
+ void (*ep_setup)(usbd_device *usbd_dev, uint8_t addr, uint8_t type,
+ uint16_t max_size,
+ void (*cb)(usbd_device *usbd_dev, uint8_t ep));
+ void (*ep_reset)(usbd_device *usbd_dev);
+ void (*ep_stall_set)(usbd_device *usbd_dev, uint8_t addr,
+ uint8_t stall);
+ void (*ep_nak_set)(usbd_device *usbd_dev, uint8_t addr, uint8_t nak);
+ uint8_t (*ep_stall_get)(usbd_device *usbd_dev, uint8_t addr);
+ uint16_t (*ep_write_packet)(usbd_device *usbd_dev, uint8_t addr,
+ const void *buf, uint16_t len);
+ uint16_t (*ep_read_packet)(usbd_device *usbd_dev, uint8_t addr,
+ void *buf, uint16_t len);
+ void (*poll)(usbd_device *usbd_dev);
+ void (*disconnect)(usbd_device *usbd_dev, bool disconnected);
+ uint32_t base_address;
+ bool set_address_before_status;
+ uint16_t rx_fifo_size;
+};
+
+#endif
+
diff --git a/libopencm3/lib/usb/usb_standard.c b/libopencm3/lib/usb/usb_standard.c
new file mode 100644
index 0000000..3b0dac9
--- /dev/null
+++ b/libopencm3/lib/usb/usb_standard.c
@@ -0,0 +1,532 @@
+/** @defgroup usb_standard_file Generic USB Standard Request Interface
+
+@ingroup USB
+
+@brief <b>Generic USB Standard Request Interface</b>
+
+@version 1.0.0
+
+@author @htmlonly &copy; @endhtmlonly 2010
+Gareth McMullin <gareth@blacksphere.co.nz>
+
+@date 10 March 2013
+
+LGPL License Terms @ref lgpl_license
+*/
+
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/**@{*/
+
+#include <string.h>
+#include <libopencm3/usb/usbd.h>
+#include "usb_private.h"
+
+int usbd_register_set_config_callback(usbd_device *usbd_dev,
+ void (*callback)(usbd_device *usbd_dev,
+ uint16_t wValue))
+{
+ int i;
+
+ for (i = 0; i < MAX_USER_SET_CONFIG_CALLBACK; i++) {
+ if (usbd_dev->user_callback_set_config[i])
+ continue;
+
+ usbd_dev->user_callback_set_config[i] = callback;
+ return 0;
+ }
+
+ return -1;
+}
+
+static uint16_t build_config_descriptor(usbd_device *usbd_dev,
+ uint8_t index, uint8_t *buf, uint16_t len)
+{
+ uint8_t *tmpbuf = buf;
+ const struct usb_config_descriptor *cfg = &usbd_dev->config[index];
+ uint16_t count, total = 0, totallen = 0;
+ uint16_t i, j, k;
+
+ memcpy(buf, cfg, count = MIN(len, cfg->bLength));
+ buf += count;
+ len -= count;
+ total += count;
+ totallen += cfg->bLength;
+
+ /* For each interface... */
+ for (i = 0; i < cfg->bNumInterfaces; i++) {
+ /* Interface Association Descriptor, if any */
+ if (cfg->interface[i].iface_assoc) {
+ const struct usb_iface_assoc_descriptor *assoc =
+ cfg->interface[i].iface_assoc;
+ memcpy(buf, assoc, count = MIN(len, assoc->bLength));
+ buf += count;
+ len -= count;
+ total += count;
+ totallen += assoc->bLength;
+ }
+ /* For each alternate setting... */
+ for (j = 0; j < cfg->interface[i].num_altsetting; j++) {
+ const struct usb_interface_descriptor *iface =
+ &cfg->interface[i].altsetting[j];
+ /* Copy interface descriptor. */
+ memcpy(buf, iface, count = MIN(len, iface->bLength));
+ buf += count;
+ len -= count;
+ total += count;
+ totallen += iface->bLength;
+ /* Copy extra bytes (function descriptors). */
+ memcpy(buf, iface->extra,
+ count = MIN(len, iface->extralen));
+ buf += count;
+ len -= count;
+ total += count;
+ totallen += iface->extralen;
+ /* For each endpoint... */
+ for (k = 0; k < iface->bNumEndpoints; k++) {
+ const struct usb_endpoint_descriptor *ep =
+ &iface->endpoint[k];
+ memcpy(buf, ep, count = MIN(len, ep->bLength));
+ buf += count;
+ len -= count;
+ total += count;
+ totallen += ep->bLength;
+ }
+ }
+ }
+
+ /* Fill in wTotalLength. */
+ *(uint16_t *)(tmpbuf + 2) = totallen;
+
+ return total;
+}
+
+static int usb_descriptor_type(uint16_t wValue)
+{
+ return wValue >> 8;
+}
+
+static int usb_descriptor_index(uint16_t wValue)
+{
+ return wValue & 0xFF;
+}
+
+static int usb_standard_get_descriptor(usbd_device *usbd_dev,
+ struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len)
+{
+ int i, array_idx, descr_idx;
+ struct usb_string_descriptor *sd;
+
+ descr_idx = usb_descriptor_index(req->wValue);
+
+ switch (usb_descriptor_type(req->wValue)) {
+ case USB_DT_DEVICE:
+ *buf = (uint8_t *) usbd_dev->desc;
+ *len = MIN(*len, usbd_dev->desc->bLength);
+ return USBD_REQ_HANDLED;
+ case USB_DT_CONFIGURATION:
+ *buf = usbd_dev->ctrl_buf;
+ *len = build_config_descriptor(usbd_dev, descr_idx, *buf, *len);
+ return USBD_REQ_HANDLED;
+ case USB_DT_STRING:
+ sd = (struct usb_string_descriptor *)usbd_dev->ctrl_buf;
+
+ if (descr_idx == 0) {
+ /* Send sane Language ID descriptor... */
+ sd->wData[0] = USB_LANGID_ENGLISH_US;
+ sd->bLength = sizeof(sd->bLength) +
+ sizeof(sd->bDescriptorType) +
+ sizeof(sd->wData[0]);
+
+ *len = MIN(*len, sd->bLength);
+ } else {
+ array_idx = descr_idx - 1;
+
+ if (!usbd_dev->strings) {
+ /* Device doesn't support strings. */
+ return USBD_REQ_NOTSUPP;
+ }
+
+ /* Check that string index is in range. */
+ if (array_idx >= usbd_dev->num_strings) {
+ return USBD_REQ_NOTSUPP;
+ }
+
+ /* Strings with Language ID differnet from
+ * USB_LANGID_ENGLISH_US are not supported */
+ if (req->wIndex != USB_LANGID_ENGLISH_US) {
+ return USBD_REQ_NOTSUPP;
+ }
+
+ /* Ths string is returned as UTF16, hence the
+ * multiplication
+ */
+ sd->bLength = strlen(usbd_dev->strings[array_idx]) * 2 +
+ sizeof(sd->bLength) +
+ sizeof(sd->bDescriptorType);
+
+ *len = MIN(*len, sd->bLength);
+
+ for (i = 0; i < (*len / 2) - 1; i++) {
+ sd->wData[i] =
+ usbd_dev->strings[array_idx][i];
+ }
+ }
+
+ sd->bDescriptorType = USB_DT_STRING;
+ *buf = (uint8_t *)sd;
+
+ return USBD_REQ_HANDLED;
+ }
+ return USBD_REQ_NOTSUPP;
+}
+
+static int usb_standard_set_address(usbd_device *usbd_dev,
+ struct usb_setup_data *req, uint8_t **buf,
+ uint16_t *len)
+{
+ (void)req;
+ (void)buf;
+ (void)len;
+
+ /* The actual address is only latched at the STATUS IN stage. */
+ if ((req->bmRequestType != 0) || (req->wValue >= 128)) {
+ return 0;
+ }
+
+ usbd_dev->current_address = req->wValue;
+
+ /*
+ * Special workaround for STM32F10[57] that require the address
+ * to be set here. This is undocumented!
+ */
+ if (usbd_dev->driver->set_address_before_status) {
+ usbd_dev->driver->set_address(usbd_dev, req->wValue);
+ }
+
+ return 1;
+}
+
+static int usb_standard_set_configuration(usbd_device *usbd_dev,
+ struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len)
+{
+ int i;
+
+ (void)req;
+ (void)buf;
+ (void)len;
+
+ /* Is this correct, or should we reset alternate settings. */
+ if (req->wValue == usbd_dev->current_config) {
+ return 1;
+ }
+
+ usbd_dev->current_config = req->wValue;
+
+ /* Reset all endpoints. */
+ usbd_dev->driver->ep_reset(usbd_dev);
+
+ if (usbd_dev->user_callback_set_config) {
+ /*
+ * Flush control callbacks. These will be reregistered
+ * by the user handler.
+ */
+ for (i = 0; i < MAX_USER_CONTROL_CALLBACK; i++) {
+ usbd_dev->user_control_callback[i].cb = NULL;
+ }
+
+ for (i = 0; i < MAX_USER_SET_CONFIG_CALLBACK; i++) {
+ if (usbd_dev->user_callback_set_config[i]) {
+ usbd_dev->user_callback_set_config[i](usbd_dev,
+ req->wValue);
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int usb_standard_get_configuration(usbd_device *usbd_dev,
+ struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len)
+{
+ (void)req;
+
+ if (*len > 1) {
+ *len = 1;
+ }
+ (*buf)[0] = usbd_dev->current_config;
+
+ return 1;
+}
+
+static int usb_standard_set_interface(usbd_device *usbd_dev,
+ struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len)
+{
+ (void)usbd_dev;
+ (void)req;
+ (void)buf;
+
+ /* FIXME: Adapt if we have more than one interface. */
+ if (req->wValue != 0) {
+ return 0;
+ }
+ *len = 0;
+
+ return 1;
+}
+
+static int usb_standard_get_interface(usbd_device *usbd_dev,
+ struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len)
+{
+ (void)usbd_dev;
+ (void)req;
+ (void)buf;
+
+ /* FIXME: Adapt if we have more than one interface. */
+ *len = 1;
+ (*buf)[0] = 0;
+
+ return 1;
+}
+
+static int usb_standard_device_get_status(usbd_device *usbd_dev,
+ struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len)
+{
+ (void)usbd_dev;
+ (void)req;
+
+ /* bit 0: self powered */
+ /* bit 1: remote wakeup */
+ if (*len > 2) {
+ *len = 2;
+ }
+ (*buf)[0] = 0;
+ (*buf)[1] = 0;
+
+ return 1;
+}
+
+static int usb_standard_interface_get_status(usbd_device *usbd_dev,
+ struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len)
+{
+ (void)usbd_dev;
+ (void)req;
+ /* not defined */
+
+ if (*len > 2) {
+ *len = 2;
+ }
+ (*buf)[0] = 0;
+ (*buf)[1] = 0;
+
+ return 1;
+}
+
+static int usb_standard_endpoint_get_status(usbd_device *usbd_dev,
+ struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len)
+{
+ (void)req;
+
+ if (*len > 2) {
+ *len = 2;
+ }
+ (*buf)[0] = usbd_ep_stall_get(usbd_dev, req->wIndex) ? 1 : 0;
+ (*buf)[1] = 0;
+
+ return 1;
+}
+
+static int usb_standard_endpoint_stall(usbd_device *usbd_dev,
+ struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len)
+{
+ (void)buf;
+ (void)len;
+
+ usbd_ep_stall_set(usbd_dev, req->wIndex, 1);
+
+ return 1;
+}
+
+static int usb_standard_endpoint_unstall(usbd_device *usbd_dev,
+ struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len)
+{
+ (void)buf;
+ (void)len;
+
+ usbd_ep_stall_set(usbd_dev, req->wIndex, 0);
+
+ return 1;
+}
+
+/* Do not appear to belong to the API, so are omitted from docs */
+/**@}*/
+
+int _usbd_standard_request_device(usbd_device *usbd_dev,
+ struct usb_setup_data *req, uint8_t **buf,
+ uint16_t *len)
+{
+ int (*command)(usbd_device *usbd_dev, struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len) = NULL;
+
+ switch (req->bRequest) {
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ if (req->wValue == USB_FEAT_DEVICE_REMOTE_WAKEUP) {
+ /* Device wakeup code goes here. */
+ }
+
+ if (req->wValue == USB_FEAT_TEST_MODE) {
+ /* Test mode code goes here. */
+ }
+
+ break;
+ case USB_REQ_SET_ADDRESS:
+ /*
+ * SET ADDRESS is an exception.
+ * It is only processed at STATUS stage.
+ */
+ command = usb_standard_set_address;
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ command = usb_standard_set_configuration;
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ command = usb_standard_get_configuration;
+ break;
+ case USB_REQ_GET_DESCRIPTOR:
+ command = usb_standard_get_descriptor;
+ break;
+ case USB_REQ_GET_STATUS:
+ /*
+ * GET_STATUS always responds with zero reply.
+ * The application may override this behaviour.
+ */
+ command = usb_standard_device_get_status;
+ break;
+ case USB_REQ_SET_DESCRIPTOR:
+ /* SET_DESCRIPTOR is optional and not implemented. */
+ break;
+ }
+
+ if (!command) {
+ return 0;
+ }
+
+ return command(usbd_dev, req, buf, len);
+}
+
+int _usbd_standard_request_interface(usbd_device *usbd_dev,
+ struct usb_setup_data *req, uint8_t **buf,
+ uint16_t *len)
+{
+ int (*command)(usbd_device *usbd_dev, struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len) = NULL;
+
+ switch (req->bRequest) {
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ /* not defined */
+ break;
+ case USB_REQ_GET_INTERFACE:
+ command = usb_standard_get_interface;
+ break;
+ case USB_REQ_SET_INTERFACE:
+ command = usb_standard_set_interface;
+ break;
+ case USB_REQ_GET_STATUS:
+ command = usb_standard_interface_get_status;
+ break;
+ }
+
+ if (!command) {
+ return 0;
+ }
+
+ return command(usbd_dev, req, buf, len);
+}
+
+int _usbd_standard_request_endpoint(usbd_device *usbd_dev,
+ struct usb_setup_data *req, uint8_t **buf,
+ uint16_t *len)
+{
+ int (*command) (usbd_device *usbd_dev, struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len) = NULL;
+
+ switch (req->bRequest) {
+ case USB_REQ_CLEAR_FEATURE:
+ if (req->wValue == USB_FEAT_ENDPOINT_HALT) {
+ command = usb_standard_endpoint_unstall;
+ }
+ break;
+ case USB_REQ_SET_FEATURE:
+ if (req->wValue == USB_FEAT_ENDPOINT_HALT) {
+ command = usb_standard_endpoint_stall;
+ }
+ break;
+ case USB_REQ_GET_STATUS:
+ command = usb_standard_endpoint_get_status;
+ break;
+ case USB_REQ_SET_SYNCH_FRAME:
+ /* FIXME: SYNCH_FRAME is not implemented. */
+ /*
+ * SYNCH_FRAME is used for synchronization of isochronous
+ * endpoints which are not yet implemented.
+ */
+ break;
+ }
+
+ if (!command) {
+ return 0;
+ }
+
+ return command(usbd_dev, req, buf, len);
+}
+
+int _usbd_standard_request(usbd_device *usbd_dev, struct usb_setup_data *req,
+ uint8_t **buf, uint16_t *len)
+{
+ /* FIXME: Have class/vendor requests as well. */
+ if ((req->bmRequestType & USB_REQ_TYPE_TYPE) != USB_REQ_TYPE_STANDARD) {
+ return 0;
+ }
+
+ switch (req->bmRequestType & USB_REQ_TYPE_RECIPIENT) {
+ case USB_REQ_TYPE_DEVICE:
+ return _usbd_standard_request_device(usbd_dev, req, buf, len);
+ case USB_REQ_TYPE_INTERFACE:
+ return _usbd_standard_request_interface(usbd_dev, req,
+ buf, len);
+ case USB_REQ_TYPE_ENDPOINT:
+ return _usbd_standard_request_endpoint(usbd_dev, req, buf, len);
+ default:
+ return 0;
+ }
+}
+