diff options
author | David S. Miller <davem@davemloft.net> | 2010-02-26 02:26:21 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-26 02:26:21 -0500 |
commit | 19bc291c99f018bd4f2c38bbf69144086dca903f (patch) | |
tree | 9d3cf9bc0c5a78e363dc0547da8bcd1e7c394265 /net/wireless/nl80211.c | |
parent | 04488734806948624dabc4514f96f14cd75b9a50 (diff) | |
parent | 4a6967b88af02eebeedfbb91bc09160750225bb5 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Conflicts:
drivers/net/wireless/iwlwifi/iwl-core.h
drivers/net/wireless/rt2x00/rt2800pci.c
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 440 |
1 files changed, 403 insertions, 37 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a001ea32cb7..e447db04cf7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * This is the new netlink-based wireless configuration interface. | 2 | * This is the new netlink-based wireless configuration interface. |
3 | * | 3 | * |
4 | * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <linux/if.h> | 7 | #include <linux/if.h> |
@@ -145,6 +145,10 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
145 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, | 145 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, |
146 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, | 146 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, |
147 | [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, | 147 | [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, |
148 | [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, | ||
149 | .len = IEEE80211_MAX_DATA_LEN }, | ||
150 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, | ||
151 | [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, | ||
148 | }; | 152 | }; |
149 | 153 | ||
150 | /* policy for the attributes */ | 154 | /* policy for the attributes */ |
@@ -576,6 +580,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
576 | CMD(flush_pmksa, FLUSH_PMKSA); | 580 | CMD(flush_pmksa, FLUSH_PMKSA); |
577 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); | 581 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); |
578 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); | 582 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); |
583 | CMD(action, ACTION); | ||
579 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | 584 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { |
580 | i++; | 585 | i++; |
581 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 586 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
@@ -2009,6 +2014,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2009 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) | 2014 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) |
2010 | return -EINVAL; | 2015 | return -EINVAL; |
2011 | 2016 | ||
2017 | if (!info->attrs[NL80211_ATTR_STA_AID]) | ||
2018 | return -EINVAL; | ||
2019 | |||
2012 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | 2020 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); |
2013 | params.supported_rates = | 2021 | params.supported_rates = |
2014 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 2022 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
@@ -2017,11 +2025,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2017 | params.listen_interval = | 2025 | params.listen_interval = |
2018 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | 2026 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); |
2019 | 2027 | ||
2020 | if (info->attrs[NL80211_ATTR_STA_AID]) { | 2028 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); |
2021 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); | 2029 | if (!params.aid || params.aid > IEEE80211_MAX_AID) |
2022 | if (!params.aid || params.aid > IEEE80211_MAX_AID) | 2030 | return -EINVAL; |
2023 | return -EINVAL; | ||
2024 | } | ||
2025 | 2031 | ||
2026 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | 2032 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) |
2027 | params.ht_capa = | 2033 | params.ht_capa = |
@@ -2036,6 +2042,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2036 | if (err) | 2042 | if (err) |
2037 | goto out_rtnl; | 2043 | goto out_rtnl; |
2038 | 2044 | ||
2045 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | ||
2046 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { | ||
2047 | err = -EINVAL; | ||
2048 | goto out; | ||
2049 | } | ||
2050 | |||
2039 | err = get_vlan(info, rdev, ¶ms.vlan); | 2051 | err = get_vlan(info, rdev, ¶ms.vlan); |
2040 | if (err) | 2052 | if (err) |
2041 | goto out; | 2053 | goto out; |
@@ -2043,35 +2055,6 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
2043 | /* validate settings */ | 2055 | /* validate settings */ |
2044 | err = 0; | 2056 | err = 0; |
2045 | 2057 | ||
2046 | switch (dev->ieee80211_ptr->iftype) { | ||
2047 | case NL80211_IFTYPE_AP: | ||
2048 | case NL80211_IFTYPE_AP_VLAN: | ||
2049 | /* all ok but must have AID */ | ||
2050 | if (!params.aid) | ||
2051 | err = -EINVAL; | ||
2052 | break; | ||
2053 | case NL80211_IFTYPE_MESH_POINT: | ||
2054 | /* disallow things mesh doesn't support */ | ||
2055 | if (params.vlan) | ||
2056 | err = -EINVAL; | ||
2057 | if (params.aid) | ||
2058 | err = -EINVAL; | ||
2059 | if (params.ht_capa) | ||
2060 | err = -EINVAL; | ||
2061 | if (params.listen_interval >= 0) | ||
2062 | err = -EINVAL; | ||
2063 | if (params.supported_rates) | ||
2064 | err = -EINVAL; | ||
2065 | if (params.sta_flags_mask) | ||
2066 | err = -EINVAL; | ||
2067 | break; | ||
2068 | default: | ||
2069 | err = -EINVAL; | ||
2070 | } | ||
2071 | |||
2072 | if (err) | ||
2073 | goto out; | ||
2074 | |||
2075 | if (!rdev->ops->add_station) { | 2058 | if (!rdev->ops->add_station) { |
2076 | err = -EOPNOTSUPP; | 2059 | err = -EOPNOTSUPP; |
2077 | goto out; | 2060 | goto out; |
@@ -2112,8 +2095,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | |||
2112 | goto out_rtnl; | 2095 | goto out_rtnl; |
2113 | 2096 | ||
2114 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 2097 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
2115 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && | 2098 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { |
2116 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { | ||
2117 | err = -EINVAL; | 2099 | err = -EINVAL; |
2118 | goto out; | 2100 | goto out; |
2119 | } | 2101 | } |
@@ -4545,6 +4527,257 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, | |||
4545 | return err; | 4527 | return err; |
4546 | } | 4528 | } |
4547 | 4529 | ||
4530 | static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info) | ||
4531 | { | ||
4532 | struct cfg80211_registered_device *rdev; | ||
4533 | struct net_device *dev; | ||
4534 | int err; | ||
4535 | |||
4536 | if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) | ||
4537 | return -EINVAL; | ||
4538 | |||
4539 | if (nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]) < 1) | ||
4540 | return -EINVAL; | ||
4541 | |||
4542 | rtnl_lock(); | ||
4543 | |||
4544 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4545 | if (err) | ||
4546 | goto unlock_rtnl; | ||
4547 | |||
4548 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4549 | err = -EOPNOTSUPP; | ||
4550 | goto out; | ||
4551 | } | ||
4552 | |||
4553 | /* not much point in registering if we can't reply */ | ||
4554 | if (!rdev->ops->action) { | ||
4555 | err = -EOPNOTSUPP; | ||
4556 | goto out; | ||
4557 | } | ||
4558 | |||
4559 | err = cfg80211_mlme_register_action(dev->ieee80211_ptr, info->snd_pid, | ||
4560 | nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), | ||
4561 | nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); | ||
4562 | out: | ||
4563 | cfg80211_unlock_rdev(rdev); | ||
4564 | dev_put(dev); | ||
4565 | unlock_rtnl: | ||
4566 | rtnl_unlock(); | ||
4567 | return err; | ||
4568 | } | ||
4569 | |||
4570 | static int nl80211_action(struct sk_buff *skb, struct genl_info *info) | ||
4571 | { | ||
4572 | struct cfg80211_registered_device *rdev; | ||
4573 | struct net_device *dev; | ||
4574 | struct ieee80211_channel *chan; | ||
4575 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
4576 | u32 freq; | ||
4577 | int err; | ||
4578 | void *hdr; | ||
4579 | u64 cookie; | ||
4580 | struct sk_buff *msg; | ||
4581 | |||
4582 | if (!info->attrs[NL80211_ATTR_FRAME] || | ||
4583 | !info->attrs[NL80211_ATTR_WIPHY_FREQ]) | ||
4584 | return -EINVAL; | ||
4585 | |||
4586 | rtnl_lock(); | ||
4587 | |||
4588 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4589 | if (err) | ||
4590 | goto unlock_rtnl; | ||
4591 | |||
4592 | if (!rdev->ops->action) { | ||
4593 | err = -EOPNOTSUPP; | ||
4594 | goto out; | ||
4595 | } | ||
4596 | |||
4597 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4598 | err = -EOPNOTSUPP; | ||
4599 | goto out; | ||
4600 | } | ||
4601 | |||
4602 | if (!netif_running(dev)) { | ||
4603 | err = -ENETDOWN; | ||
4604 | goto out; | ||
4605 | } | ||
4606 | |||
4607 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
4608 | channel_type = nla_get_u32( | ||
4609 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
4610 | if (channel_type != NL80211_CHAN_NO_HT && | ||
4611 | channel_type != NL80211_CHAN_HT20 && | ||
4612 | channel_type != NL80211_CHAN_HT40PLUS && | ||
4613 | channel_type != NL80211_CHAN_HT40MINUS) | ||
4614 | err = -EINVAL; | ||
4615 | goto out; | ||
4616 | } | ||
4617 | |||
4618 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
4619 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
4620 | if (chan == NULL) { | ||
4621 | err = -EINVAL; | ||
4622 | goto out; | ||
4623 | } | ||
4624 | |||
4625 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4626 | if (!msg) { | ||
4627 | err = -ENOMEM; | ||
4628 | goto out; | ||
4629 | } | ||
4630 | |||
4631 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4632 | NL80211_CMD_ACTION); | ||
4633 | |||
4634 | if (IS_ERR(hdr)) { | ||
4635 | err = PTR_ERR(hdr); | ||
4636 | goto free_msg; | ||
4637 | } | ||
4638 | err = cfg80211_mlme_action(rdev, dev, chan, channel_type, | ||
4639 | nla_data(info->attrs[NL80211_ATTR_FRAME]), | ||
4640 | nla_len(info->attrs[NL80211_ATTR_FRAME]), | ||
4641 | &cookie); | ||
4642 | if (err) | ||
4643 | goto free_msg; | ||
4644 | |||
4645 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
4646 | |||
4647 | genlmsg_end(msg, hdr); | ||
4648 | err = genlmsg_reply(msg, info); | ||
4649 | goto out; | ||
4650 | |||
4651 | nla_put_failure: | ||
4652 | err = -ENOBUFS; | ||
4653 | free_msg: | ||
4654 | nlmsg_free(msg); | ||
4655 | out: | ||
4656 | cfg80211_unlock_rdev(rdev); | ||
4657 | dev_put(dev); | ||
4658 | unlock_rtnl: | ||
4659 | rtnl_unlock(); | ||
4660 | return err; | ||
4661 | } | ||
4662 | |||
4663 | static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) | ||
4664 | { | ||
4665 | struct cfg80211_registered_device *rdev; | ||
4666 | struct wireless_dev *wdev; | ||
4667 | struct net_device *dev; | ||
4668 | u8 ps_state; | ||
4669 | bool state; | ||
4670 | int err; | ||
4671 | |||
4672 | if (!info->attrs[NL80211_ATTR_PS_STATE]) { | ||
4673 | err = -EINVAL; | ||
4674 | goto out; | ||
4675 | } | ||
4676 | |||
4677 | ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]); | ||
4678 | |||
4679 | if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) { | ||
4680 | err = -EINVAL; | ||
4681 | goto out; | ||
4682 | } | ||
4683 | |||
4684 | rtnl_lock(); | ||
4685 | |||
4686 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4687 | if (err) | ||
4688 | goto unlock_rdev; | ||
4689 | |||
4690 | wdev = dev->ieee80211_ptr; | ||
4691 | |||
4692 | if (!rdev->ops->set_power_mgmt) { | ||
4693 | err = -EOPNOTSUPP; | ||
4694 | goto unlock_rdev; | ||
4695 | } | ||
4696 | |||
4697 | state = (ps_state == NL80211_PS_ENABLED) ? true : false; | ||
4698 | |||
4699 | if (state == wdev->ps) | ||
4700 | goto unlock_rdev; | ||
4701 | |||
4702 | wdev->ps = state; | ||
4703 | |||
4704 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps, | ||
4705 | wdev->ps_timeout)) | ||
4706 | /* assume this means it's off */ | ||
4707 | wdev->ps = false; | ||
4708 | |||
4709 | unlock_rdev: | ||
4710 | cfg80211_unlock_rdev(rdev); | ||
4711 | dev_put(dev); | ||
4712 | rtnl_unlock(); | ||
4713 | |||
4714 | out: | ||
4715 | return err; | ||
4716 | } | ||
4717 | |||
4718 | static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) | ||
4719 | { | ||
4720 | struct cfg80211_registered_device *rdev; | ||
4721 | enum nl80211_ps_state ps_state; | ||
4722 | struct wireless_dev *wdev; | ||
4723 | struct net_device *dev; | ||
4724 | struct sk_buff *msg; | ||
4725 | void *hdr; | ||
4726 | int err; | ||
4727 | |||
4728 | rtnl_lock(); | ||
4729 | |||
4730 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4731 | if (err) | ||
4732 | goto unlock_rtnl; | ||
4733 | |||
4734 | wdev = dev->ieee80211_ptr; | ||
4735 | |||
4736 | if (!rdev->ops->set_power_mgmt) { | ||
4737 | err = -EOPNOTSUPP; | ||
4738 | goto out; | ||
4739 | } | ||
4740 | |||
4741 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4742 | if (!msg) { | ||
4743 | err = -ENOMEM; | ||
4744 | goto out; | ||
4745 | } | ||
4746 | |||
4747 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4748 | NL80211_CMD_GET_POWER_SAVE); | ||
4749 | if (!hdr) { | ||
4750 | err = -ENOMEM; | ||
4751 | goto free_msg; | ||
4752 | } | ||
4753 | |||
4754 | if (wdev->ps) | ||
4755 | ps_state = NL80211_PS_ENABLED; | ||
4756 | else | ||
4757 | ps_state = NL80211_PS_DISABLED; | ||
4758 | |||
4759 | NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); | ||
4760 | |||
4761 | genlmsg_end(msg, hdr); | ||
4762 | err = genlmsg_reply(msg, info); | ||
4763 | goto out; | ||
4764 | |||
4765 | nla_put_failure: | ||
4766 | err = -ENOBUFS; | ||
4767 | |||
4768 | free_msg: | ||
4769 | nlmsg_free(msg); | ||
4770 | |||
4771 | out: | ||
4772 | cfg80211_unlock_rdev(rdev); | ||
4773 | dev_put(dev); | ||
4774 | |||
4775 | unlock_rtnl: | ||
4776 | rtnl_unlock(); | ||
4777 | |||
4778 | return err; | ||
4779 | } | ||
4780 | |||
4548 | static struct genl_ops nl80211_ops[] = { | 4781 | static struct genl_ops nl80211_ops[] = { |
4549 | { | 4782 | { |
4550 | .cmd = NL80211_CMD_GET_WIPHY, | 4783 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -4825,6 +5058,30 @@ static struct genl_ops nl80211_ops[] = { | |||
4825 | .policy = nl80211_policy, | 5058 | .policy = nl80211_policy, |
4826 | .flags = GENL_ADMIN_PERM, | 5059 | .flags = GENL_ADMIN_PERM, |
4827 | }, | 5060 | }, |
5061 | { | ||
5062 | .cmd = NL80211_CMD_REGISTER_ACTION, | ||
5063 | .doit = nl80211_register_action, | ||
5064 | .policy = nl80211_policy, | ||
5065 | .flags = GENL_ADMIN_PERM, | ||
5066 | }, | ||
5067 | { | ||
5068 | .cmd = NL80211_CMD_ACTION, | ||
5069 | .doit = nl80211_action, | ||
5070 | .policy = nl80211_policy, | ||
5071 | .flags = GENL_ADMIN_PERM, | ||
5072 | }, | ||
5073 | { | ||
5074 | .cmd = NL80211_CMD_SET_POWER_SAVE, | ||
5075 | .doit = nl80211_set_power_save, | ||
5076 | .policy = nl80211_policy, | ||
5077 | .flags = GENL_ADMIN_PERM, | ||
5078 | }, | ||
5079 | { | ||
5080 | .cmd = NL80211_CMD_GET_POWER_SAVE, | ||
5081 | .doit = nl80211_get_power_save, | ||
5082 | .policy = nl80211_policy, | ||
5083 | /* can be retrieved by unprivileged users */ | ||
5084 | }, | ||
4828 | }; | 5085 | }; |
4829 | 5086 | ||
4830 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5087 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
@@ -5497,6 +5754,110 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, | |||
5497 | nl80211_mlme_mcgrp.id, gfp); | 5754 | nl80211_mlme_mcgrp.id, gfp); |
5498 | } | 5755 | } |
5499 | 5756 | ||
5757 | int nl80211_send_action(struct cfg80211_registered_device *rdev, | ||
5758 | struct net_device *netdev, u32 nlpid, | ||
5759 | int freq, const u8 *buf, size_t len, gfp_t gfp) | ||
5760 | { | ||
5761 | struct sk_buff *msg; | ||
5762 | void *hdr; | ||
5763 | int err; | ||
5764 | |||
5765 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5766 | if (!msg) | ||
5767 | return -ENOMEM; | ||
5768 | |||
5769 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION); | ||
5770 | if (!hdr) { | ||
5771 | nlmsg_free(msg); | ||
5772 | return -ENOMEM; | ||
5773 | } | ||
5774 | |||
5775 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5776 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5777 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); | ||
5778 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
5779 | |||
5780 | err = genlmsg_end(msg, hdr); | ||
5781 | if (err < 0) { | ||
5782 | nlmsg_free(msg); | ||
5783 | return err; | ||
5784 | } | ||
5785 | |||
5786 | err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); | ||
5787 | if (err < 0) | ||
5788 | return err; | ||
5789 | return 0; | ||
5790 | |||
5791 | nla_put_failure: | ||
5792 | genlmsg_cancel(msg, hdr); | ||
5793 | nlmsg_free(msg); | ||
5794 | return -ENOBUFS; | ||
5795 | } | ||
5796 | |||
5797 | void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | ||
5798 | struct net_device *netdev, u64 cookie, | ||
5799 | const u8 *buf, size_t len, bool ack, | ||
5800 | gfp_t gfp) | ||
5801 | { | ||
5802 | struct sk_buff *msg; | ||
5803 | void *hdr; | ||
5804 | |||
5805 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5806 | if (!msg) | ||
5807 | return; | ||
5808 | |||
5809 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION_TX_STATUS); | ||
5810 | if (!hdr) { | ||
5811 | nlmsg_free(msg); | ||
5812 | return; | ||
5813 | } | ||
5814 | |||
5815 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5816 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5817 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
5818 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
5819 | if (ack) | ||
5820 | NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); | ||
5821 | |||
5822 | if (genlmsg_end(msg, hdr) < 0) { | ||
5823 | nlmsg_free(msg); | ||
5824 | return; | ||
5825 | } | ||
5826 | |||
5827 | genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); | ||
5828 | return; | ||
5829 | |||
5830 | nla_put_failure: | ||
5831 | genlmsg_cancel(msg, hdr); | ||
5832 | nlmsg_free(msg); | ||
5833 | } | ||
5834 | |||
5835 | static int nl80211_netlink_notify(struct notifier_block * nb, | ||
5836 | unsigned long state, | ||
5837 | void *_notify) | ||
5838 | { | ||
5839 | struct netlink_notify *notify = _notify; | ||
5840 | struct cfg80211_registered_device *rdev; | ||
5841 | struct wireless_dev *wdev; | ||
5842 | |||
5843 | if (state != NETLINK_URELEASE) | ||
5844 | return NOTIFY_DONE; | ||
5845 | |||
5846 | rcu_read_lock(); | ||
5847 | |||
5848 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) | ||
5849 | list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) | ||
5850 | cfg80211_mlme_unregister_actions(wdev, notify->pid); | ||
5851 | |||
5852 | rcu_read_unlock(); | ||
5853 | |||
5854 | return NOTIFY_DONE; | ||
5855 | } | ||
5856 | |||
5857 | static struct notifier_block nl80211_netlink_notifier = { | ||
5858 | .notifier_call = nl80211_netlink_notify, | ||
5859 | }; | ||
5860 | |||
5500 | /* initialisation/exit functions */ | 5861 | /* initialisation/exit functions */ |
5501 | 5862 | ||
5502 | int nl80211_init(void) | 5863 | int nl80211_init(void) |
@@ -5530,6 +5891,10 @@ int nl80211_init(void) | |||
5530 | goto err_out; | 5891 | goto err_out; |
5531 | #endif | 5892 | #endif |
5532 | 5893 | ||
5894 | err = netlink_register_notifier(&nl80211_netlink_notifier); | ||
5895 | if (err) | ||
5896 | goto err_out; | ||
5897 | |||
5533 | return 0; | 5898 | return 0; |
5534 | err_out: | 5899 | err_out: |
5535 | genl_unregister_family(&nl80211_fam); | 5900 | genl_unregister_family(&nl80211_fam); |
@@ -5538,5 +5903,6 @@ int nl80211_init(void) | |||
5538 | 5903 | ||
5539 | void nl80211_exit(void) | 5904 | void nl80211_exit(void) |
5540 | { | 5905 | { |
5906 | netlink_unregister_notifier(&nl80211_netlink_notifier); | ||
5541 | genl_unregister_family(&nl80211_fam); | 5907 | genl_unregister_family(&nl80211_fam); |
5542 | } | 5908 | } |