Skip to content

Commit

Permalink
nl80211: add testmode dump support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Wey-Yi Guy authored and John W. Linville committed Jun 1, 2011
1 parent 2e5ef45 commit 71063f0
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 0 deletions.
11 changes: 11 additions & 0 deletions include/net/cfg80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,12 @@ struct cfg80211_wowlan {
* frame on another channel
*
* @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
*
Expand Down Expand Up @@ -1433,6 +1439,9 @@ struct cfg80211_ops {

#ifdef CONFIG_NL80211_TESTMODE
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

int (*set_bitrate_mask)(struct wiphy *wiphy,
Expand Down Expand Up @@ -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);

#define CFG80211_TESTMODE_CMD(cmd) .testmode_cmd = (cmd),
#define CFG80211_TESTMODE_DUMP(cmd) .testmode_dump = (cmd),
#else
#define CFG80211_TESTMODE_CMD(cmd)
#define CFG80211_TESTMODE_DUMP(cmd)
#endif

/**
Expand Down
4 changes: 4 additions & 0 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -1816,6 +1816,7 @@ enum ieee80211_ampdu_mlme_action {
*
* @testmode_cmd: Implement a cfg80211 test mode command.
* 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
* that the hardware queues are empty. If the parameter @drop is set
Expand Down Expand Up @@ -1936,6 +1937,9 @@ struct ieee80211_ops {
void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class);
#ifdef CONFIG_NL80211_TESTMODE
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
void (*flush)(struct ieee80211_hw *hw, bool drop);
void (*channel_switch)(struct ieee80211_hw *hw,
Expand Down
14 changes: 14 additions & 0 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

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

int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
Expand Down Expand Up @@ -2134,6 +2147,7 @@ struct cfg80211_ops mac80211_config_ops = {
.set_wds_peer = ieee80211_set_wds_peer,
.rfkill_poll = ieee80211_rfkill_poll,
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump)
.set_power_mgmt = ieee80211_set_power_mgmt,
.set_bitrate_mask = ieee80211_set_bitrate_mask,
.remain_on_channel = ieee80211_remain_on_channel,
Expand Down
88 changes: 88 additions & 0 deletions net/wireless/nl80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -4361,6 +4361,93 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
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 *
__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
int approxlen, u32 pid, u32 seq, gfp_t gfp)
Expand Down Expand Up @@ -5658,6 +5745,7 @@ static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_TESTMODE,
.doit = nl80211_testmode_do,
.dumpit = nl80211_testmode_dump,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WIPHY |
Expand Down

0 comments on commit 71063f0

Please sign in to comment.