#include <drv/timer.h>
#include <drv/usb.h>
+#include <mware/event.h>
+
#include <string.h> /* memcpy() */
#include "usb_stm32.h"
-#define ALIGN_UP(value, align) (((value) & ((align) - 1)) ? \
- (((value) + ((align) - 1)) & ~((align) - 1)) : \
- (value))
+/* XXX: consider to move this to cfg/macros.h */
/* XXX: redefine this to make it usable within C expression */
#define _MIN(a,b) (((a) < (b)) ? (a) : (b))
static struct stm32_usb *usb = (struct stm32_usb *)USB_BASE_ADDR;
/* Endpoint descriptors: used for handling requests to use with endpoints */
-static stm32_UsbEp ep_cnfg[ENP_MAX_NUMB];
+static stm32_UsbEp ep_cnfg[EP_MAX_NUM];
+STATIC_ASSERT(EP_MAX_NUM <= EP_MAX_HW_NUM);
/* USB EP0 control descriptor */
static const UsbEndpointDesc USB_CtrlEpDescr0 =
static UsbCtrlRequest setup_packet;
/* USB device controller: max supported interfaces */
-#define USB_MAX_INTERFACE 1
+#define USB_MAX_INTERFACE CONFIG_USB_INTERFACE_MAX
/* USB device controller features */
#define STM32_UDC_FEATURE_SELFPOWERED BV(0)
static stm32_UsbMemSlot *mem_use;
/* USB packet memory management: memory buffer metadata */
-#define EP_MAX_SLOTS 16
-static stm32_UsbMemSlot memory_buffer[EP_MAX_SLOTS];
+static stm32_UsbMemSlot memory_buffer[EP_MAX_NUM];
+
+/* Endpoint TX and RX buffers */
+static size_t rx_size, tx_size;
+
+#define EP_BUFFER_SIZE _MIN(CONFIG_USB_BUFSIZE, USB_XFER_MAX_SIZE)
+STATIC_ASSERT(!(EP_BUFFER_SIZE & 0x03));
+
+static uint8_t ep_buffer[EP_MAX_NUM][EP_BUFFER_SIZE] ALIGNED(4);
+
+static Event usb_event_done[EP_MAX_SLOTS];
+
+/* Check if we're running in atomic (non-sleepable) context or not */
+static volatile bool in_atomic = false;
/* Allocate a free block of the packet memory */
static stm32_UsbMemSlot *usb_malloc(void)
mem = mem->next;
}
/* Check for out-of-memory condition */
- if ((*pOffset + max_size) >= USB_BDT_OFFSET)
+ if (UNLIKELY((*pOffset + max_size) >= USB_BDT_OFFSET))
return false;
/*
* Allocate a new memory block, next to the last allocated block.
*/
mem_useNew = usb_malloc();
- if (mem_useNew == NULL)
+ if (UNLIKELY(mem_useNew == NULL))
return false;
/* Insert the block to the list of allocated blocks */
if (mem_use == NULL)
static stm32_UsbIoStatus
__usb_ep_read(int ep, void *buffer, ssize_t size, void (*complete)(int))
{
- if (UNLIKELY((ep >= ENP_MAX_NUMB) || (ep & 0x01)))
+ if (UNLIKELY((ep >= EP_MAX_NUM) || (ep & 0x01)))
{
LOG_ERR("%s: invalid EP number %d\n", __func__, ep);
ASSERT(0);
static stm32_UsbIoStatus
__usb_ep_write(int ep, const void *buffer, ssize_t size, void (*complete)(int))
{
- if (UNLIKELY((ep >= ENP_MAX_NUMB) || !(ep & 0x01)))
+ if (UNLIKELY((ep >= EP_MAX_NUM) || !(ep & 0x01)))
{
LOG_ERR("%s: invalid EP number %d\n", __func__, ep);
ASSERT(0);
switch (ep_hw->type)
{
case USB_ENDPOINT_XFER_CONTROL:
- LOG_INFO("EP%d: CONTROL IN\n", EP >> 1);
+ LOG_INFO("EP%d: CONTROL %s\n", EP >> 1,
+ EP & 1 ? "IN" : "OUT");
ep_ctrl_set_ep_type(hw, EP_CTRL);
ep_ctrl_set_ep_kind(hw, 0);
break;
case USB_ENDPOINT_XFER_INT:
- LOG_INFO("EP%d: INTERRUPT IN\n", EP >> 1);
+ LOG_INFO("EP%d: INTERRUPT %s\n", EP >> 1,
+ EP & 1 ? "IN" : "OUT");
ep_ctrl_set_ep_type(hw, EP_INTERRUPT);
ep_ctrl_set_ep_kind(hw, 0);
break;
case USB_ENDPOINT_XFER_BULK:
- LOG_INFO("EP%d: BULK IN\n", EP >> 1);
+ LOG_INFO("EP%d: BULK %s\n", EP >> 1,
+ EP & 1 ? "IN" : "OUT");
ep_ctrl_set_ep_type(hw, EP_BULK);
ep_ctrl_set_ep_kind(hw, 0);
break;
case USB_ENDPOINT_XFER_ISOC:
- LOG_ERR("EP%d: ISOCHRONOUS IN: not supported\n", EP >> 1);
+ LOG_ERR("EP%d: ISOCHRONOUS %s: not supported\n",
+ EP >> 1,
+ EP & 1 ? "IN" : "OUT");
/* Fallback to default */
default:
ASSERT(0);
}
}
-static bool rx_done;
-static size_t rx_size;
-static uint8_t rx_buffer[_MIN(CONFIG_USB_RXBUFSIZE, USB_RX_MAX_SIZE)]
- __attribute__ ((__aligned__(4)));
-
static void usb_endpointRead_complete(int ep)
{
- if (UNLIKELY(ep >= ENP_MAX_NUMB))
+ if (UNLIKELY(ep >= EP_MAX_NUM))
{
ASSERT(0);
return;
}
ASSERT(!(ep & 0x01));
- rx_done = true;
+ event_do(&usb_event_done[ep >> 1]);
rx_size = ep_cnfg[ep].size;
}
-ssize_t usb_endpointRead(int ep, void *buffer, ssize_t size)
+ssize_t usb_endpointReadTimeout(int ep, void *buffer, ssize_t size,
+ ticks_t timeout)
{
int ep_num = usb_ep_logical_to_hw(ep);
- ssize_t max_size = sizeof(rx_buffer);
+ ssize_t max_size = sizeof(ep_buffer[ep_num]);
/* Non-blocking read for EP0 */
- if (ep_num == CTRL_ENP_OUT)
+ if (in_atomic && (ep_num == CTRL_ENP_OUT))
{
size = usb_size(size, usb_le16_to_cpu(setup_packet.wLength));
if (UNLIKELY(size > max_size))
{
- LOG_ERR("%s: rx_buffer exceeded, try to enlarge CONFIG_USB_RXBUFSIZE\n",
+ LOG_ERR("%s: ep_buffer exceeded, try to enlarge CONFIG_USB_BUFSIZE\n",
__func__);
ASSERT(0);
return -USB_BUF_OVERFLOW;
usb_status_handler(ep_num);
else
{
- __usb_ep_read(ep_num, rx_buffer, size,
+ __usb_ep_read(ep_num, ep_buffer[ep_num], size,
usb_status_handler);
- memcpy(buffer, rx_buffer, size);
+ memcpy(buffer, ep_buffer[ep_num], size);
}
return size;
}
if (UNLIKELY(!size))
return 0;
size = MIN(size, max_size);
- rx_done = false;
+ event_initGeneric(&usb_event_done[ep_num >> 1]);
rx_size = 0;
/* Blocking read */
- __usb_ep_read(ep_num, rx_buffer, size, usb_endpointRead_complete);
- while (!rx_done)
- cpu_relax();
- memcpy(buffer, rx_buffer, rx_size);
+ __usb_ep_read(ep_num, ep_buffer[ep_num], size,
+ usb_endpointRead_complete);
+ if (timeout < 0)
+ event_wait(&usb_event_done[ep_num >> 1]);
+ else
+ if (!event_waitTimeout(&usb_event_done[ep_num >> 1], timeout))
+ return 0;
+ memcpy(buffer, ep_buffer[ep_num], rx_size);
return rx_size;
}
-static bool tx_done;
-static size_t tx_size;
-static uint8_t tx_buffer[_MIN(CONFIG_USB_TXBUFSIZE, USB_TX_MAX_SIZE)]
- __attribute__ ((__aligned__(4)));
-
static void usb_endpointWrite_complete(int ep)
{
- if (UNLIKELY(ep >= ENP_MAX_NUMB))
+ if (UNLIKELY(ep >= EP_MAX_NUM))
{
ASSERT(0);
return;
}
ASSERT(ep & 0x01);
- tx_done = true;
+ event_do(&usb_event_done[ep >> 1]);
tx_size = ep_cnfg[ep].size;
}
-ssize_t usb_endpointWrite(int ep, const void *buffer, ssize_t size)
+ssize_t usb_endpointWriteTimeout(int ep, const void *buffer, ssize_t size,
+ ticks_t timeout)
{
int ep_num = usb_ep_logical_to_hw(ep);
- ssize_t max_size = sizeof(tx_buffer);
+ ssize_t max_size = sizeof(ep_buffer[ep_num]);
/* Non-blocking write for EP0 */
- if (ep_num == CTRL_ENP_IN)
+ if (in_atomic && (ep_num == CTRL_ENP_IN))
{
size = usb_size(size, usb_le16_to_cpu(setup_packet.wLength));
if (UNLIKELY(size > max_size))
{
- LOG_ERR("%s: tx_buffer exceeded, try to enlarge CONFIG_USB_TXBUFSIZE\n",
+ LOG_ERR("%s: ep_buffer exceeded, try to enlarge CONFIG_USB_BUFSIZE\n",
__func__);
ASSERT(0);
return -USB_BUF_OVERFLOW;
usb_status_handler(ep_num);
else
{
- memcpy(tx_buffer, buffer, size);
- __usb_ep_write(ep_num, tx_buffer, size,
+ memcpy(ep_buffer[ep_num], buffer, size);
+ __usb_ep_write(ep_num, ep_buffer[ep_num], size,
usb_status_handler);
}
return size;
if (UNLIKELY(!size))
return 0;
size = MIN(size, max_size);
- tx_done = false;
+ event_initGeneric(&usb_event_done[ep_num >> 1]);
tx_size = 0;
/* Blocking write */
- memcpy(tx_buffer, buffer, size);
- __usb_ep_write(ep_num, tx_buffer, size, usb_endpointWrite_complete);
- while (!tx_done)
- cpu_relax();
+ memcpy(ep_buffer[ep_num], buffer, size);
+ __usb_ep_write(ep_num, ep_buffer[ep_num], size,
+ usb_endpointWrite_complete);
+ if (timeout < 0)
+ event_wait(&usb_event_done[ep_num >> 1]);
+ else
+ if (!event_waitTimeout(&usb_event_done[ep_num >> 1], timeout))
+ return 0;
return tx_size;
}
if ((setup_packet.mRequestType & USB_RECIP_MASK) ==
USB_RECIP_DEVICE)
usb_get_descriptor();
- /* Getting descriptor for a device is a standard request */
- else if ((setup_packet.mRequestType & USB_DIR_MASK) == USB_DIR_IN)
- usb_event_handler(usb_dev);
else
- ep_cnfg[CTRL_ENP_OUT].status = STALLED;
+ usb_event_handler(usb_dev);
}
/* USB setup packet: SET_ADDRESS handler */
udc.alt[i] = 0;
usb_set_device_state(USB_STATE_CONFIGURED);
usb_dev->configured = true;
+ event_do(&usb_event_done[0]);
LOG_INFO("%s: device configured\n", __func__);
}
else
interrupt.status = usb->ISTR;
interrupt.status &= usb->CNTR | 0x1f;
+ /* Set the context as atomic */
+ in_atomic = true;
+
if (interrupt.PMAOVR)
{
LOG_WARN("%s: DMA overrun / underrun\n", __func__);
{
usb_isr_correct_transfer(interrupt);
}
+ in_atomic = false;
}
/* USB: hardware initialization */
MOD_CHECK(proc);
#endif
usb_dev = dev;
+ usb_dev->configured = false;
+
+ event_initGeneric(&usb_event_done[0]);
usb_init();
- while (!usb_dev->configured)
- cpu_relax();
+ event_wait(&usb_event_done[0]);
+
return 0;
}