Skip to content

Commit b23d599

Browse files
Loic Poulaingalak
authored andcommitted
usb: stm32: Introduce transfer method
Transfer is an important concept of USB specification. During a transfer, several packets can be transmitted. Today the usb API only provides ep_read and ep_write functions but the transfer concept is missing. This is typically ok for basic drivers which don't have to take care of how data is transfered/splitted. However there are some drivers like CDC EEM, in which transfer concept is important for packet management. Moreover, current ep-write and ep_read method have a different implementation in usb_dc_dw and usb_dc_stm32 device drivers. For example usb_dc_dw supports only 1-data-packet transfers due to its implementation. This can increase latency and reduce performance. I think this is something we need to fix/improve by implementing better transfer management. This patch introduces usb_dc_ ep_transfer method which can be used to configure IN/OUT transfers. This allows to configure and request different transfer sizes and should prevent usage of the current stm32 temporary buffer. This method has asynchronous and synchronous mode. Synchronous mode waits for transfer completion before returning. Asynchronouse mode (irq safe) configures the transfer and returns immediately, the provided callback will be then called on transfer completion. This also update ep_write and ep_read stm32 implementation to use this new method but keep their behavior unchanged for legacy reasons. Note that for now this method is local to stm32 device driver, however the goal would be to expose this function as a new USB device driver API method so that class drivers use it. This will request same implementation in the usb_dc_dw_driver. Signed-off-by: Loic Poulain <[email protected]>
1 parent cd931bb commit b23d599

File tree

1 file changed

+138
-47
lines changed

1 file changed

+138
-47
lines changed

drivers/usb/device/usb_dc_stm32.c

Lines changed: 138 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@
7575
#define EP_IS_IN(ep) (((ep) & USB_EP_DIR_MASK) == USB_EP_DIR_IN)
7676
#define EP_IS_OUT(ep) (((ep) & USB_EP_DIR_MASK) == USB_EP_DIR_OUT)
7777

78+
/* Transfer completion callback */
79+
typedef void (*usb_dc_transfer_callback)(u8_t ep, int status, size_t tsize);
80+
7881
/* Endpoint state */
7982
struct usb_dc_stm32_ep_state {
8083
u16_t ep_mps; /** Endpoint max packet size */
@@ -83,7 +86,11 @@ struct usb_dc_stm32_ep_state {
8386
u8_t ep_stalled; /** Endpoint stall flag */
8487
u32_t read_count; /** Number of bytes in read buffer */
8588
u32_t read_offset; /** Current offset in read buffer */
86-
struct k_sem write_sem; /** Write boolean semaphore */
89+
u8_t *transfer_buf; /** IN/OUT transfer buffer */
90+
u32_t transfer_size; /** number of bytes processed by the tranfer */
91+
int transfer_result; /** Transfer result */
92+
usb_dc_transfer_callback transfer_cb; /** Transfer callback */
93+
struct k_sem transfer_sem; /** transfer boolean semaphore */
8794
};
8895

8996
/* Driver state */
@@ -172,7 +179,10 @@ static int usb_dc_stm32_init(void)
172179
for (i = 0; i < CONFIG_USB_DC_STM32_EP_NUM; i++) {
173180
HAL_PCDEx_SetTxFiFo(&usb_dc_stm32_state.pcd, i,
174181
FIFO_EP_WORDS);
175-
k_sem_init(&usb_dc_stm32_state.in_ep_state[i].write_sem, 1, 1);
182+
k_sem_init(&usb_dc_stm32_state.in_ep_state[i].transfer_sem, 1,
183+
1);
184+
k_sem_init(&usb_dc_stm32_state.out_ep_state[i].transfer_sem, 1,
185+
1);
176186
}
177187

178188
IRQ_CONNECT(STM32F4_IRQ_OTG_FS, CONFIG_USB_DC_STM32_IRQ_PRI,
@@ -243,10 +253,105 @@ int usb_dc_set_address(const u8_t addr)
243253
return 0;
244254
}
245255

246-
int usb_dc_ep_start_read(u8_t ep, u8_t *data, u32_t max_data_len)
256+
static int usb_dc_ep_transfer(const u8_t ep, u8_t *buf, size_t dlen, bool is_in,
257+
usb_dc_transfer_callback cb)
247258
{
259+
struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
248260
HAL_StatusTypeDef status;
261+
int ret = 0;
262+
263+
SYS_LOG_DBG("ep 0x%02x, len=%d, in=%d, sync=%s", ep, dlen, is_in,
264+
cb ? "no" : "yes");
265+
266+
if (!dlen && !is_in) {
267+
HAL_PCD_EP_Receive(&usb_dc_stm32_state.pcd, ep, NULL, 0);
268+
return 0;
269+
}
270+
271+
/* Transfer Already Ongoing ? */
272+
if (k_sem_take(&ep_state->transfer_sem, K_NO_WAIT)) {
273+
return -EBUSY;
274+
}
275+
276+
ep_state->transfer_buf = buf;
277+
ep_state->transfer_result = -EBUSY;
278+
ep_state->transfer_size = dlen;
279+
ep_state->transfer_cb = cb;
280+
281+
if (!k_is_in_isr()) {
282+
irq_disable(STM32F4_IRQ_OTG_FS);
283+
}
284+
285+
/* Configure and start transfer */
286+
if (is_in) { /* DEV to HOST */
287+
status = HAL_PCD_EP_Transmit(&usb_dc_stm32_state.pcd, ep,
288+
ep_state->transfer_buf, dlen);
289+
} else { /* HOST TO DEV */
290+
status = HAL_PCD_EP_Receive(&usb_dc_stm32_state.pcd, ep,
291+
ep_state->transfer_buf, dlen);
292+
}
293+
294+
if (status != HAL_OK) {
295+
SYS_LOG_ERR("ep 0x%02x, transfer error %d", ep, ret);
296+
ep_state->transfer_buf = NULL;
297+
ret = -EIO;
298+
}
299+
300+
if (!k_is_in_isr()) {
301+
irq_enable(STM32F4_IRQ_OTG_FS);
302+
}
303+
304+
if (ep_state->transfer_cb || ret) { /* asynchronous transfer or error */
305+
return ret;
306+
}
307+
308+
/* Synchronous transfer */
309+
if (k_sem_take(&ep_state->transfer_sem, K_FOREVER)) {
310+
SYS_LOG_ERR("ep 0x%02x, transfer error", ep);
311+
ep_state->transfer_buf = NULL;
312+
return -ETIMEDOUT;
313+
}
314+
315+
if (ep_state->transfer_result) { /* error < 0 */
316+
ret = ep_state->transfer_result;
317+
} else { /* synchronous transfer success, return processed bytes */
318+
ret = ep_state->transfer_size;
319+
}
320+
321+
k_sem_give(&ep_state->transfer_sem);
322+
323+
return ret;
324+
}
325+
326+
static void __legacy_out_cb(u8_t ep, int status, size_t tsize)
327+
{
328+
struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
329+
330+
ARG_UNUSED(status);
331+
332+
/* Transfer completed, data is stored in our legacy endpoint buffer */
333+
ep_state->read_count = tsize;
334+
ep_state->read_offset = 0;
335+
336+
if (ep_state->cb) {
337+
ep_state->cb(ep, USB_DC_EP_DATA_OUT);
338+
}
339+
}
249340

341+
static void __legacy_in_cb(u8_t ep, int status, size_t tsize)
342+
{
343+
struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
344+
345+
ARG_UNUSED(status);
346+
ARG_UNUSED(tsize);
347+
348+
if (ep_state->cb) {
349+
ep_state->cb(ep, USB_DC_EP_DATA_IN);
350+
}
351+
}
352+
353+
int usb_dc_ep_start_read(u8_t ep, u8_t *data, u32_t max_data_len)
354+
{
250355
SYS_LOG_DBG("ep 0x%02x, len %u", ep, max_data_len);
251356

252357
/* we flush EP0_IN by doing a 0 length receive on it */
@@ -259,16 +364,9 @@ int usb_dc_ep_start_read(u8_t ep, u8_t *data, u32_t max_data_len)
259364
max_data_len = USB_OTG_FS_MAX_PACKET_SIZE;
260365
}
261366

262-
status = HAL_PCD_EP_Receive(&usb_dc_stm32_state.pcd, ep,
263-
usb_dc_stm32_state.ep_buf[EP_IDX(ep)],
264-
max_data_len);
265-
if (status != HAL_OK) {
266-
SYS_LOG_ERR("HAL_PCD_EP_Receive failed(0x%02x), %d",
267-
ep, (int)status);
268-
return -EIO;
269-
}
270-
271-
return 0;
367+
/* asynchronous out transfer to keep legacy behaviour */
368+
return usb_dc_ep_transfer(ep, data, max_data_len, false,
369+
__legacy_out_cb);
272370
}
273371

274372
int usb_dc_ep_get_read_count(u8_t ep, u32_t *read_bytes)
@@ -440,8 +538,6 @@ int usb_dc_ep_disable(const u8_t ep)
440538
int usb_dc_ep_write(const u8_t ep, const u8_t *const data,
441539
const u32_t data_len, u32_t * const ret_bytes)
442540
{
443-
struct usb_dc_stm32_ep_state *ep_state = usb_dc_stm32_get_ep_state(ep);
444-
HAL_StatusTypeDef status;
445541
int ret = 0;
446542

447543
SYS_LOG_DBG("ep 0x%02x, len %u", ep, data_len);
@@ -451,24 +547,14 @@ int usb_dc_ep_write(const u8_t ep, const u8_t *const data,
451547
return -EINVAL;
452548
}
453549

454-
ret = k_sem_take(&ep_state->write_sem, 1000);
455-
if (ret) {
456-
SYS_LOG_ERR("Unable to write ep 0x%02x (%d)", ep, ret);
457-
return ret;
458-
}
459-
460-
if (!k_is_in_isr()) {
461-
irq_disable(STM32F4_IRQ_OTG_FS);
462-
}
463-
464-
status = HAL_PCD_EP_Transmit(&usb_dc_stm32_state.pcd, ep,
465-
(void *)data, data_len);
466-
if (status != HAL_OK) {
467-
SYS_LOG_ERR("HAL_PCD_EP_Transmit failed(0x%02x), %d",
468-
ep, (int)status);
469-
k_sem_give(&ep_state->write_sem);
470-
ret = -EIO;
471-
}
550+
do {
551+
/* For now we want to preserve legacy ep_write behavior.
552+
* If ep transfer fails due to ongoing transfer, try again.
553+
*/
554+
ret = usb_dc_ep_transfer(ep, (u8_t *)data, data_len, true,
555+
__legacy_in_cb);
556+
k_yield();
557+
} while (ret == -EBUSY);
472558

473559
if (!ret && ep == EP0_IN) {
474560
/* Wait for an empty package as from the host.
@@ -477,10 +563,6 @@ int usb_dc_ep_write(const u8_t ep, const u8_t *const data,
477563
usb_dc_ep_start_read(ep, NULL, 0);
478564
}
479565

480-
if (!k_is_in_isr()) {
481-
irq_enable(STM32F4_IRQ_OTG_FS);
482-
}
483-
484566
if (ret_bytes) {
485567
*ret_bytes = data_len;
486568
}
@@ -639,14 +721,17 @@ void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, u8_t epnum)
639721
SYS_LOG_DBG("epnum 0x%02x, rx_count %u", epnum,
640722
HAL_PCD_EP_GetRxCount(&usb_dc_stm32_state.pcd, epnum));
641723

642-
/* Transaction complete, data is now stored in the buffer and ready
643-
* for the upper stack (usb_dc_ep_read to retrieve).
644-
*/
645-
usb_dc_ep_get_read_count(ep, &ep_state->read_count);
646-
ep_state->read_offset = 0;
724+
if (!ep_state->transfer_buf) { /* ignore if no transfer buffer */
725+
return;
726+
}
647727

648-
if (ep_state->cb) {
649-
ep_state->cb(ep, USB_DC_EP_DATA_OUT);
728+
ep_state->transfer_buf = NULL;
729+
ep_state->transfer_result = 0;
730+
usb_dc_ep_get_read_count(ep, &ep_state->transfer_size);
731+
k_sem_give(&ep_state->transfer_sem);
732+
733+
if (ep_state->transfer_cb) {
734+
ep_state->transfer_cb(ep, 0, ep_state->transfer_size);
650735
}
651736
}
652737

@@ -658,9 +743,15 @@ void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, u8_t epnum)
658743

659744
SYS_LOG_DBG("epnum 0x%02x", epnum);
660745

661-
k_sem_give(&ep_state->write_sem);
746+
if (!ep_state->transfer_buf) { /* ignore if no transfer buffer */
747+
return;
748+
}
662749

663-
if (ep_state->cb) {
664-
ep_state->cb(ep, USB_DC_EP_DATA_IN);
750+
ep_state->transfer_buf = NULL;
751+
ep_state->transfer_result = 0;
752+
k_sem_give(&ep_state->transfer_sem);
753+
754+
if (ep_state->transfer_cb) {
755+
ep_state->transfer_cb(ep, 0, ep_state->transfer_size);
665756
}
666757
}

0 commit comments

Comments
 (0)