qmk_firmware

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

usb_report_handling.c (8559B)


      1 // Copyright 2023 Stefan Kerkmann (@KarlK90)
      2 // SPDX-License-Identifier: GPL-3.0-or-later
      3 
      4 #include <string.h>
      5 #include <stdint.h>
      6 #include <stdbool.h>
      7 
      8 #include "usb_report_handling.h"
      9 #include "usb_endpoints.h"
     10 #include "usb_main.h"
     11 #include "usb_types.h"
     12 #include "usb_driver.h"
     13 #include "report.h"
     14 
     15 extern usb_endpoint_in_t     usb_endpoints_in[USB_ENDPOINT_IN_COUNT];
     16 extern usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES];
     17 
     18 void usb_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length) {
     19     if (*reports == NULL) {
     20         return;
     21     }
     22 
     23     (*reports)->last_report = chVTGetSystemTimeX();
     24     (*reports)->length      = length;
     25     memcpy(&(*reports)->data, data, length);
     26 }
     27 
     28 void usb_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report) {
     29     (void)report_id;
     30     if (*reports == NULL) {
     31         return;
     32     }
     33 
     34     report->length = (*reports)->length;
     35     memcpy(&report->data, &(*reports)->data, report->length);
     36 }
     37 
     38 void usb_reset_report(usb_fs_report_t **reports) {
     39     if (*reports == NULL) {
     40         return;
     41     }
     42 
     43     memset(&(*reports)->data, 0, (*reports)->length);
     44     (*reports)->idle_rate   = 0;
     45     (*reports)->last_report = 0;
     46 }
     47 
     48 void usb_shared_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length) {
     49     uint8_t report_id = data[0];
     50 
     51     if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
     52         return;
     53     }
     54 
     55     reports[report_id]->last_report = chVTGetSystemTimeX();
     56     reports[report_id]->length      = length;
     57     memcpy(&reports[report_id]->data, data, length);
     58 }
     59 
     60 void usb_shared_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report) {
     61     if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
     62         return;
     63     }
     64 
     65     report->length = reports[report_id]->length;
     66     memcpy(&report->data, &reports[report_id]->data, report->length);
     67 }
     68 
     69 void usb_shared_reset_report(usb_fs_report_t **reports) {
     70     for (int i = 0; i <= REPORT_ID_COUNT; i++) {
     71         if (reports[i] == NULL) {
     72             continue;
     73         }
     74         memset(&reports[i]->data, 0, reports[i]->length);
     75         reports[i]->idle_rate   = 0;
     76         reports[i]->last_report = 0;
     77     }
     78 }
     79 
     80 bool usb_get_report_cb(USBDriver *driver) {
     81     usb_control_request_t *setup     = (usb_control_request_t *)driver->setup;
     82     uint8_t                interface = setup->wIndex;
     83     uint8_t                report_id = setup->wValue.lbyte;
     84 
     85     static usb_fs_report_t report;
     86 
     87     if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {
     88         return false;
     89     }
     90 
     91     usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];
     92 
     93     if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {
     94         return false;
     95     }
     96 
     97     usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
     98 
     99     if (report_storage == NULL) {
    100         return false;
    101     }
    102 
    103     report_storage->get_report(report_storage->reports, report_id, &report);
    104 
    105     usbSetupTransfer(driver, (uint8_t *)report.data, report.length, NULL);
    106 
    107     return true;
    108 }
    109 
    110 static bool run_idle_task = false;
    111 
    112 void usb_set_idle_rate(usb_fs_report_t **reports, uint8_t report_id, uint8_t idle_rate) {
    113     (void)report_id;
    114 
    115     if (*reports == NULL) {
    116         return;
    117     }
    118 
    119     (*reports)->idle_rate = idle_rate * 4;
    120 
    121     run_idle_task |= idle_rate != 0;
    122 }
    123 
    124 uint8_t usb_get_idle_rate(usb_fs_report_t **reports, uint8_t report_id) {
    125     (void)report_id;
    126 
    127     if (*reports == NULL) {
    128         return 0;
    129     }
    130 
    131     return (*reports)->idle_rate / 4;
    132 }
    133 
    134 bool usb_idle_timer_elapsed(usb_fs_report_t **reports, uint8_t report_id) {
    135     (void)report_id;
    136 
    137     if (*reports == NULL) {
    138         return false;
    139     }
    140 
    141     osalSysLock();
    142     time_msecs_t idle_rate   = (*reports)->idle_rate;
    143     systime_t    last_report = (*reports)->last_report;
    144     osalSysUnlock();
    145 
    146     if (idle_rate == 0) {
    147         return false;
    148     }
    149 
    150     return chTimeI2MS(chVTTimeElapsedSinceX(last_report)) >= idle_rate;
    151 }
    152 
    153 void usb_shared_set_idle_rate(usb_fs_report_t **reports, uint8_t report_id, uint8_t idle_rate) {
    154     // USB spec demands that a report_id of 0 would set the idle rate for all
    155     // reports of that endpoint, but this can easily lead to resource
    156     // exhaustion, therefore we deliberalty break the spec at this point.
    157     if (report_id == 0 || report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
    158         return;
    159     }
    160 
    161     reports[report_id]->idle_rate = idle_rate * 4;
    162 
    163     run_idle_task |= idle_rate != 0;
    164 }
    165 
    166 uint8_t usb_shared_get_idle_rate(usb_fs_report_t **reports, uint8_t report_id) {
    167     if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
    168         return 0;
    169     }
    170 
    171     return reports[report_id]->idle_rate / 4;
    172 }
    173 
    174 bool usb_shared_idle_timer_elapsed(usb_fs_report_t **reports, uint8_t report_id) {
    175     if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
    176         return false;
    177     }
    178 
    179     osalSysLock();
    180     time_msecs_t idle_rate   = reports[report_id]->idle_rate;
    181     systime_t    last_report = reports[report_id]->last_report;
    182     osalSysUnlock();
    183 
    184     if (idle_rate == 0) {
    185         return false;
    186     }
    187 
    188     return chTimeI2MS(chVTTimeElapsedSinceX(last_report)) >= idle_rate;
    189 }
    190 
    191 void usb_idle_task(void) {
    192     if (!run_idle_task) {
    193         return;
    194     }
    195 
    196     static usb_fs_report_t report;
    197     bool                   non_zero_idle_rate_found = false;
    198 
    199     for (int ep = 0; ep < USB_ENDPOINT_IN_COUNT; ep++) {
    200         usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
    201 
    202         if (report_storage == NULL) {
    203             continue;
    204         }
    205 
    206 #if defined(SHARED_EP_ENABLE)
    207         if (ep == USB_ENDPOINT_IN_SHARED) {
    208             for (int report_id = 1; report_id <= REPORT_ID_COUNT; report_id++) {
    209                 osalSysLock();
    210                 non_zero_idle_rate_found |= report_storage->get_idle(report_storage->reports, report_id) != 0;
    211                 osalSysUnlock();
    212 
    213                 if (usb_endpoint_in_is_inactive(&usb_endpoints_in[ep]) && report_storage->idle_timer_elasped(report_storage->reports, report_id)) {
    214                     osalSysLock();
    215                     report_storage->get_report(report_storage->reports, report_id, &report);
    216                     osalSysUnlock();
    217                     send_report(ep, &report.data, report.length);
    218                 }
    219             }
    220             continue;
    221         }
    222 #endif
    223 
    224         osalSysLock();
    225         non_zero_idle_rate_found |= report_storage->get_idle(report_storage->reports, 0) != 0;
    226         osalSysUnlock();
    227 
    228         if (usb_endpoint_in_is_inactive(&usb_endpoints_in[ep]) && report_storage->idle_timer_elasped(report_storage->reports, 0)) {
    229             osalSysLock();
    230             report_storage->get_report(report_storage->reports, 0, &report);
    231             osalSysUnlock();
    232             send_report(ep, &report.data, report.length);
    233         }
    234     }
    235 
    236     run_idle_task = non_zero_idle_rate_found;
    237 }
    238 
    239 bool usb_get_idle_cb(USBDriver *driver) {
    240     usb_control_request_t *setup     = (usb_control_request_t *)driver->setup;
    241     uint8_t                interface = setup->wIndex;
    242     uint8_t                report_id = setup->wValue.lbyte;
    243 
    244     static uint8_t _Alignas(4) idle_rate;
    245 
    246     if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {
    247         return false;
    248     }
    249 
    250     usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];
    251 
    252     if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {
    253         return false;
    254     }
    255 
    256     usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
    257 
    258     if (report_storage == NULL) {
    259         return false;
    260     }
    261 
    262     idle_rate = report_storage->get_idle(report_storage->reports, report_id);
    263 
    264     usbSetupTransfer(driver, &idle_rate, 1, NULL);
    265 
    266     return true;
    267 }
    268 
    269 bool usb_set_idle_cb(USBDriver *driver) {
    270     usb_control_request_t *setup     = (usb_control_request_t *)driver->setup;
    271     uint8_t                interface = setup->wIndex;
    272     uint8_t                report_id = setup->wValue.lbyte;
    273     uint8_t                idle_rate = setup->wValue.hbyte;
    274 
    275     if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {
    276         return false;
    277     }
    278 
    279     usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];
    280 
    281     if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {
    282         return false;
    283     }
    284 
    285     usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
    286 
    287     if (report_storage == NULL) {
    288         return false;
    289     }
    290 
    291     report_storage->set_idle(report_storage->reports, report_id, idle_rate);
    292 
    293     usbSetupTransfer(driver, NULL, 0, NULL);
    294 
    295     return true;
    296 }