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 <krishna.t@nordicsemi.no>
This commit is contained in:
Krishna T 2023-01-06 01:13:47 +05:30 committed by Fabio Baltieri
commit fadb1fd168
4 changed files with 110 additions and 0 deletions

View file

@ -12,6 +12,8 @@
#ifndef ZEPHYR_INCLUDE_NET_WIFI_H_ #ifndef ZEPHYR_INCLUDE_NET_WIFI_H_
#define 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 /* Not having support for legacy types is deliberate to enforce
* higher security. * higher security.
*/ */

View file

@ -40,6 +40,7 @@ enum net_request_wifi_cmd {
NET_REQUEST_WIFI_CMD_PS_MODE, NET_REQUEST_WIFI_CMD_PS_MODE,
NET_REQUEST_WIFI_CMD_TWT, NET_REQUEST_WIFI_CMD_TWT,
NET_REQUEST_WIFI_CMD_PS_CONFIG, NET_REQUEST_WIFI_CMD_PS_CONFIG,
NET_REQUEST_WIFI_CMD_REG_DOMAIN,
}; };
#define NET_REQUEST_WIFI_SCAN \ #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_WIFI_BASE | NET_REQUEST_WIFI_CMD_PS_CONFIG)
NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_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 { enum net_event_wifi_cmd {
NET_EVENT_WIFI_CMD_SCAN_RESULT = 1, NET_EVENT_WIFI_CMD_SCAN_RESULT = 1,
@ -231,6 +236,19 @@ struct wifi_ps_config {
char num_twt_flows; 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 <zephyr/net/net_if.h> #include <zephyr/net/net_if.h>
typedef void (*scan_result_cb_t)(struct net_if *iface, int status, 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_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 (*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 (*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 /* Make sure that the network interface API is properly setup inside

View file

@ -293,3 +293,24 @@ void wifi_mgmt_raise_twt_event(struct net_if *iface, struct wifi_twt_params *twt
iface, twt_params, iface, twt_params,
sizeof(struct wifi_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);

View file

@ -735,6 +735,67 @@ static int cmd_wifi_ap_disable(const struct shell *sh, size_t argc,
return 0; 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,
&regd, 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_STATIC_SUBCMD_SET_CREATE(wifi_cmd_ap,
SHELL_CMD(disable, NULL, SHELL_CMD(disable, NULL,
"Disable Access Point mode", "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(statistics, NULL, "Wi-Fi interface statistics", cmd_wifi_stats),
SHELL_CMD(status, NULL, "Status of the Wi-Fi interface", cmd_wifi_status), 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(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 SHELL_SUBCMD_SET_END
); );