aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-11-25 04:02:29 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-11-29 15:24:35 -0500
commitf7ca38dfe58c20cb1aa2ed9643187e8b194b5bae (patch)
tree42db54588fd5fa769a16def75708117903577b40
parent9a67d761b39614c0495dcab9a204e21a9f4c4d31 (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>
-rw-r--r--include/linux/nl80211.h25
-rw-r--r--include/net/cfg80211.h11
-rw-r--r--net/mac80211/cfg.c7
-rw-r--r--net/wireless/core.h4
-rw-r--r--net/wireless/mlme.c9
-rw-r--r--net/wireless/nl80211.c57
6 files changed, 91 insertions, 22 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index d706bf3badc8..5cfa579df476 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -358,11 +358,16 @@
358 * user space application). %NL80211_ATTR_FRAME is used to specify the 358 * user space application). %NL80211_ATTR_FRAME is used to specify the
359 * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and 359 * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and
360 * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on 360 * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on
361 * which channel the frame is to be transmitted or was received. This 361 * which channel the frame is to be transmitted or was received. If this
362 * channel has to be the current channel (remain-on-channel or the 362 * channel is not the current channel (remain-on-channel or the
363 * operational channel). When called, this operation returns a cookie 363 * operational channel) the device will switch to the given channel and
364 * (%NL80211_ATTR_COOKIE) that will be included with the TX status event 364 * transmit the frame, optionally waiting for a response for the time
365 * pertaining to the TX request. 365 * specified using %NL80211_ATTR_DURATION. When called, this operation
366 * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the
367 * TX status event pertaining to the TX request.
368 * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
369 * command may be used with the corresponding cookie to cancel the wait
370 * time if it is known that it is no longer necessary.
366 * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility. 371 * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility.
367 * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame 372 * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame
368 * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies 373 * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
@@ -493,6 +498,8 @@ enum nl80211_commands {
493 NL80211_CMD_SET_CHANNEL, 498 NL80211_CMD_SET_CHANNEL,
494 NL80211_CMD_SET_WDS_PEER, 499 NL80211_CMD_SET_WDS_PEER,
495 500
501 NL80211_CMD_FRAME_WAIT_CANCEL,
502
496 /* add new commands above here */ 503 /* add new commands above here */
497 504
498 /* used to define NL80211_CMD_MAX below */ 505 /* used to define NL80211_CMD_MAX below */
@@ -828,6 +835,12 @@ enum nl80211_commands {
828 * 835 *
829 * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS 836 * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS
830 * 837 *
838 * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be
839 * transmitted on another channel when the channel given doesn't match
840 * the current channel. If the current channel doesn't match and this
841 * flag isn't set, the frame will be rejected. This is also used as an
842 * nl80211 capability flag.
843 *
831 * @NL80211_ATTR_MAX: highest attribute number currently defined 844 * @NL80211_ATTR_MAX: highest attribute number currently defined
832 * @__NL80211_ATTR_AFTER_LAST: internal use 845 * @__NL80211_ATTR_AFTER_LAST: internal use
833 */ 846 */
@@ -1002,6 +1015,8 @@ enum nl80211_attrs {
1002 1015
1003 NL80211_ATTR_MCAST_RATE, 1016 NL80211_ATTR_MCAST_RATE,
1004 1017
1018 NL80211_ATTR_OFFCHANNEL_TX_OK,
1019
1005 /* add attributes here, update the policy in nl80211.c */ 1020 /* add attributes here, update the policy in nl80211.c */
1006 1021
1007 __NL80211_ATTR_AFTER_LAST, 1022 __NL80211_ATTR_AFTER_LAST,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 0663945cfa48..49a7c53a48ca 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1134,7 +1134,9 @@ struct cfg80211_pmksa {
1134 * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation. 1134 * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation.
1135 * This allows the operation to be terminated prior to timeout based on 1135 * This allows the operation to be terminated prior to timeout based on
1136 * the duration value. 1136 * the duration value.
1137 * @mgmt_tx: Transmit a management frame 1137 * @mgmt_tx: Transmit a management frame.
1138 * @mgmt_tx_cancel_wait: Cancel the wait time from transmitting a management
1139 * frame on another channel
1138 * 1140 *
1139 * @testmode_cmd: run a test mode command 1141 * @testmode_cmd: run a test mode command
1140 * 1142 *
@@ -1291,10 +1293,13 @@ struct cfg80211_ops {
1291 u64 cookie); 1293 u64 cookie);
1292 1294
1293 int (*mgmt_tx)(struct wiphy *wiphy, struct net_device *dev, 1295 int (*mgmt_tx)(struct wiphy *wiphy, struct net_device *dev,
1294 struct ieee80211_channel *chan, 1296 struct ieee80211_channel *chan, bool offchan,
1295 enum nl80211_channel_type channel_type, 1297 enum nl80211_channel_type channel_type,
1296 bool channel_type_valid, 1298 bool channel_type_valid, unsigned int wait,
1297 const u8 *buf, size_t len, u64 *cookie); 1299 const u8 *buf, size_t len, u64 *cookie);
1300 int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy,
1301 struct net_device *dev,
1302 u64 cookie);
1298 1303
1299 int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, 1304 int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
1300 bool enabled, int timeout); 1305 bool enabled, int timeout);
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
1554static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, 1554static 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);
341void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); 341void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
342int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, 342int 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
865int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, 865int 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
953bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, 954bool 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
4322static 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
4310static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) 4347static 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,