aboutsummaryrefslogtreecommitdiffstats
path: root/app/cdcacm.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/cdcacm.c')
-rw-r--r--app/cdcacm.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/app/cdcacm.c b/app/cdcacm.c
new file mode 100644
index 0000000..b300241
--- /dev/null
+++ b/app/cdcacm.c
@@ -0,0 +1,288 @@
+#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);
+}
+
+
+