aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/nl80211.h36
-rw-r--r--include/net/cfg80211.h47
-rw-r--r--net/wireless/chan.c41
-rw-r--r--net/wireless/core.h3
-rw-r--r--net/wireless/mlme.c27
-rw-r--r--net/wireless/nl80211.c218
-rw-r--r--net/wireless/nl80211.h11
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,
2147void cfg80211_disconnected(struct net_device *dev, u16 reason, 2166void 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 */
2179void 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 */
2192void 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
44int rdev_set_freq(struct cfg80211_registered_device *rdev, 44struct ieee80211_channel *
45 struct wireless_dev *for_wdev, 45rdev_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
78int 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);
374struct ieee80211_channel * 374struct ieee80211_channel *
375rdev_fixed_channel(struct cfg80211_registered_device *rdev, 375rdev_fixed_channel(struct cfg80211_registered_device *rdev,
376 struct wireless_dev *for_wdev); 376 struct wireless_dev *for_wdev);
377struct ieee80211_channel *
378rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
379 int freq, enum nl80211_channel_type channel_type);
377int rdev_set_freq(struct cfg80211_registered_device *rdev, 380int 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
684void 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}
695EXPORT_SYMBOL(cfg80211_ready_on_channel);
696
697void 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}
709EXPORT_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
4289static 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
4387static 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
4286static struct genl_ops nl80211_ops[] = { 4426static 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
4550static struct genl_multicast_group nl80211_mlme_mcgrp = { 4702static 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
5289static 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
5332void 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
5343void 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
5139int nl80211_init(void) 5355int 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
62void 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);
68void 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 */