nl80211: add testmode dump support
This adds dump support to testmode. The testmode dump support in nl80211 requires using two of the six cb->args, the rest can be used by the driver to figure out where the dump position is at or to store other data across invocations. Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
2e5ef4599a
commit
71063f0e89
@ -1284,6 +1284,12 @@ struct cfg80211_wowlan {
|
|||||||
* frame on another channel
|
* frame on another channel
|
||||||
*
|
*
|
||||||
* @testmode_cmd: run a test mode command
|
* @testmode_cmd: run a test mode command
|
||||||
|
* @testmode_dump: Implement a test mode dump. The cb->args[2] and up may be
|
||||||
|
* used by the function, but 0 and 1 must not be touched. Additionally,
|
||||||
|
* return error codes other than -ENOBUFS and -ENOENT will terminate the
|
||||||
|
* dump and return to userspace with an error, so be careful. If any data
|
||||||
|
* was passed in from userspace then the data/len arguments will be present
|
||||||
|
* and point to the data contained in %NL80211_ATTR_TESTDATA.
|
||||||
*
|
*
|
||||||
* @set_bitrate_mask: set the bitrate mask configuration
|
* @set_bitrate_mask: set the bitrate mask configuration
|
||||||
*
|
*
|
||||||
@ -1433,6 +1439,9 @@ struct cfg80211_ops {
|
|||||||
|
|
||||||
#ifdef CONFIG_NL80211_TESTMODE
|
#ifdef CONFIG_NL80211_TESTMODE
|
||||||
int (*testmode_cmd)(struct wiphy *wiphy, void *data, int len);
|
int (*testmode_cmd)(struct wiphy *wiphy, void *data, int len);
|
||||||
|
int (*testmode_dump)(struct wiphy *wiphy, struct sk_buff *skb,
|
||||||
|
struct netlink_callback *cb,
|
||||||
|
void *data, int len);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int (*set_bitrate_mask)(struct wiphy *wiphy,
|
int (*set_bitrate_mask)(struct wiphy *wiphy,
|
||||||
@ -2849,8 +2858,10 @@ struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
|
|||||||
void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp);
|
void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp);
|
||||||
|
|
||||||
#define CFG80211_TESTMODE_CMD(cmd) .testmode_cmd = (cmd),
|
#define CFG80211_TESTMODE_CMD(cmd) .testmode_cmd = (cmd),
|
||||||
|
#define CFG80211_TESTMODE_DUMP(cmd) .testmode_dump = (cmd),
|
||||||
#else
|
#else
|
||||||
#define CFG80211_TESTMODE_CMD(cmd)
|
#define CFG80211_TESTMODE_CMD(cmd)
|
||||||
|
#define CFG80211_TESTMODE_DUMP(cmd)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1816,6 +1816,7 @@ enum ieee80211_ampdu_mlme_action {
|
|||||||
*
|
*
|
||||||
* @testmode_cmd: Implement a cfg80211 test mode command.
|
* @testmode_cmd: Implement a cfg80211 test mode command.
|
||||||
* The callback can sleep.
|
* The callback can sleep.
|
||||||
|
* @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep.
|
||||||
*
|
*
|
||||||
* @flush: Flush all pending frames from the hardware queue, making sure
|
* @flush: Flush all pending frames from the hardware queue, making sure
|
||||||
* that the hardware queues are empty. If the parameter @drop is set
|
* that the hardware queues are empty. If the parameter @drop is set
|
||||||
@ -1936,6 +1937,9 @@ struct ieee80211_ops {
|
|||||||
void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class);
|
void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class);
|
||||||
#ifdef CONFIG_NL80211_TESTMODE
|
#ifdef CONFIG_NL80211_TESTMODE
|
||||||
int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len);
|
int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len);
|
||||||
|
int (*testmode_dump)(struct ieee80211_hw *hw, struct sk_buff *skb,
|
||||||
|
struct netlink_callback *cb,
|
||||||
|
void *data, int len);
|
||||||
#endif
|
#endif
|
||||||
void (*flush)(struct ieee80211_hw *hw, bool drop);
|
void (*flush)(struct ieee80211_hw *hw, bool drop);
|
||||||
void (*channel_switch)(struct ieee80211_hw *hw,
|
void (*channel_switch)(struct ieee80211_hw *hw,
|
||||||
|
@ -1554,6 +1554,19 @@ static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len)
|
|||||||
|
|
||||||
return local->ops->testmode_cmd(&local->hw, data, len);
|
return local->ops->testmode_cmd(&local->hw, data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ieee80211_testmode_dump(struct wiphy *wiphy,
|
||||||
|
struct sk_buff *skb,
|
||||||
|
struct netlink_callback *cb,
|
||||||
|
void *data, int len)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||||
|
|
||||||
|
if (!local->ops->testmode_dump)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
return local->ops->testmode_dump(&local->hw, skb, cb, data, len);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
|
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
|
||||||
@ -2134,6 +2147,7 @@ struct cfg80211_ops mac80211_config_ops = {
|
|||||||
.set_wds_peer = ieee80211_set_wds_peer,
|
.set_wds_peer = ieee80211_set_wds_peer,
|
||||||
.rfkill_poll = ieee80211_rfkill_poll,
|
.rfkill_poll = ieee80211_rfkill_poll,
|
||||||
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
|
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
|
||||||
|
CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump)
|
||||||
.set_power_mgmt = ieee80211_set_power_mgmt,
|
.set_power_mgmt = ieee80211_set_power_mgmt,
|
||||||
.set_bitrate_mask = ieee80211_set_bitrate_mask,
|
.set_bitrate_mask = ieee80211_set_bitrate_mask,
|
||||||
.remain_on_channel = ieee80211_remain_on_channel,
|
.remain_on_channel = ieee80211_remain_on_channel,
|
||||||
|
@ -4361,6 +4361,93 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nl80211_testmode_dump(struct sk_buff *skb,
|
||||||
|
struct netlink_callback *cb)
|
||||||
|
{
|
||||||
|
struct cfg80211_registered_device *dev;
|
||||||
|
int err;
|
||||||
|
long phy_idx;
|
||||||
|
void *data = NULL;
|
||||||
|
int data_len = 0;
|
||||||
|
|
||||||
|
if (cb->args[0]) {
|
||||||
|
/*
|
||||||
|
* 0 is a valid index, but not valid for args[0],
|
||||||
|
* so we need to offset by 1.
|
||||||
|
*/
|
||||||
|
phy_idx = cb->args[0] - 1;
|
||||||
|
} else {
|
||||||
|
err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
|
||||||
|
nl80211_fam.attrbuf, nl80211_fam.maxattr,
|
||||||
|
nl80211_policy);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY])
|
||||||
|
return -EINVAL;
|
||||||
|
phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
|
||||||
|
if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
|
||||||
|
cb->args[1] =
|
||||||
|
(long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb->args[1]) {
|
||||||
|
data = nla_data((void *)cb->args[1]);
|
||||||
|
data_len = nla_len((void *)cb->args[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&cfg80211_mutex);
|
||||||
|
dev = cfg80211_rdev_by_wiphy_idx(phy_idx);
|
||||||
|
if (!dev) {
|
||||||
|
mutex_unlock(&cfg80211_mutex);
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
cfg80211_lock_rdev(dev);
|
||||||
|
mutex_unlock(&cfg80211_mutex);
|
||||||
|
|
||||||
|
if (!dev->ops->testmode_dump) {
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
|
||||||
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||||
|
NL80211_CMD_TESTMODE);
|
||||||
|
struct nlattr *tmdata;
|
||||||
|
|
||||||
|
if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) {
|
||||||
|
genlmsg_cancel(skb, hdr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
|
||||||
|
if (!tmdata) {
|
||||||
|
genlmsg_cancel(skb, hdr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
err = dev->ops->testmode_dump(&dev->wiphy, skb, cb,
|
||||||
|
data, data_len);
|
||||||
|
nla_nest_end(skb, tmdata);
|
||||||
|
|
||||||
|
if (err == -ENOBUFS || err == -ENOENT) {
|
||||||
|
genlmsg_cancel(skb, hdr);
|
||||||
|
break;
|
||||||
|
} else if (err) {
|
||||||
|
genlmsg_cancel(skb, hdr);
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
genlmsg_end(skb, hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = skb->len;
|
||||||
|
/* see above */
|
||||||
|
cb->args[0] = phy_idx + 1;
|
||||||
|
out_err:
|
||||||
|
cfg80211_unlock_rdev(dev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static struct sk_buff *
|
static struct sk_buff *
|
||||||
__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
|
__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
|
||||||
int approxlen, u32 pid, u32 seq, gfp_t gfp)
|
int approxlen, u32 pid, u32 seq, gfp_t gfp)
|
||||||
@ -5658,6 +5745,7 @@ static struct genl_ops nl80211_ops[] = {
|
|||||||
{
|
{
|
||||||
.cmd = NL80211_CMD_TESTMODE,
|
.cmd = NL80211_CMD_TESTMODE,
|
||||||
.doit = nl80211_testmode_do,
|
.doit = nl80211_testmode_do,
|
||||||
|
.dumpit = nl80211_testmode_dump,
|
||||||
.policy = nl80211_policy,
|
.policy = nl80211_policy,
|
||||||
.flags = GENL_ADMIN_PERM,
|
.flags = GENL_ADMIN_PERM,
|
||||||
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
||||||
|
Loading…
Reference in New Issue
Block a user