Skip to content

Commit e7f30a0

Browse files
feat(usb_host): Remote wakeup
- HCD and USB Host tests
1 parent 10c3138 commit e7f30a0

File tree

13 files changed

+966
-23
lines changed

13 files changed

+966
-23
lines changed

host/usb/include/usb/usb_host.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,50 @@ esp_err_t usb_host_get_config_desc(usb_host_client_handle_t client_hdl, usb_devi
548548
*/
549549
esp_err_t usb_host_free_config_desc(const usb_config_desc_t *config_desc);
550550

551+
/**
552+
* @brief Set remote wakeup feature on a device
553+
*
554+
* This function enables, or disables remote wakeup feature on a connected device. Device must support remote wakeup
555+
* at fist place
556+
*
557+
* @note A control transfer is sent to a device, to enable/disable the feature
558+
* @note A client must open the device first
559+
*
560+
* @param[in] client_hdl Handle of a client, that opened the device
561+
* @param[in] dev_hdl Handle of a device, on which the remote wakeup is about to be enabled/disabled
562+
* @param[in] enable Remote wakeup enable/disable
563+
*
564+
* @return
565+
* - ESP_OK: Remote wakeup set successfully
566+
* - ESP_ERR_INVALID_ARG: Invalid argument
567+
* - ESP_ERR_INVALID_STATE: Client did not open the device
568+
* - ESP_ERR_NOT_ALLOWED: Device does not support remote wakeup
569+
* - ESP_ERR_NO_MEM: Not enough memory
570+
*/
571+
esp_err_t usb_host_device_remote_wakeup_enable(usb_host_client_handle_t client_hdl, usb_device_handle_t dev_hdl, bool enable);
572+
573+
/**
574+
* @brief Check if a remote wakeup is currently set
575+
*
576+
* This function checks if a remote wakeup feature is currently enabled or disabled on a connected device. Device must
577+
* support remote wakeup at fist place
578+
*
579+
* @note A control transfer is sent to a device, get a device status descriptor
580+
* @note A client must open the device first
581+
*
582+
* @param[in] client_hdl Handle of a client, that opened the device
583+
* @param[in] dev_hdl Handle of a device, on which the remote wakeup is about to be checked
584+
* @param[out] enabled Remote wakeup is currently enabled/disabled
585+
*
586+
* @return
587+
* - ESP_OK: Remote wakeup status checked successfully
588+
* - ESP_ERR_INVALID_ARG: Invalid argument
589+
* - ESP_ERR_INVALID_STATE: Client did not open the device
590+
* - ESP_ERR_NOT_ALLOWED: Device does not support remote wakeup
591+
* - ESP_ERR_NO_MEM: Not enough memory
592+
*/
593+
esp_err_t usb_host_device_remote_wakeup_check(usb_host_client_handle_t client_hdl, usb_device_handle_t dev_hdl, bool *enabled);
594+
551595
// ----------------------------------------------- Interface Functions -------------------------------------------------
552596

553597
/**

host/usb/include/usb/usb_types_ch9.h

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,19 @@ ESP_STATIC_ASSERT(sizeof(usb_device_status_t) == sizeof(uint16_t), "Size of usb_
153153
#define USB_W_VALUE_DT_OTHER_SPEED_CONFIG 0x07
154154
#define USB_W_VALUE_DT_INTERFACE_POWER 0x08
155155

156+
/**
157+
* @brief Feature selector bit masks belonging to the wValue field of a setup packet
158+
*
159+
* See Table 9-6 of USB2.0 specification for more details
160+
*/
161+
#define ENDPOINT_HALT 0x00
162+
#define DEVICE_REMOTE_WAKEUP 0x01
163+
#define TEST_MODE 0x02
164+
156165
/**
157166
* @brief Initializer for a GET_STATUS request
158167
*
159-
* Sets the address of a connected device
168+
* Gets the status of a connected device
160169
*/
161170
#define USB_SETUP_PACKET_INIT_GET_STATUS(setup_pkt_ptr) ({ \
162171
(setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \
@@ -166,6 +175,36 @@ ESP_STATIC_ASSERT(sizeof(usb_device_status_t) == sizeof(uint16_t), "Size of usb_
166175
(setup_pkt_ptr)->wLength = 2; \
167176
})
168177

178+
/**
179+
* @brief Initializer for a CLEAR_FEATURE request
180+
*
181+
* Clears the feature of a connected device
182+
*
183+
* See Chapter 9.4.1 of USB2.0 specification for more details
184+
*/
185+
#define USB_SETUP_PACKET_INIT_CLEAR_FEATURE(setup_pkt_ptr, feature_to_clear) ({ \
186+
(setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \
187+
(setup_pkt_ptr)->bRequest = USB_B_REQUEST_CLEAR_FEATURE; \
188+
(setup_pkt_ptr)->wValue = feature_to_clear; \
189+
(setup_pkt_ptr)->wIndex = 0; \
190+
(setup_pkt_ptr)->wLength = 0; \
191+
})
192+
193+
/**
194+
* @brief Initializer for a SET_FEATURE request
195+
*
196+
* Sets the feature of a connected device
197+
*
198+
* See Chapter 9.4.9 of USB2.0 specification for more details
199+
*/
200+
#define USB_SETUP_PACKET_INIT_SET_FEATURE(setup_pkt_ptr, feature_to_set) ({ \
201+
(setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \
202+
(setup_pkt_ptr)->bRequest = USB_B_REQUEST_SET_FEATURE; \
203+
(setup_pkt_ptr)->wValue = feature_to_set; \
204+
(setup_pkt_ptr)->wIndex = 0; \
205+
(setup_pkt_ptr)->wLength = 0; \
206+
})
207+
169208
/**
170209
* @brief Initializer for a SET_ADDRESS request
171210
*

host/usb/private_include/hcd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ typedef enum {
6969
HCD_PORT_EVENT_DISCONNECTION, /**< A device disconnection has been detected */
7070
HCD_PORT_EVENT_ERROR, /**< A port error has been detected. Port is now HCD_PORT_STATE_RECOVERY */
7171
HCD_PORT_EVENT_OVERCURRENT, /**< Overcurrent detected on the port. Port is now HCD_PORT_STATE_RECOVERY */
72+
HCD_PORT_EVENT_REMOTE_WAKEUP, /**< A remote-wakeup event from device has been detected */
7273
} hcd_port_event_t;
7374

7475
/**

host/usb/src/hcd_dwc.c

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,12 @@ static hcd_port_event_t _intr_hdlr_hprt(port_t *port, usb_dwc_hal_port_event_t h
831831
port->flags.conn_dev_ena = 0;
832832
break;
833833
}
834+
case USB_DWC_HAL_PORT_EVENT_REMOTE_WAKEUP: {
835+
esp_rom_printf("WAKE\n");
836+
//port->state = HCD_PORT_STATE_ENABLED;
837+
port_event = HCD_PORT_EVENT_REMOTE_WAKEUP;
838+
break;
839+
}
834840
default: {
835841
abort();
836842
break;
@@ -1217,6 +1223,34 @@ static inline bool _is_fifo_config_by_bias(const usb_dwc_hal_fifo_config_t *cfg)
12171223
cfg->ptx_fifo_lines == 0);
12181224
}
12191225

1226+
/**
1227+
* @brief Gate internal clock
1228+
*
1229+
* @param[in] port Pointer to the port object
1230+
* @param[in] enable enable/disable internal clock gating
1231+
* @return True internal clk successfully gated/un-gated
1232+
* @return False internal clk not gated/un-gated
1233+
*/
1234+
static inline bool _internal_clk_gate(port_t *port, bool enable)
1235+
{
1236+
// Stop PHY Clock and gate HCLK
1237+
usb_dwc_hal_pwr_clk_internal_clock_gate(port->hal, enable);
1238+
1239+
// Wait 10 PHY clock cycles, PHY Clock is 30MHz when using 16bit interface, 60MHz when 8bit interface
1240+
// which makes 33.3 nS. Busy wait for 1uS just to be sure
1241+
esp_rom_delay_us(1);
1242+
1243+
const bool phy_clk_stopped = usb_dwc_hal_pwr_clk_check_phy_clk_stopped(port->hal);
1244+
const bool hclk_gated = usb_dwc_hal_pwr_clk_check_hclk_gated(port->hal);
1245+
1246+
// enable == phy_clk_stopped == hclk_gated
1247+
// When gating the clock, all 3 variables must be 1. When un-gating, all 3 must be 0.
1248+
if ((enable == phy_clk_stopped) && (enable == hclk_gated)) {
1249+
return true;
1250+
}
1251+
return false;
1252+
}
1253+
12201254
// ---------------------- Commands -------------------------
12211255

12221256
static esp_err_t _port_cmd_power_on(port_t *port)
@@ -1227,6 +1261,7 @@ static esp_err_t _port_cmd_power_on(port_t *port)
12271261
port->state = HCD_PORT_STATE_DISCONNECTED;
12281262
usb_dwc_hal_port_init(port->hal);
12291263
usb_dwc_hal_port_toggle_power(port->hal, true);
1264+
//_internal_clk_gate(port, false); // Un-gate the phy clock TODO: Test this
12301265
ret = ESP_OK;
12311266
} else {
12321267
ret = ESP_ERR_INVALID_STATE;
@@ -1240,6 +1275,7 @@ static esp_err_t _port_cmd_power_off(port_t *port)
12401275
// Port can only be unpowered if already powered
12411276
if (port->state != HCD_PORT_STATE_NOT_POWERED) {
12421277
port->state = HCD_PORT_STATE_NOT_POWERED;
1278+
//_internal_clk_gate(port, false); // Un-gate the phy clock TODO: Test this
12431279
usb_dwc_hal_port_deinit(port->hal);
12441280
usb_dwc_hal_port_toggle_power(port->hal, false);
12451281
// If a device is currently connected, this should trigger a disconnect event
@@ -1255,7 +1291,8 @@ static esp_err_t _port_cmd_reset(port_t *port)
12551291
esp_err_t ret;
12561292

12571293
// Port can only a reset when it is in the enabled or disabled (in the case of a new connection)states.
1258-
if (port->state != HCD_PORT_STATE_ENABLED && port->state != HCD_PORT_STATE_DISABLED) {
1294+
// Or suspended, to exit suspended state through host initiated reset
1295+
if (port->state != HCD_PORT_STATE_ENABLED && port->state != HCD_PORT_STATE_DISABLED && port->state != HCD_PORT_STATE_SUSPENDED) {
12591296
ret = ESP_ERR_INVALID_STATE;
12601297
goto exit;
12611298
}
@@ -1264,6 +1301,10 @@ static esp_err_t _port_cmd_reset(port_t *port)
12641301
ret = ESP_ERR_INVALID_STATE;
12651302
goto exit;
12661303
}
1304+
// If resetting from suspended state, we must un-gate the internal clock
1305+
if (port->state == HCD_PORT_STATE_SUSPENDED) {
1306+
_internal_clk_gate(port, false);
1307+
}
12671308
/*
12681309
Proceed to resetting the bus
12691310
- Update the port's state variable
@@ -1320,6 +1361,21 @@ static esp_err_t _port_cmd_reset(port_t *port)
13201361
return ret;
13211362
}
13221363

1364+
/**
1365+
* @brief Suspend the root port
1366+
*
1367+
* This sequence equals to a sequence from the DesignWare Cores USB 2.0 Programming Guide version 4.00a
1368+
* 14.2.3.3 External HCLK Gating When the Host Core is in Partial Power Down Mode
1369+
* Sequence Entering suspend state
1370+
*
1371+
* @param[in] port Pointer to the port object
1372+
* @return:
1373+
* - ESP_ERR_INVALID_STATE Port is not in a correct state to be suspended, or pipe(s) routed through this port is not halted
1374+
* - ESP_ERR_INVALID_RESPONSE Port state unexpectedly changed (for example: device was disconnected)
1375+
* - ESP_ERR_NOT_FINISHED Port did not finish the suspending sequence and is not in the suspended state
1376+
* - ESP_ERR_NOT_ALLOWED PHY clk was not suspended
1377+
* - ESP_OK Root port suspended
1378+
*/
13231379
static esp_err_t _port_cmd_bus_suspend(port_t *port)
13241380
{
13251381
esp_err_t ret;
@@ -1353,13 +1409,34 @@ static esp_err_t _port_cmd_bus_suspend(port_t *port)
13531409
goto exit;
13541410
}
13551411

1412+
if (! _internal_clk_gate(port, true)) {
1413+
ret = ESP_ERR_NOT_ALLOWED;
1414+
goto exit;
1415+
}
1416+
13561417
port->state = HCD_PORT_STATE_SUSPENDED;
13571418
ret = ESP_OK;
13581419

13591420
exit:
13601421
return ret;
13611422
}
13621423

1424+
/**
1425+
* @brief Resume the root port
1426+
*
1427+
* This sequence equals to a sequence from the DesignWare Cores USB 2.0 Programming Guide version 4.00a
1428+
* 14.2.3.3 External HCLK Gating When the Host Core is in Partial Power Down Mode
1429+
* Sequence Exiting Suspend State Through Host Initiated Resume
1430+
* Exiting Suspend State Through Device Initiated Remote Wakeup
1431+
*
1432+
* @note this sequence is used for both resume scenarios: the host initiated and the device initiated (remote wakeup) resume
1433+
* @param[in] port Pointer to the port object
1434+
* @return:
1435+
* - ESP_ERR_INVALID_STATE Port is not in a correct state to be resumed
1436+
* - ESP_ERR_NOT_ALLOWED PHY clk was not resumed
1437+
* - ESP_ERR_INVALID_RESPONSE Port state unexpectedly changed (for example: device was disconnected)
1438+
* - ESP_OK Root port resumed
1439+
*/
13631440
static esp_err_t _port_cmd_bus_resume(port_t *port)
13641441
{
13651442
esp_err_t ret;
@@ -1368,6 +1445,12 @@ static esp_err_t _port_cmd_bus_resume(port_t *port)
13681445
ret = ESP_ERR_INVALID_STATE;
13691446
goto exit;
13701447
}
1448+
1449+
if (!_internal_clk_gate(port, false)) {
1450+
ret = ESP_ERR_NOT_ALLOWED;
1451+
goto exit;
1452+
}
1453+
13711454
// Put and hold the bus in the K state.
13721455
usb_dwc_hal_port_toggle_resume(port->hal, true);
13731456
port->state = HCD_PORT_STATE_RESUMING;
@@ -1485,6 +1568,7 @@ esp_err_t hcd_port_deinit(hcd_port_handle_t port_hdl)
14851568
return ESP_OK;
14861569
}
14871570

1571+
14881572
esp_err_t hcd_port_command(hcd_port_handle_t port_hdl, hcd_port_cmd_t command)
14891573
{
14901574
esp_err_t ret = ESP_ERR_INVALID_STATE;

host/usb/src/hub.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,17 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl)
455455

456456
break;
457457
}
458+
case HCD_PORT_EVENT_REMOTE_WAKEUP:
459+
// Root port, including all the connected devices were resumed (global resume)
460+
// Clear all EPs and propagate the resumed event to clients
461+
//usbh_devs_set_pm_actions_all(USBH_DEV_RESUME | USBH_DEV_RESUME_EVT);
462+
463+
// Change Port state
464+
//HUB_DRIVER_ENTER_CRITICAL();
465+
//p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_ENABLED;
466+
//HUB_DRIVER_EXIT_CRITICAL();
467+
hub_root_mark_resume();
468+
break;
458469
default:
459470
abort(); // Should never occur
460471
break;

0 commit comments

Comments
 (0)