Skip to content

Commit cd7b8c2

Browse files
dldntjr407claude
andcommitted
fix(cdc): stop arming OUT endpoint with a NULL buffer
USBD_CDC_Receive() falls back to USBD_CDC_ClearBuffer() when the receive queue has no room left for another 64 byte block. That helper re-arms the OUT endpoint with a NULL application buffer: USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, 0, 0); The endpoint is valid again while ep->xfer_buff is NULL. The next OUT packet the host sends reaches HAL_PCD_IRQHandler(), which copies the packet without checking the destination: if (count != 0U) { USB_ReadPMA(hpcd->Instance, ep->xfer_buff, ep->pmaadress, count); } The packet lands at address 0 and the write faults. On a STM32F103C8 this is an imprecise bus fault escalated to a hard fault (CFSR IMPRECISERR, HFSR FORCED), and the board stops answering until reset. Do not arm the endpoint when there is no room. An unarmed bulk OUT endpoint NAKs, the host retries, and USBSerial::read() and the readBytes() family already call CDC_resume_receive() after dequeuing, so the endpoint is armed with a real block as soon as the sketch drains. That is the flow control this transfer needs. Fixes #1399 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 4a8b28a commit cd7b8c2

1 file changed

Lines changed: 9 additions & 4 deletions

File tree

‎libraries/USBDevice/src/cdc/usbd_cdc_if.c‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,15 @@ static int8_t USBD_CDC_Receive(uint8_t *Buf, uint32_t *Len)
240240
/* It always contains required amount of free space for writing */
241241
CDC_ReceiveQueue_CommitBlock(&ReceiveQueue, (uint16_t)(*Len));
242242
receivePended = false;
243-
/* If enough space in the queue for a full buffer then continue receive */
244-
if (!CDC_resume_receive()) {
245-
USBD_CDC_ClearBuffer(&hUSBD_Device_CDC);
246-
}
243+
/*
244+
* If there is enough space in the queue for a full packet, continue receive.
245+
* Else leave the OUT endpoint unarmed: it then NAKs and the host retries.
246+
* USBSerial::read() and the readBytes() family call CDC_resume_receive()
247+
* after dequeuing, so the endpoint is armed again with a valid block as soon
248+
* as the sketch drains data. Arming it with a NULL buffer instead makes the
249+
* low level driver copy the retried packet to address 0.
250+
*/
251+
(void)CDC_resume_receive();
247252
return ((int8_t)USBD_OK);
248253
}
249254

0 commit comments

Comments
 (0)