blob: 03b1ddd95d2025034e08420155d0615ce747b99a [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302 * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28/**
29 * @file ol_txrx_encap.c
30 * @brief Provide functions to encap/decap on txrx frames.
31 * @details
32 * This file contains functions for data frame encap/decap:
33 * ol_tx_encap: encap outgoing data frames.
34 * ol_rx_decap: decap incoming data frames.
35 */
36#ifdef QCA_SUPPORT_SW_TXRX_ENCAP
37
Nirav Shahcbc6d722016-03-01 16:24:53 +053038#include <qdf_nbuf.h> /* qdf_nbuf_t, etc. */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080039#include <cds_ieee80211_common.h> /* ieee80211_frame */
40#include <net.h> /* struct llc, struct ether_header, etc. */
41#include <ol_txrx_internal.h> /* TXRX_ASSERT1 */
42#include <ol_txrx_types.h> /* struct ol_txrx_vdev_t, ol_txrx_pdev_t,etc. */
43#include <ol_txrx_encap.h> /* struct ol_rx_decap_info_t */
44
45#define OL_TX_COPY_NATIVE_WIFI_HEADER(wh, msdu, hdsize, localbuf) \
46 do { \
Nirav Shahcbc6d722016-03-01 16:24:53 +053047 wh = (struct ieee80211_frame *)qdf_nbuf_data(msdu); \
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080048 if ((wh->i_fc[1] & \
49 IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) { \
50 hdsize = sizeof(struct ieee80211_frame_addr4); \
51 } else { \
52 hdsize = sizeof(struct ieee80211_frame); \
53 } \
Nirav Shahcbc6d722016-03-01 16:24:53 +053054 if (qdf_nbuf_len(msdu) < hdsize) { \
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080055 return A_ERROR; \
56 } \
Anurag Chouhan600c3a02016-03-01 10:33:54 +053057 qdf_mem_copy(localbuf, wh, hdsize); \
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080058 wh = (struct ieee80211_frame *)localbuf; \
59 } while (0)
60
61static inline A_STATUS
Nirav Shahcbc6d722016-03-01 16:24:53 +053062ol_tx_copy_native_wifi_header(qdf_nbuf_t msdu,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080063 uint8_t *hdsize, uint8_t *localbuf)
64{
65 struct ieee80211_frame *wh =
Nirav Shahcbc6d722016-03-01 16:24:53 +053066 (struct ieee80211_frame *)qdf_nbuf_data(msdu);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080067 if ((wh->i_fc[1] &
68 IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) {
69 *hdsize = sizeof(struct ieee80211_frame_addr4);
70 } else {
71 *hdsize = sizeof(struct ieee80211_frame);
72 }
Nirav Shahcbc6d722016-03-01 16:24:53 +053073 if (qdf_nbuf_len(msdu) < *hdsize)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080074 return A_ERROR;
75
Anurag Chouhan600c3a02016-03-01 10:33:54 +053076 qdf_mem_copy(localbuf, wh, *hdsize);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080077 return A_OK;
78}
79
80static inline A_STATUS
81ol_tx_encap_from_native_wifi(struct ol_txrx_vdev_t *vdev,
82 struct ol_tx_desc_t *tx_desc,
Nirav Shahcbc6d722016-03-01 16:24:53 +053083 qdf_nbuf_t msdu,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080084 struct ol_txrx_msdu_info_t *tx_msdu_info)
85{
86 uint8_t localbuf[sizeof(struct ieee80211_qosframe_htc_addr4)];
87 struct ieee80211_frame *wh;
88 uint8_t hdsize, new_hdsize;
89 struct ieee80211_qoscntl *qos_cntl;
90 struct ol_txrx_peer_t *peer;
91
92 if (tx_msdu_info->htt.info.frame_type != htt_frm_type_data)
93 return A_OK;
94
95 peer = tx_msdu_info->peer;
96 /*
97 * for unicast,the peer should not be NULL.
98 * for multicast, the peer is AP.
99 */
100 if (tx_msdu_info->htt.info.is_unicast && peer->qos_capable) {
101 if (A_OK !=
102 ol_tx_copy_native_wifi_header(msdu, &hdsize, localbuf))
103 return A_ERROR;
104 wh = (struct ieee80211_frame *)localbuf;
105
106 /*add qos cntl */
107 qos_cntl = (struct ieee80211_qoscntl *)(localbuf + hdsize);
108 qos_cntl->i_qos[0] =
109 tx_msdu_info->htt.info.ext_tid & IEEE80211_QOS_TID;
110
111#ifdef NEVERDEFINED
112 if (wmmParam[ac].wmep_noackPolicy)
113 qos_cntl->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S;
114#endif
115
116 qos_cntl->i_qos[1] = 0;
117 wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
118 /* count for qos field */
119 new_hdsize =
120 hdsize + sizeof(struct ieee80211_qosframe) -
121 sizeof(struct ieee80211_frame);
122
123 /*add ht control field if needed */
124
125 /* copy new hd to bd */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530126 qdf_mem_copy((void *)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800127 htt_tx_desc_mpdu_header(tx_desc->htt_tx_desc,
128 new_hdsize), localbuf,
129 new_hdsize);
Nirav Shahcbc6d722016-03-01 16:24:53 +0530130 qdf_nbuf_pull_head(msdu, hdsize);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800131 tx_msdu_info->htt.info.l3_hdr_offset = new_hdsize;
132 tx_desc->orig_l2_hdr_bytes = hdsize;
133 }
134 /* Set Protected Frame bit in MAC header */
135 if (vdev->pdev->sw_pf_proc_enable
136 && tx_msdu_info->htt.action.do_encrypt) {
137 if (tx_desc->orig_l2_hdr_bytes) {
138 wh = (struct ieee80211_frame *)
139 htt_tx_desc_mpdu_header(tx_desc->htt_tx_desc,
140 tx_msdu_info->htt.info.
141 l3_hdr_offset);
142 } else {
143 if (A_OK !=
144 ol_tx_copy_native_wifi_header(msdu, &hdsize,
145 localbuf))
146 return A_ERROR;
147 wh = (struct ieee80211_frame *)
148 htt_tx_desc_mpdu_header(tx_desc->htt_tx_desc,
149 hdsize);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530150 qdf_mem_copy((void *)wh, localbuf, hdsize);
Nirav Shahcbc6d722016-03-01 16:24:53 +0530151 qdf_nbuf_pull_head(msdu, hdsize);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800152 tx_msdu_info->htt.info.l3_hdr_offset = hdsize;
153 tx_desc->orig_l2_hdr_bytes = hdsize;
154 }
155 wh->i_fc[1] |= IEEE80211_FC1_WEP;
156 }
157 return A_OK;
158}
159
160static inline A_STATUS
161ol_tx_encap_from_8023(struct ol_txrx_vdev_t *vdev,
162 struct ol_tx_desc_t *tx_desc,
Nirav Shahcbc6d722016-03-01 16:24:53 +0530163 qdf_nbuf_t msdu, struct ol_txrx_msdu_info_t *tx_msdu_info)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800164{
165 uint8_t localbuf[sizeof(struct ieee80211_qosframe_htc_addr4)
166 + sizeof(struct llc_snap_hdr_t)];
167 struct llc_snap_hdr_t *llc_hdr;
168 struct ethernet_hdr_t *eth_hdr;
169 struct ieee80211_frame *wh;
170 uint8_t hdsize, new_l2_hdsize, new_hdsize;
171 struct ieee80211_qoscntl *qos_cntl;
172 const uint8_t ethernet_II_llc_snap_header_prefix[] = {
173 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
174 struct ol_txrx_peer_t *peer;
175 uint16_t ether_type;
176
177 if (tx_msdu_info->htt.info.frame_type != htt_frm_type_data)
178 return A_OK;
179
180 /*
181 * for unicast,the peer should not be NULL.
182 * for multicast, the peer is AP.
183 */
184 peer = tx_msdu_info->peer;
185
Nirav Shahcbc6d722016-03-01 16:24:53 +0530186 eth_hdr = (struct ethernet_hdr_t *)qdf_nbuf_data(msdu);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800187 hdsize = sizeof(struct ethernet_hdr_t);
188 wh = (struct ieee80211_frame *)localbuf;
189 wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
190 *(uint16_t *) wh->i_dur = 0;
191 new_hdsize = 0;
192
193 switch (vdev->opmode) {
194 case wlan_op_mode_ap:
195 /* DA , BSSID , SA */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530196 qdf_mem_copy(wh->i_addr1, eth_hdr->dest_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800197 IEEE80211_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530198 qdf_mem_copy(wh->i_addr2, &vdev->mac_addr.raw,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800199 IEEE80211_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530200 qdf_mem_copy(wh->i_addr3, eth_hdr->src_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800201 IEEE80211_ADDR_LEN);
202 wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
203 new_hdsize = sizeof(struct ieee80211_frame);
204 break;
205 case wlan_op_mode_ibss:
206 /* DA, SA, BSSID */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530207 qdf_mem_copy(wh->i_addr1, eth_hdr->dest_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800208 IEEE80211_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530209 qdf_mem_copy(wh->i_addr2, eth_hdr->src_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800210 IEEE80211_ADDR_LEN);
211 /* need to check the bssid behaviour for IBSS vdev */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530212 qdf_mem_copy(wh->i_addr3, &vdev->mac_addr.raw,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800213 IEEE80211_ADDR_LEN);
214 wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
215 new_hdsize = sizeof(struct ieee80211_frame);
216 break;
217 case wlan_op_mode_sta:
218 /* BSSID, SA , DA */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530219 qdf_mem_copy(wh->i_addr1, &peer->mac_addr.raw,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800220 IEEE80211_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530221 qdf_mem_copy(wh->i_addr2, eth_hdr->src_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800222 IEEE80211_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530223 qdf_mem_copy(wh->i_addr3, eth_hdr->dest_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800224 IEEE80211_ADDR_LEN);
225 wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
226 new_hdsize = sizeof(struct ieee80211_frame);
227 break;
228 case wlan_op_mode_monitor:
229 default:
230 return A_ERROR;
231 }
232 /*add qos cntl */
233 if (tx_msdu_info->htt.info.is_unicast && peer->qos_capable) {
234 qos_cntl = (struct ieee80211_qoscntl *)(localbuf + new_hdsize);
235 qos_cntl->i_qos[0] =
236 tx_msdu_info->htt.info.ext_tid & IEEE80211_QOS_TID;
237 wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
238#ifdef NEVERDEFINED
239 if (wmmParam[ac].wmep_noackPolicy)
240 qos_cntl->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S;
241#endif
242 qos_cntl->i_qos[1] = 0;
243 new_hdsize += sizeof(struct ieee80211_qoscntl);
244
245 /*add ht control field if needed */
246 }
247 /* Set Protected Frame bit in MAC header */
248 if (vdev->pdev->sw_pf_proc_enable
249 && tx_msdu_info->htt.action.do_encrypt) {
250 wh->i_fc[1] |= IEEE80211_FC1_WEP;
251 }
252 new_l2_hdsize = new_hdsize;
253 /* add llc snap if needed */
254 if (vdev->pdev->sw_tx_llc_proc_enable) {
255 llc_hdr = (struct llc_snap_hdr_t *)(localbuf + new_hdsize);
256 ether_type =
257 (eth_hdr->ethertype[0] << 8) | (eth_hdr->ethertype[1]);
258 if (ether_type >= IEEE8023_MAX_LEN) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530259 qdf_mem_copy(llc_hdr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800260 ethernet_II_llc_snap_header_prefix,
261 sizeof
262 (ethernet_II_llc_snap_header_prefix));
263 if (ether_type == ETHERTYPE_AARP
264 || ether_type == ETHERTYPE_IPX) {
265 llc_hdr->org_code[2] = BTEP_SNAP_ORGCODE_2;
266 /* 0xf8; bridge tunnel header */
267 }
268 llc_hdr->ethertype[0] = eth_hdr->ethertype[0];
269 llc_hdr->ethertype[1] = eth_hdr->ethertype[1];
270 new_hdsize += sizeof(struct llc_snap_hdr_t);
271 } else {
272 /*llc ready, and it's in payload pdu,
273 do we need to move to BD pdu? */
274 }
275 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530276 qdf_mem_copy((void *)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800277 htt_tx_desc_mpdu_header(tx_desc->htt_tx_desc,
278 new_l2_hdsize), localbuf,
279 new_hdsize);
Nirav Shahcbc6d722016-03-01 16:24:53 +0530280 qdf_nbuf_pull_head(msdu, hdsize);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800281 tx_msdu_info->htt.info.l3_hdr_offset = new_l2_hdsize;
282 tx_desc->orig_l2_hdr_bytes = hdsize;
283 return A_OK;
284}
285
286A_STATUS
287ol_tx_encap(struct ol_txrx_vdev_t *vdev,
288 struct ol_tx_desc_t *tx_desc,
Nirav Shahcbc6d722016-03-01 16:24:53 +0530289 qdf_nbuf_t msdu, struct ol_txrx_msdu_info_t *msdu_info)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800290{
291 struct ol_txrx_pdev_t *pdev = vdev->pdev;
292
293 if (pdev->frame_format == wlan_frm_fmt_native_wifi) {
294 return ol_tx_encap_from_native_wifi(vdev, tx_desc, msdu,
295 msdu_info);
296 } else if (pdev->frame_format == wlan_frm_fmt_802_3) {
297 return ol_tx_encap_from_8023(vdev, tx_desc, msdu, msdu_info);
298 } else {
299 /* todo for other types */
300 return A_ERROR;
301 }
302}
303
304static inline void
305ol_rx_decap_to_native_wifi(struct ol_txrx_vdev_t *vdev,
Nirav Shahcbc6d722016-03-01 16:24:53 +0530306 qdf_nbuf_t msdu,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800307 struct ol_rx_decap_info_t *info,
308 struct ethernet_hdr_t *ethr_hdr)
309{
310 struct ieee80211_frame_addr4 *wh;
311 uint16_t hdsize;
312
313 /*
314 * we need to remove Qos control field and HT control.
315 * MSFT: http://msdn.microsoft.com/en-us/library/windows/
316 * hardware/ff552608(v=vs.85).aspx
317 */
318 wh = (struct ieee80211_frame_addr4 *)info->hdr;
319 if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) ==
320 IEEE80211_FC1_DIR_DSTODS)
321 hdsize = sizeof(struct ieee80211_frame_addr4);
322 else
323 hdsize = sizeof(struct ieee80211_frame);
324
Nirav Shahcbc6d722016-03-01 16:24:53 +0530325 wh = (struct ieee80211_frame_addr4 *)qdf_nbuf_push_head(msdu, hdsize);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800326 TXRX_ASSERT2(wh != NULL);
327 TXRX_ASSERT2(hdsize <= info->hdr_len);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530328 qdf_mem_copy((uint8_t *) wh, info->hdr, hdsize);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800329
330 /* amsdu subfrm handling if ethr_hdr is not NULL */
331 if (ethr_hdr != NULL) {
332 switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
333 case IEEE80211_FC1_DIR_NODS:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530334 qdf_mem_copy(wh->i_addr1, ethr_hdr->dest_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800335 ETHERNET_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530336 qdf_mem_copy(wh->i_addr2, ethr_hdr->src_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800337 ETHERNET_ADDR_LEN);
338 break;
339 case IEEE80211_FC1_DIR_TODS:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530340 qdf_mem_copy(wh->i_addr2, ethr_hdr->src_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800341 ETHERNET_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530342 qdf_mem_copy(wh->i_addr3, ethr_hdr->dest_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800343 ETHERNET_ADDR_LEN);
344 break;
345 case IEEE80211_FC1_DIR_FROMDS:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530346 qdf_mem_copy(wh->i_addr1, ethr_hdr->dest_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800347 ETHERNET_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530348 qdf_mem_copy(wh->i_addr3, ethr_hdr->src_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800349 ETHERNET_ADDR_LEN);
350 break;
351 case IEEE80211_FC1_DIR_DSTODS:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530352 qdf_mem_copy(wh->i_addr3, ethr_hdr->dest_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800353 ETHERNET_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530354 qdf_mem_copy(wh->i_addr4, ethr_hdr->src_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800355 ETHERNET_ADDR_LEN);
356 break;
357 }
358 }
359 if (IEEE80211_QOS_HAS_SEQ(wh)) {
360 if (wh->i_fc[1] & IEEE80211_FC1_ORDER)
361 wh->i_fc[1] &= ~IEEE80211_FC1_ORDER;
362 wh->i_fc[0] &= ~IEEE80211_FC0_SUBTYPE_QOS;
363 }
364}
365
366static inline void
367ol_rx_decap_to_8023(struct ol_txrx_vdev_t *vdev,
Nirav Shahcbc6d722016-03-01 16:24:53 +0530368 qdf_nbuf_t msdu,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800369 struct ol_rx_decap_info_t *info,
370 struct ethernet_hdr_t *ethr_hdr)
371{
372 struct llc_snap_hdr_t *llc_hdr;
373 uint16_t ether_type;
374 uint16_t l2_hdr_space;
375 struct ieee80211_frame_addr4 *wh;
376 uint8_t local_buf[ETHERNET_HDR_LEN];
377 uint8_t *buf;
378
379 /*
380 * populate Ethernet header,
381 * if ethr_hdr is null, rx frame is 802.11 format(HW ft disabled)
382 * if ethr_hdr is not null, rx frame is "subfrm of amsdu".
383 */
Nirav Shahcbc6d722016-03-01 16:24:53 +0530384 buf = (uint8_t *) qdf_nbuf_data(msdu);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800385 llc_hdr = (struct llc_snap_hdr_t *)buf;
386 ether_type = (llc_hdr->ethertype[0] << 8) | llc_hdr->ethertype[1];
387 /* do llc remove if needed */
388 l2_hdr_space = 0;
389 if (IS_SNAP(llc_hdr)) {
390 if (IS_BTEP(llc_hdr)) {
391 /* remove llc */
392 l2_hdr_space += sizeof(struct llc_snap_hdr_t);
393 llc_hdr = NULL;
394 } else if (IS_RFC1042(llc_hdr)) {
395 if (!(ether_type == ETHERTYPE_AARP ||
396 ether_type == ETHERTYPE_IPX)) {
397 /* remove llc */
398 l2_hdr_space += sizeof(struct llc_snap_hdr_t);
399 llc_hdr = NULL;
400 }
401 }
402 }
403 if (l2_hdr_space > ETHERNET_HDR_LEN)
Nirav Shahcbc6d722016-03-01 16:24:53 +0530404 buf = qdf_nbuf_pull_head(msdu, l2_hdr_space - ETHERNET_HDR_LEN);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800405 else if (l2_hdr_space < ETHERNET_HDR_LEN)
Nirav Shahcbc6d722016-03-01 16:24:53 +0530406 buf = qdf_nbuf_push_head(msdu, ETHERNET_HDR_LEN - l2_hdr_space);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800407
408 /* normal msdu(non-subfrm of A-MSDU) if ethr_hdr is null */
409 if (ethr_hdr == NULL) {
410 /* mpdu hdr should be present in info,
411 re-create ethr_hdr based on mpdu hdr */
412 TXRX_ASSERT2(info->hdr_len != 0);
413 wh = (struct ieee80211_frame_addr4 *)info->hdr;
414 ethr_hdr = (struct ethernet_hdr_t *)local_buf;
415 switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
416 case IEEE80211_FC1_DIR_NODS:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530417 qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr1,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800418 ETHERNET_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530419 qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr2,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800420 ETHERNET_ADDR_LEN);
421 break;
422 case IEEE80211_FC1_DIR_TODS:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530423 qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr3,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800424 ETHERNET_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530425 qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr2,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800426 ETHERNET_ADDR_LEN);
427 break;
428 case IEEE80211_FC1_DIR_FROMDS:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530429 qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr1,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800430 ETHERNET_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530431 qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr3,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800432 ETHERNET_ADDR_LEN);
433 break;
434 case IEEE80211_FC1_DIR_DSTODS:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530435 qdf_mem_copy(ethr_hdr->dest_addr, wh->i_addr3,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800436 ETHERNET_ADDR_LEN);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530437 qdf_mem_copy(ethr_hdr->src_addr, wh->i_addr4,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800438 ETHERNET_ADDR_LEN);
439 break;
440 }
441 }
442 if (llc_hdr == NULL) {
443 ethr_hdr->ethertype[0] = (ether_type >> 8) & 0xff;
444 ethr_hdr->ethertype[1] = (ether_type) & 0xff;
445 } else {
446 uint32_t pktlen =
Nirav Shahcbc6d722016-03-01 16:24:53 +0530447 qdf_nbuf_len(msdu) - sizeof(ethr_hdr->ethertype);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800448 TXRX_ASSERT2(pktlen <= ETHERNET_MTU);
449 ether_type = (uint16_t) pktlen;
Nirav Shahcbc6d722016-03-01 16:24:53 +0530450 ether_type = qdf_nbuf_len(msdu) - sizeof(struct ethernet_hdr_t);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800451 ethr_hdr->ethertype[0] = (ether_type >> 8) & 0xff;
452 ethr_hdr->ethertype[1] = (ether_type) & 0xff;
453 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530454 qdf_mem_copy(buf, ethr_hdr, ETHERNET_HDR_LEN);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800455}
456
457static inline A_STATUS
458ol_rx_decap_subfrm_amsdu(struct ol_txrx_vdev_t *vdev,
Nirav Shahcbc6d722016-03-01 16:24:53 +0530459 qdf_nbuf_t msdu, struct ol_rx_decap_info_t *info)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800460{
461 struct ol_txrx_pdev_t *pdev = vdev->pdev;
462 uint8_t *subfrm_hdr;
463 uint8_t localbuf[ETHERNET_HDR_LEN];
464 struct ethernet_hdr_t *ether_hdr = (struct ethernet_hdr_t *)localbuf;
465
Nirav Shahcbc6d722016-03-01 16:24:53 +0530466 subfrm_hdr = (uint8_t *) qdf_nbuf_data(msdu);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800467 if (pdev->frame_format == wlan_frm_fmt_native_wifi) {
468 /* decap to native wifi */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530469 qdf_mem_copy(ether_hdr, subfrm_hdr, ETHERNET_HDR_LEN);
Nirav Shahcbc6d722016-03-01 16:24:53 +0530470 qdf_nbuf_pull_head(msdu, ETHERNET_HDR_LEN);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800471 ol_rx_decap_to_native_wifi(vdev, msdu, info, ether_hdr);
472 } else if (pdev->frame_format == wlan_frm_fmt_802_3) {
473 if (pdev->sw_rx_llc_proc_enable) {
474 /* remove llc snap hdr if it's necessary according to
475 * 802.11 table P-3
476 */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530477 qdf_mem_copy(ether_hdr, subfrm_hdr, ETHERNET_HDR_LEN);
Nirav Shahcbc6d722016-03-01 16:24:53 +0530478 qdf_nbuf_pull_head(msdu, ETHERNET_HDR_LEN);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800479 ol_rx_decap_to_8023(vdev, msdu, info, ether_hdr);
480 } else {
481 /* subfrm of A-MSDU is already in 802.3 format.
482 * if target HW or FW has done LLC rmv process,
483 * we do nothing here.
484 */
485 }
486 } else {
487 /* todo for othertype */
488 }
489 return A_OK;
490
491}
492
493static inline A_STATUS
494ol_rx_decap_msdu(struct ol_txrx_vdev_t *vdev,
Nirav Shahcbc6d722016-03-01 16:24:53 +0530495 qdf_nbuf_t msdu, struct ol_rx_decap_info_t *info)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800496{
497 struct ol_txrx_pdev_t *pdev = vdev->pdev;
498 struct ieee80211_frame *wh;
Nirav Shahcbc6d722016-03-01 16:24:53 +0530499 wh = (struct ieee80211_frame *)qdf_nbuf_data(msdu);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800500
501 if (pdev->frame_format == wlan_frm_fmt_native_wifi) {
502 /* Decap to native wifi because according to MSFT(
503 * MSFT: http://msdn.microsoft.com/en-us/library/windows/
504 * hardware/ff552608(v=vs.85).aspx),
505 * we need to remove Qos and HTC field before indicate to OS.
506 */
507 if (IEEE80211_QOS_HAS_SEQ(wh)) {
508 info->hdr_len = ol_txrx_ieee80211_hdrsize(wh);
509 TXRX_ASSERT2(info->hdr_len <= sizeof(info->hdr));
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530510 qdf_mem_copy(info->hdr, /* use info->hdr as temp buf. */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800511 wh, info->hdr_len);
Nirav Shahcbc6d722016-03-01 16:24:53 +0530512 qdf_nbuf_pull_head(msdu, info->hdr_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800513 ol_rx_decap_to_native_wifi(vdev, msdu, info, NULL);
514 /* 802.11 hdr^ eth_hdr^ */
515 }
516 } else if (pdev->frame_format == wlan_frm_fmt_802_3) {
517 if (pdev->sw_rx_llc_proc_enable) {
518 info->hdr_len = ol_txrx_ieee80211_hdrsize(wh);
519 TXRX_ASSERT2(info->hdr_len <= sizeof(info->hdr));
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530520 qdf_mem_copy(info->hdr, /* use info->hdr as temp buf. */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800521 wh, info->hdr_len);
Nirav Shahcbc6d722016-03-01 16:24:53 +0530522 qdf_nbuf_pull_head(msdu, info->hdr_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800523 /* remove llc snap hdr if it's necessary according to
524 * 802.11 table P-3
525 */
526 ol_rx_decap_to_8023(vdev, msdu, info, /* 802.11 hdr */
527 NULL); /* ethernet hdr */
528 } else {
529 /* Subfrm of A-MSDU is already in 802.3 format.
530 * And if target HW or FW has done LLC rmv process (
531 * sw_rx_lc_proc_enable == 0), we do nothing here.
532 */
533 }
534 } else {
535 /* todo for othertype */
536 }
537 return A_OK;
538
539}
540
541A_STATUS
542ol_rx_decap(struct ol_txrx_vdev_t *vdev,
543 struct ol_txrx_peer_t *peer,
Nirav Shahcbc6d722016-03-01 16:24:53 +0530544 qdf_nbuf_t msdu, struct ol_rx_decap_info_t *info)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800545{
546 A_STATUS status;
547 uint8_t *mpdu_hdr;
548
549 if (!info->is_subfrm) {
550 if (info->is_msdu_cmpl_mpdu && !info->is_first_subfrm) {
551 /* It's normal MSDU. */
552 } else {
553 /* It's a first subfrm of A-MSDU and
554 may also be the last subfrm of A-MSDU */
555 info->is_subfrm = 1;
556 info->hdr_len = 0;
557 if (vdev->pdev->sw_subfrm_hdr_recovery_enable) {
558 /* we save the first subfrm mpdu hdr for
559 * subsequent subfrm 802.11 header recovery
560 * in certain chip(such as Riva).
561 */
Nirav Shahcbc6d722016-03-01 16:24:53 +0530562 mpdu_hdr = qdf_nbuf_data(msdu);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800563 info->hdr_len =
564 ol_txrx_ieee80211_hdrsize(mpdu_hdr);
565 TXRX_ASSERT2(info->hdr_len <=
566 sizeof(info->hdr));
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530567 qdf_mem_copy(info->hdr, mpdu_hdr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800568 info->hdr_len);
Nirav Shahcbc6d722016-03-01 16:24:53 +0530569 qdf_nbuf_pull_head(msdu, info->hdr_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800570 }
571 }
572 }
573
574 if (info->is_subfrm && vdev->pdev->sw_subfrm_hdr_recovery_enable) {
575 /*
576 * This case is enabled for some HWs (such as Riva). The HW
577 * de-aggregate doesn't have capability to generate 802.11
578 * header for non-first subframe of A-MSDU. That means sw needs
579 * to cache the first subfrm mpdu header to generate the
580 * subsequent subfrm's 802.11 header.
581 */
582 TXRX_ASSERT2(info->hdr_len != 0);
583 status = ol_rx_decap_subfrm_amsdu(vdev, msdu, info);
584 } else {
585 status = ol_rx_decap_msdu(vdev, msdu, info);
586 }
587
588 if (info->is_msdu_cmpl_mpdu)
589 info->is_subfrm = info->is_first_subfrm = info->hdr_len = 0;
590
591 return status;
592}
593#endif