| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328 |
- /*
- * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- /*
- Warning: The USB Host Library API is still a beta version and may be subject to change
- */
- #include <stdlib.h>
- #include <stdint.h>
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "freertos/queue.h"
- #include "freertos/semphr.h"
- #include "esp_err.h"
- #include "esp_log.h"
- #include "esp_heap_caps.h"
- #include "hub.h"
- #include "usbh.h"
- #include "hcd.h"
- #include "esp_private/usb_phy.h"
- #include "usb/usb_host.h"
- static portMUX_TYPE host_lock = portMUX_INITIALIZER_UNLOCKED;
- #define HOST_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&host_lock)
- #define HOST_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&host_lock)
- #define HOST_ENTER_CRITICAL() portENTER_CRITICAL(&host_lock)
- #define HOST_EXIT_CRITICAL() portEXIT_CRITICAL(&host_lock)
- #define HOST_ENTER_CRITICAL_SAFE() portENTER_CRITICAL_SAFE(&host_lock)
- #define HOST_EXIT_CRITICAL_SAFE() portEXIT_CRITICAL_SAFE(&host_lock)
- #define HOST_CHECK(cond, ret_val) ({ \
- if (!(cond)) { \
- return (ret_val); \
- } \
- })
- #define HOST_CHECK_FROM_CRIT(cond, ret_val) ({ \
- if (!(cond)) { \
- HOST_EXIT_CRITICAL(); \
- return ret_val; \
- } \
- })
- #define PROCESS_REQUEST_PENDING_FLAG_USBH 0x01
- #define PROCESS_REQUEST_PENDING_FLAG_HUB 0x02
- typedef struct ep_wrapper_s ep_wrapper_t;
- typedef struct interface_s interface_t;
- typedef struct client_s client_t;
- struct ep_wrapper_s {
- // Dynamic members require a critical section
- struct {
- TAILQ_ENTRY(ep_wrapper_s) tailq_entry;
- union {
- struct {
- uint32_t pending: 1;
- uint32_t reserved31: 31;
- };
- } flags;
- uint32_t num_urb_inflight;
- usbh_ep_event_t last_event;
- } dynamic;
- // Constant members do no change after claiming the interface thus do not require a critical section
- struct {
- usbh_ep_handle_t ep_hdl;
- interface_t *intf_obj;
- } constant;
- };
- struct interface_s {
- // Dynamic members require a critical section
- struct {
- TAILQ_ENTRY(interface_s) tailq_entry;
- } mux_protected;
- // Constant members do no change after claiming the interface thus do not require a critical section
- struct {
- const usb_intf_desc_t *intf_desc;
- usb_device_handle_t dev_hdl;
- client_t *client_obj;
- ep_wrapper_t *endpoints[0];
- } constant;
- };
- struct client_s {
- // Dynamic members require a critical section
- struct {
- TAILQ_ENTRY(client_s) tailq_entry;
- TAILQ_HEAD(tailhead_pending_ep, ep_wrapper_s) pending_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;
- union {
- struct {
- uint32_t events_pending: 1;
- uint32_t handling_events: 1;
- uint32_t blocked: 1;
- uint32_t taking_mux: 1;
- uint32_t reserved4: 4;
- uint32_t num_intf_claimed: 8;
- uint32_t reserved16: 16;
- };
- uint32_t val;
- } flags;
- uint32_t num_done_ctrl_xfer;
- uint32_t opened_dev_addr_map;
- } dynamic;
- // Mux protected members must be protected by host library the mux_lock when accessed
- struct {
- TAILQ_HEAD(tailhead_interfaces, interface_s) interface_tailq;
- } mux_protected;
- // Constant members do no change after registration thus do not require a critical section
- struct {
- SemaphoreHandle_t event_sem;
- usb_host_client_event_cb_t event_callback;
- void *callback_arg;
- QueueHandle_t event_msg_queue;
- } constant;
- };
- typedef struct {
- // Dynamic members require a critical section
- struct {
- // Access to these should be done in a critical section
- uint32_t process_pending_flags;
- uint32_t lib_event_flags;
- union {
- struct {
- uint32_t process_pending: 1;
- uint32_t handling_events: 1;
- uint32_t blocked: 1;
- uint32_t reserved5: 5;
- uint32_t num_clients: 8;
- uint32_t reserved16: 16;
- };
- uint32_t val;
- } flags;
- } dynamic;
- // Mux protected members must be protected by host library the mux_lock when accessed
- struct {
- TAILQ_HEAD(tailhead_clients, client_s) client_tailq; // List of all clients registered
- } mux_protected;
- // Constant members do no change after installation thus do not require a critical section
- struct {
- SemaphoreHandle_t event_sem;
- SemaphoreHandle_t mux_lock;
- usb_phy_handle_t phy_handle; // Will be NULL if host library is installed with skip_phy_setup
- } constant;
- } host_lib_t;
- static host_lib_t *p_host_lib_obj = NULL;
- const char *USB_HOST_TAG = "USB HOST";
- // ----------------------------------------------------- Helpers -------------------------------------------------------
- static inline void _record_client_opened_device(client_t *client_obj, uint8_t dev_addr)
- {
- assert(dev_addr != 0);
- client_obj->dynamic.opened_dev_addr_map |= (1 << (dev_addr - 1));
- }
- static inline void _clear_client_opened_device(client_t *client_obj, uint8_t dev_addr)
- {
- assert(dev_addr != 0);
- client_obj->dynamic.opened_dev_addr_map &= ~(1 << (dev_addr - 1));
- }
- static inline bool _check_client_opened_device(client_t *client_obj, uint8_t dev_addr)
- {
- assert(dev_addr != 0);
- return (client_obj->dynamic.opened_dev_addr_map & (1 << (dev_addr - 1)));
- }
- static bool _unblock_client(client_t *client_obj, bool in_isr)
- {
- bool send_sem;
- if (!client_obj->dynamic.flags.events_pending && !client_obj->dynamic.flags.handling_events) {
- client_obj->dynamic.flags.events_pending = 1;
- send_sem = true;
- } else {
- send_sem = false;
- }
- HOST_EXIT_CRITICAL_SAFE();
- bool yield = false;
- if (send_sem) {
- if (in_isr) {
- BaseType_t xTaskWoken = pdFALSE;
- xSemaphoreGiveFromISR(client_obj->constant.event_sem, &xTaskWoken);
- yield = (xTaskWoken == pdTRUE);
- } else {
- xSemaphoreGive(client_obj->constant.event_sem);
- }
- }
- HOST_ENTER_CRITICAL_SAFE();
- return yield;
- }
- static bool _unblock_lib(bool in_isr)
- {
- bool send_sem;
- if (!p_host_lib_obj->dynamic.flags.process_pending && !p_host_lib_obj->dynamic.flags.handling_events) {
- p_host_lib_obj->dynamic.flags.process_pending = 1;
- send_sem = true;
- } else {
- send_sem = false;
- }
- HOST_EXIT_CRITICAL_SAFE();
- bool yield = false;
- if (send_sem) {
- if (in_isr) {
- BaseType_t xTaskWoken = pdFALSE;
- xSemaphoreGiveFromISR(p_host_lib_obj->constant.event_sem, &xTaskWoken);
- yield = (xTaskWoken == pdTRUE);
- } else {
- xSemaphoreGive(p_host_lib_obj->constant.event_sem);
- }
- }
- HOST_ENTER_CRITICAL_SAFE();
- return yield;
- }
- static void send_event_msg_to_clients(const usb_host_client_event_msg_t *event_msg, bool send_to_all, uint8_t opened_dev_addr)
- {
- // Lock client list
- xSemaphoreTake(p_host_lib_obj->constant.mux_lock, portMAX_DELAY);
- // Send event message to relevant or all clients
- client_t *client_obj;
- TAILQ_FOREACH(client_obj, &p_host_lib_obj->mux_protected.client_tailq, dynamic.tailq_entry) {
- if (!send_to_all) {
- // Check if client opened the device
- HOST_ENTER_CRITICAL();
- bool send = _check_client_opened_device(client_obj, opened_dev_addr);
- HOST_EXIT_CRITICAL();
- if (!send) {
- continue;
- }
- }
- // Send the event message
- if (xQueueSend(client_obj->constant.event_msg_queue, event_msg, 0) == pdTRUE) {
- HOST_ENTER_CRITICAL();
- _unblock_client(client_obj, false);
- HOST_EXIT_CRITICAL();
- } else {
- ESP_LOGE(USB_HOST_TAG, "Client event message queue full");
- }
- }
- // Unlock client list
- xSemaphoreGive(p_host_lib_obj->constant.mux_lock);
- }
- // ---------------------------------------------------- Callbacks ------------------------------------------------------
- // ------------------- Library Related ---------------------
- static bool proc_req_callback(usb_proc_req_source_t source, bool in_isr, void *arg)
- {
- HOST_ENTER_CRITICAL_SAFE();
- // Store the processing request source
- switch (source) {
- case USB_PROC_REQ_SOURCE_USBH:
- p_host_lib_obj->dynamic.process_pending_flags |= PROCESS_REQUEST_PENDING_FLAG_USBH;
- break;
- case USB_PROC_REQ_SOURCE_HUB:
- p_host_lib_obj->dynamic.process_pending_flags |= PROCESS_REQUEST_PENDING_FLAG_HUB;
- break;
- }
- bool yield = _unblock_lib(in_isr);
- HOST_EXIT_CRITICAL_SAFE();
- return yield;
- }
- static void ctrl_xfer_callback(usb_device_handle_t dev_hdl, urb_t *urb, void *arg)
- {
- assert(urb->usb_host_client != NULL);
- // Redistribute done control transfer to the clients that submitted them
- client_t *client_obj = (client_t *)urb->usb_host_client;
- HOST_ENTER_CRITICAL();
- TAILQ_INSERT_TAIL(&client_obj->dynamic.done_ctrl_xfer_tailq, urb, tailq_entry);
- client_obj->dynamic.num_done_ctrl_xfer++;
- _unblock_client(client_obj, false);
- HOST_EXIT_CRITICAL();
- }
- static void dev_event_callback(usb_device_handle_t dev_hdl, usbh_event_t usbh_event, void *arg)
- {
- // Check usbh_event. The data type of event_arg depends on the type of event
- switch (usbh_event) {
- case USBH_EVENT_DEV_NEW: {
- // Prepare a NEW_DEV client event message, the send it to all clients
- uint8_t dev_addr;
- ESP_ERROR_CHECK(usbh_dev_get_addr(dev_hdl, &dev_addr));
- usb_host_client_event_msg_t event_msg = {
- .event = USB_HOST_CLIENT_EVENT_NEW_DEV,
- .new_dev.address = dev_addr,
- };
- send_event_msg_to_clients(&event_msg, true, 0);
- break;
- }
- case USBH_EVENT_DEV_GONE: {
- // Prepare event msg, send only to clients that have opened the device
- uint8_t dev_addr;
- ESP_ERROR_CHECK(usbh_dev_get_addr(dev_hdl, &dev_addr));
- usb_host_client_event_msg_t event_msg = {
- .event = USB_HOST_CLIENT_EVENT_DEV_GONE,
- .dev_gone.dev_hdl = dev_hdl,
- };
- send_event_msg_to_clients(&event_msg, false, dev_addr);
- break;
- }
- case USBH_EVENT_DEV_ALL_FREE: {
- // Notify the lib handler that all devices are free
- HOST_ENTER_CRITICAL();
- p_host_lib_obj->dynamic.lib_event_flags |= USB_HOST_LIB_EVENT_FLAGS_ALL_FREE;
- _unblock_lib(false);
- HOST_EXIT_CRITICAL();
- break;
- }
- default:
- abort(); // Should never occur
- break;
- }
- }
- // ------------------- Client Related ----------------------
- static bool endpoint_callback(usbh_ep_handle_t ep_hdl, usbh_ep_event_t ep_event, void *user_arg, bool in_isr)
- {
- ep_wrapper_t *ep_wrap = (ep_wrapper_t *)user_arg;
- client_t *client_obj = (client_t *)ep_wrap->constant.intf_obj->constant.client_obj;
- 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.
- ep_wrap->dynamic.last_event = ep_event;
- // Add the EP to the client's pending list if it's not in the list already
- if (!ep_wrap->dynamic.flags.pending) {
- ep_wrap->dynamic.flags.pending = 1;
- TAILQ_REMOVE(&client_obj->dynamic.idle_ep_tailq, ep_wrap, 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);
- HOST_EXIT_CRITICAL_SAFE();
- return yield;
- }
- // ------------------------------------------------ Library Functions --------------------------------------------------
- // ----------------------- Public --------------------------
- esp_err_t usb_host_install(const usb_host_config_t *config)
- {
- HOST_CHECK(config != NULL, ESP_ERR_INVALID_ARG);
- HOST_ENTER_CRITICAL();
- HOST_CHECK_FROM_CRIT(p_host_lib_obj == NULL, ESP_ERR_INVALID_STATE);
- HOST_EXIT_CRITICAL();
- esp_err_t ret;
- host_lib_t *host_lib_obj = heap_caps_calloc(1, sizeof(host_lib_t), MALLOC_CAP_DEFAULT);
- SemaphoreHandle_t event_sem = xSemaphoreCreateBinary();
- SemaphoreHandle_t mux_lock = xSemaphoreCreateMutex();
- if (host_lib_obj == NULL || event_sem == NULL || mux_lock == NULL) {
- ret = ESP_ERR_NO_MEM;
- goto alloc_err;
- }
- // Initialize host library object
- TAILQ_INIT(&host_lib_obj->mux_protected.client_tailq);
- host_lib_obj->constant.event_sem = event_sem;
- host_lib_obj->constant.mux_lock = mux_lock;
- /*
- 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) {
- // Host Library defaults to internal PHY
- usb_phy_config_t phy_config = {
- .controller = USB_PHY_CTRL_OTG,
- .target = USB_PHY_TARGET_INT,
- .otg_mode = USB_OTG_MODE_HOST,
- .otg_speed = USB_PHY_SPEED_UNDEFINED, // In Host mode, the speed is determined by the connected device
- .ext_io_conf = NULL,
- .otg_io_conf = NULL,
- };
- ret = usb_new_phy(&phy_config, &host_lib_obj->constant.phy_handle);
- if (ret != ESP_OK) {
- 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
- usbh_config_t usbh_config = {
- .proc_req_cb = proc_req_callback,
- .proc_req_cb_arg = NULL,
- .ctrl_xfer_cb = ctrl_xfer_callback,
- .ctrl_xfer_cb_arg = NULL,
- .event_cb = dev_event_callback,
- .event_cb_arg = NULL,
- };
- ret = usbh_install(&usbh_config);
- if (ret != ESP_OK) {
- goto usbh_err;
- }
- // Install Hub
- hub_config_t hub_config = {
- .proc_req_cb = proc_req_callback,
- .proc_req_cb_arg = NULL,
- };
- ret = hub_install(&hub_config);
- if (ret != ESP_OK) {
- goto hub_err;
- }
- // Assign host library object
- HOST_ENTER_CRITICAL();
- if (p_host_lib_obj != NULL) {
- HOST_EXIT_CRITICAL();
- ret = ESP_ERR_INVALID_STATE;
- goto assign_err;
- }
- p_host_lib_obj = host_lib_obj;
- HOST_EXIT_CRITICAL();
- // Start the root hub
- ESP_ERROR_CHECK(hub_root_start());
- ret = ESP_OK;
- return ret;
- assign_err:
- ESP_ERROR_CHECK(hub_uninstall());
- hub_err:
- ESP_ERROR_CHECK(usbh_uninstall());
- usbh_err:
- ESP_ERROR_CHECK(hcd_uninstall());
- hcd_err:
- if (host_lib_obj->constant.phy_handle) {
- ESP_ERROR_CHECK(usb_del_phy(host_lib_obj->constant.phy_handle));
- }
- phy_err:
- alloc_err:
- if (mux_lock) {
- vSemaphoreDelete(mux_lock);
- }
- if (event_sem) {
- vSemaphoreDelete(event_sem);
- }
- heap_caps_free(host_lib_obj);
- return ret;
- }
- esp_err_t usb_host_uninstall(void)
- {
- // All devices must have been freed at this point
- HOST_ENTER_CRITICAL();
- HOST_CHECK_FROM_CRIT(p_host_lib_obj != NULL, ESP_ERR_INVALID_STATE);
- HOST_CHECK_FROM_CRIT(p_host_lib_obj->dynamic.process_pending_flags == 0 &&
- p_host_lib_obj->dynamic.lib_event_flags == 0 &&
- p_host_lib_obj->dynamic.flags.val == 0,
- ESP_ERR_INVALID_STATE);
- HOST_EXIT_CRITICAL();
- // Stop the root hub
- ESP_ERROR_CHECK(hub_root_stop());
- // Unassign the host library object
- HOST_ENTER_CRITICAL();
- host_lib_t *host_lib_obj = p_host_lib_obj;
- p_host_lib_obj = NULL;
- 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 (host_lib_obj->constant.phy_handle) {
- ESP_ERROR_CHECK(usb_del_phy(host_lib_obj->constant.phy_handle));
- }
- // Free memory objects
- vSemaphoreDelete(host_lib_obj->constant.mux_lock);
- vSemaphoreDelete(host_lib_obj->constant.event_sem);
- heap_caps_free(host_lib_obj);
- return ESP_OK;
- }
- esp_err_t usb_host_lib_handle_events(TickType_t timeout_ticks, uint32_t *event_flags_ret)
- {
- esp_err_t ret;
- uint32_t event_flags = 0;
- HOST_ENTER_CRITICAL();
- if (!p_host_lib_obj->dynamic.flags.process_pending) {
- // There is currently processing that needs to be done. Wait for some processing
- HOST_EXIT_CRITICAL();
- BaseType_t sem_ret = xSemaphoreTake(p_host_lib_obj->constant.event_sem, timeout_ticks);
- if (sem_ret == pdFALSE) {
- ret = ESP_ERR_TIMEOUT;
- goto exit;
- }
- HOST_ENTER_CRITICAL();
- }
- // Read and clear process pending flags
- uint32_t process_pending_flags = p_host_lib_obj->dynamic.process_pending_flags;
- p_host_lib_obj->dynamic.process_pending_flags = 0;
- p_host_lib_obj->dynamic.flags.handling_events = 1;
- while (process_pending_flags) {
- HOST_EXIT_CRITICAL();
- if (process_pending_flags & PROCESS_REQUEST_PENDING_FLAG_USBH) {
- ESP_ERROR_CHECK(usbh_process());
- }
- if (process_pending_flags & PROCESS_REQUEST_PENDING_FLAG_HUB) {
- ESP_ERROR_CHECK(hub_process());
- }
- HOST_ENTER_CRITICAL();
- // Read and clear process pending flags again, and loop back if there is more to process
- process_pending_flags = p_host_lib_obj->dynamic.process_pending_flags;
- p_host_lib_obj->dynamic.process_pending_flags = 0;
- }
- p_host_lib_obj->dynamic.flags.process_pending = 0;
- p_host_lib_obj->dynamic.flags.handling_events = 0;
- event_flags = p_host_lib_obj->dynamic.lib_event_flags;
- p_host_lib_obj->dynamic.lib_event_flags = 0;
- HOST_EXIT_CRITICAL();
- ret = ESP_OK;
- exit:
- if (event_flags_ret != NULL) {
- *event_flags_ret = event_flags;
- }
- return ret;
- }
- esp_err_t usb_host_lib_unblock(void)
- {
- // All devices must have been freed at this point
- HOST_ENTER_CRITICAL();
- HOST_CHECK_FROM_CRIT(p_host_lib_obj != NULL, ESP_ERR_INVALID_STATE);
- _unblock_lib(false);
- HOST_EXIT_CRITICAL();
- return ESP_OK;
- }
- esp_err_t usb_host_lib_info(usb_host_lib_info_t *info_ret)
- {
- HOST_CHECK(info_ret != NULL, ESP_ERR_INVALID_ARG);
- int num_devs_temp;
- int num_clients_temp;
- HOST_ENTER_CRITICAL();
- HOST_CHECK_FROM_CRIT(p_host_lib_obj != NULL, ESP_ERR_INVALID_STATE);
- num_clients_temp = p_host_lib_obj->dynamic.flags.num_clients;
- HOST_EXIT_CRITICAL();
- usbh_num_devs(&num_devs_temp);
- // Write back return values
- info_ret->num_devices = num_devs_temp;
- info_ret->num_clients = num_clients_temp;
- return ESP_OK;
- }
- // ------------------------------------------------ Client Functions ---------------------------------------------------
- // ----------------------- Private -------------------------
- static void _handle_pending_ep(client_t *client_obj)
- {
- // Handle each EP on the pending list
- while (!TAILQ_EMPTY(&client_obj->dynamic.pending_ep_tailq)) {
- // Get the next pending EP.
- ep_wrapper_t *ep_wrap = TAILQ_FIRST(&client_obj->dynamic.pending_ep_tailq);
- TAILQ_REMOVE(&client_obj->dynamic.pending_ep_tailq, ep_wrap, dynamic.tailq_entry);
- TAILQ_INSERT_TAIL(&client_obj->dynamic.idle_ep_tailq, ep_wrap, dynamic.tailq_entry);
- ep_wrap->dynamic.flags.pending = 0;
- usbh_ep_event_t last_event = ep_wrap->dynamic.last_event;
- uint32_t num_urb_dequeued = 0;
- HOST_EXIT_CRITICAL();
- // Handle pipe event
- switch (last_event) {
- case USBH_EP_EVENT_ERROR_XFER:
- case USBH_EP_EVENT_ERROR_URB_NOT_AVAIL:
- case USBH_EP_EVENT_ERROR_OVERFLOW:
- case USBH_EP_EVENT_ERROR_STALL:
- // The endpoint is now stalled. Flush all pending URBs
- 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
- __attribute__((fallthrough));
- case USBH_EP_EVENT_URB_DONE: {
- // Dequeue all URBs and run their transfer callback
- urb_t *urb;
- usbh_ep_dequeue_urb(ep_wrap->constant.ep_hdl, &urb);
- while (urb != NULL) {
- // Clear the transfer's in-flight flag to indicate the transfer is no longer in-flight
- urb->usb_host_inflight = false;
- urb->transfer.callback(&urb->transfer);
- num_urb_dequeued++;
- usbh_ep_dequeue_urb(ep_wrap->constant.ep_hdl, &urb);
- }
- break;
- }
- default:
- abort(); // Should never occur
- break;
- }
- HOST_ENTER_CRITICAL();
- // Update the endpoint's number of URB's in-flight
- assert(num_urb_dequeued <= ep_wrap->dynamic.num_urb_inflight);
- ep_wrap->dynamic.num_urb_inflight -= num_urb_dequeued;
- }
- }
- // ----------------------- Public --------------------------
- esp_err_t usb_host_client_register(const usb_host_client_config_t *client_config, usb_host_client_handle_t *client_hdl_ret)
- {
- HOST_CHECK(p_host_lib_obj, ESP_ERR_INVALID_STATE);
- HOST_CHECK(client_config != NULL && client_hdl_ret != NULL, ESP_ERR_INVALID_ARG);
- HOST_CHECK(client_config->max_num_event_msg > 0, ESP_ERR_INVALID_ARG);
- if (!client_config->is_synchronous) {
- // Asynchronous clients must provide a
- HOST_CHECK(client_config->async.client_event_callback != NULL, ESP_ERR_INVALID_ARG);
- }
- esp_err_t ret;
- // Create client object
- client_t *client_obj = heap_caps_calloc(1, sizeof(client_t), MALLOC_CAP_DEFAULT);
- SemaphoreHandle_t event_sem = xSemaphoreCreateBinary();
- QueueHandle_t event_msg_queue = xQueueCreate(client_config->max_num_event_msg, sizeof(usb_host_client_event_msg_t));
- if (client_obj == NULL || event_sem == NULL || event_msg_queue == NULL) {
- ret = ESP_ERR_NO_MEM;
- goto alloc_err;
- }
- // Initialize client object
- TAILQ_INIT(&client_obj->dynamic.pending_ep_tailq);
- TAILQ_INIT(&client_obj->dynamic.idle_ep_tailq);
- TAILQ_INIT(&client_obj->mux_protected.interface_tailq);
- TAILQ_INIT(&client_obj->dynamic.done_ctrl_xfer_tailq);
- client_obj->constant.event_sem = event_sem;
- client_obj->constant.event_callback = client_config->async.client_event_callback;
- client_obj->constant.callback_arg = client_config->async.callback_arg;
- client_obj->constant.event_msg_queue = event_msg_queue;
- // Add client to the host library's list of clients
- xSemaphoreTake(p_host_lib_obj->constant.mux_lock, portMAX_DELAY);
- HOST_ENTER_CRITICAL();
- p_host_lib_obj->dynamic.flags.num_clients++;
- HOST_EXIT_CRITICAL();
- TAILQ_INSERT_TAIL(&p_host_lib_obj->mux_protected.client_tailq, client_obj, dynamic.tailq_entry);
- xSemaphoreGive(p_host_lib_obj->constant.mux_lock);
- // Write back client handle
- *client_hdl_ret = (usb_host_client_handle_t)client_obj;
- ret = ESP_OK;
- return ret;
- alloc_err:
- if (event_msg_queue) {
- vQueueDelete(event_msg_queue);
- }
- if (event_sem) {
- vSemaphoreDelete(event_sem);
- }
- heap_caps_free(client_obj);
- return ESP_OK;
- }
- esp_err_t usb_host_client_deregister(usb_host_client_handle_t client_hdl)
- {
- HOST_CHECK(client_hdl != NULL, ESP_ERR_INVALID_ARG);
- client_t *client_obj = (client_t *)client_hdl;
- esp_err_t ret;
- // We take the mux_lock because we need to access the host library's client_tailq
- xSemaphoreTake(p_host_lib_obj->constant.mux_lock, portMAX_DELAY);
- HOST_ENTER_CRITICAL();
- // Check that client can currently deregistered
- bool can_deregister;
- if (!TAILQ_EMPTY(&client_obj->dynamic.pending_ep_tailq) ||
- !TAILQ_EMPTY(&client_obj->dynamic.idle_ep_tailq) ||
- !TAILQ_EMPTY(&client_obj->dynamic.done_ctrl_xfer_tailq) ||
- client_obj->dynamic.flags.handling_events ||
- client_obj->dynamic.flags.blocked ||
- client_obj->dynamic.flags.taking_mux ||
- client_obj->dynamic.flags.num_intf_claimed != 0 ||
- client_obj->dynamic.num_done_ctrl_xfer != 0 ||
- client_obj->dynamic.opened_dev_addr_map != 0) {
- can_deregister = false;
- } else {
- can_deregister = true;
- }
- HOST_EXIT_CRITICAL();
- if (!can_deregister) {
- ret = ESP_ERR_INVALID_STATE;
- goto exit;
- }
- // Remove client object from the library's list of clients
- TAILQ_REMOVE(&p_host_lib_obj->mux_protected.client_tailq, client_obj, dynamic.tailq_entry);
- HOST_ENTER_CRITICAL();
- p_host_lib_obj->dynamic.flags.num_clients--;
- if (p_host_lib_obj->dynamic.flags.num_clients == 0) {
- // This is the last client being deregistered. Notify the lib handler
- p_host_lib_obj->dynamic.lib_event_flags |= USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS;
- _unblock_lib(false);
- }
- HOST_EXIT_CRITICAL();
- // Free client object
- vQueueDelete(client_obj->constant.event_msg_queue);
- vSemaphoreDelete(client_obj->constant.event_sem);
- heap_caps_free(client_obj);
- ret = ESP_OK;
- exit:
- xSemaphoreGive(p_host_lib_obj->constant.mux_lock);
- return ret;
- }
- esp_err_t usb_host_client_handle_events(usb_host_client_handle_t client_hdl, TickType_t timeout_ticks)
- {
- HOST_CHECK(client_hdl != NULL, ESP_ERR_INVALID_ARG);
- esp_err_t ret;
- client_t *client_obj = (client_t *)client_hdl;
- HOST_ENTER_CRITICAL();
- if (!client_obj->dynamic.flags.events_pending) {
- // There are currently no events, wait for one to occur
- client_obj->dynamic.flags.blocked = 1;
- HOST_EXIT_CRITICAL();
- BaseType_t sem_ret = xSemaphoreTake(client_obj->constant.event_sem, timeout_ticks);
- HOST_ENTER_CRITICAL();
- client_obj->dynamic.flags.blocked = 0;
- if (sem_ret == pdFALSE) {
- HOST_EXIT_CRITICAL();
- // Timed out waiting for semaphore
- ret = ESP_ERR_TIMEOUT;
- goto exit;
- }
- }
- // Mark that we're processing events
- client_obj->dynamic.flags.handling_events = 1;
- while (client_obj->dynamic.flags.handling_events) {
- // Handle pending endpoints
- if (!TAILQ_EMPTY(&client_obj->dynamic.pending_ep_tailq)) {
- _handle_pending_ep(client_obj);
- }
- // Handle any done control transfers
- while (client_obj->dynamic.num_done_ctrl_xfer > 0) {
- urb_t *urb = TAILQ_FIRST(&client_obj->dynamic.done_ctrl_xfer_tailq);
- TAILQ_REMOVE(&client_obj->dynamic.done_ctrl_xfer_tailq, urb, tailq_entry);
- client_obj->dynamic.num_done_ctrl_xfer--;
- HOST_EXIT_CRITICAL();
- // Clear the transfer's in-flight flag to indicate the transfer is no longer in-flight
- urb->usb_host_inflight = false;
- // Call the transfer's callback
- urb->transfer.callback(&urb->transfer);
- HOST_ENTER_CRITICAL();
- }
- // Handle event messages
- while (uxQueueMessagesWaiting(client_obj->constant.event_msg_queue) > 0) {
- HOST_EXIT_CRITICAL();
- // Dequeue the event message and call the client event callback
- usb_host_client_event_msg_t event_msg;
- BaseType_t queue_ret = xQueueReceive(client_obj->constant.event_msg_queue, &event_msg, 0);
- assert(queue_ret == pdTRUE);
- client_obj->constant.event_callback(&event_msg, client_obj->constant.callback_arg);
- HOST_ENTER_CRITICAL();
- }
- // Check each event again to see any new events occurred
- if (TAILQ_EMPTY(&client_obj->dynamic.pending_ep_tailq) &&
- client_obj->dynamic.num_done_ctrl_xfer == 0 &&
- uxQueueMessagesWaiting(client_obj->constant.event_msg_queue) == 0) {
- // All pending endpoints and event messages handled
- client_obj->dynamic.flags.events_pending = 0;
- client_obj->dynamic.flags.handling_events = 0;
- }
- }
- HOST_EXIT_CRITICAL();
- ret = ESP_OK;
- exit:
- return ret;
- }
- esp_err_t usb_host_client_unblock(usb_host_client_handle_t client_hdl)
- {
- HOST_CHECK(client_hdl != NULL, ESP_ERR_INVALID_ARG);
- client_t *client_obj = (client_t *)client_hdl;
- HOST_ENTER_CRITICAL();
- _unblock_client(client_obj, false);
- HOST_EXIT_CRITICAL();
- return ESP_OK;
- }
- // ------------------------------------------------- Device Handling ---------------------------------------------------
- esp_err_t usb_host_device_open(usb_host_client_handle_t client_hdl, uint8_t dev_addr, usb_device_handle_t *dev_hdl_ret)
- {
- HOST_CHECK(dev_addr > 0 && client_hdl != NULL && dev_hdl_ret != NULL, ESP_ERR_INVALID_ARG);
- client_t *client_obj = (client_t *)client_hdl;
- esp_err_t ret;
- usb_device_handle_t dev_hdl;
- ret = usbh_dev_open(dev_addr, &dev_hdl);
- if (ret != ESP_OK) {
- goto exit;
- }
- HOST_ENTER_CRITICAL();
- if (_check_client_opened_device(client_obj, dev_addr)) {
- // Client has already opened the device. Close it and return an error
- ret = ESP_ERR_INVALID_STATE;
- HOST_EXIT_CRITICAL();
- goto already_opened;
- }
- // Record in client object that we have opened the device of this address
- _record_client_opened_device(client_obj, dev_addr);
- HOST_EXIT_CRITICAL();
- *dev_hdl_ret = dev_hdl;
- ret = ESP_OK;
- return ret;
- already_opened:
- ESP_ERROR_CHECK(usbh_dev_close(dev_hdl));
- exit:
- return ret;
- }
- esp_err_t usb_host_device_close(usb_host_client_handle_t client_hdl, usb_device_handle_t dev_hdl)
- {
- HOST_CHECK(dev_hdl != NULL && client_hdl != NULL, ESP_ERR_INVALID_ARG);
- client_t *client_obj = (client_t *)client_hdl;
- // We take the lock because we need to walk the interface list
- xSemaphoreTake(p_host_lib_obj->constant.mux_lock, portMAX_DELAY);
- esp_err_t ret;
- // Check that all interfaces claimed by this client do not belong to this device
- bool all_released = true;
- interface_t *intf_obj;
- TAILQ_FOREACH(intf_obj, &client_obj->mux_protected.interface_tailq, mux_protected.tailq_entry) {
- if (intf_obj->constant.dev_hdl == dev_hdl) {
- all_released = false;
- break;
- }
- }
- if (!all_released) {
- ret = ESP_ERR_INVALID_STATE;
- goto exit;
- }
- // Check that client actually opened the device in the first place
- HOST_ENTER_CRITICAL();
- uint8_t dev_addr;
- ESP_ERROR_CHECK(usbh_dev_get_addr(dev_hdl, &dev_addr));
- HOST_CHECK_FROM_CRIT(_check_client_opened_device(client_obj, dev_addr), ESP_ERR_NOT_FOUND);
- if (!_check_client_opened_device(client_obj, dev_addr)) {
- // Client never opened this device
- ret = ESP_ERR_INVALID_STATE;
- HOST_EXIT_CRITICAL();
- goto exit;
- }
- // Proceed to clear the record of the device form the client
- _clear_client_opened_device(client_obj, dev_addr);
- HOST_EXIT_CRITICAL();
- ESP_ERROR_CHECK(usbh_dev_close(dev_hdl));
- ret = ESP_OK;
- exit:
- xSemaphoreGive(p_host_lib_obj->constant.mux_lock);
- return ret;
- }
- esp_err_t usb_host_device_free_all(void)
- {
- HOST_ENTER_CRITICAL();
- HOST_CHECK_FROM_CRIT(p_host_lib_obj->dynamic.flags.num_clients == 0, ESP_ERR_INVALID_STATE); // All clients must have been deregistered
- HOST_EXIT_CRITICAL();
- esp_err_t ret;
- ret = usbh_dev_mark_all_free();
- // If ESP_ERR_NOT_FINISHED is returned, caller must wait for USB_HOST_LIB_EVENT_FLAGS_ALL_FREE to confirm all devices are free
- return ret;
- }
- esp_err_t usb_host_device_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret)
- {
- HOST_CHECK(dev_addr_list != NULL && num_dev_ret != NULL, ESP_ERR_INVALID_ARG);
- return usbh_dev_addr_list_fill(list_len, dev_addr_list, num_dev_ret);
- }
- // ------------------------------------------------- Device Requests ---------------------------------------------------
- // ------------------- Cached Requests ---------------------
- esp_err_t usb_host_device_info(usb_device_handle_t dev_hdl, usb_device_info_t *dev_info)
- {
- HOST_CHECK(dev_hdl != NULL && dev_info != NULL, ESP_ERR_INVALID_ARG);
- return usbh_dev_get_info(dev_hdl, dev_info);
- }
- // ----------------------------------------------- Descriptor Requests -------------------------------------------------
- // ----------------- Cached Descriptors --------------------
- esp_err_t usb_host_get_device_descriptor(usb_device_handle_t dev_hdl, const usb_device_desc_t **device_desc)
- {
- HOST_CHECK(dev_hdl != NULL && device_desc != NULL, ESP_ERR_INVALID_ARG);
- return usbh_dev_get_desc(dev_hdl, device_desc);
- }
- esp_err_t usb_host_get_active_config_descriptor(usb_device_handle_t dev_hdl, const usb_config_desc_t **config_desc)
- {
- HOST_CHECK(dev_hdl != NULL && config_desc != NULL, ESP_ERR_INVALID_ARG);
- return usbh_dev_get_config_desc(dev_hdl, config_desc);
- }
- // ----------------------------------------------- Interface Functions -------------------------------------------------
- // ----------------------- Private -------------------------
- 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)
- {
- ep_wrapper_t *ep_wrap = heap_caps_calloc(1, sizeof(ep_wrapper_t), MALLOC_CAP_DEFAULT);
- if (ep_wrap == NULL) {
- return ESP_ERR_NO_MEM;
- }
- esp_err_t ret;
- usbh_ep_handle_t ep_hdl;
- usbh_ep_config_t ep_config = {
- .bInterfaceNumber = intf_obj->constant.intf_desc->bInterfaceNumber,
- .bAlternateSetting = intf_obj->constant.intf_desc->bAlternateSetting,
- .bEndpointAddress = ep_desc->bEndpointAddress,
- .ep_cb = endpoint_callback,
- .ep_cb_arg = (void *)ep_wrap,
- .context = (void *)ep_wrap,
- };
- ret = usbh_ep_alloc(dev_hdl, &ep_config, &ep_hdl);
- if (ret != ESP_OK) {
- goto alloc_err;
- }
- // Initialize endpoint wrapper item
- ep_wrap->constant.ep_hdl = ep_hdl;
- ep_wrap->constant.intf_obj = intf_obj;
- // Write back result
- *ep_wrap_ret = ep_wrap;
- ret = ESP_OK;
- return ret;
- alloc_err:
- heap_caps_free(ep_wrap);
- return ret;
- }
- static void ep_wrapper_free(usb_device_handle_t dev_hdl, ep_wrapper_t *ep_wrap)
- {
- if (ep_wrap == NULL) {
- return;
- }
- // Free the underlying endpoint
- ESP_ERROR_CHECK(usbh_ep_free(ep_wrap->constant.ep_hdl));
- // Free the endpoint wrapper item
- 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)
- {
- 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) {
- return NULL;
- }
- intf_obj->constant.intf_desc = intf_desc;
- intf_obj->constant.client_obj = client_obj;
- intf_obj->constant.dev_hdl = dev_hdl;
- return intf_obj;
- }
- static void interface_free(interface_t *intf_obj)
- {
- if (intf_obj == NULL) {
- return;
- }
- for (int i = 0; i < intf_obj->constant.intf_desc->bNumEndpoints; i++) {
- assert(intf_obj->constant.endpoints[i] == NULL);
- }
- heap_caps_free(intf_obj);
- }
- static esp_err_t interface_claim(client_t *client_obj, usb_device_handle_t dev_hdl, const usb_config_desc_t *config_desc, uint8_t bInterfaceNumber, uint8_t bAlternateSetting, interface_t **intf_obj_ret)
- {
- esp_err_t ret;
- // We need to walk to configuration descriptor to find the correct interface descriptor, and each of its constituent endpoint descriptors
- // Find the interface descriptor and allocate the interface object
- int offset_intf;
- const usb_intf_desc_t *intf_desc = usb_parse_interface_descriptor(config_desc, bInterfaceNumber, bAlternateSetting, &offset_intf);
- if (intf_desc == NULL) {
- ret = ESP_ERR_NOT_FOUND;
- goto exit;
- }
- // Allocate interface object
- interface_t *intf_obj = interface_alloc(client_obj, dev_hdl, intf_desc);
- if (intf_obj == NULL) {
- ret = ESP_ERR_NO_MEM;
- goto exit;
- }
- // Find each endpoint descriptor in the interface by index, and allocate those endpoints
- for (int i = 0; i < intf_desc->bNumEndpoints; i++) {
- int offset_ep = offset_intf;
- const usb_ep_desc_t *ep_desc = usb_parse_endpoint_descriptor_by_index(intf_desc, i, config_desc->wTotalLength, &offset_ep);
- if (ep_desc == NULL) {
- ret = ESP_ERR_NOT_FOUND;
- goto ep_alloc_err;
- }
- // Allocate the endpoint wrapper item
- ep_wrapper_t *ep_wrap;
- ret = ep_wrapper_alloc(dev_hdl, ep_desc, intf_obj, &ep_wrap);
- if (ret != ESP_OK) {
- goto ep_alloc_err;
- }
- // Fill the interface object with the allocated endpoints
- intf_obj->constant.endpoints[i] = ep_wrap;
- }
- // 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);
- // Add each endpoint wrapper item to the client's endpoint list
- HOST_ENTER_CRITICAL();
- 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);
- }
- HOST_EXIT_CRITICAL();
- // Write back result
- *intf_obj_ret = intf_obj;
- ret = ESP_OK;
- return ret;
- ep_alloc_err:
- for (int i = 0; i < intf_desc->bNumEndpoints; i++) {
- ep_wrapper_free(dev_hdl, intf_obj->constant.endpoints[i]);
- intf_obj->constant.endpoints[i] = NULL;
- }
- interface_free(intf_obj);
- exit:
- return ret;
- }
- static esp_err_t interface_release(client_t *client_obj, usb_device_handle_t dev_hdl, uint8_t bInterfaceNumber)
- {
- esp_err_t ret;
- // Find the interface object
- interface_t *intf_obj_iter;
- interface_t *intf_obj = NULL;
- TAILQ_FOREACH(intf_obj_iter, &client_obj->mux_protected.interface_tailq, mux_protected.tailq_entry) {
- if (intf_obj_iter->constant.dev_hdl == dev_hdl && intf_obj_iter->constant.intf_desc->bInterfaceNumber == bInterfaceNumber) {
- intf_obj = intf_obj_iter;
- break;
- }
- }
- if (intf_obj == NULL) {
- ret = ESP_ERR_NOT_FOUND;
- goto exit;
- }
- // 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();
- bool can_free = true;
- for (int i = 0; i < intf_obj->constant.intf_desc->bNumEndpoints; i++) {
- ep_wrapper_t *ep_wrap = intf_obj->constant.endpoints[i];
- // Endpoint must not be on the pending list and must not have in-flight URBs
- if (ep_wrap->dynamic.num_urb_inflight != 0 || ep_wrap->dynamic.flags.pending) {
- can_free = false;
- break;
- }
- }
- if (!can_free) {
- HOST_EXIT_CRITICAL();
- ret = ESP_ERR_INVALID_STATE;
- goto exit;
- }
- // Proceed to remove all endpoint wrapper items from the list
- 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);
- }
- HOST_EXIT_CRITICAL();
- // Remove the interface object from the list (safe because we have already taken the mutex)
- TAILQ_REMOVE(&client_obj->mux_protected.interface_tailq, intf_obj, mux_protected.tailq_entry);
- // Free each endpoint in the interface
- for (int i = 0; i < intf_obj->constant.intf_desc->bNumEndpoints; i++) {
- ep_wrapper_free(dev_hdl, intf_obj->constant.endpoints[i]);
- intf_obj->constant.endpoints[i] = NULL;
- }
- // Free the interface object itself
- interface_free(intf_obj);
- ret = ESP_OK;
- exit:
- return ret;
- }
- // ----------------------- Public --------------------------
- esp_err_t usb_host_interface_claim(usb_host_client_handle_t client_hdl, usb_device_handle_t dev_hdl, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
- {
- HOST_CHECK(client_hdl != NULL && dev_hdl != NULL, ESP_ERR_INVALID_ARG);
- client_t *client_obj = (client_t *)client_hdl;
- HOST_ENTER_CRITICAL();
- uint8_t dev_addr;
- ESP_ERROR_CHECK(usbh_dev_get_addr(dev_hdl, &dev_addr));
- // Check if client actually opened device
- HOST_CHECK_FROM_CRIT(_check_client_opened_device(client_obj, dev_addr), ESP_ERR_INVALID_STATE);
- client_obj->dynamic.flags.taking_mux = 1;
- HOST_EXIT_CRITICAL();
- // Take mux lock. This protects the client being released or other clients from claiming interfaces
- xSemaphoreTake(p_host_lib_obj->constant.mux_lock, portMAX_DELAY);
- esp_err_t ret;
- const usb_config_desc_t *config_desc;
- ESP_ERROR_CHECK(usbh_dev_get_config_desc(dev_hdl, &config_desc));
- interface_t *intf_obj;
- // Claim interface
- ret = interface_claim(client_obj, dev_hdl, config_desc, bInterfaceNumber, bAlternateSetting, &intf_obj);
- if (ret != ESP_OK) {
- goto exit;
- }
- ret = ESP_OK;
- exit:
- xSemaphoreGive(p_host_lib_obj->constant.mux_lock);
- HOST_ENTER_CRITICAL();
- if (ret == ESP_OK) {
- client_obj->dynamic.flags.num_intf_claimed++;
- }
- client_obj->dynamic.flags.taking_mux = 0;
- HOST_EXIT_CRITICAL();
- return ret;
- }
- esp_err_t usb_host_interface_release(usb_host_client_handle_t client_hdl, usb_device_handle_t dev_hdl, uint8_t bInterfaceNumber)
- {
- HOST_CHECK(client_hdl != NULL && dev_hdl != NULL, ESP_ERR_INVALID_ARG);
- client_t *client_obj = (client_t *)client_hdl;
- HOST_ENTER_CRITICAL();
- uint8_t dev_addr;
- ESP_ERROR_CHECK(usbh_dev_get_addr(dev_hdl, &dev_addr));
- // Check if client actually opened device
- HOST_CHECK_FROM_CRIT(_check_client_opened_device(client_obj, dev_addr), ESP_ERR_INVALID_STATE);
- client_obj->dynamic.flags.taking_mux = 1;
- HOST_EXIT_CRITICAL();
- // Take mux lock. This protects the client being released or other clients from claiming interfaces
- xSemaphoreTake(p_host_lib_obj->constant.mux_lock, portMAX_DELAY);
- esp_err_t ret = interface_release(client_obj, dev_hdl, bInterfaceNumber);
- xSemaphoreGive(p_host_lib_obj->constant.mux_lock);
- HOST_ENTER_CRITICAL();
- if (ret == ESP_OK) {
- client_obj->dynamic.flags.num_intf_claimed--;
- }
- client_obj->dynamic.flags.taking_mux = 0;
- HOST_EXIT_CRITICAL();
- return ret;
- }
- esp_err_t usb_host_endpoint_halt(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress)
- {
- esp_err_t ret;
- usbh_ep_handle_t ep_hdl;
- ret = usbh_ep_get_handle(dev_hdl, bEndpointAddress, &ep_hdl);
- if (ret != ESP_OK) {
- goto exit;
- }
- ret = usbh_ep_command(ep_hdl, USBH_EP_CMD_HALT);
- exit:
- return ret;
- }
- esp_err_t usb_host_endpoint_flush(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress)
- {
- esp_err_t ret;
- usbh_ep_handle_t ep_hdl;
- ret = usbh_ep_get_handle(dev_hdl, bEndpointAddress, &ep_hdl);
- if (ret != ESP_OK) {
- goto exit;
- }
- ret = usbh_ep_command(ep_hdl, USBH_EP_CMD_FLUSH);
- exit:
- return ret;
- }
- esp_err_t usb_host_endpoint_clear(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress)
- {
- esp_err_t ret;
- usbh_ep_handle_t ep_hdl;
- ret = usbh_ep_get_handle(dev_hdl, bEndpointAddress, &ep_hdl);
- if (ret != ESP_OK) {
- goto exit;
- }
- ret = usbh_ep_command(ep_hdl, USBH_EP_CMD_CLEAR);
- exit:
- return ret;
- }
- // ------------------------------------------------ Asynchronous I/O ---------------------------------------------------
- // ----------------------- Public --------------------------
- esp_err_t usb_host_transfer_alloc(size_t data_buffer_size, int num_isoc_packets, usb_transfer_t **transfer)
- {
- urb_t *urb = urb_alloc(data_buffer_size, 0, num_isoc_packets);
- if (urb == NULL) {
- return ESP_ERR_NO_MEM;
- }
- *transfer = &urb->transfer;
- return ESP_OK;
- }
- esp_err_t usb_host_transfer_free(usb_transfer_t *transfer)
- {
- if (transfer == NULL) {
- return ESP_OK;
- }
- urb_t *urb = __containerof(transfer, urb_t, transfer);
- urb_free(urb);
- return ESP_OK;
- }
- esp_err_t usb_host_transfer_submit(usb_transfer_t *transfer)
- {
- HOST_CHECK(transfer != NULL, ESP_ERR_INVALID_ARG);
- // 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->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK) != 0, ESP_ERR_INVALID_ARG);
- usbh_ep_handle_t ep_hdl;
- ep_wrapper_t *ep_wrap = NULL;
- urb_t *urb_obj = __containerof(transfer, urb_t, transfer);
- esp_err_t ret;
- ret = usbh_ep_get_handle(transfer->device_handle, transfer->bEndpointAddress, &ep_hdl);
- if (ret != ESP_OK) {
- goto err;
- }
- ep_wrap = usbh_ep_get_context(ep_hdl);
- assert(ep_wrap != NULL);
- // Check that we are not submitting a transfer already in-flight
- HOST_CHECK(!urb_obj->usb_host_inflight, ESP_ERR_NOT_FINISHED);
- urb_obj->usb_host_inflight = true;
- HOST_ENTER_CRITICAL();
- ep_wrap->dynamic.num_urb_inflight++;
- HOST_EXIT_CRITICAL();
- ret = usbh_ep_enqueue_urb(ep_hdl, urb_obj);
- if (ret != ESP_OK) {
- goto submit_err;
- }
- return ret;
- submit_err:
- HOST_ENTER_CRITICAL();
- ep_wrap->dynamic.num_urb_inflight--;
- HOST_EXIT_CRITICAL();
- urb_obj->usb_host_inflight = false;
- err:
- return ret;
- }
- 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);
- // Check that control transfer is valid
- HOST_CHECK(transfer->device_handle != NULL, ESP_ERR_INVALID_ARG); // Target device must be set
- // Control transfers must be targeted at EP 0
- 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);
- // Check that we are not submitting a transfer already in-flight
- HOST_CHECK(!urb_obj->usb_host_inflight, ESP_ERR_NOT_FINISHED);
- urb_obj->usb_host_inflight = true;
- // Save client handle into URB
- urb_obj->usb_host_client = (void *)client_hdl;
- esp_err_t ret;
- ret = usbh_dev_submit_ctrl_urb(dev_hdl, urb_obj);
- if (ret != ESP_OK) {
- urb_obj->usb_host_inflight = false;
- }
- return ret;
- }
|