aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/mwifiex/Makefile1
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c81
-rw-r--r--drivers/net/wireless/mwifiex/decl.h1
-rw-r--r--drivers/net/wireless/mwifiex/fw.h2
-rw-r--r--drivers/net/wireless/mwifiex/ioctl.h4
-rw-r--r--drivers/net/wireless/mwifiex/main.h18
-rw-r--r--drivers/net/wireless/mwifiex/sta_tx.c3
-rw-r--r--drivers/net/wireless/mwifiex/tdls.c423
8 files changed, 533 insertions, 0 deletions
diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile
index a42a506fd32b..2aa208ffbe23 100644
--- a/drivers/net/wireless/mwifiex/Makefile
+++ b/drivers/net/wireless/mwifiex/Makefile
@@ -41,6 +41,7 @@ mwifiex-y += uap_txrx.o
41mwifiex-y += cfg80211.o 41mwifiex-y += cfg80211.o
42mwifiex-y += ethtool.o 42mwifiex-y += ethtool.o
43mwifiex-y += 11h.o 43mwifiex-y += 11h.o
44mwifiex-y += tdls.o
44mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o 45mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o
45obj-$(CONFIG_MWIFIEX) += mwifiex.o 46obj-$(CONFIG_MWIFIEX) += mwifiex.o
46 47
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index c6606288c61e..2968af273ef9 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -2594,6 +2594,81 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
2594 HostCmd_ACT_GEN_SET, 0, &coalesce_cfg); 2594 HostCmd_ACT_GEN_SET, 0, &coalesce_cfg);
2595} 2595}
2596 2596
2597/* cfg80211 ops handler for tdls_mgmt.
2598 * Function prepares TDLS action frame packets and forwards them to FW
2599 */
2600static int
2601mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
2602 u8 *peer, u8 action_code, u8 dialog_token,
2603 u16 status_code, const u8 *extra_ies,
2604 size_t extra_ies_len)
2605{
2606 struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
2607 int ret;
2608
2609 if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
2610 return -ENOTSUPP;
2611
2612 /* make sure we are in station mode and connected */
2613 if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected))
2614 return -ENOTSUPP;
2615
2616 switch (action_code) {
2617 case WLAN_TDLS_SETUP_REQUEST:
2618 dev_dbg(priv->adapter->dev,
2619 "Send TDLS Setup Request to %pM status_code=%d\n", peer,
2620 status_code);
2621 ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
2622 dialog_token, status_code,
2623 extra_ies, extra_ies_len);
2624 break;
2625 case WLAN_TDLS_SETUP_RESPONSE:
2626 dev_dbg(priv->adapter->dev,
2627 "Send TDLS Setup Response to %pM status_code=%d\n",
2628 peer, status_code);
2629 ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
2630 dialog_token, status_code,
2631 extra_ies, extra_ies_len);
2632 break;
2633 case WLAN_TDLS_SETUP_CONFIRM:
2634 dev_dbg(priv->adapter->dev,
2635 "Send TDLS Confirm to %pM status_code=%d\n", peer,
2636 status_code);
2637 ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
2638 dialog_token, status_code,
2639 extra_ies, extra_ies_len);
2640 break;
2641 case WLAN_TDLS_TEARDOWN:
2642 dev_dbg(priv->adapter->dev, "Send TDLS Tear down to %pM\n",
2643 peer);
2644 ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
2645 dialog_token, status_code,
2646 extra_ies, extra_ies_len);
2647 break;
2648 case WLAN_TDLS_DISCOVERY_REQUEST:
2649 dev_dbg(priv->adapter->dev,
2650 "Send TDLS Discovery Request to %pM\n", peer);
2651 ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
2652 dialog_token, status_code,
2653 extra_ies, extra_ies_len);
2654 break;
2655 case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
2656 dev_dbg(priv->adapter->dev,
2657 "Send TDLS Discovery Response to %pM\n", peer);
2658 ret = mwifiex_send_tdls_action_frame(priv, peer, action_code,
2659 dialog_token, status_code,
2660 extra_ies, extra_ies_len);
2661 break;
2662 default:
2663 dev_warn(priv->adapter->dev,
2664 "Unknown TDLS mgmt/action frame %pM\n", peer);
2665 ret = -EINVAL;
2666 break;
2667 }
2668
2669 return ret;
2670}
2671
2597/* station cfg80211 operations */ 2672/* station cfg80211 operations */
2598static struct cfg80211_ops mwifiex_cfg80211_ops = { 2673static struct cfg80211_ops mwifiex_cfg80211_ops = {
2599 .add_virtual_intf = mwifiex_add_virtual_intf, 2674 .add_virtual_intf = mwifiex_add_virtual_intf,
@@ -2629,6 +2704,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
2629 .set_wakeup = mwifiex_cfg80211_set_wakeup, 2704 .set_wakeup = mwifiex_cfg80211_set_wakeup,
2630#endif 2705#endif
2631 .set_coalesce = mwifiex_cfg80211_set_coalesce, 2706 .set_coalesce = mwifiex_cfg80211_set_coalesce,
2707 .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt,
2632}; 2708};
2633 2709
2634#ifdef CONFIG_PM 2710#ifdef CONFIG_PM
@@ -2714,6 +2790,11 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
2714 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | 2790 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
2715 WIPHY_FLAG_AP_UAPSD | 2791 WIPHY_FLAG_AP_UAPSD |
2716 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; 2792 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
2793
2794 if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info))
2795 wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
2796 WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
2797
2717 wiphy->regulatory_flags |= 2798 wiphy->regulatory_flags |=
2718 REGULATORY_CUSTOM_REG | 2799 REGULATORY_CUSTOM_REG |
2719 REGULATORY_STRICT_REG; 2800 REGULATORY_STRICT_REG;
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
index 3a21bd03d6db..c709f1c25b97 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/mwifiex/decl.h
@@ -75,6 +75,7 @@
75 75
76#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0) 76#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0)
77#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1) 77#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1)
78#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2)
78 79
79#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024 80#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024
80#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128 81#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index 4139c6f4a52b..416518a939b2 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -181,6 +181,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
181#define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192 181#define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192
182 182
183#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) 183#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11))
184#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14))
184 185
185#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ 186#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \
186 (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ 187 (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \
@@ -497,6 +498,7 @@ struct mwifiex_ie_types_data {
497 498
498#define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01 499#define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01
499#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08 500#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
501#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10
500 502
501struct txpd { 503struct txpd {
502 u8 bss_type; 504 u8 bss_type;
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h
index 48f15906515d..fe122742b010 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/mwifiex/ioctl.h
@@ -85,6 +85,10 @@ struct wep_key {
85#define BAND_CONFIG_A 0x01 85#define BAND_CONFIG_A 0x01
86#define MWIFIEX_SUPPORTED_RATES 14 86#define MWIFIEX_SUPPORTED_RATES 14
87#define MWIFIEX_SUPPORTED_RATES_EXT 32 87#define MWIFIEX_SUPPORTED_RATES_EXT 32
88#define MWIFIEX_TDLS_SUPPORTED_RATES 8
89#define MWIFIEX_TDLS_DEF_QOS_CAPAB 0xf
90#define MWIFIEX_PRIO_BK 2
91#define MWIFIEX_PRIO_VI 5
88 92
89struct mwifiex_uap_bss_param { 93struct mwifiex_uap_bss_param {
90 u8 channel; 94 u8 channel;
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 2114475f03c3..54197847d494 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -262,6 +262,16 @@ struct ieee_types_generic {
262 u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)]; 262 u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)];
263} __packed; 263} __packed;
264 264
265struct ieee_types_bss_co_2040 {
266 struct ieee_types_header ieee_hdr;
267 u8 bss_2040co;
268} __packed;
269
270struct ieee_types_extcap {
271 struct ieee_types_header ieee_hdr;
272 u8 ext_capab[8];
273} __packed;
274
265struct mwifiex_bssdescriptor { 275struct mwifiex_bssdescriptor {
266 u8 mac_address[ETH_ALEN]; 276 u8 mac_address[ETH_ALEN];
267 struct cfg80211_ssid ssid; 277 struct cfg80211_ssid ssid;
@@ -1176,6 +1186,14 @@ struct mwifiex_sta_node *
1176mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac); 1186mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac);
1177struct mwifiex_sta_node * 1187struct mwifiex_sta_node *
1178mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); 1188mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac);
1189int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, u8 *peer,
1190 u8 action_code, u8 dialog_token,
1191 u16 status_code, const u8 *extra_ies,
1192 size_t extra_ies_len);
1193int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv,
1194 u8 *peer, u8 action_code, u8 dialog_token,
1195 u16 status_code, const u8 *extra_ies,
1196 size_t extra_ies_len);
1179 1197
1180#ifdef CONFIG_DEBUG_FS 1198#ifdef CONFIG_DEBUG_FS
1181void mwifiex_debugfs_init(void); 1199void mwifiex_debugfs_init(void);
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c
index 354d64c9606f..1236a5de7bca 100644
--- a/drivers/net/wireless/mwifiex/sta_tx.c
+++ b/drivers/net/wireless/mwifiex/sta_tx.c
@@ -95,6 +95,9 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv,
95 } 95 }
96 } 96 }
97 97
98 if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
99 local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET;
100
98 /* Offset of actual data */ 101 /* Offset of actual data */
99 pkt_offset = sizeof(struct txpd) + pad; 102 pkt_offset = sizeof(struct txpd) + pad;
100 if (pkt_type == PKT_TYPE_MGMT) { 103 if (pkt_type == PKT_TYPE_MGMT) {
diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c
new file mode 100644
index 000000000000..73cd444a6d43
--- /dev/null
+++ b/drivers/net/wireless/mwifiex/tdls.c
@@ -0,0 +1,423 @@
1/* Marvell Wireless LAN device driver: TDLS handling
2 *
3 * Copyright (C) 2014, Marvell International Ltd.
4 *
5 * This software file (the "File") is distributed by Marvell International
6 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
7 * (the "License"). You may use, redistribute and/or modify this File in
8 * accordance with the terms and conditions of the License, a copy of which
9 * is available on the worldwide web at
10 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
11 *
12 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
13 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
14 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
15 * this warranty disclaimer.
16 */
17
18#include "main.h"
19
20/* This function appends rate TLV to scan config command. */
21static int
22mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv,
23 struct sk_buff *skb)
24{
25 u8 rates[MWIFIEX_SUPPORTED_RATES], *pos;
26 u16 rates_size, supp_rates_size, ext_rates_size;
27
28 memset(rates, 0, sizeof(rates));
29 rates_size = mwifiex_get_supported_rates(priv, rates);
30
31 supp_rates_size = min_t(u16, rates_size, MWIFIEX_TDLS_SUPPORTED_RATES);
32
33 if (skb_tailroom(skb) < rates_size + 4) {
34 dev_err(priv->adapter->dev,
35 "Insuffient space while adding rates\n");
36 return -ENOMEM;
37 }
38
39 pos = skb_put(skb, supp_rates_size + 2);
40 *pos++ = WLAN_EID_SUPP_RATES;
41 *pos++ = supp_rates_size;
42 memcpy(pos, rates, supp_rates_size);
43
44 if (rates_size > MWIFIEX_TDLS_SUPPORTED_RATES) {
45 ext_rates_size = rates_size - MWIFIEX_TDLS_SUPPORTED_RATES;
46 pos = skb_put(skb, ext_rates_size + 2);
47 *pos++ = WLAN_EID_EXT_SUPP_RATES;
48 *pos++ = ext_rates_size;
49 memcpy(pos, rates + MWIFIEX_TDLS_SUPPORTED_RATES,
50 ext_rates_size);
51 }
52
53 return 0;
54}
55
56static void mwifiex_tdls_add_ext_capab(struct sk_buff *skb)
57{
58 struct ieee_types_extcap *extcap;
59
60 extcap = (void *)skb_put(skb, sizeof(struct ieee_types_extcap));
61 extcap->ieee_hdr.element_id = WLAN_EID_EXT_CAPABILITY;
62 extcap->ieee_hdr.len = 8;
63 memset(extcap->ext_capab, 0, 8);
64 extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED;
65}
66
67static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb)
68{
69 u8 *pos = (void *)skb_put(skb, 3);
70
71 *pos++ = WLAN_EID_QOS_CAPA;
72 *pos++ = 1;
73 *pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB;
74}
75
76static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
77 u8 *peer, u8 action_code, u8 dialog_token,
78 u16 status_code, struct sk_buff *skb)
79{
80 struct ieee80211_tdls_data *tf;
81 int ret;
82 u16 capab;
83 struct ieee80211_ht_cap *ht_cap;
84 u8 radio, *pos;
85
86 capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap;
87
88 tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
89 memcpy(tf->da, peer, ETH_ALEN);
90 memcpy(tf->sa, priv->curr_addr, ETH_ALEN);
91 tf->ether_type = cpu_to_be16(ETH_P_TDLS);
92 tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
93
94 switch (action_code) {
95 case WLAN_TDLS_SETUP_REQUEST:
96 tf->category = WLAN_CATEGORY_TDLS;
97 tf->action_code = WLAN_TDLS_SETUP_REQUEST;
98 skb_put(skb, sizeof(tf->u.setup_req));
99 tf->u.setup_req.dialog_token = dialog_token;
100 tf->u.setup_req.capability = cpu_to_le16(capab);
101 ret = mwifiex_tdls_append_rates_ie(priv, skb);
102 if (ret) {
103 dev_kfree_skb_any(skb);
104 return ret;
105 }
106
107 pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
108 *pos++ = WLAN_EID_HT_CAPABILITY;
109 *pos++ = sizeof(struct ieee80211_ht_cap);
110 ht_cap = (void *)pos;
111 radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
112 ret = mwifiex_fill_cap_info(priv, radio, ht_cap);
113 if (ret) {
114 dev_kfree_skb_any(skb);
115 return ret;
116 }
117
118 mwifiex_tdls_add_ext_capab(skb);
119 mwifiex_tdls_add_qos_capab(skb);
120 break;
121
122 case WLAN_TDLS_SETUP_RESPONSE:
123 tf->category = WLAN_CATEGORY_TDLS;
124 tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
125 skb_put(skb, sizeof(tf->u.setup_resp));
126 tf->u.setup_resp.status_code = cpu_to_le16(status_code);
127 tf->u.setup_resp.dialog_token = dialog_token;
128 tf->u.setup_resp.capability = cpu_to_le16(capab);
129 ret = mwifiex_tdls_append_rates_ie(priv, skb);
130 if (ret) {
131 dev_kfree_skb_any(skb);
132 return ret;
133 }
134
135 pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
136 *pos++ = WLAN_EID_HT_CAPABILITY;
137 *pos++ = sizeof(struct ieee80211_ht_cap);
138 ht_cap = (void *)pos;
139 radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
140 ret = mwifiex_fill_cap_info(priv, radio, ht_cap);
141 if (ret) {
142 dev_kfree_skb_any(skb);
143 return ret;
144 }
145
146 mwifiex_tdls_add_ext_capab(skb);
147 mwifiex_tdls_add_qos_capab(skb);
148 break;
149
150 case WLAN_TDLS_SETUP_CONFIRM:
151 tf->category = WLAN_CATEGORY_TDLS;
152 tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
153 skb_put(skb, sizeof(tf->u.setup_cfm));
154 tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
155 tf->u.setup_cfm.dialog_token = dialog_token;
156 break;
157
158 case WLAN_TDLS_TEARDOWN:
159 tf->category = WLAN_CATEGORY_TDLS;
160 tf->action_code = WLAN_TDLS_TEARDOWN;
161 skb_put(skb, sizeof(tf->u.teardown));
162 tf->u.teardown.reason_code = cpu_to_le16(status_code);
163 break;
164
165 case WLAN_TDLS_DISCOVERY_REQUEST:
166 tf->category = WLAN_CATEGORY_TDLS;
167 tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
168 skb_put(skb, sizeof(tf->u.discover_req));
169 tf->u.discover_req.dialog_token = dialog_token;
170 break;
171 default:
172 dev_err(priv->adapter->dev, "Unknown TDLS frame type.\n");
173 return -EINVAL;
174 }
175
176 return 0;
177}
178
179static void
180mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid)
181{
182 struct ieee80211_tdls_lnkie *lnkid;
183
184 lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
185 lnkid->ie_type = WLAN_EID_LINK_ID;
186 lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) -
187 sizeof(struct ieee_types_header);
188
189 memcpy(lnkid->bssid, bssid, ETH_ALEN);
190 memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
191 memcpy(lnkid->resp_sta, peer, ETH_ALEN);
192}
193
194int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv,
195 u8 *peer, u8 action_code, u8 dialog_token,
196 u16 status_code, const u8 *extra_ies,
197 size_t extra_ies_len)
198{
199 struct sk_buff *skb;
200 struct mwifiex_txinfo *tx_info;
201 struct timeval tv;
202 int ret;
203 u16 skb_len;
204
205 skb_len = MWIFIEX_MIN_DATA_HEADER_LEN +
206 max(sizeof(struct ieee80211_mgmt),
207 sizeof(struct ieee80211_tdls_data)) +
208 MWIFIEX_MGMT_FRAME_HEADER_SIZE +
209 MWIFIEX_SUPPORTED_RATES +
210 3 + /* Qos Info */
211 sizeof(struct ieee_types_extcap) +
212 sizeof(struct ieee80211_ht_cap) +
213 sizeof(struct ieee_types_bss_co_2040) +
214 sizeof(struct ieee80211_ht_operation) +
215 sizeof(struct ieee80211_tdls_lnkie) +
216 extra_ies_len;
217
218 skb = dev_alloc_skb(skb_len);
219 if (!skb) {
220 dev_err(priv->adapter->dev,
221 "allocate skb failed for management frame\n");
222 return -ENOMEM;
223 }
224 skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
225
226 switch (action_code) {
227 case WLAN_TDLS_SETUP_REQUEST:
228 case WLAN_TDLS_SETUP_CONFIRM:
229 case WLAN_TDLS_TEARDOWN:
230 case WLAN_TDLS_DISCOVERY_REQUEST:
231 ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code,
232 dialog_token, status_code,
233 skb);
234 if (ret) {
235 dev_kfree_skb_any(skb);
236 return ret;
237 }
238 if (extra_ies_len)
239 memcpy(skb_put(skb, extra_ies_len), extra_ies,
240 extra_ies_len);
241 mwifiex_tdls_add_link_ie(skb, priv->curr_addr, peer,
242 priv->cfg_bssid);
243 break;
244 case WLAN_TDLS_SETUP_RESPONSE:
245 ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code,
246 dialog_token, status_code,
247 skb);
248 if (ret) {
249 dev_kfree_skb_any(skb);
250 return ret;
251 }
252 if (extra_ies_len)
253 memcpy(skb_put(skb, extra_ies_len), extra_ies,
254 extra_ies_len);
255 mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr,
256 priv->cfg_bssid);
257 break;
258 }
259
260 switch (action_code) {
261 case WLAN_TDLS_SETUP_REQUEST:
262 case WLAN_TDLS_SETUP_RESPONSE:
263 skb->priority = MWIFIEX_PRIO_BK;
264 break;
265 default:
266 skb->priority = MWIFIEX_PRIO_VI;
267 break;
268 }
269
270 tx_info = MWIFIEX_SKB_TXCB(skb);
271 tx_info->bss_num = priv->bss_num;
272 tx_info->bss_type = priv->bss_type;
273
274 do_gettimeofday(&tv);
275 skb->tstamp = timeval_to_ktime(tv);
276 mwifiex_queue_tx_pkt(priv, skb);
277
278 return 0;
279}
280
281static int
282mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer,
283 u8 action_code, u8 dialog_token,
284 u16 status_code, struct sk_buff *skb)
285{
286 struct ieee80211_mgmt *mgmt;
287 u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
288 int ret;
289 u16 capab;
290 struct ieee80211_ht_cap *ht_cap;
291 u8 radio, *pos;
292
293 capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap;
294
295 mgmt = (void *)skb_put(skb, offsetof(struct ieee80211_mgmt, u));
296
297 memset(mgmt, 0, 24);
298 memcpy(mgmt->da, peer, ETH_ALEN);
299 memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN);
300 memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN);
301 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
302 IEEE80211_STYPE_ACTION);
303
304 /* add address 4 */
305 pos = skb_put(skb, ETH_ALEN);
306
307 switch (action_code) {
308 case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
309 skb_put(skb, sizeof(mgmt->u.action.u.tdls_discover_resp) + 1);
310 mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
311 mgmt->u.action.u.tdls_discover_resp.action_code =
312 WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
313 mgmt->u.action.u.tdls_discover_resp.dialog_token =
314 dialog_token;
315 mgmt->u.action.u.tdls_discover_resp.capability =
316 cpu_to_le16(capab);
317 /* move back for addr4 */
318 memmove(pos + ETH_ALEN, &mgmt->u.action.category,
319 sizeof(mgmt->u.action.u.tdls_discover_resp));
320 /* init address 4 */
321 memcpy(pos, bc_addr, ETH_ALEN);
322
323 ret = mwifiex_tdls_append_rates_ie(priv, skb);
324 if (ret) {
325 dev_kfree_skb_any(skb);
326 return ret;
327 }
328
329 pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
330 *pos++ = WLAN_EID_HT_CAPABILITY;
331 *pos++ = sizeof(struct ieee80211_ht_cap);
332 ht_cap = (void *)pos;
333 radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
334 ret = mwifiex_fill_cap_info(priv, radio, ht_cap);
335 if (ret) {
336 dev_kfree_skb_any(skb);
337 return ret;
338 }
339
340 mwifiex_tdls_add_ext_capab(skb);
341 mwifiex_tdls_add_qos_capab(skb);
342 break;
343 default:
344 dev_err(priv->adapter->dev, "Unknown TDLS action frame type\n");
345 return -EINVAL;
346 }
347
348 return 0;
349}
350
351int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv,
352 u8 *peer, u8 action_code, u8 dialog_token,
353 u16 status_code, const u8 *extra_ies,
354 size_t extra_ies_len)
355{
356 struct sk_buff *skb;
357 struct mwifiex_txinfo *tx_info;
358 struct timeval tv;
359 u8 *pos;
360 u32 pkt_type, tx_control;
361 u16 pkt_len, skb_len;
362
363 skb_len = MWIFIEX_MIN_DATA_HEADER_LEN +
364 max(sizeof(struct ieee80211_mgmt),
365 sizeof(struct ieee80211_tdls_data)) +
366 MWIFIEX_MGMT_FRAME_HEADER_SIZE +
367 MWIFIEX_SUPPORTED_RATES +
368 sizeof(struct ieee_types_extcap) +
369 sizeof(struct ieee80211_ht_cap) +
370 sizeof(struct ieee_types_bss_co_2040) +
371 sizeof(struct ieee80211_ht_operation) +
372 sizeof(struct ieee80211_tdls_lnkie) +
373 extra_ies_len +
374 3 + /* Qos Info */
375 ETH_ALEN; /* Address4 */
376
377 skb = dev_alloc_skb(skb_len);
378 if (!skb) {
379 dev_err(priv->adapter->dev,
380 "allocate skb failed for management frame\n");
381 return -ENOMEM;
382 }
383
384 skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
385
386 pkt_type = PKT_TYPE_MGMT;
387 tx_control = 0;
388 pos = skb_put(skb, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len));
389 memset(pos, 0, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len));
390 memcpy(pos, &pkt_type, sizeof(pkt_type));
391 memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control));
392
393 if (mwifiex_construct_tdls_action_frame(priv, peer, action_code,
394 dialog_token, status_code,
395 skb)) {
396 dev_kfree_skb_any(skb);
397 return -EINVAL;
398 }
399
400 if (extra_ies_len)
401 memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
402
403 /* the TDLS link IE is always added last we are the responder */
404
405 mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr,
406 priv->cfg_bssid);
407
408 skb->priority = MWIFIEX_PRIO_VI;
409
410 tx_info = MWIFIEX_SKB_TXCB(skb);
411 tx_info->bss_num = priv->bss_num;
412 tx_info->bss_type = priv->bss_type;
413 tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
414
415 pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len);
416 memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len,
417 sizeof(pkt_len));
418 do_gettimeofday(&tv);
419 skb->tstamp = timeval_to_ktime(tv);
420 mwifiex_queue_tx_pkt(priv, skb);
421
422 return 0;
423}