|
@@ -209,7 +209,7 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
|
|
}
|
|
}
|
|
|
|
|
|
/* policy for the attributes */
|
|
/* policy for the attributes */
|
|
-static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
|
|
|
|
|
+static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
|
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
|
|
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
|
|
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
|
|
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
|
|
.len = 20-1 },
|
|
.len = 20-1 },
|
|
@@ -388,13 +388,14 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
|
[NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
|
|
[NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
|
|
[NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
|
|
[NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
|
|
[NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
|
|
[NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
|
|
- [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG },
|
|
|
|
|
|
+ [NL80211_ATTR_SOCKET_OWNER] = { .type = NLA_FLAG },
|
|
[NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
|
|
[NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
|
|
[NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG },
|
|
[NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG },
|
|
[NL80211_ATTR_TSID] = { .type = NLA_U8 },
|
|
[NL80211_ATTR_TSID] = { .type = NLA_U8 },
|
|
[NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 },
|
|
[NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 },
|
|
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
|
|
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
|
|
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
|
|
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
|
|
|
|
+ [NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
|
|
};
|
|
};
|
|
|
|
|
|
/* policy for the key attributes */
|
|
/* policy for the key attributes */
|
|
@@ -428,6 +429,7 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
|
|
[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
|
|
[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
|
|
[NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
|
|
[NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
|
|
[NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
|
|
[NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
|
|
|
|
+ [NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_NESTED },
|
|
};
|
|
};
|
|
|
|
|
|
static const struct nla_policy
|
|
static const struct nla_policy
|
|
@@ -1088,6 +1090,8 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
|
|
if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
|
|
if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
|
|
return -ENOBUFS;
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
+ /* TODO: send wowlan net detect */
|
|
|
|
+
|
|
nla_nest_end(msg, nl_wowlan);
|
|
nla_nest_end(msg, nl_wowlan);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -2341,12 +2345,16 @@ static int nl80211_send_chandef(struct sk_buff *msg,
|
|
|
|
|
|
static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
|
|
static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
|
|
struct cfg80211_registered_device *rdev,
|
|
struct cfg80211_registered_device *rdev,
|
|
- struct wireless_dev *wdev)
|
|
|
|
|
|
+ struct wireless_dev *wdev, bool removal)
|
|
{
|
|
{
|
|
struct net_device *dev = wdev->netdev;
|
|
struct net_device *dev = wdev->netdev;
|
|
|
|
+ u8 cmd = NL80211_CMD_NEW_INTERFACE;
|
|
void *hdr;
|
|
void *hdr;
|
|
|
|
|
|
- hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_INTERFACE);
|
|
|
|
|
|
+ if (removal)
|
|
|
|
+ cmd = NL80211_CMD_DEL_INTERFACE;
|
|
|
|
+
|
|
|
|
+ hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
|
|
if (!hdr)
|
|
if (!hdr)
|
|
return -1;
|
|
return -1;
|
|
|
|
|
|
@@ -2413,7 +2421,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
|
|
}
|
|
}
|
|
if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
|
|
if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
- rdev, wdev) < 0) {
|
|
|
|
|
|
+ rdev, wdev, false) < 0) {
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
if_idx++;
|
|
if_idx++;
|
|
@@ -2441,7 +2449,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
|
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
|
- rdev, wdev) < 0) {
|
|
|
|
|
|
+ rdev, wdev, false) < 0) {
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
return -ENOBUFS;
|
|
return -ENOBUFS;
|
|
}
|
|
}
|
|
@@ -2587,7 +2595,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct vif_params params;
|
|
struct vif_params params;
|
|
struct wireless_dev *wdev;
|
|
struct wireless_dev *wdev;
|
|
- struct sk_buff *msg;
|
|
|
|
|
|
+ struct sk_buff *msg, *event;
|
|
int err;
|
|
int err;
|
|
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
|
|
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
|
|
u32 flags;
|
|
u32 flags;
|
|
@@ -2641,12 +2649,15 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
|
wdev = rdev_add_virtual_intf(rdev,
|
|
wdev = rdev_add_virtual_intf(rdev,
|
|
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
|
|
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
|
|
type, err ? NULL : &flags, ¶ms);
|
|
type, err ? NULL : &flags, ¶ms);
|
|
- if (IS_ERR(wdev)) {
|
|
|
|
|
|
+ if (WARN_ON(!wdev)) {
|
|
|
|
+ nlmsg_free(msg);
|
|
|
|
+ return -EPROTO;
|
|
|
|
+ } else if (IS_ERR(wdev)) {
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
return PTR_ERR(wdev);
|
|
return PTR_ERR(wdev);
|
|
}
|
|
}
|
|
|
|
|
|
- if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER])
|
|
|
|
|
|
+ if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
|
|
wdev->owner_nlportid = info->snd_portid;
|
|
wdev->owner_nlportid = info->snd_portid;
|
|
|
|
|
|
switch (type) {
|
|
switch (type) {
|
|
@@ -2682,11 +2693,25 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
|
}
|
|
}
|
|
|
|
|
|
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
|
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
|
- rdev, wdev) < 0) {
|
|
|
|
|
|
+ rdev, wdev, false) < 0) {
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
return -ENOBUFS;
|
|
return -ENOBUFS;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ event = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
+ if (event) {
|
|
|
|
+ if (nl80211_send_iface(event, 0, 0, 0,
|
|
|
|
+ rdev, wdev, false) < 0) {
|
|
|
|
+ nlmsg_free(event);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
|
|
|
|
+ event, 0, NL80211_MCGRP_CONFIG,
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+out:
|
|
return genlmsg_reply(msg, info);
|
|
return genlmsg_reply(msg, info);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2694,10 +2719,18 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
{
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
|
|
|
+ struct sk_buff *msg;
|
|
|
|
+ int status;
|
|
|
|
|
|
if (!rdev->ops->del_virtual_intf)
|
|
if (!rdev->ops->del_virtual_intf)
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
+ if (msg && nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, true) < 0) {
|
|
|
|
+ nlmsg_free(msg);
|
|
|
|
+ msg = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* If we remove a wireless device without a netdev then clear
|
|
* If we remove a wireless device without a netdev then clear
|
|
* user_ptr[1] so that nl80211_post_doit won't dereference it
|
|
* user_ptr[1] so that nl80211_post_doit won't dereference it
|
|
@@ -2708,7 +2741,15 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
|
|
if (!wdev->netdev)
|
|
if (!wdev->netdev)
|
|
info->user_ptr[1] = NULL;
|
|
info->user_ptr[1] = NULL;
|
|
|
|
|
|
- return rdev_del_virtual_intf(rdev, wdev);
|
|
|
|
|
|
+ status = rdev_del_virtual_intf(rdev, wdev);
|
|
|
|
+ if (status >= 0 && msg)
|
|
|
|
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
|
|
|
|
+ msg, 0, NL80211_MCGRP_CONFIG,
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
+ else
|
|
|
|
+ nlmsg_free(msg);
|
|
|
|
+
|
|
|
|
+ return status;
|
|
}
|
|
}
|
|
|
|
|
|
static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
|
|
static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
|
|
@@ -4453,7 +4494,7 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
|
|
void *hdr;
|
|
void *hdr;
|
|
struct nlattr *pinfoattr;
|
|
struct nlattr *pinfoattr;
|
|
|
|
|
|
- hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
|
|
|
|
|
|
+ hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_MPATH);
|
|
if (!hdr)
|
|
if (!hdr)
|
|
return -1;
|
|
return -1;
|
|
|
|
|
|
@@ -5478,6 +5519,43 @@ static int validate_scan_freqs(struct nlattr *freqs)
|
|
return n_channels;
|
|
return n_channels;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nl80211_parse_random_mac(struct nlattr **attrs,
|
|
|
|
+ u8 *mac_addr, u8 *mac_addr_mask)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) {
|
|
|
|
+ memset(mac_addr, 0, ETH_ALEN);
|
|
|
|
+ memset(mac_addr_mask, 0, ETH_ALEN);
|
|
|
|
+ mac_addr[0] = 0x2;
|
|
|
|
+ mac_addr_mask[0] = 0x3;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* need both or none */
|
|
|
|
+ if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_MAC_MASK])
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ memcpy(mac_addr, nla_data(attrs[NL80211_ATTR_MAC]), ETH_ALEN);
|
|
|
|
+ memcpy(mac_addr_mask, nla_data(attrs[NL80211_ATTR_MAC_MASK]), ETH_ALEN);
|
|
|
|
+
|
|
|
|
+ /* don't allow or configure an mcast address */
|
|
|
|
+ if (!is_multicast_ether_addr(mac_addr_mask) ||
|
|
|
|
+ is_multicast_ether_addr(mac_addr))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * allow users to pass a MAC address that has bits set outside
|
|
|
|
+ * of the mask, but don't bother drivers with having to deal
|
|
|
|
+ * with such bits
|
|
|
|
+ */
|
|
|
|
+ for (i = 0; i < ETH_ALEN; i++)
|
|
|
|
+ mac_addr[i] &= mac_addr_mask[i];
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
|
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
{
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
@@ -5655,6 +5733,25 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
|
err = -EOPNOTSUPP;
|
|
err = -EOPNOTSUPP;
|
|
goto out_free;
|
|
goto out_free;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
|
|
|
|
+ if (!(wiphy->features &
|
|
|
|
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)) {
|
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (wdev->current_bss) {
|
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = nl80211_parse_random_mac(info->attrs,
|
|
|
|
+ request->mac_addr,
|
|
|
|
+ request->mac_addr_mask);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
request->no_cck =
|
|
request->no_cck =
|
|
@@ -5681,14 +5778,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
-static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
|
|
- struct genl_info *info)
|
|
|
|
|
|
+static struct cfg80211_sched_scan_request *
|
|
|
|
+nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
|
|
+ struct nlattr **attrs)
|
|
{
|
|
{
|
|
struct cfg80211_sched_scan_request *request;
|
|
struct cfg80211_sched_scan_request *request;
|
|
- struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
- struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct nlattr *attr;
|
|
struct nlattr *attr;
|
|
- struct wiphy *wiphy;
|
|
|
|
int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
|
|
int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
|
|
u32 interval;
|
|
u32 interval;
|
|
enum ieee80211_band band;
|
|
enum ieee80211_band band;
|
|
@@ -5696,38 +5791,32 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
|
|
struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
|
|
s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
|
|
s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
|
|
|
|
|
|
- if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
|
|
|
|
- !rdev->ops->sched_scan_start)
|
|
|
|
- return -EOPNOTSUPP;
|
|
|
|
-
|
|
|
|
- if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE]))
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
|
|
- if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
|
|
- interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
|
|
|
|
|
|
+ interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
|
|
if (interval == 0)
|
|
if (interval == 0)
|
|
- return -EINVAL;
|
|
|
|
-
|
|
|
|
- wiphy = &rdev->wiphy;
|
|
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
|
|
- if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
|
|
|
|
|
+ if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
|
n_channels = validate_scan_freqs(
|
|
n_channels = validate_scan_freqs(
|
|
- info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
|
|
|
|
|
|
+ attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
|
|
if (!n_channels)
|
|
if (!n_channels)
|
|
- return -EINVAL;
|
|
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
} else {
|
|
} else {
|
|
n_channels = ieee80211_get_num_supported_channels(wiphy);
|
|
n_channels = ieee80211_get_num_supported_channels(wiphy);
|
|
}
|
|
}
|
|
|
|
|
|
- if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
|
|
|
|
- nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
|
|
|
|
|
|
+ if (attrs[NL80211_ATTR_SCAN_SSIDS])
|
|
|
|
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
|
|
tmp)
|
|
tmp)
|
|
n_ssids++;
|
|
n_ssids++;
|
|
|
|
|
|
if (n_ssids > wiphy->max_sched_scan_ssids)
|
|
if (n_ssids > wiphy->max_sched_scan_ssids)
|
|
- return -EINVAL;
|
|
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
|
|
/*
|
|
/*
|
|
* First, count the number of 'real' matchsets. Due to an issue with
|
|
* First, count the number of 'real' matchsets. Due to an issue with
|
|
@@ -5738,9 +5827,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
* older userspace that treated a matchset with only the RSSI as the
|
|
* older userspace that treated a matchset with only the RSSI as the
|
|
* global RSSI for all other matchsets - if there are other matchsets.
|
|
* global RSSI for all other matchsets - if there are other matchsets.
|
|
*/
|
|
*/
|
|
- if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
|
|
|
|
|
|
+ if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
|
|
nla_for_each_nested(attr,
|
|
nla_for_each_nested(attr,
|
|
- info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
|
|
|
|
|
|
+ attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
|
|
tmp) {
|
|
tmp) {
|
|
struct nlattr *rssi;
|
|
struct nlattr *rssi;
|
|
|
|
|
|
@@ -5748,7 +5837,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
nla_data(attr), nla_len(attr),
|
|
nla_data(attr), nla_len(attr),
|
|
nl80211_match_policy);
|
|
nl80211_match_policy);
|
|
if (err)
|
|
if (err)
|
|
- return err;
|
|
|
|
|
|
+ return ERR_PTR(err);
|
|
/* add other standalone attributes here */
|
|
/* add other standalone attributes here */
|
|
if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
|
|
if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
|
|
n_match_sets++;
|
|
n_match_sets++;
|
|
@@ -5765,30 +5854,23 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
n_match_sets = 1;
|
|
n_match_sets = 1;
|
|
|
|
|
|
if (n_match_sets > wiphy->max_match_sets)
|
|
if (n_match_sets > wiphy->max_match_sets)
|
|
- return -EINVAL;
|
|
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
|
|
- if (info->attrs[NL80211_ATTR_IE])
|
|
|
|
- ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
|
|
+ if (attrs[NL80211_ATTR_IE])
|
|
|
|
+ ie_len = nla_len(attrs[NL80211_ATTR_IE]);
|
|
else
|
|
else
|
|
ie_len = 0;
|
|
ie_len = 0;
|
|
|
|
|
|
if (ie_len > wiphy->max_sched_scan_ie_len)
|
|
if (ie_len > wiphy->max_sched_scan_ie_len)
|
|
- return -EINVAL;
|
|
|
|
-
|
|
|
|
- if (rdev->sched_scan_req) {
|
|
|
|
- err = -EINPROGRESS;
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
|
|
request = kzalloc(sizeof(*request)
|
|
request = kzalloc(sizeof(*request)
|
|
+ sizeof(*request->ssids) * n_ssids
|
|
+ sizeof(*request->ssids) * n_ssids
|
|
+ sizeof(*request->match_sets) * n_match_sets
|
|
+ sizeof(*request->match_sets) * n_match_sets
|
|
+ sizeof(*request->channels) * n_channels
|
|
+ sizeof(*request->channels) * n_channels
|
|
+ ie_len, GFP_KERNEL);
|
|
+ ie_len, GFP_KERNEL);
|
|
- if (!request) {
|
|
|
|
- err = -ENOMEM;
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
|
|
+ if (!request)
|
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
if (n_ssids)
|
|
if (n_ssids)
|
|
request->ssids = (void *)&request->channels[n_channels];
|
|
request->ssids = (void *)&request->channels[n_channels];
|
|
@@ -5813,10 +5895,10 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
request->n_match_sets = n_match_sets;
|
|
request->n_match_sets = n_match_sets;
|
|
|
|
|
|
i = 0;
|
|
i = 0;
|
|
- if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
|
|
|
|
|
+ if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
|
/* user specified, bail out if channel not found */
|
|
/* user specified, bail out if channel not found */
|
|
nla_for_each_nested(attr,
|
|
nla_for_each_nested(attr,
|
|
- info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
|
|
|
|
|
|
+ attrs[NL80211_ATTR_SCAN_FREQUENCIES],
|
|
tmp) {
|
|
tmp) {
|
|
struct ieee80211_channel *chan;
|
|
struct ieee80211_channel *chan;
|
|
|
|
|
|
@@ -5862,8 +5944,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
request->n_channels = i;
|
|
request->n_channels = i;
|
|
|
|
|
|
i = 0;
|
|
i = 0;
|
|
- if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
|
|
|
|
- nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
|
|
|
|
|
|
+ if (attrs[NL80211_ATTR_SCAN_SSIDS]) {
|
|
|
|
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
|
|
tmp) {
|
|
tmp) {
|
|
if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
|
|
if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
|
|
err = -EINVAL;
|
|
err = -EINVAL;
|
|
@@ -5877,9 +5959,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
}
|
|
}
|
|
|
|
|
|
i = 0;
|
|
i = 0;
|
|
- if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
|
|
|
|
|
|
+ if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
|
|
nla_for_each_nested(attr,
|
|
nla_for_each_nested(attr,
|
|
- info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
|
|
|
|
|
|
+ attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
|
|
tmp) {
|
|
tmp) {
|
|
struct nlattr *ssid, *rssi;
|
|
struct nlattr *ssid, *rssi;
|
|
|
|
|
|
@@ -5934,36 +6016,88 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
if (ie_len) {
|
|
if (ie_len) {
|
|
request->ie_len = ie_len;
|
|
request->ie_len = ie_len;
|
|
memcpy((void *)request->ie,
|
|
memcpy((void *)request->ie,
|
|
- nla_data(info->attrs[NL80211_ATTR_IE]),
|
|
|
|
|
|
+ nla_data(attrs[NL80211_ATTR_IE]),
|
|
request->ie_len);
|
|
request->ie_len);
|
|
}
|
|
}
|
|
|
|
|
|
- if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
|
|
|
|
|
|
+ if (attrs[NL80211_ATTR_SCAN_FLAGS]) {
|
|
request->flags = nla_get_u32(
|
|
request->flags = nla_get_u32(
|
|
- info->attrs[NL80211_ATTR_SCAN_FLAGS]);
|
|
|
|
|
|
+ attrs[NL80211_ATTR_SCAN_FLAGS]);
|
|
if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
|
|
if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
|
|
!(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
|
|
!(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
|
|
err = -EOPNOTSUPP;
|
|
err = -EOPNOTSUPP;
|
|
goto out_free;
|
|
goto out_free;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
|
|
|
|
+ u32 flg = NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
|
|
|
|
+
|
|
|
|
+ if (!wdev) /* must be net-detect */
|
|
|
|
+ flg = NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
|
|
|
|
+
|
|
|
|
+ if (!(wiphy->features & flg)) {
|
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (wdev && wdev->current_bss) {
|
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = nl80211_parse_random_mac(attrs, request->mac_addr,
|
|
|
|
+ request->mac_addr_mask);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- request->dev = dev;
|
|
|
|
- request->wiphy = &rdev->wiphy;
|
|
|
|
request->interval = interval;
|
|
request->interval = interval;
|
|
request->scan_start = jiffies;
|
|
request->scan_start = jiffies;
|
|
|
|
|
|
- err = rdev_sched_scan_start(rdev, dev, request);
|
|
|
|
- if (!err) {
|
|
|
|
- rdev->sched_scan_req = request;
|
|
|
|
- nl80211_send_sched_scan(rdev, dev,
|
|
|
|
- NL80211_CMD_START_SCHED_SCAN);
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
|
|
+ return request;
|
|
|
|
|
|
out_free:
|
|
out_free:
|
|
kfree(request);
|
|
kfree(request);
|
|
-out:
|
|
|
|
|
|
+ return ERR_PTR(err);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
|
|
+ struct genl_info *info)
|
|
|
|
+{
|
|
|
|
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
+ struct net_device *dev = info->user_ptr[1];
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
|
|
|
|
+ !rdev->ops->sched_scan_start)
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+
|
|
|
|
+ if (rdev->sched_scan_req)
|
|
|
|
+ return -EINPROGRESS;
|
|
|
|
+
|
|
|
|
+ rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
|
|
|
|
+ info->attrs);
|
|
|
|
+ err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out_err;
|
|
|
|
+
|
|
|
|
+ err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out_free;
|
|
|
|
+
|
|
|
|
+ rdev->sched_scan_req->dev = dev;
|
|
|
|
+ rdev->sched_scan_req->wiphy = &rdev->wiphy;
|
|
|
|
+
|
|
|
|
+ nl80211_send_sched_scan(rdev, dev,
|
|
|
|
+ NL80211_CMD_START_SCHED_SCAN);
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+out_free:
|
|
|
|
+ kfree(rdev->sched_scan_req);
|
|
|
|
+out_err:
|
|
|
|
+ rdev->sched_scan_req = NULL;
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -8681,6 +8815,39 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev,
|
|
|
|
+ const struct wiphy_wowlan_support *wowlan,
|
|
|
|
+ struct nlattr *attr,
|
|
|
|
+ struct cfg80211_wowlan *trig)
|
|
|
|
+{
|
|
|
|
+ struct nlattr **tb;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ tb = kzalloc(NUM_NL80211_ATTR * sizeof(*tb), GFP_KERNEL);
|
|
|
|
+ if (!tb)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ if (!(wowlan->flags & WIPHY_WOWLAN_NET_DETECT)) {
|
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = nla_parse(tb, NL80211_ATTR_MAX,
|
|
|
|
+ nla_data(attr), nla_len(attr),
|
|
|
|
+ nl80211_policy);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ trig->nd_config = nl80211_parse_sched_scan(&rdev->wiphy, NULL, tb);
|
|
|
|
+ err = PTR_ERR_OR_ZERO(trig->nd_config);
|
|
|
|
+ if (err)
|
|
|
|
+ trig->nd_config = NULL;
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ kfree(tb);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
|
static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
{
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
@@ -8826,6 +8993,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
|
goto error;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) {
|
|
|
|
+ err = nl80211_parse_wowlan_nd(
|
|
|
|
+ rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT],
|
|
|
|
+ &new_triggers);
|
|
|
|
+ if (err)
|
|
|
|
+ goto error;
|
|
|
|
+ }
|
|
|
|
+
|
|
ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
|
|
ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
|
|
if (!ntrig) {
|
|
if (!ntrig) {
|
|
err = -ENOMEM;
|
|
err = -ENOMEM;
|
|
@@ -9658,6 +9833,98 @@ static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info)
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nl80211_tdls_channel_switch(struct sk_buff *skb,
|
|
|
|
+ struct genl_info *info)
|
|
|
|
+{
|
|
|
|
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
+ struct net_device *dev = info->user_ptr[1];
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ struct cfg80211_chan_def chandef = {};
|
|
|
|
+ const u8 *addr;
|
|
|
|
+ u8 oper_class;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ if (!rdev->ops->tdls_channel_switch ||
|
|
|
|
+ !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+
|
|
|
|
+ switch (dev->ieee80211_ptr->iftype) {
|
|
|
|
+ case NL80211_IFTYPE_STATION:
|
|
|
|
+ case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!info->attrs[NL80211_ATTR_MAC] ||
|
|
|
|
+ !info->attrs[NL80211_ATTR_OPER_CLASS])
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ err = nl80211_parse_chandef(rdev, info, &chandef);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Don't allow wide channels on the 2.4Ghz band, as per IEEE802.11-2012
|
|
|
|
+ * section 10.22.6.2.1. Disallow 5/10Mhz channels as well for now, the
|
|
|
|
+ * specification is not defined for them.
|
|
|
|
+ */
|
|
|
|
+ if (chandef.chan->band == IEEE80211_BAND_2GHZ &&
|
|
|
|
+ chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
|
|
|
|
+ chandef.width != NL80211_CHAN_WIDTH_20)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ /* we will be active on the TDLS link */
|
|
|
|
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, wdev->iftype))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ /* don't allow switching to DFS channels */
|
|
|
|
+ if (cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, wdev->iftype))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
+ oper_class = nla_get_u8(info->attrs[NL80211_ATTR_OPER_CLASS]);
|
|
|
|
+
|
|
|
|
+ wdev_lock(wdev);
|
|
|
|
+ err = rdev_tdls_channel_switch(rdev, dev, addr, oper_class, &chandef);
|
|
|
|
+ wdev_unlock(wdev);
|
|
|
|
+
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb,
|
|
|
|
+ struct genl_info *info)
|
|
|
|
+{
|
|
|
|
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
+ struct net_device *dev = info->user_ptr[1];
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ const u8 *addr;
|
|
|
|
+
|
|
|
|
+ if (!rdev->ops->tdls_channel_switch ||
|
|
|
|
+ !rdev->ops->tdls_cancel_channel_switch ||
|
|
|
|
+ !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+
|
|
|
|
+ switch (dev->ieee80211_ptr->iftype) {
|
|
|
|
+ case NL80211_IFTYPE_STATION:
|
|
|
|
+ case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
+
|
|
|
|
+ wdev_lock(wdev);
|
|
|
|
+ rdev_tdls_cancel_channel_switch(rdev, dev, addr);
|
|
|
|
+ wdev_unlock(wdev);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
#define NL80211_FLAG_NEED_WIPHY 0x01
|
|
#define NL80211_FLAG_NEED_WIPHY 0x01
|
|
#define NL80211_FLAG_NEED_NETDEV 0x02
|
|
#define NL80211_FLAG_NEED_NETDEV 0x02
|
|
#define NL80211_FLAG_NEED_RTNL 0x04
|
|
#define NL80211_FLAG_NEED_RTNL 0x04
|
|
@@ -10456,6 +10723,22 @@ static const struct genl_ops nl80211_ops[] = {
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
NL80211_FLAG_NEED_RTNL,
|
|
NL80211_FLAG_NEED_RTNL,
|
|
},
|
|
},
|
|
|
|
+ {
|
|
|
|
+ .cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH,
|
|
|
|
+ .doit = nl80211_tdls_channel_switch,
|
|
|
|
+ .policy = nl80211_policy,
|
|
|
|
+ .flags = GENL_ADMIN_PERM,
|
|
|
|
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
+ NL80211_FLAG_NEED_RTNL,
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ .cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
|
|
|
|
+ .doit = nl80211_tdls_cancel_channel_switch,
|
|
|
|
+ .policy = nl80211_policy,
|
|
|
|
+ .flags = GENL_ADMIN_PERM,
|
|
|
|
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
+ NL80211_FLAG_NEED_RTNL,
|
|
|
|
+ },
|
|
};
|
|
};
|
|
|
|
|
|
/* notification functions */
|
|
/* notification functions */
|
|
@@ -11653,7 +11936,9 @@ EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
|
|
static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
|
|
static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
|
|
struct net_device *netdev,
|
|
struct net_device *netdev,
|
|
struct cfg80211_chan_def *chandef,
|
|
struct cfg80211_chan_def *chandef,
|
|
- gfp_t gfp)
|
|
|
|
|
|
+ gfp_t gfp,
|
|
|
|
+ enum nl80211_commands notif,
|
|
|
|
+ u8 count)
|
|
{
|
|
{
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
void *hdr;
|
|
void *hdr;
|
|
@@ -11662,7 +11947,7 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
|
|
if (!msg)
|
|
if (!msg)
|
|
return;
|
|
return;
|
|
|
|
|
|
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
|
|
|
|
|
|
+ hdr = nl80211hdr_put(msg, 0, 0, 0, notif);
|
|
if (!hdr) {
|
|
if (!hdr) {
|
|
nlmsg_free(msg);
|
|
nlmsg_free(msg);
|
|
return;
|
|
return;
|
|
@@ -11674,6 +11959,10 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
|
|
if (nl80211_send_chandef(msg, chandef))
|
|
if (nl80211_send_chandef(msg, chandef))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
+ if ((notif == NL80211_CMD_CH_SWITCH_STARTED_NOTIFY) &&
|
|
|
|
+ (nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, count)))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
genlmsg_end(msg, hdr);
|
|
genlmsg_end(msg, hdr);
|
|
|
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
|
@@ -11696,18 +11985,28 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
|
|
|
|
|
|
trace_cfg80211_ch_switch_notify(dev, chandef);
|
|
trace_cfg80211_ch_switch_notify(dev, chandef);
|
|
|
|
|
|
- if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
- wdev->iftype != NL80211_IFTYPE_P2P_GO &&
|
|
|
|
- wdev->iftype != NL80211_IFTYPE_ADHOC &&
|
|
|
|
- wdev->iftype != NL80211_IFTYPE_MESH_POINT))
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
wdev->chandef = *chandef;
|
|
wdev->chandef = *chandef;
|
|
wdev->preset_chandef = *chandef;
|
|
wdev->preset_chandef = *chandef;
|
|
- nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
|
|
|
|
|
|
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
|
|
|
|
+ NL80211_CMD_CH_SWITCH_NOTIFY, 0);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(cfg80211_ch_switch_notify);
|
|
EXPORT_SYMBOL(cfg80211_ch_switch_notify);
|
|
|
|
|
|
|
|
+void cfg80211_ch_switch_started_notify(struct net_device *dev,
|
|
|
|
+ struct cfg80211_chan_def *chandef,
|
|
|
|
+ u8 count)
|
|
|
|
+{
|
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
|
|
|
+
|
|
|
|
+ trace_cfg80211_ch_switch_started_notify(dev, chandef);
|
|
|
|
+
|
|
|
|
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
|
|
|
|
+ NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, count);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(cfg80211_ch_switch_started_notify);
|
|
|
|
+
|
|
void cfg80211_cqm_txe_notify(struct net_device *dev,
|
|
void cfg80211_cqm_txe_notify(struct net_device *dev,
|
|
const u8 *peer, u32 num_packets,
|
|
const u8 *peer, u32 num_packets,
|
|
u32 rate, u32 intvl, gfp_t gfp)
|
|
u32 rate, u32 intvl, gfp_t gfp)
|
|
@@ -11944,6 +12243,67 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
|
|
EXPORT_SYMBOL(cfg80211_report_obss_beacon);
|
|
EXPORT_SYMBOL(cfg80211_report_obss_beacon);
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
#ifdef CONFIG_PM
|
|
|
|
+static int cfg80211_net_detect_results(struct sk_buff *msg,
|
|
|
|
+ struct cfg80211_wowlan_wakeup *wakeup)
|
|
|
|
+{
|
|
|
|
+ struct cfg80211_wowlan_nd_info *nd = wakeup->net_detect;
|
|
|
|
+ struct nlattr *nl_results, *nl_match, *nl_freqs;
|
|
|
|
+ int i, j;
|
|
|
|
+
|
|
|
|
+ nl_results = nla_nest_start(
|
|
|
|
+ msg, NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS);
|
|
|
|
+ if (!nl_results)
|
|
|
|
+ return -EMSGSIZE;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < nd->n_matches; i++) {
|
|
|
|
+ struct cfg80211_wowlan_nd_match *match = nd->matches[i];
|
|
|
|
+
|
|
|
|
+ nl_match = nla_nest_start(msg, i);
|
|
|
|
+ if (!nl_match)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ /* The SSID attribute is optional in nl80211, but for
|
|
|
|
+ * simplicity reasons it's always present in the
|
|
|
|
+ * cfg80211 structure. If a driver can't pass the
|
|
|
|
+ * SSID, that needs to be changed. A zero length SSID
|
|
|
|
+ * is still a valid SSID (wildcard), so it cannot be
|
|
|
|
+ * used for this purpose.
|
|
|
|
+ */
|
|
|
|
+ if (nla_put(msg, NL80211_ATTR_SSID, match->ssid.ssid_len,
|
|
|
|
+ match->ssid.ssid)) {
|
|
|
|
+ nla_nest_cancel(msg, nl_match);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (match->n_channels) {
|
|
|
|
+ nl_freqs = nla_nest_start(
|
|
|
|
+ msg, NL80211_ATTR_SCAN_FREQUENCIES);
|
|
|
|
+ if (!nl_freqs) {
|
|
|
|
+ nla_nest_cancel(msg, nl_match);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (j = 0; j < match->n_channels; j++) {
|
|
|
|
+ if (nla_put_u32(msg,
|
|
|
|
+ NL80211_ATTR_WIPHY_FREQ,
|
|
|
|
+ match->channels[j])) {
|
|
|
|
+ nla_nest_cancel(msg, nl_freqs);
|
|
|
|
+ nla_nest_cancel(msg, nl_match);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ nla_nest_end(msg, nl_freqs);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ nla_nest_end(msg, nl_match);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ nla_nest_end(msg, nl_results);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
|
|
void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
|
|
struct cfg80211_wowlan_wakeup *wakeup,
|
|
struct cfg80211_wowlan_wakeup *wakeup,
|
|
gfp_t gfp)
|
|
gfp_t gfp)
|
|
@@ -12038,6 +12398,10 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
|
|
goto free_msg;
|
|
goto free_msg;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (wakeup->net_detect &&
|
|
|
|
+ cfg80211_net_detect_results(msg, wakeup))
|
|
|
|
+ goto free_msg;
|
|
|
|
+
|
|
nla_nest_end(msg, reasons);
|
|
nla_nest_end(msg, reasons);
|
|
}
|
|
}
|
|
|
|
|