Skip to content

Commit 76e02d2

Browse files
feat(usb_host): Remote wakeup
- HCD and USB Host tests
1 parent f4a28bb commit 76e02d2

File tree

15 files changed

+946
-19
lines changed

15 files changed

+946
-19
lines changed

.github/workflows/build_idf_examples.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ name: Build ESP-IDF USB examples
1616

1717
on:
1818
pull_request:
19-
types: [opened, reopened, synchronize]
19+
types: [opened, reopened]
2020

2121
jobs:
2222
build:

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: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,11 @@ 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_event = HCD_PORT_EVENT_REMOTE_WAKEUP;
837+
break;
838+
}
834839
default: {
835840
abort();
836841
break;
@@ -1217,6 +1222,34 @@ static inline bool _is_fifo_config_by_bias(const usb_dwc_hal_fifo_config_t *cfg)
12171222
cfg->ptx_fifo_lines == 0);
12181223
}
12191224

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

12221255
static esp_err_t _port_cmd_power_on(port_t *port)
@@ -1225,6 +1258,7 @@ static esp_err_t _port_cmd_power_on(port_t *port)
12251258
// Port can only be powered on if it's currently unpowered
12261259
if (port->state == HCD_PORT_STATE_NOT_POWERED) {
12271260
port->state = HCD_PORT_STATE_DISCONNECTED;
1261+
//assert(_internal_clk_gate(port, false)); // Un-gate the phy clock TODO: Test this
12281262
usb_dwc_hal_port_init(port->hal);
12291263
usb_dwc_hal_port_toggle_power(port->hal, true);
12301264
ret = ESP_OK;
@@ -1239,7 +1273,13 @@ static esp_err_t _port_cmd_power_off(port_t *port)
12391273
esp_err_t ret;
12401274
// Port can only be unpowered if already powered
12411275
if (port->state != HCD_PORT_STATE_NOT_POWERED) {
1276+
1277+
if (port->state == HCD_PORT_STATE_SUSPENDED) {
1278+
// un-gate internal clock, to be able to toggle power on the port
1279+
assert(_internal_clk_gate(port, false)); // Un-gate the phy clock
1280+
}
12421281
port->state = HCD_PORT_STATE_NOT_POWERED;
1282+
//assert(_internal_clk_gate(port, false)); // Gate the phy clock TODO: Test this
12431283
usb_dwc_hal_port_deinit(port->hal);
12441284
usb_dwc_hal_port_toggle_power(port->hal, false);
12451285
// If a device is currently connected, this should trigger a disconnect event
@@ -1267,6 +1307,10 @@ static esp_err_t _port_cmd_reset(port_t *port)
12671307
ret = ESP_ERR_INVALID_STATE;
12681308
goto exit;
12691309
}
1310+
// If resetting from suspended state, we must un-gate the internal clock
1311+
if (port->state == HCD_PORT_STATE_SUSPENDED) {
1312+
_internal_clk_gate(port, false);
1313+
}
12701314
/*
12711315
Proceed to resetting the bus
12721316
- Update the port's state variable
@@ -1323,6 +1367,21 @@ static esp_err_t _port_cmd_reset(port_t *port)
13231367
return ret;
13241368
}
13251369

1370+
/**
1371+
* @brief Suspend the root port
1372+
*
1373+
* This sequence equals to a sequence from the DesignWare Cores USB 2.0 Programming Guide version 4.00a
1374+
* 14.2.3.3 External HCLK Gating When the Host Core is in Partial Power Down Mode
1375+
* Sequence Entering suspend state
1376+
*
1377+
* @param[in] port Pointer to the port object
1378+
* @return:
1379+
* - ESP_ERR_INVALID_STATE Port is not in a correct state to be suspended, or pipe(s) routed through this port is not halted
1380+
* - ESP_ERR_INVALID_RESPONSE Port state unexpectedly changed (for example: device was disconnected)
1381+
* - ESP_ERR_NOT_FINISHED Port did not finish the suspending sequence and is not in the suspended state
1382+
* - ESP_ERR_NOT_ALLOWED PHY clk was not suspended
1383+
* - ESP_OK Root port suspended
1384+
*/
13261385
static esp_err_t _port_cmd_bus_suspend(port_t *port)
13271386
{
13281387
esp_err_t ret;
@@ -1356,13 +1415,34 @@ static esp_err_t _port_cmd_bus_suspend(port_t *port)
13561415
goto exit;
13571416
}
13581417

1418+
if (! _internal_clk_gate(port, true)) {
1419+
ret = ESP_ERR_NOT_ALLOWED;
1420+
goto exit;
1421+
}
1422+
13591423
port->state = HCD_PORT_STATE_SUSPENDED;
13601424
ret = ESP_OK;
13611425

13621426
exit:
13631427
return ret;
13641428
}
13651429

1430+
/**
1431+
* @brief Resume the root port
1432+
*
1433+
* This sequence equals to a sequence from the DesignWare Cores USB 2.0 Programming Guide version 4.00a
1434+
* 14.2.3.3 External HCLK Gating When the Host Core is in Partial Power Down Mode
1435+
* Sequence Exiting Suspend State Through Host Initiated Resume
1436+
* Exiting Suspend State Through Device Initiated Remote Wakeup
1437+
*
1438+
* @note this sequence is used for both resume scenarios: the host initiated and the device initiated (remote wakeup) resume
1439+
* @param[in] port Pointer to the port object
1440+
* @return:
1441+
* - ESP_ERR_INVALID_STATE Port is not in a correct state to be resumed
1442+
* - ESP_ERR_NOT_ALLOWED PHY clk was not resumed
1443+
* - ESP_ERR_INVALID_RESPONSE Port state unexpectedly changed (for example: device was disconnected)
1444+
* - ESP_OK Root port resumed
1445+
*/
13661446
static esp_err_t _port_cmd_bus_resume(port_t *port)
13671447
{
13681448
esp_err_t ret;
@@ -1371,6 +1451,12 @@ static esp_err_t _port_cmd_bus_resume(port_t *port)
13711451
ret = ESP_ERR_INVALID_STATE;
13721452
goto exit;
13731453
}
1454+
1455+
if (!_internal_clk_gate(port, false)) {
1456+
ret = ESP_ERR_NOT_ALLOWED;
1457+
goto exit;
1458+
}
1459+
13741460
// Put and hold the bus in the K state.
13751461
usb_dwc_hal_port_toggle_resume(port->hal, true);
13761462
port->state = HCD_PORT_STATE_RESUMING;
@@ -1596,6 +1682,7 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl)
15961682
ESP_ERR_INVALID_STATE);
15971683

15981684
// We are about to do a soft reset on the peripheral. Disable the peripheral throughout
1685+
//assert(_internal_clk_gate(port, false)); // Gate the phy clock
15991686
esp_intr_disable(port->isr_hdl);
16001687
usb_dwc_hal_core_soft_reset(port->hal);
16011688
port->state = HCD_PORT_STATE_NOT_POWERED;
@@ -1605,6 +1692,7 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl)
16051692
// Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset
16061693
memset(port->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t));
16071694
esp_intr_enable(port->isr_hdl);
1695+
//assert(_internal_clk_gate(port, true)); // Gate the phy clock
16081696
HCD_EXIT_CRITICAL();
16091697
return ESP_OK;
16101698
}

host/usb/src/hub.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,10 @@ 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+
// Mark root port as ready to be resumed
460+
hub_root_mark_resume();
461+
break;
458462
default:
459463
abort(); // Should never occur
460464
break;

0 commit comments

Comments
 (0)