ChibiOS 21.11.4
hal_usb.c
Go to the documentation of this file.
1/*
2 ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17/**
18 * @file hal_usb.c
19 * @brief USB Driver code.
20 *
21 * @addtogroup USB
22 * @{
23 */
24
25#include <string.h>
26
27#include "hal.h"
28
29#if (HAL_USE_USB == TRUE) || defined(__DOXYGEN__)
30
31/*===========================================================================*/
32/* Driver local definitions. */
33/*===========================================================================*/
34
35/*===========================================================================*/
36/* Driver exported variables. */
37/*===========================================================================*/
38
39/*===========================================================================*/
40/* Driver local variables and types. */
41/*===========================================================================*/
42
43static const uint8_t zero_status[] = {0x00, 0x00};
44static const uint8_t active_status[] = {0x00, 0x00};
45static const uint8_t halted_status[] = {0x01, 0x00};
46
47/*===========================================================================*/
48/* Driver local functions. */
49/*===========================================================================*/
50
51static uint16_t get_hword(uint8_t *p) {
52 uint16_t hw;
53
54 hw = (uint16_t)*p++;
55 hw |= (uint16_t)*p << 8U;
56 return hw;
57}
58
59/**
60 * @brief SET ADDRESS transaction callback.
61 *
62 * @param[in] usbp pointer to the @p USBDriver object
63 */
64static void set_address(USBDriver *usbp) {
65
66 usbp->address = usbp->setup[2];
69 usbp->state = USB_SELECTED;
70}
71
72/**
73 * @brief Standard requests handler.
74 * @details This is the standard requests default handler, most standard
75 * requests are handled here, the user can override the standard
76 * handling using the @p requests_hook_cb hook in the
77 * @p USBConfig structure.
78 *
79 * @param[in] usbp pointer to the @p USBDriver object
80 * @return The request handling exit code.
81 * @retval false Request not recognized by the handler or error.
82 * @retval true Request handled.
83 */
84static bool default_handler(USBDriver *usbp) {
85 const USBDescriptor *dp;
86
87 /* Decoding the request.*/
88 switch ((((uint32_t)usbp->setup[0] & (USB_RTYPE_RECIPIENT_MASK |
90 ((uint32_t)usbp->setup[1] << 8U))) {
91 case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_GET_STATUS << 8):
92 /* Just returns the current status word.*/
93 usbSetupTransfer(usbp, (uint8_t *)&usbp->status, 2, NULL);
94 return true;
95 case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_CLEAR_FEATURE << 8):
96 /* Only the DEVICE_REMOTE_WAKEUP is handled here, any other feature
97 number is handled as an error.*/
99 usbp->status &= ~2U;
100 usbSetupTransfer(usbp, NULL, 0, NULL);
101 return true;
102 }
103 return false;
104 case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_SET_FEATURE << 8):
105 /* Only the DEVICE_REMOTE_WAKEUP is handled here, any other feature
106 number is handled as an error.*/
107 if (usbp->setup[2] == USB_FEATURE_DEVICE_REMOTE_WAKEUP) {
108 usbp->status |= 2U;
109 usbSetupTransfer(usbp, NULL, 0, NULL);
110 return true;
111 }
112 return false;
113 case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_SET_ADDRESS << 8):
114 /* The SET_ADDRESS handling can be performed here or postponed after
115 the status packed depending on the USB_SET_ADDRESS_MODE low
116 driver setting.*/
117#if USB_SET_ADDRESS_MODE == USB_EARLY_SET_ADDRESS
118 if ((usbp->setup[0] == USB_RTYPE_RECIPIENT_DEVICE) &&
119 (usbp->setup[1] == USB_REQ_SET_ADDRESS)) {
120 set_address(usbp);
121 }
122 usbSetupTransfer(usbp, NULL, 0, NULL);
123#else
124 usbSetupTransfer(usbp, NULL, 0, set_address);
125#endif
126 return true;
127 case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_GET_DESCRIPTOR << 8):
128 case (uint32_t)USB_RTYPE_RECIPIENT_INTERFACE | ((uint32_t)USB_REQ_GET_DESCRIPTOR << 8):
129 /* Handling descriptor requests from the host.*/
130 dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3],
131 usbp->setup[2],
132 get_hword(&usbp->setup[4]));
133 if (dp == NULL) {
134 return false;
135 }
136 /*lint -save -e9005 [11.8] Removing const is fine.*/
137 usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL);
138 /*lint -restore*/
139 return true;
140 case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_GET_CONFIGURATION << 8):
141 /* Returning the last selected configuration.*/
142 usbSetupTransfer(usbp, &usbp->configuration, 1, NULL);
143 return true;
144 case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_SET_CONFIGURATION << 8):
145#if defined(USB_SET_CONFIGURATION_OLD_BEHAVIOR)
146 /* Handling configuration selection from the host only if it is different
147 from the current configuration.*/
148 if (usbp->configuration != usbp->setup[2])
149#endif
150 {
151 /* If the USB device is already active then we have to perform the clear
152 procedure on the current configuration.*/
153 if (usbp->state == USB_ACTIVE) {
154 /* Current configuration cleared.*/
158 usbp->configuration = 0U;
159 usbp->state = USB_SELECTED;
161 }
162 if (usbp->setup[2] != 0U) {
163 /* New configuration.*/
164 usbp->configuration = usbp->setup[2];
165 usbp->state = USB_ACTIVE;
167 }
168 }
169 usbSetupTransfer(usbp, NULL, 0, NULL);
170 return true;
171 case (uint32_t)USB_RTYPE_RECIPIENT_INTERFACE | ((uint32_t)USB_REQ_GET_STATUS << 8):
172 case (uint32_t)USB_RTYPE_RECIPIENT_ENDPOINT | ((uint32_t)USB_REQ_SYNCH_FRAME << 8):
173 /* Just sending two zero bytes, the application can change the behavior
174 using a hook..*/
175 /*lint -save -e9005 [11.8] Removing const is fine.*/
176 usbSetupTransfer(usbp, (uint8_t *)zero_status, 2, NULL);
177 /*lint -restore*/
178 return true;
179 case (uint32_t)USB_RTYPE_RECIPIENT_ENDPOINT | ((uint32_t)USB_REQ_GET_STATUS << 8):
180 /* Sending the EP status.*/
181 if ((usbp->setup[4] & 0x80U) != 0U) {
182 switch (usb_lld_get_status_in(usbp, usbp->setup[4] & 0x0FU)) {
184 /*lint -save -e9005 [11.8] Removing const is fine.*/
185 usbSetupTransfer(usbp, (uint8_t *)halted_status, 2, NULL);
186 /*lint -restore*/
187 return true;
188 case EP_STATUS_ACTIVE:
189 /*lint -save -e9005 [11.8] Removing const is fine.*/
190 usbSetupTransfer(usbp, (uint8_t *)active_status, 2, NULL);
191 /*lint -restore*/
192 return true;
194 default:
195 return false;
196 }
197 }
198 else {
199 switch (usb_lld_get_status_out(usbp, usbp->setup[4] & 0x0FU)) {
201 /*lint -save -e9005 [11.8] Removing const is fine.*/
202 usbSetupTransfer(usbp, (uint8_t *)halted_status, 2, NULL);
203 /*lint -restore*/
204 return true;
205 case EP_STATUS_ACTIVE:
206 /*lint -save -e9005 [11.8] Removing const is fine.*/
207 usbSetupTransfer(usbp, (uint8_t *)active_status, 2, NULL);
208 /*lint -restore*/
209 return true;
211 default:
212 return false;
213 }
214 }
215 case (uint32_t)USB_RTYPE_RECIPIENT_ENDPOINT | ((uint32_t)USB_REQ_CLEAR_FEATURE << 8):
216 /* Only ENDPOINT_HALT is handled as feature.*/
217 if (usbp->setup[2] != USB_FEATURE_ENDPOINT_HALT) {
218 return false;
219 }
220 /* Clearing the EP status, not valid for EP0, it is ignored in that case.*/
221 if ((usbp->setup[4] & 0x0FU) != 0U) {
222 if ((usbp->setup[4] & 0x80U) != 0U) {
223 usb_lld_clear_in(usbp, usbp->setup[4] & 0x0FU);
224 }
225 else {
226 usb_lld_clear_out(usbp, usbp->setup[4] & 0x0FU);
227 }
228 }
229 usbSetupTransfer(usbp, NULL, 0, NULL);
230 return true;
231 case (uint32_t)USB_RTYPE_RECIPIENT_ENDPOINT | ((uint32_t)USB_REQ_SET_FEATURE << 8):
232 /* Only ENDPOINT_HALT is handled as feature.*/
233 if (usbp->setup[2] != USB_FEATURE_ENDPOINT_HALT) {
234 return false;
235 }
236 /* Stalling the EP, not valid for EP0, it is ignored in that case.*/
237 if ((usbp->setup[4] & 0x0FU) != 0U) {
238 if ((usbp->setup[4] & 0x80U) != 0U) {
239 usb_lld_stall_in(usbp, usbp->setup[4] & 0x0FU);
240 }
241 else {
242 usb_lld_stall_out(usbp, usbp->setup[4] & 0x0FU);
243 }
244 }
245 usbSetupTransfer(usbp, NULL, 0, NULL);
246 return true;
247 case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_SET_DESCRIPTOR << 8):
248 case (uint32_t)USB_RTYPE_RECIPIENT_INTERFACE | ((uint32_t)USB_REQ_CLEAR_FEATURE << 8):
249 case (uint32_t)USB_RTYPE_RECIPIENT_INTERFACE | ((uint32_t)USB_REQ_SET_FEATURE << 8):
250 case (uint32_t)USB_RTYPE_RECIPIENT_INTERFACE | ((uint32_t)USB_REQ_GET_INTERFACE << 8):
251 case (uint32_t)USB_RTYPE_RECIPIENT_INTERFACE | ((uint32_t)USB_REQ_SET_INTERFACE << 8):
252 /* All the above requests are not handled here, if you need them then
253 use the hook mechanism and provide handling.*/
254 default:
255 return false;
256 }
257}
258
259/*===========================================================================*/
260/* Driver exported functions. */
261/*===========================================================================*/
262
263/**
264 * @brief USB Driver initialization.
265 * @note This function is implicitly invoked by @p halInit(), there is
266 * no need to explicitly initialize the driver.
267 *
268 * @init
269 */
270void usbInit(void) {
271
272 usb_lld_init();
273}
274
275/**
276 * @brief Initializes the standard part of a @p USBDriver structure.
277 *
278 * @param[out] usbp pointer to the @p USBDriver object
279 *
280 * @init
281 */
283 unsigned i;
284
285 usbp->state = USB_STOP;
286 usbp->config = NULL;
287 for (i = 0; i < (unsigned)USB_MAX_ENDPOINTS; i++) {
288 usbp->in_params[i] = NULL;
289 usbp->out_params[i] = NULL;
290 }
291 usbp->transmitting = 0;
292 usbp->receiving = 0;
293}
294
295/**
296 * @brief Configures and activates the USB peripheral.
297 *
298 * @param[in] usbp pointer to the @p USBDriver object
299 * @param[in] config pointer to the @p USBConfig object
300 * @return The operation status.
301 *
302 * @api
303 */
304msg_t usbStart(USBDriver *usbp, const USBConfig *config) {
305 msg_t msg;
306 unsigned i;
307
308 osalDbgCheck((usbp != NULL) && (config != NULL));
309
310 osalSysLock();
311 osalDbgAssert((usbp->state == USB_STOP) || (usbp->state == USB_READY),
312 "invalid state");
313
314 usbp->config = config;
315 for (i = 0; i <= (unsigned)USB_MAX_ENDPOINTS; i++) {
316 usbp->epc[i] = NULL;
317 }
318
319#if defined(USB_LLD_ENHANCED_API)
320 msg = usb_lld_start(usbp);
321 if (msg == HAL_RET_SUCCESS) {
322 usbp->state = USB_READY;
323 }
324 else {
325 usbp->state = USB_STOP;
326 }
327#else
328 usb_lld_start(usbp);
329 usbp->state = USB_READY;
330 msg = HAL_RET_SUCCESS;
331#endif
332
334
335 return msg;
336}
337
338/**
339 * @brief Deactivates the USB peripheral.
340 *
341 * @param[in] usbp pointer to the @p USBDriver object
342 *
343 * @api
344 */
345void usbStop(USBDriver *usbp) {
346 unsigned i;
347
348 osalDbgCheck(usbp != NULL);
349
350 osalSysLock();
351
352 osalDbgAssert((usbp->state == USB_STOP) || (usbp->state == USB_READY) ||
353 (usbp->state == USB_SELECTED) || (usbp->state == USB_ACTIVE) ||
354 (usbp->state == USB_SUSPENDED),
355 "invalid state");
356
357 usb_lld_stop(usbp);
358 usbp->config = NULL;
359 usbp->state = USB_STOP;
360
361 /* Resetting all ongoing synchronous operations.*/
362 for (i = 0; i <= (unsigned)USB_MAX_ENDPOINTS; i++) {
363#if USB_USE_WAIT == TRUE
364 if (usbp->epc[i] != NULL) {
365 if (usbp->epc[i]->in_state != NULL) {
367 }
368 if (usbp->epc[i]->out_state != NULL) {
370 }
371 }
372#endif
373 usbp->epc[i] = NULL;
374 }
376
378}
379
380/**
381 * @brief Enables an endpoint.
382 * @details This function enables an endpoint, both IN and/or OUT directions
383 * depending on the configuration structure.
384 * @note This function must be invoked in response of a SET_CONFIGURATION
385 * or SET_INTERFACE message.
386 *
387 * @param[in] usbp pointer to the @p USBDriver object
388 * @param[in] ep endpoint number
389 * @param[in] epcp the endpoint configuration
390 *
391 * @iclass
392 */
394 const USBEndpointConfig *epcp) {
395
397 osalDbgCheck((usbp != NULL) && (epcp != NULL));
399 "invalid state");
400 osalDbgAssert(usbp->epc[ep] == NULL, "already initialized");
401
402 /* Logically enabling the endpoint in the USBDriver structure.*/
403 usbp->epc[ep] = epcp;
404
405 /* Clearing the state structures, custom fields as well.*/
406 if (epcp->in_state != NULL) {
407 memset(epcp->in_state, 0, sizeof(USBInEndpointState));
408 }
409 if (epcp->out_state != NULL) {
410 memset(epcp->out_state, 0, sizeof(USBOutEndpointState));
411 }
412
413 /* Low level endpoint activation.*/
414 usb_lld_init_endpoint(usbp, ep);
415}
416
417/**
418 * @brief Disables all the active endpoints.
419 * @details This function disables all the active endpoints except the
420 * endpoint zero.
421 * @note This function must be invoked in response of a SET_CONFIGURATION
422 * message with configuration number zero.
423 *
424 * @param[in] usbp pointer to the @p USBDriver object
425 *
426 * @iclass
427 */
429 unsigned i;
430
432 osalDbgCheck(usbp != NULL);
433 osalDbgAssert(usbp->state == USB_ACTIVE, "invalid state");
434
435 usbp->transmitting &= 1U;
436 usbp->receiving &= 1U;
437
438 for (i = 1; i <= (unsigned)USB_MAX_ENDPOINTS; i++) {
439#if USB_USE_WAIT == TRUE
440 /* Signaling the event to threads waiting on endpoints.*/
441 if (usbp->epc[i] != NULL) {
442 if (usbp->epc[i]->in_state != NULL) {
444 }
445 if (usbp->epc[i]->out_state != NULL) {
447 }
448 }
449#endif
450 usbp->epc[i] = NULL;
451 }
452
453 /* Low level endpoints deactivation.*/
455}
456
457/**
458 * @brief Starts a receive transaction on an OUT endpoint.
459 * @note This function is meant to be called from ISR context outside
460 * critical zones because there is a potentially slow operation
461 * inside.
462 * @note The transaction terminates when one of the following conditions
463 * has been met:
464 * - The specified amount of data has been received.
465 * - A short packet has been received.
466 * - A zero-lenght packet has been received.
467 * - The USB has been reset by host or the driver went into
468 * @p USB_SUSPENDED state.
469 * .
470 *
471 * @param[in] usbp pointer to the @p USBDriver object
472 * @param[in] ep endpoint number
473 * @param[out] buf buffer where to copy the received data
474 * @param[in] n transaction size. It is recommended a multiple of
475 * the packet size because the excess is discarded.
476 *
477 * @iclass
478 */
480 uint8_t *buf, size_t n) {
482
484 osalDbgCheck((usbp != NULL) && (ep <= (usbep_t)USB_MAX_ENDPOINTS));
485 osalDbgAssert(!usbGetReceiveStatusI(usbp, ep), "already receiving");
486
487 /* Marking the endpoint as active.*/
488 usbp->receiving |= (uint16_t)((unsigned)1U << (unsigned)ep);
489
490 /* Setting up the transfer.*/
491 /*lint -save -e661 [18.1] pclint is confused by the check on ep.*/
492 osp = usbp->epc[ep]->out_state;
493 /*lint -restore*/
494 osp->rxbuf = buf;
495 osp->rxsize = n;
496 osp->rxcnt = 0;
497#if USB_USE_WAIT == TRUE
498 osp->thread = NULL;
499#endif
500
501 /* Starting transfer.*/
502 usb_lld_start_out(usbp, ep);
503}
504
505/**
506 * @brief Starts a transmit transaction on an IN endpoint.
507 * @note This function is meant to be called from ISR context outside
508 * critical zones because there is a potentially slow operation
509 * inside.
510 *
511 * @param[in] usbp pointer to the @p USBDriver object
512 * @param[in] ep endpoint number
513 * @param[in] buf buffer where to fetch the data to be transmitted
514 * @param[in] n transaction size
515 *
516 * @iclass
517 */
519 const uint8_t *buf, size_t n) {
521
523 osalDbgCheck((usbp != NULL) && (ep <= (usbep_t)USB_MAX_ENDPOINTS));
524 osalDbgAssert(!usbGetTransmitStatusI(usbp, ep), "already transmitting");
525
526 /* Marking the endpoint as active.*/
527 usbp->transmitting |= (uint16_t)((unsigned)1U << (unsigned)ep);
528
529 /* Setting up the transfer.*/
530 /*lint -save -e661 [18.1] pclint is confused by the check on ep.*/
531 isp = usbp->epc[ep]->in_state;
532 /*lint -restore*/
533 isp->txbuf = buf;
534 isp->txsize = n;
535 isp->txcnt = 0;
536#if USB_USE_WAIT == TRUE
537 isp->thread = NULL;
538#endif
539
540 /* Starting transfer.*/
541 usb_lld_start_in(usbp, ep);
542}
543
544#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__)
545/**
546 * @brief Performs a receive transaction on an OUT endpoint.
547 * @note The transaction terminates when one of the following conditions
548 * has been met:
549 * - The specified amount of data has been received.
550 * - A short packet has been received.
551 * - A zero-lenght packet has been received.
552 * - The USB has been reset by host or the driver went into
553 * @p USB_SUSPENDED state.
554 *
555 * @param[in] usbp pointer to the @p USBDriver object
556 * @param[in] ep endpoint number
557 * @param[out] buf buffer where to copy the received data
558 * @param[in] n transaction size. It is recommended a multiple of
559 * the packet size because the excess is discarded.
560 *
561 * @return The received effective data size, it can be less than
562 * the amount specified.
563 * @retval MSG_RESET driver not in @p USB_ACTIVE state or the operation
564 * has been aborted by an USB reset or a transition to
565 * the @p USB_SUSPENDED state.
566 *
567 * @api
568 */
569msg_t usbReceive(USBDriver *usbp, usbep_t ep, uint8_t *buf, size_t n) {
570 msg_t msg;
571
572 osalSysLock();
573
574 if (usbGetDriverStateI(usbp) != USB_ACTIVE) {
576 return MSG_RESET;
577 }
578
579 usbStartReceiveI(usbp, ep, buf, n);
580 msg = osalThreadSuspendS(&usbp->epc[ep]->out_state->thread);
582
583 return msg;
584}
585
586/**
587 * @brief Performs a transmit transaction on an IN endpoint.
588 *
589 * @param[in] usbp pointer to the @p USBDriver object
590 * @param[in] ep endpoint number
591 * @param[in] buf buffer where to fetch the data to be transmitted
592 * @param[in] n transaction size
593 *
594 * @return The operation status.
595 * @retval MSG_OK operation performed successfully.
596 * @retval MSG_RESET driver not in @p USB_ACTIVE state or the operation
597 * has been aborted by an USB reset or a transition to
598 * the @p USB_SUSPENDED state.
599 *
600 * @api
601 */
602msg_t usbTransmit(USBDriver *usbp, usbep_t ep, const uint8_t *buf, size_t n) {
603 msg_t msg;
604
605 osalSysLock();
606
607 if (usbGetDriverStateI(usbp) != USB_ACTIVE) {
609 return MSG_RESET;
610 }
611
612 usbStartTransmitI(usbp, ep, buf, n);
613 msg = osalThreadSuspendS(&usbp->epc[ep]->in_state->thread);
615
616 return msg;
617}
618#endif /* USB_USE_WAIT == TRUE */
619
620/**
621 * @brief Stalls an OUT endpoint.
622 *
623 * @param[in] usbp pointer to the @p USBDriver object
624 * @param[in] ep endpoint number
625 *
626 * @return The operation status.
627 * @retval false Endpoint stalled.
628 * @retval true Endpoint busy, not stalled.
629 *
630 * @iclass
631 */
633
635 osalDbgCheck(usbp != NULL);
636
637 if (usbGetReceiveStatusI(usbp, ep)) {
638 return true;
639 }
640
641 usb_lld_stall_out(usbp, ep);
642 return false;
643}
644
645/**
646 * @brief Stalls an IN endpoint.
647 *
648 * @param[in] usbp pointer to the @p USBDriver object
649 * @param[in] ep endpoint number
650 *
651 * @return The operation status.
652 * @retval false Endpoint stalled.
653 * @retval true Endpoint busy, not stalled.
654 *
655 * @iclass
656 */
658
660 osalDbgCheck(usbp != NULL);
661
662 if (usbGetTransmitStatusI(usbp, ep)) {
663 return true;
664 }
665
666 usb_lld_stall_in(usbp, ep);
667 return false;
668}
669
670/**
671 * @brief Host wake-up procedure.
672 * @note It is silently ignored if the USB device is not in the
673 * @p USB_SUSPENDED state.
674 *
675 * @param[in] usbp pointer to the @p USBDriver object
676 *
677 * @api
678 */
680
681 if (usbp->state == USB_SUSPENDED) {
682 /* Starting host wakeup procedure.*/
684 }
685}
686
687/**
688 * @brief USB reset routine.
689 * @details This function must be invoked when an USB bus reset condition is
690 * detected.
691 *
692 * @param[in] usbp pointer to the @p USBDriver object
693 *
694 * @notapi
695 */
697 unsigned i;
698
699 /* State transition.*/
700 usbp->state = USB_READY;
701
702 /* Resetting internal state.*/
703 usbp->status = 0;
704 usbp->address = 0;
705 usbp->configuration = 0;
706 usbp->transmitting = 0;
707 usbp->receiving = 0;
708
709 /* Invalidates all endpoints into the USBDriver structure.*/
710 for (i = 0; i <= (unsigned)USB_MAX_ENDPOINTS; i++) {
711#if USB_USE_WAIT == TRUE
712 /* Signaling the event to threads waiting on endpoints.*/
713 if (usbp->epc[i] != NULL) {
715 if (usbp->epc[i]->in_state != NULL) {
717 }
718 if (usbp->epc[i]->out_state != NULL) {
720 }
722 }
723#endif
724 usbp->epc[i] = NULL;
725 }
726
727 /* EP0 state machine initialization.*/
729
730 /* Low level reset.*/
731 usb_lld_reset(usbp);
732
733 /* Notification of reset event.*/
735}
736
737/**
738 * @brief USB suspend routine.
739 * @details This function must be invoked when an USB bus suspend condition is
740 * detected.
741 *
742 * @param[in] usbp pointer to the @p USBDriver object
743 *
744 * @notapi
745 */
747
748 /* It could happen that multiple suspend events are triggered.*/
749 if (usbp->state != USB_SUSPENDED) {
750
751 /* State transition, saving the current state.*/
752 usbp->saved_state = usbp->state;
753 usbp->state = USB_SUSPENDED;
754
755 /* Notification of suspend event.*/
757
758 /* Terminating all pending transactions.*/
759 usbp->transmitting = 0;
760 usbp->receiving = 0;
761
762 /* Signaling the event to threads waiting on endpoints.*/
763 #if USB_USE_WAIT == TRUE
764 {
765 unsigned i;
766
767 for (i = 0; i <= (unsigned)USB_MAX_ENDPOINTS; i++) {
768 if (usbp->epc[i] != NULL) {
770 if (usbp->epc[i]->in_state != NULL) {
772 }
773 if (usbp->epc[i]->out_state != NULL) {
775 }
777 }
778 }
779 }
780 #endif
781 }
782}
783
784/**
785 * @brief USB wake-up routine.
786 * @details This function must be invoked when an USB bus wake-up condition is
787 * detected.
788 *
789 * @param[in] usbp pointer to the @p USBDriver object
790 *
791 * @notapi
792 */
794
795 /* It could happen that multiple waakeup events are triggered.*/
796 if (usbp->state == USB_SUSPENDED) {
797
798 /* State transition, returning to the previous state.*/
799 usbp->state = usbp->saved_state;
800
801 /* Notification of suspend event.*/
803 }
804}
805
806/**
807 * @brief Default EP0 SETUP callback.
808 * @details This function is used by the low level driver as default handler
809 * for EP0 SETUP events.
810 *
811 * @param[in] usbp pointer to the @p USBDriver object
812 * @param[in] ep endpoint number, always zero
813 *
814 * @notapi
815 */
817 size_t max;
818
819 /* Is the EP0 state machine in the correct state for handling setup
820 packets?*/
821 if (usbp->ep0state != USB_EP0_STP_WAITING) {
822 /* This is unexpected could require handling with a warning event.*/
823 /* CHTODO: handling here.*/
824
825 /* Resetting the EP0 state machine and going ahead.*/
827 }
828
829 /* Reading the setup data into the driver buffer.*/
830 usbReadSetup(usbp, ep, usbp->setup);
831
832 /* First verify if the application has an handler installed for this
833 request.*/
834 /*lint -save -e9007 [13.5] No side effects, it is intentional.*/
835 if ((usbp->config->requests_hook_cb == NULL) ||
836 !(usbp->config->requests_hook_cb(usbp))) {
837 /*lint -restore*/
838 /* Invoking the default handler, if this fails then stalls the
839 endpoint zero as error.*/
840 /*lint -save -e9007 [13.5] No side effects, it is intentional.*/
841 if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) != USB_RTYPE_TYPE_STD) ||
842 !default_handler(usbp)) {
843 /*lint -restore*/
844 /* Error response, the state machine goes into an error state, the low
845 level layer will have to reset it to USB_EP0_WAITING_SETUP after
846 receiving a SETUP packet.*/
847 usb_lld_stall_in(usbp, 0);
848 usb_lld_stall_out(usbp, 0);
850 usbp->ep0state = USB_EP0_ERROR;
851 return;
852 }
853 }
854#if (USB_SET_ADDRESS_ACK_HANDLING == USB_SET_ADDRESS_ACK_HW)
855 if (usbp->setup[1] == USB_REQ_SET_ADDRESS) {
856 /* Zero-length packet sent by hardware */
857 return;
858 }
859#endif
860 /* Transfer preparation. The request handler must have populated
861 correctly the fields ep0next, ep0n and ep0endcb using the macro
862 usbSetupTransfer().*/
863 max = (size_t)get_hword(&usbp->setup[6]);
864 /* The transfer size cannot exceed the specified amount.*/
865 if (usbp->ep0n > max) {
866 usbp->ep0n = max;
867 }
868 if ((usbp->setup[0] & USB_RTYPE_DIR_MASK) == USB_RTYPE_DIR_DEV2HOST) {
869 /* IN phase.*/
870 if (usbp->ep0n != 0U) {
871 /* Starts the transmit phase.*/
872 usbp->ep0state = USB_EP0_IN_TX;
874 usbStartTransmitI(usbp, 0, usbp->ep0next, usbp->ep0n);
876 }
877 else {
878 /* No transmission phase, directly receiving the zero sized status
879 packet.*/
881#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
883 usbStartReceiveI(usbp, 0, NULL, 0);
885#else
886 usb_lld_end_setup(usbp, ep);
887#endif
888 }
889 }
890 else {
891 /* OUT phase.*/
892 if (usbp->ep0n != 0U) {
893 /* Starts the receive phase.*/
894 usbp->ep0state = USB_EP0_OUT_RX;
896 usbStartReceiveI(usbp, 0, (uint8_t *)usbp->ep0next, usbp->ep0n);
898 }
899 else {
900 /* No receive phase, directly sending the zero sized status
901 packet.*/
903#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
905 usbStartTransmitI(usbp, 0, NULL, 0);
907#else
908 usb_lld_end_setup(usbp, ep);
909#endif
910 }
911 }
912}
913
914/**
915 * @brief Default EP0 IN callback.
916 * @details This function is used by the low level driver as default handler
917 * for EP0 IN events.
918 *
919 * @param[in] usbp pointer to the @p USBDriver object
920 * @param[in] ep endpoint number, always zero
921 *
922 * @notapi
923 */
924void _usb_ep0in(USBDriver *usbp, usbep_t ep) {
925 size_t max;
926
927 (void)ep;
928 switch (usbp->ep0state) {
929 case USB_EP0_IN_TX:
930 max = (size_t)get_hword(&usbp->setup[6]);
931 /* If the transmitted size is less than the requested size and it is a
932 multiple of the maximum packet size then a zero size packet must be
933 transmitted.*/
934 if ((usbp->ep0n < max) &&
935 ((usbp->ep0n % usbp->epc[0]->in_maxsize) == 0U)) {
937 usbStartTransmitI(usbp, 0, NULL, 0);
940 return;
941 }
942 /* Falls through.*/
944 /* Transmit phase over, receiving the zero sized status packet.*/
946#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
948 usbStartReceiveI(usbp, 0, NULL, 0);
950#else
951 usb_lld_end_setup(usbp, ep);
952#endif
953 return;
955 /* Status packet sent, invoking the callback if defined.*/
956 if (usbp->ep0endcb != NULL) {
957 usbp->ep0endcb(usbp);
958 }
960 return;
963 case USB_EP0_OUT_RX:
964 /* All the above are invalid states in the IN phase.*/
965 osalDbgAssert(false, "EP0 state machine error");
966 /* Falls through.*/
967 case USB_EP0_ERROR:
968 /* Error response, the state machine goes into an error state, the low
969 level layer will have to reset it to USB_EP0_WAITING_SETUP after
970 receiving a SETUP packet.*/
971 usb_lld_stall_in(usbp, 0);
972 usb_lld_stall_out(usbp, 0);
974 usbp->ep0state = USB_EP0_ERROR;
975 return;
976 default:
977 osalDbgAssert(false, "EP0 state machine invalid state");
978 }
979}
980
981/**
982 * @brief Default EP0 OUT callback.
983 * @details This function is used by the low level driver as default handler
984 * for EP0 OUT events.
985 *
986 * @param[in] usbp pointer to the @p USBDriver object
987 * @param[in] ep endpoint number, always zero
988 *
989 * @notapi
990 */
992
993 (void)ep;
994 switch (usbp->ep0state) {
995 case USB_EP0_OUT_RX:
996 /* Receive phase over, sending the zero sized status packet.*/
998#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
1000 usbStartTransmitI(usbp, 0, NULL, 0);
1002#else
1003 usb_lld_end_setup(usbp, ep);
1004#endif
1005 return;
1007 /* Status packet received, it must be zero sized, invoking the callback
1008 if defined.*/
1009#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
1010 if (usbGetReceiveTransactionSizeX(usbp, 0) != 0U) {
1011 break;
1012 }
1013#endif
1014 if (usbp->ep0endcb != NULL) {
1015 usbp->ep0endcb(usbp);
1016 }
1018 return;
1020 case USB_EP0_IN_TX:
1023 /* All the above are invalid states in the IN phase.*/
1024 osalDbgAssert(false, "EP0 state machine error");
1025 /* Falls through.*/
1026 case USB_EP0_ERROR:
1027 /* Error response, the state machine goes into an error state, the low
1028 level layer will have to reset it to USB_EP0_WAITING_SETUP after
1029 receiving a SETUP packet.*/
1030 usb_lld_stall_in(usbp, 0);
1031 usb_lld_stall_out(usbp, 0);
1033 usbp->ep0state = USB_EP0_ERROR;
1034 return;
1035 default:
1036 osalDbgAssert(false, "EP0 state machine invalid state");
1037 }
1038}
1039
1040#endif /* HAL_USE_USB == TRUE */
1041
1042/** @} */
#define HAL_RET_SUCCESS
Definition hal.h:93
static void osalSysLock(void)
Enters a critical zone from thread context.
Definition osal.h:601
static void osalSysUnlock(void)
Leaves a critical zone from thread context.
Definition osal.h:611
static void osalSysLockFromISR(void)
Enters a critical zone from ISR context.
Definition osal.h:621
msg_t osalThreadSuspendS(thread_reference_t *trp)
Sends the current thread sleeping and sets a reference variable.
Definition osal.c:183
void osalOsRescheduleS(void)
Checks if a reschedule is required and performs it.
Definition osal.c:119
void osalThreadResumeI(thread_reference_t *trp, msg_t msg)
Wakes up a thread waiting on a thread reference object.
Definition osal.c:227
static void osalSysUnlockFromISR(void)
Leaves a critical zone from ISR context.
Definition osal.h:631
#define osalDbgAssert(c, remark)
Condition assertion.
Definition osal.h:264
#define osalDbgCheck(c)
Function parameters check.
Definition osal.h:284
#define osalDbgCheckClassI()
I-Class state check.
Definition osal.h:298
#define USB_RTYPE_RECIPIENT_MASK
Definition hal_usb.h:45
static void set_address(USBDriver *usbp)
SET ADDRESS transaction callback.
Definition hal_usb.c:64
#define usbGetDriverStateI(usbp)
Returns the driver state.
Definition hal_usb.h:391
static bool default_handler(USBDriver *usbp)
Standard requests handler.
Definition hal_usb.c:84
void usb_lld_stop(USBDriver *usbp)
Deactivates the USB peripheral.
void usb_lld_set_address(USBDriver *usbp)
Sets the USB address.
#define USB_FEATURE_ENDPOINT_HALT
Definition hal_usb.h:73
#define USB_REQ_GET_INTERFACE
Definition hal_usb.h:59
uint8_t usbep_t
Type of an endpoint identifier.
Definition hal_usb.h:264
void usb_lld_clear_in(USBDriver *usbp, usbep_t ep)
Brings an IN endpoint in the active state.
void _usb_suspend(USBDriver *usbp)
USB suspend routine.
Definition hal_usb.c:746
static const uint8_t zero_status[]
Definition hal_usb.c:43
void usbDisableEndpointsI(USBDriver *usbp)
Disables all the active endpoints.
Definition hal_usb.c:428
void usb_lld_init(void)
Low level USB driver initialization.
Definition hal_usb_lld.c:99
#define USB_REQ_SET_INTERFACE
Definition hal_usb.h:60
msg_t usbTransmit(USBDriver *usbp, usbep_t ep, const uint8_t *buf, size_t n)
Performs a transmit transaction on an IN endpoint.
Definition hal_usb.c:602
void usbStartReceiveI(USBDriver *usbp, usbep_t ep, uint8_t *buf, size_t n)
Starts a receive transaction on an OUT endpoint.
Definition hal_usb.c:479
#define USB_REQ_SET_FEATURE
Definition hal_usb.h:53
void _usb_wakeup(USBDriver *usbp)
USB wake-up routine.
Definition hal_usb.c:793
#define usbGetReceiveTransactionSizeX(usbp, ep)
Returns the exact size of a receive transaction.
Definition hal_usb.h:461
#define USB_REQ_CLEAR_FEATURE
Definition hal_usb.h:52
void usbInit(void)
USB Driver initialization.
Definition hal_usb.c:270
#define USB_RTYPE_TYPE_STD
Definition hal_usb.h:41
bool usbStallTransmitI(USBDriver *usbp, usbep_t ep)
Stalls an IN endpoint.
Definition hal_usb.c:657
msg_t usbReceive(USBDriver *usbp, usbep_t ep, uint8_t *buf, size_t n)
Performs a receive transaction on an OUT endpoint.
Definition hal_usb.c:569
void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep)
Enables an endpoint.
usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep)
Returns the status of an OUT endpoint.
void _usb_ep0setup(USBDriver *usbp, usbep_t ep)
Default EP0 SETUP callback.
Definition hal_usb.c:816
void usbStartTransmitI(USBDriver *usbp, usbep_t ep, const uint8_t *buf, size_t n)
Starts a transmit transaction on an IN endpoint.
Definition hal_usb.c:518
msg_t usbStart(USBDriver *usbp, const USBConfig *config)
Configures and activates the USB peripheral.
Definition hal_usb.c:304
#define USB_RTYPE_DIR_DEV2HOST
Definition hal_usb.h:39
#define USB_REQ_SET_CONFIGURATION
Definition hal_usb.h:58
static uint16_t get_hword(uint8_t *p)
Definition hal_usb.c:51
#define USB_RTYPE_RECIPIENT_ENDPOINT
Definition hal_usb.h:48
void usbInitEndpointI(USBDriver *usbp, usbep_t ep, const USBEndpointConfig *epcp)
Enables an endpoint.
Definition hal_usb.c:393
#define USB_REQ_SYNCH_FRAME
Definition hal_usb.h:61
#define usbGetReceiveStatusI(usbp, ep)
Returns the status of an OUT endpoint.
Definition hal_usb.h:446
#define usbSetupTransfer(usbp, buf, n, endcb)
Request transfer setup.
Definition hal_usb.h:476
#define USB_MAX_ENDPOINTS
Maximum endpoint address.
Definition hal_usb_lld.h:37
void usb_lld_reset(USBDriver *usbp)
USB low level reset routine.
#define usbGetTransmitStatusI(usbp, ep)
Returns the status of an IN endpoint.
Definition hal_usb.h:432
usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep)
Returns the status of an IN endpoint.
void usb_lld_disable_endpoints(USBDriver *usbp)
Disables all the active endpoints except the endpoint zero.
#define USB_REQ_GET_STATUS
Definition hal_usb.h:51
static const uint8_t active_status[]
Definition hal_usb.c:44
void usb_lld_start_out(USBDriver *usbp, usbep_t ep)
Starts a receive operation on an OUT endpoint.
#define usbReadSetup(usbp, ep, buf)
Reads a setup packet from the dedicated packet buffer.
Definition hal_usb.h:496
#define usb_lld_wakeup_host(usbp)
Start of host wake-up procedure.
void usb_lld_start(USBDriver *usbp)
Configures and activates the USB peripheral.
#define USB_REQ_SET_ADDRESS
Definition hal_usb.h:54
void usb_lld_start_in(USBDriver *usbp, usbep_t ep)
Starts a transmit operation on an IN endpoint.
void usbStop(USBDriver *usbp)
Deactivates the USB peripheral.
Definition hal_usb.c:345
#define USB_FEATURE_DEVICE_REMOTE_WAKEUP
Definition hal_usb.h:74
static const uint8_t halted_status[]
Definition hal_usb.c:45
void _usb_ep0in(USBDriver *usbp, usbep_t ep)
Default EP0 IN callback.
Definition hal_usb.c:924
#define _usb_isr_invoke_event_cb(usbp, evt)
Common ISR code, usb event callback.
Definition hal_usb.h:511
void _usb_reset(USBDriver *usbp)
USB reset routine.
Definition hal_usb.c:696
#define USB_RTYPE_DIR_MASK
Definition hal_usb.h:37
void usb_lld_clear_out(USBDriver *usbp, usbep_t ep)
Brings an OUT endpoint in the active state.
void usbWakeupHost(USBDriver *usbp)
Host wake-up procedure.
Definition hal_usb.c:679
#define USB_RTYPE_RECIPIENT_INTERFACE
Definition hal_usb.h:47
void usb_lld_stall_out(USBDriver *usbp, usbep_t ep)
Brings an OUT endpoint in the stalled state.
void usb_lld_stall_in(USBDriver *usbp, usbep_t ep)
Brings an IN endpoint in the stalled state.
#define USB_REQ_GET_DESCRIPTOR
Definition hal_usb.h:55
void usbObjectInit(USBDriver *usbp)
Initializes the standard part of a USBDriver structure.
Definition hal_usb.c:282
#define USB_REQ_GET_CONFIGURATION
Definition hal_usb.h:57
#define USB_REQ_SET_DESCRIPTOR
Definition hal_usb.h:56
bool usbStallReceiveI(USBDriver *usbp, usbep_t ep)
Stalls an OUT endpoint.
Definition hal_usb.c:632
#define USB_RTYPE_TYPE_MASK
Definition hal_usb.h:40
void _usb_ep0out(USBDriver *usbp, usbep_t ep)
Default EP0 OUT callback.
Definition hal_usb.c:991
#define USB_RTYPE_RECIPIENT_DEVICE
Definition hal_usb.h:46
@ EP_STATUS_ACTIVE
Definition hal_usb.h:284
@ EP_STATUS_DISABLED
Definition hal_usb.h:282
@ EP_STATUS_STALLED
Definition hal_usb.h:283
@ USB_EP0_IN_WAITING_TX0
Definition hal_usb.h:293
@ USB_EP0_IN_SENDING_STS
Definition hal_usb.h:294
@ USB_EP0_OUT_WAITING_STS
Definition hal_usb.h:295
@ USB_EP0_ERROR
Definition hal_usb.h:297
@ USB_EP0_OUT_RX
Definition hal_usb.h:296
@ USB_EP0_STP_WAITING
Definition hal_usb.h:291
@ USB_EP0_IN_TX
Definition hal_usb.h:292
@ USB_EVENT_ADDRESS
Definition hal_usb.h:305
@ USB_EVENT_STALLED
Definition hal_usb.h:310
@ USB_EVENT_CONFIGURED
Definition hal_usb.h:306
@ USB_EVENT_SUSPEND
Definition hal_usb.h:308
@ USB_EVENT_RESET
Definition hal_usb.h:304
@ USB_EVENT_UNCONFIGURED
Definition hal_usb.h:307
@ USB_EVENT_WAKEUP
Definition hal_usb.h:309
@ USB_SUSPENDED
Definition hal_usb.h:275
@ USB_SELECTED
Definition hal_usb.h:273
@ USB_STOP
Definition hal_usb.h:271
@ USB_ACTIVE
Definition hal_usb.h:274
@ USB_READY
Definition hal_usb.h:272
int32_t msg_t
Definition chearly.h:88
#define MSG_RESET
Wakeup caused by a reset condition.
Definition chschd.h:42
HAL subsystem header.
Type of an USB driver configuration structure.
usbgetdescriptor_t get_descriptor_cb
Device GET_DESCRIPTOR request callback.
usbreqhandler_t requests_hook_cb
Requests hook callback.
Type of an USB descriptor.
Definition hal_usb.h:316
const uint8_t * ud_string
Pointer to the descriptor.
Definition hal_usb.h:324
size_t ud_size
Descriptor size in unicode characters.
Definition hal_usb.h:320
Structure representing an USB driver.
uint8_t setup[8]
Setup packet buffer.
size_t ep0n
Number of bytes yet to be transferred through endpoint 0.
uint16_t status
Current USB device status.
void * out_params[USB_MAX_ENDPOINTS]
Fields available to user, it can be used to associate an application-defined handler to an OUT endpoi...
void * in_params[USB_MAX_ENDPOINTS]
Fields available to user, it can be used to associate an application-defined handler to an IN endpoin...
uint8_t configuration
Current USB device configuration.
usbstate_t saved_state
State of the driver when a suspend happened.
usbep0state_t ep0state
Endpoint 0 state.
const USBEndpointConfig * epc[USB_MAX_ENDPOINTS+1]
Active endpoints configurations.
usbstate_t state
Driver state.
uint16_t receiving
Bit map of the receiving OUT endpoints.
usbcallback_t ep0endcb
Endpoint 0 end transaction callback.
const USBConfig * config
Current configuration data.
uint8_t address
Assigned USB address.
uint16_t transmitting
Bit map of the transmitting IN endpoints.
uint8_t * ep0next
Next position in the buffer to be transferred through endpoint 0.
Type of an USB endpoint configuration structure.
USBOutEndpointState * out_state
USBEndpointState associated to the OUT endpoint.
uint16_t in_maxsize
IN endpoint maximum packet size.
USBInEndpointState * in_state
USBEndpointState associated to the IN endpoint.
Type of an IN endpoint state structure.
Definition hal_usb_lld.h:83
const uint8_t * txbuf
Pointer to the transmission linear buffer.
Definition hal_usb_lld.h:95
thread_reference_t thread
Waiting thread.
size_t txsize
Requested transmit transfer size.
Definition hal_usb_lld.h:87
size_t txcnt
Transmitted bytes so far.
Definition hal_usb_lld.h:91
Type of an OUT endpoint state structure.
size_t rxsize
Requested receive transfer size.
uint8_t * rxbuf
Pointer to the receive linear buffer.
thread_reference_t thread
Waiting thread.
size_t rxcnt
Received bytes so far.