Skip to content

Commit cb4d901

Browse files
committed
feat(usb/hal): Add support for explicit FIFO configuration
Introduce a new HAL API `usb_dwc_hal_set_fifo_config()` that allows advanced users to manually configure RX, Non-Periodic TX, and Periodic TX FIFO sizes. This offers fine-grained control beyond the previous bias-based sizing approach. The HAL function no longer returns `esp_err_t`, and internal validations are enforced via `HAL_ASSERT()`. Responsibility for input validation has been moved to the HCD layer. FIFO configuration must be applied before any USB pipes are created or activated. This feature is intended for use during `usb_host_install()`. If no custom FIFO configuration is provided (i.e., all values are zero), the driver falls back to a bias-based default layout based on Kconfig settings (`CONFIG_USB_HOST_HW_BUFFER_BIAS_*`). Bias resolution is done inside `hcd_port_init()`. The `port_obj_t` structure has been extended with a `fifo_config` field, which stores the configuration to allow re-application after a USB port reset. Obsolete FIFO bias enums (`usb_hal_fifo_bias_t`, `hcd_port_fifo_bias_t`) and related APIs (`hcd_port_set_fifo_bias()`) have been removed in favor of the new structure-based mechanism. The HCD initialization and port reset flow has been updated to use the explicit FIFO configuration. USB Host maintainer documentation (`maintainers.md`) has been updated accordingly. Test cases were updated to remove the usage of removed bias API and now rely on default or custom FIFO configuration.
1 parent 01e9f8f commit cb4d901

File tree

12 files changed

+230
-142
lines changed

12 files changed

+230
-142
lines changed

components/hal/include/hal/usb_dwc_hal.h

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -29,15 +29,6 @@ extern "C" {
2929

3030
// ----------------------- Configs -------------------------
3131

32-
/**
33-
* @brief Possible FIFO biases
34-
*/
35-
typedef enum {
36-
USB_HAL_FIFO_BIAS_DEFAULT, /**< Default (balanced) FIFO sizes */
37-
USB_HAL_FIFO_BIAS_RX, /**< Bigger RX FIFO for IN transfers */
38-
USB_HAL_FIFO_BIAS_PTX, /**< Bigger periodic TX FIFO for ISOC OUT transfers */
39-
} usb_hal_fifo_bias_t;
40-
4132
/**
4233
* @brief MPS limits based on FIFO configuration
4334
*
@@ -261,20 +252,35 @@ void usb_dwc_hal_deinit(usb_dwc_hal_context_t *hal);
261252
void usb_dwc_hal_core_soft_reset(usb_dwc_hal_context_t *hal);
262253

263254
/**
264-
* @brief Set FIFO bias
255+
* @brief Check if FIFO configuration is valid
256+
*
257+
* This function checks that the sum of FIFO sizes does not exceed available space.
258+
* It does not modify hardware state and is safe to call from HAL or upper layers.
259+
*
260+
* @param[in] hal Pointer to HAL context (must be initialized)
261+
* @param[in] config Pointer to FIFO config to validate
262+
* @return true if config is valid, false otherwise
263+
*/
264+
bool usb_dwc_hal_fifo_config_is_valid(const usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config);
265+
266+
267+
/**
268+
* @brief Set the FIFO sizes of the USB-DWC core
269+
*
270+
* This function programs the FIFO sizing registers (RX FIFO, Non-Periodic TX FIFO,
271+
* and Periodic TX FIFO) based on the provided configuration. It must be called
272+
* during USB initialization, before any channels are allocated or transfers started.
265273
*
266-
* This function will set the sizes of each of the FIFOs (RX FIFO, Non-periodic TX FIFO, Periodic TX FIFO) and must be
267-
* called at least once before allocating the channel. Based on the type of endpoints (and the endpoints' MPS), there
268-
* may be situations where this function may need to be called again to resize the FIFOs. If resizing FIFOs dynamically,
269-
* it is the user's responsibility to ensure there are no active channels when this function is called.
274+
* The sum of all FIFO sizes must not exceed the hardware-defined limit
275+
* (see HWCFG3.DfifoDepth and EPINFO_CTL).
270276
*
271-
* @note After a port reset, the FIFO size registers will reset to their default values, so this function must be called
272-
* again post reset.
277+
* @note This function must be called exactly once during initialization and after
278+
* each USB port reset. It is typically used internally by the USB Host stack.
273279
*
274-
* @param[in] hal Context of the HAL layer
275-
* @param[in] fifo_bias FIFO bias configuration
280+
* @param[inout] hal Pointer to the HAL context
281+
* @param[in] config Pointer to the FIFO configuration to apply (must be valid)
276282
*/
277-
void usb_dwc_hal_set_fifo_bias(usb_dwc_hal_context_t *hal, const usb_hal_fifo_bias_t fifo_bias);
283+
void usb_dwc_hal_set_fifo_config(usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config);
278284

279285
/**
280286
* @brief Get MPS limits

components/hal/usb_dwc_hal.c

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -185,64 +185,53 @@ void usb_dwc_hal_core_soft_reset(usb_dwc_hal_context_t *hal)
185185
}
186186
}
187187

188-
void usb_dwc_hal_set_fifo_bias(usb_dwc_hal_context_t *hal, const usb_hal_fifo_bias_t fifo_bias)
188+
bool usb_dwc_hal_fifo_config_is_valid(const usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config)
189189
{
190-
HAL_ASSERT(hal->channels.hdls);
191-
const uint16_t fifo_size_lines = hal->constant_config.fifo_size;
192-
/*
193-
* Recommended FIFO sizes (see 2.1.2.4 for programming guide)
194-
*
195-
* RXFIFO: ((LPS/4) * 2) + 2
196-
* NPTXFIFO: (LPS/4) * 2
197-
* PTXFIFO: (LPS/4) * 2
198-
*
199-
* Recommended sizes fit 2 packets of each type. For S2 and S3 we can't fit even one MPS ISOC packet (1023 FS and 1024 HS).
200-
* So the calculations below are compromises between the available FIFO size and optimal performance.
201-
*/
202-
203-
// Information for maintainers: this calculation is here for backward compatibility
204-
// It should be removed when we allow HAL users to configure the FIFO sizes IDF-9042
205-
const int otg_dfifo_depth = hal->constant_config.hsphy_type ? 1024 : 256;
206-
207-
usb_dwc_hal_fifo_config_t fifo_config;
208-
switch (fifo_bias) {
209-
// Define minimum viable (fits at least 1 MPS) FIFO sizes for non-biased FIFO types
210-
// Allocate the remaining size to the biased FIFO type
211-
case USB_HAL_FIFO_BIAS_DEFAULT:
212-
fifo_config.nptx_fifo_lines = otg_dfifo_depth / 4;
213-
fifo_config.ptx_fifo_lines = otg_dfifo_depth / 8;
214-
fifo_config.rx_fifo_lines = fifo_size_lines - fifo_config.ptx_fifo_lines - fifo_config.nptx_fifo_lines;
215-
break;
216-
case USB_HAL_FIFO_BIAS_RX:
217-
fifo_config.nptx_fifo_lines = otg_dfifo_depth / 16;
218-
fifo_config.ptx_fifo_lines = otg_dfifo_depth / 8;
219-
fifo_config.rx_fifo_lines = fifo_size_lines - fifo_config.ptx_fifo_lines - fifo_config.nptx_fifo_lines;
220-
break;
221-
case USB_HAL_FIFO_BIAS_PTX:
222-
fifo_config.rx_fifo_lines = otg_dfifo_depth / 8 + 2; // 2 extra lines are allocated for status information. See USB-OTG Programming Guide, chapter 2.1.2.1
223-
fifo_config.nptx_fifo_lines = otg_dfifo_depth / 16;
224-
fifo_config.ptx_fifo_lines = fifo_size_lines - fifo_config.nptx_fifo_lines - fifo_config.rx_fifo_lines;
225-
break;
226-
default:
227-
abort();
190+
if (!hal || !config) {
191+
return false;
228192
}
193+
uint32_t used_lines = config->rx_fifo_lines + config->nptx_fifo_lines + config->ptx_fifo_lines;
194+
return (used_lines <= hal->constant_config.fifo_size);
195+
}
196+
197+
198+
void usb_dwc_hal_set_fifo_config(usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config)
199+
{
200+
// Check internal HAL state
201+
HAL_ASSERT(hal != NULL);
202+
HAL_ASSERT(hal->channels.hdls != NULL);
229203

230-
HAL_ASSERT((fifo_config.rx_fifo_lines + fifo_config.nptx_fifo_lines + fifo_config.ptx_fifo_lines) <= fifo_size_lines);
231-
//Check that none of the channels are active
204+
// Validate provided config
205+
HAL_ASSERT(config != NULL);
206+
207+
// Check if configuration exceeds available FIFO memory
208+
HAL_ASSERT(usb_dwc_hal_fifo_config_is_valid(hal, config));
209+
210+
// Ensure no active channels (must only be called before USB install completes)
232211
for (int i = 0; i < hal->constant_config.chan_num_total; i++) {
233212
if (hal->channels.hdls[i] != NULL) {
234213
HAL_ASSERT(!hal->channels.hdls[i]->flags.active);
235214
}
236215
}
237-
//Set the new FIFO lengths
238-
usb_dwc_ll_grxfsiz_set_fifo_size(hal->dev, fifo_config.rx_fifo_lines);
239-
usb_dwc_ll_gnptxfsiz_set_fifo_size(hal->dev, fifo_config.rx_fifo_lines, fifo_config.nptx_fifo_lines);
240-
usb_dwc_ll_hptxfsiz_set_ptx_fifo_size(hal->dev, fifo_config.rx_fifo_lines + fifo_config.nptx_fifo_lines, fifo_config.ptx_fifo_lines);
241-
//Flush the FIFOs
216+
217+
// Program FIFO size registers
218+
usb_dwc_ll_grxfsiz_set_fifo_size(hal->dev, config->rx_fifo_lines);// Set RX FIFO size (GRXFSIZ)
219+
// Set Non-Periodic TX FIFO (GNPTXFSIZ)
220+
// Offset = RX FIFO lines
221+
usb_dwc_ll_gnptxfsiz_set_fifo_size(hal->dev, config->rx_fifo_lines, config->nptx_fifo_lines);
222+
// Set Periodic TX FIFO (HPTXFSIZ)
223+
// Offset = RX + NPTX
224+
usb_dwc_ll_hptxfsiz_set_ptx_fifo_size(hal->dev,
225+
config->rx_fifo_lines + config->nptx_fifo_lines,
226+
config->ptx_fifo_lines);
227+
228+
// Flush all FIFOs
242229
usb_dwc_ll_grstctl_flush_nptx_fifo(hal->dev);
243230
usb_dwc_ll_grstctl_flush_ptx_fifo(hal->dev);
244231
usb_dwc_ll_grstctl_flush_rx_fifo(hal->dev);
245-
hal->fifo_config = fifo_config; // Implicit struct copy
232+
233+
// Save configuration to HAL context
234+
hal->fifo_config = *config;
246235
hal->flags.fifo_sizes_set = 1;
247236
}
248237

components/usb/hcd_dwc.c

Lines changed: 115 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,8 @@ struct port_obj {
245245
uint32_t val;
246246
} flags;
247247
bool initialized;
248-
// FIFO biasing related
249-
usb_hal_fifo_bias_t fifo_bias; // Bias is saved so it can be reconfigured upon reset
248+
// FIFO related
249+
usb_dwc_hal_fifo_config_t fifo_config; // FIFO config to be applied at HAL level
250250
// Port callback and context
251251
hcd_port_callback_t callback;
252252
void *callback_arg;
@@ -549,6 +549,23 @@ static bool _port_check_all_pipes_halted(port_t *port);
549549
*/
550550
static bool _port_debounce(port_t *port);
551551

552+
/**
553+
* @brief Convert user-provided FIFO configuration to HAL format
554+
*
555+
* This function validates and converts a user-defined FIFO configuration
556+
* (provided via `hcd_config_t.fifo_config`) into the format expected by the HAL.
557+
* It ensures that both RX FIFO and Non-Periodic TX FIFO sizes are non-zero.
558+
*
559+
* @param[in] src Pointer to user-defined FIFO settings (HCD format)
560+
* @param[out] dst Pointer to HAL-compatible FIFO configuration structure
561+
*
562+
* @return
563+
* - ESP_OK: Conversion successful and values copied
564+
* - ESP_ERR_INVALID_SIZE: Either RX FIFO or Non-Periodic TX FIFO is zero
565+
*/
566+
567+
static esp_err_t convert_fifo_config_to_hal_config(const hcd_fifo_settings_t *src, usb_dwc_hal_fifo_config_t *dst);
568+
552569
/**
553570
* @brief Power ON the port
554571
*
@@ -739,16 +756,6 @@ static usb_speed_t get_usb_port_speed(usb_dwc_speed_t priv)
739756
}
740757
}
741758

742-
static usb_hal_fifo_bias_t get_hal_fifo_bias(hcd_port_fifo_bias_t public)
743-
{
744-
switch (public) {
745-
case HCD_PORT_FIFO_BIAS_BALANCED: return USB_HAL_FIFO_BIAS_DEFAULT;
746-
case HCD_PORT_FIFO_BIAS_RX: return USB_HAL_FIFO_BIAS_RX;
747-
case HCD_PORT_FIFO_BIAS_PTX: return USB_HAL_FIFO_BIAS_PTX;
748-
default: abort();
749-
}
750-
}
751-
752759
// ----------------- Interrupt Handlers --------------------
753760

754761
/**
@@ -949,6 +956,46 @@ static void intr_hdlr_main(void *arg)
949956
}
950957
}
951958

959+
// ----------------------- FIFO Config -------------------------
960+
961+
/**
962+
* @brief Calculate default FIFO configuration based on bias preference
963+
*
964+
* This function calculates the FIFO configuration (RX, non-periodic TX, and periodic TX)
965+
* according to the selected bias mode from Kconfig:
966+
*
967+
* - CONFIG_USB_HOST_HW_BUFFER_BIAS_IN: Prioritize RX FIFO space, useful for IN-heavy traffic.
968+
* - CONFIG_USB_HOST_HW_BUFFER_BIAS_PERIODIC_OUT: Prioritize periodic TX FIFO, suitable for periodic OUT transfers.
969+
* - USB_HOST_HW_BUFFER_BIAS_BALANCED: Balanced configuration between RX and both TX FIFOs.
970+
*
971+
* @param[inout] port Pointer to the port object whose fifo_config will be filled
972+
* @param[in] hal Pointer to an initialized HAL context providing hardware FIFO limits
973+
*/
974+
static void _calculate_fifo_from_bias(port_t *port, const usb_dwc_hal_context_t *hal)
975+
{
976+
const int otg_dfifo_depth = hal->constant_config.hsphy_type ? 1024 : 256;
977+
const uint16_t fifo_size_lines = hal->constant_config.fifo_size;
978+
979+
#if CONFIG_USB_HOST_HW_BUFFER_BIAS_IN
980+
// Prioritize RX FIFO (best for IN-heavy workloads)
981+
port->fifo_config.nptx_fifo_lines = otg_dfifo_depth / 16;
982+
port->fifo_config.ptx_fifo_lines = otg_dfifo_depth / 8;
983+
port->fifo_config.rx_fifo_lines = fifo_size_lines - port->fifo_config.ptx_fifo_lines - port->fifo_config.nptx_fifo_lines;
984+
985+
#elif CONFIG_USB_HOST_HW_BUFFER_BIAS_PERIODIC_OUT
986+
// Prioritize periodic TX FIFO (useful for high throughput periodic endpoints)
987+
port->fifo_config.rx_fifo_lines = otg_dfifo_depth / 8 + 2; // 2 extra lines are allocated for status information. See USB-OTG Programming Guide, chapter 2.1.2.1
988+
port->fifo_config.nptx_fifo_lines = otg_dfifo_depth / 16;
989+
port->fifo_config.ptx_fifo_lines = fifo_size_lines - port->fifo_config.nptx_fifo_lines - port->fifo_config.rx_fifo_lines;
990+
991+
#else // USB_HOST_HW_BUFFER_BIAS_BALANCED
992+
// Balanced configuration (default)
993+
port->fifo_config.nptx_fifo_lines = otg_dfifo_depth / 4;
994+
port->fifo_config.ptx_fifo_lines = otg_dfifo_depth / 8;
995+
port->fifo_config.rx_fifo_lines = fifo_size_lines - port->fifo_config.ptx_fifo_lines - port->fifo_config.nptx_fifo_lines;
996+
#endif
997+
}
998+
952999
// --------------------------------------------- Host Controller Driver ------------------------------------------------
9531000

9541001
static port_t *port_obj_alloc(void)
@@ -1013,6 +1060,15 @@ esp_err_t hcd_install(const hcd_config_t *config)
10131060
ESP_LOGE(HCD_DWC_TAG, "Interrupt alloc error: %s", esp_err_to_name(err_ret));
10141061
goto intr_alloc_err;
10151062
}
1063+
// Apply custom FIFO config if provided, otherwise mark as default (all zeros)
1064+
memset(&p_hcd_obj_dmy->port_obj->fifo_config, 0, sizeof(p_hcd_obj_dmy->port_obj->fifo_config));
1065+
if (config->fifo_config != NULL) {
1066+
// Convert and validate user-provided config
1067+
err_ret = convert_fifo_config_to_hal_config(config->fifo_config, &p_hcd_obj_dmy->port_obj->fifo_config);
1068+
if (err_ret != ESP_OK) {
1069+
goto assign_err;
1070+
}
1071+
}
10161072
HCD_ENTER_CRITICAL();
10171073
if (s_hcd_obj != NULL) {
10181074
HCD_EXIT_CRITICAL();
@@ -1095,6 +1151,34 @@ static bool _port_debounce(port_t *port)
10951151
return is_connected;
10961152
}
10971153

1154+
static esp_err_t convert_fifo_config_to_hal_config(const hcd_fifo_settings_t *src, usb_dwc_hal_fifo_config_t *dst)
1155+
{
1156+
// Check at least RX and NPTX are non-zero
1157+
if (src->rx_fifo_lines == 0 || src->nptx_fifo_lines == 0) {
1158+
ESP_LOGE(HCD_DWC_TAG, "RX and Non-Periodic TX FIFO must be > 0");
1159+
return ESP_ERR_INVALID_SIZE;
1160+
}
1161+
// Assign valid values
1162+
dst->rx_fifo_lines = src->rx_fifo_lines;
1163+
dst->nptx_fifo_lines = src->nptx_fifo_lines;
1164+
dst->ptx_fifo_lines = src->ptx_fifo_lines; // OK even if zero
1165+
1166+
return ESP_OK;
1167+
}
1168+
1169+
/**
1170+
* @brief Check if the FIFO config is marked to use bias-based default values.
1171+
*
1172+
* If all FIFO line sizes are zero, the configuration is considered uninitialized,
1173+
* and default values will be calculated based on bias settings.
1174+
*/
1175+
static inline bool _is_fifo_config_by_bias(const usb_dwc_hal_fifo_config_t *cfg)
1176+
{
1177+
return (cfg->rx_fifo_lines == 0 &&
1178+
cfg->nptx_fifo_lines == 0 &&
1179+
cfg->ptx_fifo_lines == 0);
1180+
}
1181+
10981182
// ---------------------- Commands -------------------------
10991183

11001184
static esp_err_t _port_cmd_power_on(port_t *port)
@@ -1172,8 +1256,16 @@ static esp_err_t _port_cmd_reset(port_t *port)
11721256
goto bailout;
11731257
}
11741258

1175-
// Reinitialize port registers.
1176-
usb_dwc_hal_set_fifo_bias(port->hal, port->fifo_bias); // Set FIFO biases
1259+
// Reinitialize port registers
1260+
if (!usb_dwc_hal_fifo_config_is_valid(port->hal, &port->fifo_config)) {
1261+
HCD_EXIT_CRITICAL();
1262+
ret = ESP_ERR_INVALID_SIZE;
1263+
ESP_LOGE(HCD_DWC_TAG, "Invalid FIFO config");
1264+
HCD_ENTER_CRITICAL();
1265+
goto bailout;
1266+
}
1267+
usb_dwc_hal_set_fifo_config(port->hal, &port->fifo_config);// Apply FIFO settings
1268+
11771269
usb_dwc_hal_port_set_frame_list(port->hal, port->frame_list, FRAME_LIST_LEN); // Set periodic frame list
11781270
usb_dwc_hal_port_periodic_enable(port->hal); // Enable periodic scheduling
11791271

@@ -1282,7 +1374,6 @@ esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, h
12821374
TAILQ_INIT(&port_obj->pipes_active_tailq);
12831375
port_obj->state = HCD_PORT_STATE_NOT_POWERED;
12841376
port_obj->last_event = HCD_PORT_EVENT_NONE;
1285-
port_obj->fifo_bias = get_hal_fifo_bias(port_config->fifo_bias);
12861377
port_obj->callback = port_config->callback;
12871378
port_obj->callback_arg = port_config->callback_arg;
12881379
port_obj->context = port_config->context;
@@ -1292,10 +1383,18 @@ esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, h
12921383
port_obj->initialized = true;
12931384
// Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset
12941385
memset(port_obj->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t));
1386+
// If FIFO config is zeroed -> calculate from bias
1387+
if (_is_fifo_config_by_bias(&port_obj->fifo_config)) {
1388+
// Calculate default FIFO sizes based on Kconfig bias settings
1389+
_calculate_fifo_from_bias(port_obj, port_obj->hal);
1390+
}
12951391
esp_intr_enable(s_hcd_obj->isr_hdl);
12961392
*port_hdl = (hcd_port_handle_t)port_obj;
12971393
HCD_EXIT_CRITICAL();
1298-
1394+
ESP_LOGD(HCD_DWC_TAG, "FIFO config lines: RX=%u, PTX=%u, NPTX=%u",
1395+
port_obj->fifo_config.rx_fifo_lines,
1396+
port_obj->fifo_config.ptx_fifo_lines,
1397+
port_obj->fifo_config.nptx_fifo_lines);
12991398
vTaskDelay(pdMS_TO_TICKS(INIT_DELAY_MS)); // Need a short delay before host mode takes effect
13001399
return ESP_OK;
13011400
}
@@ -1450,28 +1549,6 @@ void *hcd_port_get_context(hcd_port_handle_t port_hdl)
14501549
return ret;
14511550
}
14521551

1453-
esp_err_t hcd_port_set_fifo_bias(hcd_port_handle_t port_hdl, hcd_port_fifo_bias_t bias)
1454-
{
1455-
esp_err_t ret;
1456-
usb_hal_fifo_bias_t hal_bias = get_hal_fifo_bias(bias);
1457-
1458-
// Configure the new FIFO sizes and store the pointers
1459-
port_t *port = (port_t *)port_hdl;
1460-
xSemaphoreTake(port->port_mux, portMAX_DELAY);
1461-
HCD_ENTER_CRITICAL();
1462-
// Check that port is in the correct state to update FIFO sizes
1463-
if (port->initialized && !port->flags.event_pending && port->num_pipes_idle == 0 && port->num_pipes_queued == 0) {
1464-
usb_dwc_hal_set_fifo_bias(port->hal, hal_bias);
1465-
port->fifo_bias = hal_bias;
1466-
ret = ESP_OK;
1467-
} else {
1468-
ret = ESP_ERR_INVALID_STATE;
1469-
}
1470-
HCD_EXIT_CRITICAL();
1471-
xSemaphoreGive(port->port_mux);
1472-
return ret;
1473-
}
1474-
14751552
// --------------------------------------------------- HCD Pipes -------------------------------------------------------
14761553

14771554
// ----------------------- Private -------------------------

0 commit comments

Comments
 (0)