qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

usb_main.c (20035B)


      1 // Copyright 2023 Stefan Kerkmann
      2 // Copyright 2020-2021 Ryan (@fauxpark)
      3 // Copyright 2020 Nick Brassel (@tzarc)
      4 // Copyright 2020 a-chol
      5 // Copyright 2020 xyzz
      6 // Copyright 2020 Joel Challis (@zvecr)
      7 // Copyright 2020 George (@goshdarnharris)
      8 // Copyright 2018 James Laird-Wah
      9 // Copyright 2018 Drashna Jaelre (@drashna)
     10 // Copyright 2016 Fredizzimo
     11 // Copyright 2016 Giovanni Di Sirio
     12 // SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0
     13 
     14 #include <ch.h>
     15 #include <hal.h>
     16 #include <string.h>
     17 
     18 #include "usb_main.h"
     19 #include "usb_report_handling.h"
     20 
     21 #include "host.h"
     22 #include "suspend.h"
     23 #include "timer.h"
     24 #include "wait.h"
     25 #include "usb_endpoints.h"
     26 #include "usb_device_state.h"
     27 #include "usb_descriptor.h"
     28 #include "usb_driver.h"
     29 #include "usb_types.h"
     30 
     31 #ifdef RAW_ENABLE
     32 #    include "raw_hid.h"
     33 #endif
     34 
     35 #ifdef NKRO_ENABLE
     36 #    include "keycode_config.h"
     37 
     38 extern keymap_config_t keymap_config;
     39 #endif
     40 
     41 /* ---------------------------------------------------------
     42  *       Global interface variables and declarations
     43  * ---------------------------------------------------------
     44  */
     45 
     46 #ifndef usb_lld_connect_bus
     47 #    define usb_lld_connect_bus(usbp)
     48 #endif
     49 
     50 #ifndef usb_lld_disconnect_bus
     51 #    define usb_lld_disconnect_bus(usbp)
     52 #endif
     53 
     54 extern usb_endpoint_in_t  usb_endpoints_in[USB_ENDPOINT_IN_COUNT];
     55 extern usb_endpoint_out_t usb_endpoints_out[USB_ENDPOINT_OUT_COUNT];
     56 
     57 static bool __attribute__((__unused__)) send_report_buffered(usb_endpoint_in_lut_t endpoint, void *report, size_t size);
     58 static void __attribute__((__unused__)) flush_report_buffered(usb_endpoint_in_lut_t endpoint, bool padded);
     59 static bool __attribute__((__unused__)) receive_report(usb_endpoint_out_lut_t endpoint, void *report, size_t size);
     60 
     61 /* ---------------------------------------------------------
     62  *            Descriptors and USB driver objects
     63  * ---------------------------------------------------------
     64  */
     65 
     66 /* USB Low Level driver specific endpoint fields */
     67 #if !defined(usb_lld_endpoint_fields)
     68 #    define usb_lld_endpoint_fields   \
     69         2,        /* IN multiplier */ \
     70             NULL, /* SETUP buffer (not a SETUP endpoint) */
     71 #endif
     72 
     73 /*
     74  * Handles the GET_DESCRIPTOR callback
     75  *
     76  * Returns the proper descriptor
     77  */
     78 static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) {
     79     usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;
     80 
     81     static USBDescriptor descriptor;
     82     descriptor.ud_string = NULL;
     83     descriptor.ud_size   = get_usb_descriptor(setup->wValue.word, setup->wIndex, setup->wLength, (const void **const)&descriptor.ud_string);
     84 
     85     if (descriptor.ud_string == NULL) {
     86         return NULL;
     87     }
     88 
     89     return &descriptor;
     90 }
     91 
     92 /* ---------------------------------------------------------
     93  *                  USB driver functions
     94  * ---------------------------------------------------------
     95  */
     96 
     97 #define USB_EVENT_QUEUE_SIZE 16
     98 usbevent_t event_queue[USB_EVENT_QUEUE_SIZE];
     99 uint8_t    event_queue_head;
    100 uint8_t    event_queue_tail;
    101 
    102 void usb_event_queue_init(void) {
    103     // Initialise the event queue
    104     memset(&event_queue, 0, sizeof(event_queue));
    105     event_queue_head = 0;
    106     event_queue_tail = 0;
    107 }
    108 
    109 static inline bool usb_event_queue_enqueue(usbevent_t event) {
    110     uint8_t next = (event_queue_head + 1) % USB_EVENT_QUEUE_SIZE;
    111     if (next == event_queue_tail) {
    112         return false;
    113     }
    114     event_queue[event_queue_head] = event;
    115     event_queue_head              = next;
    116     return true;
    117 }
    118 
    119 static inline bool usb_event_queue_dequeue(usbevent_t *event) {
    120     if (event_queue_head == event_queue_tail) {
    121         return false;
    122     }
    123     *event           = event_queue[event_queue_tail];
    124     event_queue_tail = (event_queue_tail + 1) % USB_EVENT_QUEUE_SIZE;
    125     return true;
    126 }
    127 
    128 static inline void usb_event_suspend_handler(void) {
    129     usb_device_state_set_suspend(USB_DRIVER.configuration != 0, USB_DRIVER.configuration);
    130 }
    131 
    132 static inline void usb_event_wakeup_handler(void) {
    133     suspend_wakeup_init();
    134     usb_device_state_set_resume(USB_DRIVER.configuration != 0, USB_DRIVER.configuration);
    135 }
    136 
    137 bool last_suspend_state = false;
    138 
    139 void usb_event_queue_task(void) {
    140     usbevent_t event;
    141     while (usb_event_queue_dequeue(&event)) {
    142         switch (event) {
    143             case USB_EVENT_SUSPEND:
    144                 last_suspend_state = true;
    145                 usb_event_suspend_handler();
    146                 break;
    147             case USB_EVENT_WAKEUP:
    148                 last_suspend_state = false;
    149                 usb_event_wakeup_handler();
    150                 break;
    151             case USB_EVENT_CONFIGURED:
    152                 usb_device_state_set_configuration(USB_DRIVER.configuration != 0, USB_DRIVER.configuration);
    153                 break;
    154             case USB_EVENT_UNCONFIGURED:
    155                 usb_device_state_set_configuration(false, 0);
    156                 break;
    157             case USB_EVENT_RESET:
    158                 usb_device_state_set_reset();
    159                 usb_device_state_set_protocol(USB_PROTOCOL_REPORT);
    160                 break;
    161             default:
    162                 // Nothing to do, we don't handle it.
    163                 break;
    164         }
    165     }
    166 }
    167 
    168 /* Handles the USB driver global events. */
    169 static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
    170     switch (event) {
    171         case USB_EVENT_ADDRESS:
    172             return;
    173 
    174         case USB_EVENT_CONFIGURED:
    175             osalSysLockFromISR();
    176             for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
    177                 usb_endpoint_in_configure_cb(&usb_endpoints_in[i]);
    178             }
    179             for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
    180                 usb_endpoint_out_configure_cb(&usb_endpoints_out[i]);
    181             }
    182             osalSysUnlockFromISR();
    183             if (last_suspend_state) {
    184                 usb_event_queue_enqueue(USB_EVENT_WAKEUP);
    185             }
    186             usb_event_queue_enqueue(USB_EVENT_CONFIGURED);
    187             return;
    188         case USB_EVENT_SUSPEND:
    189             /* Falls into.*/
    190         case USB_EVENT_UNCONFIGURED:
    191             /* Falls into.*/
    192         case USB_EVENT_RESET:
    193             usb_event_queue_enqueue(event);
    194             chSysLockFromISR();
    195             for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
    196                 usb_endpoint_in_suspend_cb(&usb_endpoints_in[i]);
    197             }
    198             for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
    199                 usb_endpoint_out_suspend_cb(&usb_endpoints_out[i]);
    200             }
    201             chSysUnlockFromISR();
    202             return;
    203 
    204         case USB_EVENT_WAKEUP:
    205             chSysLockFromISR();
    206             for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
    207                 usb_endpoint_in_wakeup_cb(&usb_endpoints_in[i]);
    208             }
    209             for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
    210                 usb_endpoint_out_wakeup_cb(&usb_endpoints_out[i]);
    211             }
    212             chSysUnlockFromISR();
    213             usb_event_queue_enqueue(USB_EVENT_WAKEUP);
    214             return;
    215 
    216         case USB_EVENT_STALLED:
    217             return;
    218     }
    219 }
    220 
    221 /*
    222  * Appendix G: HID Request Support Requirements
    223  *
    224  * The following table enumerates the requests that need to be supported by various types of HID class devices.
    225  * Device type     GetReport   SetReport   GetIdle     SetIdle     GetProtocol SetProtocol
    226  * ------------------------------------------------------------------------------------------
    227  * Boot Mouse      Required    Optional    Optional    Optional    Required    Required
    228  * Non-Boot Mouse  Required    Optional    Optional    Optional    Optional    Optional
    229  * Boot Keyboard   Required    Optional    Required    Required    Required    Required
    230  * Non-Boot Keybrd Required    Optional    Required    Required    Optional    Optional
    231  * Other Device    Required    Optional    Optional    Optional    Optional    Optional
    232  */
    233 
    234 static uint8_t _Alignas(4) set_report_buf[2];
    235 
    236 static void set_led_transfer_cb(USBDriver *usbp) {
    237     usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;
    238 
    239     if (setup->wLength == 2) {
    240         uint8_t report_id = set_report_buf[0];
    241         if ((report_id == REPORT_ID_KEYBOARD) || (report_id == REPORT_ID_NKRO)) {
    242             usb_device_state_set_leds(set_report_buf[1]);
    243         }
    244     } else {
    245         usb_device_state_set_leds(set_report_buf[0]);
    246     }
    247 }
    248 
    249 static bool usb_requests_hook_cb(USBDriver *usbp) {
    250     usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;
    251 
    252     /* Handle HID class specific requests */
    253     if ((setup->bmRequestType & (USB_RTYPE_TYPE_MASK | USB_RTYPE_RECIPIENT_MASK)) == (USB_RTYPE_TYPE_CLASS | USB_RTYPE_RECIPIENT_INTERFACE)) {
    254         switch (setup->bmRequestType & USB_RTYPE_DIR_MASK) {
    255             case USB_RTYPE_DIR_DEV2HOST:
    256                 switch (setup->bRequest) {
    257                     case HID_REQ_GetReport:
    258                         return usb_get_report_cb(usbp);
    259                     case HID_REQ_GetProtocol:
    260                         if (setup->wIndex == KEYBOARD_INTERFACE) {
    261                             static uint8_t keyboard_protocol;
    262                             keyboard_protocol = usb_device_state_get_protocol();
    263                             usbSetupTransfer(usbp, &keyboard_protocol, sizeof(keyboard_protocol), NULL);
    264                             return true;
    265                         }
    266                         break;
    267 
    268                     case HID_REQ_GetIdle:
    269                         return usb_get_idle_cb(usbp);
    270                 }
    271 
    272             case USB_RTYPE_DIR_HOST2DEV:
    273                 switch (setup->bRequest) {
    274                     case HID_REQ_SetReport:
    275                         switch (setup->wIndex) {
    276                             case KEYBOARD_INTERFACE:
    277 #if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)
    278                             case SHARED_INTERFACE:
    279 #endif
    280                                 usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb);
    281                                 return true;
    282                         }
    283                         break;
    284                     case HID_REQ_SetProtocol:
    285                         if (setup->wIndex == KEYBOARD_INTERFACE) {
    286                             usb_device_state_set_protocol(setup->wValue.lbyte);
    287                         }
    288                         usbSetupTransfer(usbp, NULL, 0, NULL);
    289                         return true;
    290                     case HID_REQ_SetIdle:
    291                         usb_device_state_set_idle_rate(setup->wValue.hbyte);
    292                         return usb_set_idle_cb(usbp);
    293                 }
    294                 break;
    295         }
    296     }
    297 
    298     /* Handle the Get_Descriptor Request for HID class, which is not handled by
    299      * the ChibiOS USB driver */
    300     if (((setup->bmRequestType & (USB_RTYPE_DIR_MASK | USB_RTYPE_RECIPIENT_MASK)) == (USB_RTYPE_DIR_DEV2HOST | USB_RTYPE_RECIPIENT_INTERFACE)) && (setup->bRequest == USB_REQ_GET_DESCRIPTOR)) {
    301         const USBDescriptor *descriptor = usbp->config->get_descriptor_cb(usbp, setup->wValue.lbyte, setup->wValue.hbyte, setup->wIndex);
    302         if (descriptor == NULL) {
    303             return false;
    304         }
    305         usbSetupTransfer(usbp, (uint8_t *)descriptor->ud_string, descriptor->ud_size, NULL);
    306         return true;
    307     }
    308 
    309     for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
    310         if (usb_endpoints_in[i].usb_requests_cb != NULL) {
    311             if (usb_endpoints_in[i].usb_requests_cb(usbp)) {
    312                 return true;
    313             }
    314         }
    315     }
    316 
    317     return false;
    318 }
    319 
    320 static const USBConfig usbcfg = {
    321     usb_event_cb,          /* USB events callback */
    322     usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */
    323     usb_requests_hook_cb,  /* Requests hook callback */
    324 };
    325 
    326 void init_usb_driver(USBDriver *usbp) {
    327     for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
    328         usb_endpoint_in_init(&usb_endpoints_in[i]);
    329         usb_endpoint_in_start(&usb_endpoints_in[i]);
    330     }
    331 
    332     for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
    333         usb_endpoint_out_init(&usb_endpoints_out[i]);
    334         usb_endpoint_out_start(&usb_endpoints_out[i]);
    335     }
    336 
    337     /*
    338      * Activates the USB driver and then the USB bus pull-up on D+.
    339      * Note, a delay is inserted in order to not have to disconnect the cable
    340      * after a reset.
    341      */
    342     usbDisconnectBus(usbp);
    343     usbStop(usbp);
    344     wait_ms(50);
    345     usbStart(usbp, &usbcfg);
    346     usbConnectBus(usbp);
    347 }
    348 
    349 __attribute__((weak)) void restart_usb_driver(USBDriver *usbp) {
    350     usbDisconnectBus(usbp);
    351     usbStop(usbp);
    352 
    353     for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
    354         usb_endpoint_in_stop(&usb_endpoints_in[i]);
    355     }
    356 
    357     for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
    358         usb_endpoint_out_stop(&usb_endpoints_out[i]);
    359     }
    360 
    361     wait_ms(50);
    362 
    363     for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
    364         usb_endpoint_in_init(&usb_endpoints_in[i]);
    365         usb_endpoint_in_start(&usb_endpoints_in[i]);
    366     }
    367 
    368     for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
    369         usb_endpoint_out_init(&usb_endpoints_out[i]);
    370         usb_endpoint_out_start(&usb_endpoints_out[i]);
    371     }
    372 
    373     usbStart(usbp, &usbcfg);
    374     usbConnectBus(usbp);
    375 }
    376 
    377 /* ---------------------------------------------------------
    378  *                  Keyboard functions
    379  * ---------------------------------------------------------
    380  */
    381 
    382 /**
    383  * @brief Send a report to the host, the report is enqueued into an output
    384  * queue and send once the USB endpoint becomes empty.
    385  *
    386  * @param endpoint USB IN endpoint to send the report from
    387  * @param report pointer to the report
    388  * @param size size of the report
    389  * @return true Success
    390  * @return false Failure
    391  */
    392 bool send_report(usb_endpoint_in_lut_t endpoint, void *report, size_t size) {
    393     return usb_endpoint_in_send(&usb_endpoints_in[endpoint], (uint8_t *)report, size, TIME_MS2I(100), false);
    394 }
    395 
    396 /**
    397  * @brief Send a report to the host, but delay the sending until the size of
    398  * endpoint report is reached or the incompletely filled buffer is flushed with
    399  * a call to `flush_report_buffered`. This is useful if the report is being
    400  * updated frequently. The complete report is then enqueued into an output
    401  * queue and send once the USB endpoint becomes empty.
    402  *
    403  * @param endpoint USB IN endpoint to send the report from
    404  * @param report pointer to the report
    405  * @param size size of the report
    406  * @return true Success
    407  * @return false Failure
    408  */
    409 static bool send_report_buffered(usb_endpoint_in_lut_t endpoint, void *report, size_t size) {
    410     return usb_endpoint_in_send(&usb_endpoints_in[endpoint], (uint8_t *)report, size, TIME_MS2I(100), true);
    411 }
    412 
    413 /** @brief Flush all buffered reports which were enqueued with a call to
    414  * `send_report_buffered` that haven't been send. If necessary the buffered
    415  * report can be padded with zeros up to the endpoints maximum size.
    416  *
    417  * @param endpoint USB IN endpoint to flush the reports from
    418  * @param padded Pad the buffered report with zeros up to the endpoints maximum size
    419  */
    420 static void flush_report_buffered(usb_endpoint_in_lut_t endpoint, bool padded) {
    421     usb_endpoint_in_flush(&usb_endpoints_in[endpoint], padded);
    422 }
    423 
    424 /**
    425  * @brief Receive a report from the host.
    426  *
    427  * @param endpoint USB OUT endpoint to receive the report from
    428  * @param report pointer to the report
    429  * @param size size of the report
    430  * @return true Success
    431  * @return false Failure
    432  */
    433 static bool receive_report(usb_endpoint_out_lut_t endpoint, void *report, size_t size) {
    434     return usb_endpoint_out_receive(&usb_endpoints_out[endpoint], (uint8_t *)report, size, TIME_IMMEDIATE);
    435 }
    436 
    437 void send_keyboard(report_keyboard_t *report) {
    438     /* If we're in Boot Protocol, don't send any report ID or other funky fields */
    439     if (usb_device_state_get_protocol() == USB_PROTOCOL_BOOT) {
    440         send_report(USB_ENDPOINT_IN_KEYBOARD, &report->mods, 8);
    441     } else {
    442         send_report(USB_ENDPOINT_IN_KEYBOARD, report, KEYBOARD_REPORT_SIZE);
    443     }
    444 }
    445 
    446 void send_nkro(report_nkro_t *report) {
    447 #ifdef NKRO_ENABLE
    448     send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_nkro_t));
    449 #endif
    450 }
    451 
    452 /* ---------------------------------------------------------
    453  *                     Mouse functions
    454  * ---------------------------------------------------------
    455  */
    456 
    457 void send_mouse(report_mouse_t *report) {
    458 #ifdef MOUSE_ENABLE
    459     send_report(USB_ENDPOINT_IN_MOUSE, report, sizeof(report_mouse_t));
    460 #endif
    461 }
    462 
    463 /* ---------------------------------------------------------
    464  *                   Extrakey functions
    465  * ---------------------------------------------------------
    466  */
    467 
    468 void send_extra(report_extra_t *report) {
    469 #ifdef EXTRAKEY_ENABLE
    470     send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_extra_t));
    471 #endif
    472 }
    473 
    474 void send_programmable_button(report_programmable_button_t *report) {
    475 #ifdef PROGRAMMABLE_BUTTON_ENABLE
    476     send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_programmable_button_t));
    477 #endif
    478 }
    479 
    480 void send_joystick(report_joystick_t *report) {
    481 #ifdef JOYSTICK_ENABLE
    482     send_report(USB_ENDPOINT_IN_JOYSTICK, report, sizeof(report_joystick_t));
    483 #endif
    484 }
    485 
    486 void send_digitizer(report_digitizer_t *report) {
    487 #ifdef DIGITIZER_ENABLE
    488     send_report(USB_ENDPOINT_IN_DIGITIZER, report, sizeof(report_digitizer_t));
    489 #endif
    490 }
    491 
    492 /* ---------------------------------------------------------
    493  *                   Console functions
    494  * ---------------------------------------------------------
    495  */
    496 
    497 #ifdef CONSOLE_ENABLE
    498 
    499 int8_t sendchar(uint8_t c) {
    500     return (int8_t)send_report_buffered(USB_ENDPOINT_IN_CONSOLE, &c, sizeof(uint8_t));
    501 }
    502 
    503 void console_task(void) {
    504     flush_report_buffered(USB_ENDPOINT_IN_CONSOLE, true);
    505 }
    506 
    507 #endif /* CONSOLE_ENABLE */
    508 
    509 #ifdef RAW_ENABLE
    510 void send_raw_hid(uint8_t *data, uint8_t length) {
    511     if (length != RAW_EPSIZE) {
    512         return;
    513     }
    514     send_report(USB_ENDPOINT_IN_RAW, data, length);
    515 }
    516 
    517 void raw_hid_task(void) {
    518     uint8_t buffer[RAW_EPSIZE];
    519     while (receive_report(USB_ENDPOINT_OUT_RAW, buffer, sizeof(buffer))) {
    520         raw_hid_receive(buffer, sizeof(buffer));
    521     }
    522 }
    523 
    524 #endif
    525 
    526 #ifdef MIDI_ENABLE
    527 
    528 void send_midi_packet(MIDI_EventPacket_t *event) {
    529     send_report(USB_ENDPOINT_IN_MIDI, (uint8_t *)event, sizeof(MIDI_EventPacket_t));
    530 }
    531 
    532 bool recv_midi_packet(MIDI_EventPacket_t *const event) {
    533     return receive_report(USB_ENDPOINT_OUT_MIDI, (uint8_t *)event, sizeof(MIDI_EventPacket_t));
    534 }
    535 
    536 #endif
    537 
    538 #ifdef VIRTSER_ENABLE
    539 
    540 #    include "hal_usb_cdc.h"
    541 /**
    542  * @brief CDC serial driver configuration structure. Set to 9600 baud, 1 stop bit, no parity, 8 data bits.
    543  */
    544 static cdc_linecoding_t linecoding = {{0x00, 0x96, 0x00, 0x00}, LC_STOP_1, LC_PARITY_NONE, 8};
    545 
    546 bool virtser_usb_request_cb(USBDriver *usbp) {
    547     if ((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) { /* bmRequestType */
    548         if (usbp->setup[4] == CCI_INTERFACE) {                            /* wIndex (LSB) */
    549             switch (usbp->setup[1]) {                                     /* bRequest */
    550                 case CDC_GET_LINE_CODING:
    551                     usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL);
    552                     return true;
    553                 case CDC_SET_LINE_CODING:
    554                     usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL);
    555                     return true;
    556                 case CDC_SET_CONTROL_LINE_STATE:
    557                     /* Nothing to do, there are no control lines.*/
    558                     usbSetupTransfer(usbp, NULL, 0, NULL);
    559                     return true;
    560                 default:
    561                     return false;
    562             }
    563         }
    564     }
    565 
    566     return false;
    567 }
    568 
    569 void virtser_init(void) {}
    570 
    571 void virtser_send(const uint8_t byte) {
    572     send_report_buffered(USB_ENDPOINT_IN_CDC_DATA, (void *)&byte, sizeof(byte));
    573 }
    574 
    575 __attribute__((weak)) void virtser_recv(uint8_t c) {
    576     // Ignore by default
    577 }
    578 
    579 void virtser_task(void) {
    580     uint8_t buffer[CDC_EPSIZE];
    581     while (receive_report(USB_ENDPOINT_OUT_CDC_DATA, buffer, sizeof(buffer))) {
    582         for (int i = 0; i < sizeof(buffer); i++) {
    583             virtser_recv(buffer[i]);
    584         }
    585     }
    586 
    587     flush_report_buffered(USB_ENDPOINT_IN_CDC_DATA, false);
    588 }
    589 
    590 #endif