struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
- struct nlattr *acl;
+ struct nl_msg *acl;
unsigned int i;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
params->acl_policy ? "Accept" : "Deny", params->num_mac_acl);
+ acl = nlmsg_alloc();
+ if (!acl)
+ return -ENOMEM;
+ for (i = 0; i < params->num_mac_acl; i++) {
+ if (nla_put(acl, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
+ nlmsg_free(acl);
+ return -ENOMEM;
+ }
+ }
+
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MAC_ACL)) ||
nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) ||
- (acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS)) == NULL) {
+ nla_put_nested(msg, NL80211_ATTR_MAC_ADDRS, acl)) {
nlmsg_free(msg);
+ nlmsg_free(acl);
return -ENOMEM;
}
-
- for (i = 0; i < params->num_mac_acl; i++) {
- if (nla_put(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
- nlmsg_free(msg);
- return -ENOMEM;
- }
- }
-
- nla_nest_end(msg, acl);
+ nlmsg_free(acl);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret) {
return NL80211_IFTYPE_P2P_DEVICE;
case WPA_IF_MESH:
return NL80211_IFTYPE_MESH_POINT;
+ default:
+ return -1;
}
- return -1;
}
struct wpa_driver_scan_params *params)
{
struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ /*
+ * Do a vendor specific scan if possible. If only_new_results is
+ * set, do a normal scan since a kernel (cfg80211) BSS cache flush
+ * cannot be achieved through a vendor scan. The below condition may
+ * need to be modified if new scan flags are added in the future whose
+ * functionality can only be achieved through a normal scan.
+ */
+ if (drv->scan_vendor_cmd_avail && !params->only_new_results)
+ return wpa_driver_nl80211_vendor_scan(bss, params);
return wpa_driver_nl80211_scan(bss, params);
}
}
+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;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ struct nlattr *params;
+
+ if (!drv->set_prob_oper_freq)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set P2P probable operating freq %u for ifindex %d",
+ freq, bss->ifindex);
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
+ QCA_IFACE_TYPE_P2P_CLIENT) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
+ freq)) {
+ wpa_printf(MSG_ERROR,
+ "%s: err in adding vendor_cmd and vendor_data",
+ __func__);
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
+ __func__);
+ return ret;
+ }
+ nlmsg_free(msg);
+ return 0;
+}
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
.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,
};