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:
Ahmad Kholaif 2015-07-23 16:32:58 -07:00 committed by Jouni Malinen
parent 7c813acf9c
commit 983422088f
7 changed files with 243 additions and 1 deletions

View File

@ -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

View File

@ -5804,8 +5804,9 @@ 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;
}
return -1;
}
@ -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, &param);
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,
};

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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)
{

View File

@ -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 }
};