diff --git a/samples/wifi/twt/Kconfig b/samples/wifi/twt/Kconfig index e417ef58dd4f..9237c2204c29 100644 --- a/samples/wifi/twt/Kconfig +++ b/samples/wifi/twt/Kconfig @@ -47,4 +47,29 @@ config TWT_SAMPLE_PASSWORD help Specify the Password to connect +config TWT_TRIGGER_ENABLE + bool "Enable trigger mode" + help + STA expects AP to send triggers for start of every service period. + default y + +config TWT_ANNOUNCED_MODE + bool "Enable announced mode" + help + STA announces its wakeup to AP for every service period start. + default y + +config TWT_WAKE_INTERVAL + int "TWT awake interval" + help + The period where STA is awake and does data transfer, interval should be in us. + default 65000 + +config TWT_INTERVAL + int "TWT service interval" + help + The period for TWT that includes both wake and sleep durations, interval should be in us. + Please note higher intervals can be rejected by AP or cause interoperability issues. + default 524000 + endmenu diff --git a/samples/wifi/twt/README.rst b/samples/wifi/twt/README.rst index f4109ee42b75..4e168a6bc566 100644 --- a/samples/wifi/twt/README.rst +++ b/samples/wifi/twt/README.rst @@ -156,6 +156,19 @@ Testing [00:00:07.720,245] sta: MFP: UNKNOWN [00:00:07.720,245] sta: RSSI: -57 [00:00:07.720,245] sta: Static IP address: + [00:01:14.217,224] twt: == TWT negotiated parameters == + [00:01:14.217,224] twt: TWT Dialog token: 1 + [00:01:14.217,224] twt: TWT flow ID: 1 + [00:01:14.217,254] twt: TWT negotiation type: TWT individual negotiation + [00:01:14.217,285] twt: TWT responder: true + [00:01:14.217,315] twt: TWT implicit: true + [00:01:14.217,315] twt: TWT announce: true + [00:01:14.217,346] twt: TWT trigger: true + [00:01:14.217,376] twt: TWT wake interval: 65024 us + [00:01:14.217,376] twt: TWT interval: 524000 us + [00:01:14.217,376] twt: ======================== + [00:01:14.439,270] twt: TWT Setup Success + [00:01:17.061,767] twt: TWT teardown success Power management testing ************************ diff --git a/samples/wifi/twt/prj.conf b/samples/wifi/twt/prj.conf index 7188e8085bee..d6c80dbb1ab4 100644 --- a/samples/wifi/twt/prj.conf +++ b/samples/wifi/twt/prj.conf @@ -16,6 +16,8 @@ CONFIG_WPA_SUPP=y # CONFIG_TWT_STA_KEY_MGMT_WPA3=y CONFIG_TWT_SAMPLE_SSID="Myssid" CONFIG_TWT_SAMPLE_PASSWORD="Mypassword" +#CONFIG_TWT_TRIGGER_ENABLE=n +#CONFIG_TWT_ANNOUNCED_MODE=n # System settings CONFIG_NEWLIB_LIBC=y diff --git a/samples/wifi/twt/src/main.c b/samples/wifi/twt/src/main.c index 07aa617a3132..bfc714a33640 100644 --- a/samples/wifi/twt/src/main.c +++ b/samples/wifi/twt/src/main.c @@ -29,12 +29,14 @@ LOG_MODULE_REGISTER(twt, CONFIG_LOG_DEFAULT_LEVEL); #define WIFI_SHELL_MODULE "wifi" #define WIFI_SHELL_MGMT_EVENTS (NET_EVENT_WIFI_CONNECT_RESULT | \ - NET_EVENT_WIFI_DISCONNECT_RESULT) + NET_EVENT_WIFI_DISCONNECT_RESULT| \ + NET_EVENT_WIFI_TWT) #define MAX_SSID_LEN 32 #define DHCP_TIMEOUT 70 #define CONNECTION_TIMEOUT 100 #define STATUS_POLLING_MS 300 +#define TWT_RESP_TIMEOUT_S 20 static struct net_mgmt_event_callback wifi_shell_mgmt_cb; static struct net_mgmt_event_callback net_shell_mgmt_cb; @@ -52,6 +54,77 @@ static struct { }; } context; +static bool twt_supported, twt_resp_received; + +static int wait_for_twt_resp_received(void) +{ + int i, timeout_polls = (TWT_RESP_TIMEOUT_S * 1000) / STATUS_POLLING_MS; + + for (i = 0; i < timeout_polls; i++) { + k_sleep(K_MSEC(STATUS_POLLING_MS)); + if (twt_resp_received) { + return 1; + } + } + + return 0; +} + +static int setup_twt(void) +{ + struct net_if *iface = net_if_get_first_wifi(); + struct wifi_twt_params params = { 0 }; + int ret; + + params.operation = WIFI_TWT_SETUP; + params.negotiation_type = WIFI_TWT_INDIVIDUAL; + params.setup_cmd = WIFI_TWT_SETUP_CMD_REQUEST; + params.dialog_token = 1; + params.flow_id = 1; + params.setup.responder = 0; + params.setup.trigger = IS_ENABLED(CONFIG_TWT_TRIGGER_ENABLE); + params.setup.implicit = 1; + params.setup.announce = IS_ENABLED(CONFIG_TWT_ANNOUNCED_MODE); + params.setup.twt_wake_interval = CONFIG_TWT_WAKE_INTERVAL; + params.setup.twt_interval = CONFIG_TWT_INTERVAL; + + ret = net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params)); + if (ret) { + LOG_INF("TWT setup failed: %d", ret); + return ret; + } + + LOG_INF("TWT setup requested"); + + return 0; +} + +static int teardown_twt(void) +{ + struct net_if *iface = net_if_get_first_wifi(); + struct wifi_twt_params params = { 0 }; + int ret; + + params.operation = WIFI_TWT_TEARDOWN; + params.negotiation_type = WIFI_TWT_INDIVIDUAL; + params.setup_cmd = WIFI_TWT_TEARDOWN; + params.dialog_token = 1; + params.flow_id = 1; + + ret = net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params)); + if (ret) { + LOG_ERR("%s with %s failed, reason : %s", + wifi_twt_operation2str[params.operation], + wifi_twt_negotiation_type2str[params.negotiation_type], + get_twt_err_code_str(params.fail_reason)); + return ret; + } + + LOG_INF("TWT teardown success"); + + return 0; +} + static int cmd_wifi_status(void) { struct net_if *iface = net_if_get_default(); @@ -86,7 +159,12 @@ static int cmd_wifi_status(void) LOG_INF("MFP: %s", wifi_mfp_txt(status.mfp)); LOG_INF("RSSI: %d", status.rssi); LOG_INF("TWT: %s", status.twt_capable ? "Supported" : "Not supported"); + + if (status.twt_capable) { + twt_supported = 1; + } } + return 0; } @@ -131,6 +209,63 @@ static void handle_wifi_disconnect_result(struct net_mgmt_event_callback *cb) cmd_wifi_status(); } +static void print_twt_params(uint8_t dialog_token, uint8_t flow_id, + enum wifi_twt_negotiation_type negotiation_type, + bool responder, bool implicit, bool announce, + bool trigger, uint32_t twt_wake_interval, + uint64_t twt_interval) +{ + LOG_INF("TWT Dialog token: %d", + dialog_token); + LOG_INF("TWT flow ID: %d", + flow_id); + LOG_INF("TWT negotiation type: %s", + wifi_twt_negotiation_type2str[negotiation_type]); + LOG_INF("TWT responder: %s", + responder ? "true" : "false"); + LOG_INF("TWT implicit: %s", + implicit ? "true" : "false"); + LOG_INF("TWT announce: %s", + announce ? "true" : "false"); + LOG_INF("TWT trigger: %s", + trigger ? "true" : "false"); + LOG_INF("TWT wake interval: %d us", + twt_wake_interval); + LOG_INF("TWT interval: %lld us", + twt_interval); + LOG_INF("========================"); +} + +static void handle_wifi_twt_event(struct net_mgmt_event_callback *cb) +{ + const struct wifi_twt_params *resp = + (const struct wifi_twt_params *)cb->info; + + if (resp->operation == WIFI_TWT_TEARDOWN) { + LOG_INF("TWT teardown received for flow ID %d\n", + resp->flow_id); + return; + } + + if (resp->resp_status == WIFI_TWT_RESP_RECEIVED) { + twt_resp_received = 1; + LOG_INF("TWT response: %s", + wifi_twt_setup_cmd2str[resp->setup_cmd]); + LOG_INF("== TWT negotiated parameters =="); + print_twt_params(resp->dialog_token, + resp->flow_id, + resp->negotiation_type, + resp->setup.responder, + resp->setup.implicit, + resp->setup.announce, + resp->setup.trigger, + resp->setup.twt_wake_interval, + resp->setup.twt_interval); + } else { + LOG_INF("TWT response timed out\n"); + } +} + static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, struct net_if *iface) { @@ -141,6 +276,9 @@ static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, case NET_EVENT_WIFI_DISCONNECT_RESULT: handle_wifi_disconnect_result(cb); break; + case NET_EVENT_WIFI_TWT: + handle_wifi_twt_event(cb); + break; default: break; } @@ -248,7 +386,6 @@ int main(void) CONFIG_NET_CONFIG_MY_IPV4_GW); while (1) { - wifi_connect(); for (i = 0; i < CONNECTION_TIMEOUT; i++) { @@ -258,8 +395,39 @@ int main(void) break; } } + if (context.connected) { - k_sleep(K_FOREVER); + int ret; + + k_sleep(K_SECONDS(2)); + + if (!twt_supported) { + LOG_INF("AP is not TWT capable, exiting the sample\n"); + return 1; + } + + LOG_INF("AP is TWT capable, establishing TWT"); + + ret = setup_twt(); + if (ret) { + LOG_ERR("Failed to establish TWT flow: %d\n", ret); + return 1; + } + + if (wait_for_twt_resp_received()) { + LOG_INF("TWT Setup success"); + } else { + LOG_INF("TWT Setup timed out\n"); + } + + /* Wait for few service periods before tearing down */ + k_sleep(K_USEC(5 * CONFIG_TWT_INTERVAL)); + ret = teardown_twt(); + if (ret) { + LOG_ERR("Failed to teardown TWT flow: %d\n", ret); + return 1; + } + return 0; } else if (!context.connect_result) { LOG_ERR("Connection Timed Out"); }