blob: e8ecc887161b989bec4d7df9045a5d1e45412535 [file] [log] [blame]
Jeff Johnson295189b2012-06-20 16:38:30 -07001/*
2 * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
3 *
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
24 \file wlan_hdd_tx_rx.c
25
26 \brief Linux HDD Tx/RX APIs
27 Copyright 2008 (c) Qualcomm, Incorporated.
28 All Rights Reserved.
29 Qualcomm Confidential and Proprietary.
30
31 ==========================================================================*/
32
33/*---------------------------------------------------------------------------
34 Include files
35 -------------------------------------------------------------------------*/
36#include <wlan_hdd_tx_rx.h>
37#include <wlan_hdd_softap_tx_rx.h>
38#include <wlan_hdd_dp_utils.h>
39#include <wlan_qct_tl.h>
40#include <linux/netdevice.h>
41#include <linux/skbuff.h>
42#include <linux/etherdevice.h>
43
44#ifdef CONFIG_CFG80211
45#include <wlan_hdd_p2p.h>
46#include <linux/wireless.h>
47#include <net/cfg80211.h>
48#include <net/ieee80211_radiotap.h>
49#include "sapApi.h"
50#endif
51
52/*---------------------------------------------------------------------------
53 Preprocessor definitions and constants
54 -------------------------------------------------------------------------*/
55
56const v_U8_t hddWmmAcToHighestUp[] = {
57 SME_QOS_WMM_UP_RESV,
58 SME_QOS_WMM_UP_EE,
59 SME_QOS_WMM_UP_VI,
60 SME_QOS_WMM_UP_NC
61};
62
63//Mapping Linux AC interpretation to TL AC.
64const v_U8_t hdd_QdiscAcToTlAC[] = {
65 WLANTL_AC_VO,
66 WLANTL_AC_VI,
67 WLANTL_AC_BE,
68 WLANTL_AC_BK,
69};
70
71#ifdef CONFIG_CFG80211
72static struct sk_buff* hdd_mon_tx_fetch_pkt(hdd_adapter_t* pAdapter);
73#endif
74
75/*---------------------------------------------------------------------------
76 Type declarations
77 -------------------------------------------------------------------------*/
78
79/*---------------------------------------------------------------------------
80 Function definitions and documenation
81 -------------------------------------------------------------------------*/
82
83#ifdef DATA_PATH_UNIT_TEST
84//Utility function to dump an sk_buff
85static void dump_sk_buff(struct sk_buff * skb)
86{
87 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: head = %p ", __FUNCTION__, skb->head);
88 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: data = %p ", __FUNCTION__, skb->data);
89 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: tail = %p ", __FUNCTION__, skb->tail);
90 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: end = %p ", __FUNCTION__, skb->end);
91 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: len = %d ", __FUNCTION__, skb->len);
92 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: data_len = %d ", __FUNCTION__, skb->data_len);
93 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: mac_len = %d\n", __FUNCTION__, skb->mac_len);
94
95 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x ",
96 skb->data[0], skb->data[1], skb->data[2], skb->data[3], skb->data[4],
97 skb->data[5], skb->data[6], skb->data[7]);
98 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x \n",
99 skb->data[8], skb->data[9], skb->data[10], skb->data[11], skb->data[12],
100 skb->data[13], skb->data[14], skb->data[15]);
101}
102
103//Function for Unit Test only
104static void transport_thread(hdd_adapter_t *pAdapter)
105{
106 v_U8_t staId;
107 WLANTL_ACEnumType ac = WLANTL_AC_BE;
108 vos_pkt_t *pVosPacket = NULL ;
109 vos_pkt_t dummyPacket;
110 WLANTL_MetaInfoType pktMetaInfo;
111 WLANTL_RxMetaInfoType pktRxMetaInfo;
112 VOS_STATUS status = VOS_STATUS_E_FAILURE;
113
114 status = hdd_tx_fetch_packet_cbk( pAdapter->pvosContext,
115 &staId,
116 &ac,
117 &pVosPacket,
118 &pktMetaInfo );
119 if (status != VOS_STATUS_SUCCESS && status != VOS_STATUS_E_EMPTY)
120 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Test FAIL hdd_tx_fetch_packet_cbk", __FUNCTION__);
121 else
122 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Test PASS hdd_tx_fetch_packet_cbk", __FUNCTION__);
123
124 status = hdd_tx_complete_cbk(pAdapter->pvosContext, &dummyPacket, VOS_STATUS_SUCCESS);;
125 if (status != VOS_STATUS_SUCCESS)
126 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Test FAIL hdd_tx_complete_cbk", __FUNCTION__);
127 else
128 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Test PASS hdd_tx_complete_cbk", __FUNCTION__);
129
130 status = hdd_tx_low_resource_cbk(pVosPacket, pAdapter);
131 if (status != VOS_STATUS_SUCCESS)
132 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Test FAIL hdd_tx_low_resource_cbk", __FUNCTION__);
133 else
134 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Test PASS hdd_tx_low_resource_cbk", __FUNCTION__);
135
136 status = hdd_rx_packet_cbk( pAdapter->pvosContext,
137 &dummyPacket,
138 staId,
139 &pktRxMetaInfo);
140 if (status != VOS_STATUS_SUCCESS)
141 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Test FAIL hdd_rx_packet_cbk", __FUNCTION__);
142 else
143 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Test PASS hdd_rx_packet_cbk", __FUNCTION__);
144
145}
146#endif
147
148
149/**============================================================================
150 @brief hdd_flush_tx_queues() - Utility function to flush the TX queues
151
152 @param pAdapter : [in] pointer to adapter context
153 @return : VOS_STATUS_E_FAILURE if any errors encountered
154 : VOS_STATUS_SUCCESS otherwise
155 ===========================================================================*/
156static VOS_STATUS hdd_flush_tx_queues( hdd_adapter_t *pAdapter )
157{
158 VOS_STATUS status = VOS_STATUS_SUCCESS;
159 v_SINT_t i = -1;
160 hdd_list_node_t *anchor = NULL;
161 skb_list_node_t *pktNode = NULL;
162 struct sk_buff *skb = NULL;
163
164 while (++i != NUM_TX_QUEUES)
165 {
166 //Free up any packets in the Tx queue
167 spin_lock_bh(&pAdapter->wmm_tx_queue[i].lock);
168 while (true)
169 {
170 status = hdd_list_remove_front( &pAdapter->wmm_tx_queue[i], &anchor );
171 if(VOS_STATUS_E_EMPTY != status)
172 {
173 pktNode = list_entry(anchor, skb_list_node_t, anchor);
174 skb = pktNode->skb;
175 //TODO
176 //++pAdapter->stats.tx_dropped;
177 ++pAdapter->hdd_stats.hddTxRxStats.txFlushed;
178 ++pAdapter->hdd_stats.hddTxRxStats.txFlushedAC[i];
179 kfree_skb(skb);
180 continue;
181 }
182 break;
183 }
184 spin_unlock_bh(&pAdapter->wmm_tx_queue[i].lock);
185 // backpressure is no longer in effect
186 pAdapter->isTxSuspended[i] = VOS_FALSE;
187 }
188
189 return status;
190}
191
192#ifdef CONFIG_CFG80211
193static struct sk_buff* hdd_mon_tx_fetch_pkt(hdd_adapter_t* pAdapter)
194{
195 skb_list_node_t *pktNode = NULL;
196 struct sk_buff *skb = NULL;
197 v_SIZE_t size = 0;
198 WLANTL_ACEnumType ac = 0;
199 VOS_STATUS status = VOS_STATUS_E_FAILURE;
200 hdd_list_node_t *anchor = NULL;
201
202 if( NULL == pAdapter )
203 {
204 VOS_ASSERT(0);
205 return NULL;
206 }
207
208 // do we have any packets pending in this AC?
209 hdd_list_size( &pAdapter->wmm_tx_queue[ac], &size );
210 if( size == 0 )
211 {
212 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
213 "%s: NO Packet Pending", __FUNCTION__);
214 return NULL;
215 }
216
217 //Remove the packet from the queue
218 spin_lock_bh(&pAdapter->wmm_tx_queue[ac].lock);
219 status = hdd_list_remove_front( &pAdapter->wmm_tx_queue[ac], &anchor );
220 spin_unlock_bh(&pAdapter->wmm_tx_queue[ac].lock);
221
222 if(VOS_STATUS_SUCCESS == status)
223 {
224 //If success then we got a valid packet from some AC
225 pktNode = list_entry(anchor, skb_list_node_t, anchor);
226 skb = pktNode->skb;
227 }
228 else
229 {
230 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
231 "%s: Not able to remove Packet from the list",
232 __FUNCTION__);
233
234 return NULL;
235 }
236
237 // if we are in a backpressure situation see if we can turn the hose back on
238 if ( (pAdapter->isTxSuspended[ac]) &&
239 (size <= HDD_TX_QUEUE_LOW_WATER_MARK) )
240 {
241 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
242 "%s: TX queue[%d] re-enabled", __FUNCTION__, ac);
243 pAdapter->isTxSuspended[ac] = VOS_FALSE;
244 /* Enable Queues which we have disabled earlier */
245 netif_tx_start_all_queues( pAdapter->dev );
246 }
247
248 return skb;
249}
250
251void hdd_mon_tx_mgmt_pkt(hdd_adapter_t* pAdapter)
252{
253 hdd_cfg80211_state_t *cfgState;
254 struct sk_buff* skb;
255 hdd_adapter_t* pMonAdapter = NULL;
256 struct ieee80211_hdr *hdr;
257
258 if (pAdapter == NULL )
259 {
260 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
261 "%s: pAdapter is NULL", __func__);
262 return;
263 }
264
265 pMonAdapter = hdd_get_adapter( pAdapter->pHddCtx, WLAN_HDD_MONITOR );
266
267 cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
268
269 if( NULL != cfgState->buf )
270 {
271 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
272 "%s: Already one MGMT packet Tx going on", __func__);
273 return;
274 }
275
276 skb = hdd_mon_tx_fetch_pkt(pMonAdapter);
277
278 if (NULL == skb)
279 {
280 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
281 "%s: No Packet Pending", __func__);
282 return;
283 }
284
285 cfgState->buf = vos_mem_malloc( skb->len ); //buf;
286 if( cfgState->buf == NULL )
287 {
288 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
289 "%s: Failed to Allocate memory", __func__);
290 goto fail;
291 }
292
293 cfgState->len = skb->len;
294
295 vos_mem_copy( cfgState->buf, skb->data, skb->len);
296
297 cfgState->skb = skb; //buf;
298 cfgState->action_cookie = (tANI_U32)cfgState->buf;
299
300 hdr = (struct ieee80211_hdr *)skb->data;
301 if( (hdr->frame_control & HDD_FRAME_TYPE_MASK)
302 == HDD_FRAME_TYPE_MGMT )
303 {
304 if( (hdr->frame_control & HDD_FRAME_SUBTYPE_MASK)
305 == HDD_FRAME_SUBTYPE_DEAUTH )
306 {
307 hdd_softap_sta_deauth( pAdapter, hdr->addr1 );
308 goto mgmt_handled;
309 }
310 else if( (hdr->frame_control & HDD_FRAME_SUBTYPE_MASK)
311 == HDD_FRAME_SUBTYPE_DISASSOC )
312 {
313 hdd_softap_sta_disassoc( pAdapter, hdr->addr1 );
314 goto mgmt_handled;
315 }
316 }
317 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
318 "%s: Sending action frame to SAP to TX, Len %d", __func__, skb->len);
319
Jeff Johnson43971f52012-07-17 12:26:56 -0700320 if (VOS_STATUS_SUCCESS !=
Jeff Johnson295189b2012-06-20 16:38:30 -0700321 WLANSAP_SendAction( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
Jeff Johnsone7245742012-09-05 17:12:55 -0700322 skb->data, skb->len, 0) )
Jeff Johnson295189b2012-06-20 16:38:30 -0700323 {
324 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
325 "%s: WLANSAP_SendAction returned fail", __func__);
326 hdd_sendActionCnf( pAdapter, FALSE );
327 }
328 return;
329
330mgmt_handled:
331 hdd_sendActionCnf( pAdapter, TRUE );
332 return;
333fail:
334 kfree_skb(pAdapter->skb_to_tx);
335 pAdapter->skb_to_tx = NULL;
336 return;
337}
338
339void hdd_mon_tx_work_queue(struct work_struct *work)
340{
341 hdd_adapter_t* pAdapter = container_of(work, hdd_adapter_t, monTxWorkQueue);
342 hdd_mon_tx_mgmt_pkt(pAdapter);
343}
344
345int hdd_mon_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
346{
347 v_U16_t rt_hdr_len;
348 struct ieee80211_hdr *hdr;
349 hdd_adapter_t *pPgBkAdapter, *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
350 struct ieee80211_radiotap_header *rtap_hdr =
351 (struct ieee80211_radiotap_header *)skb->data;
352
353 /*Supplicant sends the EAPOL packet on monitor interface*/
354 pPgBkAdapter = pAdapter->sessionCtx.monitor.pAdapterForTx;
355 if(pPgBkAdapter == NULL)
356 {
357 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
358 "%s: No Adapter to piggy back. Dropping the pkt on monitor inf",
359 __func__);
360 goto fail; /* too short to be possibly valid */
361 }
362
363 /* check if toal skb length is greater then radio tab header length of not */
364 if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
365 goto fail; /* too short to be possibly valid */
366
367 /* check if radio tap header version is correct or not */
368 if (unlikely(rtap_hdr->it_version))
369 goto fail; /* only version 0 is supported */
370
371 /*Strip off the radio tap header*/
372 rt_hdr_len = ieee80211_get_radiotap_len(skb->data);
373
374 /* check if skb length if greator then total radio tap header length ot not*/
375 if (unlikely(skb->len < rt_hdr_len))
376 goto fail;
377
378 /* Update the trans_start for this netdev */
379 dev->trans_start = jiffies;
380 /*
381 * fix up the pointers accounting for the radiotap
382 * header still being in there.
383 */
384 skb_set_mac_header(skb, rt_hdr_len);
385 skb_set_network_header(skb, rt_hdr_len);
386 skb_set_transport_header(skb, rt_hdr_len);
387
388 /* Pull rtap header out of the skb */
389 skb_pull(skb, rt_hdr_len);
390
391 /*Supplicant adds: radiotap Hdr + radiotap data + 80211 Header. So after
392 * radio tap header and 802.11 header starts
393 */
394 hdr = (struct ieee80211_hdr *)skb->data;
395
396 /* Send data frames through the normal Data path. In this path we will
397 * conver rcvd 802.11 packet to 802.3 packet */
398 if ( (hdr->frame_control & HDD_FRAME_TYPE_MASK) == HDD_FRAME_TYPE_DATA)
399 {
400 v_U8_t da[6];
401 v_U8_t sa[6];
402
403 memcpy (da, hdr->addr1, VOS_MAC_ADDR_SIZE);
404 memcpy (sa, hdr->addr2, VOS_MAC_ADDR_SIZE);
405
406 /* Pull 802.11 MAC header */
407 skb_pull(skb, HDD_80211_HEADER_LEN);
408
409 if ( HDD_FRAME_SUBTYPE_QOSDATA ==
410 (hdr->frame_control & HDD_FRAME_SUBTYPE_MASK))
411 {
412 skb_pull(skb, HDD_80211_HEADER_QOS_CTL);
413 }
414
415 /* Pull LLC header */
416 skb_pull(skb, HDD_LLC_HDR_LEN);
417
418 /* Create space for Ethernet header */
419 skb_push(skb, HDD_MAC_HDR_SIZE*2);
420 memcpy(&skb->data[0], da, HDD_MAC_HDR_SIZE);
421 memcpy(&skb->data[HDD_DEST_ADDR_OFFSET], sa, HDD_MAC_HDR_SIZE);
422
423 /* Only EAPOL Data packets are allowed through monitor interface */
424 if (vos_be16_to_cpu(
425 (*(unsigned short*)&skb->data[HDD_ETHERTYPE_802_1_X_FRAME_OFFSET]) )
426 != HDD_ETHERTYPE_802_1_X)
427 {
428 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
429 "%s: Not a Eapol packet. Drop this frame", __func__);
430 //If not EAPOL frames, drop them.
431 kfree_skb(skb);
432 return NETDEV_TX_OK;
433 }
434
435 skb->protocol = htons(HDD_ETHERTYPE_802_1_X);
436
437 hdd_hostapd_select_queue(pPgBkAdapter->dev, skb);
438 return hdd_softap_hard_start_xmit( skb, pPgBkAdapter->dev );
439 }
440 else
441 {
442 VOS_STATUS status;
443 WLANTL_ACEnumType ac = 0;
444 skb_list_node_t *pktNode = NULL;
445 v_SIZE_t pktListSize = 0;
446
447 spin_lock(&pAdapter->wmm_tx_queue[ac].lock);
448 //If we have already reached the max queue size, disable the TX queue
449 if ( pAdapter->wmm_tx_queue[ac].count == pAdapter->wmm_tx_queue[ac].max_size)
450 {
451 /* We want to process one packet at a time, so lets disable all TX queues
452 * and re-enable the queues once we get TX feedback for this packet */
453 netif_tx_stop_all_queues(pAdapter->dev);
454 pAdapter->isTxSuspended[ac] = VOS_TRUE;
455 spin_unlock(&pAdapter->wmm_tx_queue[ac].lock);
456 return NETDEV_TX_BUSY;
457 }
458 spin_unlock(&pAdapter->wmm_tx_queue[ac].lock);
459
460 //Use the skb->cb field to hold the list node information
461 pktNode = (skb_list_node_t *)&skb->cb;
462
463 //Stick the OS packet inside this node.
464 pktNode->skb = skb;
465
466 INIT_LIST_HEAD(&pktNode->anchor);
467
468 //Insert the OS packet into the appropriate AC queue
469 spin_lock(&pAdapter->wmm_tx_queue[ac].lock);
470 status = hdd_list_insert_back_size( &pAdapter->wmm_tx_queue[ac],
471 &pktNode->anchor, &pktListSize );
472 spin_unlock(&pAdapter->wmm_tx_queue[ac].lock);
473
474 if ( !VOS_IS_STATUS_SUCCESS( status ) )
475 {
476 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
477 "%s:Insert Tx queue failed. Pkt dropped", __FUNCTION__);
478 kfree_skb(skb);
479 return NETDEV_TX_OK;
480 }
481
482 if ( pktListSize == 1 )
483 {
484 /* In this context we cannot acquire any mutex etc. And to transmit
485 * this packet we need to call SME API. So to take care of this we will
486 * schedule a workqueue
487 */
488 schedule_work(&pPgBkAdapter->monTxWorkQueue);
489 }
490 return NETDEV_TX_OK;
491 }
492
493fail:
494 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
495 "%s: Packet Rcvd at Monitor interface is not proper,"
496 " Dropping the packet",
497 __func__);
498 kfree_skb(skb);
499 return NETDEV_TX_OK;
500}
501#endif
502/**============================================================================
503 @brief hdd_hard_start_xmit() - Function registered with the Linux OS for
504 transmitting packets. There are 2 versions of this function. One that uses
505 locked queue and other that uses lockless queues. Both have been retained to
506 do some performance testing
507
508 @param skb : [in] pointer to OS packet (sk_buff)
509 @param dev : [in] pointer to Libra network device
510
511 @return : NET_XMIT_DROP if packets are dropped
512 : NET_XMIT_SUCCESS if packet is enqueued succesfully
513 ===========================================================================*/
514int hdd_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
515{
516 VOS_STATUS status;
517 WLANTL_ACEnumType ac;
518 sme_QosWmmUpType up;
519 skb_list_node_t *pktNode = NULL;
520 hdd_list_node_t *anchor = NULL;
521 v_SIZE_t pktListSize = 0;
522 hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
523 v_BOOL_t granted;
524
525 hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station;
526
527 v_BOOL_t txSuspended = VOS_FALSE;
528
529 ++pAdapter->hdd_stats.hddTxRxStats.txXmitCalled;
530
531 //Get TL AC corresponding to Qdisc queue index/AC.
532 ac = hdd_QdiscAcToTlAC[skb->queue_mapping];
533
534 //user priority from IP header, which is already extracted and set from
535 //select_queue call back function
536 up = skb->priority;
537
538 ++pAdapter->hdd_stats.hddTxRxStats.txXmitClassifiedAC[ac];
539
540#ifdef HDD_WMM_DEBUG
541 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
542 "%s: Classified as ac %d up %d", __FUNCTION__, ac, up);
543#endif // HDD_WMM_DEBUG
544
545 spin_lock(&pAdapter->wmm_tx_queue[ac].lock);
546
547 //If we have already reached the max queue size, disable the TX queue
548 if ( pAdapter->wmm_tx_queue[ac].count == pAdapter->wmm_tx_queue[ac].max_size)
549 {
550 ++pAdapter->hdd_stats.hddTxRxStats.txXmitBackPressured;
551 ++pAdapter->hdd_stats.hddTxRxStats.txXmitBackPressuredAC[ac];
552
553 netif_tx_stop_queue(netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)));
554 pAdapter->isTxSuspended[ac] = VOS_TRUE;
555 txSuspended = VOS_TRUE;
556 }
557
558 spin_unlock(&pAdapter->wmm_tx_queue[ac].lock);
559 if (VOS_TRUE == txSuspended)
560 {
561 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
562 "%s: TX queue full for AC=%d Disable OS TX queue",
563 __FUNCTION__, ac );
564 return NETDEV_TX_BUSY;
565 }
566
567 //Use the skb->cb field to hold the list node information
568 pktNode = (skb_list_node_t *)&skb->cb;
569
570 //Stick the OS packet inside this node.
571 pktNode->skb = skb;
572
573 //Stick the User Priority inside this node
574 pktNode->userPriority = up;
575
576
577 INIT_LIST_HEAD(&pktNode->anchor);
578
579 //Insert the OS packet into the appropriate AC queue
580 spin_lock(&pAdapter->wmm_tx_queue[ac].lock);
581 status = hdd_list_insert_back_size( &pAdapter->wmm_tx_queue[ac], &pktNode->anchor, &pktListSize );
582 spin_unlock(&pAdapter->wmm_tx_queue[ac].lock);
583
584 if ( !VOS_IS_STATUS_SUCCESS( status ) )
585 {
586 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s:Insert Tx queue failed. Pkt dropped", __FUNCTION__);
587 ++pAdapter->hdd_stats.hddTxRxStats.txXmitDropped;
588 ++pAdapter->hdd_stats.hddTxRxStats.txXmitDroppedAC[ac];
589 ++pAdapter->stats.tx_dropped;
590 kfree_skb(skb);
591 return NETDEV_TX_OK;
592 }
593
594 ++pAdapter->hdd_stats.hddTxRxStats.txXmitQueued;
595 ++pAdapter->hdd_stats.hddTxRxStats.txXmitQueuedAC[ac];
596
597 //Make sure we have access to this access category
598 if (likely(pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessAllowed) ||
599 ( pHddStaCtx->conn_info.uIsAuthenticated == VOS_FALSE))
600 {
601 granted = VOS_TRUE;
602 }
603 else
604 {
605 status = hdd_wmm_acquire_access( pAdapter, ac, &granted );
606 }
607
608 if ( granted && ( pktListSize == 1 ))
609 {
610 //Let TL know we have a packet to send for this AC
611 //VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s:Indicating Packet to TL", __FUNCTION__);
612 status = WLANTL_STAPktPending( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext, pHddStaCtx->conn_info.staId[0], ac );
613
614 if ( !VOS_IS_STATUS_SUCCESS( status ) )
615 {
616 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "%s: Failed to signal TL for AC=%d", __FUNCTION__, ac );
617
618 //Remove the packet from queue. It must be at the back of the queue, as TX thread cannot preempt us in the middle
619 //as we are in a soft irq context. Also it must be the same packet that we just allocated.
620 spin_lock(&pAdapter->wmm_tx_queue[ac].lock);
621 status = hdd_list_remove_back( &pAdapter->wmm_tx_queue[ac], &anchor );
622 spin_unlock(&pAdapter->wmm_tx_queue[ac].lock);
623 ++pAdapter->stats.tx_dropped;
624 ++pAdapter->hdd_stats.hddTxRxStats.txXmitDropped;
625 ++pAdapter->hdd_stats.hddTxRxStats.txXmitDroppedAC[ac];
626 kfree_skb(skb);
627 return NETDEV_TX_OK;
628 }
629 }
630
631 dev->trans_start = jiffies;
632
633 return NETDEV_TX_OK;
634}
635
636/**============================================================================
637 @brief hdd_tx_timeout() - Function called by OS if there is any
638 timeout during transmission. Since HDD simply enqueues packet
639 and returns control to OS right away, this would never be invoked
640
641 @param dev : [in] pointer to Libra network device
642 @return : None
643 ===========================================================================*/
644void hdd_tx_timeout(struct net_device *dev)
645{
646 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
647 "%s: Transmission timeout occurred", __FUNCTION__);
648 //Getting here implies we disabled the TX queues for too long. Queues are
649 //disabled either because of disassociation or low resource scenarios. In
650 //case of disassociation it is ok to ignore this. But if associated, we have
651 //do possible recovery here
652}
653
654
655/**============================================================================
656 @brief hdd_stats() - Function registered with the Linux OS for
657 device TX/RX statistic
658
659 @param dev : [in] pointer to Libra network device
660
661 @return : pointer to net_device_stats structure
662 ===========================================================================*/
663struct net_device_stats* hdd_stats(struct net_device *dev)
664{
665 hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
666
667 return &pAdapter->stats;
668}
669
670
671/**============================================================================
672 @brief hdd_init_tx_rx() - Init function to initialize Tx/RX
673 modules in HDD
674
675 @param pAdapter : [in] pointer to adapter context
676 @return : VOS_STATUS_E_FAILURE if any errors encountered
677 : VOS_STATUS_SUCCESS otherwise
678 ===========================================================================*/
679VOS_STATUS hdd_init_tx_rx( hdd_adapter_t *pAdapter )
680{
681 VOS_STATUS status = VOS_STATUS_SUCCESS;
682 v_SINT_t i = -1;
683
684 pAdapter->isVosOutOfResource = VOS_FALSE;
685
686 //vos_mem_zero(&pAdapter->stats, sizeof(struct net_device_stats));
687 //Will be zeroed out during alloc
688
689 while (++i != NUM_TX_QUEUES)
690 {
691 pAdapter->isTxSuspended[i] = VOS_FALSE;
692 hdd_list_init( &pAdapter->wmm_tx_queue[i], HDD_TX_QUEUE_MAX_LEN);
693 }
694
695 return status;
696}
697
698
699/**============================================================================
700 @brief hdd_deinit_tx_rx() - Deinit function to clean up Tx/RX
701 modules in HDD
702
703 @param pAdapter : [in] pointer to adapter context
704 @return : VOS_STATUS_E_FAILURE if any errors encountered
705 : VOS_STATUS_SUCCESS otherwise
706 ===========================================================================*/
707VOS_STATUS hdd_deinit_tx_rx( hdd_adapter_t *pAdapter )
708{
709 VOS_STATUS status = VOS_STATUS_SUCCESS;
710 v_SINT_t i = -1;
711
712 status = hdd_flush_tx_queues(pAdapter);
713 while (++i != NUM_TX_QUEUES)
714 {
715 //Free up actual list elements in the Tx queue
716 hdd_list_destroy( &pAdapter->wmm_tx_queue[i] );
717 }
718
719 return status;
720}
721
722
723/**============================================================================
724 @brief hdd_disconnect_tx_rx() - Disconnect function to clean up Tx/RX
725 modules in HDD
726
727 @param pAdapter : [in] pointer to adapter context
728 @return : VOS_STATUS_E_FAILURE if any errors encountered
729 : VOS_STATUS_SUCCESS otherwise
730 ===========================================================================*/
731VOS_STATUS hdd_disconnect_tx_rx( hdd_adapter_t *pAdapter )
732{
733 return hdd_flush_tx_queues(pAdapter);
734}
735
736
737/**============================================================================
738 @brief hdd_IsEAPOLPacket() - Checks the packet is EAPOL or not.
739
740 @param pVosPacket : [in] pointer to vos packet
741 @return : VOS_TRUE if the packet is EAPOL
742 : VOS_FALSE otherwise
743 ===========================================================================*/
744
745v_BOOL_t hdd_IsEAPOLPacket( vos_pkt_t *pVosPacket )
746{
747 VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
748 v_BOOL_t fEAPOL = VOS_FALSE;
749 void *pBuffer = NULL;
750
751
752 vosStatus = vos_pkt_peek_data( pVosPacket, (v_SIZE_t)HDD_ETHERTYPE_802_1_X_FRAME_OFFSET,
753 &pBuffer, HDD_ETHERTYPE_802_1_X_SIZE );
754 if (VOS_IS_STATUS_SUCCESS( vosStatus ) )
755 {
756 if ( vos_be16_to_cpu( *(unsigned short*)pBuffer ) == HDD_ETHERTYPE_802_1_X )
757 {
758 fEAPOL = VOS_TRUE;
759 }
760 }
761
762 return fEAPOL;
763}
764
765
766#ifdef FEATURE_WLAN_WAPI // Need to update this function
767/**============================================================================
768 @brief hdd_IsWAIPacket() - Checks the packet is WAI or not.
769
770 @param pVosPacket : [in] pointer to vos packet
771 @return : VOS_TRUE if the packet is WAI
772 : VOS_FALSE otherwise
773 ===========================================================================*/
774
775v_BOOL_t hdd_IsWAIPacket( vos_pkt_t *pVosPacket )
776{
777 VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
778 v_BOOL_t fIsWAI = VOS_FALSE;
779 void *pBuffer = NULL;
780
781 // Need to update this function
782 vosStatus = vos_pkt_peek_data( pVosPacket, (v_SIZE_t)HDD_ETHERTYPE_802_1_X_FRAME_OFFSET,
783 &pBuffer, HDD_ETHERTYPE_802_1_X_SIZE );
784
785 if (VOS_IS_STATUS_SUCCESS( vosStatus ) )
786 {
787 if ( vos_be16_to_cpu( *(unsigned short*)pBuffer ) == HDD_ETHERTYPE_WAI)
788 {
789 fIsWAI = VOS_TRUE;
790 }
791 }
792
793 return fIsWAI;
794}
795#endif /* FEATURE_WLAN_WAPI */
796
797/**============================================================================
798 @brief hdd_tx_complete_cbk() - Callback function invoked by TL
799 to indicate that a packet has been transmitted across the SDIO bus
800 succesfully. OS packet resources can be released after this cbk.
801
802 @param vosContext : [in] pointer to VOS context
803 @param pVosPacket : [in] pointer to VOS packet (containing skb)
804 @param vosStatusIn : [in] status of the transmission
805
806 @return : VOS_STATUS_E_FAILURE if any errors encountered
807 : VOS_STATUS_SUCCESS otherwise
808 ===========================================================================*/
809VOS_STATUS hdd_tx_complete_cbk( v_VOID_t *vosContext,
810 vos_pkt_t *pVosPacket,
811 VOS_STATUS vosStatusIn )
812{
813 VOS_STATUS status = VOS_STATUS_SUCCESS;
814 hdd_adapter_t *pAdapter = NULL;
815 hdd_context_t *pHddCtx = NULL;
816 void* pOsPkt = NULL;
817
818 if( ( NULL == vosContext ) || ( NULL == pVosPacket ) )
819 {
820 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Null params being passed", __FUNCTION__);
821 return VOS_STATUS_E_FAILURE;
822 }
823
824 //Return the skb to the OS
825 status = vos_pkt_get_os_packet( pVosPacket, &pOsPkt, VOS_TRUE );
826 if(!VOS_IS_STATUS_SUCCESS( status ))
827 {
828 //This is bad but still try to free the VOSS resources if we can
829 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Failure extracting skb from vos pkt", __FUNCTION__);
830 vos_pkt_return_packet( pVosPacket );
831 return VOS_STATUS_E_FAILURE;
832 }
833
834 //Get the HDD context.
835 pHddCtx = (hdd_context_t *)vos_get_context( VOS_MODULE_ID_HDD, vosContext );
836 //Get the Adapter context.
837 pAdapter = hdd_get_adapter(pHddCtx,WLAN_HDD_INFRA_STATION);
838 if(pAdapter == NULL)
839 {
840 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: HDD adapter context is Null", __FUNCTION__);
841 }
842 else
843 {
844 ++pAdapter->hdd_stats.hddTxRxStats.txCompleted;
845 }
846
847 kfree_skb((struct sk_buff *)pOsPkt);
848
849 //Return the VOS packet resources.
850 status = vos_pkt_return_packet( pVosPacket );
851 if(!VOS_IS_STATUS_SUCCESS( status ))
852 {
853 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Could not return VOS packet to the pool", __FUNCTION__);
854 }
855
856 return status;
857}
858
859
860/**============================================================================
861 @brief hdd_tx_fetch_packet_cbk() - Callback function invoked by TL to
862 fetch a packet for transmission.
863
864 @param vosContext : [in] pointer to VOS context
865 @param staId : [in] Station for which TL is requesting a pkt
866 @param ac : [in] access category requested by TL
867 @param pVosPacket : [out] pointer to VOS packet packet pointer
868 @param pPktMetaInfo : [out] pointer to meta info for the pkt
869
870 @return : VOS_STATUS_E_EMPTY if no packets to transmit
871 : VOS_STATUS_E_FAILURE if any errors encountered
872 : VOS_STATUS_SUCCESS otherwise
873 ===========================================================================*/
874VOS_STATUS hdd_tx_fetch_packet_cbk( v_VOID_t *vosContext,
875 v_U8_t *pStaId,
876 WLANTL_ACEnumType ac,
877 vos_pkt_t **ppVosPacket,
878 WLANTL_MetaInfoType *pPktMetaInfo )
879{
880 VOS_STATUS status = VOS_STATUS_E_FAILURE;
881 hdd_adapter_t *pAdapter = NULL;
882 hdd_context_t *pHddCtx = NULL;
883 hdd_list_node_t *anchor = NULL;
884 skb_list_node_t *pktNode = NULL;
885 struct sk_buff *skb = NULL;
886 vos_pkt_t *pVosPacket = NULL;
887 v_MACADDR_t* pDestMacAddress = NULL;
888 v_TIME_t timestamp;
889 WLANTL_ACEnumType newAc;
890 v_SIZE_t size = 0;
891 tANI_U8 acAdmitted, i;
892
893 //Sanity check on inputs
894 if ( ( NULL == vosContext ) ||
895 ( NULL == pStaId ) ||
896 ( NULL == ppVosPacket ) ||
897 ( NULL == pPktMetaInfo ) )
898 {
899 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Null Params being passed", __FUNCTION__);
900 return VOS_STATUS_E_FAILURE;
901 }
902
903 //Get the HDD context.
904 pHddCtx = (hdd_context_t *)vos_get_context( VOS_MODULE_ID_HDD, vosContext );
905 if(pHddCtx == NULL)
906 {
907 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: HDD adapter context is Null", __FUNCTION__);
908 return VOS_STATUS_E_FAILURE;
909 }
910
911 pAdapter = pHddCtx->sta_to_adapter[*pStaId];
912 if( NULL == pAdapter )
913 {
914 VOS_ASSERT(0);
915 return VOS_STATUS_E_FAILURE;
916 }
917
918 ++pAdapter->hdd_stats.hddTxRxStats.txFetched;
919
920 *ppVosPacket = NULL;
921
922 //Make sure the AC being asked for is sane
923 if( ac >= WLANTL_MAX_AC || ac < 0)
924 {
925 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Invalid AC %d passed by TL", __FUNCTION__, ac);
926 return VOS_STATUS_E_FAILURE;
927 }
928
929 ++pAdapter->hdd_stats.hddTxRxStats.txFetchedAC[ac];
930
931#ifdef HDD_WMM_DEBUG
932 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,"%s: AC %d passed by TL", __FUNCTION__, ac);
933#endif // HDD_WMM_DEBUG
934
935 // We find an AC with packets
936 // or we determine we have no more packets to send
937 // HDD is not allowed to change AC.
938
939 // has this AC been admitted? or
940 // To allow EAPOL packets when not authenticated
941 if (unlikely((0==pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessAllowed) &&
942 (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.uIsAuthenticated))
943 {
944 ++pAdapter->hdd_stats.hddTxRxStats.txFetchEmpty;
945#ifdef HDD_WMM_DEBUG
946 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
947 "%s: no packets pending", __FUNCTION__);
948#endif // HDD_WMM_DEBUG
949 return VOS_STATUS_E_FAILURE;
950 }
951
952 // do we have any packets pending in this AC?
953 hdd_list_size( &pAdapter->wmm_tx_queue[ac], &size );
954 if( size > 0 )
955 {
956 // yes, so process it
957#ifdef HDD_WMM_DEBUG
958 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
959 "%s: AC %d has packets pending", __FUNCTION__, ac);
960#endif // HDD_WMM_DEBUG
961 }
962 else
963 {
964 ++pAdapter->hdd_stats.hddTxRxStats.txFetchEmpty;
965#ifdef HDD_WMM_DEBUG
966 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
967 "%s: no packets pending", __FUNCTION__);
968#endif // HDD_WMM_DEBUG
969 return VOS_STATUS_E_FAILURE;
970 }
971
972 //Get the vos packet. I don't want to dequeue and enqueue again if we are out of VOS resources
973 //This simplifies the locking and unlocking of Tx queue
974 status = vos_pkt_wrap_data_packet( &pVosPacket,
975 VOS_PKT_TYPE_TX_802_3_DATA,
976 NULL, //OS Pkt is not being passed
977 hdd_tx_low_resource_cbk,
978 pAdapter );
979
980 if (status == VOS_STATUS_E_ALREADY || status == VOS_STATUS_E_RESOURCES)
981 {
982 //Remember VOS is in a low resource situation
983 pAdapter->isVosOutOfResource = VOS_TRUE;
984 ++pAdapter->hdd_stats.hddTxRxStats.txFetchLowResources;
985 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: VOSS in Low Resource scenario", __FUNCTION__);
986 //TL will now think we have no more packets in this AC
987 return VOS_STATUS_E_FAILURE;
988 }
989
990 //Remove the packet from the queue
991 spin_lock_bh(&pAdapter->wmm_tx_queue[ac].lock);
992 status = hdd_list_remove_front( &pAdapter->wmm_tx_queue[ac], &anchor );
993 spin_unlock_bh(&pAdapter->wmm_tx_queue[ac].lock);
994
995 if(VOS_STATUS_SUCCESS == status)
996 {
997 //If success then we got a valid packet from some AC
998 pktNode = list_entry(anchor, skb_list_node_t, anchor);
999 skb = pktNode->skb;
1000 }
1001 else
1002 {
1003 ++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeueError;
1004 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "%s: Error in de-queuing "
1005 "skb from Tx queue status = %d", __FUNCTION__, status );
1006 vos_pkt_return_packet(pVosPacket);
1007 return VOS_STATUS_E_FAILURE;
1008 }
1009
1010 //Attach skb to VOS packet.
1011 status = vos_pkt_set_os_packet( pVosPacket, skb );
1012 if (status != VOS_STATUS_SUCCESS)
1013 {
1014 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Error attaching skb", __FUNCTION__);
1015 vos_pkt_return_packet(pVosPacket);
1016 ++pAdapter->stats.tx_dropped;
1017 ++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeueError;
1018 kfree_skb(skb);
1019 return VOS_STATUS_E_FAILURE;
1020 }
1021
1022 //Just being paranoid. To be removed later
1023 if(pVosPacket == NULL)
1024 {
1025 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: VOS packet returned by VOSS is NULL", __FUNCTION__);
1026 ++pAdapter->stats.tx_dropped;
1027 ++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeueError;
1028 kfree_skb(skb);
1029 return VOS_STATUS_E_FAILURE;
1030 }
1031
1032 //Return VOS packet to TL;
1033 *ppVosPacket = pVosPacket;
1034
1035 //Fill out the meta information needed by TL
1036 //FIXME This timestamp is really the time stamp of wrap_data_packet
1037 vos_pkt_get_timestamp( pVosPacket, &timestamp );
1038 pPktMetaInfo->usTimeStamp = (v_U16_t)timestamp;
1039
1040 if(pAdapter->sessionCtx.station.conn_info.uIsAuthenticated == VOS_TRUE)
1041 pPktMetaInfo->ucIsEapol = 0;
1042 else
1043 pPktMetaInfo->ucIsEapol = hdd_IsEAPOLPacket( pVosPacket ) ? 1 : 0;
1044
1045#ifdef FEATURE_WLAN_WAPI
1046 // Override usIsEapol value when its zero for WAPI case
1047 pPktMetaInfo->ucIsWai = hdd_IsWAIPacket( pVosPacket ) ? 1 : 0;
1048#endif /* FEATURE_WLAN_WAPI */
1049
1050 if ((HDD_WMM_USER_MODE_NO_QOS == pHddCtx->cfg_ini->WmmMode) ||
1051 (!pAdapter->hddWmmStatus.wmmQap))
1052 {
1053 // either we don't want QoS or the AP doesn't support QoS
1054 pPktMetaInfo->ucUP = 0;
1055 pPktMetaInfo->ucTID = 0;
1056 }
1057 else
1058 {
1059 /* 1. Check if ACM is set for this AC
1060 * 2. If set, check if this AC had already admitted
1061 * 3. If not already admitted, downgrade the UP to next best UP */
1062 if(!pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessRequired ||
1063 pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcTspecValid)
1064 {
1065 pPktMetaInfo->ucUP = pktNode->userPriority;
1066 pPktMetaInfo->ucTID = pPktMetaInfo->ucUP;
1067 }
1068 else
1069 {
1070 //Downgrade the UP
1071 acAdmitted = pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcTspecValid;
1072 newAc = WLANTL_AC_BK;
1073 for (i=ac-1; i>0; i--)
1074 {
1075 if (pAdapter->hddWmmStatus.wmmAcStatus[i].wmmAcAccessRequired == 0)
1076 {
1077 newAc = i;
1078 break;
1079 }
1080 }
1081 pPktMetaInfo->ucUP = hddWmmAcToHighestUp[newAc];
1082 pPktMetaInfo->ucTID = pPktMetaInfo->ucUP;
1083 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_LOW,"Downgrading UP %d to UP %d ", pktNode->userPriority, pPktMetaInfo->ucUP);
1084 }
1085 }
1086
1087 pPktMetaInfo->ucType = 0; //FIXME Don't know what this is
1088 pPktMetaInfo->ucDisableFrmXtl = 0; //802.3 frame so we need to xlate
1089 if ( 1 < size )
1090 {
1091 pPktMetaInfo->bMorePackets = 1; //HDD has more packets to send
1092 }
1093 else
1094 {
1095 pPktMetaInfo->bMorePackets = 0;
1096 }
1097
1098 //Extract the destination address from ethernet frame
1099 pDestMacAddress = (v_MACADDR_t*)skb->data;
1100 pPktMetaInfo->ucBcast = vos_is_macaddr_broadcast( pDestMacAddress ) ? 1 : 0;
1101 pPktMetaInfo->ucMcast = vos_is_macaddr_group( pDestMacAddress ) ? 1 : 0;
1102
1103
1104
1105 // if we are in a backpressure situation see if we can turn the hose back on
1106 if ( (pAdapter->isTxSuspended[ac]) &&
1107 (size <= HDD_TX_QUEUE_LOW_WATER_MARK) )
1108 {
1109 ++pAdapter->hdd_stats.hddTxRxStats.txFetchDePressured;
1110 ++pAdapter->hdd_stats.hddTxRxStats.txFetchDePressuredAC[ac];
1111 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
1112 "%s: TX queue[%d] re-enabled", __FUNCTION__, ac);
1113 pAdapter->isTxSuspended[ac] = VOS_FALSE;
1114 netif_tx_wake_queue(netdev_get_tx_queue(pAdapter->dev,
1115 skb_get_queue_mapping(skb) ));
1116 }
1117
1118
1119 // We're giving the packet to TL so consider it transmitted from
1120 // a statistics perspective. We account for it here instead of
1121 // when the packet is returned for two reasons. First, TL will
1122 // manipulate the skb to the point where the len field is not
1123 // accurate, leading to inaccurate byte counts if we account for
1124 // it later. Second, TL does not provide any feedback as to
1125 // whether or not the packet was successfully sent over the air,
1126 // so the packet counts will be the same regardless of where we
1127 // account for them
1128 pAdapter->stats.tx_bytes += skb->len;
1129 ++pAdapter->stats.tx_packets;
1130 ++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeued;
1131 ++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeuedAC[ac];
1132
1133 if(pHddCtx->cfg_ini->thermalMitigationEnable)
1134 {
1135 if(mutex_lock_interruptible(&pHddCtx->tmInfo.tmOperationLock))
1136 {
1137 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
1138 "%s: Tm Lock fail", __FUNCTION__);
1139 return VOS_STATUS_E_FAILURE;
1140 }
1141 if(WLAN_HDD_TM_LEVEL_1 < pHddCtx->tmInfo.currentTmLevel)
1142 {
1143 if(0 == pHddCtx->tmInfo.txFrameCount)
1144 {
1145 /* Just recovered from sleep timeout */
1146 pHddCtx->tmInfo.lastOpenTs = timestamp;
1147 }
1148
1149 if(((timestamp - pHddCtx->tmInfo.lastOpenTs) > (pHddCtx->tmInfo.tmAction.txOperationDuration / 10)) &&
1150 (pHddCtx->tmInfo.txFrameCount >= pHddCtx->tmInfo.tmAction.txBlockFrameCountThreshold))
1151 {
1152 spin_lock(&pAdapter->wmm_tx_queue[ac].lock);
1153 /* During TX open duration, TX frame count is larger than threshold
1154 * Block TX during Sleep time */
1155 netif_tx_stop_all_queues(pAdapter->dev);
1156 spin_unlock(&pAdapter->wmm_tx_queue[ac].lock);
1157 pHddCtx->tmInfo.lastblockTs = timestamp;
1158 if(VOS_TIMER_STATE_STOPPED == vos_timer_getCurrentState(&pHddCtx->tmInfo.txSleepTimer))
1159 {
1160 vos_timer_start(&pHddCtx->tmInfo.txSleepTimer, pHddCtx->tmInfo.tmAction.txSleepDuration);
1161 }
1162 }
1163 else if(((timestamp - pHddCtx->tmInfo.lastOpenTs) > (pHddCtx->tmInfo.tmAction.txOperationDuration / 10)) &&
1164 (pHddCtx->tmInfo.txFrameCount < pHddCtx->tmInfo.tmAction.txBlockFrameCountThreshold))
1165 {
1166 /* During TX open duration, TX frame count is less than threshold
1167 * Reset count and timestamp to prepare next cycle */
1168 pHddCtx->tmInfo.lastOpenTs = timestamp;
1169 pHddCtx->tmInfo.txFrameCount = 0;
1170 }
1171 else
1172 {
1173 /* Do Nothing */
1174 }
1175 pHddCtx->tmInfo.txFrameCount++;
1176 }
1177 mutex_unlock(&pHddCtx->tmInfo.tmOperationLock);
1178 }
1179
1180
1181#ifdef HDD_WMM_DEBUG
1182 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,"%s: Valid VOS PKT returned to TL", __FUNCTION__);
1183#endif // HDD_WMM_DEBUG
1184
1185 return status;
1186}
1187
1188
1189/**============================================================================
1190 @brief hdd_tx_low_resource_cbk() - Callback function invoked in the
1191 case where VOS packets are not available at the time of the call to get
1192 packets. This callback function is invoked by VOS when packets are
1193 available.
1194
1195 @param pVosPacket : [in] pointer to VOS packet
1196 @param userData : [in] opaque user data that was passed initially
1197
1198 @return : VOS_STATUS_E_FAILURE if any errors encountered,
1199 : VOS_STATUS_SUCCESS otherwise
1200 =============================================================================*/
1201VOS_STATUS hdd_tx_low_resource_cbk( vos_pkt_t *pVosPacket,
1202 v_VOID_t *userData )
1203{
1204 VOS_STATUS status;
1205 v_SINT_t i = 0;
1206 v_SIZE_t size = 0;
1207 hdd_adapter_t* pAdapter = (hdd_adapter_t *)userData;
1208
1209 if(pAdapter == NULL)
1210 {
1211 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: HDD adapter context is Null", __FUNCTION__);
1212 return VOS_STATUS_E_FAILURE;
1213 }
1214
1215 //Return the packet to VOS. We just needed to know that VOS is out of low resource
1216 //situation. Here we will only signal TL that there is a pending data for a STA.
1217 //VOS packet will be requested (if needed) when TL comes back to fetch data.
1218 vos_pkt_return_packet( pVosPacket );
1219
1220 pAdapter->isVosOutOfResource = VOS_FALSE;
1221
1222 //Indicate to TL that there is pending data if a queue is non empty
1223 for( i=NUM_TX_QUEUES-1; i>=0; --i )
1224 {
1225 size = 0;
1226 hdd_list_size( &pAdapter->wmm_tx_queue[i], &size );
1227 if ( size > 0 )
1228 {
1229 status = WLANTL_STAPktPending( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
1230 (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId [0],
1231 (WLANTL_ACEnumType)i );
1232 if( !VOS_IS_STATUS_SUCCESS( status ) )
1233 {
1234 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Failure in indicating pkt to TL for ac=%d", __FUNCTION__,i);
1235 }
1236 }
1237 }
1238
1239 return VOS_STATUS_SUCCESS;
1240}
1241
1242
1243/**============================================================================
1244 @brief hdd_rx_packet_cbk() - Receive callback registered with TL.
1245 TL will call this to notify the HDD when one or more packets were
1246 received for a registered STA.
1247
1248 @param vosContext : [in] pointer to VOS context
1249 @param pVosPacketChain : [in] pointer to VOS packet chain
1250 @param staId : [in] Station Id
1251 @param pRxMetaInfo : [in] pointer to meta info for the received pkt(s)
1252
1253 @return : VOS_STATUS_E_FAILURE if any errors encountered,
1254 : VOS_STATUS_SUCCESS otherwise
1255 ===========================================================================*/
1256VOS_STATUS hdd_rx_packet_cbk( v_VOID_t *vosContext,
1257 vos_pkt_t *pVosPacketChain,
1258 v_U8_t staId,
1259 WLANTL_RxMetaInfoType* pRxMetaInfo )
1260{
1261 hdd_adapter_t *pAdapter = NULL;
1262 hdd_context_t *pHddCtx = NULL;
1263 VOS_STATUS status = VOS_STATUS_E_FAILURE;
1264 int rxstat;
1265 struct sk_buff *skb = NULL;
1266 vos_pkt_t* pVosPacket;
1267 vos_pkt_t* pNextVosPacket;
1268
1269 //Sanity check on inputs
1270 if ( ( NULL == vosContext ) ||
1271 ( NULL == pVosPacketChain ) ||
1272 ( NULL == pRxMetaInfo ) )
1273 {
1274 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Null params being passed", __FUNCTION__);
1275 return VOS_STATUS_E_FAILURE;
1276 }
1277
1278 pHddCtx = (hdd_context_t *)vos_get_context( VOS_MODULE_ID_HDD, vosContext );
1279 if ( NULL == pHddCtx )
1280 {
1281 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: HDD adapter context is Null", __FUNCTION__);
1282 return VOS_STATUS_E_FAILURE;
1283 }
1284
1285 pAdapter = pHddCtx->sta_to_adapter[staId];
1286 if( NULL == pAdapter )
1287 {
1288 VOS_ASSERT(0);
1289 return VOS_STATUS_E_FAILURE;
1290 }
1291
1292 ++pAdapter->hdd_stats.hddTxRxStats.rxChains;
1293
1294 // walk the chain until all are processed
1295 pVosPacket = pVosPacketChain;
1296 do
1297 {
1298 // get the pointer to the next packet in the chain
1299 // (but don't unlink the packet since we free the entire chain later)
1300 status = vos_pkt_walk_packet_chain( pVosPacket, &pNextVosPacket, VOS_FALSE);
1301
1302 // both "success" and "empty" are acceptable results
1303 if (!((status == VOS_STATUS_SUCCESS) || (status == VOS_STATUS_E_EMPTY)))
1304 {
1305 ++pAdapter->hdd_stats.hddTxRxStats.rxDropped;
1306 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Failure walking packet chain", __FUNCTION__);
1307 return VOS_STATUS_E_FAILURE;
1308 }
1309
1310 // Extract the OS packet (skb).
1311 // Tell VOS to detach the OS packet from the VOS packet
1312 status = vos_pkt_get_os_packet( pVosPacket, (v_VOID_t **)&skb, VOS_TRUE );
1313 if(!VOS_IS_STATUS_SUCCESS( status ))
1314 {
1315 ++pAdapter->hdd_stats.hddTxRxStats.rxDropped;
1316 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Failure extracting skb from vos pkt", __FUNCTION__);
1317 return VOS_STATUS_E_FAILURE;
1318 }
Jeff Johnsone7245742012-09-05 17:12:55 -07001319
1320 if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)
1321 {
1322 VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
1323 "Magic cookie(%x) for adapter sanity verification is invalid", pAdapter->magic);
1324 return eHAL_STATUS_FAILURE;
1325 }
1326
Jeff Johnson295189b2012-06-20 16:38:30 -07001327 skb->dev = pAdapter->dev;
1328 skb->protocol = eth_type_trans(skb, skb->dev);
1329 skb->ip_summed = CHECKSUM_UNNECESSARY;
1330 ++pAdapter->hdd_stats.hddTxRxStats.rxPackets;
1331 ++pAdapter->stats.rx_packets;
1332 pAdapter->stats.rx_bytes += skb->len;
Jeff Johnsone7245742012-09-05 17:12:55 -07001333#ifdef WLAN_FEATURE_HOLD_RX_WAKELOCK
1334 wake_lock_timeout(&pHddCtx->rx_wake_lock, HDD_WAKE_LOCK_DURATION);
1335#endif
Jeff Johnson295189b2012-06-20 16:38:30 -07001336 rxstat = netif_rx_ni(skb);
1337 if (NET_RX_SUCCESS == rxstat)
1338 {
1339 ++pAdapter->hdd_stats.hddTxRxStats.rxDelivered;
1340 }
1341 else
1342 {
1343 ++pAdapter->hdd_stats.hddTxRxStats.rxRefused;
1344 }
1345 // now process the next packet in the chain
1346 pVosPacket = pNextVosPacket;
1347
1348 } while (pVosPacket);
1349
1350 //Return the entire VOS packet chain to the resource pool
1351 status = vos_pkt_return_packet( pVosPacketChain );
1352 if(!VOS_IS_STATUS_SUCCESS( status ))
1353 {
1354 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: Failure returning vos pkt", __FUNCTION__);
1355 }
1356
1357 pAdapter->dev->last_rx = jiffies;
1358
1359 return status;
1360}
1361