Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 71063f0e authored by Wey-Yi Guy's avatar Wey-Yi Guy Committed by John W. Linville
Browse files

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: default avatarWey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 2e5ef459
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -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


/**
/**
+4 −0
Original line number Original line Diff line number Diff line
@@ -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,
+14 −0
Original line number Original line Diff line number Diff line
@@ -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,
+88 −0
Original line number Original line Diff line number Diff line
@@ -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 |