@@ -158,6 +158,8 @@ struct udc_stm32_data {
158158 const struct device * dev ;
159159 uint32_t irq ;
160160 uint32_t occupied_mem ;
161+ /* wLength of SETUP packet for s-out-status */
162+ uint32_t ep0_out_wlength ;
161163 void (* pcd_prepare )(const struct device * dev );
162164 int (* clk_enable )(void );
163165 int (* clk_disable )(void );
@@ -275,20 +277,50 @@ void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
275277 udc_submit_sof_event (priv -> dev );
276278}
277279
278- static int usbd_ctrl_feed_dout (const struct device * dev , const size_t length )
280+ /*
281+ * Prepare OUT EP0 for reception.
282+ *
283+ * @param dev USB controller
284+ * @param length wLength from SETUP packet for s-out-status
285+ * 0 for s-in-status ZLP
286+ */
287+ static int udc_stm32_prep_out_ep0_rx (const struct device * dev , const size_t length )
279288{
280289 struct udc_stm32_data * priv = udc_get_private (dev );
281290 struct udc_ep_config * cfg = udc_get_ep_cfg (dev , USB_CONTROL_EP_OUT );
282291 struct net_buf * buf ;
292+ uint32_t buf_size ;
293+
294+ udc_ep_set_busy (cfg , true);
295+
296+ /*
297+ * Make sure OUT EP0 can receive bMaxPacketSize0 bytes
298+ * from each Data packet by rounding up allocation size
299+ * even if "device behaviour is undefined if the host
300+ * should send more data than specified in wLength"
301+ * according to the USB Specification.
302+ *
303+ * Note that ROUND_UP() will return 0 for ZLP.
304+ */
305+ buf_size = ROUND_UP (length , UDC_STM32_EP0_MAX_PACKET_SIZE );
283306
284- buf = udc_ctrl_alloc (dev , USB_CONTROL_EP_OUT , length );
307+ buf = udc_ctrl_alloc (dev , USB_CONTROL_EP_OUT , buf_size );
285308 if (buf == NULL ) {
286309 return - ENOMEM ;
287310 }
288311
289312 k_fifo_put (& cfg -> fifo , buf );
290313
291- HAL_PCD_EP_Receive (& priv -> pcd , cfg -> addr , buf -> data , buf -> size );
314+ /*
315+ * Keep track of how much data we're expecting from
316+ * host so we know when the transfer is complete.
317+ * Unlike other endpoints, this bookkeeping isn't
318+ * done by the HAL for OUT EP0.
319+ */
320+ priv -> ep0_out_wlength = length ;
321+
322+ /* Don't try to receive more than bMaxPacketSize0 */
323+ HAL_PCD_EP_Receive (& priv -> pcd , cfg -> addr , net_buf_tail (buf ), UDC_STM32_EP0_MAX_PACKET_SIZE );
292324
293325 return 0 ;
294326}
@@ -339,7 +371,7 @@ static int udc_stm32_tx(const struct device *dev, struct udc_ep_config *epcfg,
339371 if (DT_HAS_COMPAT_STATUS_OKAY (st_stm32_usb )) {
340372 udc_stm32_flush_tx_fifo (dev );
341373 } else {
342- usbd_ctrl_feed_dout (dev , 0 );
374+ udc_stm32_prep_out_ep0_rx (dev , 0 );
343375 }
344376 }
345377
@@ -352,6 +384,9 @@ static int udc_stm32_rx(const struct device *dev, struct udc_ep_config *epcfg,
352384 struct udc_stm32_data * priv = udc_get_private (dev );
353385 HAL_StatusTypeDef status ;
354386
387+ /* OUT EP0 requires special logic! */
388+ __ASSERT_NO_MSG (epcfg -> addr != USB_CONTROL_EP_OUT );
389+
355390 LOG_DBG ("RX ep 0x%02x len %u" , epcfg -> addr , buf -> size );
356391
357392 if (udc_ep_is_busy (epcfg )) {
@@ -411,33 +446,72 @@ static void handle_msg_data_out(struct udc_stm32_data *priv, uint8_t epnum, uint
411446 LOG_DBG ("DataOut ep 0x%02x" , ep );
412447
413448 epcfg = udc_get_ep_cfg (dev , ep );
414- udc_ep_set_busy (epcfg , false);
415449
416- buf = udc_buf_get (epcfg );
450+ buf = udc_buf_peek (epcfg );
417451 if (unlikely (buf == NULL )) {
418452 LOG_ERR ("ep 0x%02x queue is empty" , ep );
453+ udc_ep_set_busy (epcfg , false);
419454 return ;
420455 }
421456
457+ /* HAL copies data - we just need to update bookkeeping */
422458 net_buf_add (buf , rx_count );
423459
424460 if (ep == USB_CONTROL_EP_OUT ) {
461+ /*
462+ * OUT EP0 is used for two purposes:
463+ * - receive 'out' Data packets during s-(out)-status
464+ * - receive Status OUT ZLP during s-in-(status)
465+ */
425466 if (udc_ctrl_stage_is_status_out (dev )) {
467+ /* s-in-status completed */
468+ __ASSERT_NO_MSG (rx_count == 0 );
426469 udc_ctrl_update_stage (dev , buf );
427470 udc_ctrl_submit_status (dev , buf );
428471 } else {
429- udc_ctrl_update_stage (dev , buf );
430- }
472+ /* Verify that host did not send more data than it promised */
473+ __ASSERT (buf -> len <= priv -> ep0_out_wlength ,
474+ "Received more data from Host than expected!" );
475+
476+ /* Check if the data stage is complete */
477+ if (buf -> len < priv -> ep0_out_wlength ) {
478+ /* Not yet - prepare to receive more data and wait */
479+ HAL_PCD_EP_Receive (& priv -> pcd , epcfg -> addr , net_buf_tail (buf ),
480+ UDC_STM32_EP0_MAX_PACKET_SIZE );
481+ return ;
482+ } /* else: buf->len == priv->ep0_out_wlength */
431483
432- if (udc_ctrl_stage_is_status_in (dev )) {
484+ /*
485+ * Data stage is complete: update to next step
486+ * which should be Status IN, then submit the
487+ * Setup+Data phase buffers to UDC stack and
488+ * let it handle the next stage.
489+ */
490+ udc_ctrl_update_stage (dev , buf );
491+ __ASSERT_NO_MSG (udc_ctrl_stage_is_status_in (dev ));
433492 udc_ctrl_submit_s_out_status (dev , buf );
434493 }
435494 } else {
436495 udc_submit_ep_event (dev , buf , 0 );
437496 }
438497
498+ /* Buffer was filled and submitted - remove it from queue */
499+ (void )udc_buf_get (epcfg );
500+
501+ /* Endpoint is no longer busy */
502+ udc_ep_set_busy (epcfg , false);
503+
504+ /* Prepare next transfer for EP if its queue is not empty */
439505 buf = udc_buf_peek (epcfg );
440506 if (buf ) {
507+ /*
508+ * Only the driver is allowed to queue transfers on OUT EP0,
509+ * and it should only be doing so once per Control transfer.
510+ * If it has a queued transfer, something must be wrong.
511+ */
512+ __ASSERT (epcfg -> addr != USB_CONTROL_EP_OUT ,
513+ "OUT EP0 should never have pending transfers!" );
514+
441515 udc_stm32_rx (dev , epcfg , buf );
442516 }
443517}
@@ -548,7 +622,7 @@ static void handle_msg_setup(struct udc_stm32_data *priv)
548622
549623 if (udc_ctrl_stage_is_data_out (dev )) {
550624 /* Allocate and feed buffer for data OUT stage */
551- err = usbd_ctrl_feed_dout (dev , udc_data_stage_length (buf ));
625+ err = udc_stm32_prep_out_ep0_rx (dev , udc_data_stage_length (buf ));
552626 if (err == - ENOMEM ) {
553627 udc_submit_ep_event (dev , buf , err );
554628 }
0 commit comments