/** ****************************************************************************** * @file cdc.c * @copyright AndrĂ¡s Wiesner, 2024-\showdate "%Y" * @brief CDC ACM implementation module for the flatUSB project. ****************************************************************************** */ #include "acm.h" #include #include #include "../usb.h" #include "../usb_device_types.h" #include // ------------------------- 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 // ------------------------- 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; acms.interrupt_pending = true; // communication parameters have not been set acms.commInit = false; // from now on CDC module is considered initialized acms.moduleInit = true; } 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; } 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.control_ep) { // if notification feeding is requested if (acms.interrupt_pending) { usbcore_schedule_transmission(acms.ep_assignments.control_ep, (const uint8_t *)&(acms.interrupt_data), sizeof(uint16_t)); // send ZLP acms.interrupt_pending = false; } ret = 0; } else if (cbevt->ep == acms.ep_assignments.data_ep) { // if data are requested ret = 0; // read from the fifo if (acms.commInit) { uint32_t readSize = bfifo_read(&fifo, tx_buffer, USB_ACM_PCKT_BUFSIZE); 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 } } } } default: break; } return ret; } void usb_acm_write(const uint8_t *data, uint32_t size) { if (acms.moduleInit) { bfifo_push_all(&fifo, data, size); usbcore_wake_up_endpoint(acms.ep_assignments.data_ep, USB_IN); } } /** * @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; }