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,
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
|
@ -3404,6 +3414,20 @@ struct wpa_driver_ops {
|
|||
*/
|
||||
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
|
||||
* @priv: Private driver interface data
|
||||
|
|
|
@ -5804,9 +5804,10 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type(
|
|||
return NL80211_IFTYPE_P2P_DEVICE;
|
||||
case WPA_IF_MESH:
|
||||
return NL80211_IFTYPE_MESH_POINT;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
struct i802_bss *bss = priv;
|
||||
|
@ -8663,5 +8805,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
|
|||
.del_tx_ts = nl80211_del_ts,
|
||||
.do_acs = wpa_driver_do_acs,
|
||||
.set_band = nl80211_set_band,
|
||||
.get_pref_freq_list = nl80211_get_pref_freq_list,
|
||||
.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 p2p_go_ctwindow_supported:1;
|
||||
unsigned int setband_vendor_cmd_avail:1;
|
||||
unsigned int get_pref_freq_list:1;
|
||||
unsigned int set_prob_oper_freq:1;
|
||||
|
||||
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:
|
||||
drv->get_features_vendor_cmd_avail = 1;
|
||||
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:
|
||||
drv->set_prob_oper_freq = 1;
|
||||
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,
|
||||
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) {
|
||||
if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
|
||||
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 {
|
||||
os_memcpy(reply, "UNKNOWN COMMAND\n", 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);
|
||||
}
|
||||
|
||||
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,
|
||||
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 {
|
||||
cli_cmd_flag_none = 0x00,
|
||||
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 "
|
||||
"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 }
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue