Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ports/espressif/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,10 @@ SRC_C += \
lib/tinyusb/src/portable/synopsys/dwc2/dwc2_common.c
endif

ifeq ($(CIRCUITPY_USB_HOST),1)
SRC_C += lib/tinyusb/src/portable/synopsys/dwc2/hcd_dwc2.c
endif

ifneq ($(CIRCUITPY_AUDIOBUSIO),0)
CHIP_COMPONENTS += esp_driver_i2s
endif
Expand Down
10 changes: 10 additions & 0 deletions ports/espressif/background.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,21 @@
#include "supervisor/filesystem.h"
#include "supervisor/port.h"
#include "supervisor/shared/stack.h"
#if CIRCUITPY_USB_HOST
#include "supervisor/usb.h"
#include "tusb.h"
#endif

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void port_background_tick(void) {
#if CIRCUITPY_USB_HOST
// Enumeration delays need polling even when no new USB IRQ arrives.
if (tuh_task_event_ready()) {
usb_background_schedule();
}
#endif
// Yield with zero delay in case FreeRTOS wants to switch to something else.
port_task_yield();
}
Expand Down
2 changes: 1 addition & 1 deletion ports/espressif/common-hal/microcontroller/Pin.c
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ void common_hal_mcu_pin_claim(const mcu_pin_obj_t *pin) {
}

bool pin_number_is_free(gpio_num_t pin_number) {
return !(_in_use_pin_mask & PIN_BIT(pin_number));
return GPIO_IS_VALID_GPIO(pin_number) && !(_in_use_pin_mask & PIN_BIT(pin_number));
}

bool common_hal_mcu_pin_is_free(const mcu_pin_obj_t *pin) {
Expand Down
4 changes: 4 additions & 0 deletions ports/espressif/common-hal/microcontroller/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,5 +347,9 @@ static const mp_rom_map_elem_t mcu_pin_global_dict_table[] = {
#ifdef GPIO54_EXISTS
{ MP_ROM_QSTR(MP_QSTR_GPIO54), MP_ROM_PTR(&pin_GPIO54) },
#endif
#if defined(CONFIG_IDF_TARGET_ESP32P4)
{ MP_ROM_QSTR(MP_QSTR_USB_HS_DP), MP_ROM_PTR(&pin_USB_HS_DP) },
{ MP_ROM_QSTR(MP_QSTR_USB_HS_DM), MP_ROM_PTR(&pin_USB_HS_DM) },
#endif
};
MP_DEFINE_CONST_DICT(mcu_pin_globals, mcu_pin_global_dict_table);
59 changes: 59 additions & 0 deletions ports/espressif/common-hal/usb_host/Port.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2026 Limor Fried for Adafruit Industries
//
// SPDX-License-Identifier: MIT

#include "bindings/espidf/__init__.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/usb_host/Port.h"
#include "supervisor/usb.h"
#include "supervisor/shared/tick.h"
#include "esp_private/usb_phy.h"
#include "tusb.h"

#if !defined(CONFIG_IDF_TARGET_ESP32P4) || CIRCUITPY_USB_HOST_INSTANCE != 1
#error "USB host requires the ESP32-P4 high-speed controller"
#endif
#if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_DEVICE_INSTANCE == CIRCUITPY_USB_HOST_INSTANCE
#error "USB host and device must use different controllers"
#endif

static usb_host_port_obj_t usb_host_instance;
static usb_phy_handle_t host_phy;

usb_host_port_obj_t *common_hal_usb_host_port_construct(const mcu_pin_obj_t *dp, const mcu_pin_obj_t *dm) {
// The high-speed PHY has dedicated pads, not GPIOs.
if (dp != &pin_USB_HS_DP || dm != &pin_USB_HS_DM) {
raise_ValueError_invalid_pins();
}
usb_host_port_obj_t *self = &usb_host_instance;
if (self->dp != NULL) {
return self;
}

const usb_phy_config_t config = {
.controller = USB_PHY_CTRL_OTG,
.target = USB_PHY_TARGET_UTMI,
.otg_mode = USB_OTG_MODE_HOST,
.otg_speed = USB_PHY_SPEED_HIGH,
};
CHECK_ESP_RESULT(usb_new_phy(&config, &host_phy));
if (!tuh_init(TUH_OPT_RHPORT)) {
usb_del_phy(host_phy);
host_phy = NULL;
CHECK_ESP_RESULT(ESP_FAIL);
}
self->base.type = &usb_host_port_type;
self->dp = dp;
self->dm = dm;
// Keep polling enumeration timers even without an active display.
supervisor_enable_tick();
return self;
}

// TinyUSB installs the controller IRQ. Process its events on the VM task,
// where the Python USB device and keyboard callbacks are safe to run.
void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
usb_background_schedule();
}
15 changes: 15 additions & 0 deletions ports/espressif/common-hal/usb_host/Port.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT

#pragma once

#include "py/obj.h"

typedef struct {
mp_obj_base_t base;
const mcu_pin_obj_t *dp;
const mcu_pin_obj_t *dm;
} usb_host_port_obj_t;
7 changes: 7 additions & 0 deletions ports/espressif/common-hal/usb_host/__init__.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT

// Nothing
4 changes: 4 additions & 0 deletions ports/espressif/peripherals/esp32p4/pins.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,7 @@ const mcu_pin_obj_t pin_GPIO51 = PIN(51, ADC_UNIT_2, ADC_CHANNEL_4, NO_TOUCH_CHA
const mcu_pin_obj_t pin_GPIO52 = PIN(52, ADC_UNIT_2, ADC_CHANNEL_5, NO_TOUCH_CHANNEL);
const mcu_pin_obj_t pin_GPIO53 = PIN(53, ADC_UNIT_2, ADC_CHANNEL_6, NO_TOUCH_CHANNEL);
const mcu_pin_obj_t pin_GPIO54 = PIN(54, ADC_UNIT_2, ADC_CHANNEL_7, NO_TOUCH_CHANNEL);

// The USB high-speed PHY pads cannot be used as GPIOs.
const mcu_pin_obj_t pin_USB_HS_DP = PIN(NO_PIN, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL);
const mcu_pin_obj_t pin_USB_HS_DM = PIN(NO_PIN, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL);
3 changes: 3 additions & 0 deletions ports/espressif/peripherals/esp32p4/pins.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,6 @@ extern const mcu_pin_obj_t pin_GPIO52;
extern const mcu_pin_obj_t pin_GPIO53;
#define GPIO54_EXISTS 1
extern const mcu_pin_obj_t pin_GPIO54;

extern const mcu_pin_obj_t pin_USB_HS_DP;
extern const mcu_pin_obj_t pin_USB_HS_DM;
2 changes: 1 addition & 1 deletion supervisor/shared/usb/tusb_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ extern "C" {
#define CFG_TUH_RPI_PIO_USB 1
#endif

#if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1
#if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 || (defined(CONFIG_IDF_TARGET_ESP32P4) && CIRCUITPY_USB_HOST_INSTANCE == 1)
#define _HOST_SPEED OPT_MODE_HIGH_SPEED
#else
#define _HOST_SPEED 0
Expand Down
5 changes: 5 additions & 0 deletions supervisor/shared/usb/usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ void usb_background(void) {
#elif CFG_TUSB_OS == OPT_OS_FREERTOS
// TinyUSB may run in a separate task, at the same priority as CircuitPython.
port_task_yield();
#if CIRCUITPY_USB_HOST
if (tuh_inited()) {
tuh_task_ext(0, false);
}
#endif
#endif
// No need to flush if there's no REPL.
#if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC
Expand Down
Loading