Skip to content

Commit

Permalink
Get Windows to use WinUSB driver for device
Browse files Browse the repository at this point in the history
Combines usb_stm32.c and usb_stm32_serial.c and ties together
previous changes by providing enough functionality to get
Windows to automatically select the WinUSB driver.
  • Loading branch information
nkarstens committed Oct 26, 2023
1 parent fb45d7b commit 4f36857
Show file tree
Hide file tree
Showing 2 changed files with 228 additions and 267 deletions.
230 changes: 228 additions & 2 deletions lib/pbio/drv/usb/usb_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
#include <stdbool.h>

#include <contiki.h>
#include <contiki-lib.h>
#include <stm32f4xx_hal.h>
#include <stm32f4xx_hal_pcd_ex.h>
#include <usbd_cdc.h>
#include <usbd_core.h>

#include <pbdrv/usb.h>
Expand All @@ -22,8 +24,30 @@

PROCESS(pbdrv_usb_process, "USB");

static uint8_t usb_in_buf[CDC_DATA_FS_MAX_PACKET_SIZE];
static uint8_t usb_out_buf[CDC_DATA_FS_MAX_PACKET_SIZE - 1];
static volatile bool usb_in_busy;
static volatile bool usb_out_busy;

static volatile bool usb_connected;

// size must be power of 2 for ringbuf! also can't be > 255!
static uint8_t stdout_data[128];
static uint8_t stdin_data[128];
static struct ringbuf stdout_buf;
static struct ringbuf stdin_buf;

static USBD_CDC_LineCodingTypeDef LineCoding = {
.bitrate = 115200,
.format = CDC_STOP_BITS_1,
.paritytype = CDC_PARITY_NONE,
.datatype = 8,
};

static USBD_HandleTypeDef husbd;
static PCD_HandleTypeDef hpcd;
extern USBD_DescriptorsTypeDef VCP_Desc;

static volatile bool vbus_active;
static pbdrv_usb_bcd_t pbdrv_usb_bcd;

Expand Down Expand Up @@ -121,14 +145,146 @@ void pbdrv_usb_stm32_handle_vbus_irq(bool active) {
process_poll(&pbdrv_usb_process);
}

/**
* @brief CDC_Itf_Init
* Initializes the CDC media low layer
* @param None
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CDC_Itf_Init(void) {
ringbuf_init(&stdin_buf, stdin_data, (uint8_t)PBIO_ARRAY_SIZE(stdin_data));
ringbuf_init(&stdout_buf, stdout_data, (uint8_t)PBIO_ARRAY_SIZE(stdout_data));
USBD_CDC_SetTxBuffer(&husbd, usb_out_buf, 0);
USBD_CDC_SetRxBuffer(&husbd, usb_in_buf);
usb_in_busy = false;
usb_out_busy = false;
usb_connected = false;

return USBD_OK;
}

/**
* @brief CDC_Itf_DeInit
* DeInitializes the CDC media low layer
* @param None
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CDC_Itf_DeInit(void) {
usb_connected = false;
return USBD_OK;
}

/**
* @brief CDC_Itf_Control
* Manage the CDC class requests
* @param Cmd: Command code
* @param Buf: Buffer containing command data (request parameters)
* @param Len: Number of data to be sent (in bytes)
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CDC_Itf_Control(uint8_t cmd, uint8_t *pbuf, uint16_t length) {
switch (cmd) {
case CDC_SEND_ENCAPSULATED_COMMAND:
break;

case CDC_GET_ENCAPSULATED_RESPONSE:
break;

case CDC_SET_COMM_FEATURE:
break;

case CDC_GET_COMM_FEATURE:
break;

case CDC_CLEAR_COMM_FEATURE:
break;

case CDC_SET_LINE_CODING:
LineCoding.bitrate = pbuf[0] | (pbuf[1] << 8) | (pbuf[2] << 16) | (pbuf[3] << 24);
LineCoding.format = pbuf[4];
LineCoding.paritytype = pbuf[5];
LineCoding.datatype = pbuf[6];
break;

case CDC_GET_LINE_CODING:
pbuf[0] = (uint8_t)(LineCoding.bitrate);
pbuf[1] = (uint8_t)(LineCoding.bitrate >> 8);
pbuf[2] = (uint8_t)(LineCoding.bitrate >> 16);
pbuf[3] = (uint8_t)(LineCoding.bitrate >> 24);
pbuf[4] = LineCoding.format;
pbuf[5] = LineCoding.paritytype;
pbuf[6] = LineCoding.datatype;
break;

case CDC_SET_CONTROL_LINE_STATE: {
USBD_SetupReqTypedef *req = (void *)pbuf;
// REVISIT: MicroPython defers the connection state here to allow
// some time to disable local echo on the remote terminal
usb_connected = !!(req->wValue & CDC_CONTROL_LINE_DTR);
break;
}
case CDC_SEND_BREAK:
break;
}

return USBD_OK;
}

/**
* @brief CDC_Itf_DataRx
* Data received over USB OUT endpoint are sent over CDC interface
* through this function.
* @param Buf: Buffer of data to be transmitted
* @param Len: Number of data received (in bytes)
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CDC_Itf_Receive(uint8_t *Buf, uint32_t *Len) {
for (uint32_t i = 0; i < *Len; i++) {
ringbuf_put(&stdin_buf, Buf[i]);
}
usb_in_busy = false;
// process_poll(&pbdrv_usb_process);
return USBD_OK;
}

/**
* @brief CDC_Itf_TransmitCplt
* Data transmitted callback
*
* @note
* This function is IN transfer complete callback used to inform user that
* the submitted Data is successfully sent over USB.
*
* @param Buf: Buffer of data to be received
* @param Len: Number of data received (in bytes)
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CDC_Itf_TransmitCplt(uint8_t *Buf, uint32_t *Len, uint8_t epnum) {
usb_out_busy = false;
// process_poll(&pbdrv_usb_process);
return USBD_OK;
}

static USBD_CDC_ItfTypeDef USBD_CDC_fops = {
.Init = CDC_Itf_Init,
.DeInit = CDC_Itf_DeInit,
.Control = CDC_Itf_Control,
.Receive = CDC_Itf_Receive,
.TransmitCplt = CDC_Itf_TransmitCplt,
};

// Common USB driver implementation.

void pbdrv_usb_init(void) {
// Link the driver data structures
husbd.pData = &hpcd;
hpcd.pData = &husbd;

USBD_Init(&husbd, NULL, 0);
USBD_Init(&husbd, &VCP_Desc, 0);
USBD_RegisterClass(&husbd, USBD_CDC_CLASS);
USBD_CDC_RegisterInterface(&husbd, &USBD_CDC_fops);
USBD_Start(&husbd);

process_start(&pbdrv_usb_process);

// VBUS may already be active
Expand All @@ -139,13 +295,75 @@ pbdrv_usb_bcd_t pbdrv_usb_get_bcd(void) {
return pbdrv_usb_bcd;
}

static void pbdrv_usb_serial_transmit(void) {
static uint32_t tx_size = 0;

if (usb_out_busy) {
return;
}

// If tx_size > 0 it means we have a pending retry, otherwise we get as
// much as we can from the stdout buffer.
if (tx_size == 0) {
tx_size = ringbuf_elements(&stdout_buf);
if (tx_size > PBIO_ARRAY_SIZE(usb_out_buf)) {
tx_size = PBIO_ARRAY_SIZE(usb_out_buf);
}
if (tx_size > 0) {
for (uint32_t i = 0; i < tx_size; i++) {
usb_out_buf[i] = ringbuf_get(&stdout_buf);
}
}
}

if (tx_size > 0) {
USBD_CDC_SetTxBuffer(&husbd, usb_out_buf, tx_size);
if (USBD_CDC_TransmitPacket(&husbd) == USBD_OK) {
usb_out_busy = true;
tx_size = 0;
}
}
}

static void pbdrv_usb_serial_receive(void) {
if (usb_in_busy) {
return;
}

if (USBD_CDC_ReceivePacket(&husbd) == USBD_OK) {
usb_in_busy = true;
}
}

// TODO: need to multiplex stdin/stdout with Bluetooth or remove USB serial

pbio_error_t pbsys_usb_put_char(uint8_t c) {
if (!usb_connected) {
// don't lock up print() when USB not connected - data is discarded
return PBIO_SUCCESS;
}
if (ringbuf_put(&stdout_buf, c) == 0) {
return PBIO_SUCCESS;
}
return PBIO_SUCCESS;
}

pbio_error_t pbsys_usb_get_char(uint8_t *c) {
if (ringbuf_elements(&stdin_buf) == 0) {
return PBIO_ERROR_AGAIN;
}
*c = ringbuf_get(&stdin_buf);
return PBIO_SUCCESS;
}

// Event loop

PROCESS_THREAD(pbdrv_usb_process, ev, data) {
static struct pt bcd_pt;
static PBIO_ONESHOT(no_vbus_oneshot);
static PBIO_ONESHOT(bcd_oneshot);
static bool bcd_busy;
static struct etimer timer;

PROCESS_POLLHANDLER({
if (!bcd_busy && pbio_oneshot(!vbus_active, &no_vbus_oneshot)) {
Expand All @@ -164,12 +382,20 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {

PROCESS_BEGIN();

etimer_set(&timer, 5);

for (;;) {
PROCESS_WAIT_EVENT();
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_POLL || (ev == PROCESS_EVENT_TIMER && etimer_expired(&timer)));

if (bcd_busy) {
bcd_busy = PT_SCHEDULE(pbdrv_usb_stm32_bcd_detect(&bcd_pt));
}

if (etimer_expired(&timer)) {
etimer_reset(&timer);
pbdrv_usb_serial_transmit();
pbdrv_usb_serial_receive();
}
}

PROCESS_END();
Expand Down
Loading

0 comments on commit 4f36857

Please sign in to comment.