#elif defined(SEREMU_INTERFACE) | #elif defined(SEREMU_INTERFACE) | ||||
usb_seremu_configure(); | usb_seremu_configure(); | ||||
#endif | #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) | #if defined(RAWHID_INTERFACE) | ||||
usb_rawhid_configure(); | usb_rawhid_configure(); | ||||
#endif | #endif | ||||
break; | break; | ||||
#if defined(CDC_STATUS_INTERFACE) | #if defined(CDC_STATUS_INTERFACE) | ||||
case 0x2221: // CDC_SET_CONTROL_LINE_STATE | 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 | case 0x2321: // CDC_SEND_BREAK | ||||
endpoint0_receive(NULL, 0, 0); | endpoint0_receive(NULL, 0, 0); | ||||
return; | return; | ||||
setup.bothwords = endpoint0_setupdata.bothwords; | setup.bothwords = endpoint0_setupdata.bothwords; | ||||
//printf("complete %x %x %x\n", setup.word1, setup.word2, endpoint0_buffer[0]); | //printf("complete %x %x %x\n", setup.word1, setup.word2, endpoint0_buffer[0]); | ||||
#ifdef CDC_STATUS_INTERFACE | #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); | memcpy(usb_cdc_line_coding, endpoint0_buffer, 7); | ||||
printf("usb_cdc_line_coding, baud=%u\n", usb_cdc_line_coding[0]); | printf("usb_cdc_line_coding, baud=%u\n", usb_cdc_line_coding[0]); | ||||
if (usb_cdc_line_coding[0] == 134) { | if (usb_cdc_line_coding[0] == 134) { | ||||
} | } | ||||
} | } | ||||
#endif | #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 | #ifdef KEYBOARD_INTERFACE | ||||
if (setup.word1 == 0x02000921 && setup.word2 == ((1 << 16) | KEYBOARD_INTERFACE)) { | if (setup.word1 == 0x02000921 && setup.word2 == ((1 << 16) | KEYBOARD_INTERFACE)) { | ||||
keyboard_leds = endpoint0_buffer[0]; | keyboard_leds = endpoint0_buffer[0]; | ||||
#ifdef AUDIO_INTERFACE | #ifdef AUDIO_INTERFACE | ||||
if (setup.word1 == 0x02010121 /* TODO: check setup.word2 */) { | if (setup.word1 == 0x02010121 /* TODO: check setup.word2 */) { | ||||
usb_audio_set_feature(&endpoint0_setupdata, endpoint0_buffer); | usb_audio_set_feature(&endpoint0_setupdata, endpoint0_buffer); | ||||
// TODO: usb_audio_set_feature() | |||||
} | } | ||||
#endif | #endif | ||||
} | } |
#define CDC_DATA_INTERFACE_DESC_SIZE 0 | #define CDC_DATA_INTERFACE_DESC_SIZE 0 | ||||
#endif | #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 | #ifdef MIDI_INTERFACE | ||||
#if !defined(MIDI_NUM_CABLES) || MIDI_NUM_CABLES < 1 || MIDI_NUM_CABLES > 16 | #if !defined(MIDI_NUM_CABLES) || MIDI_NUM_CABLES < 1 || MIDI_NUM_CABLES > 16 | ||||
#error "MIDI_NUM_CABLES must be defined between 1 to 16" | #error "MIDI_NUM_CABLES must be defined between 1 to 16" | ||||
CDC_ACM_ENDPOINT | 0x80, // bEndpointAddress | CDC_ACM_ENDPOINT | 0x80, // bEndpointAddress | ||||
0x03, // bmAttributes (0x03=intr) | 0x03, // bmAttributes (0x03=intr) | ||||
LSB(CDC_ACM_SIZE),MSB(CDC_ACM_SIZE), // wMaxPacketSize | 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 | // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 | ||||
9, // bLength | 9, // bLength | ||||
4, // bDescriptorType | 4, // bDescriptorType | ||||
0, // bInterval | 0, // bInterval | ||||
#endif // CDC_DATA_INTERFACE | #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 | #ifdef MIDI_INTERFACE | ||||
// configuration for 480 Mbit/sec speed | // configuration for 480 Mbit/sec speed | ||||
// Standard MS Interface Descriptor, | // Standard MS Interface Descriptor, | ||||
0, // bInterval | 0, // bInterval | ||||
#endif // CDC_DATA_INTERFACE | #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 | #ifdef MIDI_INTERFACE | ||||
// configuration for 12 Mbit/sec speed | // configuration for 12 Mbit/sec speed | ||||
// Standard MS Interface Descriptor, | // Standard MS Interface Descriptor, |
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_UNUSED | #define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_UNUSED | ||||
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_BULK | #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) | #elif defined(USB_KEYBOARDONLY) | ||||
#define VENDOR_ID 0x16C0 | #define VENDOR_ID 0x16C0 | ||||
#define PRODUCT_ID 0x04D0 | #define PRODUCT_ID 0x04D0 |
#pragma once | #pragma once | ||||
#include "usb_desc.h" | #include "usb_desc.h" | ||||
#include <stdint.h> | |||||
#if (defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE)) || defined(USB_DISABLED) | #if (defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE)) || defined(USB_DISABLED) | ||||
#include <stdint.h> | |||||
// C language implementation | // C language implementation | ||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
extern "C" { | extern "C" { | ||||
extern void serialEvent(void); | extern void serialEvent(void); | ||||
#endif // __cplusplus | #endif // __cplusplus | ||||
#endif // CDC_STATUS_INTERFACE && CDC_DATA_INTERFACE | #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 | |||||
/* 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 |
/* 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 |