aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
authorLuciano Coelho <coelho@ti.com>2011-05-11 10:09:35 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-05-11 15:12:26 -0400
commit807f8a8c300435d5483e8d78df9dcdbc27333166 (patch)
tree1537d40e149d7a8712fe63d17ea3b51093bf03a1 /net/wireless/nl80211.c
parent6bdbdbf4a151a3a1333818cd17a7d7795e936041 (diff)
cfg80211/nl80211: add support for scheduled scans
Implement new functionality for scheduled scan offload. With this feature we can scan automatically at certain intervals. The idea is that the hardware can perform scan automatically and filter on desired results without waking up the host unnecessarily. Add NL80211_CMD_START_SCHED_SCAN and NL80211_CMD_STOP_SCHED_SCAN commands to the nl80211 interface. When results are available they are reported by NL80211_CMD_SCHED_SCAN_RESULTS events. The userspace is informed when the scheduled scan has stopped with a NL80211_CMD_SCHED_SCAN_STOPPED event, which can be triggered either by the driver or by a call to NL80211_CMD_STOP_SCHED_SCAN. Signed-off-by: Luciano Coelho <coelho@ti.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r--net/wireless/nl80211.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 95dd5832e719..4fac370284c0 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -761,6 +761,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
761 } 761 }
762 CMD(set_channel, SET_CHANNEL); 762 CMD(set_channel, SET_CHANNEL);
763 CMD(set_wds_peer, SET_WDS_PEER); 763 CMD(set_wds_peer, SET_WDS_PEER);
764 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
765 CMD(sched_scan_start, START_SCHED_SCAN);
764 766
765#undef CMD 767#undef CMD
766 768
@@ -3357,6 +3359,179 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3357 return err; 3359 return err;
3358} 3360}
3359 3361
3362static int nl80211_start_sched_scan(struct sk_buff *skb,
3363 struct genl_info *info)
3364{
3365 struct cfg80211_sched_scan_request *request;
3366 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3367 struct net_device *dev = info->user_ptr[1];
3368 struct cfg80211_ssid *ssid;
3369 struct ieee80211_channel *channel;
3370 struct nlattr *attr;
3371 struct wiphy *wiphy;
3372 int err, tmp, n_ssids = 0, n_channels, i;
3373 enum ieee80211_band band;
3374 size_t ie_len;
3375
3376 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3377 !rdev->ops->sched_scan_start)
3378 return -EOPNOTSUPP;
3379
3380 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3381 return -EINVAL;
3382
3383 if (rdev->sched_scan_req)
3384 return -EINPROGRESS;
3385
3386 wiphy = &rdev->wiphy;
3387
3388 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3389 n_channels = validate_scan_freqs(
3390 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
3391 if (!n_channels)
3392 return -EINVAL;
3393 } else {
3394 n_channels = 0;
3395
3396 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3397 if (wiphy->bands[band])
3398 n_channels += wiphy->bands[band]->n_channels;
3399 }
3400
3401 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3402 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3403 tmp)
3404 n_ssids++;
3405
3406 if (n_ssids > wiphy->max_scan_ssids)
3407 return -EINVAL;
3408
3409 if (info->attrs[NL80211_ATTR_IE])
3410 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3411 else
3412 ie_len = 0;
3413
3414 if (ie_len > wiphy->max_scan_ie_len)
3415 return -EINVAL;
3416
3417 request = kzalloc(sizeof(*request)
3418 + sizeof(*ssid) * n_ssids
3419 + sizeof(channel) * n_channels
3420 + ie_len, GFP_KERNEL);
3421 if (!request)
3422 return -ENOMEM;
3423
3424 if (n_ssids)
3425 request->ssids = (void *)&request->channels[n_channels];
3426 request->n_ssids = n_ssids;
3427 if (ie_len) {
3428 if (request->ssids)
3429 request->ie = (void *)(request->ssids + n_ssids);
3430 else
3431 request->ie = (void *)(request->channels + n_channels);
3432 }
3433
3434 i = 0;
3435 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3436 /* user specified, bail out if channel not found */
3437 nla_for_each_nested(attr,
3438 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
3439 tmp) {
3440 struct ieee80211_channel *chan;
3441
3442 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3443
3444 if (!chan) {
3445 err = -EINVAL;
3446 goto out_free;
3447 }
3448
3449 /* ignore disabled channels */
3450 if (chan->flags & IEEE80211_CHAN_DISABLED)
3451 continue;
3452
3453 request->channels[i] = chan;
3454 i++;
3455 }
3456 } else {
3457 /* all channels */
3458 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3459 int j;
3460 if (!wiphy->bands[band])
3461 continue;
3462 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
3463 struct ieee80211_channel *chan;
3464
3465 chan = &wiphy->bands[band]->channels[j];
3466
3467 if (chan->flags & IEEE80211_CHAN_DISABLED)
3468 continue;
3469
3470 request->channels[i] = chan;
3471 i++;
3472 }
3473 }
3474 }
3475
3476 if (!i) {
3477 err = -EINVAL;
3478 goto out_free;
3479 }
3480
3481 request->n_channels = i;
3482
3483 i = 0;
3484 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3485 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3486 tmp) {
3487 if (request->ssids[i].ssid_len >
3488 IEEE80211_MAX_SSID_LEN) {
3489 err = -EINVAL;
3490 goto out_free;
3491 }
3492 memcpy(request->ssids[i].ssid, nla_data(attr),
3493 nla_len(attr));
3494 request->ssids[i].ssid_len = nla_len(attr);
3495 i++;
3496 }
3497 }
3498
3499 if (info->attrs[NL80211_ATTR_IE]) {
3500 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3501 memcpy((void *)request->ie,
3502 nla_data(info->attrs[NL80211_ATTR_IE]),
3503 request->ie_len);
3504 }
3505
3506 request->dev = dev;
3507 request->wiphy = &rdev->wiphy;
3508
3509 err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
3510 if (!err) {
3511 rdev->sched_scan_req = request;
3512 nl80211_send_sched_scan(rdev, dev,
3513 NL80211_CMD_START_SCHED_SCAN);
3514 goto out;
3515 }
3516
3517out_free:
3518 kfree(request);
3519out:
3520 return err;
3521}
3522
3523static int nl80211_stop_sched_scan(struct sk_buff *skb,
3524 struct genl_info *info)
3525{
3526 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3527
3528 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3529 !rdev->ops->sched_scan_stop)
3530 return -EOPNOTSUPP;
3531
3532 return __cfg80211_stop_sched_scan(rdev, false);
3533}
3534
3360static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, 3535static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
3361 struct cfg80211_registered_device *rdev, 3536 struct cfg80211_registered_device *rdev,
3362 struct wireless_dev *wdev, 3537 struct wireless_dev *wdev,
@@ -5327,6 +5502,22 @@ static struct genl_ops nl80211_ops[] = {
5327 .dumpit = nl80211_dump_scan, 5502 .dumpit = nl80211_dump_scan,
5328 }, 5503 },
5329 { 5504 {
5505 .cmd = NL80211_CMD_START_SCHED_SCAN,
5506 .doit = nl80211_start_sched_scan,
5507 .policy = nl80211_policy,
5508 .flags = GENL_ADMIN_PERM,
5509 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5510 NL80211_FLAG_NEED_RTNL,
5511 },
5512 {
5513 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
5514 .doit = nl80211_stop_sched_scan,
5515 .policy = nl80211_policy,
5516 .flags = GENL_ADMIN_PERM,
5517 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5518 NL80211_FLAG_NEED_RTNL,
5519 },
5520 {
5330 .cmd = NL80211_CMD_AUTHENTICATE, 5521 .cmd = NL80211_CMD_AUTHENTICATE,
5331 .doit = nl80211_authenticate, 5522 .doit = nl80211_authenticate,
5332 .policy = nl80211_policy, 5523 .policy = nl80211_policy,
@@ -5652,6 +5843,28 @@ static int nl80211_send_scan_msg(struct sk_buff *msg,
5652 return -EMSGSIZE; 5843 return -EMSGSIZE;
5653} 5844}
5654 5845
5846static int
5847nl80211_send_sched_scan_msg(struct sk_buff *msg,
5848 struct cfg80211_registered_device *rdev,
5849 struct net_device *netdev,
5850 u32 pid, u32 seq, int flags, u32 cmd)
5851{
5852 void *hdr;
5853
5854 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
5855 if (!hdr)
5856 return -1;
5857
5858 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5859 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5860
5861 return genlmsg_end(msg, hdr);
5862
5863 nla_put_failure:
5864 genlmsg_cancel(msg, hdr);
5865 return -EMSGSIZE;
5866}
5867
5655void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, 5868void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
5656 struct net_device *netdev) 5869 struct net_device *netdev)
5657{ 5870{
@@ -5709,6 +5922,43 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
5709 nl80211_scan_mcgrp.id, GFP_KERNEL); 5922 nl80211_scan_mcgrp.id, GFP_KERNEL);
5710} 5923}
5711 5924
5925void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
5926 struct net_device *netdev)
5927{
5928 struct sk_buff *msg;
5929
5930 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5931 if (!msg)
5932 return;
5933
5934 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
5935 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
5936 nlmsg_free(msg);
5937 return;
5938 }
5939
5940 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5941 nl80211_scan_mcgrp.id, GFP_KERNEL);
5942}
5943
5944void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
5945 struct net_device *netdev, u32 cmd)
5946{
5947 struct sk_buff *msg;
5948
5949 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
5950 if (!msg)
5951 return;
5952
5953 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
5954 nlmsg_free(msg);
5955 return;
5956 }
5957
5958 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5959 nl80211_scan_mcgrp.id, GFP_KERNEL);
5960}
5961
5712/* 5962/*
5713 * This can happen on global regulatory changes or device specific settings 5963 * This can happen on global regulatory changes or device specific settings
5714 * based on custom world regulatory domains. 5964 * based on custom world regulatory domains.