Staging: add CSR wifi module
[linux-2.6-block.git] / drivers / staging / csr / bh.c
CommitLineData
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 */
35int
36uf_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, &param);
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
92uf_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
122uf_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
159handle_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 */
224static int
225bh_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
317uf_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 */
365CsrResult 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