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