mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
Merge branch 'feature/usb_dual_host_2_backport_v5.3' into 'release/v5.3'
feat(usb/host): Add option to choose peripheral for USB host library (backport v5.3) See merge request espressif/esp-idf!39723
This commit is contained in:
@@ -106,9 +106,9 @@ extern const usb_dwc_info_t usb_dwc_info;
|
||||
/* ------------------------------- Deprecated ------------------------------- */
|
||||
/* Todo: Remove in ESP-IDF v6.0 (IDF-9052) */
|
||||
#include <stdint.h>
|
||||
#include "soc/periph_defs.h"
|
||||
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
#include "soc/periph_defs.h"
|
||||
|
||||
/*
|
||||
Stores a bunch of USB-peripheral data.
|
||||
|
||||
+75
-48
@@ -42,7 +42,7 @@
|
||||
#define CTRL_EP_MAX_MPS_LS 8 // Largest Maximum Packet Size for Low Speed control endpoints
|
||||
#define CTRL_EP_MAX_MPS_HSFS 64 // Largest Maximum Packet Size for High & Full Speed control endpoints
|
||||
|
||||
#define NUM_PORTS 1 // The controller only has one port.
|
||||
#define NUM_PORTS SOC_USB_OTG_PERIPH_NUM // Each peripheral is a root port
|
||||
|
||||
// ----------------------- Configs -------------------------
|
||||
|
||||
@@ -106,7 +106,6 @@ const char *HCD_DWC_TAG = "HCD DWC";
|
||||
* For other SOCs this is no-operation
|
||||
*/
|
||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
#define CACHE_SYNC_FRAME_LIST(frame_list) cache_sync_frame_list(frame_list)
|
||||
#define CACHE_SYNC_XFER_DESCRIPTOR_LIST_M2C(buffer) cache_sync_xfer_descriptor_list(buffer, true)
|
||||
#define CACHE_SYNC_XFER_DESCRIPTOR_LIST_C2M(buffer) cache_sync_xfer_descriptor_list(buffer, false)
|
||||
@@ -255,15 +254,15 @@ struct port_obj {
|
||||
void *callback_arg;
|
||||
SemaphoreHandle_t port_mux;
|
||||
void *context;
|
||||
intr_handle_t isr_hdl; // Interrupt handle for this root port (USB-OTG peripheral)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Object representing the HCD
|
||||
*/
|
||||
typedef struct {
|
||||
// Ports (Hardware only has one)
|
||||
port_t *port_obj;
|
||||
intr_handle_t isr_hdl;
|
||||
// Ports: Each peripheral is a root port
|
||||
port_t *port_obj[NUM_PORTS];
|
||||
} hcd_obj_t;
|
||||
|
||||
static portMUX_TYPE hcd_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
@@ -1040,52 +1039,66 @@ esp_err_t hcd_install(const hcd_config_t *config)
|
||||
HCD_CHECK_FROM_CRIT(s_hcd_obj == NULL, ESP_ERR_INVALID_STATE);
|
||||
HCD_EXIT_CRITICAL();
|
||||
|
||||
// Check if peripheral_map does not have bits set outside of valid range. Valid bits are BIT0 - BIT(NUM_PORTS - 1)
|
||||
HCD_CHECK((config->peripheral_map != 0) && (config->peripheral_map < BIT(NUM_PORTS)), ESP_ERR_INVALID_ARG);
|
||||
|
||||
esp_err_t err_ret;
|
||||
|
||||
// Allocate memory for the driver object
|
||||
hcd_obj_t *p_hcd_obj_dmy = calloc(1, sizeof(hcd_obj_t));
|
||||
if (p_hcd_obj_dmy == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
// Allocate each port object (the hardware currently only has one port)
|
||||
p_hcd_obj_dmy->port_obj = port_obj_alloc();
|
||||
if (p_hcd_obj_dmy->port_obj == NULL) {
|
||||
err_ret = ESP_ERR_NO_MEM;
|
||||
goto port_alloc_err;
|
||||
}
|
||||
// Allocate interrupt
|
||||
err_ret = esp_intr_alloc(usb_dwc_info.controllers[0].irq,
|
||||
config->intr_flags | ESP_INTR_FLAG_INTRDISABLED, // The interrupt must be disabled until the port is initialized
|
||||
intr_hdlr_main,
|
||||
(void *)p_hcd_obj_dmy->port_obj,
|
||||
&p_hcd_obj_dmy->isr_hdl);
|
||||
if (err_ret != ESP_OK) {
|
||||
ESP_LOGE(HCD_DWC_TAG, "Interrupt alloc error: %s", esp_err_to_name(err_ret));
|
||||
goto intr_alloc_err;
|
||||
}
|
||||
// Apply custom FIFO config if provided, otherwise mark as default (all zeros)
|
||||
memset(&p_hcd_obj_dmy->port_obj->fifo_config, 0, sizeof(p_hcd_obj_dmy->port_obj->fifo_config));
|
||||
if (config->fifo_config != NULL) {
|
||||
// Convert and validate user-provided config
|
||||
err_ret = convert_fifo_config_to_hal_config(config->fifo_config, &p_hcd_obj_dmy->port_obj->fifo_config);
|
||||
if (err_ret != ESP_OK) {
|
||||
goto assign_err;
|
||||
|
||||
// Allocate each port object
|
||||
for (int i = 0; i < NUM_PORTS; i++) {
|
||||
if (BIT(i) & config->peripheral_map) {
|
||||
p_hcd_obj_dmy->port_obj[i] = port_obj_alloc();
|
||||
if (p_hcd_obj_dmy->port_obj[i] == NULL) {
|
||||
err_ret = ESP_ERR_NO_MEM;
|
||||
goto clean_up;
|
||||
}
|
||||
// Allocate interrupt
|
||||
const int irq_index = usb_dwc_info.controllers[i].irq;
|
||||
err_ret = esp_intr_alloc(irq_index,
|
||||
config->intr_flags | ESP_INTR_FLAG_INTRDISABLED, // The interrupt must be disabled until the port is initialized
|
||||
intr_hdlr_main,
|
||||
(void *)p_hcd_obj_dmy->port_obj[i],
|
||||
&p_hcd_obj_dmy->port_obj[i]->isr_hdl);
|
||||
if (err_ret != ESP_OK) {
|
||||
ESP_LOGE(HCD_DWC_TAG, "Interrupt alloc error: %s", esp_err_to_name(err_ret));
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
// Apply custom FIFO config if provided
|
||||
if (config->fifo_config != NULL) {
|
||||
// Convert and validate user-provided config
|
||||
err_ret = convert_fifo_config_to_hal_config(config->fifo_config, &p_hcd_obj_dmy->port_obj[i]->fifo_config);
|
||||
if (err_ret != ESP_OK) {
|
||||
goto clean_up;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HCD_ENTER_CRITICAL();
|
||||
if (s_hcd_obj != NULL) {
|
||||
HCD_EXIT_CRITICAL();
|
||||
err_ret = ESP_ERR_INVALID_STATE;
|
||||
goto assign_err;
|
||||
goto clean_up;
|
||||
}
|
||||
s_hcd_obj = p_hcd_obj_dmy;
|
||||
HCD_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
|
||||
assign_err:
|
||||
esp_intr_free(p_hcd_obj_dmy->isr_hdl);
|
||||
intr_alloc_err:
|
||||
port_obj_free(p_hcd_obj_dmy->port_obj);
|
||||
port_alloc_err:
|
||||
clean_up:
|
||||
// Free resources
|
||||
for (int i = 0; i < NUM_PORTS; i++) {
|
||||
if (p_hcd_obj_dmy->port_obj[i]) {
|
||||
esp_intr_free(p_hcd_obj_dmy->port_obj[i]->isr_hdl);
|
||||
port_obj_free(p_hcd_obj_dmy->port_obj[i]);
|
||||
}
|
||||
}
|
||||
free(p_hcd_obj_dmy);
|
||||
return err_ret;
|
||||
}
|
||||
@@ -1093,8 +1106,14 @@ port_alloc_err:
|
||||
esp_err_t hcd_uninstall(void)
|
||||
{
|
||||
HCD_ENTER_CRITICAL();
|
||||
// Check that all ports have been disabled (there's only one port)
|
||||
if (s_hcd_obj == NULL || s_hcd_obj->port_obj->initialized) {
|
||||
// Check that all ports have been disabled
|
||||
bool all_ports_disabled = true;
|
||||
for (int i = 0; i < NUM_PORTS; i++) {
|
||||
if (s_hcd_obj->port_obj[i]) {
|
||||
all_ports_disabled = all_ports_disabled && !(s_hcd_obj->port_obj[i]->initialized);
|
||||
}
|
||||
}
|
||||
if (s_hcd_obj == NULL || !all_ports_disabled) {
|
||||
HCD_EXIT_CRITICAL();
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
@@ -1103,8 +1122,12 @@ esp_err_t hcd_uninstall(void)
|
||||
HCD_EXIT_CRITICAL();
|
||||
|
||||
// Free resources
|
||||
port_obj_free(p_hcd_obj_dmy->port_obj);
|
||||
esp_intr_free(p_hcd_obj_dmy->isr_hdl);
|
||||
for (int i = 0; i < NUM_PORTS; i++) {
|
||||
if (p_hcd_obj_dmy->port_obj[i]) {
|
||||
esp_intr_free(p_hcd_obj_dmy->port_obj[i]->isr_hdl);
|
||||
port_obj_free(p_hcd_obj_dmy->port_obj[i]);
|
||||
}
|
||||
}
|
||||
free(p_hcd_obj_dmy);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -1364,13 +1387,13 @@ exit:
|
||||
|
||||
esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, hcd_port_handle_t *port_hdl)
|
||||
{
|
||||
HCD_CHECK(port_number > 0 && port_config != NULL && port_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
HCD_CHECK(port_number <= NUM_PORTS, ESP_ERR_NOT_FOUND);
|
||||
HCD_CHECK(port_number >= 0 && port_config != NULL && port_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||
HCD_CHECK(port_number < NUM_PORTS, ESP_ERR_NOT_FOUND);
|
||||
|
||||
HCD_ENTER_CRITICAL();
|
||||
HCD_CHECK_FROM_CRIT(s_hcd_obj != NULL && !s_hcd_obj->port_obj->initialized, ESP_ERR_INVALID_STATE);
|
||||
HCD_CHECK_FROM_CRIT(s_hcd_obj != NULL && !s_hcd_obj->port_obj[port_number]->initialized, ESP_ERR_INVALID_STATE);
|
||||
// Port object memory and resources (such as the mutex) already be allocated. Just need to initialize necessary fields only
|
||||
port_t *port_obj = s_hcd_obj->port_obj;
|
||||
port_t *port_obj = s_hcd_obj->port_obj[port_number];
|
||||
TAILQ_INIT(&port_obj->pipes_idle_tailq);
|
||||
TAILQ_INIT(&port_obj->pipes_active_tailq);
|
||||
port_obj->state = HCD_PORT_STATE_NOT_POWERED;
|
||||
@@ -1378,18 +1401,22 @@ esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, h
|
||||
port_obj->callback = port_config->callback;
|
||||
port_obj->callback_arg = port_config->callback_arg;
|
||||
port_obj->context = port_config->context;
|
||||
usb_dwc_hal_init(port_obj->hal, 0);
|
||||
|
||||
// USB-HAL's size is dependent on its configuration, namely on number of channels in the configuration
|
||||
// We must first initialize the HAL, to get the number of channels and then allocate memory for the channels
|
||||
usb_dwc_hal_init(port_obj->hal, port_number);
|
||||
port_obj->hal->channels.hdls = calloc(port_obj->hal->constant_config.chan_num_total, sizeof(usb_dwc_hal_chan_t*));
|
||||
HCD_CHECK_FROM_CRIT(port_obj->hal->channels.hdls != NULL, ESP_ERR_NO_MEM);
|
||||
|
||||
port_obj->initialized = true;
|
||||
// Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset
|
||||
// Clear the frame list. We will set the frame list register and enable periodic scheduling after a successful reset
|
||||
memset(port_obj->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t));
|
||||
// If FIFO config is zeroed -> calculate from bias
|
||||
if (_is_fifo_config_by_bias(&port_obj->fifo_config)) {
|
||||
// Calculate default FIFO sizes based on Kconfig bias settings
|
||||
_calculate_fifo_from_bias(port_obj, port_obj->hal);
|
||||
}
|
||||
esp_intr_enable(s_hcd_obj->isr_hdl);
|
||||
esp_intr_enable(port_obj->isr_hdl);
|
||||
*port_hdl = (hcd_port_handle_t)port_obj;
|
||||
HCD_EXIT_CRITICAL();
|
||||
ESP_LOGD(HCD_DWC_TAG, "FIFO config lines: RX=%" PRIu32 ", PTX=%" PRIu32 ", NPTX=%" PRIu32,
|
||||
@@ -1411,7 +1438,7 @@ esp_err_t hcd_port_deinit(hcd_port_handle_t port_hdl)
|
||||
&& port->task_waiting_port_notif == NULL,
|
||||
ESP_ERR_INVALID_STATE);
|
||||
port->initialized = false;
|
||||
esp_intr_disable(s_hcd_obj->isr_hdl);
|
||||
esp_intr_disable(port->isr_hdl);
|
||||
free(port->hal->channels.hdls);
|
||||
usb_dwc_hal_deinit(port->hal);
|
||||
HCD_EXIT_CRITICAL();
|
||||
@@ -1527,7 +1554,7 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl)
|
||||
ESP_ERR_INVALID_STATE);
|
||||
|
||||
// We are about to do a soft reset on the peripheral. Disable the peripheral throughout
|
||||
esp_intr_disable(s_hcd_obj->isr_hdl);
|
||||
esp_intr_disable(port->isr_hdl);
|
||||
usb_dwc_hal_core_soft_reset(port->hal);
|
||||
port->state = HCD_PORT_STATE_NOT_POWERED;
|
||||
port->last_event = HCD_PORT_EVENT_NONE;
|
||||
@@ -1535,7 +1562,7 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl)
|
||||
|
||||
// Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset
|
||||
memset(port->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t));
|
||||
esp_intr_enable(s_hcd_obj->isr_hdl);
|
||||
esp_intr_enable(port->isr_hdl);
|
||||
HCD_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "freertos/portmacro.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_bit_defs.h"
|
||||
#include "esp_log.h"
|
||||
#include "usb_private.h"
|
||||
#include "hcd.h"
|
||||
@@ -28,8 +29,6 @@ Implementation of the HUB driver that only supports the Root Hub with a single p
|
||||
implement the bare minimum to control the root HCD port.
|
||||
*/
|
||||
|
||||
#define HUB_ROOT_PORT_NUM 1 // HCD only supports one port
|
||||
|
||||
#ifdef CONFIG_USB_HOST_HW_BUFFER_BIAS_IN
|
||||
#define HUB_ROOT_HCD_PORT_FIFO_BIAS HCD_PORT_FIFO_BIAS_RX
|
||||
#elif CONFIG_USB_HOST_HW_BUFFER_BIAS_PERIODIC_OUT
|
||||
@@ -61,7 +60,6 @@ typedef enum {
|
||||
ROOT_PORT_STATE_POWERED, /**< Root port is powered, device is not connected */
|
||||
ROOT_PORT_STATE_DISABLED, /**< A device is connected but is disabled (i.e., not reset, no SOFs are sent) */
|
||||
ROOT_PORT_STATE_ENABLED, /**< A device is connected, port has been reset, SOFs are sent */
|
||||
ROOT_PORT_STATE_RECOVERY, /**< Root port encountered an error and needs to be recovered */
|
||||
} root_port_state_t;
|
||||
|
||||
/**
|
||||
@@ -567,7 +565,14 @@ esp_err_t hub_install(hub_config_t *hub_config, void **client_ret)
|
||||
.context = NULL,
|
||||
};
|
||||
hcd_port_handle_t root_port_hdl;
|
||||
ret = hcd_port_init(HUB_ROOT_PORT_NUM, &port_config, &root_port_hdl);
|
||||
|
||||
// Right now we support only one root port, can be extended in future
|
||||
int root_port_index = 0;
|
||||
if (hub_config->port_map & BIT1) {
|
||||
root_port_index = 1;
|
||||
}
|
||||
|
||||
ret = hcd_port_init(root_port_index, &port_config, &root_port_hdl);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(HUB_DRIVER_TAG, "HCD Port init error: %s", esp_err_to_name(ret));
|
||||
goto err;
|
||||
|
||||
@@ -121,6 +121,13 @@ typedef struct {
|
||||
} fifo_settings_custom; /**< Optional custom FIFO configuration (advanced use).
|
||||
RX and NPTX must be > 0. If all fields are zero,
|
||||
a default configuration will be selected based on Kconfig bias. */
|
||||
unsigned peripheral_map; /**< Selects the USB peripheral(s) to use.
|
||||
- On targets with multiple USB peripherals, this field can be used to specify which ones to enable.
|
||||
- Set to 0 to use the default peripheral.
|
||||
- On High-Speed capable targets, the default is the High-Speed peripheral.
|
||||
- On Full-Speed only targets, the default is the Full-Speed peripheral.
|
||||
- Example: peripheral_map = BIT1; installs USB host on peripheral 1.
|
||||
- The mapping of bits to specific peripherals is defined in the USB_DWC_LL_GET_HW() macro. */
|
||||
} usb_host_config_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -154,9 +154,10 @@ typedef struct {
|
||||
* @brief HCD configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
int intr_flags; /**< Interrupt flags for HCD interrupt */
|
||||
const hcd_fifo_settings_t *fifo_config;/**< Optional pointer to custom FIFO config.
|
||||
If NULL, default configuration is used. */
|
||||
int intr_flags; /**< Interrupt flags for HCD interrupt */
|
||||
const hcd_fifo_settings_t *fifo_config; /**< Optional pointer to custom FIFO config.
|
||||
If NULL, default configuration is used. */
|
||||
unsigned peripheral_map; /**< Bit map of USB-OTG peripherals that belong to HCD. Set to zero to use default */
|
||||
} hcd_config_t;
|
||||
|
||||
/**
|
||||
@@ -166,6 +167,7 @@ typedef struct {
|
||||
hcd_port_callback_t callback; /**< HCD port event callback */
|
||||
void *callback_arg; /**< User argument for HCD port callback */
|
||||
void *context; /**< Context variable used to associate the port with upper layer object */
|
||||
unsigned peripheral_idx; /**< Index of USB peripheral this port belongs to. Index numbers are defined in USB_DWC_LL_GET_HW */
|
||||
} hcd_port_config_t;
|
||||
|
||||
/**
|
||||
@@ -225,8 +227,6 @@ esp_err_t hcd_uninstall(void);
|
||||
*
|
||||
* After a port is initialized, it will be put into the HCD_PORT_STATE_NOT_POWERED state.
|
||||
*
|
||||
* @note The host controller only has one port, thus the only valid port_number is 1
|
||||
*
|
||||
* @param[in] port_number Port number
|
||||
* @param[in] port_config Port configuration
|
||||
* @param[out] port_hdl Port handle
|
||||
|
||||
@@ -60,6 +60,7 @@ typedef void (*hub_event_cb_t)(hub_event_data_t *event_data, void *arg);
|
||||
* @brief Hub driver configuration
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned port_map; /**< Bitmap of root ports to enable */
|
||||
usb_proc_req_cb_t proc_req_cb; /**< Processing request callback */
|
||||
void *proc_req_cb_arg; /**< Processing request callback argument */
|
||||
hub_event_cb_t event_cb; /**< Hub event callback */
|
||||
|
||||
@@ -16,9 +16,11 @@ Warning: The USB Host Library API is still a beta version and may be subject to
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "soc/usb_dwc_periph.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_bit_defs.h"
|
||||
#include "hub.h"
|
||||
#include "enum.h"
|
||||
#include "usbh.h"
|
||||
@@ -466,16 +468,26 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
|
||||
- Hub
|
||||
*/
|
||||
|
||||
// For backward compatibility accept 0 too
|
||||
const unsigned peripheral_map = config->peripheral_map == 0 ? BIT0 : config->peripheral_map;
|
||||
|
||||
// Install USB PHY (if necessary). USB PHY driver will also enable the underlying Host Controller
|
||||
if (!config->skip_phy_setup) {
|
||||
bool init_utmi_phy = false; // Default value for Linux simulation
|
||||
|
||||
#if SOC_USB_OTG_SUPPORTED // In case we run on a real target, select the PHY from usb_dwc_info description structure
|
||||
// Right now we support only one peripheral, can be extended in future
|
||||
int peripheral_index = 0;
|
||||
if (peripheral_map & BIT1) {
|
||||
peripheral_index = 1;
|
||||
}
|
||||
init_utmi_phy = (usb_dwc_info.controllers[peripheral_index].supported_phys == USB_PHY_INST_UTMI_0);
|
||||
#endif // SOC_USB_OTG_SUPPORTED
|
||||
|
||||
// Host Library defaults to internal PHY
|
||||
usb_phy_config_t phy_config = {
|
||||
.controller = USB_PHY_CTRL_OTG,
|
||||
#if CONFIG_IDF_TARGET_ESP32P4 // ESP32-P4 has 2 USB-DWC peripherals, each with its dedicated PHY. We support HS+UTMI only ATM.
|
||||
.target = USB_PHY_TARGET_UTMI,
|
||||
#else
|
||||
.target = USB_PHY_TARGET_INT,
|
||||
#endif
|
||||
.target = init_utmi_phy ? USB_PHY_TARGET_UTMI : 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,
|
||||
@@ -493,6 +505,7 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
|
||||
hcd_fifo_settings_t user_fifo_config;
|
||||
hcd_config_t hcd_config = {
|
||||
.intr_flags = config->intr_flags,
|
||||
.peripheral_map = peripheral_map,
|
||||
.fifo_config = NULL, // Default: use bias strategy from Kconfig
|
||||
};
|
||||
|
||||
@@ -547,6 +560,7 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
|
||||
|
||||
// Install Hub
|
||||
hub_config_t hub_config = {
|
||||
.port_map = peripheral_map, // Each USB-OTG peripheral maps to a root port
|
||||
.proc_req_cb = proc_req_callback,
|
||||
.proc_req_cb_arg = NULL,
|
||||
.event_cb = hub_event_callback,
|
||||
|
||||
Reference in New Issue
Block a user