From fadb1fd168753bb17f9f919d640bbb10685323a9 Mon Sep 17 00:00:00 2001 From: Krishna T Date: Fri, 6 Jan 2023 01:13:47 +0530 Subject: [PATCH] net: wifi: Add support for regulatory domain configuration Wi-Fi bands are regulated by a governing body depending on operating country, add support for the user to provide a country of operation as a hint to the Wi-Fi chipset. Ideally if the chipset supports this is all handled internally, in that case "get" is useful but for testing and other usecases add a "set" as well, similar to "iw reg set" or "country_code=" configuration in hostapd/wpa_supplicant in Linux world. This add a new offload API operation "reg_domain" that can be used to either get or set the regulatory information. The validation is left to the underlying chipset, shell only does basic validation, (XY/00). This is just a regulatory hint to the chipset, there could be other regulatory hints e.g., beacon that can override this configuration, so, an additional option to force this setting despite other hints is also given for testing purposes. FYI, the standard database used is [1]. [1] - https://git.kernel.org/pub/scm/linux/kernel/git/sforshee/wireless-regdb.git/tree/db.txt Signed-off-by: Krishna T --- include/zephyr/net/wifi.h | 2 + include/zephyr/net/wifi_mgmt.h | 19 +++++++++ subsys/net/l2/wifi/wifi_mgmt.c | 21 ++++++++++ subsys/net/l2/wifi/wifi_shell.c | 68 +++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/include/zephyr/net/wifi.h b/include/zephyr/net/wifi.h index 44e8f39eb9f..1d6eac68e78 100644 --- a/include/zephyr/net/wifi.h +++ b/include/zephyr/net/wifi.h @@ -12,6 +12,8 @@ #ifndef ZEPHYR_INCLUDE_NET_WIFI_H_ #define ZEPHYR_INCLUDE_NET_WIFI_H_ +#define WIFI_COUNTRY_CODE_LEN 2 + /* Not having support for legacy types is deliberate to enforce * higher security. */ diff --git a/include/zephyr/net/wifi_mgmt.h b/include/zephyr/net/wifi_mgmt.h index 924f4630d30..f916b497ed8 100644 --- a/include/zephyr/net/wifi_mgmt.h +++ b/include/zephyr/net/wifi_mgmt.h @@ -40,6 +40,7 @@ enum net_request_wifi_cmd { NET_REQUEST_WIFI_CMD_PS_MODE, NET_REQUEST_WIFI_CMD_TWT, NET_REQUEST_WIFI_CMD_PS_CONFIG, + NET_REQUEST_WIFI_CMD_REG_DOMAIN, }; #define NET_REQUEST_WIFI_SCAN \ @@ -91,6 +92,10 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_TWT); (_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_PS_CONFIG) NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_PS_CONFIG); +#define NET_REQUEST_WIFI_REG_DOMAIN \ + (_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_REG_DOMAIN) + +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_REG_DOMAIN); enum net_event_wifi_cmd { NET_EVENT_WIFI_CMD_SCAN_RESULT = 1, @@ -231,6 +236,19 @@ struct wifi_ps_config { char num_twt_flows; }; +/* Generic get/set operation for any command*/ +enum wifi_mgmt_op { + WIFI_MGMT_GET = 0, + WIFI_MGMT_SET = 1, +}; + +struct wifi_reg_domain { + enum wifi_mgmt_op oper; + /* Ignore all other regulatory hints */ + bool force; + uint8_t country_code[WIFI_COUNTRY_CODE_LEN]; +}; + #include typedef void (*scan_result_cb_t)(struct net_if *iface, int status, @@ -268,6 +286,7 @@ struct net_wifi_mgmt_offload { int (*set_power_save_mode)(const struct device *dev, struct wifi_ps_mode_params *params); int (*set_twt)(const struct device *dev, struct wifi_twt_params *params); int (*get_power_save_config)(const struct device *dev, struct wifi_ps_config *config); + int (*reg_domain)(const struct device *dev, struct wifi_reg_domain *reg_domain); }; /* Make sure that the network interface API is properly setup inside diff --git a/subsys/net/l2/wifi/wifi_mgmt.c b/subsys/net/l2/wifi/wifi_mgmt.c index dceafc81dec..20ca63d9d5f 100644 --- a/subsys/net/l2/wifi/wifi_mgmt.c +++ b/subsys/net/l2/wifi/wifi_mgmt.c @@ -293,3 +293,24 @@ void wifi_mgmt_raise_twt_event(struct net_if *iface, struct wifi_twt_params *twt iface, twt_params, sizeof(struct wifi_twt_params)); } + +static int wifi_reg_domain(uint32_t mgmt_request, struct net_if *iface, + void *data, size_t len) +{ + const struct device *dev = net_if_get_device(iface); + struct net_wifi_mgmt_offload *off_api = + (struct net_wifi_mgmt_offload *) dev->api; + struct wifi_reg_domain *reg_domain = data; + + if (off_api == NULL || off_api->reg_domain == NULL) { + return -ENOTSUP; + } + + if (!data || len != sizeof(*reg_domain)) { + return -EINVAL; + } + + return off_api->reg_domain(dev, reg_domain); +} + +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_REG_DOMAIN, wifi_reg_domain); diff --git a/subsys/net/l2/wifi/wifi_shell.c b/subsys/net/l2/wifi/wifi_shell.c index 3e6a61529b6..7ee271a6d96 100644 --- a/subsys/net/l2/wifi/wifi_shell.c +++ b/subsys/net/l2/wifi/wifi_shell.c @@ -735,6 +735,67 @@ static int cmd_wifi_ap_disable(const struct shell *sh, size_t argc, 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", @@ -785,6 +846,13 @@ SHELL_STATIC_SUBCMD_SET_CREATE(wifi_commands, 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_SUBCMD_SET_END );