Skip to content

Commit

Permalink
stm32: Implement manual double buffering tx for usbotg
Browse files Browse the repository at this point in the history
Signed-off-by: Kevin O'Connor <[email protected]>
  • Loading branch information
KevinOConnor committed Feb 6, 2025
1 parent fec3e68 commit e594b34
Showing 1 changed file with 47 additions and 4 deletions.
51 changes: 47 additions & 4 deletions src/stm32/usbotg.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Hardware interface to "USB OTG (on the go) controller" on stm32
//
// Copyright (C) 2019 Kevin O'Connor <[email protected]>
// Copyright (C) 2019-2025 Kevin O'Connor <[email protected]>
//
// This file may be distributed under the terms of the GNU GPLv3 license.

Expand Down Expand Up @@ -119,6 +119,24 @@ fifo_write_packet(uint32_t ep, const uint8_t *src, uint32_t len)
return len;
}

// Write a packet to a tx fifo (optimized for already aligned data)
static int
fifo_write_packet_fast(uint32_t ep, const uint32_t *src, uint32_t len)
{
void *fifo = EPFIFO(ep);
USB_OTG_INEndpointTypeDef *epi = EPIN(ep);
uint32_t ctl = epi->DIEPCTL;
if (ctl & USB_OTG_DIEPCTL_EPENA)
return -1;
epi->DIEPINT = USB_OTG_DIEPINT_XFRC;
epi->DIEPTSIZ = len | (1 << USB_OTG_DIEPTSIZ_PKTCNT_Pos);
epi->DIEPCTL = ctl | USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK;
uint32_t i;
for (i=0; i < DIV_ROUND_UP(len, sizeof(uint32_t)); i++)
writel(fifo, src[i]);
return 0;
}

// Read a packet from the rx queue
static int_fast8_t
fifo_read_packet(uint8_t *dest, uint_fast8_t max_len)
Expand Down Expand Up @@ -208,6 +226,12 @@ usb_read_bulk_out(void *data, uint_fast8_t max_len)
return ret;
}

// Storage for "bulk in" transmissions for a kind of manual "double buffering"
static struct {
uint32_t len;
uint32_t buf[USB_CDC_EP_BULK_IN_SIZE / sizeof(uint32_t)];
} TX_BUF;

int_fast8_t
usb_send_bulk_in(void *data, uint_fast8_t len)
{
Expand All @@ -219,10 +243,21 @@ usb_send_bulk_in(void *data, uint_fast8_t len)
return len;
}
if (ctl & USB_OTG_DIEPCTL_EPENA) {
// Wait for space to transmit
if (TX_BUF.len || !len) {
// Wait for space to transmit
OTGD->DAINTMSK |= 1 << USB_CDC_EP_BULK_IN;
usb_irq_enable();
return -1;
}
// Buffer next packet for transmission from irq handler
len = len > USB_CDC_EP_BULK_IN_SIZE ? USB_CDC_EP_BULK_IN_SIZE : len;
uint32_t blocks = DIV_ROUND_UP(len, sizeof(uint32_t));
TX_BUF.buf[blocks-1] = 0;
memcpy(TX_BUF.buf, data, len);
TX_BUF.len = len;
OTGD->DAINTMSK |= 1 << USB_CDC_EP_BULK_IN;
usb_irq_enable();
return -1;
return len;
}
int_fast8_t ret = fifo_write_packet(USB_CDC_EP_BULK_IN, data, len);
usb_irq_enable();
Expand Down Expand Up @@ -373,6 +408,7 @@ usb_set_configure(void)
| USB_OTG_GRSTCTL_TXFFLSH);
while (OTG->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH)
;
TX_BUF.len = 0;
usb_irq_enable();
}

Expand Down Expand Up @@ -401,8 +437,15 @@ OTG_FS_IRQHandler(void)
OTGD->DAINTMSK = msk & ~daint;
if (pend & (1 << 0))
usb_notify_ep0();
if (pend & (1 << USB_CDC_EP_BULK_IN))
if (pend & (1 << USB_CDC_EP_BULK_IN)) {
usb_notify_bulk_in();
if (TX_BUF.len) {
int ret = fifo_write_packet_fast(USB_CDC_EP_BULK_IN
, TX_BUF.buf, TX_BUF.len);
if (!ret)
TX_BUF.len = 0;
}
}
}
}

Expand Down

0 comments on commit e594b34

Please sign in to comment.