From cd0f6389105dfe599aafa1450278498bda85f58a Mon Sep 17 00:00:00 2001 From: Valentin Ochs Date: Sun, 31 Jan 2021 19:26:40 +0100 Subject: [PATCH] Initial commit --- .gitignore | 9 ++ Makefile | 14 +++ main.c | 13 +++ usb.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++ usb.h | 4 + 5 files changed, 323 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 main.c create mode 100644 usb.c create mode 100644 usb.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..11374aa --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.ld +*.o +*.d +*.elf +*.map +.#* +/main.bin +/compile_commands.json +/.ccls-cache diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3f264a2 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +OOCD ?= openocd +OOCD_INTERFACE ?= stlink-v2 +OOCD_TARGET ?= stm32f4x +BMP_PORT ?= /dev/ttyBmpGdb + +CFLAGS=-g -O0 -std=c99 -pedantic -Wall +CXXFLAGS=-g -O0 -std=c++17 -pedantic -Wall + +OBJS = usb.o +BINARY ?= main +DEVICE=STM32F405RG + +include ../rules.mk + diff --git a/main.c b/main.c new file mode 100644 index 0000000..7460944 --- /dev/null +++ b/main.c @@ -0,0 +1,13 @@ +#include + +#include "usb.h" + +int main(void) { + rcc_clock_setup_pll(&rcc_hse_12mhz_3v3[RCC_CLOCK_3V3_168MHZ]); + + usb_setup(); + + while (1) { + __asm__("wfi"); + } +} diff --git a/usb.c b/usb.c new file mode 100644 index 0000000..de3dde2 --- /dev/null +++ b/usb.c @@ -0,0 +1,283 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "usb.h" + +// Number of serial devices we want to provide. +#define NUM_SERIAL 2 + +static const struct usb_device_descriptor dev = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + + // Devices with class 0xEF, subclass 2, and protocol 1 are interface associations + .bDeviceClass = 0xEF, + .bDeviceSubClass = 2, + .bDeviceProtocol = 1, + + .bMaxPacketSize0 = 64, + + .idVendor = 0x0483, + .idProduct = 0x5740, + + // Revision of the device: 2.00 + .bcdDevice = 0x0200, + + // Indices into the string table (starts at 1 - 0 is none) + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + + // We only have one configuration + .bNumConfigurations = 1, +}; + +// CDC-ACM devices need a functional descriptor that looks like this +struct usb_cdcacm_functional_descriptor { + struct usb_cdc_header_descriptor header; + struct usb_cdc_call_management_descriptor call_mgmt; + struct usb_cdc_acm_descriptor acm; + struct usb_cdc_union_descriptor cdc_union; +} __attribute__((packed)); + +// Each CDC-ACM device has... +// two data endpoints and one control endpoint +static struct usb_endpoint_descriptor endpoints[3 * NUM_SERIAL]; +// one functional descriptor +static struct usb_cdcacm_functional_descriptor + cdcacm_functional_descriptors[NUM_SERIAL]; +// a data interface combining the two data endpoints, and a control interface +static struct usb_interface_descriptor interface_descriptors[2 * NUM_SERIAL]; + +// instantiate the usb_interface struct needed by libopencm3 +static struct usb_interface interfaces[2 * NUM_SERIAL]; + +// Create the one configuration we have +static const struct usb_config_descriptor config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .bNumInterfaces = + sizeof(interface_descriptors) / sizeof(interface_descriptors[0]), + // this is filled in later by libopencm3 + .wTotalLength = 0, + // first and only config + .bConfigurationValue = 1, + // name of the config (none here) + .iConfiguration = 0, + // 0x80 is required to be set + .bmAttributes = 0x80, + // multiply by 2 mA to get maximum power we request + .bMaxPower = 100, + // pointer to the interfaces declared above + .interface = interfaces, +}; + +static char serial[24]; +static char * usb_strings[] = { + "Imaginaerraum.de", + "DoorControl", + serial, + "RFID", + "Other" +}; + +/* Buffer to be used for control requests. */ +uint8_t usbd_control_buffer[512]; + +static enum usbd_request_return_codes cdcacm_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)buf; + (void)usbd_dev; + + switch (req->bRequest) { + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: { + /* + * This Linux cdc_acm driver requires this to be implemented + * even though it's optional in the CDC spec, and we don't + * advertise it in the ACM functional descriptor. + */ + return USBD_REQ_HANDLED; + } + case USB_CDC_REQ_SET_LINE_CODING: + if (*len < sizeof(struct usb_cdc_line_coding)) { + return USBD_REQ_NOTSUPP; + } + + return USBD_REQ_HANDLED; + } + return USBD_REQ_NOTSUPP; +} + +static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) +{ + (void)ep; + + char buf[64]; + + // Read some data from the endpoint + int len = usbd_ep_read_packet(usbd_dev, ep, buf, 64); + + if (len) { + // Write it to the endpoint of the other interface + while (usbd_ep_write_packet(usbd_dev, ep | 0x80, buf, len) == 0); + } +} + +static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue) +{ + (void)wValue; + + for (int i = 0; i < NUM_SERIAL; i++) { + // set up all the endpoints + usbd_ep_setup(usbd_dev, 0x01 + 2*i, USB_ENDPOINT_ATTR_BULK, 64, + cdcacm_data_rx_cb); + usbd_ep_setup(usbd_dev, 0x81 + 2*i, USB_ENDPOINT_ATTR_BULK, 64, NULL); + usbd_ep_setup(usbd_dev, 0x81 + 2*i + 1, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); + } + + usbd_register_control_callback( + usbd_dev, + USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, + USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, + cdcacm_control_request); +} + +usbd_device *usbd_dev; + +void usb_setup() { + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_OTGFS); + desig_get_unique_id_as_string(serial, sizeof serial); + + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12); + gpio_set_af(GPIOA, GPIO_AF10, GPIO11 | GPIO12); + + for (uint8_t i = 0; i < NUM_SERIAL; i++) { + // set up the endpoints + // data endpoints are at addresses 1, 3, 5 (or 0x81, 0x83, 0x85) and so on + // control endpoints are at addresses 0x82, 0x84, 0x86... + + // index of the first endpoint in our array + struct usb_endpoint_descriptor *data_ep = &endpoints[3 * i]; + struct usb_endpoint_descriptor *ctrl_ep = data_ep + 2; + + // address of the first endpoint - + const uint8_t data_ep_num = 0x01 + 2*i; + const uint8_t ctrl_ep_num = data_ep_num + 1; + const uint8_t data_iface = 2*i; + const uint8_t control_iface = data_iface + 1; + + // OUT endpoint for data (host to device) + data_ep[0] = (struct usb_endpoint_descriptor) { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = (uint8_t)(data_ep_num), + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = 64 + }; + // IN endpoint (device to host) is the same except for the direction + data_ep[1] = data_ep[0]; + data_ep[1].bEndpointAddress |= 0x80; + + // The control endpoint is for interrupt transfers. Its address is one + // higher than the data endpoints, has smaller packets, and we don't really + // care about its interval (because we're not using it) + *ctrl_ep = (struct usb_endpoint_descriptor) { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = (uint8_t)(0x80 + ctrl_ep_num), + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 16, + .bInterval = 255 + }; + + cdcacm_functional_descriptors[i] = (struct usb_cdcacm_functional_descriptor) { + .header = (struct usb_cdc_header_descriptor) { + .bFunctionLength = sizeof (struct usb_cdc_header_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_HEADER, + // Version of the standard + .bcdCDC = 0x0110 + }, + .call_mgmt = (struct usb_cdc_call_management_descriptor) { + .bFunctionLength = sizeof (struct usb_cdc_call_management_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT, + // We do no call management + .bmCapabilities = 0, + // Index of our data interface + .bDataInterface = data_iface, + }, + .acm = (struct usb_cdc_acm_descriptor){ + .bFunctionLength = sizeof (struct usb_cdc_acm_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_ACM, + // Nothing here either + .bmCapabilities = 0, + }, + .cdc_union = (struct usb_cdc_union_descriptor) { + .bFunctionLength = sizeof (struct usb_cdc_union_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_UNION, + // Our interfaces + .bControlInterface = control_iface, + .bSubordinateInterface0 = data_iface, + } + }; + + // A plain data interface + interface_descriptors[data_iface] = (struct usb_interface_descriptor) { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = data_iface, + .bAlternateSetting = 0, + // OUT and IN + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + .endpoint = data_ep + }; + interfaces[data_iface] = (struct usb_interface) { + .num_altsetting = 1, .altsetting = &interface_descriptors[data_iface] + }; + + + interface_descriptors[control_iface] = (struct usb_interface_descriptor) { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = control_iface, + .bAlternateSetting = 0, + // Just one direction here + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_CDC, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_PROTOCOL_AT, + .iInterface = (uint8_t)(4 + i), + .endpoint = ctrl_ep, + .extra = &cdcacm_functional_descriptors[i], + .extralen = sizeof cdcacm_functional_descriptors[0] + }; + interfaces[control_iface] = (struct usb_interface){ + .num_altsetting = 1, .altsetting = &interface_descriptors[control_iface]}; + } + + usbd_dev = usbd_init(&otgfs_usb_driver, &dev, &config, + (char const * const *)usb_strings, (sizeof usb_strings) / sizeof(usb_strings[0]), + usbd_control_buffer, sizeof(usbd_control_buffer)); + + usbd_register_set_config_callback(usbd_dev, cdcacm_set_config); + nvic_enable_irq(NVIC_OTG_FS_IRQ); +} + +void otg_fs_isr() { usbd_poll(usbd_dev); } diff --git a/usb.h b/usb.h new file mode 100644 index 0000000..d7041a8 --- /dev/null +++ b/usb.h @@ -0,0 +1,4 @@ +#ifndef USB_H +#define USB_H +void usb_setup(void); +#endif