Skip to content

Commit

Permalink
USB serial receive on IMXRT
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulStoffregen committed Apr 28, 2019
1 parent 3d0cad7 commit 7e959d4
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 16 deletions.
164 changes: 156 additions & 8 deletions teensy4/usb.c
Expand Up @@ -82,6 +82,31 @@ static void endpoint0_receive(void *data, uint32_t len, int notify);
static void endpoint0_complete(void);


static void run_callbacks(endpoint_t *ep);

static void run_callbacks(endpoint_t *ep)
{
transfer_t *t, *next;

printf("run_callbacks\n");
t = ep->first_transfer;
while (t && (uint32_t)t != 1) {
if (!(t->status & (1<<7))) {
// transfer not active anymore
next = (transfer_t *)t->next;
ep->callback_function(t);
} else {
// transfer still active
ep->first_transfer = t;
return;
}
t = next;
}
// all transfers completed
ep->first_transfer = NULL;
ep->last_transfer = NULL;
}


__attribute__((section(".progmem")))
void usb_init(void)
Expand Down Expand Up @@ -213,8 +238,17 @@ static void isr(void)
endpoint0_notify_mask = 0;
endpoint0_complete();
}
if (completestatus & endpointN_notify_mask) {
// TODO: callback functions...
completestatus &= endpointN_notify_mask;
if (completestatus) {
int i; // TODO: optimize with __builtin_ctz()
for (i=2; i < NUM_ENDPOINTS; i++) {
if (completestatus & (1 << i)) { // receive
run_callbacks(endpoint_queue_head + i * 2);
}
if (completestatus & (1 << (i + 16))) { // transmit
run_callbacks(endpoint_queue_head + i * 2 + 1);
}
}
}
}
}
Expand All @@ -230,6 +264,10 @@ static void isr(void)
// TODO; is this ever really a problem?
//printf("reset too slow\n");
}
#if defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE)
usb_serial_reset();
#endif
endpointN_notify_mask = 0;
// TODO: Free all allocated dTDs
//if (++reset_count >= 3) {
// shut off USB - easier to see results in protocol analyzer
Expand Down Expand Up @@ -307,12 +345,9 @@ static void endpoint0_setup(uint64_t setupdata)
//printf(" ep=%d: cfg=%08lX - %08lX - %08lX\n", i + 1, n, m, p);
reg++;
}
// TODO: configure all queue heads with max packet length, zlt & mult
endpoint_queue_head[CDC_ACM_ENDPOINT*2+1].config = (CDC_ACM_ENDPOINT << 16);
endpoint_queue_head[CDC_RX_ENDPOINT*2+0].config = (CDC_RX_SIZE << 16) | (1 << 29);
endpoint_queue_head[CDC_TX_ENDPOINT*2+1].config = (CDC_TX_SIZE << 16) | (1 << 29);

// TODO: de-allocate any pending transfers?
#if defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE)
usb_serial_configure();
#endif
endpoint0_receive(NULL, 0, 0);
return;

Expand Down Expand Up @@ -446,6 +481,31 @@ static void endpoint0_complete(void)
#endif
}

static void usb_endpoint_config(endpoint_t *qh, uint32_t config, void (*callback)(transfer_t *))
{
memset(qh, 0, sizeof(endpoint_t));
qh->config = config;
qh->callback_function = callback;
}

void usb_config_rx(uint32_t ep, uint32_t packet_size, int do_zlp, void (*cb)(transfer_t *))
{
uint32_t config = (packet_size << 16) | (do_zlp ? 0 : (1 << 29));
if (ep < 2 || ep > NUM_ENDPOINTS) return;
usb_endpoint_config(endpoint_queue_head + ep * 2, config, cb);
if (cb) endpointN_notify_mask |= (1 << ep);
}

void usb_config_tx(uint32_t ep, uint32_t packet_size, int do_zlp, void (*cb)(transfer_t *))
{
uint32_t config = (packet_size << 16) | (do_zlp ? 0 : (1 << 29));
if (ep < 2 || ep > NUM_ENDPOINTS) return;
usb_endpoint_config(endpoint_queue_head + ep * 2 + 1, config, cb);
if (cb) endpointN_notify_mask |= (1 << (ep + 16));
}



void usb_prepare_transfer(transfer_t *transfer, const void *data, uint32_t len, uint32_t param)
{
transfer->next = 1;
Expand All @@ -459,6 +519,93 @@ void usb_prepare_transfer(transfer_t *transfer, const void *data, uint32_t len,
transfer->callback_param = param;
}


static void schedule_transfer(endpoint_t *endpoint, uint32_t epmask, transfer_t *transfer)
{
if (endpoint->callback_function) {
transfer->status |= (1<<15);
} else {
//transfer->status |= (1<<15);
// remove all inactive transfers
}
__disable_irq();
#if 0
if (endpoint->last_transfer) {
if (!(endpoint->last_transfer->status & (1<<7))) {
endpoint->last_transfer->next = (uint32_t)transfer;
} else {
// Case 2: Link list is not empty, page 3182
endpoint->last_transfer->next = (uint32_t)transfer;
if (USB1_ENDPTPRIME & epmask) {
endpoint->last_transfer = transfer;
__enable_irq();
printf(" case 2a\n");
return;
}
uint32_t stat;
uint32_t cmd = USB1_USBCMD;
do {
USB1_USBCMD = cmd | USB_USBCMD_ATDTW;
stat = USB1_ENDPTSTATUS;
} while (!(USB1_USBCMD & USB_USBCMD_ATDTW));
USB1_USBCMD = cmd & ~USB_USBCMD_ATDTW;
if (stat & epmask) {
endpoint->last_transfer = transfer;
__enable_irq();
printf(" case 2b\n");
return;
}
}
} else {
endpoint->first_transfer = transfer;
}
endpoint->last_transfer = transfer;
#endif
// Case 1: Link list is empty, page 3182
endpoint->next = (uint32_t)transfer;
endpoint->status = 0;
endpoint->first_transfer = (uint32_t)transfer;
endpoint->last_transfer = (uint32_t)transfer;
USB1_ENDPTPRIME |= epmask;
while (USB1_ENDPTPRIME & epmask) ;
__enable_irq();
//printf(" case 1\n");


// ENDPTPRIME - momentarily set by hardware during hardware re-priming
// operations when a dTD is retired, and the dQH is updated.

// ENDPTSTAT - Transmit Buffer Ready - set to one by the hardware as a
// response to receiving a command from a corresponding bit
// in the ENDPTPRIME register. . Buffer ready is cleared by
// USB reset, by the USB DMA system, or through the ENDPTFLUSH
// register. (so 0=buffer ready, 1=buffer primed for transmit)

}

void usb_transmit(int endpoint_number, transfer_t *transfer)
{
if (endpoint_number < 2 || endpoint_number > NUM_ENDPOINTS) return;
endpoint_t *endpoint = endpoint_queue_head + endpoint_number * 2 + 1;
uint32_t mask = 1 << (endpoint_number + 16);
schedule_transfer(endpoint, mask, transfer);
}

void usb_receive(int endpoint_number, transfer_t *transfer)
{
if (endpoint_number < 2 || endpoint_number > NUM_ENDPOINTS) return;
endpoint_t *endpoint = endpoint_queue_head + endpoint_number * 2;
uint32_t mask = 1 << endpoint_number;
schedule_transfer(endpoint, mask, transfer);
}







#if 0
void usb_transmit(int endpoint_number, transfer_t *transfer)
{
// endpoint 0 reserved for control
Expand Down Expand Up @@ -525,6 +672,7 @@ void usb_transmit(int endpoint_number, transfer_t *transfer)
// register. (so 0=buffer ready, 1=buffer primed for transmit)

}
#endif

/*struct endpoint_struct {
uint32_t config;
Expand Down
5 changes: 1 addition & 4 deletions teensy4/usb_desc.h
Expand Up @@ -137,12 +137,9 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports
#define CDC_ACM_SIZE 16
#define CDC_RX_SIZE 64
#define CDC_TX_SIZE 64
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_INTERRUPT + ENDPOINT_TRANSMIT_UNUSED
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_UNUSED
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_BULK
//#define ENDPOINT2_CONFIG ENDPOINT_TRANSMIT_ONLY
//#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_ONLY
//#define ENDPOINT4_CONFIG ENDPOINT_TRANSMIT_ONLY

#elif defined(USB_KEYBOARDONLY)
#define VENDOR_ID 0x16C0
Expand Down
4 changes: 4 additions & 0 deletions teensy4/usb_dev.h
Expand Up @@ -16,7 +16,11 @@ struct transfer_struct {
void usb_init(void);
void usb_init_serialnumber(void);

void usb_config_rx(uint32_t ep, uint32_t packet_size, int do_zlp, void (*cb)(transfer_t *));
void usb_config_tx(uint32_t ep, uint32_t packet_size, int do_zlp, void (*cb)(transfer_t *));

void usb_prepare_transfer(transfer_t *transfer, const void *data, uint32_t len, uint32_t param);
void usb_transmit(int endpoint_number, transfer_t *transfer);
void usb_receive(int endpoint_number, transfer_t *transfer);


52 changes: 48 additions & 4 deletions teensy4/usb_serial.c
Expand Up @@ -52,9 +52,52 @@ static volatile uint8_t tx_noautoflush=0;

#define TRANSMIT_FLUSH_TIMEOUT 5 /* in milliseconds */



#define RX_NUM 3
static transfer_t rx_transfer[RX_NUM] __attribute__ ((used, aligned(32)));
static uint8_t rx_buffer[RX_NUM * 512];
static uint16_t rx_count[RX_NUM];
static uint16_t rx_index[RX_NUM];

static void rx_event(transfer_t *t)
{
int len = 512 - ((t->status >> 16) & 0x7FFF);
int index = t->callback_param;
printf("rx event, len=%d, i=%d\n", len, index);
rx_count[index] = len;
rx_index[index] = 0;
}

void usb_serial_reset(void)
{
printf("usb_serial_reset\n");
// deallocate all transfer descriptors
}

void usb_serial_configure(void)
{
printf("usb_serial_configure\n");
usb_config_tx(CDC_ACM_ENDPOINT, CDC_ACM_SIZE, 0, NULL);
usb_config_rx(CDC_RX_ENDPOINT, CDC_RX_SIZE, 0, rx_event);
usb_config_tx(CDC_TX_ENDPOINT, CDC_TX_SIZE, 0, NULL);
usb_prepare_transfer(rx_transfer + 0, rx_buffer + 0, 512, 0);
usb_receive(CDC_RX_ENDPOINT, rx_transfer + 0);
}


// get the next character, or -1 if nothing received
int usb_serial_getchar(void)
{
if (rx_index[0] < rx_count[0]) {
int c = rx_buffer[rx_index[0]++];
if (rx_index[0] >= rx_count[0]) {
// reschedule transfer
usb_prepare_transfer(rx_transfer + 0, rx_buffer + 0, 512, 0);
usb_receive(CDC_RX_ENDPOINT, rx_transfer + 0);
}
return c;
}
#if 0
unsigned int i;
int c;
Expand Down Expand Up @@ -95,6 +138,7 @@ int usb_serial_peekchar(void)
// number of bytes available in the receive buffer
int usb_serial_available(void)
{
return rx_count[0] - rx_index[0];
#if 0
int count;
count = usb_rx_byte_count(CDC_RX_ENDPOINT);
Expand Down Expand Up @@ -204,7 +248,7 @@ int usb_serial_putchar(uint8_t c)
}


static transfer_t transfer __attribute__ ((used, aligned(32)));
static transfer_t txtransfer __attribute__ ((used, aligned(32)));
static uint8_t txbuffer[1024];
//static uint8_t txbuffer1[1024];
//static uint8_t txbuffer2[1024];
Expand All @@ -218,7 +262,7 @@ int usb_serial_write(const void *buffer, uint32_t size)
int count=0;
//digitalWriteFast(13, HIGH);
while (1) {
uint32_t status = transfer.status;
uint32_t status = txtransfer.status;
if (count > 10) printf("status = %x\n", status);
if (!(status & 0x80)) break;
count++;
Expand All @@ -229,8 +273,8 @@ int usb_serial_write(const void *buffer, uint32_t size)
//digitalWriteFast(13, LOW);
delayMicroseconds(1); // TODO: this must not be the answer!
memcpy(txbuffer, buffer, size);
usb_prepare_transfer(&transfer, txbuffer, size, 0);
usb_transmit(CDC_TX_ENDPOINT, &transfer);
usb_prepare_transfer(&txtransfer, txbuffer, size, 0);
usb_transmit(CDC_TX_ENDPOINT, &txtransfer);

#if 0
uint32_t ret = size;
Expand Down
2 changes: 2 additions & 0 deletions teensy4/usb_serial.h
Expand Up @@ -40,6 +40,8 @@
#ifdef __cplusplus
extern "C" {
#endif
void usb_serial_reset(void);
void usb_serial_configure(void);
int usb_serial_getchar(void);
int usb_serial_peekchar(void);
int usb_serial_available(void);
Expand Down

0 comments on commit 7e959d4

Please sign in to comment.