diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 328112081358..b0495a1da22e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -148,6 +148,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
148 | [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, | 148 | [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, |
149 | .len = IEEE80211_MAX_DATA_LEN }, | 149 | .len = IEEE80211_MAX_DATA_LEN }, |
150 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, | 150 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, |
151 | [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, | ||
151 | }; | 152 | }; |
152 | 153 | ||
153 | /* policy for the attributes */ | 154 | /* policy for the attributes */ |
@@ -4663,6 +4664,124 @@ unlock_rtnl: | |||
4663 | return err; | 4664 | return err; |
4664 | } | 4665 | } |
4665 | 4666 | ||
4667 | static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) | ||
4668 | { | ||
4669 | struct cfg80211_registered_device *rdev; | ||
4670 | struct wireless_dev *wdev; | ||
4671 | struct net_device *dev; | ||
4672 | u8 ps_state; | ||
4673 | bool state; | ||
4674 | int err; | ||
4675 | |||
4676 | if (!info->attrs[NL80211_ATTR_PS_STATE]) { | ||
4677 | err = -EINVAL; | ||
4678 | goto out; | ||
4679 | } | ||
4680 | |||
4681 | ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]); | ||
4682 | |||
4683 | if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) { | ||
4684 | err = -EINVAL; | ||
4685 | goto out; | ||
4686 | } | ||
4687 | |||
4688 | rtnl_lock(); | ||
4689 | |||
4690 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4691 | if (err) | ||
4692 | goto unlock_rdev; | ||
4693 | |||
4694 | wdev = dev->ieee80211_ptr; | ||
4695 | |||
4696 | if (!rdev->ops->set_power_mgmt) { | ||
4697 | err = -EOPNOTSUPP; | ||
4698 | goto unlock_rdev; | ||
4699 | } | ||
4700 | |||
4701 | state = (ps_state == NL80211_PS_ENABLED) ? true : false; | ||
4702 | |||
4703 | if (state == wdev->ps) | ||
4704 | goto unlock_rdev; | ||
4705 | |||
4706 | wdev->ps = state; | ||
4707 | |||
4708 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps, | ||
4709 | wdev->ps_timeout)) | ||
4710 | /* assume this means it's off */ | ||
4711 | wdev->ps = false; | ||
4712 | |||
4713 | unlock_rdev: | ||
4714 | cfg80211_unlock_rdev(rdev); | ||
4715 | dev_put(dev); | ||
4716 | rtnl_unlock(); | ||
4717 | |||
4718 | out: | ||
4719 | return err; | ||
4720 | } | ||
4721 | |||
4722 | static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) | ||
4723 | { | ||
4724 | struct cfg80211_registered_device *rdev; | ||
4725 | enum nl80211_ps_state ps_state; | ||
4726 | struct wireless_dev *wdev; | ||
4727 | struct net_device *dev; | ||
4728 | struct sk_buff *msg; | ||
4729 | void *hdr; | ||
4730 | int err; | ||
4731 | |||
4732 | rtnl_lock(); | ||
4733 | |||
4734 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4735 | if (err) | ||
4736 | goto unlock_rtnl; | ||
4737 | |||
4738 | wdev = dev->ieee80211_ptr; | ||
4739 | |||
4740 | if (!rdev->ops->set_power_mgmt) { | ||
4741 | err = -EOPNOTSUPP; | ||
4742 | goto out; | ||
4743 | } | ||
4744 | |||
4745 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4746 | if (!msg) { | ||
4747 | err = -ENOMEM; | ||
4748 | goto out; | ||
4749 | } | ||
4750 | |||
4751 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4752 | NL80211_CMD_GET_POWER_SAVE); | ||
4753 | if (!hdr) { | ||
4754 | err = -ENOMEM; | ||
4755 | goto free_msg; | ||
4756 | } | ||
4757 | |||
4758 | if (wdev->ps) | ||
4759 | ps_state = NL80211_PS_ENABLED; | ||
4760 | else | ||
4761 | ps_state = NL80211_PS_DISABLED; | ||
4762 | |||
4763 | NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); | ||
4764 | |||
4765 | genlmsg_end(msg, hdr); | ||
4766 | err = genlmsg_reply(msg, info); | ||
4767 | goto out; | ||
4768 | |||
4769 | nla_put_failure: | ||
4770 | err = -ENOBUFS; | ||
4771 | |||
4772 | free_msg: | ||
4773 | nlmsg_free(msg); | ||
4774 | |||
4775 | out: | ||
4776 | cfg80211_unlock_rdev(rdev); | ||
4777 | dev_put(dev); | ||
4778 | |||
4779 | unlock_rtnl: | ||
4780 | rtnl_unlock(); | ||
4781 | |||
4782 | return err; | ||
4783 | } | ||
4784 | |||
4666 | static struct genl_ops nl80211_ops[] = { | 4785 | static struct genl_ops nl80211_ops[] = { |
4667 | { | 4786 | { |
4668 | .cmd = NL80211_CMD_GET_WIPHY, | 4787 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -4955,6 +5074,18 @@ static struct genl_ops nl80211_ops[] = { | |||
4955 | .policy = nl80211_policy, | 5074 | .policy = nl80211_policy, |
4956 | .flags = GENL_ADMIN_PERM, | 5075 | .flags = GENL_ADMIN_PERM, |
4957 | }, | 5076 | }, |
5077 | { | ||
5078 | .cmd = NL80211_CMD_SET_POWER_SAVE, | ||
5079 | .doit = nl80211_set_power_save, | ||
5080 | .policy = nl80211_policy, | ||
5081 | .flags = GENL_ADMIN_PERM, | ||
5082 | }, | ||
5083 | { | ||
5084 | .cmd = NL80211_CMD_GET_POWER_SAVE, | ||
5085 | .doit = nl80211_get_power_save, | ||
5086 | .policy = nl80211_policy, | ||
5087 | /* can be retrieved by unprivileged users */ | ||
5088 | }, | ||
4958 | }; | 5089 | }; |
4959 | 5090 | ||
4960 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5091 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |