1 #include <wl_android.h>
2 #ifdef WL_TIMER
3
4 #define TIMER_ERROR(name, arg1, args...) \
5 do { \
6 if (android_msg_level & ANDROID_ERROR_LEVEL) { \
7 printf("[%s] TIMER-ERROR) %s : " arg1, name, __func__, ## args); \
8 } \
9 } while (0)
10 #define TIMER_TRACE(name, arg1, args...) \
11 do { \
12 if (android_msg_level & ANDROID_TRACE_LEVEL) { \
13 printf("[%s] TIMER-TRACE) %s : " arg1, name, __func__, ## args); \
14 } \
15 } while (0)
16
17 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
18 4 && __GNUC_MINOR__ >= 6))
19 #define BCM_SET_LIST_FIRST_ENTRY(entry, ptr, type, member) \
20 _Pragma("GCC diagnostic push") \
21 _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") \
22 (entry) = list_first_entry((ptr), type, member); \
23 _Pragma("GCC diagnostic pop") \
24
25 #define BCM_SET_CONTAINER_OF(entry, ptr, type, member) \
26 _Pragma("GCC diagnostic push") \
27 _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") \
28 entry = container_of((ptr), type, member); \
29 _Pragma("GCC diagnostic pop") \
30
31 #else
32 #define BCM_SET_LIST_FIRST_ENTRY(entry, ptr, type, member) \
33 (entry) = list_first_entry((ptr), type, member); \
34
35 #define BCM_SET_CONTAINER_OF(entry, ptr, type, member) \
36 entry = container_of((ptr), type, member); \
37
38 #endif /* STRICT_GCC_WARNINGS */
39
40 typedef void(*FUNC_HANDLER) (void *cb_argu);
41
42 struct wl_func_q {
43 struct list_head eq_list;
44 FUNC_HANDLER cb_func;
45 void *cb_argu;
46 };
47
48 typedef void(*TIMER_HANDLER) (void *cb_argu);
49
50 typedef struct timer_handler_list {
51 struct list_head list;
52 struct net_device *net;
53 timer_list_compat_t *timer;
54 TIMER_HANDLER cb_func;
55 void *cb_argu;
56 uint tmo_ms;
57 ulong tmo_jiffies;
58 } timer_handler_list_t;
59
60 typedef struct wl_timer_params {
61 dhd_pub_t *pub;
62 struct list_head timer_list;
63 struct list_head eq_list;
64 timer_list_compat_t timer;
65 spinlock_t eq_lock;
66 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
67 struct workqueue_struct *timer_workq;
68 struct work_struct timer_work;
69 struct workqueue_struct *func_workq;
70 struct work_struct func_work;
71 #else
72 tsk_ctl_t thr_timer_ctl;
73 tsk_ctl_t thr_func_ctl;
74 #endif
75 } wl_timer_params_t;
76
77 static unsigned long
wl_func_lock_eq(struct wl_timer_params * timer_params)78 wl_func_lock_eq(struct wl_timer_params *timer_params)
79 {
80 unsigned long flags;
81
82 spin_lock_irqsave(&timer_params->eq_lock, flags);
83 return flags;
84 }
85
86 static void
wl_func_unlock_eq(struct wl_timer_params * timer_params,unsigned long flags)87 wl_func_unlock_eq(struct wl_timer_params *timer_params, unsigned long flags)
88 {
89 spin_unlock_irqrestore(&timer_params->eq_lock, flags);
90 }
91
92 static void
wl_func_init_eq_lock(struct wl_timer_params * timer_params)93 wl_func_init_eq_lock(struct wl_timer_params *timer_params)
94 {
95 spin_lock_init(&timer_params->eq_lock);
96 }
97
98 static void
wl_func_init_eq(struct wl_timer_params * timer_params)99 wl_func_init_eq(struct wl_timer_params *timer_params)
100 {
101 wl_func_init_eq_lock(timer_params);
102 INIT_LIST_HEAD(&timer_params->eq_list);
103 }
104
105 static void
wl_func_flush_eq(struct wl_timer_params * timer_params)106 wl_func_flush_eq(struct wl_timer_params *timer_params)
107 {
108 struct wl_func_q *e;
109 unsigned long flags;
110
111 flags = wl_func_lock_eq(timer_params);
112 while (!list_empty_careful(&timer_params->eq_list)) {
113 BCM_SET_LIST_FIRST_ENTRY(e, &timer_params->eq_list, struct wl_func_q, eq_list);
114 list_del(&e->eq_list);
115 kfree(e);
116 }
117 wl_func_unlock_eq(timer_params, flags);
118 }
119
120 static struct wl_func_q *
wl_func_deq(struct wl_timer_params * timer_params)121 wl_func_deq(struct wl_timer_params *timer_params)
122 {
123 struct wl_func_q *e = NULL;
124 unsigned long flags;
125
126 flags = wl_func_lock_eq(timer_params);
127 if (likely(!list_empty(&timer_params->eq_list))) {
128 BCM_SET_LIST_FIRST_ENTRY(e, &timer_params->eq_list, struct wl_func_q, eq_list);
129 list_del(&e->eq_list);
130 }
131 wl_func_unlock_eq(timer_params, flags);
132
133 return e;
134 }
135
136 static s32
wl_func_enq(struct wl_timer_params * timer_params,void * cb_func,void * cb_argu)137 wl_func_enq(struct wl_timer_params *timer_params,
138 void *cb_func, void *cb_argu)
139 {
140 struct wl_func_q *e;
141 s32 err = 0;
142 uint32 funcq_size;
143 unsigned long flags;
144 gfp_t aflags;
145
146 funcq_size = sizeof(struct wl_func_q);
147 aflags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
148 e = kzalloc(funcq_size, aflags);
149 if (unlikely(!e)) {
150 TIMER_ERROR("wlan", "funcq_size alloc failed %d\n", funcq_size);
151 return -ENOMEM;
152 }
153 e->cb_func = cb_func;
154 e->cb_argu = cb_argu;
155 flags = wl_func_lock_eq(timer_params);
156 list_add_tail(&e->eq_list, &timer_params->eq_list);
157 wl_func_unlock_eq(timer_params, flags);
158
159 return err;
160 }
161
162 static void
wl_func_put(struct wl_func_q * e)163 wl_func_put(struct wl_func_q *e)
164 {
165 kfree(e);
166 }
167
168 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
169 static void wl_func_handler(struct work_struct *data);
170 #define WL_FUNC_HANDLER() static void wl_func_handler(struct work_struct *data)
171 #else
172 static int wl_func_handler(void *data);
173 #define WL_FUNC_HANDLER() static int wl_func_handler(void *data)
174 #endif
175
WL_FUNC_HANDLER()176 WL_FUNC_HANDLER()
177 {
178 struct wl_timer_params *timer_params = NULL;
179 struct wl_func_q *e;
180 struct net_device *net = NULL;
181 dhd_pub_t *dhd;
182 unsigned long flags = 0;
183 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0))
184 tsk_ctl_t *tsk = (tsk_ctl_t *)data;
185 timer_params = (struct wl_timer_params *)tsk->parent;
186 #else
187 BCM_SET_CONTAINER_OF(timer_params, data, struct wl_timer_params, func_work);
188 #endif
189
190 dhd = timer_params->pub;
191 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0))
192 while (1) {
193 if (down_interruptible(&tsk->sema) == 0) {
194 SMP_RD_BARRIER_DEPENDS();
195 if (tsk->terminated) {
196 break;
197 }
198 #endif
199 DHD_EVENT_WAKE_LOCK(dhd);
200 while ((e = wl_func_deq(timer_params))) {
201 DHD_GENERAL_LOCK(dhd, flags);
202 if (DHD_BUS_CHECK_DOWN_OR_DOWN_IN_PROGRESS(dhd)) {
203 TIMER_ERROR(net->name, "BUS is DOWN.\n");
204 DHD_GENERAL_UNLOCK(dhd, flags);
205 goto fail;
206 }
207 DHD_GENERAL_UNLOCK(dhd, flags);
208 e->cb_func(e->cb_argu);
209 fail:
210 wl_func_put(e);
211 }
212 DHD_EVENT_WAKE_UNLOCK(dhd);
213 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0))
214 } else {
215 break;
216 }
217 }
218 complete_and_exit(&tsk->completed, 0);
219 #endif
220 }
221
222 void
wl_func_send(void * params,void * cb_func,void * cb_argu)223 wl_func_send(void *params, void *cb_func, void *cb_argu)
224 {
225 struct wl_timer_params *timer_params = params;
226
227 if (timer_params == NULL) {
228 TIMER_ERROR("wlan", "Stale ignored\n");
229 return;
230 }
231
232 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
233 if (timer_params->func_workq == NULL) {
234 TIMER_ERROR("wlan", "Event handler is not created\n");
235 return;
236 }
237 #endif
238
239 if (likely(!wl_func_enq(timer_params, cb_func, cb_argu))) {
240 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
241 queue_work(timer_params->func_workq, &timer_params->func_work);
242 #else
243 if (timer_params->thr_func_ctl.thr_pid >= 0) {
244 up(&timer_params->thr_func_ctl.sema);
245 }
246 #endif
247 }
248 }
249
250 static s32
wl_func_create_handler(struct wl_timer_params * timer_params)251 wl_func_create_handler(struct wl_timer_params *timer_params)
252 {
253 int ret = 0;
254 TIMER_TRACE("wlan", "Enter\n");
255
256 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
257 if (!timer_params->func_workq) {
258 timer_params->func_workq = alloc_workqueue("timer_funcd",
259 WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_UNBOUND, 0);
260 }
261 if (!timer_params->func_workq) {
262 TIMER_ERROR("wlan", "func_workq alloc_workqueue failed\n");
263 ret = -ENOMEM;
264 } else {
265 INIT_WORK(&timer_params->func_work, wl_func_handler);
266 }
267 #else
268 PROC_START(wl_func_handler, timer_params, &timer_params->thr_func_ctl, 0, "timer_funcd");
269 if (timer_params->thr_func_ctl.thr_pid < 0) {
270 ret = -ENOMEM;
271 }
272 #endif
273
274 return ret;
275 }
276
277 static void
wl_func_destroy_handler(struct wl_timer_params * timer_params)278 wl_func_destroy_handler(struct wl_timer_params *timer_params)
279 {
280 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
281 if (timer_params && timer_params->func_workq) {
282 cancel_work_sync(&timer_params->func_work);
283 destroy_workqueue(timer_params->func_workq);
284 timer_params->func_workq = NULL;
285 }
286 #else
287 if (timer_params->thr_func_ctl.thr_pid >= 0) {
288 PROC_STOP(&timer_params->thr_func_ctl);
289 }
290 #endif
291 }
292
293 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
294 static void wl_timer_handler(struct work_struct *data);
295 #define WL_TIMER_HANDLER() static void wl_timer_handler(struct work_struct *data)
296 #else
297 static int wl_timer_handler(void *data);
298 #define WL_TIMER_HANDLER() static int wl_timer_handler(void *data)
299 #endif
300
WL_TIMER_HANDLER()301 WL_TIMER_HANDLER()
302 {
303 struct wl_timer_params *timer_params = NULL;
304 struct timer_handler_list *node, *next;
305 dhd_pub_t *dhd;
306 unsigned long flags = 0;
307 unsigned long cur_jiffies, diff_jiffies, min_jiffies = 0;
308 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0))
309 tsk_ctl_t *tsk = (tsk_ctl_t *)data;
310 timer_params = (struct wl_timer_params *)tsk->parent;
311 #else
312 BCM_SET_CONTAINER_OF(timer_params, data, struct wl_timer_params, timer_work);
313 #endif
314
315 dhd = timer_params->pub;
316 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0))
317 while (1) {
318 if (down_interruptible(&tsk->sema) == 0) {
319 SMP_RD_BARRIER_DEPENDS();
320 if (tsk->terminated) {
321 break;
322 }
323 #endif
324 DHD_EVENT_WAKE_LOCK(dhd);
325 DHD_GENERAL_LOCK(dhd, flags);
326 if (DHD_BUS_CHECK_DOWN_OR_DOWN_IN_PROGRESS(dhd)) {
327 TIMER_ERROR("wlan", "BUS is DOWN.\n");
328 DHD_GENERAL_UNLOCK(dhd, flags);
329 goto exit;
330 }
331 DHD_GENERAL_UNLOCK(dhd, flags);
332 cur_jiffies = jiffies;
333 list_for_each_entry_safe(node, next, &timer_params->timer_list, list) {
334 if (node->tmo_ms) {
335 if (time_after(cur_jiffies, node->tmo_jiffies)) {
336 wl_func_send(timer_params, node->cb_func, node->cb_argu);
337 node->tmo_ms = 0;
338 } else {
339 diff_jiffies = node->tmo_jiffies - cur_jiffies;
340 if (min_jiffies == 0)
341 min_jiffies = diff_jiffies;
342 else if (diff_jiffies < min_jiffies)
343 min_jiffies = diff_jiffies;
344 }
345 }
346 }
347 if (min_jiffies)
348 mod_timer(&timer_params->timer, jiffies + min_jiffies);
349 exit:
350 DHD_EVENT_WAKE_UNLOCK(dhd);
351 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0))
352 } else {
353 break;
354 }
355 }
356 complete_and_exit(&tsk->completed, 0);
357 #endif
358 }
359
360 void
wl_timer_kick_handler(wl_timer_params_t * timer_params)361 wl_timer_kick_handler(wl_timer_params_t *timer_params)
362 {
363 if (timer_params == NULL) {
364 TIMER_ERROR("wlan", "timer_params not ready\n");
365 return;
366 }
367
368 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
369 if (timer_params->timer_workq == NULL) {
370 TIMER_ERROR("wlan", "timer handler is not created\n");
371 return;
372 }
373 queue_work(timer_params->timer_workq, &timer_params->timer_work);
374 #else
375 if (timer_params->thr_timer_ctl.thr_pid >= 0) {
376 up(&timer_params->thr_timer_ctl.sema);
377 }
378 #endif
379 }
380
381 static void
wl_timer_timeout(unsigned long data)382 wl_timer_timeout(unsigned long data)
383 {
384 struct wl_timer_params *timer_params = (struct wl_timer_params *)data;
385
386 if (!timer_params) {
387 TIMER_ERROR("wlan", "timer_params is not ready\n");
388 return;
389 }
390
391 TIMER_TRACE("wlan", "timer expired\n");
392 wl_timer_kick_handler(timer_params);
393 }
394
395 static s32
wl_timer_create_handler(struct wl_timer_params * timer_params)396 wl_timer_create_handler(struct wl_timer_params *timer_params)
397 {
398 int ret = 0;
399
400 TIMER_TRACE("wlan", "Enter\n");
401
402 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
403 if (!timer_params->timer_workq) {
404 timer_params->timer_workq = alloc_workqueue("timerd",
405 WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_UNBOUND, 0);
406 }
407 if (!timer_params->timer_workq) {
408 TIMER_ERROR("wlan", "timer_workq alloc_workqueue failed\n");
409 ret = -ENOMEM;
410 } else {
411 INIT_WORK(&timer_params->timer_work, wl_timer_handler);
412 }
413 #else
414 PROC_START(wl_timer_handler, timer_params, &timer_params->thr_timer_ctl, 0, "timerd");
415 if (timer_params->thr_timer_ctl.thr_pid < 0) {
416 ret = -ENOMEM;
417 }
418 #endif
419
420 return ret;
421 }
422
423 static void
wl_timer_destroy_handler(struct wl_timer_params * timer_params)424 wl_timer_destroy_handler(struct wl_timer_params *timer_params)
425 {
426 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
427 if (timer_params && timer_params->timer_workq) {
428 cancel_work_sync(&timer_params->timer_work);
429 destroy_workqueue(timer_params->timer_workq);
430 timer_params->timer_workq = NULL;
431 }
432 #else
433 if (timer_params->thr_timer_ctl.thr_pid >= 0) {
434 PROC_STOP(&timer_params->thr_timer_ctl);
435 }
436 #endif
437 }
438
439 static void
wl_timer_free(struct wl_timer_params * timer_params)440 wl_timer_free(struct wl_timer_params *timer_params)
441 {
442 timer_handler_list_t *node, *next;
443
444 list_for_each_entry_safe(node, next, &timer_params->timer_list, list) {
445 TIMER_TRACE(node->net->name, "Free timer\n");
446 list_del(&node->list);
447 kfree(node);
448 }
449 }
450
451 void
wl_timer_mod(dhd_pub_t * dhd,timer_list_compat_t * timer,uint32 tmo_ms)452 wl_timer_mod(dhd_pub_t *dhd, timer_list_compat_t *timer, uint32 tmo_ms)
453 {
454 wl_timer_params_t *timer_params = dhd->timer_params;
455 timer_handler_list_t *node, *next;
456 bool found = FALSE;
457
458 list_for_each_entry_safe(node, next, &timer_params->timer_list, list) {
459 if (node->timer == timer) {
460 node->tmo_ms = tmo_ms;
461 if (tmo_ms) {
462 TIMER_TRACE(node->net->name, "update timer %dms\n", tmo_ms);
463 node->tmo_jiffies = jiffies + msecs_to_jiffies(tmo_ms);
464 wl_timer_kick_handler(timer_params);
465 }
466 found = TRUE;
467 break;
468 }
469 }
470 if (!found)
471 TIMER_ERROR("wlan", "timer not found\n");
472 }
473
474 void
wl_timer_register(struct net_device * net,timer_list_compat_t * timer,void * cb_func)475 wl_timer_register(struct net_device *net, timer_list_compat_t *timer, void *cb_func)
476 {
477 dhd_pub_t *dhd = dhd_get_pub(net);
478 wl_timer_params_t *timer_params = dhd->timer_params;
479 timer_handler_list_t *node, *next, *leaf;
480
481 list_for_each_entry_safe(node, next, &timer_params->timer_list, list) {
482 if (node->timer == timer) {
483 TIMER_TRACE(net->name, "timer already registered\n");
484 return;
485 }
486 }
487
488 leaf = kmalloc(sizeof(timer_handler_list_t), GFP_KERNEL);
489 if (!leaf) {
490 TIMER_ERROR(net->name, "Memory alloc failure %d\n",
491 (int)sizeof(timer_handler_list_t));
492 return;
493 }
494 memset(leaf, 0, sizeof(timer_handler_list_t));
495 leaf->net = net;
496 leaf->timer = timer;
497 leaf->cb_func = cb_func;
498 leaf->cb_argu = net;
499 TIMER_ERROR(net->name, "timer registered tmo=%d\n", leaf->tmo_ms);
500 list_add_tail(&leaf->list, &timer_params->timer_list);
501
502 return;
503 }
504
505 void
wl_timer_deregister(struct net_device * net,timer_list_compat_t * timer)506 wl_timer_deregister(struct net_device *net, timer_list_compat_t *timer)
507 {
508 dhd_pub_t *dhd = dhd_get_pub(net);
509 wl_timer_params_t *timer_params = dhd->timer_params;
510 timer_handler_list_t *node, *next;
511
512 list_for_each_entry_safe(node, next, &timer_params->timer_list, list) {
513 if (node->timer == timer) {
514 TIMER_TRACE(net->name, "timer deregistered\n");
515 list_del(&node->list);
516 kfree(node);
517 }
518 }
519 return;
520 }
521
522 static s32
wl_timer_init_priv(struct wl_timer_params * timer_params)523 wl_timer_init_priv(struct wl_timer_params *timer_params)
524 {
525 s32 err = 0;
526
527 INIT_LIST_HEAD(&timer_params->timer_list);
528 if (wl_timer_create_handler(timer_params))
529 return -ENOMEM;
530 wl_func_init_eq(timer_params);
531 if (wl_func_create_handler(timer_params))
532 return -ENOMEM;
533
534 return err;
535 }
536
537 static void
wl_timer_deinit_priv(struct wl_timer_params * timer_params)538 wl_timer_deinit_priv(struct wl_timer_params *timer_params)
539 {
540 wl_timer_free(timer_params);
541 wl_func_destroy_handler(timer_params);
542 wl_func_flush_eq(timer_params);
543 wl_timer_destroy_handler(timer_params);
544 }
545
546 void
wl_timer_dettach(dhd_pub_t * dhdp)547 wl_timer_dettach(dhd_pub_t *dhdp)
548 {
549 struct wl_timer_params *timer_params = dhdp->timer_params;
550
551 if (timer_params) {
552 if (timer_pending(&timer_params->timer))
553 del_timer_sync(&timer_params->timer);
554 wl_timer_deinit_priv(timer_params);
555 kfree(timer_params);
556 dhdp->timer_params = NULL;
557 }
558 }
559
560 s32
wl_timer_attach(struct net_device * net)561 wl_timer_attach(struct net_device *net)
562 {
563 struct dhd_pub *dhdp = dhd_get_pub(net);
564 struct wl_timer_params *timer_params = NULL;
565 s32 err = 0;
566
567 timer_params = kmalloc(sizeof(wl_timer_params_t), GFP_KERNEL);
568 if (!timer_params) {
569 TIMER_ERROR(net->name, "Failed to allocate memory (%zu)\n",
570 sizeof(wl_timer_params_t));
571 return -ENOMEM;
572 }
573 dhdp->timer_params = timer_params;
574 memset(timer_params, 0, sizeof(wl_timer_params_t));
575 timer_params->pub = dhdp;
576
577 err = wl_timer_init_priv(timer_params);
578 if (err) {
579 TIMER_ERROR(net->name, "Failed to wl_timer_init_priv (%d)\n", err);
580 goto exit;
581 }
582 init_timer_compat(&timer_params->timer, wl_timer_timeout, timer_params);
583
584 exit:
585 if (err)
586 wl_timer_dettach(dhdp);
587 return err;
588 }
589 #else
590 void
wl_timer_mod(dhd_pub_t * dhd,timer_list_compat_t * timer,uint32 tmo_ms)591 wl_timer_mod(dhd_pub_t *dhd, timer_list_compat_t *timer, uint32 tmo_ms)
592 {
593 if (timer_pending(timer))
594 del_timer_sync(timer);
595 if (tmo_ms)
596 mod_timer(timer, jiffies + msecs_to_jiffies(tmo_ms));
597 }
598
599 void
wl_timer_register(struct net_device * net,timer_list_compat_t * timer,void * cb_func)600 wl_timer_register(struct net_device *net, timer_list_compat_t *timer, void *cb_func)
601 {
602 init_timer_compat(timer, cb_func, net);
603 }
604
605 void
wl_timer_deregister(struct net_device * net,timer_list_compat_t * timer)606 wl_timer_deregister(struct net_device *net, timer_list_compat_t *timer)
607 {
608 if (timer_pending(timer))
609 del_timer_sync(timer);
610 }
611 #endif
612