Epagris c0ea9c3ab3 - CH32F207 port fixed
- ACM stack size fixed
2025-03-07 16:34:25 +01:00

198 lines
6.9 KiB
C

/**
******************************************************************************
* @file cdc.c
* @copyright András Wiesner, 2024-\showdate "%Y"
* @brief CDC ACM implementation module for the flatUSB project.
******************************************************************************
*/
#include "acm.h"
#include <memory.h>
#include <stdint.h>
#include <string.h>
#include "../usb.h"
#include "../usb_device_types.h"
#include "cmsis_os2.h"
#include <blocking_io/blocking_fifo.h>
#include <strings.h>
// -------------------------
static Usb_AcmState acms = {0}; ///< ACM module state
static uint8_t tx_buffer[USB_ACM_PCKT_BUFSIZE]; ///< Transmit buffer
static uint8_t fifo_mem[USB_ACM_FIFO_MEM_SIZE]; ///< Memory assigned to the TX BFifo
static BFifo fifo; ///< TX Blocking FIFO
static osThreadId_t th; ///< ACM thread
static osEventFlagsId_t flags; ///< Event flags
#define USB_ACM_DATA_IN_DONE (0x01) ///< IN transfer done flag
#define USB_ACM_COMM_INIT (0x02) ///< Communication has been initialized
#define USB_ACM_HOSTBOUND_DATA_AVAIL (0x04) ///< Hostbound data is available
#define USB_ACM_LOOP_TIMEOUT_TICKS (100) ///< Main loop timeout for interrupt status transmission
#define USB_ACM_INITIAL_DELAY_TICKS (100) ///< Delay before sending data
// -------------------------
static void thread_usb_acm(void *arg) {
osEventFlagsWait(flags, USB_ACM_COMM_INIT, 0, osWaitForever); // wait for communication to become initialized
osEventFlagsSet(flags, USB_ACM_DATA_IN_DONE); // assume we can write to the data endpoint
osDelay(USB_ACM_INITIAL_DELAY_TICKS); // inject some initial delay
while (true) {
uint32_t signals = osEventFlagsWait(flags, USB_ACM_HOSTBOUND_DATA_AVAIL, osFlagsWaitAny, USB_ACM_LOOP_TIMEOUT_TICKS);
if (signals != osErrorTimeout) { // check timeout
if (signals & USB_ACM_HOSTBOUND_DATA_AVAIL) { // data hostbound available
do {
osEventFlagsWait(flags, USB_ACM_DATA_IN_DONE, osFlagsWaitAny, 1); // wait for the IN DONE flag
uint32_t readSize = bfifo_read(&fifo, tx_buffer, USB_ACM_PCKT_BUFSIZE); // read from the fifo
if (readSize > 0) {
uint32_t writeSize = usbcore_schedule_transmission(acms.ep_assignments.data_ep, tx_buffer, readSize); // write data acquired from the buffer
bfifo_pop(&fifo, writeSize, 0); // pop with no blocking
}
} while (bfifo_get_used(&fifo) > 0);
}
} else { // timeout
// send an all-zero interrupt
usbcore_schedule_transmission(acms.ep_assignments.control_ep, (const uint8_t *)&(acms.interrupt_data), sizeof(uint16_t));
}
}
}
// -------------------------
void usb_acm_read_callback(const uint8_t *data, uint32_t size);
void usb_acm_init(const Usb_Acm_EpAssignments *as) {
// clear the structure
memset(&acms, 0, sizeof(Usb_AcmState));
// set Control Line State to something invalid
acms.control_line_state.D = USB_ACM_INVALID_CONTROL_LINE_STATE;
// fill-in assigments
acms.ep_assignments = *as;
// initialize buffer
bfifo_create(&fifo, fifo_mem, USB_ACM_FIFO_MEM_SIZE);
// initialize an all-0 interrupt
acms.interrupt_data = 0;
// communication parameters have not been set
acms.commInit = false;
// from now on CDC module is considered initialized
acms.moduleInit = true;
// create flags
flags = osEventFlagsNew(NULL);
// create thread
osThreadAttr_t attr;
memset(&attr, 0, sizeof(osThreadAttr_t));
attr.stack_size = 512;
attr.name = "acm";
th = osThreadNew(thread_usb_acm, NULL, &attr);
}
static void usb_cdc_review_comm_init() {
// check if line coding was initialized
Usb_Acm_LineCodingStruct *lc = &acms.line_coding;
bool lcOk = (lc->dwDTERate != 0) && (lc->bDataBits != 0);
// check if control line state was initialized
bool clsOk = acms.control_line_state.D != USB_ACM_INVALID_CONTROL_LINE_STATE;
// combine the above criteria
acms.commInit = lcOk && clsOk;
// signal the processing thread
osEventFlagsSet(flags, USB_ACM_COMM_INIT);
}
int usb_acm_process_and_return(Usb_CallbackEvent *cbevt) {
int ret = -1;
switch (cbevt->type) {
case USB_CBEVT_UNKNOWN_REQ: {
// initialize reply
cbevt->reply_data = NULL;
cbevt->reply_size = 0;
cbevt->reply_valid = true;
switch (cbevt->setup_request->bRequest) {
case USB_ACM_SET_LINE_CODING: // set line coding
memcpy(&acms.line_coding, cbevt->data, sizeof(Usb_Acm_LineCodingStruct));
usb_cdc_review_comm_init(); // review the communcation initialization state
ret = 0;
break;
case USB_ACM_GET_LINE_CODING: // get line coding
cbevt->reply_data = (const uint8_t *)&acms.line_coding; // expert move: pass the pointer, no copying
cbevt->reply_size = sizeof(Usb_Acm_LineCodingStruct);
ret = 0;
break;
case USB_ACM_SEND_BREAK: // send break
// do nothing
ret = 0;
break;
case USB_ACM_SET_CONTROL_LINE_STATE: // set control line state
acms.control_line_state.D = cbevt->setup_request->wValue; // control line state is carried in wValue of the SETUP request
usb_cdc_review_comm_init(); // review the communcation initialization state
ret = 0;
break;
default:
cbevt->reply_valid = false; // this event is not processed by or not related to the CDC ACM module
break;
}
break;
}
case USB_CBEVT_OUT: {
if (cbevt->ep == acms.ep_assignments.data_ep) {
ret = 0;
usb_acm_read_callback(cbevt->data, cbevt->size);
}
break;
}
case USB_CBEVT_IN: {
if (cbevt->ep == acms.ep_assignments.data_ep) {
osEventFlagsSet(flags, USB_ACM_DATA_IN_DONE);
}
ret = 0;
break;
}
default:
break;
}
return ret;
}
void usb_acm_write(const uint8_t *data, uint32_t size) {
if (acms.moduleInit) {
bfifo_push_all(&fifo, data, size);
osEventFlagsSet(flags, USB_ACM_HOSTBOUND_DATA_AVAIL);
}
}
/**
* @fn void usb_acm_read_callback(const uint8_t *data, uint32_t size)
* Callback function prototype for data reception. This function is
* expected to be overridden by the application.
*
* @param data ingress data
* @param size length of the data
*/
__attribute__((weak)) void usb_acm_read_callback(const uint8_t *data, uint32_t size) {
(void)data;
(void)size;
return;
}