mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
refactor(usb/usbh): Update USBH device creation and enumeration handling
This commit updates how the USBH handles device creation and enumeration so that upper layers (such as the Hub driver) can use the USBH API for enumeration instead of calling the HCD. USBH Updates: USBH now creates unenumerated devices set to address 0 with no device/config descriptor. A newly created device can be opened and communicated with immediately (using control transfers). This allows the Hub driver to call the USBH instead of the HCD. Summary of USBH changes: - Added new APIs to add/remove a device. Devices are now created as unenumerated and can be immediately opened and communicated with. - Added new APIs to enumerate a device (see 'usbh_dev_set_...()' functions). Device must be locked (see 'usbh_dev_enum_lock()') before enumeration functions can be called. - Added UID for each device. This allows the particular USBH without needing to use the device's handle (which implies opening the device). Hub Driver Updates: Hub driver now calls the USBH for enumeration. Summary of USBH changes: - Replace all 'hcd_pipe_...()' calls with 'usbh_dev_...()' calls - Refactored port event handling to fit with new USBH API - Updated to use UID to uniquely identify devices without opening them USB Host Updates: - Reroute USBH control transfers to clients and hub driver
This commit is contained in:
+494
-243
@@ -57,7 +57,8 @@ struct device_s {
|
||||
uint32_t in_pending_list: 1;
|
||||
uint32_t is_gone: 1; // Device is gone (disconnected or port error)
|
||||
uint32_t waiting_free: 1; // Device object is awaiting to be freed
|
||||
uint32_t reserved29: 29;
|
||||
uint32_t enum_lock: 1; // Device is locked for enumeration. Enum information (e.g., address, device/config desc etc) may change
|
||||
uint32_t reserved28: 28;
|
||||
};
|
||||
uint32_t val;
|
||||
} flags;
|
||||
@@ -74,17 +75,22 @@ struct device_s {
|
||||
*/
|
||||
endpoint_t *endpoints[NUM_NON_DEFAULT_EP];
|
||||
} mux_protected;
|
||||
// Constant members do not change after device allocation and enumeration thus do not require a critical section
|
||||
// Constant members do not require a critical section
|
||||
struct {
|
||||
// Assigned on device allocation and remain constant for the device's lifetime
|
||||
hcd_pipe_handle_t default_pipe;
|
||||
hcd_port_handle_t port_hdl;
|
||||
uint8_t address;
|
||||
usb_speed_t speed;
|
||||
const usb_device_desc_t *desc;
|
||||
const usb_config_desc_t *config_desc;
|
||||
const usb_str_desc_t *str_desc_manu;
|
||||
const usb_str_desc_t *str_desc_product;
|
||||
const usb_str_desc_t *str_desc_ser_num;
|
||||
unsigned int uid;
|
||||
/*
|
||||
These fields are can only be changed when enum_lock is set, thus can be treated as constant
|
||||
*/
|
||||
uint8_t address;
|
||||
usb_device_desc_t *desc;
|
||||
usb_config_desc_t *config_desc;
|
||||
usb_str_desc_t *str_desc_manu;
|
||||
usb_str_desc_t *str_desc_product;
|
||||
usb_str_desc_t *str_desc_ser_num;
|
||||
} constant;
|
||||
};
|
||||
|
||||
@@ -143,6 +149,50 @@ static bool _dev_set_actions(device_t *dev_obj, uint32_t action_flags);
|
||||
|
||||
// ----------------------------------------------------- Helpers -------------------------------------------------------
|
||||
|
||||
static device_t *_find_dev_from_uid(unsigned int uid)
|
||||
{
|
||||
/*
|
||||
THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION
|
||||
*/
|
||||
device_t *dev_iter;
|
||||
|
||||
// Search the device lists for a device with the specified address
|
||||
TAILQ_FOREACH(dev_iter, &p_usbh_obj->dynamic.devs_idle_tailq, dynamic.tailq_entry) {
|
||||
if (dev_iter->constant.uid == uid) {
|
||||
return dev_iter;
|
||||
}
|
||||
}
|
||||
TAILQ_FOREACH(dev_iter, &p_usbh_obj->dynamic.devs_pending_tailq, dynamic.tailq_entry) {
|
||||
if (dev_iter->constant.uid == uid) {
|
||||
return dev_iter;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static device_t *_find_dev_from_addr(uint8_t dev_addr)
|
||||
{
|
||||
/*
|
||||
THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION
|
||||
*/
|
||||
device_t *dev_iter;
|
||||
|
||||
// Search the device lists for a device with the specified address
|
||||
TAILQ_FOREACH(dev_iter, &p_usbh_obj->dynamic.devs_idle_tailq, dynamic.tailq_entry) {
|
||||
if (dev_iter->constant.address == dev_addr) {
|
||||
return dev_iter;
|
||||
}
|
||||
}
|
||||
TAILQ_FOREACH(dev_iter, &p_usbh_obj->dynamic.devs_pending_tailq, dynamic.tailq_entry) {
|
||||
if (dev_iter->constant.address == dev_addr) {
|
||||
return dev_iter;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool check_ep_addr(uint8_t bEndpointAddress)
|
||||
{
|
||||
/*
|
||||
@@ -204,13 +254,21 @@ static bool urb_check_args(urb_t *urb)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool transfer_check_usb_compliance(usb_transfer_t *transfer, usb_transfer_type_t type, int mps, bool is_in)
|
||||
static bool transfer_check_usb_compliance(usb_transfer_t *transfer, usb_transfer_type_t type, unsigned int mps, bool is_in)
|
||||
{
|
||||
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(USBH_TAG, "usb_transfer_t num_bytes and usb_setup_packet_t wLength mismatch");
|
||||
bool mismatch = false;
|
||||
if (is_in) {
|
||||
// For IN transfers, 'num_bytes >= sizeof(usb_setup_packet_t) + setup_pkt->wLength' due to MPS rounding
|
||||
mismatch = (transfer->num_bytes < sizeof(usb_setup_packet_t) + setup_pkt->wLength);
|
||||
} else {
|
||||
// For OUT transfers, num_bytes must match 'sizeof(usb_setup_packet_t) + setup_pkt->wLength'
|
||||
mismatch = (transfer->num_bytes != sizeof(usb_setup_packet_t) + setup_pkt->wLength);
|
||||
}
|
||||
if (mismatch) {
|
||||
ESP_LOGE(USBH_TAG, "usb_transfer_t num_bytes %d and usb_setup_packet_t wLength %d mismatch", transfer->num_bytes, setup_pkt->wLength);
|
||||
return false;
|
||||
}
|
||||
} else if (type == USB_TRANSFER_TYPE_ISOCHRONOUS) {
|
||||
@@ -300,19 +358,21 @@ static void endpoint_free(endpoint_t *ep_obj)
|
||||
heap_caps_free(ep_obj);
|
||||
}
|
||||
|
||||
static esp_err_t device_alloc(hcd_port_handle_t port_hdl, usb_speed_t speed, device_t **dev_obj_ret)
|
||||
static esp_err_t device_alloc(unsigned int uid,
|
||||
usb_speed_t speed,
|
||||
hcd_port_handle_t port_hdl,
|
||||
device_t **dev_obj_ret)
|
||||
{
|
||||
esp_err_t ret;
|
||||
device_t *dev_obj = heap_caps_calloc(1, sizeof(device_t), MALLOC_CAP_DEFAULT);
|
||||
usb_device_desc_t *dev_desc = heap_caps_calloc(1, sizeof(usb_device_desc_t), MALLOC_CAP_DEFAULT);
|
||||
if (dev_obj == NULL || dev_desc == NULL) {
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto err;
|
||||
if (dev_obj == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
// Allocate a pipe for EP0. We set the pipe callback to NULL for now
|
||||
|
||||
esp_err_t ret;
|
||||
// Allocate a pipe for EP0
|
||||
hcd_pipe_config_t pipe_config = {
|
||||
.callback = NULL,
|
||||
.callback_arg = NULL,
|
||||
.callback = ep0_pipe_callback,
|
||||
.callback_arg = (void *)dev_obj,
|
||||
.context = (void *)dev_obj,
|
||||
.ep_desc = NULL, // No endpoint descriptor means we're allocating a pipe for EP0
|
||||
.dev_speed = speed,
|
||||
@@ -327,15 +387,17 @@ static esp_err_t device_alloc(hcd_port_handle_t port_hdl, usb_speed_t speed, dev
|
||||
dev_obj->dynamic.state = USB_DEVICE_STATE_DEFAULT;
|
||||
dev_obj->constant.default_pipe = default_pipe_hdl;
|
||||
dev_obj->constant.port_hdl = port_hdl;
|
||||
// Note: dev_obj->constant.address is assigned later during enumeration
|
||||
dev_obj->constant.speed = speed;
|
||||
dev_obj->constant.desc = dev_desc;
|
||||
dev_obj->constant.uid = uid;
|
||||
// Note: Enumeration related dev_obj->constant fields are initialized later using usbh_dev_set_...() functions
|
||||
|
||||
// Write-back device object
|
||||
*dev_obj_ret = dev_obj;
|
||||
ret = ESP_OK;
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
heap_caps_free(dev_desc);
|
||||
heap_caps_free(dev_obj);
|
||||
return ret;
|
||||
}
|
||||
@@ -345,21 +407,24 @@ static void device_free(device_t *dev_obj)
|
||||
if (dev_obj == NULL) {
|
||||
return;
|
||||
}
|
||||
// Configuration might not have been allocated (in case of early enumeration failure)
|
||||
if (dev_obj->constant.config_desc) {
|
||||
heap_caps_free((usb_config_desc_t *)dev_obj->constant.config_desc);
|
||||
// Device descriptor might not have been set yet
|
||||
if (dev_obj->constant.desc) {
|
||||
heap_caps_free(dev_obj->constant.desc);
|
||||
}
|
||||
// String descriptors might not have been allocated (in case of early enumeration failure)
|
||||
// Configuration descriptor might not have been set yet
|
||||
if (dev_obj->constant.config_desc) {
|
||||
heap_caps_free(dev_obj->constant.config_desc);
|
||||
}
|
||||
// String descriptors might not have been set yet
|
||||
if (dev_obj->constant.str_desc_manu) {
|
||||
heap_caps_free((usb_str_desc_t *)dev_obj->constant.str_desc_manu);
|
||||
heap_caps_free(dev_obj->constant.str_desc_manu);
|
||||
}
|
||||
if (dev_obj->constant.str_desc_product) {
|
||||
heap_caps_free((usb_str_desc_t *)dev_obj->constant.str_desc_product);
|
||||
heap_caps_free(dev_obj->constant.str_desc_product);
|
||||
}
|
||||
if (dev_obj->constant.str_desc_ser_num) {
|
||||
heap_caps_free((usb_str_desc_t *)dev_obj->constant.str_desc_ser_num);
|
||||
heap_caps_free(dev_obj->constant.str_desc_ser_num);
|
||||
}
|
||||
heap_caps_free((usb_device_desc_t *)dev_obj->constant.desc);
|
||||
ESP_ERROR_CHECK(hcd_pipe_free(dev_obj->constant.default_pipe));
|
||||
heap_caps_free(dev_obj);
|
||||
}
|
||||
@@ -426,6 +491,9 @@ static bool epN_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_
|
||||
|
||||
static bool _dev_set_actions(device_t *dev_obj, uint32_t action_flags)
|
||||
{
|
||||
/*
|
||||
THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION
|
||||
*/
|
||||
if (action_flags == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -512,7 +580,7 @@ static inline void handle_prop_gone_evt(device_t *dev_obj)
|
||||
static inline void handle_free(device_t *dev_obj)
|
||||
{
|
||||
// Cache a copy of the device's address as we are about to free the device object
|
||||
const uint8_t dev_addr = dev_obj->constant.address;
|
||||
const unsigned int dev_uid = dev_obj->constant.uid;
|
||||
bool all_free;
|
||||
ESP_LOGD(USBH_TAG, "Freeing device %d", dev_obj->constant.address);
|
||||
|
||||
@@ -536,7 +604,7 @@ static inline void handle_free(device_t *dev_obj)
|
||||
usbh_event_data_t event_data = {
|
||||
.event = USBH_EVENT_DEV_FREE,
|
||||
.dev_free_data = {
|
||||
.dev_addr = dev_addr,
|
||||
.dev_uid = dev_uid,
|
||||
}
|
||||
};
|
||||
p_usbh_obj->constant.event_cb(&event_data, p_usbh_obj->constant.event_cb_arg);
|
||||
@@ -661,8 +729,6 @@ esp_err_t usbh_process(void)
|
||||
--------------------------------------------------------------------- */
|
||||
USBH_EXIT_CRITICAL();
|
||||
ESP_LOGD(USBH_TAG, "Processing actions 0x%"PRIx32"", action_flags);
|
||||
// Sanity check. If the device is being freed, there must not be any other action flags set
|
||||
assert(!(action_flags & DEV_ACTION_FREE) || action_flags == DEV_ACTION_FREE);
|
||||
|
||||
if (action_flags & DEV_ACTION_EPn_HALT_FLUSH) {
|
||||
handle_epn_halt_flush(dev_obj);
|
||||
@@ -714,24 +780,42 @@ esp_err_t usbh_devs_num(int *num_devs_ret)
|
||||
esp_err_t usbh_devs_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret)
|
||||
{
|
||||
USBH_CHECK(dev_addr_list != NULL && num_dev_ret != NULL, ESP_ERR_INVALID_ARG);
|
||||
USBH_ENTER_CRITICAL();
|
||||
int num_filled = 0;
|
||||
device_t *dev_obj;
|
||||
// Fill list with devices from idle tailq
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
/*
|
||||
Fill list with devices from idle tailq and pending tailq. Only devices that
|
||||
are fully enumerated are added to the list. Thus, the following devices are
|
||||
not excluded:
|
||||
- Devices with their enum_lock set
|
||||
- Devices not in the configured state
|
||||
- Devices with address 0
|
||||
*/
|
||||
TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_idle_tailq, dynamic.tailq_entry) {
|
||||
if (num_filled < list_len) {
|
||||
dev_addr_list[num_filled] = dev_obj->constant.address;
|
||||
num_filled++;
|
||||
if (!dev_obj->dynamic.flags.enum_lock &&
|
||||
dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED &&
|
||||
dev_obj->constant.address != 0) {
|
||||
dev_addr_list[num_filled] = dev_obj->constant.address;
|
||||
num_filled++;
|
||||
}
|
||||
} else {
|
||||
// Address list is already full
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Fill list with devices from pending tailq
|
||||
TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_pending_tailq, dynamic.tailq_entry) {
|
||||
if (num_filled < list_len) {
|
||||
dev_addr_list[num_filled] = dev_obj->constant.address;
|
||||
num_filled++;
|
||||
if (!dev_obj->dynamic.flags.enum_lock &&
|
||||
dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED &&
|
||||
dev_obj->constant.address != 0) {
|
||||
dev_addr_list[num_filled] = dev_obj->constant.address;
|
||||
num_filled++;
|
||||
}
|
||||
} else {
|
||||
// Address list is already full
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -741,6 +825,82 @@ esp_err_t usbh_devs_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *nu
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t usbh_devs_add(unsigned int uid, usb_speed_t dev_speed, hcd_port_handle_t port_hdl)
|
||||
{
|
||||
USBH_CHECK(port_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
device_t *dev_obj;
|
||||
|
||||
// Allocate a device object (initialized to address 0)
|
||||
ret = device_alloc(uid, dev_speed, port_hdl, &dev_obj);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// We need to take the mux_lock to access mux_protected members
|
||||
xSemaphoreTake(p_usbh_obj->constant.mux_lock, portMAX_DELAY);
|
||||
USBH_ENTER_CRITICAL();
|
||||
|
||||
// Check that there is not already a device with the same uid
|
||||
if (_find_dev_from_uid(uid) != NULL) {
|
||||
ret = ESP_ERR_INVALID_ARG;
|
||||
goto exit;
|
||||
}
|
||||
// Check that there is not already a device currently with address 0
|
||||
if (_find_dev_from_addr(0) != NULL) {
|
||||
ret = ESP_ERR_NOT_FINISHED;
|
||||
goto exit;
|
||||
}
|
||||
// Add the device to the idle device list
|
||||
TAILQ_INSERT_TAIL(&p_usbh_obj->dynamic.devs_idle_tailq, dev_obj, dynamic.tailq_entry);
|
||||
p_usbh_obj->mux_protected.num_device++;
|
||||
ret = ESP_OK;
|
||||
|
||||
exit:
|
||||
USBH_EXIT_CRITICAL();
|
||||
xSemaphoreGive(p_usbh_obj->constant.mux_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t usbh_devs_remove(unsigned int uid)
|
||||
{
|
||||
esp_err_t ret;
|
||||
device_t *dev_obj;
|
||||
bool call_proc_req_cb = false;
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
dev_obj = _find_dev_from_uid(uid);
|
||||
if (dev_obj == NULL) {
|
||||
ret = ESP_ERR_NOT_FOUND;
|
||||
goto exit;
|
||||
}
|
||||
// Mark the device as gone
|
||||
dev_obj->dynamic.flags.is_gone = 1;
|
||||
// Check if the device can be freed immediately
|
||||
if (dev_obj->dynamic.open_count == 0) {
|
||||
// Device is not currently opened at all. Can free immediately.
|
||||
call_proc_req_cb = _dev_set_actions(dev_obj, DEV_ACTION_FREE);
|
||||
} else {
|
||||
// Device is still opened. Flush endpoints and propagate device gone event
|
||||
call_proc_req_cb = _dev_set_actions(dev_obj,
|
||||
DEV_ACTION_EPn_HALT_FLUSH |
|
||||
DEV_ACTION_EP0_FLUSH |
|
||||
DEV_ACTION_EP0_DEQUEUE |
|
||||
DEV_ACTION_PROP_GONE_EVT);
|
||||
}
|
||||
ret = ESP_OK;
|
||||
exit:
|
||||
USBH_EXIT_CRITICAL();
|
||||
|
||||
// Call the processing request callback
|
||||
if (call_proc_req_cb) {
|
||||
p_usbh_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_USBH, false, p_usbh_obj->constant.proc_req_cb_arg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t usbh_devs_mark_all_free(void)
|
||||
{
|
||||
USBH_ENTER_CRITICAL();
|
||||
@@ -790,28 +950,17 @@ esp_err_t usbh_devs_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl)
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
// Go through the device lists to find the device with the specified address
|
||||
device_t *found_dev_obj = NULL;
|
||||
device_t *dev_obj;
|
||||
TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_idle_tailq, dynamic.tailq_entry) {
|
||||
if (dev_obj->constant.address == dev_addr) {
|
||||
found_dev_obj = dev_obj;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_pending_tailq, dynamic.tailq_entry) {
|
||||
if (dev_obj->constant.address == dev_addr) {
|
||||
found_dev_obj = dev_obj;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
if (found_dev_obj != NULL) {
|
||||
// The device is not in a state to be opened
|
||||
if (dev_obj->dynamic.flags.is_gone || dev_obj->dynamic.flags.waiting_free) {
|
||||
device_t *dev_obj = _find_dev_from_addr(dev_addr);
|
||||
if (dev_obj != NULL) {
|
||||
// Check if the device is in a state to be opened
|
||||
if (dev_obj->dynamic.flags.is_gone || // Device is already gone (disconnected)
|
||||
dev_obj->dynamic.flags.waiting_free) { // Device is waiting to be freed
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
} else if (dev_obj->dynamic.flags.enum_lock) { // Device's enum_lock is set
|
||||
ret = ESP_ERR_NOT_ALLOWED;
|
||||
} else {
|
||||
dev_obj->dynamic.open_count++;
|
||||
*dev_hdl = (usb_device_handle_t)found_dev_obj;
|
||||
*dev_hdl = (usb_device_handle_t)dev_obj;
|
||||
ret = ESP_OK;
|
||||
}
|
||||
} else {
|
||||
@@ -828,6 +977,8 @@ esp_err_t usbh_devs_close(usb_device_handle_t dev_hdl)
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
// Device should never be closed while its enum_lock is
|
||||
USBH_CHECK_FROM_CRIT(!dev_obj->dynamic.flags.enum_lock, ESP_ERR_NOT_ALLOWED);
|
||||
dev_obj->dynamic.open_count--;
|
||||
bool call_proc_req_cb = false;
|
||||
if (dev_obj->dynamic.open_count == 0) {
|
||||
@@ -845,6 +996,26 @@ esp_err_t usbh_devs_close(usb_device_handle_t dev_hdl)
|
||||
if (call_proc_req_cb) {
|
||||
p_usbh_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_USBH, false, p_usbh_obj->constant.proc_req_cb_arg);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t usbh_devs_new_dev_event(usb_device_handle_t dev_hdl)
|
||||
{
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
bool call_proc_req_cb = false;
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
// Device must be in the configured state
|
||||
USBH_CHECK_FROM_CRIT(dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
|
||||
call_proc_req_cb = _dev_set_actions(dev_obj, DEV_ACTION_PROP_NEW_DEV);
|
||||
USBH_EXIT_CRITICAL();
|
||||
|
||||
// Call the processing request callback
|
||||
if (call_proc_req_cb) {
|
||||
p_usbh_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_USBH, false, p_usbh_obj->constant.proc_req_cb_arg);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -870,28 +1041,26 @@ esp_err_t usbh_dev_get_info(usb_device_handle_t dev_hdl, usb_device_info_t *dev_
|
||||
USBH_CHECK(dev_hdl != NULL && dev_info != NULL, ESP_ERR_INVALID_ARG);
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
|
||||
esp_err_t ret;
|
||||
// Device must be configured, or not attached (if it suddenly disconnected)
|
||||
USBH_ENTER_CRITICAL();
|
||||
if (!(dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED || dev_obj->dynamic.state == USB_DEVICE_STATE_NOT_ATTACHED)) {
|
||||
USBH_EXIT_CRITICAL();
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto exit;
|
||||
}
|
||||
// Critical section for the dynamic members
|
||||
dev_info->speed = dev_obj->constant.speed;
|
||||
dev_info->dev_addr = dev_obj->constant.address;
|
||||
dev_info->bMaxPacketSize0 = dev_obj->constant.desc->bMaxPacketSize0;
|
||||
USBH_EXIT_CRITICAL();
|
||||
assert(dev_obj->constant.config_desc);
|
||||
dev_info->bConfigurationValue = dev_obj->constant.config_desc->bConfigurationValue;
|
||||
// String descriptors are allowed to be NULL as not all devices support them
|
||||
// Device descriptor might not have been set yet
|
||||
if (dev_obj->constant.desc) {
|
||||
dev_info->bMaxPacketSize0 = dev_obj->constant.desc->bMaxPacketSize0;
|
||||
} else {
|
||||
// Use the default pipe's MPS instead
|
||||
dev_info->bMaxPacketSize0 = hcd_pipe_get_mps(dev_obj->constant.default_pipe);
|
||||
}
|
||||
// Configuration descriptor might not have been set yet
|
||||
if (dev_obj->constant.config_desc) {
|
||||
dev_info->bConfigurationValue = dev_obj->constant.config_desc->bConfigurationValue;
|
||||
} else {
|
||||
dev_info->bConfigurationValue = 0;
|
||||
}
|
||||
dev_info->str_desc_manufacturer = dev_obj->constant.str_desc_manu;
|
||||
dev_info->str_desc_product = dev_obj->constant.str_desc_product;
|
||||
dev_info->str_desc_serial_num = dev_obj->constant.str_desc_ser_num;
|
||||
ret = ESP_OK;
|
||||
exit:
|
||||
return ret;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t **dev_desc_ret)
|
||||
@@ -899,10 +1068,6 @@ esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t
|
||||
USBH_CHECK(dev_hdl != NULL && dev_desc_ret != NULL, ESP_ERR_INVALID_ARG);
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
USBH_CHECK_FROM_CRIT(dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
|
||||
USBH_EXIT_CRITICAL();
|
||||
|
||||
*dev_desc_ret = dev_obj->constant.desc;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -912,19 +1077,256 @@ esp_err_t usbh_dev_get_config_desc(usb_device_handle_t dev_hdl, const usb_config
|
||||
USBH_CHECK(dev_hdl != NULL && config_desc_ret != NULL, ESP_ERR_INVALID_ARG);
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
|
||||
*config_desc_ret = dev_obj->constant.config_desc;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// ----------------------- Setters -------------------------
|
||||
|
||||
esp_err_t usbh_dev_enum_lock(usb_device_handle_t dev_hdl)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
// Device must be in the configured state
|
||||
USBH_ENTER_CRITICAL();
|
||||
if (dev_obj->dynamic.state != USB_DEVICE_STATE_CONFIGURED) {
|
||||
USBH_EXIT_CRITICAL();
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
|
||||
// We need to take the mux_lock to access mux_protected members
|
||||
xSemaphoreTake(p_usbh_obj->constant.mux_lock, portMAX_DELAY);
|
||||
|
||||
/*
|
||||
The device's enum_lock can only be set when the following conditions are met:
|
||||
- No other endpoints except EP0 have been allocated
|
||||
- We are the sole opener
|
||||
- Device's enum_lock is not already set
|
||||
*/
|
||||
// Check that no other endpoints except EP0 have been allocated
|
||||
bool ep_found = false;
|
||||
for (int i = 0; i < NUM_NON_DEFAULT_EP; i++) {
|
||||
if (dev_obj->mux_protected.endpoints[i] != NULL) {
|
||||
ep_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ep_found) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto exit;
|
||||
}
|
||||
// Check that we are the sole opener and enum_lock is not already set
|
||||
USBH_ENTER_CRITICAL();
|
||||
if (!dev_obj->dynamic.flags.enum_lock && (dev_obj->dynamic.open_count == 1)) {
|
||||
dev_obj->dynamic.flags.enum_lock = true;
|
||||
ret = ESP_OK;
|
||||
} else {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
USBH_EXIT_CRITICAL();
|
||||
assert(dev_obj->constant.config_desc);
|
||||
*config_desc_ret = dev_obj->constant.config_desc;
|
||||
ret = ESP_OK;
|
||||
|
||||
exit:
|
||||
xSemaphoreGive(p_usbh_obj->constant.mux_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t usbh_dev_enum_unlock(usb_device_handle_t dev_hdl)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
// Device's enum_lock must have been previously set
|
||||
if (dev_obj->dynamic.flags.enum_lock) {
|
||||
assert(dev_obj->dynamic.open_count == 1); // We must still be the sole opener
|
||||
dev_obj->dynamic.flags.enum_lock = false;
|
||||
ret = ESP_OK;
|
||||
} else {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
USBH_EXIT_CRITICAL();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t usbh_dev_set_ep0_mps(usb_device_handle_t dev_hdl, uint16_t wMaxPacketSize)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
// Device's EP0 MPS can only be updated when in the default state
|
||||
if (dev_obj->dynamic.state != USB_DEVICE_STATE_DEFAULT) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto exit;
|
||||
}
|
||||
// Device's enum_lock must be set before enumeration related data fields can be set
|
||||
if (dev_obj->dynamic.flags.enum_lock) {
|
||||
ret = hcd_pipe_update_mps(dev_obj->constant.default_pipe, wMaxPacketSize);
|
||||
} else {
|
||||
ret = ESP_ERR_NOT_ALLOWED;
|
||||
}
|
||||
exit:
|
||||
USBH_EXIT_CRITICAL();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t usbh_dev_set_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
// Device's address can only be set when in the default state
|
||||
USBH_CHECK_FROM_CRIT(dev_obj->dynamic.state == USB_DEVICE_STATE_DEFAULT, ESP_ERR_INVALID_STATE);
|
||||
// Device's enum_lock must be set before enumeration related data fields can be set
|
||||
USBH_CHECK_FROM_CRIT(dev_obj->dynamic.flags.enum_lock, ESP_ERR_NOT_ALLOWED);
|
||||
// Update the device and default pipe's target address
|
||||
ret = hcd_pipe_update_dev_addr(dev_obj->constant.default_pipe, dev_addr);
|
||||
if (ret == ESP_OK) {
|
||||
dev_obj->constant.address = dev_addr;
|
||||
dev_obj->dynamic.state = USB_DEVICE_STATE_ADDRESS;
|
||||
}
|
||||
USBH_EXIT_CRITICAL();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t usbh_dev_set_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t *device_desc)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL && device_desc != NULL, ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
usb_device_desc_t *new_desc, *old_desc;
|
||||
|
||||
// Allocate and copy new device descriptor
|
||||
new_desc = heap_caps_malloc(sizeof(usb_device_desc_t), MALLOC_CAP_DEFAULT);
|
||||
if (new_desc == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(new_desc, device_desc, sizeof(usb_device_desc_t));
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
// Device's descriptor can only be set in the default or addressed state
|
||||
if (!(dev_obj->dynamic.state == USB_DEVICE_STATE_DEFAULT || dev_obj->dynamic.state == USB_DEVICE_STATE_ADDRESS)) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto err;
|
||||
}
|
||||
// Device's enum_lock must be set before we can set its device descriptor
|
||||
if (!dev_obj->dynamic.flags.enum_lock) {
|
||||
ret = ESP_ERR_NOT_ALLOWED;
|
||||
goto err;
|
||||
}
|
||||
old_desc = dev_obj->constant.desc; // Save old descriptor for cleanup
|
||||
dev_obj->constant.desc = new_desc; // Assign new descriptor
|
||||
USBH_EXIT_CRITICAL();
|
||||
|
||||
// Clean up old descriptor or failed assignment
|
||||
heap_caps_free(old_desc);
|
||||
ret = ESP_OK;
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
USBH_EXIT_CRITICAL();
|
||||
heap_caps_free(new_desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t usbh_dev_set_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t *config_desc_full)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL && config_desc_full != NULL, ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
usb_config_desc_t *new_desc, *old_desc;
|
||||
|
||||
// Allocate and copy new config descriptor
|
||||
new_desc = heap_caps_malloc(config_desc_full->wTotalLength, MALLOC_CAP_DEFAULT);
|
||||
if (new_desc == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(new_desc, config_desc_full, config_desc_full->wTotalLength);
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
// Device's config descriptor can only be set when in the addressed state
|
||||
if (dev_obj->dynamic.state != USB_DEVICE_STATE_ADDRESS) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto err;
|
||||
}
|
||||
// Device's enum_lock must be set before we can set its config descriptor
|
||||
if (!dev_obj->dynamic.flags.enum_lock) {
|
||||
ret = ESP_ERR_NOT_ALLOWED;
|
||||
goto err;
|
||||
}
|
||||
old_desc = dev_obj->constant.config_desc; // Save old descriptor for cleanup
|
||||
dev_obj->constant.config_desc = new_desc; // Assign new descriptor
|
||||
dev_obj->dynamic.state = USB_DEVICE_STATE_CONFIGURED;
|
||||
USBH_EXIT_CRITICAL();
|
||||
|
||||
// Clean up old descriptor or failed assignment
|
||||
heap_caps_free(old_desc);
|
||||
ret = ESP_OK;
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
USBH_EXIT_CRITICAL();
|
||||
heap_caps_free(new_desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t usbh_dev_set_str_desc(usb_device_handle_t dev_hdl, const usb_str_desc_t *str_desc, int select)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL && str_desc != NULL && (select >= 0 && select < 3), ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
usb_str_desc_t *new_desc, *old_desc;
|
||||
|
||||
// Allocate and copy new string descriptor
|
||||
new_desc = heap_caps_malloc(str_desc->bLength, MALLOC_CAP_DEFAULT);
|
||||
if (new_desc == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(new_desc, str_desc, str_desc->bLength);
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
// Device's string descriptors can only be set when in the default state
|
||||
if (dev_obj->dynamic.state != USB_DEVICE_STATE_CONFIGURED) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto err;
|
||||
}
|
||||
// Device's enum_lock must be set before we can set its string descriptors
|
||||
if (!dev_obj->dynamic.flags.enum_lock) {
|
||||
ret = ESP_ERR_NOT_ALLOWED;
|
||||
goto err;
|
||||
}
|
||||
// Assign to the selected descriptor
|
||||
switch (select) {
|
||||
case 0:
|
||||
old_desc = dev_obj->constant.str_desc_manu;
|
||||
dev_obj->constant.str_desc_manu = new_desc;
|
||||
break;
|
||||
case 1:
|
||||
old_desc = dev_obj->constant.str_desc_product;
|
||||
dev_obj->constant.str_desc_product = new_desc;
|
||||
break;
|
||||
default: // 2
|
||||
old_desc = dev_obj->constant.str_desc_ser_num;
|
||||
dev_obj->constant.str_desc_ser_num = new_desc;
|
||||
break;
|
||||
}
|
||||
USBH_EXIT_CRITICAL();
|
||||
|
||||
// Clean up old descriptor or failed assignment
|
||||
heap_caps_free(old_desc);
|
||||
ret = ESP_OK;
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
USBH_EXIT_CRITICAL();
|
||||
heap_caps_free(new_desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -939,6 +1341,7 @@ esp_err_t usbh_ep_alloc(usb_device_handle_t dev_hdl, usbh_ep_config_t *ep_config
|
||||
esp_err_t ret;
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
endpoint_t *ep_obj;
|
||||
USBH_CHECK(dev_obj->constant.config_desc, ESP_ERR_INVALID_STATE); // Configuration descriptor must be set
|
||||
|
||||
// Find the endpoint descriptor from the device's current configuration descriptor
|
||||
const usb_ep_desc_t *ep_desc = usb_parse_endpoint_descriptor_by_address(dev_obj->constant.config_desc, ep_config->bInterfaceNumber, ep_config->bAlternateSetting, ep_config->bEndpointAddress, NULL);
|
||||
@@ -1065,10 +1468,11 @@ esp_err_t usbh_dev_submit_ctrl_urb(usb_device_handle_t dev_hdl, urb_t *urb)
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
USBH_CHECK(urb_check_args(urb), ESP_ERR_INVALID_ARG);
|
||||
bool xfer_is_in = ((usb_setup_packet_t *)urb->transfer.data_buffer)->bmRequestType & USB_BM_REQUEST_TYPE_DIR_IN;
|
||||
USBH_CHECK(transfer_check_usb_compliance(&(urb->transfer), USB_TRANSFER_TYPE_CTRL, dev_obj->constant.desc->bMaxPacketSize0, xfer_is_in), ESP_ERR_INVALID_ARG);
|
||||
// Device descriptor could still be NULL at this point, so we get the MPS from the pipe instead.
|
||||
unsigned int mps = hcd_pipe_get_mps(dev_obj->constant.default_pipe);
|
||||
USBH_CHECK(transfer_check_usb_compliance(&(urb->transfer), USB_TRANSFER_TYPE_CTRL, mps, xfer_is_in), ESP_ERR_INVALID_ARG);
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
USBH_CHECK_FROM_CRIT(dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
|
||||
// Increment the control transfer count first
|
||||
dev_obj->dynamic.num_ctrl_xfers_inflight++;
|
||||
USBH_EXIT_CRITICAL();
|
||||
@@ -1121,156 +1525,3 @@ esp_err_t usbh_ep_dequeue_urb(usbh_ep_handle_t ep_hdl, urb_t **urb_ret)
|
||||
*urb_ret = hcd_urb_dequeue(ep_obj->constant.pipe_hdl);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// -------------------------------------------------- Hub Functions ----------------------------------------------------
|
||||
|
||||
// ------------------- Device Related ----------------------
|
||||
|
||||
esp_err_t usbh_hub_add_dev(hcd_port_handle_t port_hdl, usb_speed_t dev_speed, usb_device_handle_t *new_dev_hdl, hcd_pipe_handle_t *default_pipe_hdl)
|
||||
{
|
||||
// Note: Parent device handle can be NULL if it's connected to the root hub
|
||||
USBH_CHECK(new_dev_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
device_t *dev_obj;
|
||||
ret = device_alloc(port_hdl, dev_speed, &dev_obj);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
// Write-back device handle
|
||||
*new_dev_hdl = (usb_device_handle_t)dev_obj;
|
||||
*default_pipe_hdl = dev_obj->constant.default_pipe;
|
||||
ret = ESP_OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t usbh_hub_dev_gone(usb_device_handle_t dev_hdl)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
bool call_proc_req_cb;
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
dev_obj->dynamic.flags.is_gone = 1;
|
||||
// Check if the device can be freed immediately
|
||||
if (dev_obj->dynamic.open_count == 0) {
|
||||
// Device is not currently opened at all. Can free immediately.
|
||||
call_proc_req_cb = _dev_set_actions(dev_obj, DEV_ACTION_FREE);
|
||||
} else {
|
||||
// Device is still opened. Flush endpoints and propagate device gone event
|
||||
call_proc_req_cb = _dev_set_actions(dev_obj,
|
||||
DEV_ACTION_EPn_HALT_FLUSH |
|
||||
DEV_ACTION_EP0_FLUSH |
|
||||
DEV_ACTION_EP0_DEQUEUE |
|
||||
DEV_ACTION_PROP_GONE_EVT);
|
||||
}
|
||||
USBH_EXIT_CRITICAL();
|
||||
|
||||
if (call_proc_req_cb) {
|
||||
p_usbh_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_USBH, false, p_usbh_obj->constant.proc_req_cb_arg);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// ----------------- Enumeration Related -------------------
|
||||
|
||||
esp_err_t usbh_hub_enum_fill_dev_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
|
||||
USBH_ENTER_CRITICAL();
|
||||
dev_obj->dynamic.state = USB_DEVICE_STATE_ADDRESS;
|
||||
USBH_EXIT_CRITICAL();
|
||||
|
||||
// We can modify the info members outside the critical section
|
||||
dev_obj->constant.address = dev_addr;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t usbh_hub_enum_fill_dev_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t *device_desc)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL && device_desc != NULL, ESP_ERR_INVALID_ARG);
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
// We can modify the info members outside the critical section
|
||||
memcpy((usb_device_desc_t *)dev_obj->constant.desc, device_desc, sizeof(usb_device_desc_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t usbh_hub_enum_fill_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t *config_desc_full)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL && config_desc_full != NULL, ESP_ERR_INVALID_ARG);
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
// Allocate memory to store the configuration descriptor
|
||||
usb_config_desc_t *config_desc = heap_caps_malloc(config_desc_full->wTotalLength, MALLOC_CAP_DEFAULT); // Buffer to copy over full configuration descriptor (wTotalLength)
|
||||
if (config_desc == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
// Copy the configuration descriptor
|
||||
memcpy(config_desc, config_desc_full, config_desc_full->wTotalLength);
|
||||
// Assign the config desc to the device object
|
||||
assert(dev_obj->constant.config_desc == NULL);
|
||||
dev_obj->constant.config_desc = config_desc;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t usbh_hub_enum_fill_str_desc(usb_device_handle_t dev_hdl, const usb_str_desc_t *str_desc, int select)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL && str_desc != NULL && (select >= 0 && select < 3), ESP_ERR_INVALID_ARG);
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
// Allocate memory to store the manufacturer string descriptor
|
||||
usb_str_desc_t *str_desc_fill = heap_caps_malloc(str_desc->bLength, MALLOC_CAP_DEFAULT);
|
||||
if (str_desc_fill == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
// Copy the string descriptor
|
||||
memcpy(str_desc_fill, str_desc, str_desc->bLength);
|
||||
// Assign filled string descriptor to the device object
|
||||
switch (select) {
|
||||
case 0:
|
||||
assert(dev_obj->constant.str_desc_manu == NULL);
|
||||
dev_obj->constant.str_desc_manu = str_desc_fill;
|
||||
break;
|
||||
case 1:
|
||||
assert(dev_obj->constant.str_desc_product == NULL);
|
||||
dev_obj->constant.str_desc_product = str_desc_fill;
|
||||
break;
|
||||
default: // 2
|
||||
assert(dev_obj->constant.str_desc_ser_num == NULL);
|
||||
dev_obj->constant.str_desc_ser_num = str_desc_fill;
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t usbh_hub_enum_done(usb_device_handle_t dev_hdl)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
|
||||
// We need to take the mux_lock to access mux_protected members
|
||||
xSemaphoreTake(p_usbh_obj->constant.mux_lock, portMAX_DELAY);
|
||||
USBH_ENTER_CRITICAL();
|
||||
dev_obj->dynamic.state = USB_DEVICE_STATE_CONFIGURED;
|
||||
// Add the device to list of devices, then trigger a device event
|
||||
TAILQ_INSERT_TAIL(&p_usbh_obj->dynamic.devs_idle_tailq, dev_obj, dynamic.tailq_entry); // Add it to the idle device list first
|
||||
bool call_proc_req_cb = _dev_set_actions(dev_obj, DEV_ACTION_PROP_NEW_DEV);
|
||||
USBH_EXIT_CRITICAL();
|
||||
p_usbh_obj->mux_protected.num_device++;
|
||||
xSemaphoreGive(p_usbh_obj->constant.mux_lock);
|
||||
|
||||
// Update the EP0's underlying pipe's callback
|
||||
ESP_ERROR_CHECK(hcd_pipe_update_callback(dev_obj->constant.default_pipe, ep0_pipe_callback, (void *)dev_obj));
|
||||
// Call the processing request callback
|
||||
if (call_proc_req_cb) {
|
||||
p_usbh_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_USBH, false, p_usbh_obj->constant.proc_req_cb_arg);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t usbh_hub_enum_failed(usb_device_handle_t dev_hdl)
|
||||
{
|
||||
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
device_t *dev_obj = (device_t *)dev_hdl;
|
||||
device_free(dev_obj);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user