diff options
-rw-r--r-- | drivers/net/wireless/mwifiex/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/cfg80211.c | 81 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/decl.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/fw.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/ioctl.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/main.h | 18 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/sta_tx.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/tdls.c | 423 |
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 | |||
41 | mwifiex-y += cfg80211.o | 41 | mwifiex-y += cfg80211.o |
42 | mwifiex-y += ethtool.o | 42 | mwifiex-y += ethtool.o |
43 | mwifiex-y += 11h.o | 43 | mwifiex-y += 11h.o |
44 | mwifiex-y += tdls.o | ||
44 | mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o | 45 | mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o |
45 | obj-$(CONFIG_MWIFIEX) += mwifiex.o | 46 | obj-$(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 | */ | ||
2600 | static int | ||
2601 | mwifiex_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 */ |
2598 | static struct cfg80211_ops mwifiex_cfg80211_ops = { | 2673 | static 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 | ||
501 | struct txpd { | 503 | struct 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 | ||
89 | struct mwifiex_uap_bss_param { | 93 | struct 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 | ||
265 | struct ieee_types_bss_co_2040 { | ||
266 | struct ieee_types_header ieee_hdr; | ||
267 | u8 bss_2040co; | ||
268 | } __packed; | ||
269 | |||
270 | struct ieee_types_extcap { | ||
271 | struct ieee_types_header ieee_hdr; | ||
272 | u8 ext_capab[8]; | ||
273 | } __packed; | ||
274 | |||
265 | struct mwifiex_bssdescriptor { | 275 | struct 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 * | |||
1176 | mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac); | 1186 | mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac); |
1177 | struct mwifiex_sta_node * | 1187 | struct mwifiex_sta_node * |
1178 | mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); | 1188 | mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); |
1189 | int 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); | ||
1193 | int 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 |
1181 | void mwifiex_debugfs_init(void); | 1199 | void 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. */ | ||
21 | static int | ||
22 | mwifiex_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 | |||
56 | static 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 | |||
67 | static 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 | |||
76 | static 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 | |||
179 | static void | ||
180 | mwifiex_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 | |||
194 | int 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 | |||
281 | static int | ||
282 | mwifiex_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 | |||
351 | int 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 | } | ||