From 109086ce0b0f94760bdb0e8e2566ff8a2d673639 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Sep 2011 14:12:50 +0300 Subject: nl80211: support sending TDLS commands/frames Add support for sending high-level TDLS commands and TDLS frames via NL80211_CMD_TDLS_OPER and NL80211_CMD_TDLS_MGMT, respectively. Add appropriate cfg80211 callbacks for lower level drivers. Add wiphy capability flags for TDLS support and advertise them via nl80211. Signed-off-by: Arik Nemtsov Cc: Kalyan C Gaddam Signed-off-by: John W. Linville --- include/linux/nl80211.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c73582fb9d20..a5ab23df5b17 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -506,6 +506,9 @@ * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace * of PMKSA caching dandidates. * + * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). + * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -632,6 +635,9 @@ enum nl80211_commands { NL80211_CMD_PMKSA_CANDIDATE, + NL80211_CMD_TDLS_OPER, + NL80211_CMD_TDLS_MGMT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1089,6 +1095,20 @@ enum nl80211_commands { * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and * %NL80211_CMD_FRAME commands. * + * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup + * request, link setup confirm, link teardown, etc.). Values are + * described in the TDLS (802.11z) specification. + * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a + * TDLS conversation between two devices. + * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see + * &enum nl80211_tdls_operation, represented as a u8. + * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate + * as a TDLS peer sta. + * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown + * procedures should be performed by sending TDLS packets via + * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be + * used for asking the driver to perform a TDLS operation. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1311,6 +1331,12 @@ enum nl80211_attrs { NL80211_ATTR_TX_NO_CCK_RATE, + NL80211_ATTR_TDLS_ACTION, + NL80211_ATTR_TDLS_DIALOG_TOKEN, + NL80211_ATTR_TDLS_OPERATION, + NL80211_ATTR_TDLS_SUPPORT, + NL80211_ATTR_TDLS_EXTERNAL_SETUP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2604,4 +2630,20 @@ enum nl80211_pmksa_candidate_attr { MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 }; +/** + * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION + * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request + * @NL80211_TDLS_SETUP: Setup TDLS link + * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established + * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link + * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link + */ +enum nl80211_tdls_operation { + NL80211_TDLS_DISCOVERY_REQ, + NL80211_TDLS_SETUP, + NL80211_TDLS_TEARDOWN, + NL80211_TDLS_ENABLE_LINK, + NL80211_TDLS_DISABLE_LINK, +}; + #endif /* __LINUX_NL80211_H */ -- cgit v1.2.3 From dfe018bf99537e42c816d3f543620a7e09fcf3cd Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Sep 2011 14:12:52 +0300 Subject: mac80211: handle TDLS high-level commands and frames Register and implement the TDLS cfg80211 callback functions. Internally prepare and send TDLS management frames. We incorporate local STA capabilities and supported rates with extra IEs given by usermode. The resulting packet is either encapsulated in a data frame, or assembled as an action frame. It is transmitted either directly or through the AP, as mandated by the TDLS specification. Declare support for the TDLS external setup wiphy capability. This tells usermode to handle link setup and discovery on its own, and use the kernel driver for sending TDLS mgmt packets. Signed-off-by: Arik Nemtsov Cc: Kalyan C Gaddam Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 85 +++++++++++++ include/linux/if_ether.h | 1 + net/mac80211/Kconfig | 12 ++ net/mac80211/cfg.c | 310 ++++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/main.c | 4 + 5 files changed, 412 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b5e0a5c344fd..48363c3c40f8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -759,6 +759,12 @@ struct ieee80211_mgmt { u8 action; u8 smps_control; } __attribute__ ((packed)) ht_smps; + struct { + u8 action_code; + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed tdls_discover_resp; } u; } __attribute__ ((packed)) action; } u; @@ -805,6 +811,52 @@ struct ieee80211_pspoll { u8 ta[6]; } __attribute__ ((packed)); +/* TDLS */ + +/* Link-id information element */ +struct ieee80211_tdls_lnkie { + u8 ie_type; /* Link Identifier IE */ + u8 ie_len; + u8 bssid[6]; + u8 init_sta[6]; + u8 resp_sta[6]; +} __packed; + +struct ieee80211_tdls_data { + u8 da[6]; + u8 sa[6]; + __be16 ether_type; + u8 payload_type; + u8 category; + u8 action_code; + union { + struct { + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed setup_req; + struct { + __le16 status_code; + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed setup_resp; + struct { + __le16 status_code; + u8 dialog_token; + u8 variable[0]; + } __packed setup_cfm; + struct { + __le16 reason_code; + u8 variable[0]; + } __packed teardown; + struct { + u8 dialog_token; + u8 variable[0]; + } __packed discover_req; + } u; +} __packed; + /** * struct ieee80211_bar - HT Block Ack Request * @@ -1196,6 +1248,8 @@ enum ieee80211_eid { WLAN_EID_TS_DELAY = 43, WLAN_EID_TCLAS_PROCESSING = 44, WLAN_EID_QOS_CAPA = 46, + /* 802.11z */ + WLAN_EID_LINK_ID = 101, /* 802.11s */ WLAN_EID_MESH_CONFIG = 113, WLAN_EID_MESH_ID = 114, @@ -1279,6 +1333,7 @@ enum ieee80211_category { WLAN_CATEGORY_HT = 7, WLAN_CATEGORY_SA_QUERY = 8, WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9, + WLAN_CATEGORY_TDLS = 12, WLAN_CATEGORY_MESH_ACTION = 13, WLAN_CATEGORY_MULTIHOP_ACTION = 14, WLAN_CATEGORY_SELF_PROTECTED = 15, @@ -1342,6 +1397,36 @@ enum ieee80211_key_len { WLAN_KEY_LEN_AES_CMAC = 16, }; +/* Public action codes */ +enum ieee80211_pub_actioncode { + WLAN_PUB_ACTION_TDLS_DISCOVER_RES = 14, +}; + +/* TDLS action codes */ +enum ieee80211_tdls_actioncode { + WLAN_TDLS_SETUP_REQUEST = 0, + WLAN_TDLS_SETUP_RESPONSE = 1, + WLAN_TDLS_SETUP_CONFIRM = 2, + WLAN_TDLS_TEARDOWN = 3, + WLAN_TDLS_PEER_TRAFFIC_INDICATION = 4, + WLAN_TDLS_CHANNEL_SWITCH_REQUEST = 5, + WLAN_TDLS_CHANNEL_SWITCH_RESPONSE = 6, + WLAN_TDLS_PEER_PSM_REQUEST = 7, + WLAN_TDLS_PEER_PSM_RESPONSE = 8, + WLAN_TDLS_PEER_TRAFFIC_RESPONSE = 9, + WLAN_TDLS_DISCOVERY_REQUEST = 10, +}; + +/* + * TDLS capabililites to be enabled in the 5th byte of the + * @WLAN_EID_EXT_CAPABILITY information element + */ +#define WLAN_EXT_CAPA5_TDLS_ENABLED BIT(5) +#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6) + +/* TDLS specific payload type in the LLC/SNAP header */ +#define WLAN_TDLS_SNAP_RFTYPE 0x2 + /** * enum - mesh path selection protocol identifier * diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index a3d99ff6e3b5..49c38fc8dbc3 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -83,6 +83,7 @@ #define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ #define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ #define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ +#define ETH_P_TDLS 0x890D /* TDLS */ #define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */ #define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index d1886b59bec4..7d3b438755f0 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -225,6 +225,18 @@ config MAC80211_VERBOSE_MHWMP_DEBUG Do not select this option. +config MAC80211_VERBOSE_TDLS_DEBUG + bool "Verbose TDLS debugging" + depends on MAC80211_DEBUG_MENU + ---help--- + Selecting this option causes mac80211 to print out very + verbose TDLS selection debugging messages (when mac80211 + is a TDLS STA). + It should not be selected on production systems as those + messages are remotely triggerable. + + Do not select this option. + config MAC80211_DEBUG_COUNTERS bool "Extra statistics for TX/RX debugging" depends on MAC80211_DEBUG_MENU diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 13061ebc93ef..1d17677a0ec1 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "ieee80211_i.h" #include "driver-ops.h" @@ -2128,6 +2129,313 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy, return 0; } +static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) +{ + u8 *pos = (void *)skb_put(skb, 7); + + *pos++ = WLAN_EID_EXT_CAPABILITY; + *pos++ = 5; /* len */ + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; +} + +static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + u16 capab; + + capab = 0; + if (local->oper_channel->band != IEEE80211_BAND_2GHZ) + return capab; + + if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) + capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; + if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) + capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; + + return capab; +} + +static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, + u8 *peer, u8 *bssid) +{ + struct ieee80211_tdls_lnkie *lnkid; + + lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); + + lnkid->ie_type = WLAN_EID_LINK_ID; + lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; + + memcpy(lnkid->bssid, bssid, ETH_ALEN); + memcpy(lnkid->init_sta, src_addr, ETH_ALEN); + memcpy(lnkid->resp_sta, peer, ETH_ALEN); +} + +static int +ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_tdls_data *tf; + + tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); + + memcpy(tf->da, peer, ETH_ALEN); + memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); + tf->ether_type = cpu_to_be16(ETH_P_TDLS); + tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_REQUEST; + + skb_put(skb, sizeof(tf->u.setup_req)); + tf->u.setup_req.dialog_token = dialog_token; + tf->u.setup_req.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(&sdata->vif, skb); + ieee80211_add_ext_srates_ie(&sdata->vif, skb); + ieee80211_tdls_add_ext_capab(skb); + break; + case WLAN_TDLS_SETUP_RESPONSE: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_RESPONSE; + + skb_put(skb, sizeof(tf->u.setup_resp)); + tf->u.setup_resp.status_code = cpu_to_le16(status_code); + tf->u.setup_resp.dialog_token = dialog_token; + tf->u.setup_resp.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(&sdata->vif, skb); + ieee80211_add_ext_srates_ie(&sdata->vif, skb); + ieee80211_tdls_add_ext_capab(skb); + break; + case WLAN_TDLS_SETUP_CONFIRM: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_CONFIRM; + + skb_put(skb, sizeof(tf->u.setup_cfm)); + tf->u.setup_cfm.status_code = cpu_to_le16(status_code); + tf->u.setup_cfm.dialog_token = dialog_token; + break; + case WLAN_TDLS_TEARDOWN: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_TEARDOWN; + + skb_put(skb, sizeof(tf->u.teardown)); + tf->u.teardown.reason_code = cpu_to_le16(status_code); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + + skb_put(skb, sizeof(tf->u.discover_req)); + tf->u.discover_req.dialog_token = dialog_token; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int +ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_mgmt *mgmt; + + mgmt = (void *)skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, peer, ETH_ALEN); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); + + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + switch (action_code) { + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); + mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; + mgmt->u.action.u.tdls_discover_resp.action_code = + WLAN_PUB_ACTION_TDLS_DISCOVER_RES; + mgmt->u.action.u.tdls_discover_resp.dialog_token = + dialog_token; + mgmt->u.action.u.tdls_discover_resp.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(&sdata->vif, skb); + ieee80211_add_ext_srates_ie(&sdata->vif, skb); + ieee80211_tdls_add_ext_capab(skb); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct ieee80211_tx_info *info; + struct sk_buff *skb = NULL; + bool send_direct; + int ret; + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + /* make sure we are in managed mode, and associated */ + if (sdata->vif.type != NL80211_IFTYPE_STATION || + !sdata->u.mgd.associated) + return -EINVAL; + +#ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG + printk(KERN_DEBUG "TDLS mgmt action %d peer %pM\n", action_code, peer); +#endif + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + 50 + /* supported rates */ + 7 + /* ext capab */ + extra_ies_len + + sizeof(struct ieee80211_tdls_lnkie)); + if (!skb) + return -ENOMEM; + + info = IEEE80211_SKB_CB(skb); + skb_reserve(skb, local->hw.extra_tx_headroom); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, + action_code, dialog_token, + status_code, skb); + send_direct = false; + break; + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, + dialog_token, status_code, + skb); + send_direct = true; + break; + default: + ret = -ENOTSUPP; + break; + } + + if (ret < 0) + goto fail; + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + + /* the TDLS link IE is always added last */ + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + /* we are the initiator */ + ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, + sdata->u.mgd.bssid); + break; + case WLAN_TDLS_SETUP_RESPONSE: + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + /* we are the responder */ + ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, + sdata->u.mgd.bssid); + break; + default: + ret = -ENOTSUPP; + goto fail; + } + + if (send_direct) { + ieee80211_tx_skb(sdata, skb); + return 0; + } + + /* + * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise + * we should default to AC_VI. + */ + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + skb_set_queue_mapping(skb, IEEE80211_AC_BK); + skb->priority = 2; + break; + default: + skb_set_queue_mapping(skb, IEEE80211_AC_VI); + skb->priority = 5; + break; + } + + /* disable bottom halves when entering the Tx path */ + local_bh_disable(); + ret = ieee80211_subif_start_xmit(skb, dev); + local_bh_enable(); + + return ret; + +fail: + dev_kfree_skb(skb); + return ret; +} + +static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, enum nl80211_tdls_operation oper) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EINVAL; + +#ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG + printk(KERN_DEBUG "TDLS oper %d peer %pM\n", oper, peer); +#endif + + switch (oper) { + case NL80211_TDLS_ENABLE_LINK: + break; + case NL80211_TDLS_DISABLE_LINK: + return sta_info_destroy_addr(sdata, peer); + case NL80211_TDLS_TEARDOWN: + case NL80211_TDLS_SETUP: + case NL80211_TDLS_DISCOVERY_REQ: + /* We don't support in-driver setup/teardown/discovery */ + return -ENOTSUPP; + default: + return -ENOTSUPP; + } + + return 0; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -2191,4 +2499,6 @@ struct cfg80211_ops mac80211_config_ops = { .set_ringparam = ieee80211_set_ringparam, .get_ringparam = ieee80211_get_ringparam, .set_rekey_data = ieee80211_set_rekey_data, + .tdls_oper = ieee80211_tdls_oper, + .tdls_mgmt = ieee80211_tdls_mgmt, }; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index a5809a1a6239..336ceb9d2462 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -863,6 +863,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (local->ops->sched_scan_start) local->hw.wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + /* mac80211 based drivers don't support internal TDLS setup */ + if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) + local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; + result = wiphy_register(local->hw.wiphy); if (result < 0) goto fail_wiphy_register; -- cgit v1.2.3 From 07ba55d7f1d0da174c9bc545c713b44cee760197 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Sep 2011 14:12:53 +0300 Subject: nl80211/mac80211: allow adding TDLS peers as stations When adding a TDLS peer STA, mark it with a new flag in both nl80211 and mac80211. Before adding a peer, make sure the wiphy supports TDLS and our operating mode is appropriate (managed). In addition, make sure all peers are removed on disassociation. A TDLS peer is first added just before link setup is initiated. In later setup stages we have more info about peer supported rates, capabilities, etc. This info is reported via nl80211_set_station(). Signed-off-by: Arik Nemtsov Cc: Kalyan C Gaddam Signed-off-by: John W. Linville --- include/linux/nl80211.h | 2 ++ net/mac80211/cfg.c | 20 ++++++++++++++++++++ net/mac80211/mlme.c | 7 ++++--- net/mac80211/sta_info.h | 2 ++ net/wireless/nl80211.c | 26 ++++++++++++++++++++++---- 5 files changed, 50 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index a5ab23df5b17..9d797f253d8e 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1434,6 +1434,7 @@ enum nl80211_iftype { * @NL80211_STA_FLAG_WME: station is WME/QoS capable * @NL80211_STA_FLAG_MFP: station uses management frame protection * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated + * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ @@ -1444,6 +1445,7 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_WME, NL80211_STA_FLAG_MFP, NL80211_STA_FLAG_AUTHENTICATED, + NL80211_STA_FLAG_TDLS_PEER, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1d17677a0ec1..119a573af14b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -713,6 +713,12 @@ static void sta_apply_parameters(struct ieee80211_local *local, if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) sta->flags |= WLAN_STA_AUTH; } + + if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) { + sta->flags &= ~WLAN_STA_TDLS_PEER; + if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) + sta->flags |= WLAN_STA_TDLS_PEER; + } spin_unlock_irqrestore(&sta->flaglock, flags); if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) { @@ -813,6 +819,12 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, sta_apply_parameters(local, sta, params); + /* Only TDLS-supporting stations can add TDLS peers */ + if ((sta->flags & WLAN_STA_TDLS_PEER) && + !((wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) && + sdata->vif.type == NL80211_IFTYPE_STATION)) + return -ENOTSUPP; + rate_control_rate_init(sta); layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN || @@ -865,6 +877,14 @@ static int ieee80211_change_station(struct wiphy *wiphy, return -ENOENT; } + /* The TDLS bit cannot be toggled after the STA was added */ + if ((params->sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + !!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) != + !!test_sta_flags(sta, WLAN_STA_TDLS_PEER)) { + rcu_read_unlock(); + return -EINVAL; + } + if (params->vlan && params->vlan != sta->sdata->dev) { vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index cd37a4e3c0d7..b98c43a7f191 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1137,8 +1137,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; ieee80211_bss_info_change_notify(sdata, changed); + /* remove AP and TDLS peers */ if (remove_sta) - sta_info_destroy_addr(sdata, bssid); + sta_info_flush(local, sdata); del_timer_sync(&sdata->u.mgd.conn_mon_timer); del_timer_sync(&sdata->u.mgd.bcn_mon_timer); @@ -2738,7 +2739,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, req->reason_code, cookie, !req->local_state_change); if (assoc_bss) - sta_info_destroy_addr(sdata, bssid); + sta_info_flush(sdata->local, sdata); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); @@ -2778,7 +2779,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, IEEE80211_STYPE_DISASSOC, req->reason_code, cookie, !req->local_state_change); - sta_info_destroy_addr(sdata, bssid); + sta_info_flush(sdata->local, sdata); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 56a3d38a2cd1..b6bd4e9d8722 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -45,6 +45,7 @@ * station in power-save mode, reply when the driver unblocks. * @WLAN_STA_PS_DRIVER_BUF: Station has frames pending in driver internal * buffers. Automatically cleared on station wake-up. + * @WLAN_STA_TDLS_PEER: station is a TDLS peer. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, @@ -61,6 +62,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_PS_DRIVER = 1<<12, WLAN_STA_PSPOLL = 1<<13, WLAN_STA_PS_DRIVER_BUF = 1<<14, + WLAN_STA_TDLS_PEER = 1<<15, }; #define STA_TID_NUM 16 diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 25a37fc951e3..edf655aeea00 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2530,18 +2530,25 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: - /* disallow everything but AUTHORIZED flag */ + /* disallow things sta doesn't support */ if (params.plink_action) err = -EINVAL; if (params.vlan) err = -EINVAL; - if (params.supported_rates) + if (params.supported_rates && + !(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) err = -EINVAL; if (params.ht_capa) err = -EINVAL; if (params.listen_interval >= 0) err = -EINVAL; - if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) + if (params.sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_TDLS_PEER))) + err = -EINVAL; + /* can't change the TDLS bit */ + if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + (params.sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER))) err = -EINVAL; break; case NL80211_IFTYPE_MESH_POINT: @@ -2662,7 +2669,18 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) + return -EINVAL; + + /* + * Only managed stations can add TDLS peers, and only when the + * wiphy supports external TDLS setup. + */ + if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION && + !((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) && + (rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))) return -EINVAL; err = get_vlan(info, rdev, ¶ms.vlan); -- cgit v1.2.3 From e209c5a7ed1870ab7f112ad47083b5d616e8b6a4 Mon Sep 17 00:00:00 2001 From: Sangwook Lee Date: Thu, 29 Sep 2011 12:57:17 +0100 Subject: net:rfkill: add a gpio setup function into GPIO rfkill Add a gpio setup function which gives a chance to set up platform specific configuration such as pin multiplexing, input/output direction at the runtime or booting time. Signed-off-by: Sangwook Lee Signed-off-by: John W. Linville --- include/linux/rfkill-gpio.h | 4 ++++ net/rfkill/rfkill-gpio.c | 11 +++++++++++ 2 files changed, 15 insertions(+) (limited to 'include/linux') diff --git a/include/linux/rfkill-gpio.h b/include/linux/rfkill-gpio.h index a175d0598033..4d09f6eab359 100644 --- a/include/linux/rfkill-gpio.h +++ b/include/linux/rfkill-gpio.h @@ -30,6 +30,8 @@ * @reset_gpio: GPIO which is used for reseting rfkill switch * @shutdown_gpio: GPIO which is used for shutdown of rfkill switch * @power_clk_name: [optional] name of clk to turn off while blocked + * @gpio_runtime_close: clean up platform specific gpio configuration + * @gpio_runtime_setup: set up platform specific gpio configuration */ struct rfkill_gpio_platform_data { @@ -38,6 +40,8 @@ struct rfkill_gpio_platform_data { int shutdown_gpio; const char *power_clk_name; enum rfkill_type type; + void (*gpio_runtime_close)(struct platform_device *); + int (*gpio_runtime_setup)(struct platform_device *); }; #endif /* __RFKILL_GPIO_H */ diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index 256c5ddd2d72..128677d69056 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -101,6 +101,14 @@ static int rfkill_gpio_probe(struct platform_device *pdev) if (!rfkill) return -ENOMEM; + if (pdata->gpio_runtime_setup) { + ret = pdata->gpio_runtime_setup(pdev); + if (ret) { + pr_warn("%s: can't set up gpio\n", __func__); + return ret; + } + } + rfkill->pdata = pdata; len = strlen(pdata->name); @@ -182,7 +190,10 @@ fail_alloc: static int rfkill_gpio_remove(struct platform_device *pdev) { struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev); + struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; + if (pdata->gpio_runtime_close) + pdata->gpio_runtime_close(pdev); rfkill_unregister(rfkill->rfkill_dev); rfkill_destroy(rfkill->rfkill_dev); if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) -- cgit v1.2.3