diff options
-rw-r--r-- | drivers/net/wireless/mac80211_hwsim.c | 68 | ||||
-rw-r--r-- | include/linux/nl80211.h | 11 | ||||
-rw-r--r-- | include/net/cfg80211.h | 83 | ||||
-rw-r--r-- | include/net/mac80211.h | 5 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 13 | ||||
-rw-r--r-- | net/wireless/Kconfig | 15 | ||||
-rw-r--r-- | net/wireless/core.h | 4 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 136 |
8 files changed, 335 insertions, 0 deletions
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 93c1c4a73e6c..6ac8565072e9 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c | |||
@@ -700,6 +700,73 @@ static int mac80211_hwsim_conf_tx( | |||
700 | return 0; | 700 | return 0; |
701 | } | 701 | } |
702 | 702 | ||
703 | #ifdef CONFIG_NL80211_TESTMODE | ||
704 | /* | ||
705 | * This section contains example code for using netlink | ||
706 | * attributes with the testmode command in nl80211. | ||
707 | */ | ||
708 | |||
709 | /* These enums need to be kept in sync with userspace */ | ||
710 | enum hwsim_testmode_attr { | ||
711 | __HWSIM_TM_ATTR_INVALID = 0, | ||
712 | HWSIM_TM_ATTR_CMD = 1, | ||
713 | HWSIM_TM_ATTR_PS = 2, | ||
714 | |||
715 | /* keep last */ | ||
716 | __HWSIM_TM_ATTR_AFTER_LAST, | ||
717 | HWSIM_TM_ATTR_MAX = __HWSIM_TM_ATTR_AFTER_LAST - 1 | ||
718 | }; | ||
719 | |||
720 | enum hwsim_testmode_cmd { | ||
721 | HWSIM_TM_CMD_SET_PS = 0, | ||
722 | HWSIM_TM_CMD_GET_PS = 1, | ||
723 | }; | ||
724 | |||
725 | static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = { | ||
726 | [HWSIM_TM_ATTR_CMD] = { .type = NLA_U32 }, | ||
727 | [HWSIM_TM_ATTR_PS] = { .type = NLA_U32 }, | ||
728 | }; | ||
729 | |||
730 | static int hwsim_fops_ps_write(void *dat, u64 val); | ||
731 | |||
732 | int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) | ||
733 | { | ||
734 | struct mac80211_hwsim_data *hwsim = hw->priv; | ||
735 | struct nlattr *tb[HWSIM_TM_ATTR_MAX + 1]; | ||
736 | struct sk_buff *skb; | ||
737 | int err, ps; | ||
738 | |||
739 | err = nla_parse(tb, HWSIM_TM_ATTR_MAX, data, len, | ||
740 | hwsim_testmode_policy); | ||
741 | if (err) | ||
742 | return err; | ||
743 | |||
744 | if (!tb[HWSIM_TM_ATTR_CMD]) | ||
745 | return -EINVAL; | ||
746 | |||
747 | switch (nla_get_u32(tb[HWSIM_TM_ATTR_CMD])) { | ||
748 | case HWSIM_TM_CMD_SET_PS: | ||
749 | if (!tb[HWSIM_TM_ATTR_PS]) | ||
750 | return -EINVAL; | ||
751 | ps = nla_get_u32(tb[HWSIM_TM_ATTR_PS]); | ||
752 | return hwsim_fops_ps_write(hwsim, ps); | ||
753 | case HWSIM_TM_CMD_GET_PS: | ||
754 | skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, | ||
755 | nla_total_size(sizeof(u32))); | ||
756 | if (!skb) | ||
757 | return -ENOMEM; | ||
758 | NLA_PUT_U32(skb, HWSIM_TM_ATTR_PS, hwsim->ps); | ||
759 | return cfg80211_testmode_reply(skb); | ||
760 | default: | ||
761 | return -EOPNOTSUPP; | ||
762 | } | ||
763 | |||
764 | nla_put_failure: | ||
765 | kfree_skb(skb); | ||
766 | return -ENOBUFS; | ||
767 | } | ||
768 | #endif | ||
769 | |||
703 | static const struct ieee80211_ops mac80211_hwsim_ops = | 770 | static const struct ieee80211_ops mac80211_hwsim_ops = |
704 | { | 771 | { |
705 | .tx = mac80211_hwsim_tx, | 772 | .tx = mac80211_hwsim_tx, |
@@ -713,6 +780,7 @@ static const struct ieee80211_ops mac80211_hwsim_ops = | |||
713 | .sta_notify = mac80211_hwsim_sta_notify, | 780 | .sta_notify = mac80211_hwsim_sta_notify, |
714 | .set_tim = mac80211_hwsim_set_tim, | 781 | .set_tim = mac80211_hwsim_set_tim, |
715 | .conf_tx = mac80211_hwsim_conf_tx, | 782 | .conf_tx = mac80211_hwsim_conf_tx, |
783 | CFG80211_TESTMODE_CMD(mac80211_hwsim_testmode_cmd) | ||
716 | }; | 784 | }; |
717 | 785 | ||
718 | 786 | ||
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index dbea93b694e5..651b18839088 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h | |||
@@ -242,6 +242,10 @@ | |||
242 | * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is | 242 | * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is |
243 | * determined by the network interface. | 243 | * determined by the network interface. |
244 | * | 244 | * |
245 | * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute | ||
246 | * to identify the device, and the TESTDATA blob attribute to pass through | ||
247 | * to the driver. | ||
248 | * | ||
245 | * @NL80211_CMD_MAX: highest used command number | 249 | * @NL80211_CMD_MAX: highest used command number |
246 | * @__NL80211_CMD_AFTER_LAST: internal use | 250 | * @__NL80211_CMD_AFTER_LAST: internal use |
247 | */ | 251 | */ |
@@ -310,6 +314,8 @@ enum nl80211_commands { | |||
310 | NL80211_CMD_JOIN_IBSS, | 314 | NL80211_CMD_JOIN_IBSS, |
311 | NL80211_CMD_LEAVE_IBSS, | 315 | NL80211_CMD_LEAVE_IBSS, |
312 | 316 | ||
317 | NL80211_CMD_TESTMODE, | ||
318 | |||
313 | /* add new commands above here */ | 319 | /* add new commands above here */ |
314 | 320 | ||
315 | /* used to define NL80211_CMD_MAX below */ | 321 | /* used to define NL80211_CMD_MAX below */ |
@@ -511,6 +517,9 @@ enum nl80211_commands { | |||
511 | * authorized by user space. Otherwise, port is marked authorized by | 517 | * authorized by user space. Otherwise, port is marked authorized by |
512 | * default in station mode. | 518 | * default in station mode. |
513 | * | 519 | * |
520 | * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. | ||
521 | * We recommend using nested, driver-specific attributes within this. | ||
522 | * | ||
514 | * @NL80211_ATTR_MAX: highest attribute number currently defined | 523 | * @NL80211_ATTR_MAX: highest attribute number currently defined |
515 | * @__NL80211_ATTR_AFTER_LAST: internal use | 524 | * @__NL80211_ATTR_AFTER_LAST: internal use |
516 | */ | 525 | */ |
@@ -619,6 +628,8 @@ enum nl80211_attrs { | |||
619 | 628 | ||
620 | NL80211_ATTR_CONTROL_PORT, | 629 | NL80211_ATTR_CONTROL_PORT, |
621 | 630 | ||
631 | NL80211_ATTR_TESTDATA, | ||
632 | |||
622 | /* add attributes here, update the policy in nl80211.c */ | 633 | /* add attributes here, update the policy in nl80211.c */ |
623 | 634 | ||
624 | __NL80211_ATTR_AFTER_LAST, | 635 | __NL80211_ATTR_AFTER_LAST, |
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 10eb53e2bc9f..885d4e5bc4b5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -857,6 +857,8 @@ enum tx_power_setting { | |||
857 | * | 857 | * |
858 | * @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting | 858 | * @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting |
859 | * functions to adjust rfkill hw state | 859 | * functions to adjust rfkill hw state |
860 | * | ||
861 | * @testmode_cmd: run a test mode command | ||
860 | */ | 862 | */ |
861 | struct cfg80211_ops { | 863 | struct cfg80211_ops { |
862 | int (*suspend)(struct wiphy *wiphy); | 864 | int (*suspend)(struct wiphy *wiphy); |
@@ -955,6 +957,10 @@ struct cfg80211_ops { | |||
955 | int (*get_tx_power)(struct wiphy *wiphy, int *dbm); | 957 | int (*get_tx_power)(struct wiphy *wiphy, int *dbm); |
956 | 958 | ||
957 | void (*rfkill_poll)(struct wiphy *wiphy); | 959 | void (*rfkill_poll)(struct wiphy *wiphy); |
960 | |||
961 | #ifdef CONFIG_NL80211_TESTMODE | ||
962 | int (*testmode_cmd)(struct wiphy *wiphy, void *data, int len); | ||
963 | #endif | ||
958 | }; | 964 | }; |
959 | 965 | ||
960 | /* | 966 | /* |
@@ -1705,4 +1711,81 @@ void wiphy_rfkill_start_polling(struct wiphy *wiphy); | |||
1705 | */ | 1711 | */ |
1706 | void wiphy_rfkill_stop_polling(struct wiphy *wiphy); | 1712 | void wiphy_rfkill_stop_polling(struct wiphy *wiphy); |
1707 | 1713 | ||
1714 | #ifdef CONFIG_NL80211_TESTMODE | ||
1715 | /** | ||
1716 | * cfg80211_testmode_alloc_reply_skb - allocate testmode reply | ||
1717 | * @wiphy: the wiphy | ||
1718 | * @approxlen: an upper bound of the length of the data that will | ||
1719 | * be put into the skb | ||
1720 | * | ||
1721 | * This function allocates and pre-fills an skb for a reply to | ||
1722 | * the testmode command. Since it is intended for a reply, calling | ||
1723 | * it outside of the @testmode_cmd operation is invalid. | ||
1724 | * | ||
1725 | * The returned skb (or %NULL if any errors happen) is pre-filled | ||
1726 | * with the wiphy index and set up in a way that any data that is | ||
1727 | * put into the skb (with skb_put(), nla_put() or similar) will end | ||
1728 | * up being within the %NL80211_ATTR_TESTDATA attribute, so all that | ||
1729 | * needs to be done with the skb is adding data for the corresponding | ||
1730 | * userspace tool which can then read that data out of the testdata | ||
1731 | * attribute. You must not modify the skb in any other way. | ||
1732 | * | ||
1733 | * When done, call cfg80211_testmode_reply() with the skb and return | ||
1734 | * its error code as the result of the @testmode_cmd operation. | ||
1735 | */ | ||
1736 | struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, | ||
1737 | int approxlen); | ||
1738 | |||
1739 | /** | ||
1740 | * cfg80211_testmode_reply - send the reply skb | ||
1741 | * @skb: The skb, must have been allocated with | ||
1742 | * cfg80211_testmode_alloc_reply_skb() | ||
1743 | * | ||
1744 | * Returns an error code or 0 on success, since calling this | ||
1745 | * function will usually be the last thing before returning | ||
1746 | * from the @testmode_cmd you should return the error code. | ||
1747 | * Note that this function consumes the skb regardless of the | ||
1748 | * return value. | ||
1749 | */ | ||
1750 | int cfg80211_testmode_reply(struct sk_buff *skb); | ||
1751 | |||
1752 | /** | ||
1753 | * cfg80211_testmode_alloc_event_skb - allocate testmode event | ||
1754 | * @wiphy: the wiphy | ||
1755 | * @approxlen: an upper bound of the length of the data that will | ||
1756 | * be put into the skb | ||
1757 | * @gfp: allocation flags | ||
1758 | * | ||
1759 | * This function allocates and pre-fills an skb for an event on the | ||
1760 | * testmode multicast group. | ||
1761 | * | ||
1762 | * The returned skb (or %NULL if any errors happen) is set up in the | ||
1763 | * same way as with cfg80211_testmode_alloc_reply_skb() but prepared | ||
1764 | * for an event. As there, you should simply add data to it that will | ||
1765 | * then end up in the %NL80211_ATTR_TESTDATA attribute. Again, you must | ||
1766 | * not modify the skb in any other way. | ||
1767 | * | ||
1768 | * When done filling the skb, call cfg80211_testmode_event() with the | ||
1769 | * skb to send the event. | ||
1770 | */ | ||
1771 | struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, | ||
1772 | int approxlen, gfp_t gfp); | ||
1773 | |||
1774 | /** | ||
1775 | * cfg80211_testmode_event - send the event | ||
1776 | * @skb: The skb, must have been allocated with | ||
1777 | * cfg80211_testmode_alloc_event_skb() | ||
1778 | * @gfp: allocation flags | ||
1779 | * | ||
1780 | * This function sends the given @skb, which must have been allocated | ||
1781 | * by cfg80211_testmode_alloc_event_skb(), as an event. It always | ||
1782 | * consumes it. | ||
1783 | */ | ||
1784 | void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp); | ||
1785 | |||
1786 | #define CFG80211_TESTMODE_CMD(cmd) .testmode_cmd = (cmd), | ||
1787 | #else | ||
1788 | #define CFG80211_TESTMODE_CMD(cmd) | ||
1789 | #endif | ||
1790 | |||
1708 | #endif /* __NET_CFG80211_H */ | 1791 | #endif /* __NET_CFG80211_H */ |
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index fe80771d95f1..ce7cb1b5d453 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -1416,6 +1416,8 @@ enum ieee80211_ampdu_mlme_action { | |||
1416 | * @rfkill_poll: Poll rfkill hardware state. If you need this, you also | 1416 | * @rfkill_poll: Poll rfkill hardware state. If you need this, you also |
1417 | * need to set wiphy->rfkill_poll to %true before registration, | 1417 | * need to set wiphy->rfkill_poll to %true before registration, |
1418 | * and need to call wiphy_rfkill_set_hw_state() in the callback. | 1418 | * and need to call wiphy_rfkill_set_hw_state() in the callback. |
1419 | * | ||
1420 | * @testmode_cmd: Implement a cfg80211 test mode command. | ||
1419 | */ | 1421 | */ |
1420 | struct ieee80211_ops { | 1422 | struct ieee80211_ops { |
1421 | int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); | 1423 | int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); |
@@ -1466,6 +1468,9 @@ struct ieee80211_ops { | |||
1466 | struct ieee80211_sta *sta, u16 tid, u16 *ssn); | 1468 | struct ieee80211_sta *sta, u16 tid, u16 *ssn); |
1467 | 1469 | ||
1468 | void (*rfkill_poll)(struct ieee80211_hw *hw); | 1470 | void (*rfkill_poll)(struct ieee80211_hw *hw); |
1471 | #ifdef CONFIG_NL80211_TESTMODE | ||
1472 | int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len); | ||
1473 | #endif | ||
1469 | }; | 1474 | }; |
1470 | 1475 | ||
1471 | /** | 1476 | /** |
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index eb93eb6a9cc7..c34c1a41019a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1376,6 +1376,18 @@ static void ieee80211_rfkill_poll(struct wiphy *wiphy) | |||
1376 | drv_rfkill_poll(local); | 1376 | drv_rfkill_poll(local); |
1377 | } | 1377 | } |
1378 | 1378 | ||
1379 | #ifdef CONFIG_NL80211_TESTMODE | ||
1380 | int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) | ||
1381 | { | ||
1382 | struct ieee80211_local *local = wiphy_priv(wiphy); | ||
1383 | |||
1384 | if (!local->ops->testmode_cmd) | ||
1385 | return -EOPNOTSUPP; | ||
1386 | |||
1387 | return local->ops->testmode_cmd(&local->hw, data, len); | ||
1388 | } | ||
1389 | #endif | ||
1390 | |||
1379 | struct cfg80211_ops mac80211_config_ops = { | 1391 | struct cfg80211_ops mac80211_config_ops = { |
1380 | .add_virtual_intf = ieee80211_add_iface, | 1392 | .add_virtual_intf = ieee80211_add_iface, |
1381 | .del_virtual_intf = ieee80211_del_iface, | 1393 | .del_virtual_intf = ieee80211_del_iface, |
@@ -1418,4 +1430,5 @@ struct cfg80211_ops mac80211_config_ops = { | |||
1418 | .set_tx_power = ieee80211_set_tx_power, | 1430 | .set_tx_power = ieee80211_set_tx_power, |
1419 | .get_tx_power = ieee80211_get_tx_power, | 1431 | .get_tx_power = ieee80211_get_tx_power, |
1420 | .rfkill_poll = ieee80211_rfkill_poll, | 1432 | .rfkill_poll = ieee80211_rfkill_poll, |
1433 | CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) | ||
1421 | }; | 1434 | }; |
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index ec64571c4c23..040263118a20 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig | |||
@@ -2,6 +2,21 @@ config CFG80211 | |||
2 | tristate "Improved wireless configuration API" | 2 | tristate "Improved wireless configuration API" |
3 | depends on RFKILL || !RFKILL | 3 | depends on RFKILL || !RFKILL |
4 | 4 | ||
5 | config NL80211_TESTMODE | ||
6 | bool "nl80211 testmode command" | ||
7 | depends on CFG80211 | ||
8 | help | ||
9 | The nl80211 testmode command helps implementing things like | ||
10 | factory calibration or validation tools for wireless chips. | ||
11 | |||
12 | Select this option ONLY for kernels that are specifically | ||
13 | built for such purposes. | ||
14 | |||
15 | Debugging tools that are supposed to end up in the hands of | ||
16 | users should better be implemented with debugfs. | ||
17 | |||
18 | Say N. | ||
19 | |||
5 | config CFG80211_REG_DEBUG | 20 | config CFG80211_REG_DEBUG |
6 | bool "cfg80211 regulatory debugging" | 21 | bool "cfg80211 regulatory debugging" |
7 | depends on CFG80211 | 22 | depends on CFG80211 |
diff --git a/net/wireless/core.h b/net/wireless/core.h index bfa340c7abb5..bc084b68865c 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -58,6 +58,10 @@ struct cfg80211_registered_device { | |||
58 | struct cfg80211_scan_request *scan_req; /* protected by RTNL */ | 58 | struct cfg80211_scan_request *scan_req; /* protected by RTNL */ |
59 | unsigned long suspend_at; | 59 | unsigned long suspend_at; |
60 | 60 | ||
61 | #ifdef CONFIG_NL80211_TESTMODE | ||
62 | struct genl_info *testmode_info; | ||
63 | #endif | ||
64 | |||
61 | #ifdef CONFIG_CFG80211_DEBUGFS | 65 | #ifdef CONFIG_CFG80211_DEBUGFS |
62 | /* Debugfs entries */ | 66 | /* Debugfs entries */ |
63 | struct wiphy_debugfsdentries { | 67 | struct wiphy_debugfsdentries { |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 01523ba81baf..bb8de268a6bf 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -3416,6 +3416,128 @@ unlock_rtnl: | |||
3416 | return err; | 3416 | return err; |
3417 | } | 3417 | } |
3418 | 3418 | ||
3419 | #ifdef CONFIG_NL80211_TESTMODE | ||
3420 | static struct genl_multicast_group nl80211_testmode_mcgrp = { | ||
3421 | .name = "testmode", | ||
3422 | }; | ||
3423 | |||
3424 | static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) | ||
3425 | { | ||
3426 | struct cfg80211_registered_device *rdev; | ||
3427 | int err; | ||
3428 | |||
3429 | if (!info->attrs[NL80211_ATTR_TESTDATA]) | ||
3430 | return -EINVAL; | ||
3431 | |||
3432 | rtnl_lock(); | ||
3433 | |||
3434 | rdev = cfg80211_get_dev_from_info(info); | ||
3435 | if (IS_ERR(rdev)) { | ||
3436 | err = PTR_ERR(rdev); | ||
3437 | goto unlock_rtnl; | ||
3438 | } | ||
3439 | |||
3440 | err = -EOPNOTSUPP; | ||
3441 | if (rdev->ops->testmode_cmd) { | ||
3442 | rdev->testmode_info = info; | ||
3443 | err = rdev->ops->testmode_cmd(&rdev->wiphy, | ||
3444 | nla_data(info->attrs[NL80211_ATTR_TESTDATA]), | ||
3445 | nla_len(info->attrs[NL80211_ATTR_TESTDATA])); | ||
3446 | rdev->testmode_info = NULL; | ||
3447 | } | ||
3448 | |||
3449 | cfg80211_put_dev(rdev); | ||
3450 | |||
3451 | unlock_rtnl: | ||
3452 | rtnl_unlock(); | ||
3453 | return err; | ||
3454 | } | ||
3455 | |||
3456 | static struct sk_buff * | ||
3457 | __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, | ||
3458 | int approxlen, u32 pid, u32 seq, gfp_t gfp) | ||
3459 | { | ||
3460 | struct sk_buff *skb; | ||
3461 | void *hdr; | ||
3462 | struct nlattr *data; | ||
3463 | |||
3464 | skb = nlmsg_new(approxlen + 100, gfp); | ||
3465 | if (!skb) | ||
3466 | return NULL; | ||
3467 | |||
3468 | hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE); | ||
3469 | if (!hdr) { | ||
3470 | kfree_skb(skb); | ||
3471 | return NULL; | ||
3472 | } | ||
3473 | |||
3474 | NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
3475 | data = nla_nest_start(skb, NL80211_ATTR_TESTDATA); | ||
3476 | |||
3477 | ((void **)skb->cb)[0] = rdev; | ||
3478 | ((void **)skb->cb)[1] = hdr; | ||
3479 | ((void **)skb->cb)[2] = data; | ||
3480 | |||
3481 | return skb; | ||
3482 | |||
3483 | nla_put_failure: | ||
3484 | kfree_skb(skb); | ||
3485 | return NULL; | ||
3486 | } | ||
3487 | |||
3488 | struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, | ||
3489 | int approxlen) | ||
3490 | { | ||
3491 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
3492 | |||
3493 | if (WARN_ON(!rdev->testmode_info)) | ||
3494 | return NULL; | ||
3495 | |||
3496 | return __cfg80211_testmode_alloc_skb(rdev, approxlen, | ||
3497 | rdev->testmode_info->snd_pid, | ||
3498 | rdev->testmode_info->snd_seq, | ||
3499 | GFP_KERNEL); | ||
3500 | } | ||
3501 | EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); | ||
3502 | |||
3503 | int cfg80211_testmode_reply(struct sk_buff *skb) | ||
3504 | { | ||
3505 | struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; | ||
3506 | void *hdr = ((void **)skb->cb)[1]; | ||
3507 | struct nlattr *data = ((void **)skb->cb)[2]; | ||
3508 | |||
3509 | if (WARN_ON(!rdev->testmode_info)) { | ||
3510 | kfree_skb(skb); | ||
3511 | return -EINVAL; | ||
3512 | } | ||
3513 | |||
3514 | nla_nest_end(skb, data); | ||
3515 | genlmsg_end(skb, hdr); | ||
3516 | return genlmsg_reply(skb, rdev->testmode_info); | ||
3517 | } | ||
3518 | EXPORT_SYMBOL(cfg80211_testmode_reply); | ||
3519 | |||
3520 | struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, | ||
3521 | int approxlen, gfp_t gfp) | ||
3522 | { | ||
3523 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
3524 | |||
3525 | return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp); | ||
3526 | } | ||
3527 | EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); | ||
3528 | |||
3529 | void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) | ||
3530 | { | ||
3531 | void *hdr = ((void **)skb->cb)[1]; | ||
3532 | struct nlattr *data = ((void **)skb->cb)[2]; | ||
3533 | |||
3534 | nla_nest_end(skb, data); | ||
3535 | genlmsg_end(skb, hdr); | ||
3536 | genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp); | ||
3537 | } | ||
3538 | EXPORT_SYMBOL(cfg80211_testmode_event); | ||
3539 | #endif | ||
3540 | |||
3419 | static struct genl_ops nl80211_ops[] = { | 3541 | static struct genl_ops nl80211_ops[] = { |
3420 | { | 3542 | { |
3421 | .cmd = NL80211_CMD_GET_WIPHY, | 3543 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -3629,6 +3751,14 @@ static struct genl_ops nl80211_ops[] = { | |||
3629 | .policy = nl80211_policy, | 3751 | .policy = nl80211_policy, |
3630 | .flags = GENL_ADMIN_PERM, | 3752 | .flags = GENL_ADMIN_PERM, |
3631 | }, | 3753 | }, |
3754 | #ifdef CONFIG_NL80211_TESTMODE | ||
3755 | { | ||
3756 | .cmd = NL80211_CMD_TESTMODE, | ||
3757 | .doit = nl80211_testmode_do, | ||
3758 | .policy = nl80211_policy, | ||
3759 | .flags = GENL_ADMIN_PERM, | ||
3760 | }, | ||
3761 | #endif | ||
3632 | }; | 3762 | }; |
3633 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 3763 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
3634 | .name = "mlme", | 3764 | .name = "mlme", |
@@ -4102,6 +4232,12 @@ int nl80211_init(void) | |||
4102 | if (err) | 4232 | if (err) |
4103 | goto err_out; | 4233 | goto err_out; |
4104 | 4234 | ||
4235 | #ifdef CONFIG_NL80211_TESTMODE | ||
4236 | err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp); | ||
4237 | if (err) | ||
4238 | goto err_out; | ||
4239 | #endif | ||
4240 | |||
4105 | return 0; | 4241 | return 0; |
4106 | err_out: | 4242 | err_out: |
4107 | genl_unregister_family(&nl80211_fam); | 4243 | genl_unregister_family(&nl80211_fam); |