diff --git a/Makefile b/Makefile index fce0a317c..7c270e621 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ ifneq ($(EMULATOR),1) OBJS += timer.o endif +OBJS += usb_standard.o OBJS += usb21_standard.o OBJS += webusb.o OBJS += winusb.o diff --git a/bootloader/bootloader.c b/bootloader/bootloader.c index b9f840cc9..292632db2 100644 --- a/bootloader/bootloader.c +++ b/bootloader/bootloader.c @@ -129,6 +129,8 @@ int main(void) oledInit(); #endif + mpu_config_bootloader(); + #ifndef APPVER // at least one button is unpressed uint16_t state = gpio_port_read(BTN_PORT); @@ -147,6 +149,7 @@ int main(void) timer_init(); } + mpu_config_off(); load_app(signed_firmware); } #endif diff --git a/firmware/usb.c b/firmware/usb.c index 9da00ea91..356bd0365 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -259,6 +259,8 @@ static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uin (void)complete; (void)dev; + wait_random(); + if ((req->bmRequestType != 0x81) || (req->bRequest != USB_REQ_GET_DESCRIPTOR) || (req->wValue != 0x2200)) @@ -266,7 +268,7 @@ static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uin debugLog(0, "", "hid_control_request u2f"); *buf = (uint8_t *)hid_report_descriptor_u2f; - *len = MIN(*len, sizeof(hid_report_descriptor_u2f)); + *len = MIN_8bits(*len, sizeof(hid_report_descriptor_u2f)); return 1; } diff --git a/setup.c b/setup.c index e59682b28..e554ad17a 100644 --- a/setup.c +++ b/setup.c @@ -141,6 +141,7 @@ void setupApp(void) gpio_set_af(GPIOA, GPIO_AF10, GPIO10); } +#define MPU_RASR_SIZE_32B (0x04UL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_1KB (0x09UL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_4KB (0x0BUL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_8KB (0x0CUL << MPU_RASR_SIZE_LSB) @@ -161,6 +162,54 @@ void setupApp(void) #define FLASH_BASE (0x08000000U) #define SRAM_BASE (0x20000000U) +void mpu_config_off(void) +{ + // Disable MPU + MPU_CTRL = 0; + + __asm__ volatile("dsb"); + __asm__ volatile("isb"); +} + +void mpu_config_bootloader(void) +{ + // Disable MPU + MPU_CTRL = 0; + + // Note: later entries overwrite previous ones + // Flash (0x08000000 - 0x0807FFFF, 1 MiB, read-write) + MPU_RBAR = FLASH_BASE | MPU_RBAR_VALID | (0 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_1MB | MPU_RASR_ATTR_AP_PRW_URW; + + // Flash (0x8007FE0 - 0x08007FFF, 32 B, no-access) + MPU_RBAR = (FLASH_BASE + 0x7FE0) | MPU_RBAR_VALID | (1 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_32B | MPU_RASR_ATTR_AP_PNO_UNO; + + // SRAM (0x20000000 - 0x2001FFFF, read-write, execute never) + MPU_RBAR = SRAM_BASE | MPU_RBAR_VALID | (2 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_SRAM | MPU_RASR_SIZE_128KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + + // Peripherals (0x40000000 - 0x4001FFFF, read-write, execute never) + MPU_RBAR = PERIPH_BASE | MPU_RBAR_VALID | (3 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_128KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + // Peripherals (0x40020000 - 0x40023FFF, read-write, execute never) + MPU_RBAR = 0x40020000 | MPU_RBAR_VALID | (4 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_16KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + // Don't enable DMA controller access + // Peripherals (0x50000000 - 0x5007ffff, read-write, execute never) + MPU_RBAR = 0x50000000 | MPU_RBAR_VALID | (5 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_512KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + + // Enable MPU + MPU_CTRL = MPU_CTRL_ENABLE | MPU_CTRL_HFNMIENA; + + // Enable memory fault handler + SCB_SHCSR |= SCB_SHCSR_MEMFAULTENA; + + __asm__ volatile("dsb"); + __asm__ volatile("isb"); +} + // Never use in bootloader! Disables access to PPB (including MPU, NVIC, SCB) void mpu_config_firmware(void) { diff --git a/setup.h b/setup.h index f6e3f91f7..503e418ca 100644 --- a/setup.h +++ b/setup.h @@ -27,6 +27,8 @@ extern uint32_t __stack_chk_guard; void setup(void); void setupApp(void); +void mpu_config_off(void); +void mpu_config_bootloader(void); void mpu_config_firmware(void); #endif diff --git a/usb21_standard.c b/usb21_standard.c index 7ed1986cf..d6aab2bfd 100644 --- a/usb21_standard.c +++ b/usb21_standard.c @@ -62,13 +62,15 @@ static int usb21_standard_get_descriptor(usbd_device* usbd_dev, (void)complete; (void)usbd_dev; + wait_random(); + if (req->bRequest == USB_REQ_GET_DESCRIPTOR) { int descr_type = req->wValue >> 8; if (descr_type == USB_DT_BOS) { if (!usb21_bos) { return USBD_REQ_NOTSUPP; } - *len = MIN(*len, build_bos_descriptor(usb21_bos, *buf, *len)); + *len = MIN_8bits(*len, build_bos_descriptor(usb21_bos, *buf, *len)); return USBD_REQ_HANDLED; } } diff --git a/usb_private.h b/usb_private.h new file mode 100644 index 000000000..bb553be32 --- /dev/null +++ b/usb_private.h @@ -0,0 +1,161 @@ +/** @defgroup usb_private_defines USB Private Structures + +@brief Defined Constants and Types for the USB Private Structures + +@ingroup USB_defines + +@version 1.0.0 + +@author @htmlonly © @endhtmlonly 2010 +Gareth McMullin + +@date 10 March 2013 + +LGPL License Terms @ref lgpl_license +*/ + +/* + * 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 . + */ + +/**@{*/ + +#ifndef __USB_PRIVATE_H +#define __USB_PRIVATE_H + +#define MAX_USER_CONTROL_CALLBACK 4 +#define MAX_USER_SET_CONFIG_CALLBACK 4 + +/** 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; + usbd_control_complete_callback complete; + bool needs_zlp; + } control_state; + + struct user_control_callback { + usbd_control_callback cb; + uint8_t type; + uint8_t type_mask; + } user_control_callback[MAX_USER_CONTROL_CALLBACK]; + + usbd_endpoint_callback user_callback_ctr[8][3]; + + /* User callback function for some standard USB function hooks */ + usbd_set_config_callback user_callback_set_config[MAX_USER_SET_CONFIG_CALLBACK]; + + usbd_set_altsetting_callback user_callback_set_altsetting; + + 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, usbd_endpoint_callback cb); + 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/usb_standard.c b/usb_standard.c new file mode 100644 index 000000000..3576c289c --- /dev/null +++ b/usb_standard.c @@ -0,0 +1,604 @@ +/** @defgroup usb_standard_file Generic USB Standard Request Interface + +@ingroup USB + +@brief Generic USB Standard Request Interface + +@version 1.0.0 + +@author @htmlonly © @endhtmlonly 2010 +Gareth McMullin + +@date 10 March 2013 + +LGPL License Terms @ref lgpl_license +*/ + +/* + * 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 "usb_private.h" +#include "util.h" + +int usbd_register_set_config_callback(usbd_device *usbd_dev, + usbd_set_config_callback callback) +{ + 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; +} + +void usbd_register_set_altsetting_callback(usbd_device *usbd_dev, + usbd_set_altsetting_callback callback) +{ + usbd_dev->user_callback_set_altsetting = callback; +} + +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). */ + if (iface->extra) { + 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; + /* Copy extra bytes (class specific). */ + if (ep->extra) { + memcpy(buf, ep->extra, + count = MIN(len, ep->extralen)); + buf += count; + len -= count; + total += count; + totallen += ep->extralen; + } + } + } + } + + /* 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) +{ + + wait_random(); + + 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_8bits(*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_8bits(*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; + } + + /* This 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_8bits(*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) +{ + unsigned i; + int found_index = -1; + const struct usb_config_descriptor *cfg; + + (void)req; + (void)buf; + (void)len; + + if (req->wValue > 0) { + for (i = 0; i < usbd_dev->desc->bNumConfigurations; i++) { + if (req->wValue + == usbd_dev->config[i].bConfigurationValue) { + found_index = i; + break; + } + } + if (found_index < 0) { + return USBD_REQ_NOTSUPP; + } + } + + usbd_dev->current_config = found_index + 1; + + if (usbd_dev->current_config > 0) { + cfg = &usbd_dev->config[usbd_dev->current_config - 1]; + + /* reset all alternate settings configuration */ + for (i = 0; i < cfg->bNumInterfaces; i++) { + if (cfg->interface[i].cur_altsetting) { + *cfg->interface[i].cur_altsetting = 0; + } + } + } + + /* Reset all endpoints. */ + usbd_dev->driver->ep_reset(usbd_dev); + + if (usbd_dev->user_callback_set_config[0]) { + /* + * 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; + } + if (usbd_dev->current_config > 0) { + const struct usb_config_descriptor *cfg = + &usbd_dev->config[usbd_dev->current_config - 1]; + (*buf)[0] = cfg->bConfigurationValue; + } else { + (*buf)[0] = 0; + } + + return 1; +} + +static int usb_standard_set_interface(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + const struct usb_config_descriptor *cfx = + &usbd_dev->config[usbd_dev->current_config - 1]; + const struct usb_interface *iface; + + (void)buf; + + if (req->wIndex >= cfx->bNumInterfaces) { + return USBD_REQ_NOTSUPP; + } + + iface = &cfx->interface[req->wIndex]; + + if (req->wValue >= iface->num_altsetting) { + return USBD_REQ_NOTSUPP; + } + + if (iface->cur_altsetting) { + *iface->cur_altsetting = req->wValue; + } else if (req->wValue > 0) { + return USBD_REQ_NOTSUPP; + } + + if (usbd_dev->user_callback_set_altsetting) { + usbd_dev->user_callback_set_altsetting(usbd_dev, + req->wIndex, + req->wValue); + } + + *len = 0; + + return USBD_REQ_HANDLED; +} + +static int usb_standard_get_interface(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len) +{ + uint8_t *cur_altsetting; + const struct usb_config_descriptor *cfx = + &usbd_dev->config[usbd_dev->current_config - 1]; + + if (req->wIndex >= cfx->bNumInterfaces) { + return USBD_REQ_NOTSUPP; + } + + *len = 1; + cur_altsetting = cfx->interface[req->wIndex].cur_altsetting; + (*buf)[0] = (cur_altsetting) ? *cur_altsetting : 0; + + return USBD_REQ_HANDLED; +} + +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; + } +} + diff --git a/util.c b/util.c index 575127421..7d32092a8 100644 --- a/util.c +++ b/util.c @@ -17,6 +17,7 @@ * along with this library. If not, see . */ +#include "rng.h" #include "util.h" inline void delay(uint32_t wait) @@ -24,6 +25,24 @@ inline void delay(uint32_t wait) while (--wait > 0) __asm__("nop"); } +void wait_random(void) +{ + int wait = random32() & 0xff; + volatile int i = 0; + volatile int j = wait; + while (i < wait) { + if (i + j != wait) { + shutdown(); + } + ++i; + --j; + } + // Double-check loop completion. + if (i != wait || j != 0) { + shutdown(); + } +} + static const char *hexdigits = "0123456789ABCDEF"; void uint32hex(uint32_t num, char *str) diff --git a/util.h b/util.h index bd9509908..31003ed69 100644 --- a/util.h +++ b/util.h @@ -30,11 +30,14 @@ #endif // Statement expressions make these macros side-effect safe +#define MIN_8bits(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? (_a & 0xFF) : (_b & 0xFF); }) #define MIN(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; }) #define MAX(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; }) void delay(uint32_t wait); +void wait_random(void); + // converts uint32 to hexa (8 digits) void uint32hex(uint32_t num, char *str); diff --git a/webusb.c b/webusb.c index 710618781..21c6d3b5e 100644 --- a/webusb.c +++ b/webusb.c @@ -53,6 +53,8 @@ static int webusb_control_vendor_request(usbd_device *usbd_dev, (void)complete; (void)usbd_dev; + wait_random(); + if (req->bRequest != WEBUSB_VENDOR_CODE) { return USBD_REQ_NEXT_CALLBACK; } @@ -72,7 +74,7 @@ static int webusb_control_vendor_request(usbd_device *usbd_dev, url->bDescriptorType = WEBUSB_DT_URL; url->bScheme = WEBUSB_URL_SCHEME_HTTPS; memcpy(&url->URL, webusb_https_url, url_len); - *len = MIN(*len, url->bLength); + *len = MIN_8bits(*len, url->bLength); status = USBD_REQ_HANDLED; } else { // TODO: stall instead? diff --git a/winusb.c b/winusb.c index afdaf8b70..06321fd3d 100644 --- a/winusb.c +++ b/winusb.c @@ -82,6 +82,8 @@ static int winusb_descriptor_request(usbd_device *usbd_dev, (void)complete; (void)usbd_dev; + wait_random(); + if ((req->bmRequestType & USB_REQ_TYPE_TYPE) != USB_REQ_TYPE_STANDARD) { return USBD_REQ_NEXT_CALLBACK; } @@ -89,7 +91,7 @@ static int winusb_descriptor_request(usbd_device *usbd_dev, if (req->bRequest == USB_REQ_GET_DESCRIPTOR && usb_descriptor_type(req->wValue) == USB_DT_STRING) { if (usb_descriptor_index(req->wValue) == WINUSB_EXTRA_STRING_INDEX) { *buf = (uint8_t*)(&winusb_string_descriptor); - *len = MIN(*len, winusb_string_descriptor.bLength); + *len = MIN_8bits(*len, winusb_string_descriptor.bLength); return USBD_REQ_HANDLED; } } @@ -103,6 +105,8 @@ static int winusb_control_vendor_request(usbd_device *usbd_dev, (void)complete; (void)usbd_dev; + wait_random(); + if (req->bRequest != WINUSB_MS_VENDOR_CODE) { return USBD_REQ_NEXT_CALLBACK; } @@ -111,7 +115,7 @@ static int winusb_control_vendor_request(usbd_device *usbd_dev, if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_DEVICE) && (req->wIndex == WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR)) { *buf = (uint8_t*)(&winusb_wcid); - *len = MIN(*len, winusb_wcid.header.dwLength); + *len = MIN_8bits(*len, winusb_wcid.header.dwLength); status = USBD_REQ_HANDLED; } else if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_INTERFACE) && @@ -119,7 +123,7 @@ static int winusb_control_vendor_request(usbd_device *usbd_dev, (usb_descriptor_index(req->wValue) == winusb_wcid.functions[0].bInterfaceNumber)) { *buf = (uint8_t*)(&guid); - *len = MIN(*len, guid.header.dwLength); + *len = MIN_8bits(*len, guid.header.dwLength); status = USBD_REQ_HANDLED; } else {