#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 int cdcacm_ready = 0; #define COMM_EP 0x83 #define DATA_IN 0x01 #define DATA_OUT 0x82 static const struct usb_endpoint_descriptor comm_endp[] = { { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = COMM_EP, .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 = DATA_IN, .bmAttributes = USB_ENDPOINT_ATTR_BULK, .wMaxPacketSize = 64, .bInterval = 1, }, { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = DATA_OUT, .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 = 3, }, .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 = 2, .bSubordinateInterface0 = 3, } }; const struct usb_interface_descriptor comm_iface = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 2, .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) }; const struct usb_interface_descriptor data_iface = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 3, .bAlternateSetting = 0, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_DATA, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, .iInterface = 0, .endpoint = data_endp, }; const struct usb_iface_assoc_descriptor cdc_iface_assoc = { .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, .bFirstInterface = 2, .bInterfaceCount = 2, .bFunctionClass = USB_CLASS_CDC, .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, .bFunctionProtocol = USB_CDC_PROTOCOL_AT, .iFunction = 6, }; enum usbd_request_return_codes 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; 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_NEXT_CALLBACK; } void cdcacm_tick (void) { unsigned ep = DATA_OUT; uint8_t buf[16]; uint8_t *ptr = buf; size_t n = 0; if (!cdcacm_ready) return; if (ring_empty (&cdcacm_tx_ring)) return; /* Return if endpoint is already enabled. */ if (OTG_FS_DIEPTSIZ (ep & 0x7f) & OTG_DIEPSIZ0_PKTCNT) return; while (!ring_read_byte (&cdcacm_tx_ring, ptr++)) { n++; if (n == sizeof (buf)) break; } usbd_ep_write_packet (usb_device, ep, buf, n); } int cdcacm_write (char *ptr, int len, int blocking) { int ret; ret = ring_write (&cdcacm_tx_ring, (uint8_t *) ptr, len, blocking); return ret; } 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, DATA_IN, buf, 64); if (len) ring_write (&cdcacm_rx_ring, buf, len, 0); } void cdcacm_set_config (usbd_device *usbd_dev, uint16_t wValue) { (void) wValue; usbd_ep_setup (usbd_dev, DATA_IN, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb); usbd_ep_setup (usbd_dev, DATA_OUT, USB_ENDPOINT_ATTR_BULK, 64, NULL); usbd_ep_setup (usbd_dev, COMM_EP, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); cdcacm_ready = 1; } void cdcacm_rings_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)); }