Browse Source

USB Dual & Triple Serial on Teensy 4 (work in progress)

teensy4-core
PaulStoffregen 4 years ago
parent
commit
e4b6abf862
6 changed files with 1461 additions and 9 deletions
  1. +46
    -4
      teensy4/usb.c
  2. +320
    -2
      teensy4/usb_desc.c
  3. +69
    -0
      teensy4/usb_desc.h
  4. +182
    -3
      teensy4/usb_serial.h
  5. +422
    -0
      teensy4/usb_serial2.c
  6. +422
    -0
      teensy4/usb_serial3.c

+ 46
- 4
teensy4/usb.c View File

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

+ 320
- 2
teensy4/usb_desc.c View File

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

+ 69
- 0
teensy4/usb_desc.h View File

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

+ 182
- 3
teensy4/usb_serial.h View File

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






+ 422
- 0
teensy4/usb_serial2.c View File

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

+ 422
- 0
teensy4/usb_serial3.c View File

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

Loading…
Cancel
Save