OSDN Git Service

mac80211: move TDLS code to another file
authorArik Nemtsov <arik@wizery.com>
Thu, 1 May 2014 07:17:28 +0000 (10:17 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 5 May 2014 13:56:15 +0000 (15:56 +0200)
With new additions planned, this code is getting too big for cfg.c.

Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/Makefile
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/tdls.c [new file with mode: 0644]

index 9d7d840..1e46ffa 100644 (file)
@@ -25,7 +25,8 @@ mac80211-y := \
        wme.o \
        event.o \
        chan.o \
-       trace.o mlme.o
+       trace.o mlme.o \
+       tdls.o
 
 mac80211-$(CONFIG_MAC80211_LEDS) += led.o
 mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
index d8b2366..19a7e6f 100644 (file)
@@ -3508,320 +3508,6 @@ 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 (ieee80211_get_sdata_band(sdata) != 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);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
-       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, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               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, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               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);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
-       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, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               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, u32 peer_capability,
-                              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 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;
-
-       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
-                action_code, peer);
-
-       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;
-
-       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 sta_info *sta;
-       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;
-
-       tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
-
-       switch (oper) {
-       case NL80211_TDLS_ENABLE_LINK:
-               rcu_read_lock();
-               sta = sta_info_get(sdata, peer);
-               if (!sta) {
-                       rcu_read_unlock();
-                       return -ENOLINK;
-               }
-
-               set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
-               rcu_read_unlock();
-               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;
-}
-
 static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
                                  const u8 *peer, u64 *cookie)
 {
index b455f62..f86d06a 100644 (file)
@@ -1833,6 +1833,15 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
                                 u8 radar_detect);
 int ieee80211_max_num_channels(struct ieee80211_local *local);
 
+/* TDLS */
+int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                       u8 *peer, u8 action_code, u8 dialog_token,
+                       u16 status_code, u32 peer_capability,
+                       const u8 *extra_ies, size_t extra_ies_len);
+int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+                       u8 *peer, enum nl80211_tdls_operation oper);
+
+
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
 #else
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
new file mode 100644 (file)
index 0000000..8e14e2a
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * mac80211 TDLS handling code
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2014, Intel Corporation
+ *
+ * This file is GPLv2 as found in COPYING.
+ */
+
+#include <linux/ieee80211.h>
+#include "ieee80211_i.h"
+
+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 (ieee80211_get_sdata_band(sdata) != 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);
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       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, skb, false, band);
+               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+               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, skb, false, band);
+               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+               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);
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       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, skb, false, band);
+               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+               ieee80211_tdls_add_ext_capab(skb);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                       u8 *peer, u8 action_code, u8 dialog_token,
+                       u16 status_code, u32 peer_capability,
+                       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 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;
+
+       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
+                action_code, peer);
+
+       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;
+
+       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;
+}
+
+int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+                       u8 *peer, enum nl80211_tdls_operation oper)
+{
+       struct sta_info *sta;
+       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;
+
+       tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
+
+       switch (oper) {
+       case NL80211_TDLS_ENABLE_LINK:
+               rcu_read_lock();
+               sta = sta_info_get(sdata, peer);
+               if (!sta) {
+                       rcu_read_unlock();
+                       return -ENOLINK;
+               }
+
+               set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
+               rcu_read_unlock();
+               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;
+}