From 89a54e48b9cbb44aed1bf6cd712e087b96b6ae65 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Jun 2012 14:33:17 +0200 Subject: nl80211: prepare for non-netdev wireless devs In order to support a P2P device abstraction and Bluetooth high-speed AMPs, we need to have a way to identify virtual interfaces that don't have a netdev associated. Do this by adding a NL80211_ATTR_WDEV attribute to identify a wdev which may or may not also be a netdev. To simplify things, use a 64-bit value with the high 32 bits being the wiphy index for this new wdev identifier in the nl80211 API. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 135 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 108 insertions(+), 27 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2a5cdb60bc6e..35a9b15289f1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -46,28 +46,60 @@ static struct genl_family nl80211_fam = { .post_doit = nl80211_post_doit, }; -/* internal helper: get rdev and dev */ -static int get_rdev_dev_by_ifindex(struct net *netns, struct nlattr **attrs, - struct cfg80211_registered_device **rdev, - struct net_device **dev) +/* returns ERR_PTR values */ +static struct wireless_dev * +__cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs) { - int ifindex; + struct cfg80211_registered_device *rdev; + struct wireless_dev *result = NULL; + bool have_ifidx = attrs[NL80211_ATTR_IFINDEX]; + bool have_wdev_id = attrs[NL80211_ATTR_WDEV]; + u64 wdev_id; + int wiphy_idx = -1; + int ifidx = -1; - if (!attrs[NL80211_ATTR_IFINDEX]) - return -EINVAL; + assert_cfg80211_lock(); - ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); - *dev = dev_get_by_index(netns, ifindex); - if (!*dev) - return -ENODEV; + if (!have_ifidx && !have_wdev_id) + return ERR_PTR(-EINVAL); - *rdev = cfg80211_get_dev_from_ifindex(netns, ifindex); - if (IS_ERR(*rdev)) { - dev_put(*dev); - return PTR_ERR(*rdev); + if (have_ifidx) + ifidx = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); + if (have_wdev_id) { + wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]); + wiphy_idx = wdev_id >> 32; } - return 0; + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + struct wireless_dev *wdev; + + if (wiphy_net(&rdev->wiphy) != netns) + continue; + + if (have_wdev_id && rdev->wiphy_idx != wiphy_idx) + continue; + + mutex_lock(&rdev->devlist_mtx); + list_for_each_entry(wdev, &rdev->wdev_list, list) { + if (have_ifidx && wdev->netdev && + wdev->netdev->ifindex == ifidx) { + result = wdev; + break; + } + if (have_wdev_id && wdev->identifier == (u32)wdev_id) { + result = wdev; + break; + } + } + mutex_unlock(&rdev->devlist_mtx); + + if (result) + break; + } + + if (result) + return result; + return ERR_PTR(-ENODEV); } static struct cfg80211_registered_device * @@ -79,13 +111,40 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs) assert_cfg80211_lock(); if (!attrs[NL80211_ATTR_WIPHY] && - !attrs[NL80211_ATTR_IFINDEX]) + !attrs[NL80211_ATTR_IFINDEX] && + !attrs[NL80211_ATTR_WDEV]) return ERR_PTR(-EINVAL); if (attrs[NL80211_ATTR_WIPHY]) rdev = cfg80211_rdev_by_wiphy_idx( nla_get_u32(attrs[NL80211_ATTR_WIPHY])); + if (attrs[NL80211_ATTR_WDEV]) { + u64 wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]); + struct wireless_dev *wdev; + bool found = false; + + tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32); + if (tmp) { + /* make sure wdev exists */ + mutex_lock(&tmp->devlist_mtx); + list_for_each_entry(wdev, &tmp->wdev_list, list) { + if (wdev->identifier != (u32)wdev_id) + continue; + found = true; + break; + } + mutex_unlock(&tmp->devlist_mtx); + + if (!found) + tmp = NULL; + + if (rdev && tmp != rdev) + return ERR_PTR(-EINVAL); + rdev = tmp; + } + } + if (attrs[NL80211_ATTR_IFINDEX]) { int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); netdev = dev_get_by_index(netns, ifindex); @@ -294,6 +353,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 }, [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, + [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, }; /* policy for the key attributes */ @@ -1674,6 +1734,8 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct net_device *dev) { void *hdr; + u64 wdev_id = (u64)dev->ieee80211_ptr->identifier | + ((u64)rdev->wiphy_idx << 32); hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE); if (!hdr) @@ -1684,6 +1746,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name) || nla_put_u32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype) || + nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id) || nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->devlist_generation ^ (cfg80211_rdev_list_generation << 2))) @@ -1724,7 +1787,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * if_idx = 0; mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) { + list_for_each_entry(wdev, &rdev->wdev_list, list) { if (if_idx < if_start) { if_idx++; continue; @@ -2350,7 +2413,7 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) { + list_for_each_entry(wdev, &rdev->wdev_list, list) { if (wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO) continue; @@ -6660,8 +6723,8 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; struct net_device *dev; - int err; bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL; if (rtnl) @@ -6676,21 +6739,39 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, } info->user_ptr[0] = rdev; } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) { - err = get_rdev_dev_by_ifindex(genl_info_net(info), info->attrs, - &rdev, &dev); - if (err) { + mutex_lock(&cfg80211_mutex); + wdev = __cfg80211_wdev_from_attrs(genl_info_net(info), + info->attrs); + if (IS_ERR(wdev)) { + mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); - return err; + return PTR_ERR(wdev); } + + if (!wdev->netdev) { + mutex_unlock(&cfg80211_mutex); + if (rtnl) + rtnl_unlock(); + return -EINVAL; + } + + dev = wdev->netdev; + rdev = wiphy_to_dev(wdev->wiphy); + if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && !netif_running(dev)) { - cfg80211_unlock_rdev(rdev); - dev_put(dev); + mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); return -ENETDOWN; } + + dev_hold(dev); + cfg80211_lock_rdev(rdev); + + mutex_unlock(&cfg80211_mutex); + info->user_ptr[0] = rdev; info->user_ptr[1] = dev; } @@ -8483,7 +8564,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb, rcu_read_lock(); list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { - list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) + list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) cfg80211_mlme_unregister_socket(wdev, notify->pid); if (rdev->ap_beacons_nlpid == notify->pid) rdev->ap_beacons_nlpid = 0; -- cgit v1.2.1 From 1bf614ef7946436246de3c6a6593ba4799f7bac8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Jun 2012 15:23:36 +0200 Subject: nl80211: add NL80211_FLAG_NEED_WDEV Some nl80211 callbacks will soon need the wdev instead of the netdev, so add NL80211_FLAG_NEED_WDEV to allow them to request that. Add NL80211_FLAG_NEED_WDEV_UP as well which checks the netdev is UP if one exists. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 57 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 18 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 35a9b15289f1..27cd18e256c5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6718,6 +6718,10 @@ static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) #define NL80211_FLAG_CHECK_NETDEV_UP 0x08 #define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\ NL80211_FLAG_CHECK_NETDEV_UP) +#define NL80211_FLAG_NEED_WDEV 0x10 +/* If a netdev is associated, it must be UP */ +#define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\ + NL80211_FLAG_CHECK_NETDEV_UP) static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info) @@ -6738,7 +6742,8 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, return PTR_ERR(rdev); } info->user_ptr[0] = rdev; - } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) { + } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV || + ops->internal_flags & NL80211_FLAG_NEED_WDEV) { mutex_lock(&cfg80211_mutex); wdev = __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs); @@ -6749,31 +6754,39 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, return PTR_ERR(wdev); } - if (!wdev->netdev) { - mutex_unlock(&cfg80211_mutex); - if (rtnl) - rtnl_unlock(); - return -EINVAL; - } - dev = wdev->netdev; rdev = wiphy_to_dev(wdev->wiphy); - if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && - !netif_running(dev)) { - mutex_unlock(&cfg80211_mutex); - if (rtnl) - rtnl_unlock(); - return -ENETDOWN; + if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) { + if (!dev) { + mutex_unlock(&cfg80211_mutex); + if (rtnl) + rtnl_unlock(); + return -EINVAL; + } + + info->user_ptr[1] = dev; + } else { + info->user_ptr[1] = wdev; + } + + if (dev) { + if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && + !netif_running(dev)) { + mutex_unlock(&cfg80211_mutex); + if (rtnl) + rtnl_unlock(); + return -ENETDOWN; + } + + dev_hold(dev); } - dev_hold(dev); cfg80211_lock_rdev(rdev); mutex_unlock(&cfg80211_mutex); info->user_ptr[0] = rdev; - info->user_ptr[1] = dev; } return 0; @@ -6784,8 +6797,16 @@ static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, { if (info->user_ptr[0]) cfg80211_unlock_rdev(info->user_ptr[0]); - if (info->user_ptr[1]) - dev_put(info->user_ptr[1]); + if (info->user_ptr[1]) { + if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) { + struct wireless_dev *wdev = info->user_ptr[1]; + + if (wdev->netdev) + dev_put(wdev->netdev); + } else { + dev_put(info->user_ptr[1]); + } + } if (ops->internal_flags & NL80211_FLAG_NEED_RTNL) rtnl_unlock(); } -- cgit v1.2.1 From 72fb2abcf5f985a72e0a30c214b7ed49afa6571b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Jun 2012 17:52:47 +0200 Subject: nl80211: retrieve interface data by wdev Since soon there will be virtual interfaces that don't have a netdev, use the wdev identifier for the API to retrieve interface data. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 27cd18e256c5..5800c49d6942 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1731,21 +1731,25 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct cfg80211_registered_device *rdev, - struct net_device *dev) + struct wireless_dev *wdev) { + struct net_device *dev = wdev->netdev; void *hdr; - u64 wdev_id = (u64)dev->ieee80211_ptr->identifier | + u64 wdev_id = (u64)wdev->identifier | ((u64)rdev->wiphy_idx << 32); hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE); if (!hdr) return -1; - if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || - nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name) || - nla_put_u32(msg, NL80211_ATTR_IFTYPE, - dev->ieee80211_ptr->iftype) || + if (dev && + (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || + nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dev->dev_addr))) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u32(msg, NL80211_ATTR_IFTYPE, wdev->iftype) || nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id) || nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->devlist_generation ^ @@ -1794,7 +1798,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * } if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - rdev, wdev->netdev) < 0) { + rdev, wdev) < 0) { mutex_unlock(&rdev->devlist_mtx); goto out; } @@ -1817,14 +1821,14 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) { struct sk_buff *msg; struct cfg80211_registered_device *dev = info->user_ptr[0]; - struct net_device *netdev = info->user_ptr[1]; + struct wireless_dev *wdev = info->user_ptr[1]; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, - dev, netdev) < 0) { + dev, wdev) < 0) { nlmsg_free(msg); return -ENOBUFS; } @@ -6833,7 +6837,7 @@ static struct genl_ops nl80211_ops[] = { .dumpit = nl80211_dump_interface, .policy = nl80211_policy, /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_NETDEV, + .internal_flags = NL80211_FLAG_NEED_WDEV, }, { .cmd = NL80211_CMD_SET_INTERFACE, -- cgit v1.2.1 From 71bbc9943883cffaf5d7a7728a4e4c50b3ac44d3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Jun 2012 15:30:18 +0200 Subject: cfg80211: use wdev in mgmt-tx/ROC APIs The management frame and remain-on-channel APIs will be needed in the P2P device abstraction, so move them over to the new wdev-based APIs. Userspace can still use both the interface index and wdev identifier for them so it's backward compatible, but for the P2P Device wdev it will be able to use the wdev identifier only. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 119 +++++++++++++++++++++++++++++-------------------- 1 file changed, 70 insertions(+), 49 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5800c49d6942..0dc3356eea40 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1728,6 +1728,11 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) return result; } +static inline u64 wdev_id(struct wireless_dev *wdev) +{ + return (u64)wdev->identifier | + ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32); +} static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct cfg80211_registered_device *rdev, @@ -1735,8 +1740,6 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, { struct net_device *dev = wdev->netdev; void *hdr; - u64 wdev_id = (u64)wdev->identifier | - ((u64)rdev->wiphy_idx << 32); hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE); if (!hdr) @@ -1750,7 +1753,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFTYPE, wdev->iftype) || - nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id) || + nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->devlist_generation ^ (cfg80211_rdev_list_generation << 2))) @@ -5752,7 +5755,7 @@ static int nl80211_remain_on_channel(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 = info->user_ptr[1]; struct ieee80211_channel *chan; struct sk_buff *msg; void *hdr; @@ -5800,7 +5803,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, goto free_msg; } - err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, + err = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan, channel_type, duration, &cookie); if (err) @@ -5824,7 +5827,7 @@ static int nl80211_cancel_remain_on_channel(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 = info->user_ptr[1]; u64 cookie; if (!info->attrs[NL80211_ATTR_COOKIE]) @@ -5835,7 +5838,7 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); - return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); + return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, wdev, cookie); } static u32 rateset_to_mask(struct ieee80211_supported_band *sband, @@ -5984,7 +5987,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, static int nl80211_register_mgmt(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 = info->user_ptr[1]; u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION; if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) @@ -5993,21 +5996,24 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_FRAME_TYPE]) frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]); - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_P2P_GO: + break; + default: return -EOPNOTSUPP; + } /* not much point in registering if we can't reply */ if (!rdev->ops->mgmt_tx) return -EOPNOTSUPP; - return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid, - frame_type, + return cfg80211_mlme_register_mgmt(wdev, info->snd_pid, frame_type, nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); } @@ -6015,7 +6021,7 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) static int nl80211_tx_mgmt(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 = info->user_ptr[1]; struct ieee80211_channel *chan; enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; bool channel_type_valid = false; @@ -6036,14 +6042,18 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->mgmt_tx) return -EOPNOTSUPP; - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_P2P_GO: + break; + default: return -EOPNOTSUPP; + } if (info->attrs[NL80211_ATTR_DURATION]) { if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) @@ -6092,7 +6102,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } } - err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type, + err = cfg80211_mlme_mgmt_tx(rdev, wdev, chan, offchan, channel_type, channel_type_valid, wait, nla_data(info->attrs[NL80211_ATTR_FRAME]), nla_len(info->attrs[NL80211_ATTR_FRAME]), @@ -6120,7 +6130,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) static int nl80211_tx_mgmt_cancel_wait(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 = info->user_ptr[1]; u64 cookie; if (!info->attrs[NL80211_ATTR_COOKIE]) @@ -6129,17 +6139,21 @@ static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *in if (!rdev->ops->mgmt_tx_cancel_wait) return -EOPNOTSUPP; - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_P2P_GO: + break; + default: return -EOPNOTSUPP; + } cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); - return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie); + return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie); } static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) @@ -7172,7 +7186,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_remain_on_channel, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -7180,7 +7194,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_cancel_remain_on_channel, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -7196,7 +7210,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_register_mgmt, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_WDEV | NL80211_FLAG_NEED_RTNL, }, { @@ -7204,7 +7218,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_tx_mgmt, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -7212,7 +7226,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_tx_mgmt_cancel_wait, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -8040,7 +8054,7 @@ nla_put_failure: static void nl80211_send_remain_on_chan_event( int cmd, struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, + struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) @@ -8059,7 +8073,9 @@ static void nl80211_send_remain_on_chan_event( } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || + (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, + wdev->netdev->ifindex)) || + nla_put_u32(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) || nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) || nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) @@ -8081,23 +8097,24 @@ static void nl80211_send_remain_on_chan_event( } void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, + struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, - rdev, netdev, cookie, chan, + rdev, wdev, cookie, chan, channel_type, duration, gfp); } void nl80211_send_remain_on_channel_cancel( - struct cfg80211_registered_device *rdev, struct net_device *netdev, + struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, gfp_t gfp) { nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, - rdev, netdev, cookie, chan, + rdev, wdev, cookie, chan, channel_type, 0, gfp); } @@ -8211,10 +8228,11 @@ bool nl80211_unexpected_4addr_frame(struct net_device *dev, } int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 nlpid, + struct wireless_dev *wdev, u32 nlpid, int freq, int sig_dbm, const u8 *buf, size_t len, gfp_t gfp) { + struct net_device *netdev = wdev->netdev; struct sk_buff *msg; void *hdr; @@ -8229,7 +8247,8 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || + (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, + netdev->ifindex)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) || (sig_dbm && nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || @@ -8247,10 +8266,11 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, } void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, + struct wireless_dev *wdev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp) { + struct net_device *netdev = wdev->netdev; struct sk_buff *msg; void *hdr; @@ -8265,7 +8285,8 @@ void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || + (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, + netdev->ifindex)) || nla_put(msg, NL80211_ATTR_FRAME, len, buf) || nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) || (ack && nla_put_flag(msg, NL80211_ATTR_ACK))) -- cgit v1.2.1 From 84efbb84cf76238faf26facf481c8675859bfaeb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 16 Jun 2012 00:00:26 +0200 Subject: cfg80211: use wireless_dev for interface management In order to be able to create P2P Device wdevs, move the virtual interface management over to wireless_dev structures. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0dc3356eea40..789d0c7b287e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1971,7 +1971,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct vif_params params; - struct net_device *dev; + struct wireless_dev *wdev; int err; enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; u32 flags; @@ -2001,16 +2001,14 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, &flags); - dev = rdev->ops->add_virtual_intf(&rdev->wiphy, + wdev = rdev->ops->add_virtual_intf(&rdev->wiphy, nla_data(info->attrs[NL80211_ATTR_IFNAME]), type, err ? NULL : &flags, ¶ms); - if (IS_ERR(dev)) - return PTR_ERR(dev); + if (IS_ERR(wdev)) + return PTR_ERR(wdev); if (type == NL80211_IFTYPE_MESH_POINT && info->attrs[NL80211_ATTR_MESH_ID]) { - struct wireless_dev *wdev = dev->ieee80211_ptr; - wdev_lock(wdev); BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); @@ -2027,12 +2025,22 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_interface(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 = info->user_ptr[1]; if (!rdev->ops->del_virtual_intf) return -EOPNOTSUPP; - return rdev->ops->del_virtual_intf(&rdev->wiphy, dev); + /* + * If we remove a wireless device without a netdev then clear + * user_ptr[1] so that nl80211_post_doit won't dereference it + * to check if it needs to do dev_put(). Otherwise it crashes + * since the wdev has been freed, unlike with a netdev where + * we need the dev_put() for the netdev to really be freed. + */ + if (!wdev->netdev) + info->user_ptr[1] = NULL; + + return rdev->ops->del_virtual_intf(&rdev->wiphy, wdev); } static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info) @@ -6874,7 +6882,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_del_interface, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_WDEV | NL80211_FLAG_NEED_RTNL, }, { -- cgit v1.2.1 From 1c90f9d404a45a1677c1e5791f5a2a8aaee0370a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 16 Jun 2012 00:05:37 +0200 Subject: nl80211: send interface after creation After a new virtual interface is created, reply to userspace with a message detailing it so it knows the new wdev identifier. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 789d0c7b287e..6a9a1d7f51d1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1972,6 +1972,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct vif_params params; struct wireless_dev *wdev; + struct sk_buff *msg; int err; enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; u32 flags; @@ -1998,14 +1999,20 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) return err; } + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, &flags); wdev = rdev->ops->add_virtual_intf(&rdev->wiphy, nla_data(info->attrs[NL80211_ATTR_IFNAME]), type, err ? NULL : &flags, ¶ms); - if (IS_ERR(wdev)) + if (IS_ERR(wdev)) { + nlmsg_free(msg); return PTR_ERR(wdev); + } if (type == NL80211_IFTYPE_MESH_POINT && info->attrs[NL80211_ATTR_MESH_ID]) { @@ -2019,7 +2026,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) wdev_unlock(wdev); } - return 0; + if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, + rdev, wdev) < 0) { + nlmsg_free(msg); + return -ENOBUFS; + } + + return genlmsg_reply(msg, info); } static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) -- cgit v1.2.1 From fd0142844efa85d89017c89227a0f03de1eee327 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 18 Jun 2012 19:17:03 +0200 Subject: nl80211: move scan API to wdev The new P2P Device will have to be able to scan for P2P search, so move scanning to use struct wireless_dev instead of struct net_device. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6a9a1d7f51d1..6472c7f928dc 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4130,7 +4130,7 @@ static int validate_scan_freqs(struct nlattr *freqs) static int nl80211_trigger_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 = info->user_ptr[1]; struct cfg80211_scan_request *request; struct nlattr *attr; struct wiphy *wiphy; @@ -4290,15 +4290,16 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) request->no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); - request->dev = dev; + request->wdev = wdev; request->wiphy = &rdev->wiphy; rdev->scan_req = request; - err = rdev->ops->scan(&rdev->wiphy, dev, request); + err = rdev->ops->scan(&rdev->wiphy, request); if (!err) { - nl80211_send_scan_start(rdev, dev); - dev_hold(dev); + nl80211_send_scan_start(rdev, wdev); + if (wdev->netdev) + dev_hold(wdev->netdev); } else { out_free: rdev->scan_req = NULL; @@ -7066,7 +7067,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_trigger_scan, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -7458,7 +7459,7 @@ static int nl80211_add_scan_req(struct sk_buff *msg, static int nl80211_send_scan_msg(struct sk_buff *msg, struct cfg80211_registered_device *rdev, - struct net_device *netdev, + struct wireless_dev *wdev, u32 pid, u32 seq, int flags, u32 cmd) { @@ -7469,7 +7470,9 @@ static int nl80211_send_scan_msg(struct sk_buff *msg, return -1; if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex)) + (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, + wdev->netdev->ifindex)) || + nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev))) goto nla_put_failure; /* ignore errors and send incomplete event anyway */ @@ -7506,7 +7509,7 @@ nl80211_send_sched_scan_msg(struct sk_buff *msg, } void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, - struct net_device *netdev) + struct wireless_dev *wdev) { struct sk_buff *msg; @@ -7514,7 +7517,7 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, if (!msg) return; - if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, + if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0, NL80211_CMD_TRIGGER_SCAN) < 0) { nlmsg_free(msg); return; @@ -7525,7 +7528,7 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, } void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, - struct net_device *netdev) + struct wireless_dev *wdev) { struct sk_buff *msg; @@ -7533,7 +7536,7 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, if (!msg) return; - if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, + if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0, NL80211_CMD_NEW_SCAN_RESULTS) < 0) { nlmsg_free(msg); return; @@ -7544,7 +7547,7 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, } void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, - struct net_device *netdev) + struct wireless_dev *wdev) { struct sk_buff *msg; @@ -7552,7 +7555,7 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, if (!msg) return; - if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, + if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0, NL80211_CMD_SCAN_ABORTED) < 0) { nlmsg_free(msg); return; -- cgit v1.2.1 From ae33bd817a10f39174453b754e9b548132acae4a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Jul 2012 16:25:02 +0200 Subject: nl80211: allow enabling WoWLAN without triggers It may be desirable to use WoWLAN without triggers to keep the connection alive to the AP while suspended. Allow this use by enabling WoWLAN without triggers if no triggers were requested. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6472c7f928dc..079fc49e3975 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6466,8 +6466,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; - struct cfg80211_wowlan no_triggers = {}; struct cfg80211_wowlan new_triggers = {}; + struct cfg80211_wowlan *ntrig; struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan; int err, i; bool prev_enabled = rdev->wowlan; @@ -6475,8 +6475,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) return -EOPNOTSUPP; - if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) - goto no_triggers; + if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) { + cfg80211_rdev_free_wowlan(rdev); + rdev->wowlan = NULL; + goto set_wakeup; + } err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), @@ -6587,22 +6590,15 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) } } - if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) { - struct cfg80211_wowlan *ntrig; - ntrig = kmemdup(&new_triggers, sizeof(new_triggers), - GFP_KERNEL); - if (!ntrig) { - err = -ENOMEM; - goto error; - } - cfg80211_rdev_free_wowlan(rdev); - rdev->wowlan = ntrig; - } else { - no_triggers: - cfg80211_rdev_free_wowlan(rdev); - rdev->wowlan = NULL; + ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); + if (!ntrig) { + err = -ENOMEM; + goto error; } + cfg80211_rdev_free_wowlan(rdev); + rdev->wowlan = ntrig; + set_wakeup: if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan) rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan); -- cgit v1.2.1 From 5b7ccaf3fc7446e42b83a77fd7aa7ad92850acdd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Jul 2012 19:45:08 +0200 Subject: cfg80211/mac80211: re-add get_channel operation This essentially reverts commit 2e165b818456 but introduces the get_channel operation with a new wireless_dev argument so that you can retrieve the channel per interface. This is necessary as even though we can track all interface channels (except monitor) we can't track the channel type used. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 079fc49e3975..6b001e445718 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1759,11 +1759,17 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, (cfg80211_rdev_list_generation << 2))) goto nla_put_failure; - if (rdev->monitor_channel) { - if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, - rdev->monitor_channel->center_freq) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - rdev->monitor_channel_type)) + if (rdev->ops->get_channel) { + struct ieee80211_channel *chan; + enum nl80211_channel_type channel_type; + + chan = rdev->ops->get_channel(&rdev->wiphy, wdev, + &channel_type); + if (chan && + (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + chan->center_freq) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + channel_type))) goto nla_put_failure; } -- cgit v1.2.1 From 00f5335079689cd65a9430b5df8974dc35c7914b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 17 Jul 2012 11:53:12 +0200 Subject: nl80211: add wdev ID as u64 as it should In one of my previous patches I erroneously used nla_put_u32 for the wdev_id, fix that to use nla_put_u64. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6b001e445718..be8750f91d78 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8101,7 +8101,7 @@ static void nl80211_send_remain_on_chan_event( if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) || - nla_put_u32(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || + nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) || nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) || nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) -- cgit v1.2.1 From 84f10708f73254878246772cead70a2eb6a123f2 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 12 Jul 2012 16:17:33 -0700 Subject: cfg80211: support TX error rate CQM Let the user configure serveral TX error conection quality monitoring parameters: % error rate, survey interval, and # of attempted packets. On exceeding the TX failure rate over the given interval, the driver will send a CQM notify event with the actual TX failure rate and packets attempted. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index be8750f91d78..9216e45e53a0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6267,8 +6267,35 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 }, }; +static int nl80211_set_cqm_txe(struct genl_info *info, + u32 rate, u32 pkts, u32 intvl) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev; + struct net_device *dev = info->user_ptr[1]; + + if ((rate < 0 || rate > 100) || + (intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL)) + return -EINVAL; + + wdev = dev->ieee80211_ptr; + + if (!rdev->ops->set_cqm_txe_config) + return -EOPNOTSUPP; + + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + return rdev->ops->set_cqm_txe_config(wdev->wiphy, dev, + rate, pkts, intvl); +} + static int nl80211_set_cqm_rssi(struct genl_info *info, s32 threshold, u32 hysteresis) { @@ -6316,6 +6343,14 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); err = nl80211_set_cqm_rssi(info, threshold, hysteresis); + } else if (attrs[NL80211_ATTR_CQM_TXE_RATE] && + attrs[NL80211_ATTR_CQM_TXE_PKTS] && + attrs[NL80211_ATTR_CQM_TXE_INTVL]) { + u32 rate, pkts, intvl; + rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]); + pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]); + intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]); + err = nl80211_set_cqm_txe(info, rate, pkts, intvl); } else err = -EINVAL; @@ -8495,6 +8530,56 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void +nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *peer, + u32 num_packets, u32 rate, u32 intvl, gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *pinfoattr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) + goto nla_put_failure; + + pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); + if (!pinfoattr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets)) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate)) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl)) + goto nla_put_failure; + + nla_nest_end(msg, pinfoattr); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + void nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, -- cgit v1.2.1 From 57b5ce072e7361218a8e2ea1d62960cbb71d9cff Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 12 Jul 2012 11:49:18 -0700 Subject: cfg80211: add cellular base station regulatory hint support Cellular base stations can provide hints to cfg80211 about where they think we are. This can be done for example on a cell phone. To enable these hints we simply allow them through as user regulatory hints but we allow userspace to clasify the hint as either coming directly from the user or coming from a cellular base station. This option is only available when you enable CONFIG_CFG80211_CERTIFICATION_ONUS. The base station hints themselves will not be processed by the core unless at least one device on the system supports this feature. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9216e45e53a0..50b1a0e84f1a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -354,6 +354,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, + [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 }, }; /* policy for the key attributes */ @@ -3582,6 +3583,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) { int r; char *data = NULL; + enum nl80211_user_reg_hint_type user_reg_hint_type; /* * You should only get this when cfg80211 hasn't yet initialized @@ -3601,7 +3603,21 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); - r = regulatory_hint_user(data); + if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]) + user_reg_hint_type = + nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]); + else + user_reg_hint_type = NL80211_USER_REG_HINT_USER; + + switch (user_reg_hint_type) { + case NL80211_USER_REG_HINT_USER: + case NL80211_USER_REG_HINT_CELL_BASE: + break; + default: + return -EINVAL; + } + + r = regulatory_hint_user(data, user_reg_hint_type); return r; } @@ -3971,6 +3987,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) cfg80211_regdomain->dfs_region))) goto nla_put_failure; + if (reg_last_request_cell_base() && + nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, + NL80211_USER_REG_HINT_CELL_BASE)) + goto nla_put_failure; + nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); if (!nl_reg_rules) goto nla_put_failure; -- cgit v1.2.1