Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USB "fast buffer switching" on stm32 usbfs devices #6352

Merged
merged 4 commits into from
Oct 4, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 133 additions & 75 deletions src/stm32/usbfs.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Hardware interface to "fullspeed USB controller"
//
// Copyright (C) 2018-2021 Kevin O'Connor <[email protected]>
// Copyright (C) 2018-2023 Kevin O'Connor <[email protected]>
//
// This file may be distributed under the terms of the GNU GPLv3 license.

Expand Down Expand Up @@ -48,78 +48,86 @@
#define USB_CNTR_FRES USB_CNTR_USBRST
#endif

// Some chip variants do not define these fields
#ifndef USB_EP_DTOG_TX_Pos
#define USB_EP_DTOG_TX_Pos 6
#define USB_EP_DTOG_RX_Pos 14
#endif


/****************************************************************
* USB transfer memory
****************************************************************/

// Layout of the USB transfer memory
#define EPM ((epmword_t*)USB_PMAADDR)
#define EPM_EP_DESC(ep) (&EPM[(ep) * (8 / WSIZE)])
#define EPM_EP_DESC(ep, bufnum) (&EPM[((ep)*2 + (bufnum)) * (4 / WSIZE)])
#define EPM_BUF_OFFSET 0x10
#define EPM_EP_BUF_SIZE (64 / WSIZE + 1)
#define EPM_EP_TX_BUF(ep) (&EPM[EPM_BUF_OFFSET + (ep)*2*EPM_EP_BUF_SIZE])
#define EPM_EP_RX_BUF(ep) (&EPM[EPM_BUF_OFFSET + (1+(ep)*2)*EPM_EP_BUF_SIZE])
#define EPM_EP_BUF(ep, bufnum) \
(&EPM[EPM_BUF_OFFSET + ((ep)*2 + (bufnum)) * EPM_EP_BUF_SIZE])
#define BUFTX 0
#define BUFRX 1

// Configure the usb descriptor for an endpoint
static void
epm_ep_desc_setup(int ep, int rx_size)
epm_ep_desc_setup(int ep, int bufnum, int rx_size)
{
uint32_t addr_tx = (EPM_EP_TX_BUF(ep) - EPM) * WSIZE, count_tx = 0;
uint32_t addr_rx = (EPM_EP_RX_BUF(ep) - EPM) * WSIZE;
uint32_t addr = (EPM_EP_BUF(ep, bufnum) - EPM) * WSIZE;
uint32_t count_rx = (rx_size <= 30 ? DIV_ROUND_UP(rx_size, 2) << 10
: ((DIV_ROUND_UP(rx_size, 32) - 1) << 10) | 0x8000);
epmword_t *desc = EPM_EP_DESC(ep);
epmword_t *desc = EPM_EP_DESC(ep, bufnum);
if (WSIZE == 2) {
desc[0] = addr_tx;
desc[1] = count_tx;
desc[2] = addr_rx;
desc[3] = count_rx;
desc[0] = addr;
desc[1] = count_rx;
} else {
desc[0] = addr_tx | (count_tx << 16);
desc[1] = addr_rx | (count_rx << 16);
*desc = addr | (count_rx << 16);
}
}

// Return number of read bytes on an rx endpoint
static uint32_t
epm_get_ep_count_rx(int ep)
epm_get_ep_count_rx(int ep, int bufnum)
{
epmword_t *desc = EPM_EP_DESC(ep);
epmword_t *desc = EPM_EP_DESC(ep, bufnum);
if (WSIZE == 2)
return desc[3] & 0x3ff;
return (desc[1] >> 16) & 0x3ff;
return desc[1] & 0x3ff;
return (*desc >> 16) & 0x3ff;
}

// Set number of bytes ready to be transmitted on a tx endpoint
static void
epm_set_ep_count_tx(int ep, uint32_t count)
epm_set_ep_count_tx(int ep, int bufnum, uint32_t count)
{
epmword_t *desc = EPM_EP_DESC(ep);
epmword_t *desc = EPM_EP_DESC(ep, bufnum);
if (WSIZE == 2) {
desc[1] = count;
} else {
uint32_t addr_tx = (EPM_EP_TX_BUF(ep) - EPM) * WSIZE;
desc[0] = addr_tx | (count << 16);
uint32_t addr_tx = (EPM_EP_BUF(ep, bufnum) - EPM) * WSIZE;
*desc = addr_tx | (count << 16);
}
}

// Setup the transfer descriptors in dedicated usb memory
static void
btable_configure(void)
{
epm_ep_desc_setup(0, USB_CDC_EP0_SIZE);
epm_ep_desc_setup(USB_CDC_EP_ACM, 0);
epm_ep_desc_setup(USB_CDC_EP_BULK_OUT, USB_CDC_EP_BULK_OUT_SIZE);
epm_ep_desc_setup(USB_CDC_EP_BULK_IN, 0);
epm_ep_desc_setup(0, BUFTX, 0);
epm_ep_desc_setup(0, BUFRX, USB_CDC_EP0_SIZE);
epm_ep_desc_setup(USB_CDC_EP_ACM, BUFTX, 0);
epm_ep_desc_setup(USB_CDC_EP_ACM, BUFRX, 0);
epm_ep_desc_setup(USB_CDC_EP_BULK_OUT, 0, USB_CDC_EP_BULK_OUT_SIZE);
epm_ep_desc_setup(USB_CDC_EP_BULK_OUT, 1, USB_CDC_EP_BULK_OUT_SIZE);
epm_ep_desc_setup(USB_CDC_EP_BULK_IN, 0, 0);
epm_ep_desc_setup(USB_CDC_EP_BULK_IN, 1, 0);
}

// Read a packet stored in dedicated usb memory
static uint32_t
btable_read_packet(int ep, uint8_t *dest, int max_len)
btable_read_packet(int ep, int bufnum, uint8_t *dest, int max_len)
{
epmword_t *src = EPM_EP_RX_BUF(ep);
uint32_t count = epm_get_ep_count_rx(ep);
epmword_t *src = EPM_EP_BUF(ep, bufnum);
uint32_t count = epm_get_ep_count_rx(ep, bufnum);
if (count > max_len)
count = max_len;
int i;
Expand All @@ -145,9 +153,9 @@ btable_read_packet(int ep, uint8_t *dest, int max_len)

// Write a packet to dedicated usb memory
static void
btable_write_packet(int ep, const uint8_t *src, int count)
btable_write_packet(int ep, int bufnum, const uint8_t *src, int count)
{
epmword_t *dest = EPM_EP_TX_BUF(ep);
epmword_t *dest = EPM_EP_BUF(ep, bufnum);
int i;
for (i=0; i<count/WSIZE; i++) {
uint8_t b1 = *src++, b2 = *src++, b3 = 0, b4 = 0;
Expand All @@ -165,7 +173,7 @@ btable_write_packet(int ep, const uint8_t *src, int count)
d |= *src++ << 16;
*dest = d;
}
epm_set_ep_count_tx(ep, count);
epm_set_ep_count_tx(ep, bufnum, count);
}


Expand All @@ -175,66 +183,93 @@ btable_write_packet(int ep, const uint8_t *src, int count)

#define USB_EPR ((volatile uint32_t *)USB_BASE)

#define EPR_TBITS (USB_EP_DTOG_RX | USB_EPRX_STAT \
| USB_EP_DTOG_TX | USB_EPTX_STAT)
#define EPR_RWBITS (USB_EPADDR_FIELD | USB_EP_KIND | USB_EP_TYPE_MASK)
#define EPR_RWCBITS (USB_EP_CTR_RX | USB_EP_CTR_TX)

// Calculate the memory update needed to set the epr register
static uint32_t
set_stat_rx_bits(uint32_t epr, uint32_t bits)
{
return ((epr & (EPR_RWBITS | USB_EPRX_STAT)) ^ bits) | EPR_RWCBITS;
}

static uint32_t
set_stat_tx_bits(uint32_t epr, uint32_t bits)
calc_epr_bits(uint32_t epr, uint32_t mask, uint32_t value)
{
return ((epr & (EPR_RWBITS | USB_EPTX_STAT)) ^ bits) | EPR_RWCBITS;
uint32_t tmask = mask & EPR_TBITS, tvalue = value & tmask;
uint32_t rwmask = mask & EPR_RWBITS, rwbits = value & rwmask;
uint32_t cbits = EPR_RWCBITS & ~mask;
return (((epr & (EPR_RWBITS | tmask)) ^ tvalue) & ~rwmask) | rwbits | cbits;
}

static uint32_t
set_stat_rxtx_bits(uint32_t epr, uint32_t bits)
// Check if double buffering endpoint hardware can no longer send/receive
static int
epr_is_dbuf_blocking(uint32_t epr)
{
uint32_t mask = EPR_RWBITS | USB_EPRX_STAT | USB_EPTX_STAT;
return ((epr & mask) ^ bits) | EPR_RWCBITS;
return !(((epr >> (USB_EP_DTOG_RX_Pos - USB_EP_DTOG_TX_Pos)) ^ epr)
& USB_EP_DTOG_TX);
}


/****************************************************************
* USB interface
****************************************************************/

static uint32_t bulk_out_pop_count, bulk_out_push_flag;

int_fast8_t
usb_read_bulk_out(void *data, uint_fast8_t max_len)
{
uint32_t epr = USB_EPR[USB_CDC_EP_BULK_OUT];
if ((epr & USB_EPRX_STAT) == USB_EP_RX_VALID)
if (readl(&bulk_out_push_flag))
// No data ready
return -1;
uint32_t count = btable_read_packet(USB_CDC_EP_BULK_OUT, data, max_len);
USB_EPR[USB_CDC_EP_BULK_OUT] = set_stat_rx_bits(epr, USB_EP_RX_VALID);
uint32_t ep = USB_CDC_EP_BULK_OUT;
int bufnum = bulk_out_pop_count & 1;
bulk_out_pop_count++;
uint32_t count = btable_read_packet(ep, bufnum, data, max_len);
writel(&bulk_out_push_flag, USB_EP_DTOG_TX);

// Check if irq handler pulled another packet before push flag update
uint32_t epr = USB_EPR[ep];
if (epr_is_dbuf_blocking(epr) && readl(&bulk_out_push_flag)) {
// Second packet was already read - must notify hardware
writel(&bulk_out_push_flag, 0);
USB_EPR[ep] = calc_epr_bits(epr, 0, 0) | USB_EP_DTOG_TX;
}

return count;
}

static uint32_t bulk_in_push_count, bulk_in_pop_flag;

int_fast8_t
usb_send_bulk_in(void *data, uint_fast8_t len)
{
uint32_t epr = USB_EPR[USB_CDC_EP_BULK_IN];
if ((epr & USB_EPTX_STAT) != USB_EP_TX_NAK)
if (readl(&bulk_in_pop_flag))
// No buffer space available
return -1;
btable_write_packet(USB_CDC_EP_BULK_IN, data, len);
USB_EPR[USB_CDC_EP_BULK_IN] = set_stat_tx_bits(epr, USB_EP_TX_VALID);
uint32_t ep = USB_CDC_EP_BULK_IN;
int bufnum = bulk_in_push_count & 1;
bulk_in_push_count++;
btable_write_packet(ep, bufnum, data, len);
writel(&bulk_in_pop_flag, USB_EP_DTOG_RX);

// Check if hardware needs to be notified
uint32_t epr = USB_EPR[ep];
if (epr_is_dbuf_blocking(epr) && readl(&bulk_in_pop_flag)) {
writel(&bulk_in_pop_flag, 0);
USB_EPR[ep] = calc_epr_bits(epr, 0, 0) | USB_EP_DTOG_RX;
}

return len;
}

int_fast8_t
usb_read_ep0(void *data, uint_fast8_t max_len)
{
uint32_t epr = USB_EPR[0];
uint32_t ep = 0, epr = USB_EPR[ep];
if ((epr & USB_EPRX_STAT) != USB_EP_RX_NAK)
// No data ready
return -1;
uint32_t count = btable_read_packet(0, data, max_len);
USB_EPR[0] = set_stat_rxtx_bits(epr, USB_EP_RX_VALID | USB_EP_TX_NAK);
uint32_t count = btable_read_packet(ep, BUFRX, data, max_len);
USB_EPR[ep] = calc_epr_bits(epr, USB_EPRX_STAT | USB_EPTX_STAT
, USB_EP_RX_VALID | USB_EP_TX_NAK);
return count;
}

Expand All @@ -247,23 +282,24 @@ usb_read_ep0_setup(void *data, uint_fast8_t max_len)
int_fast8_t
usb_send_ep0(const void *data, uint_fast8_t len)
{
uint32_t epr = USB_EPR[0];
uint32_t ep = 0, epr = USB_EPR[ep];
if ((epr & USB_EPRX_STAT) != USB_EP_RX_VALID)
// Transfer interrupted
return -2;
if ((epr & USB_EPTX_STAT) != USB_EP_TX_NAK)
// No buffer space available
return -1;
btable_write_packet(0, data, len);
USB_EPR[0] = set_stat_tx_bits(epr, USB_EP_TX_VALID);
btable_write_packet(ep, BUFTX, data, len);
USB_EPR[ep] = calc_epr_bits(epr, USB_EPTX_STAT, USB_EP_TX_VALID);
return len;
}

void
usb_stall_ep0(void)
{
USB_EPR[0] = set_stat_rxtx_bits(USB_EPR[0]
, USB_EP_RX_STALL | USB_EP_TX_STALL);
uint32_t ep = 0, epr = USB_EPR[ep];
USB_EPR[ep] = calc_epr_bits(epr, USB_EPRX_STAT | USB_EPTX_STAT
, USB_EP_RX_STALL | USB_EP_TX_STALL);
}

static uint8_t set_address;
Expand All @@ -278,6 +314,14 @@ usb_set_address(uint_fast8_t addr)
void
usb_set_configure(void)
{
uint32_t ep = USB_CDC_EP_BULK_OUT;
bulk_out_pop_count = 0;
USB_EPR[ep] = calc_epr_bits(USB_EPR[ep], USB_EPRX_STAT, USB_EP_RX_VALID);

ep = USB_CDC_EP_BULK_IN;
bulk_in_push_count = 0;
writel(&bulk_in_pop_flag, 0);
USB_EPR[ep] = calc_epr_bits(USB_EPR[ep], USB_EPTX_STAT, USB_EP_TX_VALID);
}


Expand All @@ -289,13 +333,22 @@ usb_set_configure(void)
static void
usb_reset(void)
{
USB_EPR[0] = 0 | USB_EP_CONTROL | USB_EP_RX_VALID | USB_EP_TX_NAK;
USB_EPR[USB_CDC_EP_ACM] = (USB_CDC_EP_ACM | USB_EP_INTERRUPT
| USB_EP_RX_NAK | USB_EP_TX_NAK);
USB_EPR[USB_CDC_EP_BULK_OUT] = (USB_CDC_EP_BULK_OUT | USB_EP_BULK
| USB_EP_RX_VALID | USB_EP_TX_NAK);
USB_EPR[USB_CDC_EP_BULK_IN] = (USB_CDC_EP_BULK_IN | USB_EP_BULK
| USB_EP_RX_NAK | USB_EP_TX_NAK);
uint32_t ep = 0;
USB_EPR[ep] = 0 | USB_EP_CONTROL | USB_EP_RX_VALID | USB_EP_TX_NAK;

ep = USB_CDC_EP_ACM;
USB_EPR[ep] = (USB_CDC_EP_ACM | USB_EP_INTERRUPT
| USB_EP_RX_NAK | USB_EP_TX_NAK);

ep = USB_CDC_EP_BULK_OUT;
USB_EPR[ep] = (USB_CDC_EP_BULK_OUT | USB_EP_BULK | USB_EP_KIND
| USB_EP_RX_VALID | USB_EP_TX_NAK | USB_EP_DTOG_TX);
bulk_out_push_flag = USB_EP_DTOG_TX;

ep = USB_CDC_EP_BULK_IN;
USB_EPR[ep] = (USB_CDC_EP_BULK_IN | USB_EP_BULK | USB_EP_KIND
| USB_EP_RX_NAK | USB_EP_TX_NAK);
bulk_in_pop_flag = USB_EP_DTOG_RX;

USB->CNTR = USB_CNTR_CTRM | USB_CNTR_RESETM;
USB->DADDR = USB_DADDR_EF;
Expand All @@ -308,20 +361,25 @@ USB_IRQHandler(void)
uint32_t istr = USB->ISTR;
if (istr & USB_ISTR_CTR) {
// Endpoint activity
uint32_t ep = istr & USB_ISTR_EP_ID;
uint32_t epr = USB_EPR[ep];
USB_EPR[ep] = epr & EPR_RWBITS;
if (ep == 0) {
uint32_t ep = istr & USB_ISTR_EP_ID, epr = USB_EPR[ep];
if (ep == USB_CDC_EP_BULK_OUT) {
USB_EPR[ep] = (calc_epr_bits(epr, USB_EP_CTR_RX | USB_EP_CTR_TX, 0)
| bulk_out_push_flag);
bulk_out_push_flag = 0;
usb_notify_bulk_out();
} else if (ep == USB_CDC_EP_BULK_IN) {
USB_EPR[ep] = (calc_epr_bits(epr, USB_EP_CTR_RX | USB_EP_CTR_TX, 0)
| bulk_in_pop_flag);
bulk_in_pop_flag = 0;
usb_notify_bulk_in();
} else if (ep == 0) {
USB_EPR[ep] = calc_epr_bits(epr, USB_EP_CTR_RX | USB_EP_CTR_TX, 0);
usb_notify_ep0();
if (epr & USB_EP_CTR_TX && set_address) {
// Apply address after last "in" message transmitted
USB->DADDR = set_address;
set_address = 0;
}
} else if (ep == USB_CDC_EP_BULK_OUT) {
usb_notify_bulk_out();
} else if (ep == USB_CDC_EP_BULK_IN) {
usb_notify_bulk_in();
}
}
if (istr & USB_ISTR_RESET) {
Expand Down