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>
This commit is contained in:
parent
9a67d761b3
commit
f7ca38dfe5
6 changed files with 91 additions and 22 deletions
|
@ -1552,9 +1552,9 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
|
|||
}
|
||||
|
||||
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct ieee80211_channel *chan,
|
||||
struct ieee80211_channel *chan, bool offchan,
|
||||
enum nl80211_channel_type channel_type,
|
||||
bool channel_type_valid,
|
||||
bool channel_type_valid, unsigned int wait,
|
||||
const u8 *buf, size_t len, u64 *cookie)
|
||||
{
|
||||
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,
|
|||
u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
|
||||
IEEE80211_TX_CTL_REQ_TX_STATUS;
|
||||
|
||||
if (offchan)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Check that we are on the requested channel for transmission */
|
||||
if (chan != local->tmp_channel &&
|
||||
chan != local->oper_channel)
|
||||
|
|
|
@ -341,9 +341,9 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
|
|||
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
|
||||
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ieee80211_channel *chan,
|
||||
struct ieee80211_channel *chan, bool offchan,
|
||||
enum nl80211_channel_type channel_type,
|
||||
bool channel_type_valid,
|
||||
bool channel_type_valid, unsigned int wait,
|
||||
const u8 *buf, size_t len, u64 *cookie);
|
||||
|
||||
/* SME */
|
||||
|
|
|
@ -864,9 +864,9 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
|
|||
|
||||
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ieee80211_channel *chan,
|
||||
struct ieee80211_channel *chan, bool offchan,
|
||||
enum nl80211_channel_type channel_type,
|
||||
bool channel_type_valid,
|
||||
bool channel_type_valid, unsigned int wait,
|
||||
const u8 *buf, size_t len, u64 *cookie)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
@ -946,8 +946,9 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
|
|||
return -EINVAL;
|
||||
|
||||
/* Transmit the Action frame as requested by user space */
|
||||
return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, channel_type,
|
||||
channel_type_valid, buf, len, cookie);
|
||||
return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan,
|
||||
channel_type, channel_type_valid,
|
||||
wait, buf, len, cookie);
|
||||
}
|
||||
|
||||
bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf,
|
||||
|
|
|
@ -163,16 +163,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
|||
[NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
|
||||
[NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
|
||||
|
||||
[NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
|
||||
|
||||
[NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
|
||||
|
||||
[NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
|
||||
|
||||
[NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
|
@ -677,6 +674,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
|||
CMD(remain_on_channel, REMAIN_ON_CHANNEL);
|
||||
CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
|
||||
CMD(mgmt_tx, FRAME);
|
||||
CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
|
||||
if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
|
||||
i++;
|
||||
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,
|
|||
|
||||
nla_nest_end(msg, nl_cmds);
|
||||
|
||||
/* for now at least assume all drivers have it */
|
||||
if (dev->ops->mgmt_tx)
|
||||
NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
|
||||
|
||||
if (mgmt_stypes) {
|
||||
u16 stypes;
|
||||
struct nlattr *nl_ftypes, *nl_ifs;
|
||||
|
@ -4244,6 +4246,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
|
|||
void *hdr;
|
||||
u64 cookie;
|
||||
struct sk_buff *msg;
|
||||
unsigned int wait = 0;
|
||||
bool offchan;
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_FRAME] ||
|
||||
!info->attrs[NL80211_ATTR_WIPHY_FREQ])
|
||||
|
@ -4260,6 +4264,12 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
|
|||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (info->attrs[NL80211_ATTR_DURATION]) {
|
||||
if (!rdev->ops->mgmt_tx_cancel_wait)
|
||||
return -EINVAL;
|
||||
wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
|
||||
channel_type = nla_get_u32(
|
||||
info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
|
||||
|
@ -4271,6 +4281,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
|
|||
channel_type_valid = true;
|
||||
}
|
||||
|
||||
offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
|
||||
|
||||
freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
|
||||
chan = rdev_freq_to_chan(rdev, freq, channel_type);
|
||||
if (chan == NULL)
|
||||
|
@ -4287,8 +4299,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
|
|||
err = PTR_ERR(hdr);
|
||||
goto free_msg;
|
||||
}
|
||||
err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, channel_type,
|
||||
channel_type_valid,
|
||||
err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
|
||||
channel_type_valid, wait,
|
||||
nla_data(info->attrs[NL80211_ATTR_FRAME]),
|
||||
nla_len(info->attrs[NL80211_ATTR_FRAME]),
|
||||
&cookie);
|
||||
|
@ -4307,6 +4319,31 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
u64 cookie;
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_COOKIE])
|
||||
return -EINVAL;
|
||||
|
||||
if (!rdev->ops->mgmt_tx_cancel_wait)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
|
||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
|
||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
|
||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
|
||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
|
||||
|
||||
return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie);
|
||||
}
|
||||
|
||||
static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
|
@ -4879,6 +4916,14 @@ static struct genl_ops nl80211_ops[] = {
|
|||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
|
||||
.doit = nl80211_tx_mgmt_cancel_wait,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_SET_POWER_SAVE,
|
||||
.doit = nl80211_set_power_save,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue