summaryrefslogtreecommitdiffstats
path: root/app/cdcacm.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/cdcacm.c')
-rw-r--r--app/cdcacm.c213
1 files changed, 213 insertions, 0 deletions
diff --git a/app/cdcacm.c b/app/cdcacm.c
new file mode 100644
index 0000000..213592e
--- /dev/null
+++ b/app/cdcacm.c
@@ -0,0 +1,213 @@
+#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;
+
+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,
+ }
+};
+
+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)
+};
+
+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 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;
+
+ if (dfu_control_request (usbd_dev, req, buf, len, complete))
+ return 1;
+
+
+ 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 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 (!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, 0x01, 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, 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);
+
+ 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));
+}
+
+