logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

usb_driver.c (12126B)


  1. // Copyright 2023 Stefan Kerkmann (@KarlK90)
  2. // Copyright 2021 Purdea Andrei
  3. // Copyright 2021 Michael Stapelberg
  4. // Copyright 2020 Ryan (@fauxpark)
  5. // Copyright 2016 Fredizzimo
  6. // Copyright 2016 Giovanni Di Sirio
  7. // SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0
  8. #include <hal.h>
  9. #include <string.h>
  10. #include "usb_driver.h"
  11. #include "util.h"
  12. /*===========================================================================*/
  13. /* Driver local functions. */
  14. /*===========================================================================*/
  15. static void usb_start_receive(usb_endpoint_out_t *endpoint) {
  16. /* If the USB driver is not in the appropriate state then transactions
  17. must not be started.*/
  18. if ((usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE)) {
  19. return;
  20. }
  21. /* Checking if there is already a transaction ongoing on the endpoint.*/
  22. if (usbGetReceiveStatusI(endpoint->config.usbp, endpoint->config.ep)) {
  23. return;
  24. }
  25. /* Checking if there is a buffer ready for incoming data.*/
  26. uint8_t *buffer = ibqGetEmptyBufferI(&endpoint->ibqueue);
  27. if (buffer == NULL) {
  28. return;
  29. }
  30. /* Buffer found, starting a new transaction.*/
  31. usbStartReceiveI(endpoint->config.usbp, endpoint->config.ep, buffer, endpoint->ibqueue.bsize - sizeof(size_t));
  32. }
  33. /**
  34. * @brief Notification of empty buffer released into the input buffers queue.
  35. *
  36. * @param[in] bqp the buffers queue pointer.
  37. */
  38. static void ibnotify(io_buffers_queue_t *bqp) {
  39. usb_endpoint_out_t *endpoint = bqGetLinkX(bqp);
  40. usb_start_receive(endpoint);
  41. }
  42. /**
  43. * @brief Notification of filled buffer inserted into the output buffers queue.
  44. *
  45. * @param[in] bqp the buffers queue pointer.
  46. */
  47. static void obnotify(io_buffers_queue_t *bqp) {
  48. usb_endpoint_in_t *endpoint = bqGetLinkX(bqp);
  49. /* If the USB endpoint is not in the appropriate state then transactions
  50. must not be started.*/
  51. if ((usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE)) {
  52. return;
  53. }
  54. /* Checking if there is already a transaction ongoing on the endpoint.*/
  55. if (!usbGetTransmitStatusI(endpoint->config.usbp, endpoint->config.ep)) {
  56. /* Trying to get a full buffer.*/
  57. size_t n;
  58. uint8_t *buffer = obqGetFullBufferI(&endpoint->obqueue, &n);
  59. if (buffer != NULL) {
  60. /* Buffer found, starting a new transaction.*/
  61. usbStartTransmitI(endpoint->config.usbp, endpoint->config.ep, buffer, n);
  62. }
  63. }
  64. }
  65. /*===========================================================================*/
  66. /* Driver exported functions. */
  67. /*===========================================================================*/
  68. void usb_endpoint_in_init(usb_endpoint_in_t *endpoint) {
  69. usb_endpoint_config_t *config = &endpoint->config;
  70. endpoint->ep_config.in_state = &endpoint->ep_in_state;
  71. #if defined(USB_ENDPOINTS_ARE_REORDERABLE)
  72. if (endpoint->is_shared) {
  73. endpoint->ep_config.out_state = &endpoint->ep_out_state;
  74. }
  75. #endif
  76. obqObjectInit(&endpoint->obqueue, true, config->buffer, config->buffer_size, config->buffer_capacity, obnotify, endpoint);
  77. }
  78. void usb_endpoint_out_init(usb_endpoint_out_t *endpoint) {
  79. usb_endpoint_config_t *config = &endpoint->config;
  80. endpoint->ep_config.out_state = &endpoint->ep_out_state;
  81. ibqObjectInit(&endpoint->ibqueue, true, config->buffer, config->buffer_size, config->buffer_capacity, ibnotify, endpoint);
  82. }
  83. void usb_endpoint_in_start(usb_endpoint_in_t *endpoint) {
  84. osalDbgCheck(endpoint != NULL);
  85. osalSysLock();
  86. osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), "invalid state");
  87. endpoint->config.usbp->in_params[endpoint->config.ep - 1U] = endpoint;
  88. endpoint->timed_out = false;
  89. osalSysUnlock();
  90. }
  91. void usb_endpoint_out_start(usb_endpoint_out_t *endpoint) {
  92. osalDbgCheck(endpoint != NULL);
  93. osalSysLock();
  94. osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), "invalid state");
  95. endpoint->config.usbp->out_params[endpoint->config.ep - 1U] = endpoint;
  96. endpoint->timed_out = false;
  97. osalSysUnlock();
  98. }
  99. void usb_endpoint_in_stop(usb_endpoint_in_t *endpoint) {
  100. osalDbgCheck(endpoint != NULL);
  101. osalSysLock();
  102. endpoint->config.usbp->in_params[endpoint->config.ep - 1U] = NULL;
  103. bqSuspendI(&endpoint->obqueue);
  104. obqResetI(&endpoint->obqueue);
  105. if (endpoint->report_storage != NULL) {
  106. endpoint->report_storage->reset_report(endpoint->report_storage->reports);
  107. }
  108. osalOsRescheduleS();
  109. osalSysUnlock();
  110. }
  111. void usb_endpoint_out_stop(usb_endpoint_out_t *endpoint) {
  112. osalDbgCheck(endpoint != NULL);
  113. osalSysLock();
  114. osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), "invalid state");
  115. bqSuspendI(&endpoint->ibqueue);
  116. ibqResetI(&endpoint->ibqueue);
  117. osalOsRescheduleS();
  118. osalSysUnlock();
  119. }
  120. void usb_endpoint_in_suspend_cb(usb_endpoint_in_t *endpoint) {
  121. bqSuspendI(&endpoint->obqueue);
  122. obqResetI(&endpoint->obqueue);
  123. if (endpoint->report_storage != NULL) {
  124. endpoint->report_storage->reset_report(endpoint->report_storage->reports);
  125. }
  126. }
  127. void usb_endpoint_out_suspend_cb(usb_endpoint_out_t *endpoint) {
  128. bqSuspendI(&endpoint->ibqueue);
  129. ibqResetI(&endpoint->ibqueue);
  130. }
  131. void usb_endpoint_in_wakeup_cb(usb_endpoint_in_t *endpoint) {
  132. bqResumeX(&endpoint->obqueue);
  133. }
  134. void usb_endpoint_out_wakeup_cb(usb_endpoint_out_t *endpoint) {
  135. bqResumeX(&endpoint->ibqueue);
  136. }
  137. void usb_endpoint_in_configure_cb(usb_endpoint_in_t *endpoint) {
  138. usbInitEndpointI(endpoint->config.usbp, endpoint->config.ep, &endpoint->ep_config);
  139. obqResetI(&endpoint->obqueue);
  140. bqResumeX(&endpoint->obqueue);
  141. }
  142. void usb_endpoint_out_configure_cb(usb_endpoint_out_t *endpoint) {
  143. /* The current assumption is that there are no standalone OUT endpoints,
  144. * therefore if we share an endpoint with an IN endpoint, it is already
  145. * initialized. */
  146. #if !defined(USB_ENDPOINTS_ARE_REORDERABLE)
  147. usbInitEndpointI(endpoint->config.usbp, endpoint->config.ep, &endpoint->ep_config);
  148. #endif
  149. ibqResetI(&endpoint->ibqueue);
  150. bqResumeX(&endpoint->ibqueue);
  151. (void)usb_start_receive(endpoint);
  152. }
  153. void usb_endpoint_in_tx_complete_cb(USBDriver *usbp, usbep_t ep) {
  154. usb_endpoint_in_t *endpoint = usbp->in_params[ep - 1U];
  155. size_t n;
  156. uint8_t * buffer;
  157. if (endpoint == NULL) {
  158. return;
  159. }
  160. osalSysLockFromISR();
  161. /* Sending succeded, so we can reset the timed out state. */
  162. endpoint->timed_out = false;
  163. /* Freeing the buffer just transmitted, if it was not a zero size packet.*/
  164. if (!obqIsEmptyI(&endpoint->obqueue) && usbp->epc[ep]->in_state->txsize > 0U) {
  165. /* Store the last send report in the endpoint to be retrieved by a
  166. * GET_REPORT request or IDLE report handling. */
  167. if (endpoint->report_storage != NULL) {
  168. buffer = obqGetFullBufferI(&endpoint->obqueue, &n);
  169. endpoint->report_storage->set_report(endpoint->report_storage->reports, buffer, n);
  170. }
  171. obqReleaseEmptyBufferI(&endpoint->obqueue);
  172. }
  173. /* Checking if there is a buffer ready for transmission.*/
  174. buffer = obqGetFullBufferI(&endpoint->obqueue, &n);
  175. if (buffer != NULL) {
  176. /* The endpoint cannot be busy, we are in the context of the callback,
  177. so it is safe to transmit without a check.*/
  178. usbStartTransmitI(usbp, ep, buffer, n);
  179. } else if ((usbp->epc[ep]->ep_mode == USB_EP_MODE_TYPE_BULK) && (usbp->epc[ep]->in_state->txsize > 0U) && ((usbp->epc[ep]->in_state->txsize & ((size_t)usbp->epc[ep]->in_maxsize - 1U)) == 0U)) {
  180. /* Transmit zero sized packet in case the last one has maximum allowed
  181. * size. Otherwise the recipient may expect more data coming soon and
  182. * not return buffered data to app. See section 5.8.3 Bulk Transfer
  183. * Packet Size Constraints of the USB Specification document. */
  184. usbStartTransmitI(usbp, ep, usbp->setup, 0);
  185. } else {
  186. /* Nothing to transmit.*/
  187. }
  188. osalSysUnlockFromISR();
  189. }
  190. void usb_endpoint_out_rx_complete_cb(USBDriver *usbp, usbep_t ep) {
  191. usb_endpoint_out_t *endpoint = usbp->out_params[ep - 1U];
  192. if (endpoint == NULL) {
  193. return;
  194. }
  195. osalSysLockFromISR();
  196. size_t size = usbGetReceiveTransactionSizeX(usbp, ep);
  197. if (size > 0) {
  198. /* Posting the filled buffer in the queue.*/
  199. ibqPostFullBufferI(&endpoint->ibqueue, usbGetReceiveTransactionSizeX(endpoint->config.usbp, endpoint->config.ep));
  200. }
  201. /* The endpoint cannot be busy, we are in the context of the callback, so a
  202. * packet is in the buffer for sure. Trying to get a free buffer for the
  203. * next transaction.*/
  204. usb_start_receive(endpoint);
  205. osalSysUnlockFromISR();
  206. }
  207. bool usb_endpoint_in_send(usb_endpoint_in_t *endpoint, const uint8_t *data, size_t size, sysinterval_t timeout, bool buffered) {
  208. osalDbgCheck((endpoint != NULL) && (data != NULL) && (size > 0U) && (size <= endpoint->config.buffer_size));
  209. osalSysLock();
  210. if (usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE) {
  211. osalSysUnlock();
  212. return false;
  213. }
  214. /* Short circuit the waiting if this endpoint timed out before, e.g. if
  215. * nobody is listening on this endpoint (is disconnected) such as
  216. * `hid_listen`/`qmk console` or we are in an environment with a very
  217. * restricted USB stack. The reason is to not introduce micro lock-ups if
  218. * the report is send periodically. */
  219. if (endpoint->timed_out && timeout != TIME_INFINITE) {
  220. timeout = TIME_IMMEDIATE;
  221. }
  222. osalSysUnlock();
  223. while (true) {
  224. size_t sent = obqWriteTimeout(&endpoint->obqueue, data, size, timeout);
  225. if (sent < size) {
  226. osalSysLock();
  227. endpoint->timed_out |= sent == 0;
  228. bqSuspendI(&endpoint->obqueue);
  229. obqResetI(&endpoint->obqueue);
  230. bqResumeX(&endpoint->obqueue);
  231. osalOsRescheduleS();
  232. osalSysUnlock();
  233. continue;
  234. }
  235. if (!buffered) {
  236. obqFlush(&endpoint->obqueue);
  237. }
  238. return true;
  239. }
  240. }
  241. void usb_endpoint_in_flush(usb_endpoint_in_t *endpoint, bool padded) {
  242. osalDbgCheck(endpoint != NULL);
  243. output_buffers_queue_t *obqp = &endpoint->obqueue;
  244. if (padded && obqp->ptr != NULL) {
  245. ptrdiff_t bytes_left = (size_t)obqp->top - (size_t)obqp->ptr;
  246. while (bytes_left > 0) {
  247. // Putting bytes into a buffer that has space left should never
  248. // fail and be instant, therefore we don't check the return value
  249. // for errors here.
  250. obqPutTimeout(obqp, 0, TIME_IMMEDIATE);
  251. bytes_left--;
  252. }
  253. }
  254. obqFlush(obqp);
  255. }
  256. bool usb_endpoint_in_is_inactive(usb_endpoint_in_t *endpoint) {
  257. osalDbgCheck(endpoint != NULL);
  258. osalSysLock();
  259. bool inactive = obqIsEmptyI(&endpoint->obqueue) && !usbGetTransmitStatusI(endpoint->config.usbp, endpoint->config.ep);
  260. osalSysUnlock();
  261. return inactive;
  262. }
  263. bool usb_endpoint_out_receive(usb_endpoint_out_t *endpoint, uint8_t *data, size_t size, sysinterval_t timeout) {
  264. osalDbgCheck((endpoint != NULL) && (data != NULL) && (size > 0U));
  265. osalSysLock();
  266. if (usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE) {
  267. osalSysUnlock();
  268. return false;
  269. }
  270. if (endpoint->timed_out && timeout != TIME_INFINITE) {
  271. timeout = TIME_IMMEDIATE;
  272. }
  273. osalSysUnlock();
  274. const size_t received = ibqReadTimeout(&endpoint->ibqueue, data, size, timeout);
  275. endpoint->timed_out = received == 0;
  276. return received == size;
  277. }