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