diff options
-rw-r--r-- | include/linux/nl80211.h | 36 | ||||
-rw-r--r-- | include/net/cfg80211.h | 47 | ||||
-rw-r--r-- | net/wireless/chan.c | 41 | ||||
-rw-r--r-- | net/wireless/core.h | 3 | ||||
-rw-r--r-- | net/wireless/mlme.c | 27 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 218 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 11 |
7 files changed, 368 insertions, 15 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index da8ea2e19273..2bfbe88837ef 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h | |||
@@ -270,6 +270,31 @@ | |||
270 | * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices | 270 | * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices |
271 | * associated with this wiphy must be down and will follow. | 271 | * associated with this wiphy must be down and will follow. |
272 | * | 272 | * |
273 | * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified | ||
274 | * channel for the specified amount of time. This can be used to do | ||
275 | * off-channel operations like transmit a Public Action frame and wait for | ||
276 | * a response while being associated to an AP on another channel. | ||
277 | * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify which | ||
278 | * radio is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the | ||
279 | * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be | ||
280 | * optionally used to specify additional channel parameters. | ||
281 | * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds | ||
282 | * to remain on the channel. This command is also used as an event to | ||
283 | * notify when the requested duration starts (it may take a while for the | ||
284 | * driver to schedule this time due to other concurrent needs for the | ||
285 | * radio). | ||
286 | * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE) | ||
287 | * that will be included with any events pertaining to this request; | ||
288 | * the cookie is also used to cancel the request. | ||
289 | * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a | ||
290 | * pending remain-on-channel duration if the desired operation has been | ||
291 | * completed prior to expiration of the originally requested duration. | ||
292 | * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the | ||
293 | * radio. The %NL80211_ATTR_COOKIE attribute must be given as well to | ||
294 | * uniquely identify the request. | ||
295 | * This command is also used as an event to notify when a requested | ||
296 | * remain-on-channel duration has expired. | ||
297 | * | ||
273 | * @NL80211_CMD_MAX: highest used command number | 298 | * @NL80211_CMD_MAX: highest used command number |
274 | * @__NL80211_CMD_AFTER_LAST: internal use | 299 | * @__NL80211_CMD_AFTER_LAST: internal use |
275 | */ | 300 | */ |
@@ -353,6 +378,9 @@ enum nl80211_commands { | |||
353 | NL80211_CMD_DEL_PMKSA, | 378 | NL80211_CMD_DEL_PMKSA, |
354 | NL80211_CMD_FLUSH_PMKSA, | 379 | NL80211_CMD_FLUSH_PMKSA, |
355 | 380 | ||
381 | NL80211_CMD_REMAIN_ON_CHANNEL, | ||
382 | NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
383 | |||
356 | /* add new commands above here */ | 384 | /* add new commands above here */ |
357 | 385 | ||
358 | /* used to define NL80211_CMD_MAX below */ | 386 | /* used to define NL80211_CMD_MAX below */ |
@@ -606,6 +634,10 @@ enum nl80211_commands { | |||
606 | * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can | 634 | * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can |
607 | * cache, a wiphy attribute. | 635 | * cache, a wiphy attribute. |
608 | * | 636 | * |
637 | * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32. | ||
638 | * | ||
639 | * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects. | ||
640 | * | ||
609 | * @NL80211_ATTR_MAX: highest attribute number currently defined | 641 | * @NL80211_ATTR_MAX: highest attribute number currently defined |
610 | * @__NL80211_ATTR_AFTER_LAST: internal use | 642 | * @__NL80211_ATTR_AFTER_LAST: internal use |
611 | */ | 643 | */ |
@@ -743,6 +775,10 @@ enum nl80211_attrs { | |||
743 | NL80211_ATTR_PMKID, | 775 | NL80211_ATTR_PMKID, |
744 | NL80211_ATTR_MAX_NUM_PMKIDS, | 776 | NL80211_ATTR_MAX_NUM_PMKIDS, |
745 | 777 | ||
778 | NL80211_ATTR_DURATION, | ||
779 | |||
780 | NL80211_ATTR_COOKIE, | ||
781 | |||
746 | /* add attributes here, update the policy in nl80211.c */ | 782 | /* add attributes here, update the policy in nl80211.c */ |
747 | 783 | ||
748 | __NL80211_ATTR_AFTER_LAST, | 784 | __NL80211_ATTR_AFTER_LAST, |
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 542a477a94da..b66beb052054 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -988,6 +988,15 @@ struct cfg80211_pmksa { | |||
988 | * | 988 | * |
989 | * @dump_survey: get site survey information. | 989 | * @dump_survey: get site survey information. |
990 | * | 990 | * |
991 | * @remain_on_channel: Request the driver to remain awake on the specified | ||
992 | * channel for the specified duration to complete an off-channel | ||
993 | * operation (e.g., public action frame exchange). When the driver is | ||
994 | * ready on the requested channel, it must indicate this with an event | ||
995 | * notification by calling cfg80211_ready_on_channel(). | ||
996 | * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation. | ||
997 | * This allows the operation to be terminated prior to timeout based on | ||
998 | * the duration value. | ||
999 | * | ||
991 | * @testmode_cmd: run a test mode command | 1000 | * @testmode_cmd: run a test mode command |
992 | * | 1001 | * |
993 | * @set_pmksa: Cache a PMKID for a BSSID. This is mostly useful for fullmac | 1002 | * @set_pmksa: Cache a PMKID for a BSSID. This is mostly useful for fullmac |
@@ -1123,6 +1132,16 @@ struct cfg80211_ops { | |||
1123 | struct cfg80211_pmksa *pmksa); | 1132 | struct cfg80211_pmksa *pmksa); |
1124 | int (*flush_pmksa)(struct wiphy *wiphy, struct net_device *netdev); | 1133 | int (*flush_pmksa)(struct wiphy *wiphy, struct net_device *netdev); |
1125 | 1134 | ||
1135 | int (*remain_on_channel)(struct wiphy *wiphy, | ||
1136 | struct net_device *dev, | ||
1137 | struct ieee80211_channel *chan, | ||
1138 | enum nl80211_channel_type channel_type, | ||
1139 | unsigned int duration, | ||
1140 | u64 *cookie); | ||
1141 | int (*cancel_remain_on_channel)(struct wiphy *wiphy, | ||
1142 | struct net_device *dev, | ||
1143 | u64 cookie); | ||
1144 | |||
1126 | /* some temporary stuff to finish wext */ | 1145 | /* some temporary stuff to finish wext */ |
1127 | int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, | 1146 | int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, |
1128 | bool enabled, int timeout); | 1147 | bool enabled, int timeout); |
@@ -2147,5 +2166,33 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, | |||
2147 | void cfg80211_disconnected(struct net_device *dev, u16 reason, | 2166 | void cfg80211_disconnected(struct net_device *dev, u16 reason, |
2148 | u8 *ie, size_t ie_len, gfp_t gfp); | 2167 | u8 *ie, size_t ie_len, gfp_t gfp); |
2149 | 2168 | ||
2169 | /** | ||
2170 | * cfg80211_ready_on_channel - notification of remain_on_channel start | ||
2171 | * @dev: network device | ||
2172 | * @cookie: the request cookie | ||
2173 | * @chan: The current channel (from remain_on_channel request) | ||
2174 | * @channel_type: Channel type | ||
2175 | * @duration: Duration in milliseconds that the driver intents to remain on the | ||
2176 | * channel | ||
2177 | * @gfp: allocation flags | ||
2178 | */ | ||
2179 | void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie, | ||
2180 | struct ieee80211_channel *chan, | ||
2181 | enum nl80211_channel_type channel_type, | ||
2182 | unsigned int duration, gfp_t gfp); | ||
2183 | |||
2184 | /** | ||
2185 | * cfg80211_remain_on_channel_expired - remain_on_channel duration expired | ||
2186 | * @dev: network device | ||
2187 | * @cookie: the request cookie | ||
2188 | * @chan: The current channel (from remain_on_channel request) | ||
2189 | * @channel_type: Channel type | ||
2190 | * @gfp: allocation flags | ||
2191 | */ | ||
2192 | void cfg80211_remain_on_channel_expired(struct net_device *dev, | ||
2193 | u64 cookie, | ||
2194 | struct ieee80211_channel *chan, | ||
2195 | enum nl80211_channel_type channel_type, | ||
2196 | gfp_t gfp); | ||
2150 | 2197 | ||
2151 | #endif /* __NET_CFG80211_H */ | 2198 | #endif /* __NET_CFG80211_H */ |
diff --git a/net/wireless/chan.c b/net/wireless/chan.c index a46ac6c9b365..bf1737fc9a7e 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c | |||
@@ -41,44 +41,57 @@ rdev_fixed_channel(struct cfg80211_registered_device *rdev, | |||
41 | return result; | 41 | return result; |
42 | } | 42 | } |
43 | 43 | ||
44 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | 44 | struct ieee80211_channel * |
45 | struct wireless_dev *for_wdev, | 45 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, |
46 | int freq, enum nl80211_channel_type channel_type) | 46 | int freq, enum nl80211_channel_type channel_type) |
47 | { | 47 | { |
48 | struct ieee80211_channel *chan; | 48 | struct ieee80211_channel *chan; |
49 | struct ieee80211_sta_ht_cap *ht_cap; | 49 | struct ieee80211_sta_ht_cap *ht_cap; |
50 | int result; | ||
51 | |||
52 | if (rdev_fixed_channel(rdev, for_wdev)) | ||
53 | return -EBUSY; | ||
54 | |||
55 | if (!rdev->ops->set_channel) | ||
56 | return -EOPNOTSUPP; | ||
57 | 50 | ||
58 | chan = ieee80211_get_channel(&rdev->wiphy, freq); | 51 | chan = ieee80211_get_channel(&rdev->wiphy, freq); |
59 | 52 | ||
60 | /* Primary channel not allowed */ | 53 | /* Primary channel not allowed */ |
61 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) | 54 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) |
62 | return -EINVAL; | 55 | return NULL; |
63 | 56 | ||
64 | if (channel_type == NL80211_CHAN_HT40MINUS && | 57 | if (channel_type == NL80211_CHAN_HT40MINUS && |
65 | chan->flags & IEEE80211_CHAN_NO_HT40MINUS) | 58 | chan->flags & IEEE80211_CHAN_NO_HT40MINUS) |
66 | return -EINVAL; | 59 | return NULL; |
67 | else if (channel_type == NL80211_CHAN_HT40PLUS && | 60 | else if (channel_type == NL80211_CHAN_HT40PLUS && |
68 | chan->flags & IEEE80211_CHAN_NO_HT40PLUS) | 61 | chan->flags & IEEE80211_CHAN_NO_HT40PLUS) |
69 | return -EINVAL; | 62 | return NULL; |
70 | 63 | ||
71 | ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; | 64 | ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; |
72 | 65 | ||
73 | if (channel_type != NL80211_CHAN_NO_HT) { | 66 | if (channel_type != NL80211_CHAN_NO_HT) { |
74 | if (!ht_cap->ht_supported) | 67 | if (!ht_cap->ht_supported) |
75 | return -EINVAL; | 68 | return NULL; |
76 | 69 | ||
77 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || | 70 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || |
78 | ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) | 71 | ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) |
79 | return -EINVAL; | 72 | return NULL; |
80 | } | 73 | } |
81 | 74 | ||
75 | return chan; | ||
76 | } | ||
77 | |||
78 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | ||
79 | struct wireless_dev *for_wdev, | ||
80 | int freq, enum nl80211_channel_type channel_type) | ||
81 | { | ||
82 | struct ieee80211_channel *chan; | ||
83 | int result; | ||
84 | |||
85 | if (rdev_fixed_channel(rdev, for_wdev)) | ||
86 | return -EBUSY; | ||
87 | |||
88 | if (!rdev->ops->set_channel) | ||
89 | return -EOPNOTSUPP; | ||
90 | |||
91 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
92 | if (!chan) | ||
93 | return -EINVAL; | ||
94 | |||
82 | result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type); | 95 | result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type); |
83 | if (result) | 96 | if (result) |
84 | return result; | 97 | return result; |
diff --git a/net/wireless/core.h b/net/wireless/core.h index 35b712127143..30ec95f05b52 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -374,6 +374,9 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); | |||
374 | struct ieee80211_channel * | 374 | struct ieee80211_channel * |
375 | rdev_fixed_channel(struct cfg80211_registered_device *rdev, | 375 | rdev_fixed_channel(struct cfg80211_registered_device *rdev, |
376 | struct wireless_dev *for_wdev); | 376 | struct wireless_dev *for_wdev); |
377 | struct ieee80211_channel * | ||
378 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, | ||
379 | int freq, enum nl80211_channel_type channel_type); | ||
377 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | 380 | int rdev_set_freq(struct cfg80211_registered_device *rdev, |
378 | struct wireless_dev *for_wdev, | 381 | struct wireless_dev *for_wdev, |
379 | int freq, enum nl80211_channel_type channel_type); | 382 | int freq, enum nl80211_channel_type channel_type); |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index acaeaa784d68..11f6469b3f98 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -680,3 +680,30 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, | |||
680 | } | 680 | } |
681 | } | 681 | } |
682 | } | 682 | } |
683 | |||
684 | void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie, | ||
685 | struct ieee80211_channel *chan, | ||
686 | enum nl80211_channel_type channel_type, | ||
687 | unsigned int duration, gfp_t gfp) | ||
688 | { | ||
689 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | ||
690 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
691 | |||
692 | nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type, | ||
693 | duration, gfp); | ||
694 | } | ||
695 | EXPORT_SYMBOL(cfg80211_ready_on_channel); | ||
696 | |||
697 | void cfg80211_remain_on_channel_expired(struct net_device *dev, | ||
698 | u64 cookie, | ||
699 | struct ieee80211_channel *chan, | ||
700 | enum nl80211_channel_type channel_type, | ||
701 | gfp_t gfp) | ||
702 | { | ||
703 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | ||
704 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
705 | |||
706 | nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan, | ||
707 | channel_type, gfp); | ||
708 | } | ||
709 | EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 60f854377f90..ff857f10cb85 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -141,6 +141,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
141 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, | 141 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, |
142 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, | 142 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, |
143 | .len = WLAN_PMKID_LEN }, | 143 | .len = WLAN_PMKID_LEN }, |
144 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, | ||
145 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, | ||
144 | }; | 146 | }; |
145 | 147 | ||
146 | /* policy for the attributes */ | 148 | /* policy for the attributes */ |
@@ -569,6 +571,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
569 | CMD(set_pmksa, SET_PMKSA); | 571 | CMD(set_pmksa, SET_PMKSA); |
570 | CMD(del_pmksa, DEL_PMKSA); | 572 | CMD(del_pmksa, DEL_PMKSA); |
571 | CMD(flush_pmksa, FLUSH_PMKSA); | 573 | CMD(flush_pmksa, FLUSH_PMKSA); |
574 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); | ||
572 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | 575 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { |
573 | i++; | 576 | i++; |
574 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 577 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
@@ -4283,6 +4286,143 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) | |||
4283 | 4286 | ||
4284 | } | 4287 | } |
4285 | 4288 | ||
4289 | static int nl80211_remain_on_channel(struct sk_buff *skb, | ||
4290 | struct genl_info *info) | ||
4291 | { | ||
4292 | struct cfg80211_registered_device *rdev; | ||
4293 | struct net_device *dev; | ||
4294 | struct ieee80211_channel *chan; | ||
4295 | struct sk_buff *msg; | ||
4296 | void *hdr; | ||
4297 | u64 cookie; | ||
4298 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
4299 | u32 freq, duration; | ||
4300 | int err; | ||
4301 | |||
4302 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || | ||
4303 | !info->attrs[NL80211_ATTR_DURATION]) | ||
4304 | return -EINVAL; | ||
4305 | |||
4306 | duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); | ||
4307 | |||
4308 | /* | ||
4309 | * We should be on that channel for at least one jiffie, | ||
4310 | * and more than 5 seconds seems excessive. | ||
4311 | */ | ||
4312 | if (!duration || !msecs_to_jiffies(duration) || duration > 5000) | ||
4313 | return -EINVAL; | ||
4314 | |||
4315 | rtnl_lock(); | ||
4316 | |||
4317 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4318 | if (err) | ||
4319 | goto unlock_rtnl; | ||
4320 | |||
4321 | if (!rdev->ops->remain_on_channel) { | ||
4322 | err = -EOPNOTSUPP; | ||
4323 | goto out; | ||
4324 | } | ||
4325 | |||
4326 | if (!netif_running(dev)) { | ||
4327 | err = -ENETDOWN; | ||
4328 | goto out; | ||
4329 | } | ||
4330 | |||
4331 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
4332 | channel_type = nla_get_u32( | ||
4333 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
4334 | if (channel_type != NL80211_CHAN_NO_HT && | ||
4335 | channel_type != NL80211_CHAN_HT20 && | ||
4336 | channel_type != NL80211_CHAN_HT40PLUS && | ||
4337 | channel_type != NL80211_CHAN_HT40MINUS) | ||
4338 | err = -EINVAL; | ||
4339 | goto out; | ||
4340 | } | ||
4341 | |||
4342 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
4343 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
4344 | if (chan == NULL) { | ||
4345 | err = -EINVAL; | ||
4346 | goto out; | ||
4347 | } | ||
4348 | |||
4349 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4350 | if (!msg) { | ||
4351 | err = -ENOMEM; | ||
4352 | goto out; | ||
4353 | } | ||
4354 | |||
4355 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4356 | NL80211_CMD_REMAIN_ON_CHANNEL); | ||
4357 | |||
4358 | if (IS_ERR(hdr)) { | ||
4359 | err = PTR_ERR(hdr); | ||
4360 | goto free_msg; | ||
4361 | } | ||
4362 | |||
4363 | err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, | ||
4364 | channel_type, duration, &cookie); | ||
4365 | |||
4366 | if (err) | ||
4367 | goto free_msg; | ||
4368 | |||
4369 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
4370 | |||
4371 | genlmsg_end(msg, hdr); | ||
4372 | err = genlmsg_reply(msg, info); | ||
4373 | goto out; | ||
4374 | |||
4375 | nla_put_failure: | ||
4376 | err = -ENOBUFS; | ||
4377 | free_msg: | ||
4378 | nlmsg_free(msg); | ||
4379 | out: | ||
4380 | cfg80211_unlock_rdev(rdev); | ||
4381 | dev_put(dev); | ||
4382 | unlock_rtnl: | ||
4383 | rtnl_unlock(); | ||
4384 | return err; | ||
4385 | } | ||
4386 | |||
4387 | static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, | ||
4388 | struct genl_info *info) | ||
4389 | { | ||
4390 | struct cfg80211_registered_device *rdev; | ||
4391 | struct net_device *dev; | ||
4392 | u64 cookie; | ||
4393 | int err; | ||
4394 | |||
4395 | if (!info->attrs[NL80211_ATTR_COOKIE]) | ||
4396 | return -EINVAL; | ||
4397 | |||
4398 | rtnl_lock(); | ||
4399 | |||
4400 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4401 | if (err) | ||
4402 | goto unlock_rtnl; | ||
4403 | |||
4404 | if (!rdev->ops->cancel_remain_on_channel) { | ||
4405 | err = -EOPNOTSUPP; | ||
4406 | goto out; | ||
4407 | } | ||
4408 | |||
4409 | if (!netif_running(dev)) { | ||
4410 | err = -ENETDOWN; | ||
4411 | goto out; | ||
4412 | } | ||
4413 | |||
4414 | cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); | ||
4415 | |||
4416 | err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); | ||
4417 | |||
4418 | out: | ||
4419 | cfg80211_unlock_rdev(rdev); | ||
4420 | dev_put(dev); | ||
4421 | unlock_rtnl: | ||
4422 | rtnl_unlock(); | ||
4423 | return err; | ||
4424 | } | ||
4425 | |||
4286 | static struct genl_ops nl80211_ops[] = { | 4426 | static struct genl_ops nl80211_ops[] = { |
4287 | { | 4427 | { |
4288 | .cmd = NL80211_CMD_GET_WIPHY, | 4428 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -4545,8 +4685,20 @@ static struct genl_ops nl80211_ops[] = { | |||
4545 | .policy = nl80211_policy, | 4685 | .policy = nl80211_policy, |
4546 | .flags = GENL_ADMIN_PERM, | 4686 | .flags = GENL_ADMIN_PERM, |
4547 | }, | 4687 | }, |
4548 | 4688 | { | |
4689 | .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, | ||
4690 | .doit = nl80211_remain_on_channel, | ||
4691 | .policy = nl80211_policy, | ||
4692 | .flags = GENL_ADMIN_PERM, | ||
4693 | }, | ||
4694 | { | ||
4695 | .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
4696 | .doit = nl80211_cancel_remain_on_channel, | ||
4697 | .policy = nl80211_policy, | ||
4698 | .flags = GENL_ADMIN_PERM, | ||
4699 | }, | ||
4549 | }; | 4700 | }; |
4701 | |||
4550 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 4702 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
4551 | .name = "mlme", | 4703 | .name = "mlme", |
4552 | }; | 4704 | }; |
@@ -5134,6 +5286,70 @@ nla_put_failure: | |||
5134 | nlmsg_free(msg); | 5286 | nlmsg_free(msg); |
5135 | } | 5287 | } |
5136 | 5288 | ||
5289 | static void nl80211_send_remain_on_chan_event( | ||
5290 | int cmd, struct cfg80211_registered_device *rdev, | ||
5291 | struct net_device *netdev, u64 cookie, | ||
5292 | struct ieee80211_channel *chan, | ||
5293 | enum nl80211_channel_type channel_type, | ||
5294 | unsigned int duration, gfp_t gfp) | ||
5295 | { | ||
5296 | struct sk_buff *msg; | ||
5297 | void *hdr; | ||
5298 | |||
5299 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5300 | if (!msg) | ||
5301 | return; | ||
5302 | |||
5303 | hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); | ||
5304 | if (!hdr) { | ||
5305 | nlmsg_free(msg); | ||
5306 | return; | ||
5307 | } | ||
5308 | |||
5309 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5310 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5311 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq); | ||
5312 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type); | ||
5313 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
5314 | |||
5315 | if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL) | ||
5316 | NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); | ||
5317 | |||
5318 | if (genlmsg_end(msg, hdr) < 0) { | ||
5319 | nlmsg_free(msg); | ||
5320 | return; | ||
5321 | } | ||
5322 | |||
5323 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
5324 | nl80211_mlme_mcgrp.id, gfp); | ||
5325 | return; | ||
5326 | |||
5327 | nla_put_failure: | ||
5328 | genlmsg_cancel(msg, hdr); | ||
5329 | nlmsg_free(msg); | ||
5330 | } | ||
5331 | |||
5332 | void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, | ||
5333 | struct net_device *netdev, u64 cookie, | ||
5334 | struct ieee80211_channel *chan, | ||
5335 | enum nl80211_channel_type channel_type, | ||
5336 | unsigned int duration, gfp_t gfp) | ||
5337 | { | ||
5338 | nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, | ||
5339 | rdev, netdev, cookie, chan, | ||
5340 | channel_type, duration, gfp); | ||
5341 | } | ||
5342 | |||
5343 | void nl80211_send_remain_on_channel_cancel( | ||
5344 | struct cfg80211_registered_device *rdev, struct net_device *netdev, | ||
5345 | u64 cookie, struct ieee80211_channel *chan, | ||
5346 | enum nl80211_channel_type channel_type, gfp_t gfp) | ||
5347 | { | ||
5348 | nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
5349 | rdev, netdev, cookie, chan, | ||
5350 | channel_type, 0, gfp); | ||
5351 | } | ||
5352 | |||
5137 | /* initialisation/exit functions */ | 5353 | /* initialisation/exit functions */ |
5138 | 5354 | ||
5139 | int nl80211_init(void) | 5355 | int nl80211_init(void) |
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 44cc2a76a1b0..a5e2de419b7a 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
@@ -59,4 +59,15 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, | |||
59 | struct net_device *netdev, const u8 *bssid, | 59 | struct net_device *netdev, const u8 *bssid, |
60 | gfp_t gfp); | 60 | gfp_t gfp); |
61 | 61 | ||
62 | void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, | ||
63 | struct net_device *netdev, | ||
64 | u64 cookie, | ||
65 | struct ieee80211_channel *chan, | ||
66 | enum nl80211_channel_type channel_type, | ||
67 | unsigned int duration, gfp_t gfp); | ||
68 | void nl80211_send_remain_on_channel_cancel( | ||
69 | struct cfg80211_registered_device *rdev, struct net_device *netdev, | ||
70 | u64 cookie, struct ieee80211_channel *chan, | ||
71 | enum nl80211_channel_type channel_type, gfp_t gfp); | ||
72 | |||
62 | #endif /* __NET_WIRELESS_NL80211_H */ | 73 | #endif /* __NET_WIRELESS_NL80211_H */ |