nl80211: Add means to query preferred channels
Extend the QCA vendor specific nl80211 interface to query the preferred frequency list from driver and add a new wpa_cli command to query this information. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
7c813acf9c
commit
983422088f
7 changed files with 243 additions and 1 deletions
|
@ -1404,6 +1404,16 @@ enum wpa_driver_if_type {
|
||||||
* WPA_IF_MESH - Mesh interface
|
* WPA_IF_MESH - Mesh interface
|
||||||
*/
|
*/
|
||||||
WPA_IF_MESH,
|
WPA_IF_MESH,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
|
||||||
|
*/
|
||||||
|
WPA_IF_TDLS,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WPA_IF_IBSS - IBSS interface (used for pref freq only)
|
||||||
|
*/
|
||||||
|
WPA_IF_IBSS,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wpa_init_params {
|
struct wpa_init_params {
|
||||||
|
@ -3404,6 +3414,20 @@ struct wpa_driver_ops {
|
||||||
*/
|
*/
|
||||||
int (*set_band)(void *priv, enum set_band band);
|
int (*set_band)(void *priv, enum set_band band);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_pref_freq_list - Get preferred frequency list for an interface
|
||||||
|
* @priv: Private driver interface data
|
||||||
|
* @if_type: Interface type
|
||||||
|
* @num: Number of channels
|
||||||
|
* @freq_list: Preferred channel frequency list encoded in MHz values
|
||||||
|
* Returns 0 on success, -1 on failure
|
||||||
|
*
|
||||||
|
* This command can be used to query the preferred frequency list from
|
||||||
|
* the driver specific to a particular interface type.
|
||||||
|
*/
|
||||||
|
int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
|
||||||
|
unsigned int *num, unsigned int *freq_list);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set_prob_oper_freq - Indicate probable P2P operating channel
|
* set_prob_oper_freq - Indicate probable P2P operating channel
|
||||||
* @priv: Private driver interface data
|
* @priv: Private driver interface data
|
||||||
|
|
|
@ -5804,9 +5804,10 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type(
|
||||||
return NL80211_IFTYPE_P2P_DEVICE;
|
return NL80211_IFTYPE_P2P_DEVICE;
|
||||||
case WPA_IF_MESH:
|
case WPA_IF_MESH:
|
||||||
return NL80211_IFTYPE_MESH_POINT;
|
return NL80211_IFTYPE_MESH_POINT;
|
||||||
}
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
|
static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
|
||||||
|
@ -8509,6 +8510,147 @@ static int nl80211_set_band(void *priv, enum set_band band)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct nl80211_pcl {
|
||||||
|
unsigned int num;
|
||||||
|
unsigned int *freq_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
|
||||||
|
{
|
||||||
|
struct nlattr *tb[NL80211_ATTR_MAX + 1];
|
||||||
|
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
|
||||||
|
struct nl80211_pcl *param = arg;
|
||||||
|
struct nlattr *nl_vend, *attr;
|
||||||
|
enum qca_iface_type iface_type;
|
||||||
|
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
|
||||||
|
unsigned int num, max_num;
|
||||||
|
u32 *freqs;
|
||||||
|
|
||||||
|
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||||
|
genlmsg_attrlen(gnlh, 0), NULL);
|
||||||
|
|
||||||
|
nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
|
||||||
|
if (!nl_vend)
|
||||||
|
return NL_SKIP;
|
||||||
|
|
||||||
|
nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
|
||||||
|
nla_data(nl_vend), nla_len(nl_vend), NULL);
|
||||||
|
|
||||||
|
attr = tb_vendor[
|
||||||
|
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE];
|
||||||
|
if (!attr) {
|
||||||
|
wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found");
|
||||||
|
param->num = 0;
|
||||||
|
return NL_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface_type = (enum qca_iface_type) nla_get_u32(attr);
|
||||||
|
wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d",
|
||||||
|
iface_type);
|
||||||
|
|
||||||
|
attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST];
|
||||||
|
if (!attr) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"nl80211: preferred_freq_list couldn't be found");
|
||||||
|
param->num = 0;
|
||||||
|
return NL_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* param->num has the maximum number of entries for which there
|
||||||
|
* is room in the freq_list provided by the caller.
|
||||||
|
*/
|
||||||
|
freqs = nla_data(attr);
|
||||||
|
max_num = nla_len(attr) / sizeof(u32);
|
||||||
|
if (max_num > param->num)
|
||||||
|
max_num = param->num;
|
||||||
|
for (num = 0; num < max_num; num++)
|
||||||
|
param->freq_list[num] = freqs[num];
|
||||||
|
param->num = num;
|
||||||
|
|
||||||
|
return NL_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int nl80211_get_pref_freq_list(void *priv,
|
||||||
|
enum wpa_driver_if_type if_type,
|
||||||
|
unsigned int *num,
|
||||||
|
unsigned int *freq_list)
|
||||||
|
{
|
||||||
|
struct i802_bss *bss = priv;
|
||||||
|
struct wpa_driver_nl80211_data *drv = bss->drv;
|
||||||
|
struct nl_msg *msg;
|
||||||
|
int ret;
|
||||||
|
unsigned int i;
|
||||||
|
struct nlattr *params;
|
||||||
|
struct nl80211_pcl param;
|
||||||
|
enum qca_iface_type iface_type;
|
||||||
|
|
||||||
|
if (!drv->get_pref_freq_list)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
switch (if_type) {
|
||||||
|
case WPA_IF_STATION:
|
||||||
|
iface_type = QCA_IFACE_TYPE_STA;
|
||||||
|
break;
|
||||||
|
case WPA_IF_AP_BSS:
|
||||||
|
iface_type = QCA_IFACE_TYPE_AP;
|
||||||
|
break;
|
||||||
|
case WPA_IF_P2P_GO:
|
||||||
|
iface_type = QCA_IFACE_TYPE_P2P_GO;
|
||||||
|
break;
|
||||||
|
case WPA_IF_P2P_CLIENT:
|
||||||
|
iface_type = QCA_IFACE_TYPE_P2P_CLIENT;
|
||||||
|
break;
|
||||||
|
case WPA_IF_IBSS:
|
||||||
|
iface_type = QCA_IFACE_TYPE_IBSS;
|
||||||
|
break;
|
||||||
|
case WPA_IF_TDLS:
|
||||||
|
iface_type = QCA_IFACE_TYPE_TDLS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
param.num = *num;
|
||||||
|
param.freq_list = freq_list;
|
||||||
|
|
||||||
|
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
|
||||||
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
|
||||||
|
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
|
||||||
|
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
|
||||||
|
QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST) ||
|
||||||
|
!(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
|
||||||
|
nla_put_u32(msg,
|
||||||
|
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
|
||||||
|
iface_type)) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"%s: err in adding vendor_cmd and vendor_data",
|
||||||
|
__func__);
|
||||||
|
nlmsg_free(msg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
nla_nest_end(msg, params);
|
||||||
|
|
||||||
|
os_memset(freq_list, 0, *num * sizeof(freq_list[0]));
|
||||||
|
ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, ¶m);
|
||||||
|
if (ret) {
|
||||||
|
wpa_printf(MSG_ERROR,
|
||||||
|
"%s: err in send_and_recv_msgs", __func__);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
*num = param.num;
|
||||||
|
|
||||||
|
for (i = 0; i < *num; i++) {
|
||||||
|
wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d",
|
||||||
|
i, freq_list[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
|
static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
|
||||||
{
|
{
|
||||||
struct i802_bss *bss = priv;
|
struct i802_bss *bss = priv;
|
||||||
|
@ -8663,5 +8805,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
|
||||||
.del_tx_ts = nl80211_del_ts,
|
.del_tx_ts = nl80211_del_ts,
|
||||||
.do_acs = wpa_driver_do_acs,
|
.do_acs = wpa_driver_do_acs,
|
||||||
.set_band = nl80211_set_band,
|
.set_band = nl80211_set_band,
|
||||||
|
.get_pref_freq_list = nl80211_get_pref_freq_list,
|
||||||
.set_prob_oper_freq = nl80211_set_prob_oper_freq,
|
.set_prob_oper_freq = nl80211_set_prob_oper_freq,
|
||||||
};
|
};
|
||||||
|
|
|
@ -146,6 +146,7 @@ struct wpa_driver_nl80211_data {
|
||||||
unsigned int set_rekey_offload:1;
|
unsigned int set_rekey_offload:1;
|
||||||
unsigned int p2p_go_ctwindow_supported:1;
|
unsigned int p2p_go_ctwindow_supported:1;
|
||||||
unsigned int setband_vendor_cmd_avail:1;
|
unsigned int setband_vendor_cmd_avail:1;
|
||||||
|
unsigned int get_pref_freq_list:1;
|
||||||
unsigned int set_prob_oper_freq:1;
|
unsigned int set_prob_oper_freq:1;
|
||||||
|
|
||||||
u64 remain_on_chan_cookie;
|
u64 remain_on_chan_cookie;
|
||||||
|
|
|
@ -589,6 +589,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
|
||||||
case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
|
case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
|
||||||
drv->get_features_vendor_cmd_avail = 1;
|
drv->get_features_vendor_cmd_avail = 1;
|
||||||
break;
|
break;
|
||||||
|
case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST:
|
||||||
|
drv->get_pref_freq_list = 1;
|
||||||
|
break;
|
||||||
case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL:
|
case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL:
|
||||||
drv->set_prob_oper_freq = 1;
|
drv->set_prob_oper_freq = 1;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -6654,6 +6654,53 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpas_ctrl_iface_get_pref_freq_list(
|
||||||
|
struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
unsigned int freq_list[100], num = 100, i;
|
||||||
|
int ret;
|
||||||
|
enum wpa_driver_if_type iface_type;
|
||||||
|
char *pos, *end;
|
||||||
|
|
||||||
|
pos = buf;
|
||||||
|
end = buf + buflen;
|
||||||
|
|
||||||
|
/* buf: "<interface_type>" */
|
||||||
|
if (os_strcmp(cmd, "STATION") == 0)
|
||||||
|
iface_type = WPA_IF_STATION;
|
||||||
|
else if (os_strcmp(cmd, "AP") == 0)
|
||||||
|
iface_type = WPA_IF_AP_BSS;
|
||||||
|
else if (os_strcmp(cmd, "P2P_GO") == 0)
|
||||||
|
iface_type = WPA_IF_P2P_GO;
|
||||||
|
else if (os_strcmp(cmd, "P2P_CLIENT") == 0)
|
||||||
|
iface_type = WPA_IF_P2P_CLIENT;
|
||||||
|
else if (os_strcmp(cmd, "IBSS") == 0)
|
||||||
|
iface_type = WPA_IF_IBSS;
|
||||||
|
else if (os_strcmp(cmd, "TDLS") == 0)
|
||||||
|
iface_type = WPA_IF_TDLS;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)",
|
||||||
|
iface_type, buf);
|
||||||
|
|
||||||
|
ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list);
|
||||||
|
if (ret)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
ret = os_snprintf(pos, end - pos, "%s%u",
|
||||||
|
i > 0 ? "," : "", freq_list[i]);
|
||||||
|
if (os_snprintf_error(end - pos, ret))
|
||||||
|
return -1;
|
||||||
|
pos += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos - buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
|
static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
|
||||||
size_t buflen)
|
size_t buflen)
|
||||||
{
|
{
|
||||||
|
@ -8750,6 +8797,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
|
||||||
} else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
|
} else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
|
||||||
if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
|
if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
|
||||||
reply_len = -1;
|
reply_len = -1;
|
||||||
|
} else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
|
||||||
|
reply_len = wpas_ctrl_iface_get_pref_freq_list(
|
||||||
|
wpa_s, buf + 19, reply, reply_size);
|
||||||
} else {
|
} else {
|
||||||
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
|
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
|
||||||
reply_len = 16;
|
reply_len = 16;
|
||||||
|
|
|
@ -893,6 +893,17 @@ static inline int wpa_drv_setband(struct wpa_supplicant *wpa_s,
|
||||||
return wpa_s->driver->set_band(wpa_s->drv_priv, band);
|
return wpa_s->driver->set_band(wpa_s->drv_priv, band);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s,
|
||||||
|
enum wpa_driver_if_type if_type,
|
||||||
|
unsigned int *num,
|
||||||
|
unsigned int *freq_list)
|
||||||
|
{
|
||||||
|
if (!wpa_s->driver->get_pref_freq_list)
|
||||||
|
return 0;
|
||||||
|
return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type,
|
||||||
|
num, freq_list);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s,
|
static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s,
|
||||||
unsigned int freq)
|
unsigned int freq)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2801,6 +2801,13 @@ static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
return wpa_cli_cmd(ctrl, "GET_PREF_FREQ_LIST", 1, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
enum wpa_cli_cmd_flags {
|
enum wpa_cli_cmd_flags {
|
||||||
cli_cmd_flag_none = 0x00,
|
cli_cmd_flag_none = 0x00,
|
||||||
cli_cmd_flag_sensitive = 0x01
|
cli_cmd_flag_sensitive = 0x01
|
||||||
|
@ -3355,6 +3362,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
|
||||||
"<scan|sched|pno|all> enable=<0/1> [addr=mac-address "
|
"<scan|sched|pno|all> enable=<0/1> [addr=mac-address "
|
||||||
"mask=mac-address-mask] = scan MAC randomization"
|
"mask=mac-address-mask] = scan MAC randomization"
|
||||||
},
|
},
|
||||||
|
{ "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
|
||||||
|
cli_cmd_flag_none,
|
||||||
|
"<interface type> = retrieve preferred freq list for the specified interface type" },
|
||||||
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
|
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue