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