aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJouni Malinen <jouni.malinen@atheros.com>2009-12-23 07:15:41 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-12-28 16:55:02 -0500
commit9588bbd5529461a3dacd435bf239c84c3508f569 (patch)
treedd00011714bdefdef1f818ac7caf53682253a515 /net
parentb203ffc3a447eb8d9e6120b783ddee081b143061 (diff)
cfg80211: add remain-on-channel command
Add new commands for requesting the driver to remain awake on a specified channel for the specified amount of time (and another command to cancel such an operation). This can be used to implement userspace-controlled off-channel operations, like Public Action frame exchange on another channel than the operation channel. The off-channel operation should behave similarly to scan, i.e. the local station (if associated) moves into power save mode to request the AP to buffer frames for it and then moves to the other channel to allow the off-channel operation to be completed. The duration parameter can be used to request enough time to receive a response from the target station. Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com> Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-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
5 files changed, 285 insertions, 15 deletions
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 */