Browse Source

Fix USB schedule_transfer()

main
PaulStoffregen 5 years ago
parent
commit
f5201cfe1d
2 changed files with 52 additions and 174 deletions
  1. +37
    -162
      teensy4/usb.c
  2. +15
    -12
      teensy4/usb_serial.c

+ 37
- 162
teensy4/usb.c View File

@@ -245,7 +245,7 @@ static void isr(void)
usb_serial_reset();
#endif
endpointN_notify_mask = 0;
// TODO: Free all allocated dTDs
// TODO: Free all allocated dTDs
//if (++reset_count >= 3) {
// shut off USB - easier to see results in protocol analyzer
//USB1_USBCMD &= ~USB_USBCMD_RS;
@@ -518,47 +518,26 @@ void usb_prepare_transfer(transfer_t *transfer, const void *data, uint32_t len,
transfer->callback_param = param;
}

static uint32_t get_endptstatus(void)
{
uint32_t status, cmd;
cmd = USB1_USBCMD;
do {
USB1_USBCMD = cmd | USB_USBCMD_ATDTW;
status = USB1_ENDPTSTATUS;
} while (!(USB1_USBCMD & USB_USBCMD_ATDTW));
return status;
}

static void schedule_transfer(endpoint_t *endpoint, uint32_t epmask, transfer_t *transfer)
{
transfer_t *last, *next;

// 41.5.6.6.3 Executing A Transfer Descriptor, page 2468 (RT1060 manual, Rev 1, 12/2018)
transfer->next = 1;
if (endpoint->callback_function) {
// endpoint uses interrupts and maintains linked list of all transfers
transfer->status |= (1<<15);
last = endpoint->last_transfer;
} else {
// endpoint has no callback, no list of transfers
//if ((USB1_ENDPTPRIME & epmask) || (get_endptstatus() & epmask)) {
last = (transfer_t *)(endpoint->next & ~0x1F);
if (last) {
while (1) {
next = (transfer_t *)(last->next & ~0x1F);
if (!next) break;
last = next;
}
}
//} else {
//last = NULL;
//}
}
if (last) {
last->next = transfer;
if ((USB1_ENDPTPRIME & epmask) || (get_endptstatus() & epmask)) {
endpoint->last_transfer = transfer;
__enable_irq();
return;
__disable_irq();
// 41.5.6.6.3 Executing A Transfer Descriptor, page 2468 (RT1060 manual, Rev 1, 12/2018)
// Not exactly the same process as NXP suggests....
if ((USB1_ENDPTPRIME & epmask) || (USB1_ENDPTSTATUS & epmask)) {
transfer_t *last = endpoint->last_transfer;
if (last) {
USB1_USBCMD |= USB_USBCMD_ATDTW;
last->next = (uint32_t)transfer;
if ((USB1_ENDPTPRIME & epmask) ||
((USB1_ENDPTSTATUS & epmask) && (USB1_USBCMD & USB_USBCMD_ATDTW))) {
endpoint->last_transfer = transfer;
__enable_irq();
return;
}
}
}
endpoint->next = (uint32_t)transfer;
@@ -567,6 +546,7 @@ static void schedule_transfer(endpoint_t *endpoint, uint32_t epmask, transfer_t
endpoint->last_transfer = transfer;
USB1_ENDPTPRIME |= epmask;
__enable_irq();
}
// ENDPTPRIME - Software should write a one to the corresponding bit when
// posting a new transfer descriptor to an endpoint queue head.
// Hardware automatically uses this bit to begin parsing for a
@@ -588,7 +568,25 @@ static void schedule_transfer(endpoint_t *endpoint, uint32_t epmask, transfer_t
// This bit would also be cleared by hardware when state machine
// is hazard region for which adding a dTD to a primed endpoint
// may go unrecognized.
}

/*struct endpoint_struct {
uint32_t config;
uint32_t current;
uint32_t next;
uint32_t status;
uint32_t pointer0;
uint32_t pointer1;
uint32_t pointer2;
uint32_t pointer3;
uint32_t pointer4;
uint32_t reserved;
uint32_t setup0;
uint32_t setup1;
transfer_t *first_transfer;
transfer_t *last_transfer;
void (*callback_function)(transfer_t *completed_transfer);
uint32_t unused1;
};*/

static void run_callbacks(endpoint_t *ep)
{
@@ -606,6 +604,7 @@ static void run_callbacks(endpoint_t *ep)
ep->first_transfer = t;
return;
}
if (next == ep->last_transfer) break;
t = next;
}
// all transfers completed
@@ -613,7 +612,6 @@ static void run_callbacks(endpoint_t *ep)
ep->last_transfer = NULL;
}


void usb_transmit(int endpoint_number, transfer_t *transfer)
{
if (endpoint_number < 2 || endpoint_number > NUM_ENDPOINTS) return;
@@ -633,137 +631,14 @@ void usb_receive(int endpoint_number, transfer_t *transfer)
uint32_t usb_transfer_status(const transfer_t *transfer)
{
uint32_t status, cmd;
//uint32_t count=0;

cmd = USB1_USBCMD;
do {
//count++;
USB1_USBCMD = cmd | USB_USBCMD_ATDTW;
status = transfer->status;
cmd = USB1_USBCMD;
} while (!(cmd & USB_USBCMD_ATDTW));
//if (count > 1) printf("s=%08X, count=%d\n", status, count);
//printf("s=%08X, count=%d\n", status, count);
return status;
}





#if 0
void usb_transmit(int endpoint_number, transfer_t *transfer)
{
// endpoint 0 reserved for control
// endpoint 1 reserved for debug
//printf("usb_transmit %d\n", endpoint_number);
if (endpoint_number < 2 || endpoint_number > NUM_ENDPOINTS) return;
endpoint_t *endpoint = &endpoint_queue_head[endpoint_number * 2 + 1];
if (endpoint->callback_function) {
transfer->status |= (1<<15);
} else {
//transfer->status |= (1<<15);
// remove all inactive transfers
}
uint32_t mask = 1 << (endpoint_number + 16);
__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 & mask) {
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 & mask) {
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;
USB1_ENDPTPRIME |= mask;
while (USB1_ENDPTPRIME & mask) ;
__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)

}
#endif

/*struct endpoint_struct {
uint32_t config;
uint32_t current;
uint32_t next;
uint32_t status;
uint32_t pointer0;
uint32_t pointer1;
uint32_t pointer2;
uint32_t pointer3;
uint32_t pointer4;
uint32_t reserved;
uint32_t setup0;
uint32_t setup1;
transfer_t *first_transfer;
transfer_t *last_transfer;
void (*callback_function)(transfer_t *completed_transfer);
uint32_t unused1;
};*/






























+ 15
- 12
teensy4/usb_serial.c View File

@@ -247,20 +247,21 @@ int usb_serial_putchar(uint8_t c)
return usb_serial_write(&c, 1);
}


static transfer_t txtransfer __attribute__ ((used, aligned(32)));
static uint8_t txbuffer[1024];
//static uint8_t txbuffer1[1024];
//static uint8_t txbuffer2[1024];
//static uint8_t txstate=0;
#define TX_NUM 4
#define TX_SIZE 64 /* should be a multiple of CDC_TX_SIZE */
static transfer_t tx_transfer[TX_NUM] __attribute__ ((used, aligned(32)));
static uint8_t txbuffer[TX_SIZE * TX_NUM];
static uint8_t tx_head=0;

int usb_serial_write(const void *buffer, uint32_t size)
{
if (!usb_configuration) return 0;
if (size > sizeof(txbuffer)) size = sizeof(txbuffer);
if (size > TX_SIZE) size = TX_SIZE;

transfer_t *xfer = tx_transfer + tx_head;
int count=0;
while (1) {
uint32_t status = usb_transfer_status(&txtransfer);
uint32_t status = usb_transfer_status(xfer);
if (count > 2000) {
printf("status = %x\n", status);
//while (1) ;
@@ -270,10 +271,12 @@ int usb_serial_write(const void *buffer, uint32_t size)
//if (count > 50) break; // TODO: proper timout?
// TODO: check for USB offline
}
memcpy(txbuffer, buffer, size);
usb_prepare_transfer(&txtransfer, txbuffer, size, 0);
usb_transmit(CDC_TX_ENDPOINT, &txtransfer);

uint8_t *txdata = txbuffer + (tx_head * TX_SIZE);
memcpy(txdata, buffer, size);
usb_prepare_transfer(xfer, txdata, size, 0);
usb_transmit(CDC_TX_ENDPOINT, xfer);
if (++tx_head >= TX_NUM) tx_head = 0;
return size;
#if 0
uint32_t ret = size;
uint32_t len;

Loading…
Cancel
Save