blob: f22999c44916f35d2cb346cd57aa0e7365626535 [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 EDIT HISTORY FOR FILE
25
26
27 This section contains comments describing changes made to the module.
28 Notice that changes are listed in reverse chronological order.
29
30
31 $Header:$ $DateTime: $ $Author: $
32
33
34 when who what, where, why
35 -------- --- --------------------------------------------------------
36 03/29/11 tbh Created module.
37
38 ==========================================================================*/
39
40/*----------------------------------------------------------------------------
41 * Include Files
42 * -------------------------------------------------------------------------*/
43#include <wlan_hdd_dev_pwr.h>
44#ifdef ANI_BUS_TYPE_PLATFORM
45#include <linux/wcnss_wlan.h>
46#else
47#include <wcnss_wlan.h>
48#endif // ANI_BUS_TYP_PLATFORM
49
50/*----------------------------------------------------------------------------
51 * Preprocessor Definitions and Constants
52 * -------------------------------------------------------------------------*/
53
54/*----------------------------------------------------------------------------
55 * Type Declarations
56 * -------------------------------------------------------------------------*/
57
58
59/*-------------------------------------------------------------------------
60 * Global variables.
61 *-------------------------------------------------------------------------*/
62
63/*-------------------------------------------------------------------------
64 * Local variables.
65 *-------------------------------------------------------------------------*/
66/* Reference VoIP, 100msec delay make disconnect.
67 * So TX sleep must be less than 100msec
68 * Every 20msec TX frame will goes out.
69 * 10 frame means 2seconds TX operation */
70static const hdd_tmLevelAction_t thermalMigrationAction[WLAN_HDD_TM_LEVEL_MAX] =
71{
72 /* TM Level 0, Do nothing, just normal operaton */
73 {1, 0, 0, 0, 0xFFFFF},
74 /* Tm Level 1, disable TX AMPDU */
75 {0, 0, 0, 0, 0xFFFFF},
76 /* TM Level 2, disable AMDPU,
77 * TX sleep 100msec if TX frame count is larger than 16 during 300msec */
78 {0, 0, 100, 300, 16},
79 /* TM Level 3, disable AMDPU,
80 * TX sleep 500msec if TX frame count is larger than 11 during 500msec */
81 {0, 0, 500, 500, 11},
82 /* TM Level 4, MAX TM level, enter IMPS */
83 {0, 1, 1000, 500, 10}
84};
85
86
87/*----------------------------------------------------------------------------
88
89 @brief Function to suspend the wlan driver.
90
91 @param hdd_context_t pHddCtx
92 Global hdd context
93
94
95 @return None
96
97----------------------------------------------------------------------------*/
98static int wlan_suspend(hdd_context_t* pHddCtx)
99{
100 int rc = 0;
101
102 pVosSchedContext vosSchedContext = NULL;
103
104 /* Get the global VOSS context */
105 vosSchedContext = get_vos_sched_ctxt();
106
107 if(!vosSchedContext) {
108 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_FATAL,"%s: Global VOS_SCHED context is Null",__func__);
109 return 0;
110 }
111 if(!vos_is_apps_power_collapse_allowed(pHddCtx))
112 {
113 /* Fail this suspend */
114 VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "%s: Fail wlan suspend: not in IMPS/BMPS", __func__);
115 return -1;
116 }
117
118 /* Set the Station state as Suspended */
119 pHddCtx->isWlanSuspended = TRUE;
120
121 /*
122 Suspending MC Thread, Rx Thread and Tx Thread as the platform driver is going to Suspend.
123 */
124 VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: Suspending Mc, Rx and Tx Threads",__func__);
125
126 init_completion(&pHddCtx->tx_sus_event_var);
127
128 /* Indicate Tx Thread to Suspend */
129 set_bit(TX_SUSPEND_EVENT_MASK, &vosSchedContext->txEventFlag);
130
131 wake_up_interruptible(&vosSchedContext->txWaitQueue);
132
133 /* Wait for Suspend Confirmation from Tx Thread */
134 rc = wait_for_completion_interruptible_timeout(&pHddCtx->tx_sus_event_var, msecs_to_jiffies(200));
135
136 if(!rc)
137 {
138 VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, "%s: Not able to suspend TX thread timeout happened", __func__);
139 clear_bit(TX_SUSPEND_EVENT_MASK, &vosSchedContext->txEventFlag);
140
141 return -1;
142 }
143 /* Set the Tx Thread as Suspended */
144 pHddCtx->isTxThreadSuspended = TRUE;
145
146 init_completion(&pHddCtx->rx_sus_event_var);
147
148 /* Indicate Rx Thread to Suspend */
149 set_bit(RX_SUSPEND_EVENT_MASK, &vosSchedContext->rxEventFlag);
150
151 wake_up_interruptible(&vosSchedContext->rxWaitQueue);
152
153 /* Wait for Suspend Confirmation from Rx Thread */
154 rc = wait_for_completion_interruptible_timeout(&pHddCtx->rx_sus_event_var, msecs_to_jiffies(200));
155
156 if(!rc)
157 {
158 VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, "%s: Not able to suspend Rx thread timeout happened", __func__);
159
160 clear_bit(RX_SUSPEND_EVENT_MASK, &vosSchedContext->rxEventFlag);
161
162 /* Indicate Tx Thread to Resume */
163 complete(&vosSchedContext->ResumeTxEvent);
164
165 /* Set the Tx Thread as Resumed */
166 pHddCtx->isTxThreadSuspended = FALSE;
167
168 return -1;
169 }
170
171 /* Set the Rx Thread as Suspended */
172 pHddCtx->isRxThreadSuspended = TRUE;
173
174 init_completion(&pHddCtx->mc_sus_event_var);
175
176 /* Indicate MC Thread to Suspend */
177 set_bit(MC_SUSPEND_EVENT_MASK, &vosSchedContext->mcEventFlag);
178
179 wake_up_interruptible(&vosSchedContext->mcWaitQueue);
180
181 /* Wait for Suspend Confirmation from MC Thread */
182 rc = wait_for_completion_interruptible_timeout(&pHddCtx->mc_sus_event_var, msecs_to_jiffies(200));
183
184 if(!rc)
185 {
186 VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, "%s: Not able to suspend MC thread timeout happened", __func__);
187
188 clear_bit(MC_SUSPEND_EVENT_MASK, &vosSchedContext->mcEventFlag);
189
190 /* Indicate Rx Thread to Resume */
191 complete(&vosSchedContext->ResumeRxEvent);
192
193 /* Set the Rx Thread as Resumed */
194 pHddCtx->isRxThreadSuspended = FALSE;
195
196 /* Indicate Tx Thread to Resume */
197 complete(&vosSchedContext->ResumeTxEvent);
198
199 /* Set the Tx Thread as Resumed */
200 pHddCtx->isTxThreadSuspended = FALSE;
201
202 return -1;
203 }
204
205 /* Set the Mc Thread as Suspended */
206 pHddCtx->isMcThreadSuspended = TRUE;
207
208 /* Set the Station state as Suspended */
209 pHddCtx->isWlanSuspended = TRUE;
210
211 return 0;
212}
213
214/*----------------------------------------------------------------------------
215
216 @brief Function to resume the wlan driver.
217
218 @param hdd_context_t pHddCtx
219 Global hdd context
220
221
222 @return None
223
224----------------------------------------------------------------------------*/
225static void wlan_resume(hdd_context_t* pHddCtx)
226{
227 pVosSchedContext vosSchedContext = NULL;
228
229 //Get the global VOSS context.
230 vosSchedContext = get_vos_sched_ctxt();
231
232 if(!vosSchedContext) {
233 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_FATAL,"%s: Global VOS_SCHED context is Null",__func__);
234 return;
235 }
236
237 /*
238 Resuming Mc, Rx and Tx Thread as platform Driver is resuming.
239 */
240 VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: Resuming Mc, Rx and Tx Thread",__func__);
241
242 /* Indicate MC Thread to Resume */
243 complete(&vosSchedContext->ResumeMcEvent);
244
245 /* Set the Mc Thread as Resumed */
246 pHddCtx->isMcThreadSuspended = FALSE;
247
248 /* Indicate Rx Thread to Resume */
249 complete(&vosSchedContext->ResumeRxEvent);
250
251 /* Set the Rx Thread as Resumed */
252 pHddCtx->isRxThreadSuspended = FALSE;
253
254 /* Indicate Tx Thread to Resume */
255 complete(&vosSchedContext->ResumeTxEvent);
256
257 /* Set the Tx Thread as Resumed */
258 pHddCtx->isTxThreadSuspended = FALSE;
259
260 /* Set the Station state as Suspended */
261 pHddCtx->isWlanSuspended = FALSE;
262}
263
264/*----------------------------------------------------------------------------
265
266 @brief Function to suspend the wlan driver.
267 This function will get called by platform driver Suspend on System Suspend
268
269 @param dev platform_func_device
270
271
272 @return None
273
274----------------------------------------------------------------------------*/
275int hddDevSuspendHdlr(struct device *dev)
276{
277 int ret = 0;
278 hdd_context_t* pHddCtx = NULL;
279
280 pHddCtx = (hdd_context_t*)wcnss_wlan_get_drvdata(dev);
281
282 VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: WLAN suspended by platform driver",__func__);
283
284 /* Get the HDD context */
285 if(!pHddCtx) {
286 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_FATAL,"%s: HDD context is Null",__func__);
287 return 0;
288 }
289
290 if(pHddCtx->isWlanSuspended == TRUE)
291 {
292 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_FATAL,"%s: WLAN is already in suspended state",__func__);
293 return 0;
294 }
295
296 /* Suspend the wlan driver */
297 ret = wlan_suspend(pHddCtx);
298 if(ret != 0)
299 {
300 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_FATAL,"%s: Not able to suspend wlan",__func__);
301 return ret;
302 }
303
304 return 0;
305}
306
307/*----------------------------------------------------------------------------
308
309 @brief Function to resume the wlan driver.
310 This function will get called by platform driver Resume on System Resume
311
312 @param dev platform_func_device
313
314
315 @return None
316
317----------------------------------------------------------------------------*/
318int hddDevResumeHdlr(struct device *dev)
319{
320 hdd_context_t* pHddCtx = NULL;
321
322 pHddCtx = (hdd_context_t*)wcnss_wlan_get_drvdata(dev);
323
324 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_INFO, "%s: WLAN being resumed by Android OS",__func__);
325
326 if(pHddCtx->isWlanSuspended != TRUE)
327 {
328 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_FATAL,"%s: WLAN is already in resumed state",__func__);
329 return 0;
330 }
331
332 /* Resume the wlan driver */
333 wlan_resume(pHddCtx);
334
335 return 0;
336}
337
338static const struct dev_pm_ops pm_ops = {
339 .suspend = hddDevSuspendHdlr,
340 .resume = hddDevResumeHdlr,
341};
342
343/*----------------------------------------------------------------------------
344 *
345
346 @brief Registration function.
347 Register suspend, resume callback functions with platform driver.
348
349 @param hdd_context_t pHddCtx
350 Global hdd context
351
352 @return General status code
353 VOS_STATUS_SUCCESS Registration Success
354 VOS_STATUS_E_FAILURE Registration Fail
355
356----------------------------------------------------------------------------*/
357VOS_STATUS hddRegisterPmOps(hdd_context_t *pHddCtx)
358{
359 wcnss_wlan_set_drvdata(pHddCtx->parent_dev, pHddCtx);
360#ifndef FEATURE_R33D
361 wcnss_wlan_register_pm_ops(pHddCtx->parent_dev, &pm_ops);
362#endif /* FEATURE_R33D */
363 return VOS_STATUS_SUCCESS;
364}
365
366/*----------------------------------------------------------------------------
367
368 @brief De-registration function.
369 Deregister the suspend, resume callback functions with platform driver
370
371 @param hdd_context_t pHddCtx
372 Global hdd context
373
374 @return General status code
375 VOS_STATUS_SUCCESS De-Registration Success
376 VOS_STATUS_E_FAILURE De-Registration Fail
377
378----------------------------------------------------------------------------*/
379VOS_STATUS hddDeregisterPmOps(hdd_context_t *pHddCtx)
380{
381#ifndef FEATURE_R33D
382 wcnss_wlan_unregister_pm_ops(pHddCtx->parent_dev, &pm_ops);
383#endif /* FEATURE_R33D */
384 return VOS_STATUS_SUCCESS;
385}
386
387/*----------------------------------------------------------------------------
388
389 @brief TX frame block timeout handler
390 Resume TX, and reset TX frame count
391
392 @param hdd_context_t pHddCtx
393 Global hdd context
394
395 @return NONE
396
397----------------------------------------------------------------------------*/
398void hddDevTmTxBlockTimeoutHandler(void *usrData)
399{
400 hdd_context_t *pHddCtx = (hdd_context_t *)usrData;
401 hdd_adapter_t *staAdapater;
402 /* Sanity, This should not happen */
403 if(NULL == pHddCtx)
404 {
405 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_ERROR,
406 "%s: NULL Context", __func__);
407 VOS_ASSERT(0);
408 return;
409 }
410
411 staAdapater = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION);
412 if(mutex_lock_interruptible(&pHddCtx->tmInfo.tmOperationLock))
413 {
414 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_ERROR,
415 "%s: Aquire lock fail", __func__);
416 return;
417 }
418 pHddCtx->tmInfo.txFrameCount = 0;
419
420 /* Resume TX flow */
421 netif_tx_start_all_queues(staAdapater->dev);
422 mutex_unlock(&pHddCtx->tmInfo.tmOperationLock);
423
424 return;
425}
426
427/*----------------------------------------------------------------------------
428
429 @brief TM Level Change handler
430 Received Tm Level changed notification
431
432 @param dev : Device context
433 changedTmLevel : Changed new TM level
434
435 @return
436
437----------------------------------------------------------------------------*/
438void hddDevTmLevelChangedHandler(struct device *dev, int changedTmLevel)
439{
440 hdd_context_t *pHddCtx = NULL;
441 WLAN_TmLevelEnumType newTmLevel = changedTmLevel;
442 hdd_adapter_t *staAdapater;
443
444 pHddCtx = (hdd_context_t*)wcnss_wlan_get_drvdata(dev);
445
446 if((pHddCtx->tmInfo.currentTmLevel == newTmLevel) ||
447 (!pHddCtx->cfg_ini->thermalMitigationEnable))
448 {
449 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_WARN,
450 "%s: TM Not enabled %d or Level does not changed %d",
451 __func__, pHddCtx->cfg_ini->thermalMitigationEnable, newTmLevel);
452 /* TM Level does not changed,
453 * Or feature does not enabled
454 * do nothing */
455 return;
456 }
457
458 sme_SetTmLevel(pHddCtx->hHal, changedTmLevel, 0);
459
460 if(mutex_lock_interruptible(&pHddCtx->tmInfo.tmOperationLock))
461 {
462 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_ERROR,
463 "%s: Aquire lock fail", __func__);
464 return;
465 }
466
467 pHddCtx->tmInfo.currentTmLevel = changedTmLevel;
468 pHddCtx->tmInfo.txFrameCount = 0;
469 vos_mem_copy(&pHddCtx->tmInfo.tmAction,
470 &thermalMigrationAction[newTmLevel],
471 sizeof(hdd_tmLevelAction_t));
472
473
474 if(pHddCtx->tmInfo.tmAction.enterImps)
475 {
476 staAdapater = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION);
477 if(staAdapater)
478 {
479 if(hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(staAdapater)))
480 {
481 sme_RoamDisconnect(pHddCtx->hHal,
482 staAdapater->sessionId,
483 eCSR_DISCONNECT_REASON_UNSPECIFIED);
484 }
485 }
486 }
487
488 mutex_unlock(&pHddCtx->tmInfo.tmOperationLock);
489
490 return;
491}
492
493/*----------------------------------------------------------------------------
494
495 @brief Register function
496 Register Thermal Mitigation Level Changed handle callback function
497
498 @param hdd_context_t pHddCtx
499 Global hdd context
500
501 @return General status code
502 VOS_STATUS_SUCCESS Registration Success
503 VOS_STATUS_E_FAILURE Registration Fail
504
505----------------------------------------------------------------------------*/
506VOS_STATUS hddDevTmRegisterNotifyCallback(hdd_context_t *pHddCtx)
507{
508 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_INFO,
509 "%s: Register TM Handler", __func__);
510
511 wcnss_register_thermal_mitigation(pHddCtx->parent_dev ,hddDevTmLevelChangedHandler);
512
513 /* Set Default TM Level as Lowest, do nothing */
514 pHddCtx->tmInfo.currentTmLevel = WLAN_HDD_TM_LEVEL_0;
515 vos_mem_zero(&pHddCtx->tmInfo.tmAction, sizeof(hdd_tmLevelAction_t));
516 vos_timer_init(&pHddCtx->tmInfo.txSleepTimer,
517 VOS_TIMER_TYPE_SW,
518 hddDevTmTxBlockTimeoutHandler,
519 (void *)pHddCtx);
520 mutex_init(&pHddCtx->tmInfo.tmOperationLock);
521 pHddCtx->tmInfo.txFrameCount = 0;
522 pHddCtx->tmInfo.blockedQueue = NULL;
523
524 return VOS_STATUS_SUCCESS;
525}
526
527/*----------------------------------------------------------------------------
528
529 @brief Un-Register function
530 Un-Register Thermal Mitigation Level Changed handle callback function
531
532 @param hdd_context_t pHddCtx
533 Global hdd context
534
535 @return General status code
536 VOS_STATUS_SUCCESS Un-Registration Success
537 VOS_STATUS_E_FAILURE Un-Registration Fail
538
539----------------------------------------------------------------------------*/
540VOS_STATUS hddDevTmUnregisterNotifyCallback(hdd_context_t *pHddCtx)
541{
Jeff Johnson04dd8a82012-06-29 20:41:40 -0700542 VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
543
Jeff Johnson295189b2012-06-20 16:38:30 -0700544 wcnss_unregister_thermal_mitigation(hddDevTmLevelChangedHandler);
Jeff Johnson04dd8a82012-06-29 20:41:40 -0700545
546 if(VOS_TIMER_STATE_RUNNING ==
547 vos_timer_getCurrentState(&pHddCtx->tmInfo.txSleepTimer))
548 {
549 vosStatus = vos_timer_stop(&pHddCtx->tmInfo.txSleepTimer);
550 if(!VOS_IS_STATUS_SUCCESS(vosStatus))
551 {
552 VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
553 "%s: Timer stop fail", __func__);
554 }
555 }
556
557 // Destroy the vos timer...
558 vosStatus = vos_timer_destroy(&pHddCtx->tmInfo.txSleepTimer);
559 if (!VOS_IS_STATUS_SUCCESS(vosStatus))
560 {
561 VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_ERROR,
562 "%s: Fail to destroy timer", __func__);
563 }
564
Jeff Johnson295189b2012-06-20 16:38:30 -0700565 return VOS_STATUS_SUCCESS;
566}
567