mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
usb_host: Refactor USBH and USB Host Library calls to HCD
This commit refactors the USBH and the USB Host Library in the following ways: - USBH now presents an abstraction of an endpoint (via usbh_ep_handle_t) - Added separate functions to enqueue/dequeue URBs to a particular endpoint - USB Host Library no longer calls HCD API directly. Calls USBH endpoint API instead. - Renamed "notif_cb" to "proc_req_cb" (Processing Request Callback) - This is to avoid confusion with FreerTOS task notifications and Host Library client event notifications. - The processing functions of each layer (i.e., "xxx_process()") request calls via the "proc_req_cb" - The main handling function (i.e., usb_host_lib_handle_events()) is responsible for calling the required "xxx_process()" of each layer
This commit is contained in:
parent
b530d768e6
commit
b891aa0443
@ -2045,6 +2045,16 @@ hcd_pipe_state_t hcd_pipe_get_state(hcd_pipe_handle_t pipe_hdl)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int hcd_pipe_get_num_urbs(hcd_pipe_handle_t pipe_hdl)
|
||||||
|
{
|
||||||
|
unsigned int ret;
|
||||||
|
pipe_t *pipe = (pipe_t *)pipe_hdl;
|
||||||
|
HCD_ENTER_CRITICAL();
|
||||||
|
ret = pipe->num_urb_pending + pipe->num_urb_done;
|
||||||
|
HCD_EXIT_CRITICAL();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t hcd_pipe_command(hcd_pipe_handle_t pipe_hdl, hcd_pipe_cmd_t command)
|
esp_err_t hcd_pipe_command(hcd_pipe_handle_t pipe_hdl, hcd_pipe_cmd_t command)
|
||||||
{
|
{
|
||||||
pipe_t *pipe = (pipe_t *)pipe_hdl;
|
pipe_t *pipe = (pipe_t *)pipe_hdl;
|
||||||
|
@ -187,8 +187,8 @@ typedef struct {
|
|||||||
//Constant members do no change after installation thus do not require a critical section
|
//Constant members do no change after installation thus do not require a critical section
|
||||||
struct {
|
struct {
|
||||||
hcd_port_handle_t root_port_hdl;
|
hcd_port_handle_t root_port_hdl;
|
||||||
usb_notif_cb_t notif_cb;
|
usb_proc_req_cb_t proc_req_cb;
|
||||||
void *notif_cb_arg;
|
void *proc_req_cb_arg;
|
||||||
} constant;
|
} constant;
|
||||||
} hub_driver_t;
|
} hub_driver_t;
|
||||||
|
|
||||||
@ -223,7 +223,7 @@ const char *HUB_DRIVER_TAG = "HUB";
|
|||||||
*
|
*
|
||||||
* - This callback is called from the context of the HCD, so any event handling should be deferred to hub_process()
|
* - This callback is called from the context of the HCD, so any event handling should be deferred to hub_process()
|
||||||
* - Under the current HCD implementation, this callback should only be ever be called in an ISR
|
* - Under the current HCD implementation, this callback should only be ever be called in an ISR
|
||||||
* - This callback needs to call the notification to ensure hub_process() gets a chance to run
|
* - This callback needs to call proc_req_cb to ensure that hub_process() gets a chance to run
|
||||||
*
|
*
|
||||||
* @param port_hdl HCD port handle
|
* @param port_hdl HCD port handle
|
||||||
* @param port_event HCD port event
|
* @param port_event HCD port event
|
||||||
@ -237,7 +237,7 @@ static bool root_port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port
|
|||||||
* @brief HCD pipe callback for the default pipe of the device under enumeration
|
* @brief HCD pipe callback for the default pipe of the device under enumeration
|
||||||
*
|
*
|
||||||
* - This callback is called from the context of the HCD, so any event handling should be deferred to hub_process()
|
* - This callback is called from the context of the HCD, so any event handling should be deferred to hub_process()
|
||||||
* - This callback needs to call the notification to ensure hub_process() gets a chance to run
|
* - This callback needs to call proc_req_cb to ensure that hub_process() gets a chance to run
|
||||||
*
|
*
|
||||||
* @param pipe_hdl HCD pipe handle
|
* @param pipe_hdl HCD pipe handle
|
||||||
* @param pipe_event Pipe event
|
* @param pipe_event Pipe event
|
||||||
@ -251,7 +251,7 @@ static bool enum_dflt_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t
|
|||||||
* @brief USBH Hub driver request callback
|
* @brief USBH Hub driver request callback
|
||||||
*
|
*
|
||||||
* - This callback is called from the context of the USBH, so so any event handling should be deferred to hub_process()
|
* - This callback is called from the context of the USBH, so so any event handling should be deferred to hub_process()
|
||||||
* - This callback needs to call the notification to ensure hub_process() gets a chance to run
|
* - This callback needs to call proc_req_cb to ensure that hub_process() gets a chance to run
|
||||||
*
|
*
|
||||||
* @param port_hdl HCD port handle
|
* @param port_hdl HCD port handle
|
||||||
* @param hub_req Hub driver request
|
* @param hub_req Hub driver request
|
||||||
@ -756,7 +756,7 @@ static bool root_port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port
|
|||||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ROOT_EVENT;
|
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ROOT_EVENT;
|
||||||
HUB_DRIVER_EXIT_CRITICAL_SAFE();
|
HUB_DRIVER_EXIT_CRITICAL_SAFE();
|
||||||
assert(in_isr); //Currently, this callback should only ever be called from an ISR context
|
assert(in_isr); //Currently, this callback should only ever be called from an ISR context
|
||||||
return p_hub_driver_obj->constant.notif_cb(USB_NOTIF_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.notif_cb_arg);;
|
return p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.proc_req_cb_arg);;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool enum_dflt_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr)
|
static bool enum_dflt_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr)
|
||||||
@ -765,7 +765,7 @@ static bool enum_dflt_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t
|
|||||||
HUB_DRIVER_ENTER_CRITICAL_SAFE();
|
HUB_DRIVER_ENTER_CRITICAL_SAFE();
|
||||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT;
|
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT;
|
||||||
HUB_DRIVER_EXIT_CRITICAL_SAFE();
|
HUB_DRIVER_EXIT_CRITICAL_SAFE();
|
||||||
return p_hub_driver_obj->constant.notif_cb(USB_NOTIF_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.notif_cb_arg);
|
return p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.proc_req_cb_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usbh_hub_req_callback(hcd_port_handle_t port_hdl, usbh_hub_req_t hub_req, void *arg)
|
static void usbh_hub_req_callback(hcd_port_handle_t port_hdl, usbh_hub_req_t hub_req, void *arg)
|
||||||
@ -788,7 +788,7 @@ static void usbh_hub_req_callback(hcd_port_handle_t port_hdl, usbh_hub_req_t hub
|
|||||||
}
|
}
|
||||||
HUB_DRIVER_EXIT_CRITICAL();
|
HUB_DRIVER_EXIT_CRITICAL();
|
||||||
|
|
||||||
p_hub_driver_obj->constant.notif_cb(USB_NOTIF_SOURCE_HUB, false, p_hub_driver_obj->constant.notif_cb_arg);
|
p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, false, p_hub_driver_obj->constant.proc_req_cb_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------- Handlers -------------------------
|
// ---------------------- Handlers -------------------------
|
||||||
@ -953,8 +953,8 @@ esp_err_t hub_install(hub_config_t *hub_config)
|
|||||||
hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_NONE;
|
hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_NONE;
|
||||||
hub_driver_obj->single_thread.enum_ctrl.urb = enum_urb;
|
hub_driver_obj->single_thread.enum_ctrl.urb = enum_urb;
|
||||||
hub_driver_obj->constant.root_port_hdl = port_hdl;
|
hub_driver_obj->constant.root_port_hdl = port_hdl;
|
||||||
hub_driver_obj->constant.notif_cb = hub_config->notif_cb;
|
hub_driver_obj->constant.proc_req_cb = hub_config->proc_req_cb;
|
||||||
hub_driver_obj->constant.notif_cb_arg = hub_config->notif_cb_arg;
|
hub_driver_obj->constant.proc_req_cb_arg = hub_config->proc_req_cb_arg;
|
||||||
HUB_DRIVER_ENTER_CRITICAL();
|
HUB_DRIVER_ENTER_CRITICAL();
|
||||||
if (p_hub_driver_obj != NULL) {
|
if (p_hub_driver_obj != NULL) {
|
||||||
HUB_DRIVER_EXIT_CRITICAL();
|
HUB_DRIVER_EXIT_CRITICAL();
|
||||||
|
@ -448,13 +448,24 @@ esp_err_t hcd_pipe_set_persist_reset(hcd_pipe_handle_t pipe_hdl);
|
|||||||
void *hcd_pipe_get_context(hcd_pipe_handle_t pipe_hdl);
|
void *hcd_pipe_get_context(hcd_pipe_handle_t pipe_hdl);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the current sate of the pipe
|
* @brief Get the current state of the pipe
|
||||||
*
|
*
|
||||||
* @param pipe_hdl Pipe handle
|
* @param pipe_hdl Pipe handle
|
||||||
* @return hcd_pipe_state_t Current state of the pipe
|
* @return hcd_pipe_state_t Current state of the pipe
|
||||||
*/
|
*/
|
||||||
hcd_pipe_state_t hcd_pipe_get_state(hcd_pipe_handle_t pipe_hdl);
|
hcd_pipe_state_t hcd_pipe_get_state(hcd_pipe_handle_t pipe_hdl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of in-flight URBs in the pipe
|
||||||
|
*
|
||||||
|
* Returns the current number of URBs that have been enqueued (via hcd_urb_enqueue()) and have yet to be dequeued (via
|
||||||
|
* hcd_urb_dequeue()).
|
||||||
|
*
|
||||||
|
* @param pipe_hdl Pipe handle
|
||||||
|
* @return Number of in-flight URBs
|
||||||
|
*/
|
||||||
|
unsigned int hcd_pipe_get_num_urbs(hcd_pipe_handle_t pipe_hdl);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Execute a command on a particular pipe
|
* @brief Execute a command on a particular pipe
|
||||||
*
|
*
|
||||||
|
@ -22,8 +22,8 @@ extern "C" {
|
|||||||
* @brief Hub driver configuration
|
* @brief Hub driver configuration
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
usb_notif_cb_t notif_cb; /**< Notification callback */
|
usb_proc_req_cb_t proc_req_cb; /**< Processing request callback */
|
||||||
void *notif_cb_arg; /**< Notification callback argument */
|
void *proc_req_cb_arg; /**< Processing request callback argument */
|
||||||
} hub_config_t;
|
} hub_config_t;
|
||||||
|
|
||||||
// ---------------------------------------------- Hub Driver Functions -------------------------------------------------
|
// ---------------------------------------------- Hub Driver Functions -------------------------------------------------
|
||||||
@ -77,7 +77,7 @@ esp_err_t hub_root_stop(void);
|
|||||||
* @brief Hub driver's processing function
|
* @brief Hub driver's processing function
|
||||||
*
|
*
|
||||||
* Hub driver handling function that must be called repeatdly to process the Hub driver's events. If blocking, the
|
* Hub driver handling function that must be called repeatdly to process the Hub driver's events. If blocking, the
|
||||||
* caller can block on the notification callback of source USB_NOTIF_SOURCE_HUB to run this function.
|
* caller can block on the notification callback of source USB_PROC_REQ_SOURCE_HUB to run this function.
|
||||||
*
|
*
|
||||||
* @return esp_err_t
|
* @return esp_err_t
|
||||||
*/
|
*/
|
||||||
|
@ -50,12 +50,24 @@ struct urb_s{
|
|||||||
};
|
};
|
||||||
typedef struct urb_s urb_t;
|
typedef struct urb_s urb_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processing request source
|
||||||
|
*
|
||||||
|
* Enum to indicate which layer of the USB Host stack requires processing. The main handling loop should then call that
|
||||||
|
* layer's processing function (i.e., xxx_process()).
|
||||||
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
USB_NOTIF_SOURCE_USBH = 0x01,
|
USB_PROC_REQ_SOURCE_USBH = 0x01,
|
||||||
USB_NOTIF_SOURCE_HUB = 0x02,
|
USB_PROC_REQ_SOURCE_HUB = 0x02,
|
||||||
} usb_notif_source_t;
|
} usb_proc_req_source_t;
|
||||||
|
|
||||||
typedef bool (*usb_notif_cb_t)(usb_notif_source_t source, bool in_isr, void *context);
|
/**
|
||||||
|
* @brief Processing request callback
|
||||||
|
*
|
||||||
|
* Callback function provided to each layer of the USB Host stack so that each layer can request calls to their
|
||||||
|
* processing function.
|
||||||
|
*/
|
||||||
|
typedef bool (*usb_proc_req_cb_t)(usb_proc_req_source_t source, bool in_isr, void *context);
|
||||||
|
|
||||||
// --------------------------------------------------- Allocation ------------------------------------------------------
|
// --------------------------------------------------- Allocation ------------------------------------------------------
|
||||||
|
|
||||||
|
@ -20,6 +20,13 @@ extern "C" {
|
|||||||
|
|
||||||
// ------------------------------------------------------ Types --------------------------------------------------------
|
// ------------------------------------------------------ Types --------------------------------------------------------
|
||||||
|
|
||||||
|
// ----------------------- Handles -------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle of a allocated endpoint
|
||||||
|
*/
|
||||||
|
typedef struct usbh_ep_handle_s * usbh_ep_handle_t;
|
||||||
|
|
||||||
// ----------------------- Events --------------------------
|
// ----------------------- Events --------------------------
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -29,16 +36,18 @@ typedef enum {
|
|||||||
} usbh_event_t;
|
} usbh_event_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Hub driver requests
|
* @brief Endpoint events
|
||||||
*
|
*
|
||||||
* Various requests of the Hub driver that the USBH can make.
|
* @note Optimization: Keep this identical to hcd_pipe_event_t
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
USBH_HUB_REQ_PORT_DISABLE, /**< Request that the Hub driver disable a particular port (occurs after a device
|
USBH_EP_EVENT_NONE, /**< The EP has no events (used to indicate no events when polling) */
|
||||||
has been freed). Hub driver should respond with a USBH_HUB_EVENT_PORT_DISABLED */
|
USBH_EP_EVENT_URB_DONE, /**< The EP has completed a URB. The URB can be dequeued */
|
||||||
USBH_HUB_REQ_PORT_RECOVER, /**< Request that the Hub driver recovers a particular port (occurs after a gone
|
USBH_EP_EVENT_ERROR_XFER, /**< The EP encountered excessive errors when transferring a URB i.e., three three consecutive transaction errors (e.g., no ACK, bad CRC etc) */
|
||||||
device has been freed). */
|
USBH_EP_EVENT_ERROR_URB_NOT_AVAIL, /**< The EP tried to execute a transfer but no URB was available */
|
||||||
} usbh_hub_req_t;
|
USBH_EP_EVENT_ERROR_OVERFLOW, /**< The EP received more data than requested. Usually a Packet babble error (i.e., an IN packet has exceeded the EP's MPS) */
|
||||||
|
USBH_EP_EVENT_ERROR_STALL, /**< EP received a STALL response */
|
||||||
|
} usbh_ep_event_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Hub driver events for the USBH
|
* @brief Hub driver events for the USBH
|
||||||
@ -63,6 +72,31 @@ typedef enum {
|
|||||||
USBH_HUB_EVENT_PORT_DISABLED, /**< Previous USBH_HUB_REQ_PORT_DISABLE request completed */
|
USBH_HUB_EVENT_PORT_DISABLED, /**< Previous USBH_HUB_REQ_PORT_DISABLE request completed */
|
||||||
} usbh_hub_event_t;
|
} usbh_hub_event_t;
|
||||||
|
|
||||||
|
// ------------------ Requests/Commands --------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hub driver requests
|
||||||
|
*
|
||||||
|
* Various requests of the Hub driver that the USBH can make.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
USBH_HUB_REQ_PORT_DISABLE, /**< Request that the Hub driver disable a particular port (occurs after a device
|
||||||
|
has been freed). Hub driver should respond with a USBH_HUB_EVENT_PORT_DISABLED */
|
||||||
|
USBH_HUB_REQ_PORT_RECOVER, /**< Request that the Hub driver recovers a particular port (occurs after a gone
|
||||||
|
device has been freed). */
|
||||||
|
} usbh_hub_req_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Endpoint commands
|
||||||
|
*
|
||||||
|
* @note Optimization: Keep this identical to hcd_pipe_cmd_t
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
USBH_EP_CMD_HALT, /**< Halt an active endpoint. Any currently executing URB will be canceled. Enqueued URBs are left untouched */
|
||||||
|
USBH_EP_CMD_FLUSH, /**< Can only be called when halted. Will cause all enqueued URBs to be canceled */
|
||||||
|
USBH_EP_CMD_CLEAR, /**< Causes a halted endpoint to become active again. Any enqueued URBs will being executing.*/
|
||||||
|
} usbh_ep_cmd_t;
|
||||||
|
|
||||||
// ---------------------- Callbacks ------------------------
|
// ---------------------- Callbacks ------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,29 +121,37 @@ typedef void (*usbh_event_cb_t)(usb_device_handle_t dev_hdl, usbh_event_t usbh_e
|
|||||||
*/
|
*/
|
||||||
typedef void (*usbh_hub_req_cb_t)(hcd_port_handle_t port_hdl, usbh_hub_req_t hub_req, void *arg);
|
typedef void (*usbh_hub_req_cb_t)(hcd_port_handle_t port_hdl, usbh_hub_req_t hub_req, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback used to indicate an event on an endpoint
|
||||||
|
*
|
||||||
|
* Return whether to yield or not if called from an ISR. Always return false if not called from an ISR
|
||||||
|
*/
|
||||||
|
typedef bool (*usbh_ep_cb_t)(usbh_ep_handle_t ep_hdl, usbh_ep_event_t ep_event, void *arg, bool in_isr);
|
||||||
|
|
||||||
// ----------------------- Objects -------------------------
|
// ----------------------- Objects -------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Configuration for an endpoint being allocated using usbh_ep_alloc()
|
* @brief Configuration for an endpoint being allocated using usbh_ep_alloc()
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const usb_ep_desc_t *ep_desc; /**< Endpoint descriptor */
|
uint8_t bInterfaceNumber; /**< Interface number */
|
||||||
hcd_pipe_callback_t pipe_cb; /**< Endpoint's pipe callback */
|
uint8_t bAlternateSetting; /**< Alternate setting number of the interface */
|
||||||
void *pipe_cb_arg; /**< Pipe callback argument */
|
uint8_t bEndpointAddress; /**< Endpoint address */
|
||||||
void *context; /**< Pipe context */
|
usbh_ep_cb_t ep_cb; /**< Endpoint event callback */
|
||||||
|
void *ep_cb_arg; /**< Endpoint callback argument */
|
||||||
|
void *context; /**< Endpoint context */
|
||||||
} usbh_ep_config_t;
|
} usbh_ep_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief USBH configuration used in usbh_install()
|
* @brief USBH configuration used in usbh_install()
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
usb_notif_cb_t notif_cb; /**< Notification callback */
|
usb_proc_req_cb_t proc_req_cb; /**< Processing request callback */
|
||||||
void *notif_cb_arg; /**< Notification callback argument */
|
void *proc_req_cb_arg; /**< Processing request callback argument */
|
||||||
usbh_ctrl_xfer_cb_t ctrl_xfer_cb; /**< Control transfer callback */
|
usbh_ctrl_xfer_cb_t ctrl_xfer_cb; /**< Control transfer callback */
|
||||||
void *ctrl_xfer_cb_arg; /**< Control transfer callback argument */
|
void *ctrl_xfer_cb_arg; /**< Control transfer callback argument */
|
||||||
usbh_event_cb_t event_cb; /**< USBH event callback */
|
usbh_event_cb_t event_cb; /**< USBH event callback */
|
||||||
void *event_cb_arg; /**< USBH event callback argument */
|
void *event_cb_arg; /**< USBH event callback argument */
|
||||||
hcd_config_t hcd_config; /**< HCD configuration */
|
|
||||||
} usbh_config_t;
|
} usbh_config_t;
|
||||||
|
|
||||||
// ------------------------------------------------- USBH Functions ----------------------------------------------------
|
// ------------------------------------------------- USBH Functions ----------------------------------------------------
|
||||||
@ -143,8 +185,8 @@ esp_err_t usbh_uninstall(void);
|
|||||||
* @brief USBH processing function
|
* @brief USBH processing function
|
||||||
*
|
*
|
||||||
* - USBH processing function that must be called repeatedly to process USBH events
|
* - USBH processing function that must be called repeatedly to process USBH events
|
||||||
* - If blocking, the caller can block until a USB_NOTIF_SOURCE_USBH notification is received before running this
|
* - If blocking, the caller can block until the proc_req_cb() is called with USB_PROC_REQ_SOURCE_USBH as the request
|
||||||
* function
|
* source. The USB_PROC_REQ_SOURCE_USBH source indicates that this function should be called.
|
||||||
*
|
*
|
||||||
* @note This function can block
|
* @note This function can block
|
||||||
* @return esp_err_t
|
* @return esp_err_t
|
||||||
@ -271,31 +313,84 @@ esp_err_t usbh_dev_submit_ctrl_urb(usb_device_handle_t dev_hdl, urb_t *urb);
|
|||||||
/**
|
/**
|
||||||
* @brief Allocate an endpoint on a device
|
* @brief Allocate an endpoint on a device
|
||||||
*
|
*
|
||||||
* Clients that have opened a device must call this function to allocate all endpoints in an interface that is claimed.
|
* This function allows clients to allocate a non-default endpoint (i.e., not EP0) on a connected device
|
||||||
* The pipe handle of the endpoint is returned so that clients can use and control the pipe directly.
|
*
|
||||||
|
* - A client must have opened the device using usbh_dev_open() before attempting to allocate an endpoint on the device
|
||||||
|
* - A client should call this function to allocate all endpoints in an interface that the client has claimed.
|
||||||
|
* - A client must allocate an endpoint using this function before attempting to communicate with it
|
||||||
|
* - Once the client allocates an endpoint, the client is now owns/manages the endpoint. No other client should use or
|
||||||
|
* deallocte the endpoint.
|
||||||
*
|
*
|
||||||
* @note This function can block
|
* @note This function can block
|
||||||
* @note Default pipes are owned by the USBH. For control transfers, use usbh_dev_submit_ctrl_urb() instead
|
* @note Default endpoints (EP0) are owned by the USBH. For control transfers, use usbh_dev_submit_ctrl_urb() instead
|
||||||
* @note Device must be opened by the client first
|
|
||||||
*
|
*
|
||||||
* @param[in] dev_hdl Device handle
|
* @param[in] dev_hdl Device handle
|
||||||
* @param[in] ep_config
|
* @param[in] ep_config Endpoint configuration
|
||||||
* @param[out] pipe_hdl_ret Pipe handle
|
* @param[out] ep_hdl_ret Endpoint handle
|
||||||
* @return esp_err_t
|
* @return esp_err_t
|
||||||
*/
|
*/
|
||||||
esp_err_t usbh_ep_alloc(usb_device_handle_t dev_hdl, usbh_ep_config_t *ep_config, hcd_pipe_handle_t *pipe_hdl_ret);
|
esp_err_t usbh_ep_alloc(usb_device_handle_t dev_hdl, usbh_ep_config_t *ep_config, usbh_ep_handle_t *ep_hdl_ret);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Free and endpoint on a device
|
* @brief Free and endpoint on a device
|
||||||
*
|
*
|
||||||
* Free an endpoint previously opened by usbh_ep_alloc()
|
* This function frees an endpoint previously allocated by the client using usbh_ep_alloc()
|
||||||
|
*
|
||||||
|
* - Only the client that allocated the endpoint should free it
|
||||||
|
* - The client must have halted and flushed the endpoint using usbh_ep_command() before attempting to free it
|
||||||
|
* - The client must ensure that there are no more function calls to the endpoint before freeing it
|
||||||
*
|
*
|
||||||
* @note This function can block
|
* @note This function can block
|
||||||
* @param[in] dev_hdl Device handle
|
* @param[in] ep_hdl Endpoint handle
|
||||||
* @param[in] bEndpointAddress Endpoint's address
|
|
||||||
* @return esp_err_t
|
* @return esp_err_t
|
||||||
*/
|
*/
|
||||||
esp_err_t usbh_ep_free(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress);
|
esp_err_t usbh_ep_free(usbh_ep_handle_t ep_hdl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the handle of an endpoint using its address
|
||||||
|
*
|
||||||
|
* The endpoint must have been previously allocated using usbh_ep_alloc()
|
||||||
|
*
|
||||||
|
* @param[in] dev_hdl Device handle
|
||||||
|
* @param[in] bEndpointAddress Endpoint address
|
||||||
|
* @param[out] ep_hdl_ret Endpoint handle
|
||||||
|
* @return esp_err_t
|
||||||
|
*/
|
||||||
|
esp_err_t usbh_ep_get_handle(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress, usbh_ep_handle_t *ep_hdl_ret);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enqueue a URB to an endpoint
|
||||||
|
*
|
||||||
|
* The URB will remain enqueued until it completes (successfully or errors out). Use usbh_ep_dequeue_urb() to dequeue
|
||||||
|
* a completed URB.
|
||||||
|
*
|
||||||
|
* @param[in] ep_hdl Endpoint handle
|
||||||
|
* @param[in] urb URB to enqueue
|
||||||
|
* @return esp_err_t
|
||||||
|
*/
|
||||||
|
esp_err_t usbh_ep_enqueue_urb(usbh_ep_handle_t ep_hdl, urb_t *urb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dequeue a URB from an endpoint
|
||||||
|
*
|
||||||
|
* Dequeue a completed URB from an endpoint. The USBH_EP_EVENT_URB_DONE indicates that URBs can be dequeued
|
||||||
|
*
|
||||||
|
* @param[in] ep_hdl Endpoint handle
|
||||||
|
* @param[out] urb_ret Dequeued URB, or NULL if no more URBs to dequeue
|
||||||
|
* @return esp_err_t
|
||||||
|
*/
|
||||||
|
esp_err_t usbh_ep_dequeue_urb(usbh_ep_handle_t ep_hdl, urb_t **urb_ret);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Execute a command on a particular endpoint
|
||||||
|
*
|
||||||
|
* Endpoint commands allows executing a certain action on an endpoint (e.g., halting, flushing, clearing etc)
|
||||||
|
*
|
||||||
|
* @param[in] ep_hdl Endpoint handle
|
||||||
|
* @param[in] command Endpoint command
|
||||||
|
* @return esp_err_t
|
||||||
|
*/
|
||||||
|
esp_err_t usbh_ep_command(usbh_ep_handle_t ep_hdl, usbh_ep_cmd_t command);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the context of an endpoint
|
* @brief Get the context of an endpoint
|
||||||
@ -303,12 +398,10 @@ esp_err_t usbh_ep_free(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress);
|
|||||||
* Get the context variable assigned to and endpoint on allocation.
|
* Get the context variable assigned to and endpoint on allocation.
|
||||||
*
|
*
|
||||||
* @note This function can block
|
* @note This function can block
|
||||||
* @param[in] dev_hdl Device handle
|
* @param[in] ep_hdl Endpoint handle
|
||||||
* @param[in] bEndpointAddress Endpoint's address
|
* @return Endpoint context
|
||||||
* @param[out] context_ret Context variable
|
|
||||||
* @return esp_err_t
|
|
||||||
*/
|
*/
|
||||||
esp_err_t usbh_ep_get_context(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress, void **context_ret);
|
void *usbh_ep_get_context(usbh_ep_handle_t ep_hdl);
|
||||||
|
|
||||||
// -------------------------------------------------- Hub Functions ----------------------------------------------------
|
// -------------------------------------------------- Hub Functions ----------------------------------------------------
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ Warning: The USB Host Library API is still a beta version and may be subject to
|
|||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
#include "hub.h"
|
#include "hub.h"
|
||||||
#include "usbh.h"
|
#include "usbh.h"
|
||||||
|
#include "hcd.h"
|
||||||
#include "esp_private/usb_phy.h"
|
#include "esp_private/usb_phy.h"
|
||||||
#include "usb/usb_host.h"
|
#include "usb/usb_host.h"
|
||||||
|
|
||||||
@ -43,18 +44,17 @@ static portMUX_TYPE host_lock = portMUX_INITIALIZER_UNLOCKED;
|
|||||||
} \
|
} \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define PROCESS_PENDING_FLAG_USBH 0x01
|
#define PROCESS_REQUEST_PENDING_FLAG_USBH 0x01
|
||||||
#define PROCESS_PENDING_FLAG_HUB 0x02
|
#define PROCESS_REQUEST_PENDING_FLAG_HUB 0x02
|
||||||
#define PROCESS_PENDING_FLAG_EVENT 0x04
|
|
||||||
|
|
||||||
typedef struct endpoint_s endpoint_t;
|
typedef struct ep_wrapper_s ep_wrapper_t;
|
||||||
typedef struct interface_s interface_t;
|
typedef struct interface_s interface_t;
|
||||||
typedef struct client_s client_t;
|
typedef struct client_s client_t;
|
||||||
|
|
||||||
struct endpoint_s {
|
struct ep_wrapper_s {
|
||||||
//Dynamic members require a critical section
|
//Dynamic members require a critical section
|
||||||
struct {
|
struct {
|
||||||
TAILQ_ENTRY(endpoint_s) tailq_entry;
|
TAILQ_ENTRY(ep_wrapper_s) tailq_entry;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t pending: 1;
|
uint32_t pending: 1;
|
||||||
@ -62,12 +62,11 @@ struct endpoint_s {
|
|||||||
};
|
};
|
||||||
} flags;
|
} flags;
|
||||||
uint32_t num_urb_inflight;
|
uint32_t num_urb_inflight;
|
||||||
hcd_pipe_event_t last_event;
|
usbh_ep_event_t last_event;
|
||||||
} dynamic;
|
} dynamic;
|
||||||
//Constant members do no change after claiming the interface thus do not require a critical section
|
//Constant members do no change after claiming the interface thus do not require a critical section
|
||||||
struct {
|
struct {
|
||||||
hcd_pipe_handle_t pipe_hdl;
|
usbh_ep_handle_t ep_hdl;
|
||||||
const usb_ep_desc_t *ep_desc;
|
|
||||||
interface_t *intf_obj;
|
interface_t *intf_obj;
|
||||||
} constant;
|
} constant;
|
||||||
};
|
};
|
||||||
@ -82,7 +81,7 @@ struct interface_s {
|
|||||||
const usb_intf_desc_t *intf_desc;
|
const usb_intf_desc_t *intf_desc;
|
||||||
usb_device_handle_t dev_hdl;
|
usb_device_handle_t dev_hdl;
|
||||||
client_t *client_obj;
|
client_t *client_obj;
|
||||||
endpoint_t *endpoints[0];
|
ep_wrapper_t *endpoints[0];
|
||||||
} constant;
|
} constant;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,8 +89,8 @@ struct client_s {
|
|||||||
//Dynamic members require a critical section
|
//Dynamic members require a critical section
|
||||||
struct {
|
struct {
|
||||||
TAILQ_ENTRY(client_s) tailq_entry;
|
TAILQ_ENTRY(client_s) tailq_entry;
|
||||||
TAILQ_HEAD(tailhead_pending_ep, endpoint_s) pending_ep_tailq;
|
TAILQ_HEAD(tailhead_pending_ep, ep_wrapper_s) pending_ep_tailq;
|
||||||
TAILQ_HEAD(tailhead_idle_ep, endpoint_s) idle_ep_tailq;
|
TAILQ_HEAD(tailhead_idle_ep, ep_wrapper_s) idle_ep_tailq;
|
||||||
TAILQ_HEAD(tailhead_done_ctrl_xfers, urb_s) done_ctrl_xfer_tailq;
|
TAILQ_HEAD(tailhead_done_ctrl_xfers, urb_s) done_ctrl_xfer_tailq;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
@ -260,16 +259,16 @@ static void send_event_msg_to_clients(const usb_host_client_event_msg_t *event_m
|
|||||||
|
|
||||||
// ------------------- Library Related ---------------------
|
// ------------------- Library Related ---------------------
|
||||||
|
|
||||||
static bool notif_callback(usb_notif_source_t source, bool in_isr, void *arg)
|
static bool proc_req_callback(usb_proc_req_source_t source, bool in_isr, void *arg)
|
||||||
{
|
{
|
||||||
HOST_ENTER_CRITICAL_SAFE();
|
HOST_ENTER_CRITICAL_SAFE();
|
||||||
//Store notification source
|
//Store the processing request source
|
||||||
switch (source) {
|
switch (source) {
|
||||||
case USB_NOTIF_SOURCE_USBH:
|
case USB_PROC_REQ_SOURCE_USBH:
|
||||||
p_host_lib_obj->dynamic.process_pending_flags |= PROCESS_PENDING_FLAG_USBH;
|
p_host_lib_obj->dynamic.process_pending_flags |= PROCESS_REQUEST_PENDING_FLAG_USBH;
|
||||||
break;
|
break;
|
||||||
case USB_NOTIF_SOURCE_HUB:
|
case USB_PROC_REQ_SOURCE_HUB:
|
||||||
p_host_lib_obj->dynamic.process_pending_flags |= PROCESS_PENDING_FLAG_HUB;
|
p_host_lib_obj->dynamic.process_pending_flags |= PROCESS_REQUEST_PENDING_FLAG_HUB;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
bool yield = _unblock_lib(in_isr);
|
bool yield = _unblock_lib(in_isr);
|
||||||
@ -333,19 +332,19 @@ static void dev_event_callback(usb_device_handle_t dev_hdl, usbh_event_t usbh_ev
|
|||||||
|
|
||||||
// ------------------- Client Related ----------------------
|
// ------------------- Client Related ----------------------
|
||||||
|
|
||||||
static bool pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr)
|
static bool endpoint_callback(usbh_ep_handle_t ep_hdl, usbh_ep_event_t ep_event, void *user_arg, bool in_isr)
|
||||||
{
|
{
|
||||||
endpoint_t *ep_obj = (endpoint_t *)user_arg;
|
ep_wrapper_t *ep_wrap = (ep_wrapper_t *)user_arg;
|
||||||
client_t *client_obj = (client_t *)ep_obj->constant.intf_obj->constant.client_obj;
|
client_t *client_obj = (client_t *)ep_wrap->constant.intf_obj->constant.client_obj;
|
||||||
|
|
||||||
HOST_ENTER_CRITICAL_SAFE();
|
HOST_ENTER_CRITICAL_SAFE();
|
||||||
//Store the event to be handled later. Note that we allow overwriting of events because more severe will halt the pipe prevent any further events.
|
//Store the event to be handled later. Note that we allow overwriting of events because more severe will halt the pipe prevent any further events.
|
||||||
ep_obj->dynamic.last_event = pipe_event;
|
ep_wrap->dynamic.last_event = ep_event;
|
||||||
//Add the EP to the client's pending list if it's not in the list already
|
//Add the EP to the client's pending list if it's not in the list already
|
||||||
if (!ep_obj->dynamic.flags.pending) {
|
if (!ep_wrap->dynamic.flags.pending) {
|
||||||
ep_obj->dynamic.flags.pending = 1;
|
ep_wrap->dynamic.flags.pending = 1;
|
||||||
TAILQ_REMOVE(&client_obj->dynamic.idle_ep_tailq, ep_obj, dynamic.tailq_entry);
|
TAILQ_REMOVE(&client_obj->dynamic.idle_ep_tailq, ep_wrap, dynamic.tailq_entry);
|
||||||
TAILQ_INSERT_TAIL(&client_obj->dynamic.pending_ep_tailq, ep_obj, dynamic.tailq_entry);
|
TAILQ_INSERT_TAIL(&client_obj->dynamic.pending_ep_tailq, ep_wrap, dynamic.tailq_entry);
|
||||||
}
|
}
|
||||||
bool yield = _unblock_client(client_obj, in_isr);
|
bool yield = _unblock_client(client_obj, in_isr);
|
||||||
HOST_EXIT_CRITICAL_SAFE();
|
HOST_EXIT_CRITICAL_SAFE();
|
||||||
@ -376,7 +375,16 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
|
|||||||
TAILQ_INIT(&host_lib_obj->mux_protected.client_tailq);
|
TAILQ_INIT(&host_lib_obj->mux_protected.client_tailq);
|
||||||
host_lib_obj->constant.event_sem = event_sem;
|
host_lib_obj->constant.event_sem = event_sem;
|
||||||
host_lib_obj->constant.mux_lock = mux_lock;
|
host_lib_obj->constant.mux_lock = mux_lock;
|
||||||
//Setup the USB PHY if necessary (USB PHY driver will also enable the underlying Host Controller)
|
|
||||||
|
/*
|
||||||
|
Install each layer of the Host stack (listed below) from the lowest layer to the highest
|
||||||
|
- USB PHY
|
||||||
|
- HCD
|
||||||
|
- USBH
|
||||||
|
- Hub
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Install USB PHY (if necessary). USB PHY driver will also enable the underlying Host Controller
|
||||||
if (!config->skip_phy_setup) {
|
if (!config->skip_phy_setup) {
|
||||||
//Host Library defaults to internal PHY
|
//Host Library defaults to internal PHY
|
||||||
usb_phy_config_t phy_config = {
|
usb_phy_config_t phy_config = {
|
||||||
@ -392,26 +400,34 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
|
|||||||
goto phy_err;
|
goto phy_err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Install HCD
|
||||||
|
hcd_config_t hcd_config = {
|
||||||
|
.intr_flags = config->intr_flags
|
||||||
|
};
|
||||||
|
ret = hcd_install(&hcd_config);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
goto hcd_err;
|
||||||
|
}
|
||||||
|
|
||||||
//Install USBH
|
//Install USBH
|
||||||
usbh_config_t usbh_config = {
|
usbh_config_t usbh_config = {
|
||||||
.notif_cb = notif_callback,
|
.proc_req_cb = proc_req_callback,
|
||||||
.notif_cb_arg = NULL,
|
.proc_req_cb_arg = NULL,
|
||||||
.ctrl_xfer_cb = ctrl_xfer_callback,
|
.ctrl_xfer_cb = ctrl_xfer_callback,
|
||||||
.ctrl_xfer_cb_arg = NULL,
|
.ctrl_xfer_cb_arg = NULL,
|
||||||
.event_cb = dev_event_callback,
|
.event_cb = dev_event_callback,
|
||||||
.event_cb_arg = NULL,
|
.event_cb_arg = NULL,
|
||||||
.hcd_config = {
|
|
||||||
.intr_flags = config->intr_flags,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
ret = usbh_install(&usbh_config);
|
ret = usbh_install(&usbh_config);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto usbh_err;
|
goto usbh_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Install Hub
|
//Install Hub
|
||||||
hub_config_t hub_config = {
|
hub_config_t hub_config = {
|
||||||
.notif_cb = notif_callback,
|
.proc_req_cb = proc_req_callback,
|
||||||
.notif_cb_arg = NULL,
|
.proc_req_cb_arg = NULL,
|
||||||
};
|
};
|
||||||
ret = hub_install(&hub_config);
|
ret = hub_install(&hub_config);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
@ -438,6 +454,8 @@ assign_err:
|
|||||||
hub_err:
|
hub_err:
|
||||||
ESP_ERROR_CHECK(usbh_uninstall());
|
ESP_ERROR_CHECK(usbh_uninstall());
|
||||||
usbh_err:
|
usbh_err:
|
||||||
|
ESP_ERROR_CHECK(hcd_uninstall());
|
||||||
|
hcd_err:
|
||||||
if (host_lib_obj->constant.phy_handle) {
|
if (host_lib_obj->constant.phy_handle) {
|
||||||
ESP_ERROR_CHECK(usb_del_phy(host_lib_obj->constant.phy_handle));
|
ESP_ERROR_CHECK(usb_del_phy(host_lib_obj->constant.phy_handle));
|
||||||
}
|
}
|
||||||
@ -466,19 +484,28 @@ esp_err_t usb_host_uninstall(void)
|
|||||||
|
|
||||||
//Stop the root hub
|
//Stop the root hub
|
||||||
ESP_ERROR_CHECK(hub_root_stop());
|
ESP_ERROR_CHECK(hub_root_stop());
|
||||||
//Uninstall Hub and USBH
|
|
||||||
ESP_ERROR_CHECK(hub_uninstall());
|
|
||||||
ESP_ERROR_CHECK(usbh_uninstall());
|
|
||||||
|
|
||||||
|
//Unassign the host library object
|
||||||
HOST_ENTER_CRITICAL();
|
HOST_ENTER_CRITICAL();
|
||||||
host_lib_t *host_lib_obj = p_host_lib_obj;
|
host_lib_t *host_lib_obj = p_host_lib_obj;
|
||||||
p_host_lib_obj = NULL;
|
p_host_lib_obj = NULL;
|
||||||
HOST_EXIT_CRITICAL();
|
HOST_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Uninstall each layer of the Host stack (listed below) from the highest layer to the lowest
|
||||||
|
- Hub
|
||||||
|
- USBH
|
||||||
|
- HCD
|
||||||
|
- USB PHY
|
||||||
|
*/
|
||||||
|
ESP_ERROR_CHECK(hub_uninstall());
|
||||||
|
ESP_ERROR_CHECK(usbh_uninstall());
|
||||||
|
ESP_ERROR_CHECK(hcd_uninstall());
|
||||||
//If the USB PHY was setup, then delete it
|
//If the USB PHY was setup, then delete it
|
||||||
if (host_lib_obj->constant.phy_handle) {
|
if (host_lib_obj->constant.phy_handle) {
|
||||||
ESP_ERROR_CHECK(usb_del_phy(host_lib_obj->constant.phy_handle));
|
ESP_ERROR_CHECK(usb_del_phy(host_lib_obj->constant.phy_handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Free memory objects
|
//Free memory objects
|
||||||
vSemaphoreDelete(host_lib_obj->constant.mux_lock);
|
vSemaphoreDelete(host_lib_obj->constant.mux_lock);
|
||||||
vSemaphoreDelete(host_lib_obj->constant.event_sem);
|
vSemaphoreDelete(host_lib_obj->constant.event_sem);
|
||||||
@ -508,10 +535,10 @@ esp_err_t usb_host_lib_handle_events(TickType_t timeout_ticks, uint32_t *event_f
|
|||||||
p_host_lib_obj->dynamic.flags.handling_events = 1;
|
p_host_lib_obj->dynamic.flags.handling_events = 1;
|
||||||
while (process_pending_flags) {
|
while (process_pending_flags) {
|
||||||
HOST_EXIT_CRITICAL();
|
HOST_EXIT_CRITICAL();
|
||||||
if (process_pending_flags & PROCESS_PENDING_FLAG_USBH) {
|
if (process_pending_flags & PROCESS_REQUEST_PENDING_FLAG_USBH) {
|
||||||
ESP_ERROR_CHECK(usbh_process());
|
ESP_ERROR_CHECK(usbh_process());
|
||||||
}
|
}
|
||||||
if (process_pending_flags & PROCESS_PENDING_FLAG_HUB) {
|
if (process_pending_flags & PROCESS_REQUEST_PENDING_FLAG_HUB) {
|
||||||
ESP_ERROR_CHECK(hub_process());
|
ESP_ERROR_CHECK(hub_process());
|
||||||
}
|
}
|
||||||
HOST_ENTER_CRITICAL();
|
HOST_ENTER_CRITICAL();
|
||||||
@ -569,33 +596,34 @@ static void _handle_pending_ep(client_t *client_obj)
|
|||||||
//Handle each EP on the pending list
|
//Handle each EP on the pending list
|
||||||
while (!TAILQ_EMPTY(&client_obj->dynamic.pending_ep_tailq)) {
|
while (!TAILQ_EMPTY(&client_obj->dynamic.pending_ep_tailq)) {
|
||||||
//Get the next pending EP.
|
//Get the next pending EP.
|
||||||
endpoint_t *ep_obj = TAILQ_FIRST(&client_obj->dynamic.pending_ep_tailq);
|
ep_wrapper_t *ep_wrap = TAILQ_FIRST(&client_obj->dynamic.pending_ep_tailq);
|
||||||
TAILQ_REMOVE(&client_obj->dynamic.pending_ep_tailq, ep_obj, dynamic.tailq_entry);
|
TAILQ_REMOVE(&client_obj->dynamic.pending_ep_tailq, ep_wrap, dynamic.tailq_entry);
|
||||||
TAILQ_INSERT_TAIL(&client_obj->dynamic.idle_ep_tailq, ep_obj, dynamic.tailq_entry);
|
TAILQ_INSERT_TAIL(&client_obj->dynamic.idle_ep_tailq, ep_wrap, dynamic.tailq_entry);
|
||||||
ep_obj->dynamic.flags.pending = 0;
|
ep_wrap->dynamic.flags.pending = 0;
|
||||||
hcd_pipe_event_t last_event = ep_obj->dynamic.last_event;
|
usbh_ep_event_t last_event = ep_wrap->dynamic.last_event;
|
||||||
uint32_t num_urb_dequeued = 0;
|
uint32_t num_urb_dequeued = 0;
|
||||||
|
|
||||||
HOST_EXIT_CRITICAL();
|
HOST_EXIT_CRITICAL();
|
||||||
//Handle pipe event
|
//Handle pipe event
|
||||||
switch (last_event) {
|
switch (last_event) {
|
||||||
case HCD_PIPE_EVENT_ERROR_XFER:
|
case USBH_EP_EVENT_ERROR_XFER:
|
||||||
case HCD_PIPE_EVENT_ERROR_URB_NOT_AVAIL:
|
case USBH_EP_EVENT_ERROR_URB_NOT_AVAIL:
|
||||||
case HCD_PIPE_EVENT_ERROR_OVERFLOW:
|
case USBH_EP_EVENT_ERROR_OVERFLOW:
|
||||||
case HCD_PIPE_EVENT_ERROR_STALL:
|
case USBH_EP_EVENT_ERROR_STALL:
|
||||||
//The pipe is now stalled. Flush all pending URBs
|
//The endpoint is now stalled. Flush all pending URBs
|
||||||
ESP_ERROR_CHECK(hcd_pipe_command(ep_obj->constant.pipe_hdl, HCD_PIPE_CMD_FLUSH));
|
ESP_ERROR_CHECK(usbh_ep_command(ep_wrap->constant.ep_hdl, USBH_EP_CMD_FLUSH));
|
||||||
//All URBs in this pipe are now retired waiting to be dequeued. Fall through to dequeue them
|
//All URBs in this pipe are now retired waiting to be dequeued. Fall through to dequeue them
|
||||||
__attribute__((fallthrough));
|
__attribute__((fallthrough));
|
||||||
case HCD_PIPE_EVENT_URB_DONE: {
|
case USBH_EP_EVENT_URB_DONE: {
|
||||||
//Dequeue all URBs and run their transfer callback
|
//Dequeue all URBs and run their transfer callback
|
||||||
urb_t *urb = hcd_urb_dequeue(ep_obj->constant.pipe_hdl);
|
urb_t *urb;
|
||||||
|
usbh_ep_dequeue_urb(ep_wrap->constant.ep_hdl, &urb);
|
||||||
while (urb != NULL) {
|
while (urb != NULL) {
|
||||||
//Clear the transfer's inflight flag to indicate the transfer is no longer inflight
|
//Clear the transfer's inflight flag to indicate the transfer is no longer inflight
|
||||||
urb->usb_host_inflight = false;
|
urb->usb_host_inflight = false;
|
||||||
urb->transfer.callback(&urb->transfer);
|
urb->transfer.callback(&urb->transfer);
|
||||||
num_urb_dequeued++;
|
num_urb_dequeued++;
|
||||||
urb = hcd_urb_dequeue(ep_obj->constant.pipe_hdl);
|
usbh_ep_dequeue_urb(ep_wrap->constant.ep_hdl, &urb);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -606,8 +634,8 @@ static void _handle_pending_ep(client_t *client_obj)
|
|||||||
HOST_ENTER_CRITICAL();
|
HOST_ENTER_CRITICAL();
|
||||||
|
|
||||||
//Update the endpoint's number of URB's inflight
|
//Update the endpoint's number of URB's inflight
|
||||||
assert(num_urb_dequeued <= ep_obj->dynamic.num_urb_inflight);
|
assert(num_urb_dequeued <= ep_wrap->dynamic.num_urb_inflight);
|
||||||
ep_obj->dynamic.num_urb_inflight -= num_urb_dequeued;
|
ep_wrap->dynamic.num_urb_inflight -= num_urb_dequeued;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -920,52 +948,53 @@ esp_err_t usb_host_get_active_config_descriptor(usb_device_handle_t dev_hdl, con
|
|||||||
|
|
||||||
// ----------------------- Private -------------------------
|
// ----------------------- Private -------------------------
|
||||||
|
|
||||||
static esp_err_t endpoint_alloc(usb_device_handle_t dev_hdl, const usb_ep_desc_t *ep_desc, interface_t *intf_obj, endpoint_t **ep_obj_ret)
|
static esp_err_t ep_wrapper_alloc(usb_device_handle_t dev_hdl, const usb_ep_desc_t *ep_desc, interface_t *intf_obj, ep_wrapper_t **ep_wrap_ret)
|
||||||
{
|
{
|
||||||
endpoint_t *ep_obj = heap_caps_calloc(1, sizeof(endpoint_t), MALLOC_CAP_DEFAULT);
|
ep_wrapper_t *ep_wrap = heap_caps_calloc(1, sizeof(ep_wrapper_t), MALLOC_CAP_DEFAULT);
|
||||||
if (ep_obj == NULL) {
|
if (ep_wrap == NULL) {
|
||||||
return ESP_ERR_NO_MEM;
|
return ESP_ERR_NO_MEM;
|
||||||
}
|
}
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
|
usbh_ep_handle_t ep_hdl;
|
||||||
usbh_ep_config_t ep_config = {
|
usbh_ep_config_t ep_config = {
|
||||||
.ep_desc = ep_desc,
|
.bInterfaceNumber = intf_obj->constant.intf_desc->bInterfaceNumber,
|
||||||
.pipe_cb = pipe_callback,
|
.bAlternateSetting = intf_obj->constant.intf_desc->bAlternateSetting,
|
||||||
.pipe_cb_arg = (void *)ep_obj,
|
.bEndpointAddress = ep_desc->bEndpointAddress,
|
||||||
.context = (void *)ep_obj,
|
.ep_cb = endpoint_callback,
|
||||||
|
.ep_cb_arg = (void *)ep_wrap,
|
||||||
|
.context = (void *)ep_wrap,
|
||||||
};
|
};
|
||||||
hcd_pipe_handle_t pipe_hdl;
|
ret = usbh_ep_alloc(dev_hdl, &ep_config, &ep_hdl);
|
||||||
ret = usbh_ep_alloc(dev_hdl, &ep_config, &pipe_hdl);
|
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto ep_alloc_err;
|
goto alloc_err;
|
||||||
}
|
}
|
||||||
//Initialize endpoint object
|
//Initialize endpoint wrapper item
|
||||||
ep_obj->constant.pipe_hdl = pipe_hdl;
|
ep_wrap->constant.ep_hdl = ep_hdl;
|
||||||
ep_obj->constant.ep_desc = ep_desc;
|
ep_wrap->constant.intf_obj = intf_obj;
|
||||||
ep_obj->constant.intf_obj = intf_obj;
|
|
||||||
//Write back result
|
//Write back result
|
||||||
*ep_obj_ret = ep_obj;
|
*ep_wrap_ret = ep_wrap;
|
||||||
ret = ESP_OK;
|
ret = ESP_OK;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ep_alloc_err:
|
alloc_err:
|
||||||
heap_caps_free(ep_obj);
|
heap_caps_free(ep_wrap);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void endpoint_free(usb_device_handle_t dev_hdl, endpoint_t *ep_obj)
|
static void ep_wrapper_free(usb_device_handle_t dev_hdl, ep_wrapper_t *ep_wrap)
|
||||||
{
|
{
|
||||||
if (ep_obj == NULL) {
|
if (ep_wrap == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//Free the underlying endpoint
|
//Free the underlying endpoint
|
||||||
ESP_ERROR_CHECK(usbh_ep_free(dev_hdl, ep_obj->constant.ep_desc->bEndpointAddress));
|
ESP_ERROR_CHECK(usbh_ep_free(ep_wrap->constant.ep_hdl));
|
||||||
//Free the endpoint object
|
//Free the endpoint wrapper item
|
||||||
heap_caps_free(ep_obj);
|
heap_caps_free(ep_wrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static interface_t *interface_alloc(client_t *client_obj, usb_device_handle_t dev_hdl, const usb_intf_desc_t *intf_desc)
|
static interface_t *interface_alloc(client_t *client_obj, usb_device_handle_t dev_hdl, const usb_intf_desc_t *intf_desc)
|
||||||
{
|
{
|
||||||
interface_t *intf_obj = heap_caps_calloc(1, sizeof(interface_t) + (sizeof(endpoint_t *) * intf_desc->bNumEndpoints), MALLOC_CAP_DEFAULT);
|
interface_t *intf_obj = heap_caps_calloc(1, sizeof(interface_t) + (sizeof(ep_wrapper_t *) * intf_desc->bNumEndpoints), MALLOC_CAP_DEFAULT);
|
||||||
if (intf_obj == NULL) {
|
if (intf_obj == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -1011,18 +1040,18 @@ static esp_err_t interface_claim(client_t *client_obj, usb_device_handle_t dev_h
|
|||||||
ret = ESP_ERR_NOT_FOUND;
|
ret = ESP_ERR_NOT_FOUND;
|
||||||
goto ep_alloc_err;
|
goto ep_alloc_err;
|
||||||
}
|
}
|
||||||
//Allocate the endpoint
|
//Allocate the endpoint wrapper item
|
||||||
endpoint_t *ep_obj;
|
ep_wrapper_t *ep_wrap;
|
||||||
ret = endpoint_alloc(dev_hdl, ep_desc, intf_obj, &ep_obj);
|
ret = ep_wrapper_alloc(dev_hdl, ep_desc, intf_obj, &ep_wrap);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto ep_alloc_err;
|
goto ep_alloc_err;
|
||||||
}
|
}
|
||||||
//Fill the interface object with the allocated endpoints
|
//Fill the interface object with the allocated endpoints
|
||||||
intf_obj->constant.endpoints[i] = ep_obj;
|
intf_obj->constant.endpoints[i] = ep_wrap;
|
||||||
}
|
}
|
||||||
//Add interface object to client (safe because we have already taken the mutex)
|
//Add interface object to client (safe because we have already taken the mutex)
|
||||||
TAILQ_INSERT_TAIL(&client_obj->mux_protected.interface_tailq, intf_obj, mux_protected.tailq_entry);
|
TAILQ_INSERT_TAIL(&client_obj->mux_protected.interface_tailq, intf_obj, mux_protected.tailq_entry);
|
||||||
//Add each endpoint to the client's endpoint list
|
//Add each endpoint wrapper item to the client's endpoint list
|
||||||
HOST_ENTER_CRITICAL();
|
HOST_ENTER_CRITICAL();
|
||||||
for (int i = 0; i < intf_desc->bNumEndpoints; i++) {
|
for (int i = 0; i < intf_desc->bNumEndpoints; i++) {
|
||||||
TAILQ_INSERT_TAIL(&client_obj->dynamic.idle_ep_tailq, intf_obj->constant.endpoints[i], dynamic.tailq_entry);
|
TAILQ_INSERT_TAIL(&client_obj->dynamic.idle_ep_tailq, intf_obj->constant.endpoints[i], dynamic.tailq_entry);
|
||||||
@ -1035,7 +1064,7 @@ static esp_err_t interface_claim(client_t *client_obj, usb_device_handle_t dev_h
|
|||||||
|
|
||||||
ep_alloc_err:
|
ep_alloc_err:
|
||||||
for (int i = 0; i < intf_desc->bNumEndpoints; i++) {
|
for (int i = 0; i < intf_desc->bNumEndpoints; i++) {
|
||||||
endpoint_free(dev_hdl, intf_obj->constant.endpoints[i]);
|
ep_wrapper_free(dev_hdl, intf_obj->constant.endpoints[i]);
|
||||||
intf_obj->constant.endpoints[i] = NULL;
|
intf_obj->constant.endpoints[i] = NULL;
|
||||||
}
|
}
|
||||||
interface_free(intf_obj);
|
interface_free(intf_obj);
|
||||||
@ -1061,12 +1090,13 @@ static esp_err_t interface_release(client_t *client_obj, usb_device_handle_t dev
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Check that all endpoints in the interface are in a state to be freed
|
//Check that all endpoints in the interface are in a state to be freed
|
||||||
|
//Todo: Check that each EP is halted before allowing them to be freed (IDF-7273)
|
||||||
HOST_ENTER_CRITICAL();
|
HOST_ENTER_CRITICAL();
|
||||||
bool can_free = true;
|
bool can_free = true;
|
||||||
for (int i = 0; i < intf_obj->constant.intf_desc->bNumEndpoints; i++) {
|
for (int i = 0; i < intf_obj->constant.intf_desc->bNumEndpoints; i++) {
|
||||||
endpoint_t *ep_obj = intf_obj->constant.endpoints[i];
|
ep_wrapper_t *ep_wrap = intf_obj->constant.endpoints[i];
|
||||||
//Endpoint must not be on the pending list and must not have inflight URBs
|
//Endpoint must not be on the pending list and must not have inflight URBs
|
||||||
if (ep_obj->dynamic.num_urb_inflight != 0 || ep_obj->dynamic.flags.pending) {
|
if (ep_wrap->dynamic.num_urb_inflight != 0 || ep_wrap->dynamic.flags.pending) {
|
||||||
can_free = false;
|
can_free = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1076,7 +1106,7 @@ static esp_err_t interface_release(client_t *client_obj, usb_device_handle_t dev
|
|||||||
ret = ESP_ERR_INVALID_STATE;
|
ret = ESP_ERR_INVALID_STATE;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
//Proceed to remove all endpoint objects from list
|
//Proceed to remove all endpoint wrapper items from the list
|
||||||
for (int i = 0; i < intf_obj->constant.intf_desc->bNumEndpoints; i++) {
|
for (int i = 0; i < intf_obj->constant.intf_desc->bNumEndpoints; i++) {
|
||||||
TAILQ_REMOVE(&client_obj->dynamic.idle_ep_tailq, intf_obj->constant.endpoints[i], dynamic.tailq_entry);
|
TAILQ_REMOVE(&client_obj->dynamic.idle_ep_tailq, intf_obj->constant.endpoints[i], dynamic.tailq_entry);
|
||||||
}
|
}
|
||||||
@ -1087,7 +1117,7 @@ static esp_err_t interface_release(client_t *client_obj, usb_device_handle_t dev
|
|||||||
|
|
||||||
//Free each endpoint in the interface
|
//Free each endpoint in the interface
|
||||||
for (int i = 0; i < intf_obj->constant.intf_desc->bNumEndpoints; i++) {
|
for (int i = 0; i < intf_obj->constant.intf_desc->bNumEndpoints; i++) {
|
||||||
endpoint_free(dev_hdl, intf_obj->constant.endpoints[i]);
|
ep_wrapper_free(dev_hdl, intf_obj->constant.endpoints[i]);
|
||||||
intf_obj->constant.endpoints[i] = NULL;
|
intf_obj->constant.endpoints[i] = NULL;
|
||||||
}
|
}
|
||||||
//Free the interface object itself
|
//Free the interface object itself
|
||||||
@ -1167,13 +1197,14 @@ esp_err_t usb_host_interface_release(usb_host_client_handle_t client_hdl, usb_de
|
|||||||
esp_err_t usb_host_endpoint_halt(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress)
|
esp_err_t usb_host_endpoint_halt(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress)
|
||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
endpoint_t *ep_obj = NULL;
|
usbh_ep_handle_t ep_hdl;
|
||||||
ret = usbh_ep_get_context(dev_hdl, bEndpointAddress, (void **)&ep_obj);
|
|
||||||
|
ret = usbh_ep_get_handle(dev_hdl, bEndpointAddress, &ep_hdl);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
assert(ep_obj != NULL);
|
ret = usbh_ep_command(ep_hdl, USBH_EP_CMD_HALT);
|
||||||
ret = hcd_pipe_command(ep_obj->constant.pipe_hdl, HCD_PIPE_CMD_HALT);
|
|
||||||
exit:
|
exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1181,13 +1212,14 @@ exit:
|
|||||||
esp_err_t usb_host_endpoint_flush(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress)
|
esp_err_t usb_host_endpoint_flush(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress)
|
||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
endpoint_t *ep_obj = NULL;
|
usbh_ep_handle_t ep_hdl;
|
||||||
ret = usbh_ep_get_context(dev_hdl, bEndpointAddress, (void **)&ep_obj);
|
|
||||||
|
ret = usbh_ep_get_handle(dev_hdl, bEndpointAddress, &ep_hdl);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
assert(ep_obj != NULL);
|
ret = usbh_ep_command(ep_hdl, USBH_EP_CMD_FLUSH);
|
||||||
ret = hcd_pipe_command(ep_obj->constant.pipe_hdl, HCD_PIPE_CMD_FLUSH);
|
|
||||||
exit:
|
exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1195,73 +1227,20 @@ exit:
|
|||||||
esp_err_t usb_host_endpoint_clear(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress)
|
esp_err_t usb_host_endpoint_clear(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress)
|
||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
endpoint_t *ep_obj = NULL;
|
usbh_ep_handle_t ep_hdl;
|
||||||
ret = usbh_ep_get_context(dev_hdl, bEndpointAddress, (void **)&ep_obj);
|
|
||||||
|
ret = usbh_ep_get_handle(dev_hdl, bEndpointAddress, &ep_hdl);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
assert(ep_obj != NULL);
|
ret = usbh_ep_command(ep_hdl, USBH_EP_CMD_CLEAR);
|
||||||
ret = hcd_pipe_command(ep_obj->constant.pipe_hdl, HCD_PIPE_CMD_CLEAR);
|
|
||||||
exit:
|
exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------ Asynchronous I/O ---------------------------------------------------
|
// ------------------------------------------------ Asynchronous I/O ---------------------------------------------------
|
||||||
|
|
||||||
// ----------------------- Private -------------------------
|
|
||||||
|
|
||||||
static bool transfer_check(usb_transfer_t *transfer, usb_transfer_type_t type, int mps, bool is_in)
|
|
||||||
{
|
|
||||||
if (transfer->callback == NULL) {
|
|
||||||
ESP_LOGE(USB_HOST_TAG, "Transfer callback is NULL");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//Check that the total transfer length does not exceed data buffer size
|
|
||||||
if (transfer->num_bytes > transfer->data_buffer_size) {
|
|
||||||
ESP_LOGE(USB_HOST_TAG, "Transfer num_bytes > data_buffer_size");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (type == USB_TRANSFER_TYPE_CTRL) {
|
|
||||||
//Check that num_bytes and wLength are set correctly
|
|
||||||
usb_setup_packet_t *setup_pkt = (usb_setup_packet_t *)transfer->data_buffer;
|
|
||||||
if (transfer->num_bytes != sizeof(usb_setup_packet_t) + setup_pkt->wLength) {
|
|
||||||
ESP_LOGE(USB_HOST_TAG, "Control transfer num_bytes wLength mismatch");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (type == USB_TRANSFER_TYPE_ISOCHRONOUS) {
|
|
||||||
//Check that there is at least one isochronous packet descriptor
|
|
||||||
if (transfer->num_isoc_packets <= 0) {
|
|
||||||
ESP_LOGE(USB_HOST_TAG, "ISOC transfer has 0 packet descriptors");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//Check that sum of all packet lengths add up to transfer length
|
|
||||||
//If IN, check that each packet length is integer multiple of MPS
|
|
||||||
int total_num_bytes = 0;
|
|
||||||
bool mod_mps_all_zero = true;
|
|
||||||
for (int i = 0; i < transfer->num_isoc_packets; i++) {
|
|
||||||
total_num_bytes += transfer->isoc_packet_desc[i].num_bytes;
|
|
||||||
if (transfer->isoc_packet_desc[i].num_bytes % mps != 0) {
|
|
||||||
mod_mps_all_zero = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (transfer->num_bytes != total_num_bytes) {
|
|
||||||
ESP_LOGE(USB_HOST_TAG, "ISOC transfer num_bytes not equal to total num_bytes of all packets");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (is_in && !mod_mps_all_zero) {
|
|
||||||
ESP_LOGE(USB_HOST_TAG, "ISOC IN transfer all packets num_bytes must be integer multiple of MPS");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Check that IN transfers are integer multiple of MPS
|
|
||||||
if (is_in && (transfer->num_bytes % mps != 0)) {
|
|
||||||
ESP_LOGE(USB_HOST_TAG, "IN transfer num_bytes must be integer multiple of MPS");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------- Public --------------------------
|
// ----------------------- Public --------------------------
|
||||||
|
|
||||||
esp_err_t usb_host_transfer_alloc(size_t data_buffer_size, int num_isoc_packets, usb_transfer_t **transfer)
|
esp_err_t usb_host_transfer_alloc(size_t data_buffer_size, int num_isoc_packets, usb_transfer_t **transfer)
|
||||||
@ -1290,39 +1269,34 @@ esp_err_t usb_host_transfer_submit(usb_transfer_t *transfer)
|
|||||||
//Check that transfer and target endpoint are valid
|
//Check that transfer and target endpoint are valid
|
||||||
HOST_CHECK(transfer->device_handle != NULL, ESP_ERR_INVALID_ARG); //Target device must be set
|
HOST_CHECK(transfer->device_handle != NULL, ESP_ERR_INVALID_ARG); //Target device must be set
|
||||||
HOST_CHECK((transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK) != 0, ESP_ERR_INVALID_ARG);
|
HOST_CHECK((transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK) != 0, ESP_ERR_INVALID_ARG);
|
||||||
endpoint_t *ep_obj = NULL;
|
|
||||||
|
usbh_ep_handle_t ep_hdl;
|
||||||
|
ep_wrapper_t *ep_wrap = NULL;
|
||||||
urb_t *urb_obj = __containerof(transfer, urb_t, transfer);
|
urb_t *urb_obj = __containerof(transfer, urb_t, transfer);
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
ret = usbh_ep_get_context(transfer->device_handle, transfer->bEndpointAddress, (void **)&ep_obj);
|
|
||||||
|
ret = usbh_ep_get_handle(transfer->device_handle, transfer->bEndpointAddress, &ep_hdl);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
assert(ep_obj != NULL);
|
ep_wrap = usbh_ep_get_context(ep_hdl);
|
||||||
HOST_CHECK(transfer_check(transfer,
|
assert(ep_wrap != NULL);
|
||||||
USB_EP_DESC_GET_XFERTYPE(ep_obj->constant.ep_desc),
|
|
||||||
USB_EP_DESC_GET_MPS(ep_obj->constant.ep_desc),
|
|
||||||
transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK), ESP_ERR_INVALID_ARG);
|
|
||||||
//Check that we are not submitting a transfer already inflight
|
//Check that we are not submitting a transfer already inflight
|
||||||
HOST_CHECK(!urb_obj->usb_host_inflight, ESP_ERR_NOT_FINISHED);
|
HOST_CHECK(!urb_obj->usb_host_inflight, ESP_ERR_NOT_FINISHED);
|
||||||
urb_obj->usb_host_inflight = true;
|
urb_obj->usb_host_inflight = true;
|
||||||
HOST_ENTER_CRITICAL();
|
HOST_ENTER_CRITICAL();
|
||||||
ep_obj->dynamic.num_urb_inflight++;
|
ep_wrap->dynamic.num_urb_inflight++;
|
||||||
HOST_EXIT_CRITICAL();
|
HOST_EXIT_CRITICAL();
|
||||||
//Check if pipe is in a state to enqueue URBs
|
|
||||||
if (hcd_pipe_get_state(ep_obj->constant.pipe_hdl) != HCD_PIPE_STATE_ACTIVE) {
|
ret = usbh_ep_enqueue_urb(ep_hdl, urb_obj);
|
||||||
ret = ESP_ERR_INVALID_STATE;
|
|
||||||
goto hcd_err;
|
|
||||||
}
|
|
||||||
ret = hcd_urb_enqueue(ep_obj->constant.pipe_hdl, urb_obj);
|
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto hcd_err;
|
goto submit_err;
|
||||||
}
|
}
|
||||||
ret = ESP_OK;
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
hcd_err:
|
submit_err:
|
||||||
HOST_ENTER_CRITICAL();
|
HOST_ENTER_CRITICAL();
|
||||||
ep_obj->dynamic.num_urb_inflight--;
|
ep_wrap->dynamic.num_urb_inflight--;
|
||||||
HOST_EXIT_CRITICAL();
|
HOST_EXIT_CRITICAL();
|
||||||
urb_obj->usb_host_inflight = false;
|
urb_obj->usb_host_inflight = false;
|
||||||
err:
|
err:
|
||||||
@ -1332,17 +1306,12 @@ err:
|
|||||||
esp_err_t usb_host_transfer_submit_control(usb_host_client_handle_t client_hdl, usb_transfer_t *transfer)
|
esp_err_t usb_host_transfer_submit_control(usb_host_client_handle_t client_hdl, usb_transfer_t *transfer)
|
||||||
{
|
{
|
||||||
HOST_CHECK(client_hdl != NULL && transfer != NULL, ESP_ERR_INVALID_ARG);
|
HOST_CHECK(client_hdl != NULL && transfer != NULL, ESP_ERR_INVALID_ARG);
|
||||||
|
|
||||||
//Check that control transfer is valid
|
//Check that control transfer is valid
|
||||||
HOST_CHECK(transfer->device_handle != NULL, ESP_ERR_INVALID_ARG); //Target device must be set
|
HOST_CHECK(transfer->device_handle != NULL, ESP_ERR_INVALID_ARG); //Target device must be set
|
||||||
usb_device_handle_t dev_hdl = transfer->device_handle;
|
|
||||||
bool xfer_is_in = ((usb_setup_packet_t *)transfer->data_buffer)->bmRequestType & USB_BM_REQUEST_TYPE_DIR_IN;
|
|
||||||
usb_device_info_t dev_info;
|
|
||||||
ESP_ERROR_CHECK(usbh_dev_get_info(dev_hdl, &dev_info));
|
|
||||||
HOST_CHECK(transfer_check(transfer, USB_TRANSFER_TYPE_CTRL, dev_info.bMaxPacketSize0, xfer_is_in), ESP_ERR_INVALID_ARG);
|
|
||||||
//Control transfers must be targeted at EP 0
|
//Control transfers must be targeted at EP 0
|
||||||
HOST_CHECK((transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK) == 0, ESP_ERR_INVALID_ARG);
|
HOST_CHECK((transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK) == 0, ESP_ERR_INVALID_ARG);
|
||||||
|
|
||||||
|
usb_device_handle_t dev_hdl = transfer->device_handle;
|
||||||
urb_t *urb_obj = __containerof(transfer, urb_t, transfer);
|
urb_t *urb_obj = __containerof(transfer, urb_t, transfer);
|
||||||
//Check that we are not submitting a transfer already inflight
|
//Check that we are not submitting a transfer already inflight
|
||||||
HOST_CHECK(!urb_obj->usb_host_inflight, ESP_ERR_NOT_FINISHED);
|
HOST_CHECK(!urb_obj->usb_host_inflight, ESP_ERR_NOT_FINISHED);
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user