diff options
author | Johannes Berg <johannes.berg@intel.com> | 2010-11-25 04:02:29 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-11-29 15:24:35 -0500 |
commit | f7ca38dfe58c20cb1aa2ed9643187e8b194b5bae (patch) | |
tree | 42db54588fd5fa769a16def75708117903577b40 /net | |
parent | 9a67d761b39614c0495dcab9a204e21a9f4c4d31 (diff) |
nl80211/cfg80211: extend mgmt-tx API for off-channel
With p2p, it is sometimes necessary to transmit
a frame (typically an action frame) on another
channel than the current channel. Enable this
through the CMD_FRAME API, and allow it to wait
for a response. A new command allows that wait
to be aborted.
However, allow userspace to specify whether or
not it wants to allow off-channel TX, it may
actually want to use the same channel only.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/cfg.c | 7 | ||||
-rw-r--r-- | net/wireless/core.h | 4 | ||||
-rw-r--r-- | net/wireless/mlme.c | 9 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 57 |
4 files changed, 63 insertions, 14 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0c544074479e..aac2d7de828e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1552,9 +1552,9 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, | |||
1552 | } | 1552 | } |
1553 | 1553 | ||
1554 | static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, | 1554 | static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, |
1555 | struct ieee80211_channel *chan, | 1555 | struct ieee80211_channel *chan, bool offchan, |
1556 | enum nl80211_channel_type channel_type, | 1556 | enum nl80211_channel_type channel_type, |
1557 | bool channel_type_valid, | 1557 | bool channel_type_valid, unsigned int wait, |
1558 | const u8 *buf, size_t len, u64 *cookie) | 1558 | const u8 *buf, size_t len, u64 *cookie) |
1559 | { | 1559 | { |
1560 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 1560 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
@@ -1565,6 +1565,9 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, | |||
1565 | u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | | 1565 | u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | |
1566 | IEEE80211_TX_CTL_REQ_TX_STATUS; | 1566 | IEEE80211_TX_CTL_REQ_TX_STATUS; |
1567 | 1567 | ||
1568 | if (offchan) | ||
1569 | return -EOPNOTSUPP; | ||
1570 | |||
1568 | /* Check that we are on the requested channel for transmission */ | 1571 | /* Check that we are on the requested channel for transmission */ |
1569 | if (chan != local->tmp_channel && | 1572 | if (chan != local->tmp_channel && |
1570 | chan != local->oper_channel) | 1573 | chan != local->oper_channel) |
diff --git a/net/wireless/core.h b/net/wireless/core.h index 6583cca0e2ee..ee80ad8dc655 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -341,9 +341,9 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid); | |||
341 | void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); | 341 | void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); |
342 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, | 342 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, |
343 | struct net_device *dev, | 343 | struct net_device *dev, |
344 | struct ieee80211_channel *chan, | 344 | struct ieee80211_channel *chan, bool offchan, |
345 | enum nl80211_channel_type channel_type, | 345 | enum nl80211_channel_type channel_type, |
346 | bool channel_type_valid, | 346 | bool channel_type_valid, unsigned int wait, |
347 | const u8 *buf, size_t len, u64 *cookie); | 347 | const u8 *buf, size_t len, u64 *cookie); |
348 | 348 | ||
349 | /* SME */ | 349 | /* SME */ |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 6980a0c315b2..d7680f2a4c5b 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -864,9 +864,9 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) | |||
864 | 864 | ||
865 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, | 865 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, |
866 | struct net_device *dev, | 866 | struct net_device *dev, |
867 | struct ieee80211_channel *chan, | 867 | struct ieee80211_channel *chan, bool offchan, |
868 | enum nl80211_channel_type channel_type, | 868 | enum nl80211_channel_type channel_type, |
869 | bool channel_type_valid, | 869 | bool channel_type_valid, unsigned int wait, |
870 | const u8 *buf, size_t len, u64 *cookie) | 870 | const u8 *buf, size_t len, u64 *cookie) |
871 | { | 871 | { |
872 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 872 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
@@ -946,8 +946,9 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, | |||
946 | return -EINVAL; | 946 | return -EINVAL; |
947 | 947 | ||
948 | /* Transmit the Action frame as requested by user space */ | 948 | /* Transmit the Action frame as requested by user space */ |
949 | return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, channel_type, | 949 | return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan, |
950 | channel_type_valid, buf, len, cookie); | 950 | channel_type, channel_type_valid, |
951 | wait, buf, len, cookie); | ||
951 | } | 952 | } |
952 | 953 | ||
953 | bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, | 954 | bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 67ff7e92cb99..960be4e650f0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -163,16 +163,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
163 | [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, | 163 | [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, |
164 | [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, | 164 | [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, |
165 | [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, | 165 | [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, |
166 | |||
167 | [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 }, | 166 | [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 }, |
168 | [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 }, | 167 | [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 }, |
169 | |||
170 | [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 }, | 168 | [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 }, |
171 | |||
172 | [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 }, | 169 | [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 }, |
173 | [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 }, | 170 | [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 }, |
174 | |||
175 | [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, | 171 | [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, |
172 | [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, | ||
176 | }; | 173 | }; |
177 | 174 | ||
178 | /* policy for the key attributes */ | 175 | /* policy for the key attributes */ |
@@ -677,6 +674,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
677 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); | 674 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); |
678 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); | 675 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); |
679 | CMD(mgmt_tx, FRAME); | 676 | CMD(mgmt_tx, FRAME); |
677 | CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); | ||
680 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | 678 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { |
681 | i++; | 679 | i++; |
682 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 680 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
@@ -698,6 +696,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
698 | 696 | ||
699 | nla_nest_end(msg, nl_cmds); | 697 | nla_nest_end(msg, nl_cmds); |
700 | 698 | ||
699 | /* for now at least assume all drivers have it */ | ||
700 | if (dev->ops->mgmt_tx) | ||
701 | NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); | ||
702 | |||
701 | if (mgmt_stypes) { | 703 | if (mgmt_stypes) { |
702 | u16 stypes; | 704 | u16 stypes; |
703 | struct nlattr *nl_ftypes, *nl_ifs; | 705 | struct nlattr *nl_ftypes, *nl_ifs; |
@@ -4244,6 +4246,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) | |||
4244 | void *hdr; | 4246 | void *hdr; |
4245 | u64 cookie; | 4247 | u64 cookie; |
4246 | struct sk_buff *msg; | 4248 | struct sk_buff *msg; |
4249 | unsigned int wait = 0; | ||
4250 | bool offchan; | ||
4247 | 4251 | ||
4248 | if (!info->attrs[NL80211_ATTR_FRAME] || | 4252 | if (!info->attrs[NL80211_ATTR_FRAME] || |
4249 | !info->attrs[NL80211_ATTR_WIPHY_FREQ]) | 4253 | !info->attrs[NL80211_ATTR_WIPHY_FREQ]) |
@@ -4260,6 +4264,12 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) | |||
4260 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 4264 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
4261 | return -EOPNOTSUPP; | 4265 | return -EOPNOTSUPP; |
4262 | 4266 | ||
4267 | if (info->attrs[NL80211_ATTR_DURATION]) { | ||
4268 | if (!rdev->ops->mgmt_tx_cancel_wait) | ||
4269 | return -EINVAL; | ||
4270 | wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); | ||
4271 | } | ||
4272 | |||
4263 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | 4273 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { |
4264 | channel_type = nla_get_u32( | 4274 | channel_type = nla_get_u32( |
4265 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | 4275 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); |
@@ -4271,6 +4281,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) | |||
4271 | channel_type_valid = true; | 4281 | channel_type_valid = true; |
4272 | } | 4282 | } |
4273 | 4283 | ||
4284 | offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; | ||
4285 | |||
4274 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | 4286 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); |
4275 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | 4287 | chan = rdev_freq_to_chan(rdev, freq, channel_type); |
4276 | if (chan == NULL) | 4288 | if (chan == NULL) |
@@ -4287,8 +4299,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) | |||
4287 | err = PTR_ERR(hdr); | 4299 | err = PTR_ERR(hdr); |
4288 | goto free_msg; | 4300 | goto free_msg; |
4289 | } | 4301 | } |
4290 | err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, channel_type, | 4302 | err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type, |
4291 | channel_type_valid, | 4303 | channel_type_valid, wait, |
4292 | nla_data(info->attrs[NL80211_ATTR_FRAME]), | 4304 | nla_data(info->attrs[NL80211_ATTR_FRAME]), |
4293 | nla_len(info->attrs[NL80211_ATTR_FRAME]), | 4305 | nla_len(info->attrs[NL80211_ATTR_FRAME]), |
4294 | &cookie); | 4306 | &cookie); |
@@ -4307,6 +4319,31 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) | |||
4307 | return err; | 4319 | return err; |
4308 | } | 4320 | } |
4309 | 4321 | ||
4322 | static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info) | ||
4323 | { | ||
4324 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
4325 | struct net_device *dev = info->user_ptr[1]; | ||
4326 | u64 cookie; | ||
4327 | |||
4328 | if (!info->attrs[NL80211_ATTR_COOKIE]) | ||
4329 | return -EINVAL; | ||
4330 | |||
4331 | if (!rdev->ops->mgmt_tx_cancel_wait) | ||
4332 | return -EOPNOTSUPP; | ||
4333 | |||
4334 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && | ||
4335 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && | ||
4336 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && | ||
4337 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | ||
4338 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && | ||
4339 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | ||
4340 | return -EOPNOTSUPP; | ||
4341 | |||
4342 | cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); | ||
4343 | |||
4344 | return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie); | ||
4345 | } | ||
4346 | |||
4310 | static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) | 4347 | static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) |
4311 | { | 4348 | { |
4312 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 4349 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
@@ -4880,6 +4917,14 @@ static struct genl_ops nl80211_ops[] = { | |||
4880 | NL80211_FLAG_NEED_RTNL, | 4917 | NL80211_FLAG_NEED_RTNL, |
4881 | }, | 4918 | }, |
4882 | { | 4919 | { |
4920 | .cmd = NL80211_CMD_FRAME_WAIT_CANCEL, | ||
4921 | .doit = nl80211_tx_mgmt_cancel_wait, | ||
4922 | .policy = nl80211_policy, | ||
4923 | .flags = GENL_ADMIN_PERM, | ||
4924 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
4925 | NL80211_FLAG_NEED_RTNL, | ||
4926 | }, | ||
4927 | { | ||
4883 | .cmd = NL80211_CMD_SET_POWER_SAVE, | 4928 | .cmd = NL80211_CMD_SET_POWER_SAVE, |
4884 | .doit = nl80211_set_power_save, | 4929 | .doit = nl80211_set_power_save, |
4885 | .policy = nl80211_policy, | 4930 | .policy = nl80211_policy, |