• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ---------------------------------------------------------------------------
3  * FILE:     bh.c
4  *
5  * PURPOSE:
6  *      Provides an implementation for the driver bottom-half.
7  *      It is part of the porting exercise in Linux.
8  *
9  * Copyright (C) 2005-2009 by Cambridge Silicon Radio Ltd.
10  *
11  * Refer to LICENSE.txt included with this source code for details on
12  * the license terms.
13  *
14  * ---------------------------------------------------------------------------
15  */
16 #include "csr_wifi_hip_unifi.h"
17 #include "unifi_priv.h"
18 #include <linux/sched/rt.h>
19 
20 /*
21  * ---------------------------------------------------------------------------
22  *  uf_start_thread
23  *
24  *      Helper function to start a new thread.
25  *
26  *  Arguments:
27  *      priv            Pointer to OS driver structure for the device.
28  *      thread          Pointer to the thread object
29  *      func            The thread function
30  *
31  *  Returns:
32  *      0 on success or else a Linux error code.
33  * ---------------------------------------------------------------------------
34  */
uf_start_thread(unifi_priv_t * priv,struct uf_thread * thread,int (* func)(void *))35 int uf_start_thread(unifi_priv_t *priv,
36 		    struct uf_thread *thread, int (*func)(void *))
37 {
38 	if (thread->thread_task != NULL) {
39 		unifi_error(priv, "%s thread already started\n", thread->name);
40 		return 0;
41 	}
42 
43 	/* Start the kernel thread that handles all h/w accesses. */
44 	thread->thread_task = kthread_run(func, priv, "%s", thread->name);
45 	if (IS_ERR(thread->thread_task))
46 		return PTR_ERR(thread->thread_task);
47 
48 	/* Module parameter overides the thread priority */
49 	if (bh_priority != -1) {
50 		if (bh_priority >= 0 && bh_priority <= MAX_RT_PRIO) {
51 			struct sched_param param;
52 			priv->bh_thread.prio = bh_priority;
53 			unifi_trace(priv, UDBG1,
54 				"%s thread (RT) priority = %d\n",
55 				thread->name, bh_priority);
56 			param.sched_priority = bh_priority;
57 			sched_setscheduler(thread->thread_task,
58 					   SCHED_FIFO, &param);
59 		} else if (bh_priority > MAX_RT_PRIO &&
60 			   bh_priority <= MAX_PRIO) {
61 			priv->bh_thread.prio = bh_priority;
62 			unifi_trace(priv, UDBG1, "%s thread priority = %d\n",
63 					thread->name,
64 					PRIO_TO_NICE(bh_priority));
65 			set_user_nice(thread->thread_task,
66 				      PRIO_TO_NICE(bh_priority));
67 		} else {
68 			priv->bh_thread.prio = DEFAULT_PRIO;
69 			unifi_warning(priv,
70 				      "%s thread unsupported (%d) priority\n",
71 				      thread->name, bh_priority);
72 		}
73 	} else
74 		priv->bh_thread.prio = DEFAULT_PRIO;
75 	unifi_trace(priv, UDBG2, "Started %s thread\n", thread->name);
76 
77 	return 0;
78 } /* uf_start_thread() */
79 
80 
81 /*
82  * ---------------------------------------------------------------------------
83  *  uf_stop_thread
84  *
85  *      Helper function to stop a thread.
86  *
87  *  Arguments:
88  *      priv            Pointer to OS driver structure for the device.
89  *      thread          Pointer to the thread object
90  *
91  *  Returns:
92  *
93  * ---------------------------------------------------------------------------
94  */
uf_stop_thread(unifi_priv_t * priv,struct uf_thread * thread)95 void uf_stop_thread(unifi_priv_t *priv, struct uf_thread *thread)
96 {
97 	if (!thread->thread_task) {
98 		unifi_notice(priv, "%s thread is already stopped\n",
99 							thread->name);
100 		return;
101 	}
102 
103 	unifi_trace(priv, UDBG2, "Stopping %s thread\n", thread->name);
104 
105 	kthread_stop(thread->thread_task);
106 	thread->thread_task = NULL;
107 
108 } /* uf_stop_thread() */
109 
110 
111 
112 /*
113  * ---------------------------------------------------------------------------
114  *  uf_wait_for_thread_to_stop
115  *
116  *      Helper function to wait until a thread is stopped.
117  *
118  *  Arguments:
119  *      priv    Pointer to OS driver structure for the device.
120  *
121  *  Returns:
122  *
123  * ---------------------------------------------------------------------------
124  */
125 void
uf_wait_for_thread_to_stop(unifi_priv_t * priv,struct uf_thread * thread)126 uf_wait_for_thread_to_stop(unifi_priv_t *priv, struct uf_thread *thread)
127 {
128 	/*
129 	 * kthread_stop() cannot handle the thread exiting while
130 	 * kthread_should_stop() is false, so sleep until kthread_stop()
131 	 * wakes us up
132 	 */
133 	unifi_trace(priv, UDBG2, "%s waiting for the stop signal.\n",
134 							thread->name);
135 	set_current_state(TASK_INTERRUPTIBLE);
136 	if (!kthread_should_stop()) {
137 		unifi_trace(priv, UDBG2, "%s schedule....\n", thread->name);
138 		schedule();
139 	}
140 
141 	thread->thread_task = NULL;
142 	unifi_trace(priv, UDBG2, "%s exiting....\n", thread->name);
143 } /* uf_wait_for_thread_to_stop() */
144 
145 
146 /*
147  * ---------------------------------------------------------------------------
148  *  handle_bh_error
149  *
150  *      This function reports an error returned from the HIP core bottom-half.
151  *      Normally, implemented during the porting exercise, passing the error
152  *      to the SME using unifi_sys_wifi_off_ind().
153  *      The SME will try to reset the device and go through
154  *      the initialisation of the UniFi.
155  *
156  *  Arguments:
157  *      priv            Pointer to OS driver structure for the device.
158  *
159  *  Returns:
160  *      None.
161  * ---------------------------------------------------------------------------
162  */
163 static void
handle_bh_error(unifi_priv_t * priv)164 handle_bh_error(unifi_priv_t *priv)
165 {
166 	netInterface_priv_t *interfacePriv;
167 	u8 conf_param = CONFIG_IND_ERROR;
168 	u8 interfaceTag;
169 
170 
171 	/* Block unifi_run_bh() until the error has been handled. */
172 	priv->bh_thread.block_thread = 1;
173 
174 	/* Consider UniFi to be uninitialised */
175 	priv->init_progress = UNIFI_INIT_NONE;
176 
177 	/* Stop the network traffic */
178 	for (interfaceTag = 0;
179 	     interfaceTag < CSR_WIFI_NUM_INTERFACES; interfaceTag++) {
180 		interfacePriv = priv->interfacePriv[interfaceTag];
181 		if (interfacePriv->netdev_registered)
182 			netif_carrier_off(priv->netdev[interfaceTag]);
183 	}
184 
185 #ifdef CSR_NATIVE_LINUX
186 	/* Force any client waiting on an mlme_wait_for_reply() to abort. */
187 	uf_abort_mlme(priv);
188 
189 	/* Cancel any pending workqueue tasks */
190 	flush_workqueue(priv->unifi_workqueue);
191 
192 #endif /* CSR_NATIVE_LINUX */
193 
194 	unifi_error(priv,
195 		"handle_bh_error: fatal error is reported to the SME.\n");
196 	/* Notify the clients (SME or unifi_manager) for the error. */
197 	ul_log_config_ind(priv, &conf_param, sizeof(u8));
198 
199 } /* handle_bh_error() */
200 
201 
202 
203 /*
204  * ---------------------------------------------------------------------------
205  *  bh_thread_function
206  *
207  *      All hardware access happens in this thread.
208  *      This means there is no need for locks on the hardware and we don't need
209  *      to worry about reentrancy with the SDIO library.
210  *      Provides and example implementation on how to call unifi_bh(), which
211  *      is part of the HIP core API.
212  *
213  *      It processes the events generated by unifi_run_bh() to serialise calls
214  *      to unifi_bh(). It also demonstrates how the timeout parameter passed in
215  *      and returned from unifi_bh() needs to be handled.
216  *
217  *  Arguments:
218  *      arg             Pointer to OS driver structure for the device.
219  *
220  *  Returns:
221  *      None.
222  *
223  *  Notes:
224  *      When the bottom half of the driver needs to process signals, events,
225  *      or simply the host status (i.e sleep mode), it invokes unifi_run_bh().
226  *      Since we need all SDIO transaction to be in a single thread, the
227  *      unifi_run_bh() will wake up this thread to process it.
228  *
229  * ---------------------------------------------------------------------------
230  */
bh_thread_function(void * arg)231 static int bh_thread_function(void *arg)
232 {
233 	unifi_priv_t *priv = (unifi_priv_t *)arg;
234 	CsrResult csrResult;
235 	long ret;
236 	u32 timeout, t;
237 	struct uf_thread *this_thread;
238 
239 	unifi_trace(priv, UDBG2, "bh_thread_function starting\n");
240 
241 	this_thread = &priv->bh_thread;
242 
243 	t = timeout = 0;
244     while (!kthread_should_stop()) {
245         /* wait until an error occurs, or we need to process something. */
246         unifi_trace(priv, UDBG3, "bh_thread goes to sleep.\n");
247 
248         if (timeout > 0) {
249             /* Convert t in ms to jiffies */
250             t = msecs_to_jiffies(timeout);
251             ret = wait_event_interruptible_timeout(this_thread->wakeup_q,
252                     (this_thread->wakeup_flag && !this_thread->block_thread) ||
253                     kthread_should_stop(),
254                     t);
255             timeout = (ret > 0) ? jiffies_to_msecs(ret) : 0;
256         } else {
257             ret = wait_event_interruptible(this_thread->wakeup_q,
258                     (this_thread->wakeup_flag && !this_thread->block_thread) ||
259                     kthread_should_stop());
260         }
261 
262         if (kthread_should_stop()) {
263             unifi_trace(priv, UDBG2, "bh_thread: signalled to exit\n");
264             break;
265         }
266 
267         if (ret < 0) {
268             unifi_notice(priv,
269                     "bh_thread: wait_event returned %d, thread will exit\n",
270                     ret);
271             uf_wait_for_thread_to_stop(priv, this_thread);
272             break;
273         }
274 
275         this_thread->wakeup_flag = 0;
276 
277         unifi_trace(priv, UDBG3, "bh_thread calls unifi_bh().\n");
278 
279         CsrSdioClaim(priv->sdio);
280         csrResult = unifi_bh(priv->card, &timeout);
281         if(csrResult != CSR_RESULT_SUCCESS) {
282             if (csrResult == CSR_WIFI_HIP_RESULT_NO_DEVICE) {
283                 CsrSdioRelease(priv->sdio);
284                 uf_wait_for_thread_to_stop(priv, this_thread);
285                 break;
286             }
287             /* Errors must be delivered to the error task */
288             handle_bh_error(priv);
289         }
290         CsrSdioRelease(priv->sdio);
291     }
292 
293     /*
294      * I would normally try to call csr_sdio_remove_irq() here to make sure
295      * that we do not get any interrupts while this thread is not running.
296      * However, the MMC/SDIO driver tries to kill its' interrupt thread.
297      * The kernel threads implementation does not allow to kill threads
298      * from a signalled to stop thread.
299      * So, instead call csr_sdio_linux_remove_irq() always after calling
300      * uf_stop_thread() to kill this thread.
301      */
302 
303     unifi_trace(priv, UDBG2, "bh_thread exiting....\n");
304     return 0;
305 } /* bh_thread_function() */
306 
307 
308 /*
309  * ---------------------------------------------------------------------------
310  *  uf_init_bh
311  *
312  *      Helper function to start the bottom half of the driver.
313  *      All we need to do here is start the I/O bh thread.
314  *
315  *  Arguments:
316  *      priv            Pointer to OS driver structure for the device.
317  *
318  *  Returns:
319  *      0 on success or else a Linux error code.
320  * ---------------------------------------------------------------------------
321  */
322     int
uf_init_bh(unifi_priv_t * priv)323 uf_init_bh(unifi_priv_t *priv)
324 {
325     int r;
326 
327     /* Enable mlme interface. */
328     priv->io_aborted = 0;
329 
330 
331     /* Start the BH thread */
332     r = uf_start_thread(priv, &priv->bh_thread, bh_thread_function);
333     if (r) {
334         unifi_error(priv,
335                 "uf_init_bh: failed to start the BH thread.\n");
336         return r;
337     }
338 
339     /* Allow interrupts */
340     r = csr_sdio_linux_install_irq(priv->sdio);
341     if (r) {
342         unifi_error(priv,
343                 "uf_init_bh: failed to install the IRQ.\n");
344 
345         uf_stop_thread(priv, &priv->bh_thread);
346     }
347 
348     return r;
349 } /* uf_init_bh() */
350 
351 
352 /*
353  * ---------------------------------------------------------------------------
354  *  unifi_run_bh
355  *
356  *      Part of the HIP core lib API, implemented in the porting exercise.
357  *      The bottom half of the driver calls this function when
358  *      it wants to process anything that requires access to unifi.
359  *      We need to call unifi_bh() which in this implementation is done
360  *      by waking up the I/O thread.
361  *
362  *  Arguments:
363  *      ospriv          Pointer to OS driver structure for the device.
364  *
365  *  Returns:
366  *      0 on success or else a Linux error code.
367  *
368  *  Notes:
369  * ---------------------------------------------------------------------------
370  */
unifi_run_bh(void * ospriv)371 CsrResult unifi_run_bh(void *ospriv)
372 {
373     unifi_priv_t *priv = ospriv;
374 
375     /*
376      * If an error has occurred, we discard silently all messages from the bh
377      * until the error has been processed and the unifi has been reinitialised.
378      */
379     if (priv->bh_thread.block_thread == 1) {
380         unifi_trace(priv, UDBG3, "unifi_run_bh: discard message.\n");
381         /*
382          * Do not try to acknowledge a pending interrupt here.
383          * This function is called by unifi_send_signal() which in turn can be
384          * running in an atomic or 'disabled irq' level if a signal is sent
385          * from a workqueue task (i.e multicass addresses set).
386          * We can not hold the SDIO lock because it might sleep.
387          */
388         return CSR_RESULT_FAILURE;
389     }
390 
391     priv->bh_thread.wakeup_flag = 1;
392     /* wake up I/O thread */
393     wake_up_interruptible(&priv->bh_thread.wakeup_q);
394 
395     return CSR_RESULT_SUCCESS;
396 } /* unifi_run_bh() */
397 
398