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