Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 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
44 changes: 44 additions & 0 deletions host/usb/include/usb/usb_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,50 @@ esp_err_t usb_host_get_config_desc(usb_host_client_handle_t client_hdl, usb_devi
*/
esp_err_t usb_host_free_config_desc(const usb_config_desc_t *config_desc);

/**
* @brief Set remote wakeup feature on a device
*
* This function enables, or disables remote wakeup feature on a connected device. Device must support remote wakeup
* at fist place
*
* @note A control transfer is sent to a device, to enable/disable the feature
* @note A client must open the device first
*
* @param[in] client_hdl Handle of a client, that opened the device
* @param[in] dev_hdl Handle of a device, on which the remote wakeup is about to be enabled/disabled
* @param[in] enable Remote wakeup enable/disable
*
* @return
* - ESP_OK: Remote wakeup set successfully
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_INVALID_STATE: Client did not open the device
* - ESP_ERR_NOT_ALLOWED: Device does not support remote wakeup
* - ESP_ERR_NO_MEM: Not enough memory
*/
esp_err_t usb_host_device_remote_wakeup_enable(usb_host_client_handle_t client_hdl, usb_device_handle_t dev_hdl, bool enable);

/**
* @brief Check if a remote wakeup is currently set
*
* This function checks if a remote wakeup feature is currently enabled or disabled on a connected device. Device must
* support remote wakeup at fist place
*
* @note A control transfer is sent to a device, get a device status descriptor
* @note A client must open the device first
*
* @param[in] client_hdl Handle of a client, that opened the device
* @param[in] dev_hdl Handle of a device, on which the remote wakeup is about to be checked
* @param[out] enabled Remote wakeup is currently enabled/disabled
*
* @return
* - ESP_OK: Remote wakeup status checked successfully
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_INVALID_STATE: Client did not open the device
* - ESP_ERR_NOT_ALLOWED: Device does not support remote wakeup
* - ESP_ERR_NO_MEM: Not enough memory
*/
esp_err_t usb_host_device_remote_wakeup_check(usb_host_client_handle_t client_hdl, usb_device_handle_t dev_hdl, bool *enabled);

// ----------------------------------------------- Interface Functions -------------------------------------------------

/**
Expand Down
41 changes: 40 additions & 1 deletion host/usb/include/usb/usb_types_ch9.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,19 @@ ESP_STATIC_ASSERT(sizeof(usb_device_status_t) == sizeof(uint16_t), "Size of usb_
#define USB_W_VALUE_DT_OTHER_SPEED_CONFIG 0x07
#define USB_W_VALUE_DT_INTERFACE_POWER 0x08

/**
* @brief Feature selector bit masks belonging to the wValue field of a setup packet
*
* See Table 9-6 of USB2.0 specification for more details
*/
#define ENDPOINT_HALT 0x00
#define DEVICE_REMOTE_WAKEUP 0x01
#define TEST_MODE 0x02

/**
* @brief Initializer for a GET_STATUS request
*
* Sets the address of a connected device
* Gets the status of a connected device
*/
#define USB_SETUP_PACKET_INIT_GET_STATUS(setup_pkt_ptr) ({ \
(setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \
Expand All @@ -166,6 +175,36 @@ ESP_STATIC_ASSERT(sizeof(usb_device_status_t) == sizeof(uint16_t), "Size of usb_
(setup_pkt_ptr)->wLength = 2; \
})

/**
* @brief Initializer for a CLEAR_FEATURE request
*
* Clears the feature of a connected device
*
* See Chapter 9.4.1 of USB2.0 specification for more details
*/
#define USB_SETUP_PACKET_INIT_CLEAR_FEATURE(setup_pkt_ptr, feature_to_clear) ({ \
(setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \
(setup_pkt_ptr)->bRequest = USB_B_REQUEST_CLEAR_FEATURE; \
(setup_pkt_ptr)->wValue = feature_to_clear; \
(setup_pkt_ptr)->wIndex = 0; \
(setup_pkt_ptr)->wLength = 0; \
})

/**
* @brief Initializer for a SET_FEATURE request
*
* Sets the feature of a connected device
*
* See Chapter 9.4.9 of USB2.0 specification for more details
*/
#define USB_SETUP_PACKET_INIT_SET_FEATURE(setup_pkt_ptr, feature_to_set) ({ \
(setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \
(setup_pkt_ptr)->bRequest = USB_B_REQUEST_SET_FEATURE; \
(setup_pkt_ptr)->wValue = feature_to_set; \
(setup_pkt_ptr)->wIndex = 0; \
(setup_pkt_ptr)->wLength = 0; \
})

/**
* @brief Initializer for a SET_ADDRESS request
*
Expand Down
1 change: 1 addition & 0 deletions host/usb/private_include/hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ typedef enum {
HCD_PORT_EVENT_DISCONNECTION, /**< A device disconnection has been detected */
HCD_PORT_EVENT_ERROR, /**< A port error has been detected. Port is now HCD_PORT_STATE_RECOVERY */
HCD_PORT_EVENT_OVERCURRENT, /**< Overcurrent detected on the port. Port is now HCD_PORT_STATE_RECOVERY */
HCD_PORT_EVENT_REMOTE_WAKEUP, /**< A remote-wakeup event from device has been detected */
} hcd_port_event_t;

/**
Expand Down
84 changes: 83 additions & 1 deletion host/usb/src/hcd_dwc.c
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,12 @@
port->flags.conn_dev_ena = 0;
break;
}
case USB_DWC_HAL_PORT_EVENT_REMOTE_WAKEUP: {

Check failure

Code scanning / clang-tidy

duplicate case value: 'USB_DWC_HAL_PORT_EVENT_DISABLED' and 'HCD_PORT_EVENT_REMOTE_WAKEUP' both equal '5' [clang-diagnostic-error] Error

duplicate case value: 'USB_DWC_HAL_PORT_EVENT_DISABLED' and 'HCD_PORT_EVENT_REMOTE_WAKEUP' both equal '5' [clang-diagnostic-error]

Check failure

Code scanning / clang-tidy

use of undeclared identifier 'USB_DWC_HAL_PORT_EVENT_REMOTE_WAKEUP'; did you mean 'HCD_PORT_EVENT_REMOTE_WAKEUP'? [clang-diagnostic-error] Error

use of undeclared identifier 'USB_DWC_HAL_PORT_EVENT_REMOTE_WAKEUP'; did you mean 'HCD_PORT_EVENT_REMOTE_WAKEUP'? [clang-diagnostic-error]
esp_rom_printf("WAKE\n");
//port->state = HCD_PORT_STATE_ENABLED;
port_event = HCD_PORT_EVENT_REMOTE_WAKEUP;
break;
}
default: {
abort();
break;
Expand Down Expand Up @@ -1217,6 +1223,34 @@
cfg->ptx_fifo_lines == 0);
}

/**
* @brief Suspend PHY clock
*
* @param[in] port Pointer to the port object
* @param[in] suspend PHY is about to be suspended/resumed
* @return True PHY clock successfully suspended/resumed
* @return False PHY clock not suspended/resumed
*/
static inline bool _suspend_phy_clk(port_t *port, bool suspend)
{
// Stop PHY Clock and gate HCLK
usb_dwc_hal_pwr_clk_toggle_phy_suspend(port->hal, suspend);

Check failure

Code scanning / clang-tidy

use of undeclared identifier 'usb_dwc_hal_pwr_clk_toggle_phy_suspend' [clang-diagnostic-error] Error

use of undeclared identifier 'usb_dwc_hal_pwr_clk_toggle_phy_suspend' [clang-diagnostic-error]

// Wait 10 PHY clock cycles, PHY Clock is 30MHz when using 16bit interface, 60MHz when 8bit interface
// which makes 33.3 nS. Busy wait for 1uS just to be sure
esp_rom_delay_us(1);

const bool phy_clk_stopped = usb_dwc_hal_pwr_clk_check_phy_clk_stopped(port->hal);

Check failure

Code scanning / clang-tidy

use of undeclared identifier 'usb_dwc_hal_pwr_clk_check_phy_clk_stopped' [clang-diagnostic-error] Error

use of undeclared identifier 'usb_dwc_hal_pwr_clk_check_phy_clk_stopped' [clang-diagnostic-error]
const bool hclk_gated = usb_dwc_hal_pwr_clk_check_hclk_gated(port->hal);

Check failure

Code scanning / clang-tidy

use of undeclared identifier 'usb_dwc_hal_pwr_clk_check_hclk_gated' [clang-diagnostic-error] Error

use of undeclared identifier 'usb_dwc_hal_pwr_clk_check_hclk_gated' [clang-diagnostic-error]

// supend == phy_clk_stopped == hclk_gated
// When suspending, all 3 variables must be 1. When resuming, all 3 must be 0.
if ((suspend == phy_clk_stopped) && (suspend == hclk_gated)) {
return true;
}
return false;
}

// ---------------------- Commands -------------------------

static esp_err_t _port_cmd_power_on(port_t *port)
Expand All @@ -1227,6 +1261,7 @@
port->state = HCD_PORT_STATE_DISCONNECTED;
usb_dwc_hal_port_init(port->hal);
usb_dwc_hal_port_toggle_power(port->hal, true);
_suspend_phy_clk(port, false);
ret = ESP_OK;
} else {
ret = ESP_ERR_INVALID_STATE;
Expand All @@ -1240,6 +1275,7 @@
// Port can only be unpowered if already powered
if (port->state != HCD_PORT_STATE_NOT_POWERED) {
port->state = HCD_PORT_STATE_NOT_POWERED;
_suspend_phy_clk(port, false);
usb_dwc_hal_port_deinit(port->hal);
usb_dwc_hal_port_toggle_power(port->hal, false);
// If a device is currently connected, this should trigger a disconnect event
Expand All @@ -1255,7 +1291,8 @@
esp_err_t ret;

// Port can only a reset when it is in the enabled or disabled (in the case of a new connection)states.
if (port->state != HCD_PORT_STATE_ENABLED && port->state != HCD_PORT_STATE_DISABLED) {
// Or suspended, to exit suspended state through host initiated reset
if (port->state != HCD_PORT_STATE_ENABLED && port->state != HCD_PORT_STATE_DISABLED && port->state != HCD_PORT_STATE_SUSPENDED) {
ret = ESP_ERR_INVALID_STATE;
goto exit;
}
Expand All @@ -1272,6 +1309,8 @@
*/
port->state = HCD_PORT_STATE_RESETTING;

_suspend_phy_clk(port, false);

// Place the bus into the reset state. If the port was previously enabled, a disabled event will occur after this
usb_dwc_hal_port_toggle_reset(port->hal, true);
HCD_EXIT_CRITICAL();
Expand Down Expand Up @@ -1320,6 +1359,21 @@
return ret;
}

/**
* @brief Suspend the root port
*
* This sequence equals to a sequence from the DesignWare Cores USB 2.0 Programming Guide version 4.00a
* 14.2.3.3 External HCLK Gating When the Host Core is in Partial Power Down Mode
* Sequence Entering suspend state
*
* @param[in] port Pointer to the port object
* @return:
* - ESP_ERR_INVALID_STATE Port is not in a correct state to be suspended, or pipe(s) routed through this port is not halted
* - ESP_ERR_INVALID_RESPONSE Port state unexpectedly changed (for example: device was disconnected)
* - ESP_ERR_NOT_FINISHED Port did not finish the suspending sequence and is not in the suspended state
* - ESP_ERR_NOT_ALLOWED PHY clk was not suspended
* - ESP_OK Root port suspended
*/
static esp_err_t _port_cmd_bus_suspend(port_t *port)
{
esp_err_t ret;
Expand Down Expand Up @@ -1353,13 +1407,34 @@
goto exit;
}

if (! _suspend_phy_clk(port, true)) {
ret = ESP_ERR_NOT_ALLOWED;
goto exit;
}

port->state = HCD_PORT_STATE_SUSPENDED;
ret = ESP_OK;

exit:
return ret;
}

/**
* @brief Resume the root port
*
* This sequence equals to a sequence from the DesignWare Cores USB 2.0 Programming Guide version 4.00a
* 14.2.3.3 External HCLK Gating When the Host Core is in Partial Power Down Mode
* Sequence Exiting Suspend State Through Host Initiated Resume
* Exiting Suspend State Through Device Initiated Remote Wakeup
*
* @note this sequence is used for both resume scenarios: the host initiated and the device initiated (remote wakeup) resume
* @param[in] port Pointer to the port object
* @return:
* - ESP_ERR_INVALID_STATE Port is not in a correct state to be resumed
* - ESP_ERR_NOT_ALLOWED PHY clk was not resumed
* - ESP_ERR_INVALID_RESPONSE Port state unexpectedly changed (for example: device was disconnected)
* - ESP_OK Root port resumed
*/
static esp_err_t _port_cmd_bus_resume(port_t *port)
{
esp_err_t ret;
Expand All @@ -1368,6 +1443,12 @@
ret = ESP_ERR_INVALID_STATE;
goto exit;
}

if (!_suspend_phy_clk(port, false)) {
ret = ESP_ERR_NOT_ALLOWED;
goto exit;
}

// Put and hold the bus in the K state.
usb_dwc_hal_port_toggle_resume(port->hal, true);
port->state = HCD_PORT_STATE_RESUMING;
Expand Down Expand Up @@ -1485,6 +1566,7 @@
return ESP_OK;
}


esp_err_t hcd_port_command(hcd_port_handle_t port_hdl, hcd_port_cmd_t command)
{
esp_err_t ret = ESP_ERR_INVALID_STATE;
Expand Down
11 changes: 11 additions & 0 deletions host/usb/src/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,17 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl)

break;
}
case HCD_PORT_EVENT_REMOTE_WAKEUP:
// Root port, including all the connected devices were resumed (global resume)
// Clear all EPs and propagate the resumed event to clients
//usbh_devs_set_pm_actions_all(USBH_DEV_RESUME | USBH_DEV_RESUME_EVT);

// Change Port state
//HUB_DRIVER_ENTER_CRITICAL();
//p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_ENABLED;
//HUB_DRIVER_EXIT_CRITICAL();
hub_root_mark_resume();
break;
default:
abort(); // Should never occur
break;
Expand Down
Loading
Loading