| @@ -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 | |||