#include "project.h" #define BUFFER_SIZE 512 ring_t cdcacm_rx_ring; static uint8_t cdcacm_rx_ring_buf[BUFFER_SIZE]; ring_t cdcacm_tx_ring; static uint8_t cdcacm_tx_ring_buf[BUFFER_SIZE]; static const struct usb_device_descriptor dev = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = 0x0200, .bDeviceClass = USB_CLASS_CDC, .bDeviceSubClass = 0, .bDeviceProtocol = 0, .bMaxPacketSize0 = 64, .idVendor = ID_VENDOR, .idProduct = ID_PRODUCT, .bcdDevice = 0x0200, .iManufacturer = 1, .iProduct = 2, .iSerialNumber = 3, .bNumConfigurations = 1, }; static const struct usb_endpoint_descriptor comm_endp[] = { { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x83, .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, .wMaxPacketSize = 16, .bInterval = 255, } }; static const struct usb_endpoint_descriptor data_endp[] = { { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x01, .bmAttributes = USB_ENDPOINT_ATTR_BULK, .wMaxPacketSize = 64, .bInterval = 1, }, { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x82, .bmAttributes = USB_ENDPOINT_ATTR_BULK, .wMaxPacketSize = 64, .bInterval = 1, } }; static const struct { 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)) cdcacm_functional_descriptors = { .header = { .bFunctionLength = sizeof (struct usb_cdc_header_descriptor), .bDescriptorType = CS_INTERFACE, .bDescriptorSubtype = USB_CDC_TYPE_HEADER, .bcdCDC = 0x0110, }, .call_mgmt = { .bFunctionLength = sizeof (struct usb_cdc_call_management_descriptor), .bDescriptorType = CS_INTERFACE, .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT, .bmCapabilities = 0, .bDataInterface = 1, }, .acm = { .bFunctionLength = sizeof (struct usb_cdc_acm_descriptor), .bDescriptorType = CS_INTERFACE, .bDescriptorSubtype = USB_CDC_TYPE_ACM, .bmCapabilities = 0, }, .cdc_union = { .bFunctionLength = sizeof (struct usb_cdc_union_descriptor), .bDescriptorType = CS_INTERFACE, .bDescriptorSubtype = USB_CDC_TYPE_UNION, .bControlInterface = 0, .bSubordinateInterface0 = 1, } }; static const struct usb_interface_descriptor comm_iface = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_CDC, .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, .bInterfaceProtocol = USB_CDC_PROTOCOL_AT, .iInterface = 0, .endpoint = comm_endp, .extra = &cdcacm_functional_descriptors, .extralen = sizeof (cdcacm_functional_descriptors) }; static const struct usb_interface_descriptor data_iface = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 1, .bAlternateSetting = 0, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_DATA, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, .iInterface = 0, .endpoint = data_endp, }; static const struct usb_interface ifaces[] = { { .num_altsetting = 1, .altsetting = &comm_iface, }, { .num_altsetting = 1, .altsetting = &data_iface, }, #ifdef INCLUDE_DFU_INTERFACE { .num_altsetting = 1, .altsetting = &dfu_iface, }, #endif }; static const struct usb_config_descriptor config = { .bLength = USB_DT_CONFIGURATION_SIZE, .bDescriptorType = USB_DT_CONFIGURATION, .wTotalLength = 0, #ifdef INCLUDE_DFU_INTERFACE .bNumInterfaces = 3, #else .bNumInterfaces = 2, #endif .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = 0x80, .bMaxPower = 0x32, .interface = ifaces, }; static const char *usb_strings[] = { "Meh", "Fish", "Soup", #ifdef INCLUDE_DFU_INTERFACE "DFU", #endif }; /* Buffer to be used for control requests. */ uint8_t usbd_control_buffer[128]; usbd_device *usbd_dev; static int cdcacm_control_request (usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete) { (void) complete; (void) buf; (void) usbd_dev; #ifdef INCLUDE_DFU_INTERFACE if (dfu_control_request (usbd_dev, req, buf, len, complete)) { return 1; } #endif 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. */ char local_buf[10]; struct usb_cdc_notification *notif = (void *) local_buf; /* We echo signals back to host as notification. */ notif->bmRequestType = 0xA1; notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE; notif->wValue = 0; notif->wIndex = 0; notif->wLength = 2; local_buf[8] = req->wValue & 3; local_buf[9] = 0; // usbd_ep_write_packet(0x83, buf, 10); return 1; } case USB_CDC_REQ_SET_LINE_CODING: if (*len < sizeof (struct usb_cdc_line_coding)) { return 0; } return 1; } return 0; } void cdcacm_tick (void) { unsigned ep = 0x82; uint8_t buf[16]; uint8_t *ptr = buf; size_t n = 0; if (ring_empty (&cdcacm_tx_ring)) { return; } if ( (*USB_EP_REG (ep & 0x7f) & USB_EP_TX_STAT) == USB_EP_TX_STAT_VALID) { return; } while (!ring_read_byte (&cdcacm_tx_ring, ptr++)) { n++; } usbd_ep_write_packet (usbd_dev, ep, buf, n); } static void cdcacm_data_rx_cb (usbd_device *usbd_dev, uint8_t ep) { (void) ep; uint8_t buf[64]; int len = usbd_ep_read_packet (usbd_dev, 0x01, buf, 64); if (len) { ring_write (&cdcacm_rx_ring, buf, len); } } static void cdcacm_set_config (usbd_device *usbd_dev, uint16_t wValue) { (void) wValue; usbd_ep_setup (usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb); usbd_ep_setup (usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL); usbd_ep_setup (usbd_dev, 0x83, 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); } void usb_init (void) { ring_init (&cdcacm_rx_ring, cdcacm_rx_ring_buf, sizeof (cdcacm_rx_ring_buf)); ring_init (&cdcacm_tx_ring, cdcacm_tx_ring_buf, sizeof (cdcacm_tx_ring_buf)); usbd_dev = usbd_init (&stm32f103_usb_driver, &dev, &config, usb_strings, #ifdef INCLUDE_DFU_INTERFACE 4, #else 3, #endif usbd_control_buffer, sizeof (usbd_control_buffer)); usbd_register_set_config_callback (usbd_dev, cdcacm_set_config); }