| /* |
| * Copyright (c) 2018 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** @file |
| * @brief WiFi shell module |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(net_wifi_shell, LOG_LEVEL_INF); |
| |
| #include <zephyr/kernel.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <zephyr/shell/shell.h> |
| #include <zephyr/sys/printk.h> |
| #include <zephyr/init.h> |
| |
| #include <zephyr/net/net_if.h> |
| #include <zephyr/net/wifi_mgmt.h> |
| #include <zephyr/net/net_event.h> |
| |
| #include "net_private.h" |
| |
| #define WIFI_SHELL_MODULE "wifi" |
| |
| #define WIFI_SHELL_MGMT_EVENTS (NET_EVENT_WIFI_SCAN_RESULT | \ |
| NET_EVENT_WIFI_SCAN_DONE | \ |
| NET_EVENT_WIFI_CONNECT_RESULT | \ |
| NET_EVENT_WIFI_DISCONNECT_RESULT | \ |
| NET_EVENT_WIFI_TWT) |
| |
| static struct { |
| const struct shell *sh; |
| |
| union { |
| struct { |
| |
| uint8_t connecting : 1; |
| uint8_t disconnecting : 1; |
| uint8_t _unused : 6; |
| }; |
| uint8_t all; |
| }; |
| } context; |
| |
| static uint32_t scan_result; |
| |
| static struct net_mgmt_event_callback wifi_shell_mgmt_cb; |
| |
| #define print(sh, level, fmt, ...) \ |
| do { \ |
| if (sh) { \ |
| shell_fprintf(sh, level, fmt, ##__VA_ARGS__); \ |
| } else { \ |
| printk(fmt, ##__VA_ARGS__); \ |
| } \ |
| } while (false) |
| |
| static bool parse_number(const struct shell *sh, long *param, char *str, int min, int max) |
| { |
| char *endptr; |
| char *str_tmp = str; |
| long num = strtol(str_tmp, &endptr, 10); |
| |
| if (*endptr != '\0') { |
| print(sh, SHELL_ERROR, "Invalid number: %s", str_tmp); |
| return false; |
| } |
| if ((num) < (min) || (num) > (max)) { |
| print(sh, SHELL_WARNING, "Value out of range: %s, (%d-%d)", str_tmp, min, max); |
| return false; |
| } |
| *param = num; |
| return true; |
| } |
| |
| static void handle_wifi_scan_result(struct net_mgmt_event_callback *cb) |
| { |
| const struct wifi_scan_result *entry = |
| (const struct wifi_scan_result *)cb->info; |
| uint8_t mac_string_buf[sizeof("xx:xx:xx:xx:xx:xx")]; |
| |
| scan_result++; |
| |
| if (scan_result == 1U) { |
| print(context.sh, SHELL_NORMAL, |
| "\n%-4s | %-32s %-5s | %-13s | %-4s | %-15s | %s\n", |
| "Num", "SSID", "(len)", "Chan (Band)", "RSSI", "Security", "BSSID"); |
| } |
| |
| print(context.sh, SHELL_NORMAL, "%-4d | %-32s %-5u | %-4u (%-6s) | %-4d | %-15s | %s\n", |
| scan_result, entry->ssid, entry->ssid_length, entry->channel, |
| wifi_band_txt(entry->band), |
| entry->rssi, |
| wifi_security_txt(entry->security), |
| ((entry->mac_length) ? |
| net_sprint_ll_addr_buf(entry->mac, WIFI_MAC_ADDR_LEN, mac_string_buf, |
| sizeof(mac_string_buf)) : "")); |
| } |
| |
| static void handle_wifi_scan_done(struct net_mgmt_event_callback *cb) |
| { |
| const struct wifi_status *status = |
| (const struct wifi_status *)cb->info; |
| |
| if (status->status) { |
| print(context.sh, SHELL_WARNING, |
| "Scan request failed (%d)\n", status->status); |
| } else { |
| print(context.sh, SHELL_NORMAL, "Scan request done\n"); |
| } |
| |
| scan_result = 0U; |
| } |
| |
| static void handle_wifi_connect_result(struct net_mgmt_event_callback *cb) |
| { |
| const struct wifi_status *status = |
| (const struct wifi_status *) cb->info; |
| |
| if (status->status) { |
| print(context.sh, SHELL_WARNING, |
| "Connection request failed (%d)\n", status->status); |
| } else { |
| print(context.sh, SHELL_NORMAL, "Connected\n"); |
| } |
| |
| context.connecting = false; |
| } |
| |
| static void handle_wifi_disconnect_result(struct net_mgmt_event_callback *cb) |
| { |
| const struct wifi_status *status = |
| (const struct wifi_status *) cb->info; |
| |
| if (context.disconnecting) { |
| print(context.sh, |
| status->status ? SHELL_WARNING : SHELL_NORMAL, |
| "Disconnection request %s (%d)\n", |
| status->status ? "failed" : "done", |
| status->status); |
| context.disconnecting = false; |
| } else { |
| print(context.sh, SHELL_NORMAL, "Disconnected\n"); |
| } |
| } |
| |
| 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; |
| |
| print(context.sh, SHELL_NORMAL, "TWT response: %s for dialog: %d and flow: %d\n", |
| wifi_twt_setup_cmd2str[resp->setup_cmd], resp->dialog_token, resp->flow_id); |
| |
| /* If accepted, then no need to print TWT params */ |
| if (resp->setup_cmd != WIFI_TWT_SETUP_CMD_ACCEPT) { |
| print(context.sh, SHELL_NORMAL, |
| "TWT parameters: trigger: %s wake_interval_ms: %d, interval_ms: %d\n", |
| resp->setup.trigger ? "trigger" : "no_trigger", |
| resp->setup.twt_wake_interval_ms, |
| resp->setup.twt_interval_ms); |
| } |
| } |
| |
| static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, |
| uint32_t mgmt_event, struct net_if *iface) |
| { |
| switch (mgmt_event) { |
| case NET_EVENT_WIFI_SCAN_RESULT: |
| handle_wifi_scan_result(cb); |
| break; |
| case NET_EVENT_WIFI_SCAN_DONE: |
| handle_wifi_scan_done(cb); |
| break; |
| case NET_EVENT_WIFI_CONNECT_RESULT: |
| handle_wifi_connect_result(cb); |
| break; |
| 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; |
| } |
| } |
| |
| static int __wifi_args_to_params(size_t argc, char *argv[], |
| struct wifi_connect_req_params *params) |
| { |
| char *endptr; |
| int idx = 1; |
| |
| if (argc < 1) { |
| return -EINVAL; |
| } |
| |
| /* SSID */ |
| params->ssid = argv[0]; |
| params->ssid_length = strlen(params->ssid); |
| |
| /* Channel (optional) */ |
| if ((idx < argc) && (strlen(argv[idx]) <= 3)) { |
| params->channel = strtol(argv[idx], &endptr, 10); |
| if (*endptr != '\0') { |
| return -EINVAL; |
| } |
| |
| if (params->channel == 0U) { |
| params->channel = WIFI_CHANNEL_ANY; |
| } |
| |
| idx++; |
| } else { |
| params->channel = WIFI_CHANNEL_ANY; |
| } |
| |
| /* PSK (optional) */ |
| if (idx < argc) { |
| params->psk = argv[idx]; |
| params->psk_length = strlen(argv[idx]); |
| /* Defaults */ |
| params->security = WIFI_SECURITY_TYPE_PSK; |
| params->mfp = WIFI_MFP_OPTIONAL; |
| idx++; |
| |
| /* Security type (optional) */ |
| if (idx < argc) { |
| unsigned int security = strtol(argv[idx], &endptr, 10); |
| |
| if (security <= WIFI_SECURITY_TYPE_MAX) { |
| params->security = security; |
| } |
| idx++; |
| |
| /* MFP (optional) */ |
| if (idx < argc) { |
| unsigned int mfp = strtol(argv[idx], &endptr, 10); |
| |
| if (mfp <= WIFI_MFP_REQUIRED) { |
| params->mfp = mfp; |
| } |
| idx++; |
| } |
| } |
| } else { |
| params->security = WIFI_SECURITY_TYPE_NONE; |
| } |
| |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_connect(const struct shell *sh, size_t argc, |
| char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| struct wifi_connect_req_params cnx_params = { 0 }; |
| |
| if (__wifi_args_to_params(argc - 1, &argv[1], &cnx_params)) { |
| shell_help(sh); |
| return -ENOEXEC; |
| } |
| |
| context.connecting = true; |
| context.sh = sh; |
| |
| if (net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, |
| &cnx_params, sizeof(struct wifi_connect_req_params))) { |
| shell_fprintf(sh, SHELL_WARNING, |
| "Connection request failed\n"); |
| context.connecting = false; |
| |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "Connection requested\n"); |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_disconnect(const struct shell *sh, size_t argc, |
| char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| int status; |
| |
| context.disconnecting = true; |
| context.sh = sh; |
| |
| status = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, iface, NULL, 0); |
| |
| if (status) { |
| context.disconnecting = false; |
| |
| if (status == -EALREADY) { |
| shell_fprintf(sh, SHELL_INFO, |
| "Already disconnected\n"); |
| } else { |
| shell_fprintf(sh, SHELL_WARNING, |
| "Disconnect request failed\n"); |
| return -ENOEXEC; |
| } |
| } else { |
| shell_fprintf(sh, SHELL_NORMAL, |
| "Disconnect requested\n"); |
| } |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_scan(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| |
| context.sh = sh; |
| |
| if (net_mgmt(NET_REQUEST_WIFI_SCAN, iface, NULL, 0)) { |
| shell_fprintf(sh, SHELL_WARNING, "Scan request failed\n"); |
| |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "Scan requested\n"); |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_status(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| struct wifi_iface_status status = { 0 }; |
| |
| context.sh = sh; |
| |
| if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, iface, &status, |
| sizeof(struct wifi_iface_status))) { |
| shell_fprintf(sh, SHELL_WARNING, "Status request failed\n"); |
| |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "Status: successful\n"); |
| shell_fprintf(sh, SHELL_NORMAL, "==================\n"); |
| shell_fprintf(sh, SHELL_NORMAL, "State: %s\n", wifi_state_txt(status.state)); |
| |
| if (status.state >= WIFI_STATE_ASSOCIATED) { |
| uint8_t mac_string_buf[sizeof("xx:xx:xx:xx:xx:xx")]; |
| |
| shell_fprintf(sh, SHELL_NORMAL, "Interface Mode: %s\n", |
| wifi_mode_txt(status.iface_mode)); |
| shell_fprintf(sh, SHELL_NORMAL, "Link Mode: %s\n", |
| wifi_link_mode_txt(status.link_mode)); |
| shell_fprintf(sh, SHELL_NORMAL, "SSID: %-32s\n", status.ssid); |
| shell_fprintf(sh, SHELL_NORMAL, "BSSID: %s\n", |
| net_sprint_ll_addr_buf(status.bssid, |
| WIFI_MAC_ADDR_LEN, mac_string_buf, |
| sizeof(mac_string_buf)) |
| ); |
| shell_fprintf(sh, SHELL_NORMAL, "Band: %s\n", |
| wifi_band_txt(status.band)); |
| shell_fprintf(sh, SHELL_NORMAL, "Channel: %d\n", status.channel); |
| shell_fprintf(sh, SHELL_NORMAL, "Security: %s\n", |
| wifi_security_txt(status.security)); |
| shell_fprintf(sh, SHELL_NORMAL, "MFP: %s\n", |
| wifi_mfp_txt(status.mfp)); |
| shell_fprintf(sh, SHELL_NORMAL, "RSSI: %d\n", status.rssi); |
| } |
| |
| return 0; |
| } |
| |
| |
| #if defined(CONFIG_NET_STATISTICS_WIFI) && \ |
| defined(CONFIG_NET_STATISTICS_USER_API) |
| static void print_wifi_stats(struct net_if *iface, struct net_stats_wifi *data, |
| const struct shell *sh) |
| { |
| shell_fprintf(sh, SHELL_NORMAL, "Statistics for Wi-Fi interface %p [%d]\n", iface, |
| net_if_get_by_iface(iface)); |
| |
| shell_fprintf(sh, SHELL_NORMAL, "Bytes received : %u\n", data->bytes.received); |
| shell_fprintf(sh, SHELL_NORMAL, "Bytes sent : %u\n", data->bytes.sent); |
| shell_fprintf(sh, SHELL_NORMAL, "Packets received : %u\n", data->pkts.rx); |
| shell_fprintf(sh, SHELL_NORMAL, "Packets sent : %u\n", data->pkts.tx); |
| shell_fprintf(sh, SHELL_NORMAL, "Bcast received : %u\n", data->broadcast.rx); |
| shell_fprintf(sh, SHELL_NORMAL, "Bcast sent : %u\n", data->broadcast.tx); |
| shell_fprintf(sh, SHELL_NORMAL, "Mcast received : %u\n", data->multicast.rx); |
| shell_fprintf(sh, SHELL_NORMAL, "Mcast sent : %u\n", data->multicast.tx); |
| shell_fprintf(sh, SHELL_NORMAL, "Beacons received : %u\n", data->sta_mgmt.beacons_rx); |
| shell_fprintf(sh, SHELL_NORMAL, "Beacons missed : %u\n", |
| data->sta_mgmt.beacons_miss); |
| } |
| #endif /* CONFIG_NET_STATISTICS_WIFI && CONFIG_NET_STATISTICS_USER_API */ |
| |
| static int cmd_wifi_stats(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| #if defined(CONFIG_NET_STATISTICS_WIFI) && \ |
| defined(CONFIG_NET_STATISTICS_USER_API) |
| struct net_if *iface = net_if_get_default(); |
| struct net_stats_wifi stats = { 0 }; |
| int ret; |
| |
| ret = net_mgmt(NET_REQUEST_STATS_GET_WIFI, iface, |
| &stats, sizeof(stats)); |
| if (!ret) { |
| print_wifi_stats(iface, &stats, sh); |
| } |
| #else |
| ARG_UNUSED(argc); |
| ARG_UNUSED(argv); |
| |
| shell_fprintf(sh, SHELL_INFO, "Set %s to enable %s support.\n", |
| "CONFIG_NET_STATISTICS_WIFI and CONFIG_NET_STATISTICS_USER_API", |
| "statistics"); |
| #endif /* CONFIG_NET_STATISTICS_WIFI && CONFIG_NET_STATISTICS_USER_API */ |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_ps(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| struct wifi_ps_params params = { 0 }; |
| |
| context.sh = sh; |
| |
| if (argc > 2) { |
| shell_fprintf(sh, SHELL_WARNING, "Invalid number of arguments\n"); |
| return -ENOEXEC; |
| } |
| |
| if (argc == 1) { |
| struct wifi_ps_config config = { 0 }; |
| |
| if (net_mgmt(NET_REQUEST_WIFI_PS_CONFIG, iface, |
| &config, sizeof(config))) { |
| shell_fprintf(sh, SHELL_WARNING, "Failed to get PS config\n"); |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "PS status: %s\n", |
| wifi_ps2str[config.enabled]); |
| if (config.enabled) { |
| shell_fprintf(sh, SHELL_NORMAL, "PS mode: %s\n", |
| wifi_ps_mode2str[config.mode]); |
| } |
| |
| if (config.num_twt_flows == 0) { |
| shell_fprintf(sh, SHELL_NORMAL, "No TWT flows\n"); |
| } else { |
| for (int i = 0; i < config.num_twt_flows; i++) { |
| shell_fprintf(sh, SHELL_NORMAL, "TWT Dialog token: %d\n", |
| config.twt_flows[i].dialog_token); |
| shell_fprintf(sh, SHELL_NORMAL, "TWT flow ID: %d\n", |
| config.twt_flows[i].flow_id); |
| shell_fprintf(sh, SHELL_NORMAL, "TWT negotiation type: %s\n", |
| wifi_twt_negotiation_type2str[ |
| config.twt_flows[i].negotiation_type]); |
| shell_fprintf(sh, SHELL_NORMAL, "TWT responder: %s\n", |
| config.twt_flows[i].responder ? "true" : "false"); |
| shell_fprintf(sh, SHELL_NORMAL, "TWT implicit: %s\n", |
| config.twt_flows[i].implicit ? "true" : "false"); |
| shell_fprintf(sh, SHELL_NORMAL, "TWT trigger: %s\n", |
| config.twt_flows[i].trigger ? "true" : "false"); |
| shell_fprintf(sh, SHELL_NORMAL, "TWT announce: %s\n", |
| config.twt_flows[i].announce ? "true" : "false"); |
| shell_fprintf(sh, SHELL_NORMAL, "TWT wake interval: %d ms\n", |
| config.twt_flows[i].twt_wake_interval_ms); |
| shell_fprintf(sh, SHELL_NORMAL, "TWT interval: %d ms\n", |
| config.twt_flows[i].twt_interval_ms); |
| shell_fprintf(sh, SHELL_NORMAL, "========================\n"); |
| } |
| } |
| return 0; |
| } |
| |
| if (!strncmp(argv[1], "on", 2)) { |
| params.enabled = WIFI_PS_ENABLED; |
| } else if (!strncmp(argv[1], "off", 3)) { |
| params.enabled = WIFI_PS_DISABLED; |
| } else { |
| shell_fprintf(sh, SHELL_WARNING, "Invalid argument\n"); |
| return -ENOEXEC; |
| } |
| |
| if (net_mgmt(NET_REQUEST_WIFI_PS, iface, ¶ms, sizeof(params))) { |
| shell_fprintf(sh, SHELL_WARNING, "Power save %s failed\n", |
| params.enabled ? "enable" : "disable"); |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "%s\n", wifi_ps2str[params.enabled]); |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_ps_mode(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| struct wifi_ps_mode_params params = { 0 }; |
| |
| context.sh = sh; |
| |
| if (argc != 2) { |
| shell_fprintf(sh, SHELL_WARNING, "Invalid number of arguments\n"); |
| return -ENOEXEC; |
| } |
| |
| if (!strncmp(argv[1], "legacy", 6)) { |
| params.mode = WIFI_PS_MODE_LEGACY; |
| } else if (!strncmp(argv[1], "wmm", 3)) { |
| params.mode = WIFI_PS_MODE_WMM; |
| } else { |
| shell_fprintf(sh, SHELL_WARNING, "Invalid power save mode\n"); |
| return -ENOEXEC; |
| } |
| |
| if (net_mgmt(NET_REQUEST_WIFI_PS_MODE, iface, ¶ms, sizeof(params))) { |
| shell_fprintf(sh, SHELL_WARNING, "%s failed\n", wifi_ps_mode2str[params.mode]); |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "%s\n", wifi_ps_mode2str[params.mode]); |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_ps_timeout(const struct shell *sh, size_t argc, char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| struct wifi_ps_timeout_params params = { 0 }; |
| long timeout_ms = 0; |
| int err = 0; |
| |
| context.sh = sh; |
| |
| if (argc != 2) { |
| shell_fprintf(sh, SHELL_WARNING, "Invalid number of arguments\n"); |
| return -ENOEXEC; |
| } |
| |
| timeout_ms = shell_strtol(argv[1], 10, &err); |
| |
| if (err) { |
| shell_error(sh, "Unable to parse input (err %d)", err); |
| return err; |
| } |
| |
| params.timeout_ms = timeout_ms; |
| |
| if (net_mgmt(NET_REQUEST_WIFI_PS_TIMEOUT, iface, ¶ms, sizeof(params))) { |
| shell_fprintf(sh, SHELL_WARNING, "Setting power save timeout failed\n"); |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, |
| "Power save timeout %d ms\n", params.timeout_ms); |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_twt_setup_quick(const struct shell *sh, size_t argc, |
| char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| struct wifi_twt_params params = { 0 }; |
| int idx = 1; |
| |
| context.sh = sh; |
| |
| if (argc != 3) { |
| shell_fprintf(sh, SHELL_WARNING, "Invalid number of arguments\n"); |
| shell_help(sh); |
| return -ENOEXEC; |
| } |
| |
| /* Sensible defaults */ |
| 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.implicit = 1; |
| params.setup.trigger = 1; |
| params.setup.announce = 0; |
| |
| if (!parse_number(sh, (long *)¶ms.setup.twt_wake_interval_ms, argv[idx++], 1, 255) || |
| !parse_number(sh, (long *)¶ms.setup.twt_interval_ms, argv[idx++], 1, |
| WIFI_MAX_TWT_INTERVAL_MS)) |
| return -EINVAL; |
| |
| if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { |
| shell_fprintf(sh, SHELL_WARNING, "%s with %s failed\n", |
| wifi_twt_operation2str[params.operation], |
| wifi_twt_negotiation_type2str[params.negotiation_type]); |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s with dg: %d, flow_id: %d requested\n", |
| wifi_twt_operation2str[params.operation], |
| params.dialog_token, params.flow_id); |
| |
| return 0; |
| } |
| |
| |
| static int cmd_wifi_twt_setup(const struct shell *sh, size_t argc, |
| char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| struct wifi_twt_params params = { 0 }; |
| int idx = 1; |
| long neg_type; |
| long setup_cmd; |
| |
| context.sh = sh; |
| |
| if (argc != 11) { |
| shell_fprintf(sh, SHELL_WARNING, "Invalid number of arguments\n"); |
| shell_help(sh); |
| return -ENOEXEC; |
| } |
| |
| params.operation = WIFI_TWT_SETUP; |
| |
| if (!parse_number(sh, &neg_type, argv[idx++], WIFI_TWT_INDIVIDUAL, |
| WIFI_TWT_WAKE_TBTT) || |
| !parse_number(sh, &setup_cmd, argv[idx++], WIFI_TWT_SETUP_CMD_REQUEST, |
| WIFI_TWT_SETUP_CMD_DEMAND) || |
| !parse_number(sh, (long *)¶ms.dialog_token, argv[idx++], 1, 255) || |
| !parse_number(sh, (long *)¶ms.flow_id, argv[idx++], 1, WIFI_MAX_TWT_FLOWS) || |
| !parse_number(sh, (long *)¶ms.setup.responder, argv[idx++], 0, 1) || |
| !parse_number(sh, (long *)¶ms.setup.trigger, argv[idx++], 0, 1) || |
| !parse_number(sh, (long *)¶ms.setup.implicit, argv[idx++], 0, 1) || |
| !parse_number(sh, (long *)¶ms.setup.announce, argv[idx++], 0, 1) || |
| !parse_number(sh, (long *)¶ms.setup.twt_wake_interval_ms, argv[idx++], 1, 255) || |
| !parse_number(sh, (long *)¶ms.setup.twt_interval_ms, argv[idx++], 1, |
| WIFI_MAX_TWT_INTERVAL_MS)) |
| return -EINVAL; |
| |
| params.negotiation_type = neg_type; |
| params.setup_cmd = setup_cmd; |
| |
| if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { |
| shell_fprintf(sh, SHELL_WARNING, "%s with %s failed\n", |
| wifi_twt_operation2str[params.operation], |
| wifi_twt_negotiation_type2str[params.negotiation_type]); |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s with dg: %d, flow_id: %d requested\n", |
| wifi_twt_operation2str[params.operation], |
| params.dialog_token, params.flow_id); |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_twt_teardown(const struct shell *sh, size_t argc, |
| char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| struct wifi_twt_params params = { 0 }; |
| long neg_type = 0; |
| long setup_cmd = 0; |
| |
| context.sh = sh; |
| int idx = 1; |
| |
| if (argc != 5) { |
| shell_fprintf(sh, SHELL_WARNING, "Invalid number of arguments\n"); |
| shell_help(sh); |
| return -ENOEXEC; |
| } |
| |
| params.operation = WIFI_TWT_TEARDOWN; |
| neg_type = params.negotiation_type; |
| setup_cmd = params.setup_cmd; |
| |
| if (!parse_number(sh, &neg_type, argv[idx++], WIFI_TWT_INDIVIDUAL, |
| WIFI_TWT_WAKE_TBTT) || |
| !parse_number(sh, &setup_cmd, argv[idx++], WIFI_TWT_SETUP_CMD_REQUEST, |
| WIFI_TWT_SETUP_CMD_DEMAND) || |
| !parse_number(sh, (long *)¶ms.dialog_token, argv[idx++], 1, 255) || |
| !parse_number(sh, (long *)¶ms.flow_id, argv[idx++], 1, WIFI_MAX_TWT_FLOWS)) |
| return -EINVAL; |
| |
| if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { |
| shell_fprintf(sh, SHELL_WARNING, "%s with %s failed\n", |
| wifi_twt_operation2str[params.operation], |
| wifi_twt_negotiation_type2str[params.negotiation_type]); |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s with dg: %d, flow_id: %d requested\n", |
| wifi_twt_operation2str[params.operation], |
| params.dialog_token, params.flow_id); |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_twt_teardown_all(const struct shell *sh, size_t argc, |
| char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| struct wifi_twt_params params = { 0 }; |
| |
| context.sh = sh; |
| |
| params.operation = WIFI_TWT_TEARDOWN; |
| params.teardown.teardown_all = 1; |
| |
| if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { |
| shell_fprintf(sh, SHELL_WARNING, "%s with %s failed\n", |
| wifi_twt_operation2str[params.operation], |
| wifi_twt_negotiation_type2str[params.negotiation_type]); |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s all flows\n", |
| wifi_twt_operation2str[params.operation]); |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_ap_enable(const struct shell *sh, size_t argc, |
| char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| static struct wifi_connect_req_params cnx_params; |
| |
| if (__wifi_args_to_params(argc - 1, &argv[1], &cnx_params)) { |
| shell_help(sh); |
| return -ENOEXEC; |
| } |
| |
| context.sh = sh; |
| |
| if (net_mgmt(NET_REQUEST_WIFI_AP_ENABLE, iface, |
| &cnx_params, sizeof(struct wifi_connect_req_params))) { |
| shell_fprintf(sh, SHELL_WARNING, "AP mode failed\n"); |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "AP mode enabled\n"); |
| |
| return 0; |
| } |
| |
| static int cmd_wifi_ap_disable(const struct shell *sh, size_t argc, |
| char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| |
| if (net_mgmt(NET_REQUEST_WIFI_AP_DISABLE, iface, NULL, 0)) { |
| shell_fprintf(sh, SHELL_WARNING, "AP mode disable failed\n"); |
| |
| return -ENOEXEC; |
| } |
| |
| shell_fprintf(sh, SHELL_NORMAL, "AP mode disabled\n"); |
| |
| return 0; |
| } |
| |
| |
| static int cmd_wifi_reg_domain(const struct shell *sh, size_t argc, |
| char *argv[]) |
| { |
| struct net_if *iface = net_if_get_default(); |
| struct wifi_reg_domain regd = {0}; |
| int ret; |
| |
| if (argc == 1) { |
| regd.oper = WIFI_MGMT_GET; |
| } else if (argc >= 2 && argc <= 3) { |
| regd.oper = WIFI_MGMT_SET; |
| if (strlen(argv[1]) != 2) { |
| shell_fprintf(sh, SHELL_WARNING, |
| "Invalid reg domain: Length should be two letters/digits\n"); |
| return -ENOEXEC; |
| } |
| |
| /* Two letter country code with special case of 00 for WORLD */ |
| if (((argv[1][0] < 'A' || argv[1][0] > 'Z') || |
| (argv[1][1] < 'A' || argv[1][1] > 'Z')) && |
| (argv[1][0] != '0' || argv[1][1] != '0')) { |
| shell_fprintf(sh, SHELL_WARNING, "Invalid reg domain %c%c\n", argv[1][0], |
| argv[1][1]); |
| return -ENOEXEC; |
| } |
| regd.country_code[0] = argv[1][0]; |
| regd.country_code[1] = argv[1][1]; |
| |
| if (argc == 3) { |
| if (strncmp(argv[2], "-f", 2) == 0) { |
| regd.force = true; |
| } else { |
| shell_fprintf(sh, SHELL_WARNING, "Invalid option %s\n", argv[2]); |
| return -ENOEXEC; |
| } |
| } |
| } else { |
| shell_help(sh); |
| return -ENOEXEC; |
| } |
| |
| ret = net_mgmt(NET_REQUEST_WIFI_REG_DOMAIN, iface, |
| ®d, sizeof(regd)); |
| if (ret) { |
| shell_fprintf(sh, SHELL_WARNING, "Cannot %s Regulatory domain: %d\n", |
| regd.oper == WIFI_MGMT_GET ? "get" : "set", ret); |
| return -ENOEXEC; |
| } |
| |
| if (regd.oper == WIFI_MGMT_GET) { |
| shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi Regulatory domain is: %c%c\n", |
| regd.country_code[0], regd.country_code[1]); |
| } else { |
| shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi Regulatory domain set to: %c%c\n", |
| regd.country_code[0], regd.country_code[1]); |
| } |
| |
| return 0; |
| } |
| |
| SHELL_STATIC_SUBCMD_SET_CREATE(wifi_cmd_ap, |
| SHELL_CMD(disable, NULL, |
| "Disable Access Point mode", |
| cmd_wifi_ap_disable), |
| SHELL_CMD(enable, NULL, "<SSID> <SSID length> [channel] [PSK]", |
| cmd_wifi_ap_enable), |
| SHELL_SUBCMD_SET_END |
| ); |
| |
| SHELL_STATIC_SUBCMD_SET_CREATE(wifi_twt_ops, |
| SHELL_CMD(quick_setup, NULL, " Start a TWT flow with defaults:\n" |
| "<twt_wake_interval_ms: 1-256ms> <twt_interval_ms: 1ms-2^32ms>\n", |
| cmd_wifi_twt_setup_quick), |
| SHELL_CMD(setup, NULL, " Start a TWT flow:\n" |
| "<negotiation_type, 0: Individual, 1: Broadcast, 2: Wake TBTT>\n" |
| "<setup_cmd: 0: Request, 1: Suggest, 2: Demand>\n" |
| "<dialog_token: 1-255> <flow_id: 1-255> <responder: 0/1> <trigger: 0/1> <implicit:0/1> " |
| "<announce: 0/1> <twt_wake_interval_ms: 1-256ms> <twt_interval_ms: 1ms-2^32ms>\n", |
| cmd_wifi_twt_setup), |
| SHELL_CMD(teardown, NULL, " Teardown a TWT flow:\n" |
| "<negotiation_type, 0: Individual, 1: Broadcast, 2: Wake TBTT>\n" |
| "<setup_cmd: 0: Request, 1: Suggest, 2: Demand>\n" |
| "<dialog_token: 1-255> <flow_id: 1-255>\n", |
| cmd_wifi_twt_teardown), |
| SHELL_CMD(teardown_all, NULL, " Teardown all TWT flows\n", |
| cmd_wifi_twt_teardown_all), |
| SHELL_SUBCMD_SET_END |
| ); |
| |
| SHELL_STATIC_SUBCMD_SET_CREATE(wifi_commands, |
| SHELL_CMD(ap, &wifi_cmd_ap, "Access Point mode commands", NULL), |
| SHELL_CMD(connect, NULL, |
| "Connect to a Wi-Fi AP\n" |
| "\"<SSID>\"\n" |
| "<channel number (optional), 0 means all>\n" |
| "<PSK (optional: valid only for secure SSIDs)>\n" |
| "<Security type (optional: valid only for secure SSIDs)>\n" |
| "0:None, 1:PSK, 2:PSK-256, 3:SAE\n" |
| "<MFP (optional: needs security type to be specified)>\n" |
| ": 0:Disable, 1:Optional, 2:Required", |
| cmd_wifi_connect), |
| SHELL_CMD(disconnect, NULL, "Disconnect from the Wi-Fi AP", |
| cmd_wifi_disconnect), |
| SHELL_CMD(ps, NULL, "Configure Wi-Fi power save on/off, no arguments will dump config", |
| cmd_wifi_ps), |
| SHELL_CMD(ps_mode, NULL, "Configure Wi-Fi power save mode legacy/wmm", cmd_wifi_ps_mode), |
| SHELL_CMD(scan, NULL, "Scan for Wi-Fi APs", cmd_wifi_scan), |
| SHELL_CMD(statistics, NULL, "Wi-Fi interface statistics", cmd_wifi_stats), |
| SHELL_CMD(status, NULL, "Status of the Wi-Fi interface", cmd_wifi_status), |
| SHELL_CMD(twt, &wifi_twt_ops, "Manage TWT flows", NULL), |
| SHELL_CMD(ap, &wifi_cmd_ap, "Access Point mode commands", NULL), |
| SHELL_CMD(reg_domain, NULL, |
| "Set or Get Wi-Fi regulatory domain\n" |
| "Usage: wifi reg_domain [ISO/IEC 3166-1 alpha2] [-f]\n" |
| "-f: Force to use this regulatory hint over any other regulatory hints\n" |
| "Note: This may cause regulatory compliance issues, use it at your own risk.", |
| cmd_wifi_reg_domain), |
| SHELL_CMD(ps_timeout, NULL, |
| "Configure Wi-Fi power save inactivity timer(in ms)", |
| cmd_wifi_ps_timeout), |
| SHELL_SUBCMD_SET_END |
| ); |
| |
| SHELL_CMD_REGISTER(wifi, &wifi_commands, "Wi-Fi commands", NULL); |
| |
| static int wifi_shell_init(const struct device *unused) |
| { |
| ARG_UNUSED(unused); |
| |
| context.sh = NULL; |
| context.all = 0U; |
| scan_result = 0U; |
| |
| net_mgmt_init_event_callback(&wifi_shell_mgmt_cb, |
| wifi_mgmt_event_handler, |
| WIFI_SHELL_MGMT_EVENTS); |
| |
| net_mgmt_add_event_callback(&wifi_shell_mgmt_cb); |
| |
| return 0; |
| } |
| |
| SYS_INIT(wifi_shell_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); |