@@ -401,6 +401,12 @@ static void endpoint0_setup(uint64_t setupdata) | |||
#elif defined(SEREMU_INTERFACE) | |||
usb_seremu_configure(); | |||
#endif | |||
#if defined(CDC2_STATUS_INTERFACE) && defined(CDC2_DATA_INTERFACE) | |||
usb_serial2_configure(); | |||
#endif | |||
#if defined(CDC3_STATUS_INTERFACE) && defined(CDC3_DATA_INTERFACE) | |||
usb_serial3_configure(); | |||
#endif | |||
#if defined(RAWHID_INTERFACE) | |||
usb_rawhid_configure(); | |||
#endif | |||
@@ -508,8 +514,24 @@ static void endpoint0_setup(uint64_t setupdata) | |||
break; | |||
#if defined(CDC_STATUS_INTERFACE) | |||
case 0x2221: // CDC_SET_CONTROL_LINE_STATE | |||
usb_cdc_line_rtsdtr_millis = systick_millis_count; | |||
usb_cdc_line_rtsdtr = setup.wValue; | |||
#ifdef CDC_STATUS_INTERFACE | |||
if (setup.wIndex == CDC_STATUS_INTERFACE) { | |||
usb_cdc_line_rtsdtr_millis = systick_millis_count; | |||
usb_cdc_line_rtsdtr = setup.wValue; | |||
} | |||
#endif | |||
#ifdef CDC2_STATUS_INTERFACE | |||
if (setup.wIndex == CDC2_STATUS_INTERFACE) { | |||
usb_cdc2_line_rtsdtr_millis = systick_millis_count; | |||
usb_cdc2_line_rtsdtr = setup.wValue; | |||
} | |||
#endif | |||
#ifdef CDC3_STATUS_INTERFACE | |||
if (setup.wIndex == CDC3_STATUS_INTERFACE) { | |||
usb_cdc3_line_rtsdtr_millis = systick_millis_count; | |||
usb_cdc3_line_rtsdtr = setup.wValue; | |||
} | |||
#endif | |||
case 0x2321: // CDC_SEND_BREAK | |||
endpoint0_receive(NULL, 0, 0); | |||
return; | |||
@@ -682,7 +704,8 @@ static void endpoint0_complete(void) | |||
setup.bothwords = endpoint0_setupdata.bothwords; | |||
//printf("complete %x %x %x\n", setup.word1, setup.word2, endpoint0_buffer[0]); | |||
#ifdef CDC_STATUS_INTERFACE | |||
if (setup.wRequestAndType == 0x2021 /*CDC_SET_LINE_CODING*/) { | |||
// 0x2021 is CDC_SET_LINE_CODING | |||
if (setup.wRequestAndType == 0x2021 && setup.wIndex == CDC_STATUS_INTERFACE) { | |||
memcpy(usb_cdc_line_coding, endpoint0_buffer, 7); | |||
printf("usb_cdc_line_coding, baud=%u\n", usb_cdc_line_coding[0]); | |||
if (usb_cdc_line_coding[0] == 134) { | |||
@@ -691,6 +714,26 @@ static void endpoint0_complete(void) | |||
} | |||
} | |||
#endif | |||
#ifdef CDC2_STATUS_INTERFACE | |||
if (setup.wRequestAndType == 0x2021 && setup.wIndex == CDC2_STATUS_INTERFACE) { | |||
memcpy(usb_cdc2_line_coding, endpoint0_buffer, 7); | |||
printf("usb_cdc2_line_coding, baud=%u\n", usb_cdc2_line_coding[0]); | |||
if (usb_cdc2_line_coding[0] == 134) { | |||
usb_start_sof_interrupts(NUM_INTERFACE); | |||
usb_reboot_timer = 80; // TODO: 10 if only 12 Mbit/sec | |||
} | |||
} | |||
#endif | |||
#ifdef CDC3_STATUS_INTERFACE | |||
if (setup.wRequestAndType == 0x2021 && setup.wIndex == CDC3_STATUS_INTERFACE) { | |||
memcpy(usb_cdc3_line_coding, endpoint0_buffer, 7); | |||
printf("usb_cdc3_line_coding, baud=%u\n", usb_cdc3_line_coding[0]); | |||
if (usb_cdc3_line_coding[0] == 134) { | |||
usb_start_sof_interrupts(NUM_INTERFACE); | |||
usb_reboot_timer = 80; // TODO: 10 if only 12 Mbit/sec | |||
} | |||
} | |||
#endif | |||
#ifdef KEYBOARD_INTERFACE | |||
if (setup.word1 == 0x02000921 && setup.word2 == ((1 << 16) | KEYBOARD_INTERFACE)) { | |||
keyboard_leds = endpoint0_buffer[0]; | |||
@@ -709,7 +752,6 @@ static void endpoint0_complete(void) | |||
#ifdef AUDIO_INTERFACE | |||
if (setup.word1 == 0x02010121 /* TODO: check setup.word2 */) { | |||
usb_audio_set_feature(&endpoint0_setupdata, endpoint0_buffer); | |||
// TODO: usb_audio_set_feature() | |||
} | |||
#endif | |||
} |
@@ -503,7 +503,21 @@ static uint8_t flightsim_report_desc[] = { | |||
#define CDC_DATA_INTERFACE_DESC_SIZE 0 | |||
#endif | |||
#define MIDI_INTERFACE_DESC_POS CDC_DATA_INTERFACE_DESC_POS+CDC_DATA_INTERFACE_DESC_SIZE | |||
#define CDC2_DATA_INTERFACE_DESC_POS CDC_DATA_INTERFACE_DESC_POS+CDC_DATA_INTERFACE_DESC_SIZE | |||
#ifdef CDC2_DATA_INTERFACE | |||
#define CDC2_DATA_INTERFACE_DESC_SIZE 8 + 9+5+5+4+5+7+9+7+7 | |||
#else | |||
#define CDC2_DATA_INTERFACE_DESC_SIZE 0 | |||
#endif | |||
#define CDC3_DATA_INTERFACE_DESC_POS CDC2_DATA_INTERFACE_DESC_POS+CDC2_DATA_INTERFACE_DESC_SIZE | |||
#ifdef CDC3_DATA_INTERFACE | |||
#define CDC3_DATA_INTERFACE_DESC_SIZE 8 + 9+5+5+4+5+7+9+7+7 | |||
#else | |||
#define CDC3_DATA_INTERFACE_DESC_SIZE 0 | |||
#endif | |||
#define MIDI_INTERFACE_DESC_POS CDC3_DATA_INTERFACE_DESC_POS+CDC3_DATA_INTERFACE_DESC_SIZE | |||
#ifdef MIDI_INTERFACE | |||
#if !defined(MIDI_NUM_CABLES) || MIDI_NUM_CABLES < 1 || MIDI_NUM_CABLES > 16 | |||
#error "MIDI_NUM_CABLES must be defined between 1 to 16" | |||
@@ -666,7 +680,7 @@ PROGMEM const uint8_t usb_config_descriptor_480[CONFIG_DESC_SIZE] = { | |||
CDC_ACM_ENDPOINT | 0x80, // bEndpointAddress | |||
0x03, // bmAttributes (0x03=intr) | |||
LSB(CDC_ACM_SIZE),MSB(CDC_ACM_SIZE), // wMaxPacketSize | |||
16, // bInterval | |||
5, // bInterval | |||
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 | |||
9, // bLength | |||
4, // bDescriptorType | |||
@@ -693,6 +707,158 @@ PROGMEM const uint8_t usb_config_descriptor_480[CONFIG_DESC_SIZE] = { | |||
0, // bInterval | |||
#endif // CDC_DATA_INTERFACE | |||
#ifdef CDC2_DATA_INTERFACE | |||
// configuration for 480 Mbit/sec speed | |||
// interface association descriptor, USB ECN, Table 9-Z | |||
8, // bLength | |||
11, // bDescriptorType | |||
CDC2_STATUS_INTERFACE, // bFirstInterface | |||
2, // bInterfaceCount | |||
0x02, // bFunctionClass | |||
0x02, // bFunctionSubClass | |||
0x01, // bFunctionProtocol | |||
0, // iFunction | |||
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 | |||
9, // bLength | |||
4, // bDescriptorType | |||
CDC2_STATUS_INTERFACE, // bInterfaceNumber | |||
0, // bAlternateSetting | |||
1, // bNumEndpoints | |||
0x02, // bInterfaceClass | |||
0x02, // bInterfaceSubClass | |||
0x01, // bInterfaceProtocol | |||
0, // iInterface | |||
// CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x00, // bDescriptorSubtype | |||
0x10, 0x01, // bcdCDC | |||
// Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x01, // bDescriptorSubtype | |||
0x01, // bmCapabilities | |||
1, // bDataInterface | |||
// Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 | |||
4, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x02, // bDescriptorSubtype | |||
0x06, // bmCapabilities | |||
// Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x06, // bDescriptorSubtype | |||
CDC2_STATUS_INTERFACE, // bMasterInterface | |||
CDC2_DATA_INTERFACE, // bSlaveInterface0 | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC2_ACM_ENDPOINT | 0x80, // bEndpointAddress | |||
0x03, // bmAttributes (0x03=intr) | |||
CDC_ACM_SIZE, 0, // wMaxPacketSize | |||
5, // bInterval | |||
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 | |||
9, // bLength | |||
4, // bDescriptorType | |||
CDC2_DATA_INTERFACE, // bInterfaceNumber | |||
0, // bAlternateSetting | |||
2, // bNumEndpoints | |||
0x0A, // bInterfaceClass | |||
0x00, // bInterfaceSubClass | |||
0x00, // bInterfaceProtocol | |||
0, // iInterface | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC2_RX_ENDPOINT, // bEndpointAddress | |||
0x02, // bmAttributes (0x02=bulk) | |||
LSB(CDC_RX_SIZE_480),MSB(CDC_RX_SIZE_480),// wMaxPacketSize | |||
0, // bInterval | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC2_TX_ENDPOINT | 0x80, // bEndpointAddress | |||
0x02, // bmAttributes (0x02=bulk) | |||
LSB(CDC_TX_SIZE_480),MSB(CDC_TX_SIZE_480),// wMaxPacketSize | |||
0, // bInterval | |||
#endif // CDC2_DATA_INTERFACE | |||
#ifdef CDC3_DATA_INTERFACE | |||
// configuration for 480 Mbit/sec speed | |||
// interface association descriptor, USB ECN, Table 9-Z | |||
8, // bLength | |||
11, // bDescriptorType | |||
CDC3_STATUS_INTERFACE, // bFirstInterface | |||
2, // bInterfaceCount | |||
0x02, // bFunctionClass | |||
0x02, // bFunctionSubClass | |||
0x01, // bFunctionProtocol | |||
0, // iFunction | |||
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 | |||
9, // bLength | |||
4, // bDescriptorType | |||
CDC3_STATUS_INTERFACE, // bInterfaceNumber | |||
0, // bAlternateSetting | |||
1, // bNumEndpoints | |||
0x02, // bInterfaceClass | |||
0x02, // bInterfaceSubClass | |||
0x01, // bInterfaceProtocol | |||
0, // iInterface | |||
// CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x00, // bDescriptorSubtype | |||
0x10, 0x01, // bcdCDC | |||
// Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x01, // bDescriptorSubtype | |||
0x01, // bmCapabilities | |||
1, // bDataInterface | |||
// Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 | |||
4, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x02, // bDescriptorSubtype | |||
0x06, // bmCapabilities | |||
// Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x06, // bDescriptorSubtype | |||
CDC3_STATUS_INTERFACE, // bMasterInterface | |||
CDC3_DATA_INTERFACE, // bSlaveInterface0 | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC3_ACM_ENDPOINT | 0x80, // bEndpointAddress | |||
0x03, // bmAttributes (0x03=intr) | |||
CDC_ACM_SIZE, 0, // wMaxPacketSize | |||
5, // bInterval | |||
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 | |||
9, // bLength | |||
4, // bDescriptorType | |||
CDC3_DATA_INTERFACE, // bInterfaceNumber | |||
0, // bAlternateSetting | |||
2, // bNumEndpoints | |||
0x0A, // bInterfaceClass | |||
0x00, // bInterfaceSubClass | |||
0x00, // bInterfaceProtocol | |||
0, // iInterface | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC3_RX_ENDPOINT, // bEndpointAddress | |||
0x02, // bmAttributes (0x02=bulk) | |||
LSB(CDC_RX_SIZE_480),MSB(CDC_RX_SIZE_480),// wMaxPacketSize | |||
0, // bInterval | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC3_TX_ENDPOINT | 0x80, // bEndpointAddress | |||
0x02, // bmAttributes (0x02=bulk) | |||
LSB(CDC_TX_SIZE_480),MSB(CDC_TX_SIZE_480),// wMaxPacketSize | |||
0, // bInterval | |||
#endif // CDC3_DATA_INTERFACE | |||
#ifdef MIDI_INTERFACE | |||
// configuration for 480 Mbit/sec speed | |||
// Standard MS Interface Descriptor, | |||
@@ -1527,6 +1693,158 @@ PROGMEM const uint8_t usb_config_descriptor_12[CONFIG_DESC_SIZE] = { | |||
0, // bInterval | |||
#endif // CDC_DATA_INTERFACE | |||
#ifdef CDC2_DATA_INTERFACE | |||
// configuration for 12 Mbit/sec speed | |||
// interface association descriptor, USB ECN, Table 9-Z | |||
8, // bLength | |||
11, // bDescriptorType | |||
CDC2_STATUS_INTERFACE, // bFirstInterface | |||
2, // bInterfaceCount | |||
0x02, // bFunctionClass | |||
0x02, // bFunctionSubClass | |||
0x01, // bFunctionProtocol | |||
0, // iFunction | |||
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 | |||
9, // bLength | |||
4, // bDescriptorType | |||
CDC2_STATUS_INTERFACE, // bInterfaceNumber | |||
0, // bAlternateSetting | |||
1, // bNumEndpoints | |||
0x02, // bInterfaceClass | |||
0x02, // bInterfaceSubClass | |||
0x01, // bInterfaceProtocol | |||
0, // iInterface | |||
// CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x00, // bDescriptorSubtype | |||
0x10, 0x01, // bcdCDC | |||
// Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x01, // bDescriptorSubtype | |||
0x01, // bmCapabilities | |||
1, // bDataInterface | |||
// Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 | |||
4, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x02, // bDescriptorSubtype | |||
0x06, // bmCapabilities | |||
// Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x06, // bDescriptorSubtype | |||
CDC2_STATUS_INTERFACE, // bMasterInterface | |||
CDC2_DATA_INTERFACE, // bSlaveInterface0 | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC2_ACM_ENDPOINT | 0x80, // bEndpointAddress | |||
0x03, // bmAttributes (0x03=intr) | |||
CDC_ACM_SIZE, 0, // wMaxPacketSize | |||
64, // bInterval | |||
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 | |||
9, // bLength | |||
4, // bDescriptorType | |||
CDC2_DATA_INTERFACE, // bInterfaceNumber | |||
0, // bAlternateSetting | |||
2, // bNumEndpoints | |||
0x0A, // bInterfaceClass | |||
0x00, // bInterfaceSubClass | |||
0x00, // bInterfaceProtocol | |||
0, // iInterface | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC2_RX_ENDPOINT, // bEndpointAddress | |||
0x02, // bmAttributes (0x02=bulk) | |||
CDC_RX_SIZE_12, 0, // wMaxPacketSize | |||
0, // bInterval | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC2_TX_ENDPOINT | 0x80, // bEndpointAddress | |||
0x02, // bmAttributes (0x02=bulk) | |||
CDC_TX_SIZE_12, 0, // wMaxPacketSize | |||
0, // bInterval | |||
#endif // CDC2_DATA_INTERFACE | |||
#ifdef CDC3_DATA_INTERFACE | |||
// configuration for 12 Mbit/sec speed | |||
// interface association descriptor, USB ECN, Table 9-Z | |||
8, // bLength | |||
11, // bDescriptorType | |||
CDC3_STATUS_INTERFACE, // bFirstInterface | |||
2, // bInterfaceCount | |||
0x02, // bFunctionClass | |||
0x02, // bFunctionSubClass | |||
0x01, // bFunctionProtocol | |||
0, // iFunction | |||
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 | |||
9, // bLength | |||
4, // bDescriptorType | |||
CDC3_STATUS_INTERFACE, // bInterfaceNumber | |||
0, // bAlternateSetting | |||
1, // bNumEndpoints | |||
0x02, // bInterfaceClass | |||
0x02, // bInterfaceSubClass | |||
0x01, // bInterfaceProtocol | |||
0, // iInterface | |||
// CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x00, // bDescriptorSubtype | |||
0x10, 0x01, // bcdCDC | |||
// Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x01, // bDescriptorSubtype | |||
0x01, // bmCapabilities | |||
1, // bDataInterface | |||
// Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 | |||
4, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x02, // bDescriptorSubtype | |||
0x06, // bmCapabilities | |||
// Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 | |||
5, // bFunctionLength | |||
0x24, // bDescriptorType | |||
0x06, // bDescriptorSubtype | |||
CDC3_STATUS_INTERFACE, // bMasterInterface | |||
CDC3_DATA_INTERFACE, // bSlaveInterface0 | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC3_ACM_ENDPOINT | 0x80, // bEndpointAddress | |||
0x03, // bmAttributes (0x03=intr) | |||
CDC_ACM_SIZE, 0, // wMaxPacketSize | |||
64, // bInterval | |||
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 | |||
9, // bLength | |||
4, // bDescriptorType | |||
CDC3_DATA_INTERFACE, // bInterfaceNumber | |||
0, // bAlternateSetting | |||
2, // bNumEndpoints | |||
0x0A, // bInterfaceClass | |||
0x00, // bInterfaceSubClass | |||
0x00, // bInterfaceProtocol | |||
0, // iInterface | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC3_RX_ENDPOINT, // bEndpointAddress | |||
0x02, // bmAttributes (0x02=bulk) | |||
CDC_RX_SIZE_12, 0, // wMaxPacketSize | |||
0, // bInterval | |||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 | |||
7, // bLength | |||
5, // bDescriptorType | |||
CDC3_TX_ENDPOINT | 0x80, // bEndpointAddress | |||
0x02, // bmAttributes (0x02=bulk) | |||
CDC_TX_SIZE_12, 0, // wMaxPacketSize | |||
0, // bInterval | |||
#endif // CDC3_DATA_INTERFACE | |||
#ifdef MIDI_INTERFACE | |||
// configuration for 12 Mbit/sec speed | |||
// Standard MS Interface Descriptor, |
@@ -137,6 +137,75 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports | |||
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_UNUSED | |||
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_BULK | |||
#elif defined(USB_DUAL_SERIAL) | |||
#define VENDOR_ID 0x16C0 | |||
#define PRODUCT_ID 0x048B | |||
#define MANUFACTURER_NAME {'T','e','e','n','s','y','d','u','i','n','o'} | |||
#define MANUFACTURER_NAME_LEN 11 | |||
#define PRODUCT_NAME {'D','u','a','l',' ','S','e','r','i','a','l'} | |||
#define PRODUCT_NAME_LEN 11 | |||
#define EP0_SIZE 64 | |||
#define NUM_ENDPOINTS 5 | |||
#define NUM_INTERFACE 4 | |||
#define CDC_IAD_DESCRIPTOR 1 // Serial | |||
#define CDC_STATUS_INTERFACE 0 | |||
#define CDC_DATA_INTERFACE 1 | |||
#define CDC_ACM_ENDPOINT 2 | |||
#define CDC_RX_ENDPOINT 3 | |||
#define CDC_TX_ENDPOINT 3 | |||
#define CDC_ACM_SIZE 16 | |||
#define CDC_RX_SIZE_480 512 | |||
#define CDC_TX_SIZE_480 512 | |||
#define CDC_RX_SIZE_12 64 | |||
#define CDC_TX_SIZE_12 64 | |||
#define CDC2_STATUS_INTERFACE 2 // SerialA | |||
#define CDC2_DATA_INTERFACE 3 | |||
#define CDC2_ACM_ENDPOINT 4 | |||
#define CDC2_RX_ENDPOINT 5 | |||
#define CDC2_TX_ENDPOINT 5 | |||
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT | |||
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK | |||
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT | |||
#define ENDPOINT5_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK | |||
#elif defined(USB_TRIPLE_SERIAL) | |||
#define VENDOR_ID 0x16C0 | |||
#define PRODUCT_ID 0x048C | |||
#define MANUFACTURER_NAME {'T','e','e','n','s','y','d','u','i','n','o'} | |||
#define MANUFACTURER_NAME_LEN 11 | |||
#define PRODUCT_NAME {'T','r','i','p','l','e',' ','S','e','r','i','a','l'} | |||
#define PRODUCT_NAME_LEN 13 | |||
#define EP0_SIZE 64 | |||
#define NUM_ENDPOINTS 7 | |||
#define NUM_INTERFACE 6 | |||
#define CDC_IAD_DESCRIPTOR 1 // Serial | |||
#define CDC_STATUS_INTERFACE 0 | |||
#define CDC_DATA_INTERFACE 1 | |||
#define CDC_ACM_ENDPOINT 2 | |||
#define CDC_RX_ENDPOINT 3 | |||
#define CDC_TX_ENDPOINT 3 | |||
#define CDC_ACM_SIZE 16 | |||
#define CDC_RX_SIZE_480 512 | |||
#define CDC_TX_SIZE_480 512 | |||
#define CDC_RX_SIZE_12 64 | |||
#define CDC_TX_SIZE_12 64 | |||
#define CDC2_STATUS_INTERFACE 2 // SerialA | |||
#define CDC2_DATA_INTERFACE 3 | |||
#define CDC2_ACM_ENDPOINT 4 | |||
#define CDC2_RX_ENDPOINT 5 | |||
#define CDC2_TX_ENDPOINT 5 | |||
#define CDC3_STATUS_INTERFACE 4 // SerialB | |||
#define CDC3_DATA_INTERFACE 5 | |||
#define CDC3_ACM_ENDPOINT 6 | |||
#define CDC3_RX_ENDPOINT 7 | |||
#define CDC3_TX_ENDPOINT 7 | |||
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT | |||
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK | |||
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT | |||
#define ENDPOINT5_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK | |||
#define ENDPOINT6_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT | |||
#define ENDPOINT7_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK | |||
#elif defined(USB_KEYBOARDONLY) | |||
#define VENDOR_ID 0x16C0 | |||
#define PRODUCT_ID 0x04D0 |
@@ -31,11 +31,10 @@ | |||
#pragma once | |||
#include "usb_desc.h" | |||
#include <stdint.h> | |||
#if (defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE)) || defined(USB_DISABLED) | |||
#include <stdint.h> | |||
// C language implementation | |||
#ifdef __cplusplus | |||
extern "C" { | |||
@@ -124,6 +123,186 @@ extern usb_serial_class Serial; | |||
extern void serialEvent(void); | |||
#endif // __cplusplus | |||
#endif // CDC_STATUS_INTERFACE && CDC_DATA_INTERFACE | |||
#if defined(CDC2_STATUS_INTERFACE) && defined(CDC2_DATA_INTERFACE) | |||
// C language implementation | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
void usb_serial2_configure(void); | |||
int usb_serial2_getchar(void); | |||
int usb_serial2_peekchar(void); | |||
int usb_serial2_available(void); | |||
int usb_serial2_read(void *buffer, uint32_t size); | |||
void usb_serial2_flush_input(void); | |||
int usb_serial2_putchar(uint8_t c); | |||
int usb_serial2_write(const void *buffer, uint32_t size); | |||
int usb_serial2_write_buffer_free(void); | |||
void usb_serial2_flush_output(void); | |||
extern uint32_t usb_cdc2_line_coding[2]; | |||
extern volatile uint32_t usb_cdc2_line_rtsdtr_millis; | |||
extern volatile uint8_t usb_cdc2_line_rtsdtr; | |||
extern volatile uint8_t usb_cdc2_transmit_flush_timer; | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
// C++ interface | |||
#ifdef __cplusplus | |||
#include "Stream.h" | |||
class usb_serial2_class : public Stream | |||
{ | |||
public: | |||
constexpr usb_serial2_class() {} | |||
void begin(long) { | |||
//uint32_t millis_begin = systick_millis_count; | |||
//disabled for now - causes more trouble than it solves? | |||
//while (!(*this)) { | |||
// wait up to 2.5 seconds for Arduino Serial Monitor | |||
// Yes, this is a long time, but some Windows systems open | |||
// the port very slowly. This wait allows programs for | |||
// Arduino Uno to "just work" (without forcing a reboot when | |||
// the port is opened), and when no PC is connected the user's | |||
// sketch still gets to run normally after this wait time. | |||
//if ((uint32_t)(systick_millis_count - millis_begin) > 2500) break; | |||
//} | |||
} | |||
void end() { /* TODO: flush output and shut down USB port */ }; | |||
virtual int available() { return usb_serial2_available(); } | |||
virtual int read() { return usb_serial2_getchar(); } | |||
virtual int peek() { return usb_serial2_peekchar(); } | |||
virtual void flush() { usb_serial2_flush_output(); } // TODO: actually wait for data to leave USB... | |||
virtual void clear(void) { usb_serial2_flush_input(); } | |||
virtual size_t write(uint8_t c) { return usb_serial2_putchar(c); } | |||
virtual size_t write(const uint8_t *buffer, size_t size) { return usb_serial2_write(buffer, size); } | |||
size_t write(unsigned long n) { return write((uint8_t)n); } | |||
size_t write(long n) { return write((uint8_t)n); } | |||
size_t write(unsigned int n) { return write((uint8_t)n); } | |||
size_t write(int n) { return write((uint8_t)n); } | |||
virtual int availableForWrite() { return usb_serial2_write_buffer_free(); } | |||
using Print::write; | |||
void send_now(void) { usb_serial2_flush_output(); } | |||
uint32_t baud(void) { return usb_cdc2_line_coding[0]; } | |||
uint8_t stopbits(void) { uint8_t b = usb_cdc2_line_coding[1]; if (!b) b = 1; return b; } | |||
uint8_t paritytype(void) { return usb_cdc2_line_coding[1] >> 8; } // 0=none, 1=odd, 2=even | |||
uint8_t numbits(void) { return usb_cdc2_line_coding[1] >> 16; } | |||
uint8_t dtr(void) { return (usb_cdc2_line_rtsdtr & USB_SERIAL_DTR) ? 1 : 0; } | |||
uint8_t rts(void) { return (usb_cdc2_line_rtsdtr & USB_SERIAL_RTS) ? 1 : 0; } | |||
operator bool() { return usb_configuration && (usb_cdc2_line_rtsdtr & USB_SERIAL_DTR) && | |||
((uint32_t)(systick_millis_count - usb_cdc2_line_rtsdtr_millis) >= 15); | |||
} | |||
size_t readBytes(char *buffer, size_t length) { | |||
size_t count=0; | |||
unsigned long startMillis = millis(); | |||
do { | |||
count += usb_serial2_read(buffer + count, length - count); | |||
if (count >= length) return count; | |||
} while(millis() - startMillis < _timeout); | |||
setReadError(); | |||
return count; | |||
} | |||
}; | |||
extern usb_serial2_class SerialA; | |||
extern void serialEventA(void); | |||
#endif // __cplusplus | |||
#endif // CDC2_STATUS_INTERFACE && CDC2_DATA_INTERFACE | |||
#if defined(CDC3_STATUS_INTERFACE) && defined(CDC3_DATA_INTERFACE) | |||
// C language implementation | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
void usb_serial3_configure(void); | |||
int usb_serial3_getchar(void); | |||
int usb_serial3_peekchar(void); | |||
int usb_serial3_available(void); | |||
int usb_serial3_read(void *buffer, uint32_t size); | |||
void usb_serial3_flush_input(void); | |||
int usb_serial3_putchar(uint8_t c); | |||
int usb_serial3_write(const void *buffer, uint32_t size); | |||
int usb_serial3_write_buffer_free(void); | |||
void usb_serial3_flush_output(void); | |||
extern uint32_t usb_cdc3_line_coding[2]; | |||
extern volatile uint32_t usb_cdc3_line_rtsdtr_millis; | |||
extern volatile uint8_t usb_cdc3_line_rtsdtr; | |||
extern volatile uint8_t usb_cdc3_transmit_flush_timer; | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
// C++ interface | |||
#ifdef __cplusplus | |||
#include "Stream.h" | |||
class usb_serial3_class : public Stream | |||
{ | |||
public: | |||
constexpr usb_serial3_class() {} | |||
void begin(long) { | |||
//uint32_t millis_begin = systick_millis_count; | |||
//disabled for now - causes more trouble than it solves? | |||
//while (!(*this)) { | |||
// wait up to 2.5 seconds for Arduino Serial Monitor | |||
// Yes, this is a long time, but some Windows systems open | |||
// the port very slowly. This wait allows programs for | |||
// Arduino Uno to "just work" (without forcing a reboot when | |||
// the port is opened), and when no PC is connected the user's | |||
// sketch still gets to run normally after this wait time. | |||
//if ((uint32_t)(systick_millis_count - millis_begin) > 2500) break; | |||
//} | |||
} | |||
void end() { /* TODO: flush output and shut down USB port */ }; | |||
virtual int available() { return usb_serial3_available(); } | |||
virtual int read() { return usb_serial3_getchar(); } | |||
virtual int peek() { return usb_serial3_peekchar(); } | |||
virtual void flush() { usb_serial3_flush_output(); } // TODO: actually wait for data to leave USB... | |||
virtual void clear(void) { usb_serial3_flush_input(); } | |||
virtual size_t write(uint8_t c) { return usb_serial3_putchar(c); } | |||
virtual size_t write(const uint8_t *buffer, size_t size) { return usb_serial3_write(buffer, size); } | |||
size_t write(unsigned long n) { return write((uint8_t)n); } | |||
size_t write(long n) { return write((uint8_t)n); } | |||
size_t write(unsigned int n) { return write((uint8_t)n); } | |||
size_t write(int n) { return write((uint8_t)n); } | |||
virtual int availableForWrite() { return usb_serial3_write_buffer_free(); } | |||
using Print::write; | |||
void send_now(void) { usb_serial3_flush_output(); } | |||
uint32_t baud(void) { return usb_cdc3_line_coding[0]; } | |||
uint8_t stopbits(void) { uint8_t b = usb_cdc3_line_coding[1]; if (!b) b = 1; return b; } | |||
uint8_t paritytype(void) { return usb_cdc3_line_coding[1] >> 8; } // 0=none, 1=odd, 2=even | |||
uint8_t numbits(void) { return usb_cdc3_line_coding[1] >> 16; } | |||
uint8_t dtr(void) { return (usb_cdc3_line_rtsdtr & USB_SERIAL_DTR) ? 1 : 0; } | |||
uint8_t rts(void) { return (usb_cdc3_line_rtsdtr & USB_SERIAL_RTS) ? 1 : 0; } | |||
operator bool() { return usb_configuration && (usb_cdc3_line_rtsdtr & USB_SERIAL_DTR) && | |||
((uint32_t)(systick_millis_count - usb_cdc3_line_rtsdtr_millis) >= 15); | |||
} | |||
size_t readBytes(char *buffer, size_t length) { | |||
size_t count=0; | |||
unsigned long startMillis = millis(); | |||
do { | |||
count += usb_serial3_read(buffer + count, length - count); | |||
if (count >= length) return count; | |||
} while(millis() - startMillis < _timeout); | |||
setReadError(); | |||
return count; | |||
} | |||
}; | |||
extern usb_serial3_class SerialB; | |||
extern void serialEventB(void); | |||
#endif // __cplusplus | |||
#endif // CDC3_STATUS_INTERFACE && CDC3_DATA_INTERFACE | |||
@@ -0,0 +1,422 @@ | |||
/* Teensyduino Core Library | |||
* http://www.pjrc.com/teensy/ | |||
* Copyright (c) 2017 PJRC.COM, LLC. | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining | |||
* a copy of this software and associated documentation files (the | |||
* "Software"), to deal in the Software without restriction, including | |||
* without limitation the rights to use, copy, modify, merge, publish, | |||
* distribute, sublicense, and/or sell copies of the Software, and to | |||
* permit persons to whom the Software is furnished to do so, subject to | |||
* the following conditions: | |||
* | |||
* 1. The above copyright notice and this permission notice shall be | |||
* included in all copies or substantial portions of the Software. | |||
* | |||
* 2. If the Software is incorporated into a build system that allows | |||
* selection among a list of target devices, then similar target | |||
* devices manufactured by PJRC.COM must be included in the list of | |||
* target devices and selectable in the same manner. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
* SOFTWARE. | |||
*/ | |||
#include "usb_dev.h" | |||
#include "usb_serial.h" | |||
#include "core_pins.h"// for delay() | |||
//#include "HardwareSerial.h" | |||
#include <string.h> // for memcpy() | |||
#include "avr/pgmspace.h" // for PROGMEM, DMAMEM, FASTRUN | |||
#include "debug/printf.h" | |||
#include "core_pins.h" | |||
// defined by usb_dev.h -> usb_desc.h | |||
#if defined(CDC2_STATUS_INTERFACE) && defined(CDC2_DATA_INTERFACE) | |||
uint32_t usb_cdc2_line_coding[2]; | |||
volatile uint32_t usb_cdc2_line_rtsdtr_millis; | |||
volatile uint8_t usb_cdc2_line_rtsdtr=0; | |||
volatile uint8_t usb_cdc2_transmit_flush_timer=0; | |||
static volatile uint8_t tx_noautoflush=0; | |||
extern volatile uint8_t usb_high_speed; | |||
// TODO: should be 2 different timeouts, high speed (480) vs full speed (12) | |||
#define TRANSMIT_FLUSH_TIMEOUT 75 /* in microseconds */ | |||
static void timer_config(void (*callback)(void), uint32_t microseconds); | |||
static void timer_start_oneshot(); | |||
static void timer_stop(); | |||
static void usb_serial2_flush_callback(void); | |||
#define TX_NUM 4 | |||
#define TX_SIZE 2048 /* should be a multiple of CDC_TX_SIZE */ | |||
static transfer_t tx_transfer[TX_NUM] __attribute__ ((used, aligned(32))); | |||
DMAMEM static uint8_t txbuffer[TX_SIZE * TX_NUM] __attribute__ ((aligned(32))); | |||
static uint8_t tx_head=0; | |||
static uint16_t tx_available=0; | |||
static uint16_t tx_packet_size=0; | |||
#define RX_NUM 8 | |||
static transfer_t rx_transfer[RX_NUM] __attribute__ ((used, aligned(32))); | |||
DMAMEM static uint8_t rx_buffer[RX_NUM * CDC_RX_SIZE_480] __attribute__ ((aligned(32))); | |||
static uint16_t rx_count[RX_NUM]; | |||
static uint16_t rx_index[RX_NUM]; | |||
static uint16_t rx_packet_size=0; | |||
static volatile uint8_t rx_head; | |||
static volatile uint8_t rx_tail; | |||
static uint8_t rx_list[RX_NUM + 1]; | |||
static volatile uint32_t rx_available; | |||
static void rx_queue_transfer(int i); | |||
static void rx_event(transfer_t *t); | |||
void usb_serial2_configure(void) | |||
{ | |||
int i; | |||
printf("usb_serial2_configure\n"); | |||
if (usb_high_speed) { | |||
tx_packet_size = CDC_TX_SIZE_480; | |||
rx_packet_size = CDC_RX_SIZE_480; | |||
} else { | |||
tx_packet_size = CDC_TX_SIZE_12; | |||
rx_packet_size = CDC_RX_SIZE_12; | |||
} | |||
memset(tx_transfer, 0, sizeof(tx_transfer)); | |||
tx_head = 0; | |||
tx_available = 0; | |||
memset(rx_transfer, 0, sizeof(rx_transfer)); | |||
memset(rx_count, 0, sizeof(rx_count)); | |||
memset(rx_index, 0, sizeof(rx_index)); | |||
rx_head = 0; | |||
rx_tail = 0; | |||
rx_available = 0; | |||
usb_config_tx(CDC2_ACM_ENDPOINT, CDC_ACM_SIZE, 0, NULL); // size same 12 & 480 | |||
usb_config_rx(CDC2_RX_ENDPOINT, rx_packet_size, 0, rx_event); | |||
usb_config_tx(CDC2_TX_ENDPOINT, tx_packet_size, 1, NULL); | |||
for (i=0; i < RX_NUM; i++) rx_queue_transfer(i); | |||
timer_config(usb_serial2_flush_callback, TRANSMIT_FLUSH_TIMEOUT); | |||
} | |||
/*************************************************************************/ | |||
/** Receive **/ | |||
/*************************************************************************/ | |||
static void rx_queue_transfer(int i) | |||
{ | |||
NVIC_DISABLE_IRQ(IRQ_USB1); | |||
printf("rx queue i=%d\n", i); | |||
void *buffer = rx_buffer + i * CDC_RX_SIZE_480; | |||
usb_prepare_transfer(rx_transfer + i, buffer, rx_packet_size, i); | |||
arm_dcache_delete(buffer, rx_packet_size); | |||
usb_receive(CDC2_RX_ENDPOINT, rx_transfer + i); | |||
NVIC_ENABLE_IRQ(IRQ_USB1); | |||
} | |||
// called by USB interrupt when any packet is received | |||
static void rx_event(transfer_t *t) | |||
{ | |||
int len = rx_packet_size - ((t->status >> 16) & 0x7FFF); | |||
int i = t->callback_param; | |||
printf("rx event, len=%d, i=%d\n", len, i); | |||
if (len > 0) { | |||
// received a packet with data | |||
uint32_t head = rx_head; | |||
if (head != rx_tail) { | |||
// a previous packet is still buffered | |||
uint32_t ii = rx_list[head]; | |||
uint32_t count = rx_count[ii]; | |||
if (len <= CDC_RX_SIZE_480 - count) { | |||
// previous buffer has enough free space for this packet's data | |||
memcpy(rx_buffer + ii * CDC_RX_SIZE_480 + count, | |||
rx_buffer + i * CDC_RX_SIZE_480, len); | |||
rx_count[ii] = count + len; | |||
rx_available += len; | |||
rx_queue_transfer(i); | |||
// TODO: trigger serialEvent | |||
return; | |||
} | |||
} | |||
// add this packet to rx_list | |||
rx_count[i] = len; | |||
rx_index[i] = 0; | |||
if (++head > RX_NUM) head = 0; | |||
rx_list[head] = i; | |||
rx_head = head; | |||
rx_available += len; | |||
// TODO: trigger serialEvent | |||
} else { | |||
// received a zero length packet | |||
rx_queue_transfer(i); | |||
} | |||
} | |||
//static int maxtimes=0; | |||
// read a block of bytes to a buffer | |||
int usb_serial2_read(void *buffer, uint32_t size) | |||
{ | |||
uint8_t *p = (uint8_t *)buffer; | |||
uint32_t count=0; | |||
NVIC_DISABLE_IRQ(IRQ_USB1); | |||
//if (++maxtimes > 15) while (1) ; | |||
uint32_t tail = rx_tail; | |||
//printf("usb_serial2_read, size=%d, tail=%d, head=%d\n", size, tail, rx_head); | |||
while (count < size && tail != rx_head) { | |||
if (++tail > RX_NUM) tail = 0; | |||
uint32_t i = rx_list[tail]; | |||
uint32_t len = size - count; | |||
uint32_t avail = rx_count[i] - rx_index[i]; | |||
//printf("usb_serial2_read, count=%d, size=%d, i=%d, index=%d, len=%d, avail=%d, c=%c\n", | |||
//count, size, i, rx_index[i], len, avail, rx_buffer[i * CDC_RX_SIZE_480]); | |||
if (avail > len) { | |||
// partially consume this packet | |||
memcpy(p, rx_buffer + i * CDC_RX_SIZE_480 + rx_index[i], len); | |||
rx_available -= len; | |||
rx_index[i] += len; | |||
count += len; | |||
} else { | |||
// fully consume this packet | |||
memcpy(p, rx_buffer + i * CDC_RX_SIZE_480 + rx_index[i], avail); | |||
p += avail; | |||
rx_available -= avail; | |||
count += avail; | |||
rx_tail = tail; | |||
rx_queue_transfer(i); | |||
} | |||
} | |||
NVIC_ENABLE_IRQ(IRQ_USB1); | |||
return count; | |||
} | |||
// peek at the next character, or -1 if nothing received | |||
int usb_serial2_peekchar(void) | |||
{ | |||
uint32_t tail = rx_tail; | |||
if (tail == rx_head) return -1; | |||
if (++tail > RX_NUM) tail = 0; | |||
uint32_t i = rx_list[tail]; | |||
return rx_buffer[i * CDC_RX_SIZE_480 + rx_index[i]]; | |||
} | |||
// number of bytes available in the receive buffer | |||
int usb_serial2_available(void) | |||
{ | |||
return rx_available; | |||
} | |||
// discard any buffered input | |||
void usb_serial2_flush_input(void) | |||
{ | |||
uint32_t tail = rx_tail; | |||
while (tail != rx_head) { | |||
if (++tail > RX_NUM) tail = 0; | |||
uint32_t i = rx_list[tail]; | |||
rx_available -= rx_count[i] - rx_index[i]; | |||
rx_queue_transfer(i); | |||
rx_tail = tail; | |||
} | |||
} | |||
// get the next character, or -1 if nothing received | |||
int usb_serial2_getchar(void) | |||
{ | |||
uint8_t c; | |||
if (usb_serial2_read(&c, 1)) return c; | |||
return -1; | |||
} | |||
/*************************************************************************/ | |||
/** Transmit **/ | |||
/*************************************************************************/ | |||
// When the PC isn't listening, how long do we wait before discarding data? If this is | |||
// too short, we risk losing data during the stalls that are common with ordinary desktop | |||
// software. If it's too long, we stall the user's program when no software is running. | |||
#define TX_TIMEOUT_MSEC 120 | |||
// When we've suffered the transmit timeout, don't wait again until the computer | |||
// begins accepting data. If no software is running to receive, we'll just discard | |||
// data as rapidly as Serial.print() can generate it, until there's something to | |||
// actually receive it. | |||
static uint8_t transmit_previous_timeout=0; | |||
// transmit a character. 0 returned on success, -1 on error | |||
int usb_serial2_putchar(uint8_t c) | |||
{ | |||
return usb_serial2_write(&c, 1); | |||
} | |||
extern volatile uint32_t systick_millis_count; | |||
static void timer_config(void (*callback)(void), uint32_t microseconds); | |||
static void timer_start_oneshot(); | |||
static void timer_stop(); | |||
static void timer_config(void (*callback)(void), uint32_t microseconds) | |||
{ | |||
// FIXME: conflicts with other interfaces | |||
usb_timer0_callback = callback; | |||
USB1_GPTIMER0CTRL = 0; | |||
USB1_GPTIMER0LD = microseconds - 1; | |||
USB1_USBINTR |= USB_USBINTR_TIE0; | |||
} | |||
static void timer_start_oneshot(void) | |||
{ | |||
// FIXME: conflicts with other interfaces | |||
// restarts timer if already running (retriggerable one-shot) | |||
USB1_GPTIMER0CTRL = USB_GPTIMERCTRL_GPTRUN | USB_GPTIMERCTRL_GPTRST; | |||
} | |||
static void timer_stop(void) | |||
{ | |||
// FIXME: conflicts with other interfaces | |||
USB1_GPTIMER0CTRL = 0; | |||
} | |||
int usb_serial2_write(const void *buffer, uint32_t size) | |||
{ | |||
uint32_t sent=0; | |||
const uint8_t *data = (const uint8_t *)buffer; | |||
if (!usb_configuration) return 0; | |||
while (size > 0) { | |||
transfer_t *xfer = tx_transfer + tx_head; | |||
int waiting=0; | |||
uint32_t wait_begin_at=0; | |||
while (!tx_available) { | |||
//digitalWriteFast(3, HIGH); | |||
uint32_t status = usb_transfer_status(xfer); | |||
if (!(status & 0x80)) { | |||
if (status & 0x68) { | |||
// TODO: what if status has errors??? | |||
printf("ERROR status = %x, i=%d, ms=%u\n", | |||
status, tx_head, systick_millis_count); | |||
} | |||
tx_available = TX_SIZE; | |||
transmit_previous_timeout = 0; | |||
break; | |||
} | |||
if (!waiting) { | |||
wait_begin_at = systick_millis_count; | |||
waiting = 1; | |||
} | |||
if (transmit_previous_timeout) return sent; | |||
if (systick_millis_count - wait_begin_at > TX_TIMEOUT_MSEC) { | |||
// waited too long, assume the USB host isn't listening | |||
transmit_previous_timeout = 1; | |||
return sent; | |||
//printf("\nstop, waited too long\n"); | |||
//printf("status = %x\n", status); | |||
//printf("tx head=%d\n", tx_head); | |||
//printf("TXFILLTUNING=%08lX\n", USB1_TXFILLTUNING); | |||
//usb_print_transfer_log(); | |||
//while (1) ; | |||
} | |||
if (!usb_configuration) return sent; | |||
yield(); | |||
} | |||
//digitalWriteFast(3, LOW); | |||
uint8_t *txdata = txbuffer + (tx_head * TX_SIZE) + (TX_SIZE - tx_available); | |||
if (size >= tx_available) { | |||
memcpy(txdata, data, tx_available); | |||
//*(txbuffer + (tx_head * TX_SIZE)) = 'A' + tx_head; // to see which buffer | |||
//*(txbuffer + (tx_head * TX_SIZE) + 1) = ' '; // really see it | |||
uint8_t *txbuf = txbuffer + (tx_head * TX_SIZE); | |||
usb_prepare_transfer(xfer, txbuf, TX_SIZE, 0); | |||
arm_dcache_flush_delete(txbuf, TX_SIZE); | |||
usb_transmit(CDC2_TX_ENDPOINT, xfer); | |||
if (++tx_head >= TX_NUM) tx_head = 0; | |||
size -= tx_available; | |||
sent += tx_available; | |||
data += tx_available; | |||
tx_available = 0; | |||
timer_stop(); | |||
} else { | |||
memcpy(txdata, data, size); | |||
tx_available -= size; | |||
sent += size; | |||
size = 0; | |||
timer_start_oneshot(); | |||
} | |||
} | |||
return sent; | |||
} | |||
int usb_serial2_write_buffer_free(void) | |||
{ | |||
uint32_t sum = 0; | |||
tx_noautoflush = 1; | |||
for (uint32_t i=0; i < TX_NUM; i++) { | |||
if (i == tx_head) continue; | |||
if (!(usb_transfer_status(tx_transfer + i) & 0x80)) sum += TX_SIZE; | |||
} | |||
tx_noautoflush = 0; | |||
return sum; | |||
} | |||
void usb_serial2_flush_output(void) | |||
{ | |||
if (!usb_configuration) return; | |||
if (tx_available == 0) return; | |||
tx_noautoflush = 1; | |||
transfer_t *xfer = tx_transfer + tx_head; | |||
uint8_t *txbuf = txbuffer + (tx_head * TX_SIZE); | |||
uint32_t txnum = TX_SIZE - tx_available; | |||
usb_prepare_transfer(xfer, txbuf, txnum, 0); | |||
arm_dcache_flush_delete(txbuf, txnum); | |||
usb_transmit(CDC2_TX_ENDPOINT, xfer); | |||
if (++tx_head >= TX_NUM) tx_head = 0; | |||
tx_available = 0; | |||
tx_noautoflush = 0; | |||
} | |||
static void usb_serial2_flush_callback(void) | |||
{ | |||
if (tx_noautoflush) return; | |||
if (!usb_configuration) return; | |||
if (tx_available == 0) return; | |||
//printf("flush callback, %d bytes\n", TX_SIZE - tx_available); | |||
transfer_t *xfer = tx_transfer + tx_head; | |||
uint8_t *txbuf = txbuffer + (tx_head * TX_SIZE); | |||
uint32_t txnum = TX_SIZE - tx_available; | |||
usb_prepare_transfer(xfer, txbuf, txnum, 0); | |||
arm_dcache_flush_delete(txbuf, txnum); | |||
usb_transmit(CDC2_TX_ENDPOINT, xfer); | |||
if (++tx_head >= TX_NUM) tx_head = 0; | |||
tx_available = 0; | |||
} | |||
#endif // CDC2_STATUS_INTERFACE && CDC2_DATA_INTERFACE |
@@ -0,0 +1,422 @@ | |||
/* Teensyduino Core Library | |||
* http://www.pjrc.com/teensy/ | |||
* Copyright (c) 2017 PJRC.COM, LLC. | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining | |||
* a copy of this software and associated documentation files (the | |||
* "Software"), to deal in the Software without restriction, including | |||
* without limitation the rights to use, copy, modify, merge, publish, | |||
* distribute, sublicense, and/or sell copies of the Software, and to | |||
* permit persons to whom the Software is furnished to do so, subject to | |||
* the following conditions: | |||
* | |||
* 1. The above copyright notice and this permission notice shall be | |||
* included in all copies or substantial portions of the Software. | |||
* | |||
* 2. If the Software is incorporated into a build system that allows | |||
* selection among a list of target devices, then similar target | |||
* devices manufactured by PJRC.COM must be included in the list of | |||
* target devices and selectable in the same manner. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
* SOFTWARE. | |||
*/ | |||
#include "usb_dev.h" | |||
#include "usb_serial.h" | |||
#include "core_pins.h"// for delay() | |||
//#include "HardwareSerial.h" | |||
#include <string.h> // for memcpy() | |||
#include "avr/pgmspace.h" // for PROGMEM, DMAMEM, FASTRUN | |||
#include "debug/printf.h" | |||
#include "core_pins.h" | |||
// defined by usb_dev.h -> usb_desc.h | |||
#if defined(CDC3_STATUS_INTERFACE) && defined(CDC3_DATA_INTERFACE) | |||
uint32_t usb_cdc3_line_coding[2]; | |||
volatile uint32_t usb_cdc3_line_rtsdtr_millis; | |||
volatile uint8_t usb_cdc3_line_rtsdtr=0; | |||
volatile uint8_t usb_cdc3_transmit_flush_timer=0; | |||
static volatile uint8_t tx_noautoflush=0; | |||
extern volatile uint8_t usb_high_speed; | |||
// TODO: should be 2 different timeouts, high speed (480) vs full speed (12) | |||
#define TRANSMIT_FLUSH_TIMEOUT 75 /* in microseconds */ | |||
static void timer_config(void (*callback)(void), uint32_t microseconds); | |||
static void timer_start_oneshot(); | |||
static void timer_stop(); | |||
static void usb_serial3_flush_callback(void); | |||
#define TX_NUM 4 | |||
#define TX_SIZE 2048 /* should be a multiple of CDC_TX_SIZE */ | |||
static transfer_t tx_transfer[TX_NUM] __attribute__ ((used, aligned(32))); | |||
DMAMEM static uint8_t txbuffer[TX_SIZE * TX_NUM] __attribute__ ((aligned(32))); | |||
static uint8_t tx_head=0; | |||
static uint16_t tx_available=0; | |||
static uint16_t tx_packet_size=0; | |||
#define RX_NUM 8 | |||
static transfer_t rx_transfer[RX_NUM] __attribute__ ((used, aligned(32))); | |||
DMAMEM static uint8_t rx_buffer[RX_NUM * CDC_RX_SIZE_480] __attribute__ ((aligned(32))); | |||
static uint16_t rx_count[RX_NUM]; | |||
static uint16_t rx_index[RX_NUM]; | |||
static uint16_t rx_packet_size=0; | |||
static volatile uint8_t rx_head; | |||
static volatile uint8_t rx_tail; | |||
static uint8_t rx_list[RX_NUM + 1]; | |||
static volatile uint32_t rx_available; | |||
static void rx_queue_transfer(int i); | |||
static void rx_event(transfer_t *t); | |||
void usb_serial3_configure(void) | |||
{ | |||
int i; | |||
printf("usb_serial3_configure\n"); | |||
if (usb_high_speed) { | |||
tx_packet_size = CDC_TX_SIZE_480; | |||
rx_packet_size = CDC_RX_SIZE_480; | |||
} else { | |||
tx_packet_size = CDC_TX_SIZE_12; | |||
rx_packet_size = CDC_RX_SIZE_12; | |||
} | |||
memset(tx_transfer, 0, sizeof(tx_transfer)); | |||
tx_head = 0; | |||
tx_available = 0; | |||
memset(rx_transfer, 0, sizeof(rx_transfer)); | |||
memset(rx_count, 0, sizeof(rx_count)); | |||
memset(rx_index, 0, sizeof(rx_index)); | |||
rx_head = 0; | |||
rx_tail = 0; | |||
rx_available = 0; | |||
usb_config_tx(CDC3_ACM_ENDPOINT, CDC_ACM_SIZE, 0, NULL); // size same 12 & 480 | |||
usb_config_rx(CDC3_RX_ENDPOINT, rx_packet_size, 0, rx_event); | |||
usb_config_tx(CDC3_TX_ENDPOINT, tx_packet_size, 1, NULL); | |||
for (i=0; i < RX_NUM; i++) rx_queue_transfer(i); | |||
timer_config(usb_serial3_flush_callback, TRANSMIT_FLUSH_TIMEOUT); | |||
} | |||
/*************************************************************************/ | |||
/** Receive **/ | |||
/*************************************************************************/ | |||
static void rx_queue_transfer(int i) | |||
{ | |||
NVIC_DISABLE_IRQ(IRQ_USB1); | |||
printf("rx queue i=%d\n", i); | |||
void *buffer = rx_buffer + i * CDC_RX_SIZE_480; | |||
usb_prepare_transfer(rx_transfer + i, buffer, rx_packet_size, i); | |||
arm_dcache_delete(buffer, rx_packet_size); | |||
usb_receive(CDC3_RX_ENDPOINT, rx_transfer + i); | |||
NVIC_ENABLE_IRQ(IRQ_USB1); | |||
} | |||
// called by USB interrupt when any packet is received | |||
static void rx_event(transfer_t *t) | |||
{ | |||
int len = rx_packet_size - ((t->status >> 16) & 0x7FFF); | |||
int i = t->callback_param; | |||
printf("rx event, len=%d, i=%d\n", len, i); | |||
if (len > 0) { | |||
// received a packet with data | |||
uint32_t head = rx_head; | |||
if (head != rx_tail) { | |||
// a previous packet is still buffered | |||
uint32_t ii = rx_list[head]; | |||
uint32_t count = rx_count[ii]; | |||
if (len <= CDC_RX_SIZE_480 - count) { | |||
// previous buffer has enough free space for this packet's data | |||
memcpy(rx_buffer + ii * CDC_RX_SIZE_480 + count, | |||
rx_buffer + i * CDC_RX_SIZE_480, len); | |||
rx_count[ii] = count + len; | |||
rx_available += len; | |||
rx_queue_transfer(i); | |||
// TODO: trigger serialEvent | |||
return; | |||
} | |||
} | |||
// add this packet to rx_list | |||
rx_count[i] = len; | |||
rx_index[i] = 0; | |||
if (++head > RX_NUM) head = 0; | |||
rx_list[head] = i; | |||
rx_head = head; | |||
rx_available += len; | |||
// TODO: trigger serialEvent | |||
} else { | |||
// received a zero length packet | |||
rx_queue_transfer(i); | |||
} | |||
} | |||
//static int maxtimes=0; | |||
// read a block of bytes to a buffer | |||
int usb_serial3_read(void *buffer, uint32_t size) | |||
{ | |||
uint8_t *p = (uint8_t *)buffer; | |||
uint32_t count=0; | |||
NVIC_DISABLE_IRQ(IRQ_USB1); | |||
//if (++maxtimes > 15) while (1) ; | |||
uint32_t tail = rx_tail; | |||
//printf("usb_serial3_read, size=%d, tail=%d, head=%d\n", size, tail, rx_head); | |||
while (count < size && tail != rx_head) { | |||
if (++tail > RX_NUM) tail = 0; | |||
uint32_t i = rx_list[tail]; | |||
uint32_t len = size - count; | |||
uint32_t avail = rx_count[i] - rx_index[i]; | |||
//printf("usb_serial3_read, count=%d, size=%d, i=%d, index=%d, len=%d, avail=%d, c=%c\n", | |||
//count, size, i, rx_index[i], len, avail, rx_buffer[i * CDC_RX_SIZE_480]); | |||
if (avail > len) { | |||
// partially consume this packet | |||
memcpy(p, rx_buffer + i * CDC_RX_SIZE_480 + rx_index[i], len); | |||
rx_available -= len; | |||
rx_index[i] += len; | |||
count += len; | |||
} else { | |||
// fully consume this packet | |||
memcpy(p, rx_buffer + i * CDC_RX_SIZE_480 + rx_index[i], avail); | |||
p += avail; | |||
rx_available -= avail; | |||
count += avail; | |||
rx_tail = tail; | |||
rx_queue_transfer(i); | |||
} | |||
} | |||
NVIC_ENABLE_IRQ(IRQ_USB1); | |||
return count; | |||
} | |||
// peek at the next character, or -1 if nothing received | |||
int usb_serial3_peekchar(void) | |||
{ | |||
uint32_t tail = rx_tail; | |||
if (tail == rx_head) return -1; | |||
if (++tail > RX_NUM) tail = 0; | |||
uint32_t i = rx_list[tail]; | |||
return rx_buffer[i * CDC_RX_SIZE_480 + rx_index[i]]; | |||
} | |||
// number of bytes available in the receive buffer | |||
int usb_serial3_available(void) | |||
{ | |||
return rx_available; | |||
} | |||
// discard any buffered input | |||
void usb_serial3_flush_input(void) | |||
{ | |||
uint32_t tail = rx_tail; | |||
while (tail != rx_head) { | |||
if (++tail > RX_NUM) tail = 0; | |||
uint32_t i = rx_list[tail]; | |||
rx_available -= rx_count[i] - rx_index[i]; | |||
rx_queue_transfer(i); | |||
rx_tail = tail; | |||
} | |||
} | |||
// get the next character, or -1 if nothing received | |||
int usb_serial3_getchar(void) | |||
{ | |||
uint8_t c; | |||
if (usb_serial3_read(&c, 1)) return c; | |||
return -1; | |||
} | |||
/*************************************************************************/ | |||
/** Transmit **/ | |||
/*************************************************************************/ | |||
// When the PC isn't listening, how long do we wait before discarding data? If this is | |||
// too short, we risk losing data during the stalls that are common with ordinary desktop | |||
// software. If it's too long, we stall the user's program when no software is running. | |||
#define TX_TIMEOUT_MSEC 120 | |||
// When we've suffered the transmit timeout, don't wait again until the computer | |||
// begins accepting data. If no software is running to receive, we'll just discard | |||
// data as rapidly as Serial.print() can generate it, until there's something to | |||
// actually receive it. | |||
static uint8_t transmit_previous_timeout=0; | |||
// transmit a character. 0 returned on success, -1 on error | |||
int usb_serial3_putchar(uint8_t c) | |||
{ | |||
return usb_serial3_write(&c, 1); | |||
} | |||
extern volatile uint32_t systick_millis_count; | |||
static void timer_config(void (*callback)(void), uint32_t microseconds); | |||
static void timer_start_oneshot(); | |||
static void timer_stop(); | |||
static void timer_config(void (*callback)(void), uint32_t microseconds) | |||
{ | |||
// FIXME: conflicts with other interfaces | |||
usb_timer0_callback = callback; | |||
USB1_GPTIMER0CTRL = 0; | |||
USB1_GPTIMER0LD = microseconds - 1; | |||
USB1_USBINTR |= USB_USBINTR_TIE0; | |||
} | |||
static void timer_start_oneshot(void) | |||
{ | |||
// FIXME: conflicts with other interfaces | |||
// restarts timer if already running (retriggerable one-shot) | |||
USB1_GPTIMER0CTRL = USB_GPTIMERCTRL_GPTRUN | USB_GPTIMERCTRL_GPTRST; | |||
} | |||
static void timer_stop(void) | |||
{ | |||
// FIXME: conflicts with other interfaces | |||
USB1_GPTIMER0CTRL = 0; | |||
} | |||
int usb_serial3_write(const void *buffer, uint32_t size) | |||
{ | |||
uint32_t sent=0; | |||
const uint8_t *data = (const uint8_t *)buffer; | |||
if (!usb_configuration) return 0; | |||
while (size > 0) { | |||
transfer_t *xfer = tx_transfer + tx_head; | |||
int waiting=0; | |||
uint32_t wait_begin_at=0; | |||
while (!tx_available) { | |||
//digitalWriteFast(3, HIGH); | |||
uint32_t status = usb_transfer_status(xfer); | |||
if (!(status & 0x80)) { | |||
if (status & 0x68) { | |||
// TODO: what if status has errors??? | |||
printf("ERROR status = %x, i=%d, ms=%u\n", | |||
status, tx_head, systick_millis_count); | |||
} | |||
tx_available = TX_SIZE; | |||
transmit_previous_timeout = 0; | |||
break; | |||
} | |||
if (!waiting) { | |||
wait_begin_at = systick_millis_count; | |||
waiting = 1; | |||
} | |||
if (transmit_previous_timeout) return sent; | |||
if (systick_millis_count - wait_begin_at > TX_TIMEOUT_MSEC) { | |||
// waited too long, assume the USB host isn't listening | |||
transmit_previous_timeout = 1; | |||
return sent; | |||
//printf("\nstop, waited too long\n"); | |||
//printf("status = %x\n", status); | |||
//printf("tx head=%d\n", tx_head); | |||
//printf("TXFILLTUNING=%08lX\n", USB1_TXFILLTUNING); | |||
//usb_print_transfer_log(); | |||
//while (1) ; | |||
} | |||
if (!usb_configuration) return sent; | |||
yield(); | |||
} | |||
//digitalWriteFast(3, LOW); | |||
uint8_t *txdata = txbuffer + (tx_head * TX_SIZE) + (TX_SIZE - tx_available); | |||
if (size >= tx_available) { | |||
memcpy(txdata, data, tx_available); | |||
//*(txbuffer + (tx_head * TX_SIZE)) = 'A' + tx_head; // to see which buffer | |||
//*(txbuffer + (tx_head * TX_SIZE) + 1) = ' '; // really see it | |||
uint8_t *txbuf = txbuffer + (tx_head * TX_SIZE); | |||
usb_prepare_transfer(xfer, txbuf, TX_SIZE, 0); | |||
arm_dcache_flush_delete(txbuf, TX_SIZE); | |||
usb_transmit(CDC3_TX_ENDPOINT, xfer); | |||
if (++tx_head >= TX_NUM) tx_head = 0; | |||
size -= tx_available; | |||
sent += tx_available; | |||
data += tx_available; | |||
tx_available = 0; | |||
timer_stop(); | |||
} else { | |||
memcpy(txdata, data, size); | |||
tx_available -= size; | |||
sent += size; | |||
size = 0; | |||
timer_start_oneshot(); | |||
} | |||
} | |||
return sent; | |||
} | |||
int usb_serial3_write_buffer_free(void) | |||
{ | |||
uint32_t sum = 0; | |||
tx_noautoflush = 1; | |||
for (uint32_t i=0; i < TX_NUM; i++) { | |||
if (i == tx_head) continue; | |||
if (!(usb_transfer_status(tx_transfer + i) & 0x80)) sum += TX_SIZE; | |||
} | |||
tx_noautoflush = 0; | |||
return sum; | |||
} | |||
void usb_serial3_flush_output(void) | |||
{ | |||
if (!usb_configuration) return; | |||
if (tx_available == 0) return; | |||
tx_noautoflush = 1; | |||
transfer_t *xfer = tx_transfer + tx_head; | |||
uint8_t *txbuf = txbuffer + (tx_head * TX_SIZE); | |||
uint32_t txnum = TX_SIZE - tx_available; | |||
usb_prepare_transfer(xfer, txbuf, txnum, 0); | |||
arm_dcache_flush_delete(txbuf, txnum); | |||
usb_transmit(CDC3_TX_ENDPOINT, xfer); | |||
if (++tx_head >= TX_NUM) tx_head = 0; | |||
tx_available = 0; | |||
tx_noautoflush = 0; | |||
} | |||
static void usb_serial3_flush_callback(void) | |||
{ | |||
if (tx_noautoflush) return; | |||
if (!usb_configuration) return; | |||
if (tx_available == 0) return; | |||
//printf("flush callback, %d bytes\n", TX_SIZE - tx_available); | |||
transfer_t *xfer = tx_transfer + tx_head; | |||
uint8_t *txbuf = txbuffer + (tx_head * TX_SIZE); | |||
uint32_t txnum = TX_SIZE - tx_available; | |||
usb_prepare_transfer(xfer, txbuf, txnum, 0); | |||
arm_dcache_flush_delete(txbuf, txnum); | |||
usb_transmit(CDC3_TX_ENDPOINT, xfer); | |||
if (++tx_head >= TX_NUM) tx_head = 0; | |||
tx_available = 0; | |||
} | |||
#endif // CDC3_STATUS_INTERFACE && CDC3_DATA_INTERFACE |