180 lines
5.9 KiB
C
180 lines
5.9 KiB
C
#include "cdc.h"
|
|
|
|
#include <memory.h>
|
|
|
|
#include "../usb_device_types.h"
|
|
|
|
#include "../usb.h"
|
|
|
|
#include <blocking_io/blocking_fifo.h>
|
|
|
|
#include <cmsis_gcc.h>
|
|
#include <string.h>
|
|
|
|
// state
|
|
static USB_CdcState cdcs = {0};
|
|
static uint8_t tx_buffer[USB_CDC_PCKT_BUFSIZE];
|
|
|
|
#define USB_CDC_FIFO_MEM_SIZE (3072) // FIXME: ez vagy a blocking FIFO bugos
|
|
|
|
static uint8_t fifo_mem[USB_CDC_FIFO_MEM_SIZE];
|
|
static BFifo fifo;
|
|
|
|
void usb_cdc_read_callback(const uint8_t *data, uint32_t size);
|
|
|
|
void usb_cdc_init(const USB_CdcAssignments *as) {
|
|
// clear the structure
|
|
memset(&cdcs, 0, sizeof(USB_CdcState));
|
|
|
|
// set Control Line State to something invalid
|
|
cdcs.control_line_state.D = USB_CDC_INVALID_CONTROL_LINE_STATE;
|
|
|
|
// fill-in default values
|
|
// USB_Cdc_LineCodingStruct *lc = &cdcs.line_coding;
|
|
// lc->dwDTERate = USB_CDC_DEFAULT_BITRATE;
|
|
// lc->bCharFormat = USB_CDC_DEFAULT_CHAR_FORMAT;
|
|
// lc->bParityType = USB_CDC_DEFAULT_PARITY_TYPE;
|
|
// lc->bDataBits = USB_CDC_DEFAULT_DATA_BITS;
|
|
|
|
// fill-in assigments
|
|
cdcs.ep_assignments = *as;
|
|
|
|
// initialize buffer
|
|
bfifo_create(&fifo, fifo_mem, USB_CDC_FIFO_MEM_SIZE);
|
|
|
|
// initialize an all-0 interrupt
|
|
cdcs.interrupt_data = 0;
|
|
cdcs.interrupt_pending = true;
|
|
|
|
// communication parameters have not been set
|
|
cdcs.commInit = false;
|
|
|
|
// from now on CDC module is considered initialized
|
|
cdcs.moduleInit = true;
|
|
}
|
|
|
|
// static uint8_t replyBuf[sizeof(USB_Cdc_LineCodingStruct)];
|
|
|
|
// static void usbcore_setup_cplt_cb(uint8_t in) {
|
|
// usbcore_wake_up_endpoint(cdcs.ep_assignments.data_ep, USB_IN);
|
|
// }
|
|
|
|
static void usb_cdc_review_comm_init() {
|
|
// check if line coding was initialized
|
|
USB_Cdc_LineCodingStruct *lc = &cdcs.line_coding;
|
|
bool lcOk = (lc->dwDTERate != 0) && (lc->bDataBits != 0);
|
|
|
|
// check if control line state was initialized
|
|
bool clsOk = cdcs.control_line_state.D != USB_CDC_INVALID_CONTROL_LINE_STATE;
|
|
|
|
// combine the above criteria
|
|
cdcs.commInit = lcOk && clsOk;
|
|
|
|
// // wake up endpoint if initialized
|
|
// if (cdcs.commInit) {
|
|
// usbcore_register_IN_callback(0, usbcore_setup_cplt_cb);
|
|
// }
|
|
}
|
|
|
|
int usb_cdc_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_CDC_SET_LINE_CODING: // set line coding
|
|
memcpy(&cdcs.line_coding, cbevt->data, sizeof(USB_Cdc_LineCodingStruct));
|
|
usb_cdc_review_comm_init(); // review the communcation initialization state
|
|
// MSG("%u\n", cdcs.line_coding.dwDTERate);
|
|
ret = 0;
|
|
break;
|
|
case USB_CDC_GET_LINE_CODING: // get line coding
|
|
cbevt->reply_data = (const uint8_t *)&cdcs.line_coding; // expert move: pass the pointer, no copying
|
|
cbevt->reply_size = sizeof(USB_Cdc_LineCodingStruct);
|
|
ret = 0;
|
|
break;
|
|
case USB_CDC_SEND_BREAK: // send break
|
|
// do nothing
|
|
ret = 0;
|
|
break;
|
|
case USB_CDC_SET_CONTROL_LINE_STATE: // set control line state
|
|
cdcs.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
|
|
// MSG("%u\n", cdcs.control_line_state.D);
|
|
ret = 0;
|
|
break;
|
|
default:
|
|
cbevt->reply_valid = false; // this event is not processed by or not related to the CDC ACM module
|
|
break;
|
|
}
|
|
|
|
// send a ZLP reply
|
|
if (ret != -1) {
|
|
cbevt->reply_data = NULL;
|
|
cbevt->reply_size = 0;
|
|
cbevt->reply_valid = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case USB_CBEVT_OUT: {
|
|
if (cbevt->ep == cdcs.ep_assignments.data_ep) {
|
|
// MSG("%c\n", cbevt->data[0]);
|
|
ret = 0;
|
|
|
|
usb_cdc_read_callback(cbevt->data, cbevt->size);
|
|
// usbcore_schedule_transmission(cdcs.ep_assignments.data_ep, cbevt->data, cbevt->size); // echo
|
|
}
|
|
break;
|
|
}
|
|
|
|
case USB_CBEVT_IN: {
|
|
if (cbevt->ep == cdcs.ep_assignments.control_ep) { // if notification feeding is requested
|
|
if (cdcs.interrupt_pending) {
|
|
usbcore_schedule_transmission(cdcs.ep_assignments.control_ep, (const uint8_t *)&(cdcs.interrupt_data), sizeof(uint16_t)); // send ZLP
|
|
cdcs.interrupt_pending = false;
|
|
}
|
|
ret = 0;
|
|
} else if (cbevt->ep == cdcs.ep_assignments.data_ep) { // if data are requested
|
|
// usbcore_schedule_transmission(cdcs.ep_assignments.data_ep, NULL, 0); // send ZLP
|
|
ret = 0;
|
|
|
|
// read from the fifo
|
|
if (cdcs.commInit) {
|
|
uint32_t readSize = bfifo_read(&fifo, tx_buffer, USB_CDC_PCKT_BUFSIZE);
|
|
if (readSize > 0) {
|
|
uint32_t writeSize = usbcore_schedule_transmission(cdcs.ep_assignments.data_ep, tx_buffer, readSize); // write data acquired from the buffer
|
|
bfifo_pop(&fifo, writeSize, 0); // pop with no blocking
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// void usb_event_callback(USB_CallbackEvent *cbevt) {
|
|
// usb_cdc_process_and_return(cbevt);
|
|
// }
|
|
|
|
void usb_cdc_write(const uint8_t *data, uint32_t size) {
|
|
if (cdcs.moduleInit) {
|
|
bfifo_push_all(&fifo, data, size);
|
|
usbcore_wake_up_endpoint(cdcs.ep_assignments.data_ep, USB_IN);
|
|
}
|
|
}
|
|
|
|
__attribute__((weak)) void usb_cdc_read_callback(const uint8_t *data, uint32_t size) {
|
|
(void)data;
|
|
(void)size;
|
|
return;
|
|
} |