1 /*
2  * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #pragma once
8 
9 #ifdef __cplusplus
10 extern "C" {
11 #endif
12 
13 #include <stdint.h>
14 #include <stdbool.h>
15 #include <sys/queue.h>
16 #include "esp_err.h"
17 #include "usb_private.h"
18 #include "usb/usb_types_ch9.h"
19 
20 // ------------------------------------------------- Macros & Types ----------------------------------------------------
21 
22 // ----------------------- States --------------------------
23 
24 /**
25  * @brief States of the HCD port
26  *
27  * @note The port can be thought of as an abstraction of the Root Hub that contains
28  *       a single port.
29  * @note These states roughly match the port states outlined in 11.5.1 of the
30  *       USB2.0 specification.
31  */
32 typedef enum {
33     HCD_PORT_STATE_NOT_POWERED,     /**< The port is not powered */
34     HCD_PORT_STATE_DISCONNECTED,    /**< The port is powered but no device is connected */
35     HCD_PORT_STATE_DISABLED,        /**< A device has connected to the port but has not been reset. SOF/keep alive are not being sent */
36     HCD_PORT_STATE_RESETTING,       /**< The port is issuing a reset condition */
37     HCD_PORT_STATE_SUSPENDED,       /**< The port has been suspended. */
38     HCD_PORT_STATE_RESUMING,        /**< The port is issuing a resume condition */
39     HCD_PORT_STATE_ENABLED,         /**< The port has been enabled. SOF/keep alive are being sent */
40     HCD_PORT_STATE_RECOVERY,        /**< Port needs to be recovered from a fatal error (port error, overcurrent, or sudden disconnection) */
41 } hcd_port_state_t;
42 
43 /**
44  * @brief States of an HCD pipe
45  *
46  * Active:
47  *  - Pipe is able to transmit data. URBs can be enqueued.
48  *  - Even if pipe has no URBs enqueued, it can still be in the active state.
49  * Halted:
50  *  - An error has occurred on the pipe. URBs will no longer be executed.
51  *  - Halt should be cleared using the HCD_PIPE_CMD_CLEAR command
52  */
53 typedef enum {
54     HCD_PIPE_STATE_ACTIVE,          /**< The pipe is active */
55     HCD_PIPE_STATE_HALTED,          /**< The pipe is halted */
56 } hcd_pipe_state_t;
57 
58 // ----------------------- Events --------------------------
59 
60 /**
61  * @brief HCD port events
62  *
63  * On receiving a port event, hcd_port_handle_event() should be called to handle that event
64  */
65 typedef enum {
66     HCD_PORT_EVENT_NONE,            /**< No event has occurred. Or the previous event is no longer valid */
67     HCD_PORT_EVENT_CONNECTION,      /**< A device has been connected to the port */
68     HCD_PORT_EVENT_DISCONNECTION,   /**< A device disconnection has been detected */
69     HCD_PORT_EVENT_ERROR,           /**< A port error has been detected. Port is now HCD_PORT_STATE_RECOVERY  */
70     HCD_PORT_EVENT_OVERCURRENT,     /**< Overcurrent detected on the port. Port is now HCD_PORT_STATE_RECOVERY */
71 } hcd_port_event_t;
72 
73 /**
74  * @brief HCD pipe events
75  *
76  * @note Pipe error events will put the pipe into the HCD_PIPE_STATE_HALTED state
77  */
78 typedef enum {
79     HCD_PIPE_EVENT_NONE,                    /**< The pipe has no events (used to indicate no events when polling) */
80     HCD_PIPE_EVENT_URB_DONE,                /**< The pipe has completed an URB. The URB can be dequeued */
81     HCD_PIPE_EVENT_ERROR_XFER,              /**< Excessive (three consecutive) transaction errors (e.g., no ACK, bad CRC etc) */
82     HCD_PIPE_EVENT_ERROR_URB_NOT_AVAIL,     /**< URB was not available */
83     HCD_PIPE_EVENT_ERROR_OVERFLOW,          /**< Received more data than requested. Usually a Packet babble error
84                                                  (i.e., an IN packet has exceeded the endpoint's MPS) */
85     HCD_PIPE_EVENT_ERROR_STALL,             /**< Pipe received a STALL response received */
86 } hcd_pipe_event_t;
87 
88 // ---------------------- Commands -------------------------
89 
90 /**
91  * @brief HCD port commands
92  */
93 typedef enum {
94     HCD_PORT_CMD_POWER_ON,          /**< Power ON the port */
95     HCD_PORT_CMD_POWER_OFF,         /**< Power OFF the port. If the port is enabled, this will cause a HCD_PORT_EVENT_SUDDEN_DISCONN event.
96                                          If the port is disabled, this will cause a HCD_PORT_EVENT_DISCONNECTION event. */
97     HCD_PORT_CMD_RESET,             /**< Issue a reset on the port */
98     HCD_PORT_CMD_SUSPEND,           /**< Suspend the port. All pipes must be halted */
99     HCD_PORT_CMD_RESUME,            /**< Resume the port */
100     HCD_PORT_CMD_DISABLE,           /**< Disable the port (stops the SOFs or keep alive). All pipes must be halted. */
101 } hcd_port_cmd_t;
102 
103 /**
104  * @brief HCD pipe commands
105  *
106  * The pipe commands represent the list of pipe manipulations outlined in 10.5.2.2. of USB2.0 specification.
107  */
108 typedef enum {
109     HCD_PIPE_CMD_HALT,              /**< Halt an active pipe. The currently executing URB will be canceled. Enqueued URBs are left untouched */
110     HCD_PIPE_CMD_FLUSH,             /**< Can only be called when halted. Will cause all enqueued URBs to be canceled */
111     HCD_PIPE_CMD_CLEAR,             /**< Causes a halted pipe to become active again. Any enqueued URBs will being executing.*/
112 } hcd_pipe_cmd_t;
113 
114 // -------------------- Object Types -----------------------
115 
116 /**
117  * @brief Port handle type
118  */
119 typedef void * hcd_port_handle_t;
120 
121 /**
122  * @brief Pipe handle type
123  */
124 typedef void * hcd_pipe_handle_t;
125 
126 /**
127  * @brief Port event callback type
128  *
129  * This callback is run when a port event occurs
130  */
131 typedef bool (*hcd_port_callback_t)(hcd_port_handle_t port_hdl, hcd_port_event_t port_event, void *user_arg, bool in_isr);
132 
133 /**
134  * @brief Pipe event callback
135  *
136  * This callback is run when a pipe event occurs
137  */
138 typedef bool (*hcd_pipe_callback_t)(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr);
139 
140 typedef enum {
141     HCD_PORT_FIFO_BIAS_BALANCED,    /**< Balanced FIFO sizing for RX, Non-periodic TX, and periodic TX */
142     HCD_PORT_FIFO_BIAS_RX,          /**< Bias towards a large RX FIFO */
143     HCD_PORT_FIFO_BIAS_PTX,         /**< Bias towards periodic TX FIFO */
144 } hcd_port_fifo_bias_t;
145 
146 /**
147  * @brief HCD configuration structure
148  */
149 typedef struct {
150     int intr_flags;                         /**< Interrupt flags for HCD interrupt */
151 } hcd_config_t;
152 
153 /**
154  * @brief Port configuration structure
155  */
156 typedef struct {
157     hcd_port_fifo_bias_t fifo_bias;         /**< HCD port internal FIFO biasing */
158     hcd_port_callback_t callback;           /**< HCD port event callback */
159     void *callback_arg;                     /**< User argument for HCD port callback */
160     void *context;                          /**< Context variable used to associate the port with upper layer object */
161 } hcd_port_config_t;
162 
163 /**
164  * @brief Pipe configuration structure
165  *
166  * @note The callback can be set to NULL if no callback is required (e.g., using HCD in a polling manner).
167  */
168 typedef struct {
169     hcd_pipe_callback_t callback;           /**< HCD pipe event ISR callback */
170     void *callback_arg;                     /**< User argument for HCD pipe callback */
171     void *context;                          /**< Context variable used to associate the pipe with upper layer object */
172     const usb_ep_desc_t *ep_desc;           /**< Pointer to endpoint descriptor of the pipe */
173     usb_speed_t dev_speed;                  /**< Speed of the device */
174     uint8_t dev_addr;                       /**< Device address of the pipe */
175 } hcd_pipe_config_t;
176 
177 // --------------------------------------------- Host Controller Driver ------------------------------------------------
178 
179 /**
180  * @brief Installs the Host Controller Driver
181  *
182  * - Allocates memory and interrupt resources for the HCD and underlying ports
183  *
184  * @note This function must be called before any other HCD function is called
185  * @note Before calling this function, the Host Controller must already be un-clock gated and reset. The USB PHY
186  *       (internal or external, and associated GPIOs) must already be configured.
187  *
188  * @param config HCD configuration
189  * @retval ESP_OK: HCD successfully installed
190  * @retval ESP_ERR_NO_MEM: Insufficient memory
191  * @retval ESP_ERR_INVALID_STATE: HCD is already installed
192  * @retval ESP_ERR_NOT_FOUND: HCD could not allocate interrupt
193  * @retval ESP_ERR_INVALID_ARG: Arguments are invalid
194  */
195 esp_err_t hcd_install(const hcd_config_t *config);
196 
197 /**
198  * @brief Uninstalls the HCD
199  *
200  * Before uninstalling the HCD, the following conditions should be met:
201  * - All ports must be uninitialized, all pipes freed
202  *
203  * @note This function will simply free the resources used by the HCD. The underlying Host Controller and USB PHY will
204  *       not be disabled.
205  *
206  * @retval ESP_OK: HCD successfully uninstalled
207  * @retval ESP_ERR_INVALID_STATE: HCD is not in the right condition to be uninstalled
208  */
209 esp_err_t hcd_uninstall(void);
210 
211 // ---------------------------------------------------- HCD Port -------------------------------------------------------
212 
213 /**
214  * @brief Initialize a particular port of the HCD
215  *
216  * After a port is initialized, it will be put into the HCD_PORT_STATE_NOT_POWERED state.
217  *
218  * @note The host controller only has one port, thus the only valid port_number is 1
219  *
220  * @param[in] port_number Port number
221  * @param[in] port_config Port configuration
222  * @param[out] port_hdl Port handle
223  * @retval ESP_OK: Port enabled
224  * @retval ESP_ERR_NO_MEM: Insufficient memory
225  * @retval ESP_ERR_INVALID_STATE: The port is already enabled
226  * @retval ESP_ERR_NOT_FOUND: Port number not found
227  * @retval ESP_ERR_INVALID_ARG: Arguments are invalid
228  */
229 esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, hcd_port_handle_t *port_hdl);
230 
231 /**
232  * @brief Deinitialize a particular port
233  *
234  * The port must be placed in the HCD_PORT_STATE_NOT_POWERED or HCD_PORT_STATE_RECOVERY state before it can be
235  * deinitialized.
236  *
237  * @param port_hdl Port handle
238  * @retval ESP_OK: Port disabled
239  * @retval ESP_ERR_INVALID_STATE: The port is not in a condition to be disabled (not unpowered)
240  */
241 esp_err_t hcd_port_deinit(hcd_port_handle_t port_hdl);
242 
243 /**
244  * @brief Execute a port command
245  *
246  * Call this function to manipulate a port (e.g., powering it ON, sending a reset etc). The following conditions
247  * must be met when calling this function:
248  * - The port is in the correct state for the command (e.g., port must be suspended in order to use the resume command)
249  * - The port does not have any pending events
250  *
251  * @note This function is internally protected by a mutex. If multiple threads call this function, this function will
252  *       can block.
253  * @note The function can block
254  * @note For some of the commands that involve a blocking delay (e.g., reset and resume), if the port's state changes
255  *       unexpectedly (e.g., a disconnect during a resume), this function will return ESP_ERR_INVALID_RESPONSE.
256  *
257  * @param port_hdl Port handle
258  * @param command Command for the HCD port
259  * @retval ESP_OK: Command executed successfully
260  * @retval ESP_ERR_INVALID_STATE: Conditions have not been met to call this function
261  * @retval ESP_ERR_INVALID_RESPONSE: The command is no longer valid due to a change in the port's state
262  */
263 esp_err_t hcd_port_command(hcd_port_handle_t port_hdl, hcd_port_cmd_t command);
264 
265 /**
266  * @brief Get the port's current state
267  *
268  * @param port_hdl Port handle
269  * @return hcd_port_state_t Current port state
270  */
271 hcd_port_state_t hcd_port_get_state(hcd_port_handle_t port_hdl);
272 
273 /**
274  * @brief Get the speed of the port
275  *
276  * The speed of the port is determined by the speed of the device connected to it.
277  *
278  * @note This function is only valid after a device directly to the port and has been reset
279  *
280  * @param[in port_hdl Port handle
281  * @param[out] speed Speed of the port
282  * @retval ESP_OK Device speed obtained
283  * @retval ESP_ERR_INVALID_STATE: No valid device connected to the port
284  * @retval ESP_ERR_INVALID_ARG: Invalid arguments
285  */
286 esp_err_t hcd_port_get_speed(hcd_port_handle_t port_hdl, usb_speed_t *speed);
287 
288 /**
289  * @brief Handle a ports event
290  *
291  * When an port event occurs (as indicated by a callback), this function should be called the handle this event. A
292  * port's event should always be handled before attempting to execute a port command. Note that is actually handled
293  * may be different than the event reflected in the callback.
294  *
295  * If the port has no events, this function will return HCD_PORT_EVENT_NONE.
296  *
297  * @note If callbacks are not used, this function can also be used in a polling manner to repeatedly check for and
298  *       handle a port's events.
299  * @note This function is internally protected by a mutex. If multiple threads call this function, this function will
300  *       can block.
301  *
302  * @param port_hdl Port handle
303  * @return hcd_port_event_t The port event that was handled
304  */
305 hcd_port_event_t hcd_port_handle_event(hcd_port_handle_t port_hdl);
306 
307 /**
308  * @brief Recover a port after a fatal error has occurred on it
309  *
310  * The port must be in the HCD_PORT_STATE_RECOVERY state to be called. Recovering the port will involve issuing a soft
311  * reset on the underlying USB controller. The port will be returned to the HCD_PORT_STATE_NOT_POWERED state.
312  *
313  * @param port_hdl Port handle
314  * @retval ESP_OK Port recovered successfully
315  * @retval ESP_ERR_INVALID_STATE Port is not in the HCD_PORT_STATE_RECOVERY state
316  */
317 esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl);
318 
319 /**
320  * @brief Get the context variable of a port
321  *
322  * @param port_hdl Port handle
323  * @return void* Context variable
324  */
325 void *hcd_port_get_context(hcd_port_handle_t port_hdl);
326 
327 /**
328  * @brief Set the bias of the HCD port's internal FIFO
329  *
330  * @note This function can only be called when the following conditions are met:
331  *  - Port is initialized
332  *  - Port does not have any pending events
333  *  - Port does not have any allocated pipes
334  *
335  * @param port_hdl Port handle
336  * @param bias Fifo bias
337  * @retval ESP_OK FIFO sizing successfully set
338  * @retval ESP_ERR_INVALID_STATE Incorrect state for FIFO sizes to be set
339  */
340 esp_err_t hcd_port_set_fifo_bias(hcd_port_handle_t port_hdl, hcd_port_fifo_bias_t bias);
341 
342 // --------------------------------------------------- HCD Pipes -------------------------------------------------------
343 
344 /**
345  * @brief Allocate a pipe
346  *
347  * When allocating a pipe, the HCD will assess whether there are sufficient resources (i.e., bus time, and controller
348  * channels). If sufficient, the pipe will be allocated.
349  *
350  * @note The host port must be in the enabled state before a pipe can be allocated
351  *
352  * @param[in] port_hdl Handle of the port this pipe will be routed through
353  * @param[in] pipe_config Pipe configuration
354  * @param[out] pipe_hdl Pipe handle
355  *
356  * @retval ESP_OK: Pipe successfully allocated
357  * @retval ESP_ERR_NO_MEM: Insufficient memory
358  * @retval ESP_ERR_INVALID_ARG: Arguments are invalid
359  * @retval ESP_ERR_INVALID_STATE: Host port is not in the correct state to allocate a pipe
360  * @retval ESP_ERR_NOT_SUPPORTED: The pipe's configuration cannot be supported
361  */
362 esp_err_t hcd_pipe_alloc(hcd_port_handle_t port_hdl, const hcd_pipe_config_t *pipe_config, hcd_pipe_handle_t *pipe_hdl);
363 
364 /**
365  * @brief Free a pipe
366  *
367  * Frees the resources used by an HCD pipe. The pipe's handle should be discarded after calling this function. The pipe
368  * must be in following condition before it can be freed:
369  * - All URBs have been dequeued
370  *
371  * @param pipe_hdl Pipe handle
372  *
373  * @retval ESP_OK: Pipe successfully freed
374  * @retval ESP_ERR_INVALID_STATE: Pipe is not in a condition to be freed
375  */
376 esp_err_t hcd_pipe_free(hcd_pipe_handle_t pipe_hdl);
377 
378 /**
379  * @brief Update a pipe's maximum packet size
380  *
381  * This function is intended to be called on default pipes during enumeration in order to update the pipe's maximum
382  * packet size. This function can only be called on a pipe that has met the following conditions:
383  * - Pipe is not current processing a command
384  * - Pipe does not have any enqueued URBs
385  * - Port cannot be resetting
386  *
387  * @param pipe_hdl Pipe handle
388  * @param mps New Maximum Packet Size
389  *
390  * @retval ESP_OK: Pipe successfully updated
391  * @retval ESP_ERR_INVALID_STATE: Pipe is not in a condition to be updated
392  */
393 esp_err_t hcd_pipe_update_mps(hcd_pipe_handle_t pipe_hdl, int mps);
394 
395 /**
396  * @brief Update a pipe's device address
397  *
398  * This function is intended to be called on default pipes during enumeration in order to update the pipe's device
399  * address. This function can only be called on a pipe that has met the following conditions:
400  * - Pipe is not current processing a command
401  * - Pipe does not have any enqueued URBs
402  * - Port cannot be resetting
403  *
404  * @param pipe_hdl Pipe handle
405  * @param dev_addr New device address
406  *
407  * @retval ESP_OK: Pipe successfully updated
408  * @retval ESP_ERR_INVALID_STATE: Pipe is not in a condition to be updated
409  */
410 esp_err_t hcd_pipe_update_dev_addr(hcd_pipe_handle_t pipe_hdl, uint8_t dev_addr);
411 
412 /**
413  * @brief Update a pipe's callback
414  *
415  * This function is intended to be called on default pipes at the end of enumeration to switch to a callback that
416  * handles the completion of regular control transfer.
417  * - Pipe is not current processing a command
418  * - Pipe does not have any enqueued URBs
419  * - Port cannot be resetting
420  *
421  * @param pipe_hdl Pipe handle
422  * @param callback Callback
423  * @param user_arg Callback argument
424  * @return esp_err_t
425  */
426 esp_err_t hcd_pipe_update_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_callback_t callback, void *user_arg);
427 
428 /**
429  * @brief Make a pipe persist through a run time reset
430  *
431  * Normally when a HCD_PORT_CMD_RESET is called, all pipes should already have been freed. However There may be cases
432  * (such as during enumeration) when a pipe must persist through a reset. This function will mark a pipe as
433  * persistent allowing it to survive a reset. When HCD_PORT_CMD_RESET is called, the pipe can continue to be used after
434  * the reset.
435  *
436  * @param pipe_hdl Pipe handle
437  * @retval ESP_OK: Pipe successfully marked as persistent
438  * @retval ESP_ERR_INVALID_STATE: Pipe is not in a condition to be made persistent
439  */
440 esp_err_t hcd_pipe_set_persist_reset(hcd_pipe_handle_t pipe_hdl);
441 
442 /**
443  * @brief Get the context variable of a pipe from its handle
444  *
445  * @param pipe_hdl Pipe handle
446  * @return void* Context variable
447  */
448 void *hcd_pipe_get_context(hcd_pipe_handle_t pipe_hdl);
449 
450 /**
451  * @brief Get the current sate of the pipe
452  *
453  * @param pipe_hdl Pipe handle
454  * @return hcd_pipe_state_t Current state of the pipe
455  */
456 hcd_pipe_state_t hcd_pipe_get_state(hcd_pipe_handle_t pipe_hdl);
457 
458 /**
459  * @brief Execute a command on a particular pipe
460  *
461  * Pipe commands allow a pipe to be manipulated (such as clearing a halt, retiring all URBs etc)
462  *
463  * @note This function can block
464  *
465  * @param pipe_hdl Pipe handle
466  * @param command Pipe command
467  * @retval ESP_OK: Command executed successfully
468  * @retval ESP_ERR_INVALID_STATE: The pipe is not in the correct state/condition too execute the command
469  */
470 esp_err_t hcd_pipe_command(hcd_pipe_handle_t pipe_hdl, hcd_pipe_cmd_t command);
471 
472 /**
473  * @brief Get the last event that occurred on a pipe
474  *
475  * This function allows a pipe to be polled for events (i.e., when callbacks are not used). Once an event has been
476  * obtained, this function reset the last event of the pipe to HCD_PIPE_EVENT_NONE.
477  *
478  * @param pipe_hdl Pipe handle
479  * @return hcd_pipe_event_t Last pipe event to occur
480  */
481 hcd_pipe_event_t hcd_pipe_get_event(hcd_pipe_handle_t pipe_hdl);
482 
483 // ---------------------------------------------------- HCD URBs -------------------------------------------------------
484 
485 /**
486  * @brief Enqueue an URB to a particular pipe
487  *
488  * The following conditions must be met before an URB can be enqueued:
489  * - The URB is properly initialized (data buffer and transfer length are set)
490  * - The URB must not already be enqueued
491  * - The pipe must be in the HCD_PIPE_STATE_ACTIVE state
492  * - The pipe cannot be executing a command
493  *
494  * @param pipe_hdl Pipe handle
495  * @param urb URB to enqueue
496  * @retval ESP_OK: URB enqueued successfully
497  * @retval ESP_ERR_INVALID_STATE: Conditions not met to enqueue URB
498  */
499 esp_err_t hcd_urb_enqueue(hcd_pipe_handle_t pipe_hdl, urb_t *urb);
500 
501 /**
502  * @brief Dequeue an URB from a particular pipe
503  *
504  * This function should be called on a pipe after a pipe receives a HCD_PIPE_EVENT_URB_DONE event. If a pipe has
505  * multiple URBs that can be dequeued, this function should be called repeatedly until all URBs are dequeued. If a pipe
506  * has no more URBs to dequeue, this function will return NULL.
507  *
508  * @param pipe_hdl Pipe handle
509  * @return urb_t* Dequeued URB, or NULL if no more URBs to dequeue
510  */
511 urb_t *hcd_urb_dequeue(hcd_pipe_handle_t pipe_hdl);
512 
513 /**
514  * @brief Abort an enqueued URB
515  *
516  * This function will attempt to abort an URB that is already enqueued. If the URB has yet to be executed, it will be
517  * "canceled" and can then be dequeued. If the URB is currently in-flight or has already completed, the URB will not be
518  * affected by this function.
519  *
520  * @param urb URB to abort
521  * @retval ESP_OK: URB successfully aborted, or was not affected by this function
522  * @retval ESP_ERR_INVALID_STATE: URB was never enqueued
523  */
524 esp_err_t hcd_urb_abort(urb_t *urb);
525 
526 #ifdef __cplusplus
527 }
528 #endif
529