Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.6-rc5 391 lines 13 kB view raw
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 u8 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 u32 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