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 }