Skip to content

Commit

Permalink
cfg80211: introduce nl80211 testmode command
Browse files Browse the repository at this point in the history
This introduces a new NL80211_CMD_TESTMODE for testing
and calibration use with nl80211. There's no multiplexing
like like iwpriv had, and the command is not available by
default, it needs to be explicitly enabled in Kconfig and
shouldn't be enabled in most kernels.

The command requires a wiphy index or interface index to
identify the device to operate on, and the new TESTDATA
attribute. There also is API for sending replies to the
command, and testmode multicast messages (on a testmode
multicast group).

I've also updated mac80211 to be able to pass through the
command to the driver, since it itself doesn't implement
the testmode command.

Additionally, to give people an idea of how to use the
command, I've added a little code to hwsim that makes use
of the new command to set the powersave mode, this is
currently done via debugfs and should remain there, and
the testmode command only serves as an example of how to
use this best -- with nested netlink attributes in the
TESTDATA attribute. A hwsim testmode tool can be found at
http://git.sipsolutions.net/hwsim.git/. This tool is BSD
licensed so people can easily use it as a basis for their
own internal fabrication and validation tools.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Johannes Berg authored and John W. Linville committed Jul 10, 2009
1 parent 5121ea0 commit aff89a9
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 0 deletions.
68 changes: 68 additions & 0 deletions drivers/net/wireless/mac80211_hwsim.c
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,73 @@ static int mac80211_hwsim_conf_tx(
return 0;
}

#ifdef CONFIG_NL80211_TESTMODE
/*
* This section contains example code for using netlink
* attributes with the testmode command in nl80211.
*/

/* These enums need to be kept in sync with userspace */
enum hwsim_testmode_attr {
__HWSIM_TM_ATTR_INVALID = 0,
HWSIM_TM_ATTR_CMD = 1,
HWSIM_TM_ATTR_PS = 2,

/* keep last */
__HWSIM_TM_ATTR_AFTER_LAST,
HWSIM_TM_ATTR_MAX = __HWSIM_TM_ATTR_AFTER_LAST - 1
};

enum hwsim_testmode_cmd {
HWSIM_TM_CMD_SET_PS = 0,
HWSIM_TM_CMD_GET_PS = 1,
};

static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = {
[HWSIM_TM_ATTR_CMD] = { .type = NLA_U32 },
[HWSIM_TM_ATTR_PS] = { .type = NLA_U32 },
};

static int hwsim_fops_ps_write(void *dat, u64 val);

int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
struct nlattr *tb[HWSIM_TM_ATTR_MAX + 1];
struct sk_buff *skb;
int err, ps;

err = nla_parse(tb, HWSIM_TM_ATTR_MAX, data, len,
hwsim_testmode_policy);
if (err)
return err;

if (!tb[HWSIM_TM_ATTR_CMD])
return -EINVAL;

switch (nla_get_u32(tb[HWSIM_TM_ATTR_CMD])) {
case HWSIM_TM_CMD_SET_PS:
if (!tb[HWSIM_TM_ATTR_PS])
return -EINVAL;
ps = nla_get_u32(tb[HWSIM_TM_ATTR_PS]);
return hwsim_fops_ps_write(hwsim, ps);
case HWSIM_TM_CMD_GET_PS:
skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
nla_total_size(sizeof(u32)));
if (!skb)
return -ENOMEM;
NLA_PUT_U32(skb, HWSIM_TM_ATTR_PS, hwsim->ps);
return cfg80211_testmode_reply(skb);
default:
return -EOPNOTSUPP;
}

nla_put_failure:
kfree_skb(skb);
return -ENOBUFS;
}
#endif

static const struct ieee80211_ops mac80211_hwsim_ops =
{
.tx = mac80211_hwsim_tx,
Expand All @@ -713,6 +780,7 @@ static const struct ieee80211_ops mac80211_hwsim_ops =
.sta_notify = mac80211_hwsim_sta_notify,
.set_tim = mac80211_hwsim_set_tim,
.conf_tx = mac80211_hwsim_conf_tx,
CFG80211_TESTMODE_CMD(mac80211_hwsim_testmode_cmd)
};


Expand Down
11 changes: 11 additions & 0 deletions include/linux/nl80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@
* @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is
* determined by the network interface.
*
* @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute
* to identify the device, and the TESTDATA blob attribute to pass through
* to the driver.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
Expand Down Expand Up @@ -310,6 +314,8 @@ enum nl80211_commands {
NL80211_CMD_JOIN_IBSS,
NL80211_CMD_LEAVE_IBSS,

NL80211_CMD_TESTMODE,

/* add new commands above here */

/* used to define NL80211_CMD_MAX below */
Expand Down Expand Up @@ -511,6 +517,9 @@ enum nl80211_commands {
* authorized by user space. Otherwise, port is marked authorized by
* default in station mode.
*
* @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
* We recommend using nested, driver-specific attributes within this.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
Expand Down Expand Up @@ -619,6 +628,8 @@ enum nl80211_attrs {

NL80211_ATTR_CONTROL_PORT,

NL80211_ATTR_TESTDATA,

/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
Expand Down
83 changes: 83 additions & 0 deletions include/net/cfg80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,8 @@ enum tx_power_setting {
*
* @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting
* functions to adjust rfkill hw state
*
* @testmode_cmd: run a test mode command
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy);
Expand Down Expand Up @@ -955,6 +957,10 @@ struct cfg80211_ops {
int (*get_tx_power)(struct wiphy *wiphy, int *dbm);

void (*rfkill_poll)(struct wiphy *wiphy);

#ifdef CONFIG_NL80211_TESTMODE
int (*testmode_cmd)(struct wiphy *wiphy, void *data, int len);
#endif
};

/*
Expand Down Expand Up @@ -1705,4 +1711,81 @@ void wiphy_rfkill_start_polling(struct wiphy *wiphy);
*/
void wiphy_rfkill_stop_polling(struct wiphy *wiphy);

#ifdef CONFIG_NL80211_TESTMODE
/**
* cfg80211_testmode_alloc_reply_skb - allocate testmode reply
* @wiphy: the wiphy
* @approxlen: an upper bound of the length of the data that will
* be put into the skb
*
* This function allocates and pre-fills an skb for a reply to
* the testmode command. Since it is intended for a reply, calling
* it outside of the @testmode_cmd operation is invalid.
*
* The returned skb (or %NULL if any errors happen) is pre-filled
* with the wiphy index and set up in a way that any data that is
* put into the skb (with skb_put(), nla_put() or similar) will end
* up being within the %NL80211_ATTR_TESTDATA attribute, so all that
* needs to be done with the skb is adding data for the corresponding
* userspace tool which can then read that data out of the testdata
* attribute. You must not modify the skb in any other way.
*
* When done, call cfg80211_testmode_reply() with the skb and return
* its error code as the result of the @testmode_cmd operation.
*/
struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
int approxlen);

/**
* cfg80211_testmode_reply - send the reply skb
* @skb: The skb, must have been allocated with
* cfg80211_testmode_alloc_reply_skb()
*
* Returns an error code or 0 on success, since calling this
* function will usually be the last thing before returning
* from the @testmode_cmd you should return the error code.
* Note that this function consumes the skb regardless of the
* return value.
*/
int cfg80211_testmode_reply(struct sk_buff *skb);

/**
* cfg80211_testmode_alloc_event_skb - allocate testmode event
* @wiphy: the wiphy
* @approxlen: an upper bound of the length of the data that will
* be put into the skb
* @gfp: allocation flags
*
* This function allocates and pre-fills an skb for an event on the
* testmode multicast group.
*
* The returned skb (or %NULL if any errors happen) is set up in the
* same way as with cfg80211_testmode_alloc_reply_skb() but prepared
* for an event. As there, you should simply add data to it that will
* then end up in the %NL80211_ATTR_TESTDATA attribute. Again, you must
* not modify the skb in any other way.
*
* When done filling the skb, call cfg80211_testmode_event() with the
* skb to send the event.
*/
struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
int approxlen, gfp_t gfp);

/**
* cfg80211_testmode_event - send the event
* @skb: The skb, must have been allocated with
* cfg80211_testmode_alloc_event_skb()
* @gfp: allocation flags
*
* This function sends the given @skb, which must have been allocated
* by cfg80211_testmode_alloc_event_skb(), as an event. It always
* consumes it.
*/
void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp);

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

#endif /* __NET_CFG80211_H */
5 changes: 5 additions & 0 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,8 @@ enum ieee80211_ampdu_mlme_action {
* @rfkill_poll: Poll rfkill hardware state. If you need this, you also
* need to set wiphy->rfkill_poll to %true before registration,
* and need to call wiphy_rfkill_set_hw_state() in the callback.
*
* @testmode_cmd: Implement a cfg80211 test mode command.
*/
struct ieee80211_ops {
int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
Expand Down Expand Up @@ -1466,6 +1468,9 @@ struct ieee80211_ops {
struct ieee80211_sta *sta, u16 tid, u16 *ssn);

void (*rfkill_poll)(struct ieee80211_hw *hw);
#ifdef CONFIG_NL80211_TESTMODE
int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len);
#endif
};

/**
Expand Down
13 changes: 13 additions & 0 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -1376,6 +1376,18 @@ static void ieee80211_rfkill_poll(struct wiphy *wiphy)
drv_rfkill_poll(local);
}

#ifdef CONFIG_NL80211_TESTMODE
int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len)
{
struct ieee80211_local *local = wiphy_priv(wiphy);

if (!local->ops->testmode_cmd)
return -EOPNOTSUPP;

return local->ops->testmode_cmd(&local->hw, data, len);
}
#endif

struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
Expand Down Expand Up @@ -1418,4 +1430,5 @@ struct cfg80211_ops mac80211_config_ops = {
.set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power,
.rfkill_poll = ieee80211_rfkill_poll,
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
};
15 changes: 15 additions & 0 deletions net/wireless/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@ config CFG80211
tristate "Improved wireless configuration API"
depends on RFKILL || !RFKILL

config NL80211_TESTMODE
bool "nl80211 testmode command"
depends on CFG80211
help
The nl80211 testmode command helps implementing things like
factory calibration or validation tools for wireless chips.

Select this option ONLY for kernels that are specifically
built for such purposes.

Debugging tools that are supposed to end up in the hands of
users should better be implemented with debugfs.

Say N.

config CFG80211_REG_DEBUG
bool "cfg80211 regulatory debugging"
depends on CFG80211
Expand Down
4 changes: 4 additions & 0 deletions net/wireless/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ struct cfg80211_registered_device {
struct cfg80211_scan_request *scan_req; /* protected by RTNL */
unsigned long suspend_at;

#ifdef CONFIG_NL80211_TESTMODE
struct genl_info *testmode_info;
#endif

#ifdef CONFIG_CFG80211_DEBUGFS
/* Debugfs entries */
struct wiphy_debugfsdentries {
Expand Down
Loading

0 comments on commit aff89a9

Please sign in to comment.