sam3 port: add serial module.
[bertos.git] / bertos / cpu / cortex-m3 / drv / usb_stm32.c
index 752b8e2d35868d5ff845944690ecaeabac87c8c1..879b2d6f0a828bd5724fe8dd6839e9c6fbc2cf35 100644 (file)
 #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))
+
 /* STM32 USB registers */
 struct stm32_usb
 {
@@ -84,7 +88,8 @@ struct stm32_usb
 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 =
@@ -140,8 +145,20 @@ static UsbDevice *usb_dev;
 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 */
+/// \cond
+/* XXX: use the empty cond section to silent a buggy doxygen warning */
+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);
+/// \endcond
+
+static Event usb_event_done[EP_MAX_SLOTS];
 
 /* Allocate a free block of the packet memory */
 static stm32_UsbMemSlot *usb_malloc(void)
@@ -194,13 +211,13 @@ static bool usb_alloc_buffer(uint16_t *pOffset, uint32_t *size,
                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)
@@ -671,12 +688,18 @@ out:                                                                      \
 static stm32_UsbIoStatus
 __usb_ep_read(int ep, void *buffer, ssize_t size, void (*complete)(int))
 {
-       if (UNLIKELY(ep >= ENP_MAX_NUMB))
+       if (UNLIKELY((ep >= EP_MAX_NUM) || (ep & 0x01)))
        {
+               LOG_ERR("%s: invalid EP number %d\n", __func__, ep);
+               ASSERT(0);
+               return STALLED;
+       }
+       if (UNLIKELY((size_t)buffer & 0x03))
+       {
+               LOG_ERR("%s: unaligned buffer @ %p\n", __func__, buffer);
                ASSERT(0);
                return STALLED;
        }
-       ASSERT(!(ep & 0x01));
        return USB_EP_IO(ep, read, buffer, size, complete);
 }
 
@@ -684,12 +707,18 @@ __usb_ep_read(int ep, void *buffer, ssize_t size, void (*complete)(int))
 static stm32_UsbIoStatus
 __usb_ep_write(int ep, const void *buffer, ssize_t size, void (*complete)(int))
 {
-       if (UNLIKELY(ep >= ENP_MAX_NUMB))
+       if (UNLIKELY((ep >= EP_MAX_NUM) || !(ep & 0x01)))
        {
+               LOG_ERR("%s: invalid EP number %d\n", __func__, ep);
+               ASSERT(0);
+               return STALLED;
+       }
+       if (UNLIKELY((size_t)buffer & 0x03))
+       {
+               LOG_ERR("%s: unaligned buffer @ %p\n", __func__, buffer);
                ASSERT(0);
                return STALLED;
        }
-       ASSERT(ep & 0x01);
        return USB_EP_IO(ep, write, buffer, size, complete);
 }
 
@@ -1076,104 +1105,110 @@ static void usb_status_handler(UNUSED_ARG(int, EP))
        }
 }
 
-static bool rx_done;
-static size_t rx_size;
-
 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)
 {
        int ep_num = usb_ep_logical_to_hw(ep);
-
-       if (UNLIKELY((size_t)buffer & 0x03))
-       {
-               LOG_ERR("unaligned buffer @ %p\n", buffer);
-               ASSERT(0);
-       }
+       ssize_t max_size = sizeof(ep_buffer[ep_num]);
 
        /* Non-blocking read for EP0 */
        if (ep_num == CTRL_ENP_OUT)
        {
                size = usb_size(size, usb_le16_to_cpu(setup_packet.wLength));
+               if (UNLIKELY(size > max_size))
+               {
+                       LOG_ERR("%s: ep_buffer exceeded, try to enlarge CONFIG_USB_BUFSIZE\n",
+                                       __func__);
+                       ASSERT(0);
+                       return -USB_BUF_OVERFLOW;
+               }
                if (!size)
                        usb_status_handler(ep_num);
                else
-                       __usb_ep_read(ep_num, buffer, size,
+               {
+                       __usb_ep_read(ep_num, ep_buffer[ep_num], size,
                                        usb_status_handler);
+                       memcpy(buffer, ep_buffer[ep_num], size);
+               }
                return size;
        }
        if (UNLIKELY(!size))
                return 0;
-       size = MIN(size, USB_RX_MAX_SIZE);
-       rx_done = false;
+       size = MIN(size, max_size);
+       event_initGeneric(&usb_event_done[ep_num >> 1]);
        rx_size = 0;
 
        /* Blocking read */
-       __usb_ep_read(ep_num, buffer, size, usb_endpointRead_complete);
-       while (!rx_done)
-               cpu_relax();
+       __usb_ep_read(ep_num, ep_buffer[ep_num], size,
+                               usb_endpointRead_complete);
+       event_wait(&usb_event_done[ep_num >> 1]);
+       memcpy(buffer, ep_buffer[ep_num], rx_size);
 
        return rx_size;
 }
 
-static bool tx_done;
-static size_t tx_size;
-
 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)
 {
        int ep_num = usb_ep_logical_to_hw(ep);
-
-       if (UNLIKELY((size_t)buffer & 0x03))
-       {
-               LOG_ERR("unaligned buffer @ %p\n", buffer);
-               ASSERT(0);
-       }
+       ssize_t max_size = sizeof(ep_buffer[ep_num]);
 
        /* Non-blocking write for EP0 */
        if (ep_num == CTRL_ENP_IN)
        {
                size = usb_size(size, usb_le16_to_cpu(setup_packet.wLength));
+               if (UNLIKELY(size > max_size))
+               {
+                       LOG_ERR("%s: ep_buffer exceeded, try to enlarge CONFIG_USB_BUFSIZE\n",
+                                       __func__);
+                       ASSERT(0);
+                       return -USB_BUF_OVERFLOW;
+               }
                if (!size)
                        usb_status_handler(ep_num);
                else
-                       __usb_ep_write(ep_num, 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, USB_TX_MAX_SIZE);
-       tx_done = false;
+       size = MIN(size, max_size);
+       event_initGeneric(&usb_event_done[ep_num >> 1]);
        tx_size = 0;
 
        /* Blocking write */
-       __usb_ep_write(ep_num, 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);
+       event_wait(&usb_event_done[ep_num >> 1]);
 
        return tx_size;
 }
@@ -1545,6 +1580,8 @@ static int usb_set_config_state(uint32_t conf)
                        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
        {
@@ -1880,8 +1917,11 @@ int usb_deviceRegister(UsbDevice *dev)
        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;
 }