cfg80211: introduce nl80211 testmode command

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>
This commit is contained in:
Johannes Berg
2009-07-01 21:26:51 +02:00
committed by John W. Linville
parent 5121ea0481
commit aff89a9b90
8 changed files with 335 additions and 0 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -3416,6 +3416,128 @@ unlock_rtnl:
return err;
}
#ifdef CONFIG_NL80211_TESTMODE
static struct genl_multicast_group nl80211_testmode_mcgrp = {
.name = "testmode",
};
static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev;
int err;
if (!info->attrs[NL80211_ATTR_TESTDATA])
return -EINVAL;
rtnl_lock();
rdev = cfg80211_get_dev_from_info(info);
if (IS_ERR(rdev)) {
err = PTR_ERR(rdev);
goto unlock_rtnl;
}
err = -EOPNOTSUPP;
if (rdev->ops->testmode_cmd) {
rdev->testmode_info = info;
err = rdev->ops->testmode_cmd(&rdev->wiphy,
nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
rdev->testmode_info = NULL;
}
cfg80211_put_dev(rdev);
unlock_rtnl:
rtnl_unlock();
return err;
}
static struct sk_buff *
__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
int approxlen, u32 pid, u32 seq, gfp_t gfp)
{
struct sk_buff *skb;
void *hdr;
struct nlattr *data;
skb = nlmsg_new(approxlen + 100, gfp);
if (!skb)
return NULL;
hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
if (!hdr) {
kfree_skb(skb);
return NULL;
}
NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
((void **)skb->cb)[0] = rdev;
((void **)skb->cb)[1] = hdr;
((void **)skb->cb)[2] = data;
return skb;
nla_put_failure:
kfree_skb(skb);
return NULL;
}
struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
int approxlen)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
if (WARN_ON(!rdev->testmode_info))
return NULL;
return __cfg80211_testmode_alloc_skb(rdev, approxlen,
rdev->testmode_info->snd_pid,
rdev->testmode_info->snd_seq,
GFP_KERNEL);
}
EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
int cfg80211_testmode_reply(struct sk_buff *skb)
{
struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
void *hdr = ((void **)skb->cb)[1];
struct nlattr *data = ((void **)skb->cb)[2];
if (WARN_ON(!rdev->testmode_info)) {
kfree_skb(skb);
return -EINVAL;
}
nla_nest_end(skb, data);
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, rdev->testmode_info);
}
EXPORT_SYMBOL(cfg80211_testmode_reply);
struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
int approxlen, gfp_t gfp)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
}
EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
{
void *hdr = ((void **)skb->cb)[1];
struct nlattr *data = ((void **)skb->cb)[2];
nla_nest_end(skb, data);
genlmsg_end(skb, hdr);
genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
}
EXPORT_SYMBOL(cfg80211_testmode_event);
#endif
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -3629,6 +3751,14 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
#ifdef CONFIG_NL80211_TESTMODE
{
.cmd = NL80211_CMD_TESTMODE,
.doit = nl80211_testmode_do,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
#endif
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
.name = "mlme",
@@ -4102,6 +4232,12 @@ int nl80211_init(void)
if (err)
goto err_out;
#ifdef CONFIG_NL80211_TESTMODE
err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
if (err)
goto err_out;
#endif
return 0;
err_out:
genl_unregister_family(&nl80211_fam);