Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/drivers/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ void serialEndWrite(serialPort_t *instance)
bool serialIsConnected(const serialPort_t *instance)
{
if (instance->vTable->isConnected)
instance->vTable->isConnected(instance);
return(instance->vTable->isConnected(instance));

// If API is not defined - assume connected
return true;
Expand Down
22 changes: 20 additions & 2 deletions src/main/drivers/serial_usb_vcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ USBD_HandleTypeDef USBD_Device;

static vcpPort_t vcpPort;

// Track DTR (Data Terminal Ready) state - indicates if host has COM port open
// Default to true - assume connected until host explicitly clears DTR
static volatile bool cdcPortOpened = true;

static void cdcCtrlLineStateCallback(void *context, uint16_t ctrlLineState)
{
UNUSED(context);
// DTR is bit 0 of control line state
cdcPortOpened = (ctrlLineState & 0x01) != 0;
}

static void usbVcpSetBaudRate(serialPort_t *instance, uint32_t baudRate)
{
UNUSED(instance);
Expand Down Expand Up @@ -103,7 +114,8 @@ static uint8_t usbVcpRead(serialPort_t *instance)
static bool usbVcpIsConnected(const serialPort_t *instance)
{
(void)instance;
return usbIsConnected() && usbIsConfigured();
// Check USB hardware state AND whether host has opened the COM port (DTR)
return usbIsConnected() && usbIsConfigured() && cdcPortOpened;
}

static void usbVcpWriteBuf(serialPort_t *instance, const void *data, int count)
Expand Down Expand Up @@ -209,6 +221,9 @@ void usbVcpInitHardware(void)
IOInit(IOGetByTag(IO_TAG(PA11)), OWNER_USB, RESOURCE_INPUT, 0);
IOInit(IOGetByTag(IO_TAG(PA12)), OWNER_USB, RESOURCE_OUTPUT, 0);
USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_CDC_cb, &USR_cb);

// Register callback for DTR state changes
CDC_SetCtrlLineStateCb(cdcCtrlLineStateCallback, NULL);
#elif defined(STM32F7) || defined(STM32H7)
usbGenerateDisconnectPulse();

Expand All @@ -225,7 +240,10 @@ void usbVcpInitHardware(void)

/* Start Device Process */
USBD_Start(&USBD_Device);


// Register callback for DTR state changes
CDC_SetCtrlLineStateCb(cdcCtrlLineStateCallback, NULL);

#ifdef STM32H7
HAL_PWREx_EnableUSBVoltageDetector();
delay(100); // Cold boot failures observed without this, even when USB cable is not connected
Expand Down
13 changes: 12 additions & 1 deletion src/main/vcp_hal/usbd_cdc_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,8 @@ uint32_t CDC_Send_FreeBytes(void)
* @param sendLength: Number of data to be sent (in bytes)
* @retval Bytes sent
*/
#define VCP_WRITE_TIMEOUT_MS 50

uint32_t CDC_Send_DATA(const uint8_t *ptrBuffer, uint32_t sendLength)
{
#if defined(STM32H7)
Expand All @@ -423,13 +425,22 @@ uint32_t CDC_Send_DATA(const uint8_t *ptrBuffer, uint32_t sendLength)
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)USBD_Device.pCDC_ClassData;
#endif

while (hcdc->TxState != 0);
uint32_t start = millis();

while (hcdc->TxState != 0) {
if (millis() - start > VCP_WRITE_TIMEOUT_MS) {
return 0;
}
}

for (uint32_t i = 0; i < sendLength; i++)
{
UserTxBuffer[UserTxBufPtrIn] = ptrBuffer[i];
UserTxBufPtrIn = (UserTxBufPtrIn + 1) % APP_TX_DATA_SIZE;
while (CDC_Send_FreeBytes() == 0) {
if (millis() - start > VCP_WRITE_TIMEOUT_MS) {
return i; // Return partial count
}
delay(1);
}
}
Expand Down
28 changes: 25 additions & 3 deletions src/main/vcpf4/usbd_cdc_vcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,24 @@ static uint16_t VCP_Ctrl(uint32_t Cmd, uint8_t* Buf, uint32_t Len)
*******************************************************************************/
uint32_t CDC_Send_DATA(const uint8_t *ptrBuffer, uint32_t sendLength)
{
VCP_DataTx(ptrBuffer, sendLength);
if (VCP_DataTx(ptrBuffer, sendLength) != USBD_OK) {
return 0;
}
return sendLength;
}

uint32_t CDC_Send_FreeBytes(void)
{
return APP_RX_DATA_SIZE - CDC_Receive_BytesAvailable();
// Calculate free space in APP_Rx_Buffer (outbound to host)
// Using correct circular buffer math with volatile pointers
uint32_t ptr_in = APP_Rx_ptr_in;
uint32_t ptr_out = APP_Rx_ptr_out;

if (ptr_out > ptr_in) {
return ptr_out - ptr_in - 1;
} else {
return APP_RX_DATA_SIZE + ptr_out - ptr_in - 1;
}
}

/**
Expand All @@ -194,18 +205,29 @@ uint32_t CDC_Send_FreeBytes(void)
* @param Len: Number of data to be sent (in bytes)
* @retval Result of the operation: USBD_OK if all operations are OK else VCP_FAIL
*/
#define VCP_WRITE_TIMEOUT_MS 50

static uint16_t VCP_DataTx(const uint8_t* Buf, uint32_t Len)
{
uint32_t start = millis();

/*
make sure that any paragraph end frame is not in play
could just check for: USB_CDC_ZLP, but better to be safe
and wait for any existing transmission to complete.
*/
while (USB_Tx_State != 0);
while (USB_Tx_State != 0) {
if (millis() - start > VCP_WRITE_TIMEOUT_MS) {
return USBD_FAIL;
}
}

for (uint32_t i = 0; i < Len; i++) {
// Stall if the ring buffer is full
while (((APP_Rx_ptr_in + 1) % APP_RX_DATA_SIZE) == APP_Rx_ptr_out) {
if (millis() - start > VCP_WRITE_TIMEOUT_MS) {
return USBD_FAIL;
}
delay(1);
}

Expand Down
9 changes: 7 additions & 2 deletions src/main/vcpf4/usbd_usr.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "usbd_usr.h"
#include "usbd_ioreq.h"
#include "usbd_cdc_vcp.h"

USBD_Usr_cb_TypeDef USR_cb =
{
Expand Down Expand Up @@ -102,13 +103,17 @@ void USBD_USR_DeviceDisconnected (void)

/**
* @brief USBD_USR_DeviceSuspended
* Displays the message on LCD on device suspend Event
* Handle device suspend event - treat as disconnect for safety.
* This helps on boards without VBUS sensing where hardware
* disconnect detection may not work.
* @param None
* @retval None
*/
void USBD_USR_DeviceSuspended(void)
{
/* Users can do their application actions here for the USB-Reset */
// Treat suspend as disconnect - prevents blocking on USB writes
// when cable is unplugged on boards without VBUS sensing
bDeviceState = UNCONNECTED;
}


Expand Down