aboutsummaryrefslogtreecommitdiff
path: root/example-fs-bb48/usb-cdc.c
diff options
context:
space:
mode:
Diffstat (limited to 'example-fs-bb48/usb-cdc.c')
-rw-r--r--example-fs-bb48/usb-cdc.c506
1 files changed, 506 insertions, 0 deletions
diff --git a/example-fs-bb48/usb-cdc.c b/example-fs-bb48/usb-cdc.c
new file mode 100644
index 0000000..290a4fd
--- /dev/null
+++ b/example-fs-bb48/usb-cdc.c
@@ -0,0 +1,506 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <chopstx.h>
+#include "usb_lld.h"
+#include "stream.h"
+
+static struct stream stream;
+
+#define USB_CDC_REQ_SET_LINE_CODING 0x20
+#define USB_CDC_REQ_GET_LINE_CODING 0x21
+#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22
+#define USB_CDC_REQ_SEND_BREAK 0x23
+
+/* USB Device Descriptor */
+static const uint8_t vcom_device_desc[18] = {
+ 18, /* bLength */
+ DEVICE_DESCRIPTOR, /* bDescriptorType */
+ 0x10, 0x01, /* bcdUSB = 1.1 */
+ 0x02, /* bDeviceClass (CDC). */
+ 0x00, /* bDeviceSubClass. */
+ 0x00, /* bDeviceProtocol. */
+ 0x40, /* bMaxPacketSize. */
+ 0xFF, 0xFF, /* idVendor */
+ 0x01, 0x00, /* idProduct */
+ 0x00, 0x01, /* bcdDevice */
+ 1, /* iManufacturer. */
+ 2, /* iProduct. */
+ 3, /* iSerialNumber. */
+ 1 /* bNumConfigurations. */
+};
+
+/* Configuration Descriptor tree for a CDC.*/
+static const uint8_t vcom_config_desc[67] = {
+ 9,
+ CONFIG_DESCRIPTOR, /* bDescriptorType: Configuration */
+ /* Configuration Descriptor.*/
+ 67, 0x00, /* wTotalLength. */
+ 0x02, /* bNumInterfaces. */
+ 0x01, /* bConfigurationValue. */
+ 0, /* iConfiguration. */
+ 0x80, /* bmAttributes (bus powered). */
+ 50, /* bMaxPower (100mA). */
+ /* Interface Descriptor.*/
+ 9,
+ INTERFACE_DESCRIPTOR,
+ 0x00, /* bInterfaceNumber. */
+ 0x00, /* bAlternateSetting. */
+ 0x01, /* bNumEndpoints. */
+ 0x02, /* bInterfaceClass (Communications Interface Class,
+ CDC section 4.2). */
+ 0x02, /* bInterfaceSubClass (Abstract Control Model, CDC
+ section 4.3). */
+ 0x01, /* bInterfaceProtocol (AT commands, CDC section
+ 4.4). */
+ 0, /* iInterface. */
+ /* Header Functional Descriptor (CDC section 5.2.3).*/
+ 5, /* bLength. */
+ 0x24, /* bDescriptorType (CS_INTERFACE). */
+ 0x00, /* bDescriptorSubtype (Header Functional Descriptor). */
+ 0x10, 0x01, /* bcdCDC. */
+ /* Call Management Functional Descriptor. */
+ 5, /* bFunctionLength. */
+ 0x24, /* bDescriptorType (CS_INTERFACE). */
+ 0x01, /* bDescriptorSubtype (Call Management Functional
+ Descriptor). */
+ 0x03, /* bmCapabilities (D0+D1). */
+ 0x01, /* bDataInterface. */
+ /* ACM Functional Descriptor.*/
+ 4, /* bFunctionLength. */
+ 0x24, /* bDescriptorType (CS_INTERFACE). */
+ 0x02, /* bDescriptorSubtype (Abstract Control Management
+ Descriptor). */
+ 0x02, /* bmCapabilities. */
+ /* Union Functional Descriptor.*/
+ 5, /* bFunctionLength. */
+ 0x24, /* bDescriptorType (CS_INTERFACE). */
+ 0x06, /* bDescriptorSubtype (Union Functional
+ Descriptor). */
+ 0x00, /* bMasterInterface (Communication Class
+ Interface). */
+ 0x01, /* bSlaveInterface0 (Data Class Interface). */
+ /* Endpoint 2 Descriptor.*/
+ 7,
+ ENDPOINT_DESCRIPTOR,
+ ENDP2|0x80, /* bEndpointAddress. */
+ 0x03, /* bmAttributes (Interrupt). */
+ 0x08, 0x00, /* wMaxPacketSize. */
+ 0xFF, /* bInterval. */
+ /* Interface Descriptor.*/
+ 9,
+ INTERFACE_DESCRIPTOR, /* bDescriptorType: */
+ 0x01, /* bInterfaceNumber. */
+ 0x00, /* bAlternateSetting. */
+ 0x02, /* bNumEndpoints. */
+ 0x0A, /* bInterfaceClass (Data Class Interface, CDC section 4.5). */
+ 0x00, /* bInterfaceSubClass (CDC section 4.6). */
+ 0x00, /* bInterfaceProtocol (CDC section 4.7). */
+ 0x00, /* iInterface. */
+ /* Endpoint 3 Descriptor.*/
+ 7,
+ ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */
+ ENDP3, /* bEndpointAddress. */
+ 0x02, /* bmAttributes (Bulk). */
+ 0x40, 0x00, /* wMaxPacketSize. */
+ 0x00, /* bInterval. */
+ /* Endpoint 1 Descriptor.*/
+ 7,
+ ENDPOINT_DESCRIPTOR, /* bDescriptorType: Endpoint */
+ ENDP1|0x80, /* bEndpointAddress. */
+ 0x02, /* bmAttributes (Bulk). */
+ 0x40, 0x00, /* wMaxPacketSize. */
+ 0x00 /* bInterval. */
+};
+
+
+/*
+ * U.S. English language identifier.
+ */
+static const uint8_t vcom_string0[4] = {
+ 4, /* bLength */
+ STRING_DESCRIPTOR,
+ 0x09, 0x04 /* LangID = 0x0409: US-English */
+};
+
+static const uint8_t vcom_string1[] = {
+ 23*2+2, /* bLength */
+ STRING_DESCRIPTOR, /* bDescriptorType */
+ /* Manufacturer: "Flying Stone Technology" */
+ 'F', 0, 'l', 0, 'y', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'S', 0,
+ 't', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'T', 0, 'e', 0, 'c', 0,
+ 'h', 0, 'n', 0, 'o', 0, 'l', 0, 'o', 0, 'g', 0, 'y', 0,
+};
+
+static const uint8_t vcom_string2[] = {
+ 14*2+2, /* bLength */
+ STRING_DESCRIPTOR, /* bDescriptorType */
+ /* Product name: "Chopstx Sample" */
+ 'C', 0, 'h', 0, 'o', 0, 'p', 0, 's', 0, 't', 0, 'x', 0, ' ', 0,
+ 'S', 0, 'a', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0,
+};
+
+/*
+ * Serial Number string.
+ */
+static const uint8_t vcom_string3[28] = {
+ 28, /* bLength */
+ STRING_DESCRIPTOR, /* bDescriptorType */
+ '0', 0, '.', 0, '0', 0, '0', 0, /* Version number */
+};
+
+
+#define NUM_INTERFACES 2
+
+uint32_t bDeviceState = UNCONNECTED; /* USB device status */
+
+
+void
+usb_cb_device_reset (void)
+{
+ usb_lld_reset (vcom_config_desc[7]);
+
+ /* Initialize Endpoint 0 */
+ usb_lld_setup_endpoint (ENDP0, 1, 1);
+
+ chopstx_mutex_lock (&stream.mtx);
+ stream.flags = 0;
+ bDeviceState = ATTACHED;
+ chopstx_mutex_unlock (&stream.mtx);
+}
+
+
+#define CDC_CTRL_DTR 0x0001
+
+void
+usb_cb_ctrl_write_finish (uint8_t req, uint8_t req_no, struct req_args *arg)
+{
+ uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
+
+ if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT)
+ && USB_SETUP_SET (req) && req_no == USB_CDC_REQ_SET_CONTROL_LINE_STATE)
+ {
+ /* Open/close the connection. */
+ chopstx_mutex_lock (&stream.mtx);
+ stream.flags &= ~FLAG_CONNECTED;
+ stream.flags |= ((arg->value & CDC_CTRL_DTR) != 0)? FLAG_CONNECTED : 0;
+ chopstx_cond_signal (&stream.cnd);
+ chopstx_mutex_unlock (&stream.mtx);
+ }
+}
+
+struct line_coding
+{
+ uint32_t bitrate;
+ uint8_t format;
+ uint8_t paritytype;
+ uint8_t datatype;
+} __attribute__((packed));
+
+static struct line_coding line_coding = {
+ 115200, /* baud rate: 115200 */
+ 0x00, /* stop bits: 1 */
+ 0x00, /* parity: none */
+ 0x08 /* bits: 8 */
+};
+
+
+static int
+vcom_port_data_setup (uint8_t req, uint8_t req_no, struct req_args *arg)
+{
+ if (USB_SETUP_GET (req))
+ {
+ if (req_no == USB_CDC_REQ_GET_LINE_CODING)
+ return usb_lld_reply_request (&line_coding, sizeof(line_coding), arg);
+ }
+ else /* USB_SETUP_SET (req) */
+ {
+ if (req_no == USB_CDC_REQ_SET_LINE_CODING
+ && arg->len == sizeof (line_coding))
+ {
+ usb_lld_set_data_to_recv (&line_coding, sizeof (line_coding));
+ return USB_SUCCESS;
+ }
+ else if (req_no == USB_CDC_REQ_SET_CONTROL_LINE_STATE)
+ return USB_SUCCESS;
+ }
+
+ return USB_UNSUPPORT;
+}
+
+int
+usb_cb_setup (uint8_t req, uint8_t req_no, struct req_args *arg)
+{
+ uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
+
+ if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0)
+ return vcom_port_data_setup (req, req_no, arg);
+
+ return USB_UNSUPPORT;
+}
+
+int
+usb_cb_get_descriptor (uint8_t rcp, uint8_t desc_type, uint8_t desc_index,
+ struct req_args *arg)
+{
+ if (rcp != DEVICE_RECIPIENT)
+ return USB_UNSUPPORT;
+
+ if (desc_type == DEVICE_DESCRIPTOR)
+ return usb_lld_reply_request (vcom_device_desc, sizeof (vcom_device_desc),
+ arg);
+ else if (desc_type == CONFIG_DESCRIPTOR)
+ return usb_lld_reply_request (vcom_config_desc, sizeof (vcom_config_desc),
+ arg);
+ else if (desc_type == STRING_DESCRIPTOR)
+ {
+ const uint8_t *str;
+ int size;
+
+ switch (desc_index)
+ {
+ case 0:
+ str = vcom_string0;
+ size = sizeof (vcom_string0);
+ break;
+ case 1:
+ str = vcom_string1;
+ size = sizeof (vcom_string1);
+ break;
+ case 2:
+ str = vcom_string2;
+ size = sizeof (vcom_string2);
+ break;
+ case 3:
+ str = vcom_string3;
+ size = sizeof (vcom_string3);
+ break;
+ default:
+ return USB_UNSUPPORT;
+ }
+
+ return usb_lld_reply_request (str, size, arg);
+ }
+
+ return USB_UNSUPPORT;
+}
+
+static void
+vcom_setup_endpoints_for_interface (uint16_t interface, int stop)
+{
+ if (interface == 0)
+ {
+ if (!stop)
+ usb_lld_setup_endpoint (ENDP2, 0, 1);
+ else
+ usb_lld_stall (ENDP2);
+ }
+ else if (interface == 1)
+ {
+ if (!stop)
+ {
+ usb_lld_setup_endpoint (ENDP1, 0, 1);
+ usb_lld_setup_endpoint (ENDP3, 1, 0);
+#if 0
+ /* Start with no data receiving */
+ usb_lld_stall (ENDP3);
+#endif
+ }
+ else
+ {
+ usb_lld_stall (ENDP1);
+ usb_lld_stall (ENDP3);
+ }
+ }
+}
+
+int
+usb_cb_handle_event (uint8_t event_type, uint16_t value)
+{
+ int i;
+ uint8_t current_conf;
+
+ switch (event_type)
+ {
+ case USB_EVENT_ADDRESS:
+ bDeviceState = ADDRESSED;
+ return USB_SUCCESS;
+ case USB_EVENT_CONFIG:
+ current_conf = usb_lld_current_configuration ();
+ if (current_conf == 0)
+ {
+ if (value != 1)
+ return USB_UNSUPPORT;
+
+ usb_lld_set_configuration (1);
+ for (i = 0; i < NUM_INTERFACES; i++)
+ vcom_setup_endpoints_for_interface (i, 0);
+ bDeviceState = CONFIGURED;
+ }
+ else if (current_conf != value)
+ {
+ if (value != 0)
+ return USB_UNSUPPORT;
+
+ usb_lld_set_configuration (0);
+ for (i = 0; i < NUM_INTERFACES; i++)
+ vcom_setup_endpoints_for_interface (i, 1);
+ bDeviceState = ADDRESSED;
+ }
+ /* Do nothing when current_conf == value */
+ return USB_SUCCESS;
+
+ return USB_SUCCESS;
+ default:
+ break;
+ }
+
+ return USB_UNSUPPORT;
+}
+
+
+int
+usb_cb_interface (uint8_t cmd, struct req_args *arg)
+{
+ const uint8_t zero = 0;
+ uint16_t interface = arg->index;
+ uint16_t alt = arg->value;
+
+ if (interface >= NUM_INTERFACES)
+ return USB_UNSUPPORT;
+
+ switch (cmd)
+ {
+ case USB_SET_INTERFACE:
+ if (alt != 0)
+ return USB_UNSUPPORT;
+ else
+ {
+ vcom_setup_endpoints_for_interface (interface, 0);
+ return USB_SUCCESS;
+ }
+
+ case USB_GET_INTERFACE:
+ return usb_lld_reply_request (&zero, 1, arg);
+
+ default:
+ case USB_QUERY_INTERFACE:
+ return USB_SUCCESS;
+ }
+}
+
+
+void
+usb_cb_tx_done (uint8_t ep_num)
+{
+ if (ep_num == ENDP1)
+ {
+ chopstx_mutex_lock (&stream.mtx);
+ if ((stream.flags & FLAG_SEND_AVAIL))
+ {
+ stream.flags &= ~FLAG_SEND_AVAIL;
+ chopstx_cond_signal (&stream.cnd);
+ }
+ chopstx_mutex_unlock (&stream.mtx);
+ }
+ else if (ep_num == ENDP2)
+ {
+ }
+}
+
+void
+usb_cb_rx_ready (uint8_t ep_num)
+{
+ if (ep_num == ENDP3)
+ {
+ chopstx_mutex_lock (&stream.mtx);
+ if ((stream.flags & FLAG_RECV_AVAIL) == 0)
+ {
+ stream.flags |= FLAG_RECV_AVAIL;
+ chopstx_cond_signal (&stream.cnd);
+ }
+ chopstx_mutex_unlock (&stream.mtx);
+ }
+}
+
+struct stream *
+stream_open (void)
+{
+ chopstx_mutex_init (&stream.mtx);
+ chopstx_cond_init (&stream.cnd);
+ return &stream;
+}
+
+int
+stream_wait_connection (struct stream *st)
+{
+ chopstx_mutex_lock (&st->mtx);
+ while ((stream.flags & FLAG_CONNECTED) == 0)
+ chopstx_cond_wait (&st->cnd, &st->mtx);
+ chopstx_mutex_unlock (&st->mtx);
+ stream.flags &= ~FLAG_SEND_AVAIL;
+ return 0;
+}
+
+
+int
+stream_send (struct stream *st, uint8_t *buf, uint8_t count)
+{
+ int r = 0;
+
+ chopstx_mutex_lock (&st->mtx);
+ if ((stream.flags & FLAG_CONNECTED) == 0)
+ r = -1;
+ else
+ {
+ usb_lld_tx_enable (ENDP1, buf, count);
+ stream.flags |= FLAG_SEND_AVAIL;
+ do
+ {
+ chopstx_cond_wait (&st->cnd, &st->mtx);
+ if ((stream.flags & FLAG_SEND_AVAIL) == 0)
+ break;
+ else if ((stream.flags & FLAG_CONNECTED) == 0)
+ {
+ r = -1;
+ break;
+ }
+ }
+ while (1);
+ }
+ chopstx_mutex_unlock (&st->mtx);
+ return r;
+}
+
+
+int
+stream_recv (struct stream *st, uint8_t *buf)
+{
+ int r;
+
+ chopstx_mutex_lock (&st->mtx);
+ if ((stream.flags & FLAG_CONNECTED) == 0)
+ r = -1;
+ else
+ {
+ usb_lld_rx_enable (ENDP3, buf, 64);
+ stream.flags &= ~FLAG_RECV_AVAIL;
+ do
+ {
+ chopstx_cond_wait (&st->cnd, &st->mtx);
+ if ((stream.flags & FLAG_RECV_AVAIL))
+ {
+ r = usb_lld_rx_data_len (ENDP3);
+ break;
+ }
+ else if ((stream.flags & FLAG_CONNECTED) == 0)
+ {
+ r = -1;
+ break;
+ }
+ }
+ while (1);
+ }
+ chopstx_mutex_unlock (&st->mtx);
+
+ return r;
+}