1 /* 2 * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved. 3 * 4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc. 5 * 6 * 7 * Permission to use, copy, modify, and/or distribute this software for 8 * any purpose with or without fee is hereby granted, provided that the 9 * above copyright notice and this permission notice appear in all 10 * copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 19 * PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 /* 23 * This file was originally distributed by Qualcomm Atheros, Inc. 24 * under proprietary terms before Copyright ownership was assigned 25 * to the Linux Foundation. 26 */ 27 28 /** 29 * DOC: qdf_event.c 30 * 31 * This source file contains linux specific definitions for QDF event APIs 32 * The APIs mentioned in this file are used for initializing, setting, 33 * resetting, destroying an event and waiting on an occurance of an event 34 * among multiple events. 35 */ 36 37 /* Include Files */ 38 #include "qdf_event.h" 39 #include "qdf_mc_timer.h" 40 #include <qdf_module.h> 41 42 #ifdef CONFIG_MCL 43 static qdf_self_recovery_callback self_recovery_cb; 44 #endif 45 46 struct qdf_evt_node { 47 qdf_list_node_t node; 48 qdf_event_t *pevent; 49 }; 50 51 #define MAX_WAIT_EVENTS 10 52 53 static qdf_list_t qdf_wait_event_list; 54 static qdf_spinlock_t qdf_wait_event_lock; 55 56 /* Function Definitions and Documentation */ 57 58 /** 59 * qdf_event_create() - initializes a QDF event 60 * @event: Pointer to the opaque event object to initialize 61 * 62 * The qdf_event_create() function initializes the specified event. Upon 63 * successful initialization, the state of the event becomes initialized 64 * and not signalled. 65 * 66 * An event must be initialized before it may be used in any other event 67 * functions. 68 * Attempting to initialize an already initialized event results in 69 * a failure. 70 * 71 * Return: QDF status 72 */ 73 QDF_STATUS qdf_event_create(qdf_event_t *event) 74 { 75 /* check for null pointer */ 76 if (NULL == event) { 77 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 78 "NULL event passed into %s", __func__); 79 QDF_ASSERT(0); 80 return QDF_STATUS_E_FAULT; 81 } 82 83 /* check for 'already initialized' event */ 84 if (LINUX_EVENT_COOKIE == event->cookie) { 85 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 86 "Initialized event passed into %s", __func__); 87 QDF_ASSERT(0); 88 return QDF_STATUS_E_BUSY; 89 } 90 91 /* initialize new event */ 92 init_completion(&event->complete); 93 event->cookie = LINUX_EVENT_COOKIE; 94 95 return QDF_STATUS_SUCCESS; 96 } 97 qdf_export_symbol(qdf_event_create); 98 99 /** 100 * qdf_event_set() - sets a QDF event 101 * @event: The event to set to the signalled state 102 * 103 * The state of the specified event is set to signalled by calling 104 * qdf_event_set(). 105 * 106 * Any threads waiting on the event as a result of a qdf_event_wait() will 107 * be unblocked and available to be scheduled for execution when the event 108 * is signaled by a call to qdf_event_set(). 109 * 110 * Return: QDF status 111 */ 112 QDF_STATUS qdf_event_set(qdf_event_t *event) 113 { 114 /* check for null pointer */ 115 if (NULL == event) { 116 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 117 "NULL event passed into %s", __func__); 118 QDF_ASSERT(0); 119 return QDF_STATUS_E_FAULT; 120 } 121 122 /* check if event refers to an initialized object */ 123 if (LINUX_EVENT_COOKIE != event->cookie) { 124 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 125 "Uninitialized event passed into %s", __func__); 126 QDF_ASSERT(0); 127 return QDF_STATUS_E_INVAL; 128 } 129 130 complete(&event->complete); 131 132 return QDF_STATUS_SUCCESS; 133 } 134 qdf_export_symbol(qdf_event_set); 135 136 /** 137 * qdf_event_reset() - resets a QDF event 138 * @event: The event to set to the NOT signalled state 139 * 140 * This function isn't required for Linux. Therefore, it doesn't do much. 141 * 142 * The state of the specified event is set to 'NOT signalled' by calling 143 * qdf_event_reset(). The state of the event remains NOT signalled until an 144 * explicit call to qdf_event_set(). 145 * 146 * This function sets the event to a NOT signalled state even if the event was 147 * signalled multiple times before being signaled. 148 * 149 * Return: QDF status 150 */ 151 QDF_STATUS qdf_event_reset(qdf_event_t *event) 152 { 153 /* check for null pointer */ 154 if (NULL == event) { 155 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 156 "NULL event passed into %s", __func__); 157 QDF_ASSERT(0); 158 return QDF_STATUS_E_FAULT; 159 } 160 161 /* check to make sure it is an 'already initialized' event */ 162 if (LINUX_EVENT_COOKIE != event->cookie) { 163 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 164 "Uninitialized event passed into %s", __func__); 165 QDF_ASSERT(0); 166 return QDF_STATUS_E_INVAL; 167 } 168 169 /* (re)initialize event */ 170 INIT_COMPLETION(event->complete); 171 return QDF_STATUS_SUCCESS; 172 } 173 qdf_export_symbol(qdf_event_reset); 174 175 /** 176 * qdf_event_destroy() - Destroys a QDF event 177 * @event: The event object to be destroyed. 178 * 179 * This function doesn't do much in Linux. There is no need for the caller 180 * to explicitly destroy an event after use. 181 * 182 * The os_event_destroy() function shall destroy the event object 183 * referenced by event. After a successful return from qdf_event_destroy() 184 * the event object becomes, in effect, uninitialized. 185 * 186 * A destroyed event object can be reinitialized using qdf_event_create(); 187 * the results of otherwise referencing the object after it has been destroyed 188 * are undefined. Calls to QDF event functions to manipulate the lock such 189 * as qdf_event_set() will fail if the event is destroyed. Therefore, 190 * don't use the event after it has been destroyed until it has 191 * been re-initialized. 192 * 193 * Return: QDF status 194 */ 195 QDF_STATUS qdf_event_destroy(qdf_event_t *event) 196 { 197 /* check for null pointer */ 198 if (NULL == event) { 199 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 200 "NULL event passed into %s", __func__); 201 QDF_ASSERT(0); 202 return QDF_STATUS_E_FAULT; 203 } 204 205 /* check to make sure it is an 'already initialized' event */ 206 if (LINUX_EVENT_COOKIE != event->cookie) { 207 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 208 "Uninitialized event passed into %s", __func__); 209 QDF_ASSERT(0); 210 return QDF_STATUS_E_INVAL; 211 } 212 213 /* make sure nobody is waiting on the event */ 214 complete_all(&event->complete); 215 216 /* destroy the event */ 217 memset(event, 0, sizeof(qdf_event_t)); 218 219 return QDF_STATUS_SUCCESS; 220 } 221 qdf_export_symbol(qdf_event_destroy); 222 223 /** 224 * qdf_wait_single_event() - Waits for a single event to be set. 225 * This API waits for the event to be set. 226 * 227 * @event: Pointer to an event to wait on. 228 * @timeout: Timeout value (in milliseconds). This function returns 229 * if this interval elapses, regardless if any of the events have 230 * been set. An input value of 0 for this timeout parameter means 231 * to wait infinitely, meaning a timeout will never occur. 232 * 233 * Return: QDF status 234 */ 235 QDF_STATUS qdf_wait_single_event(qdf_event_t *event, uint32_t timeout) 236 { 237 if (in_interrupt()) { 238 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 239 "%s cannot be called from interrupt context!!!", 240 __func__); 241 QDF_ASSERT(0); 242 return QDF_STATUS_E_FAULT; 243 } 244 245 /* check for null pointer */ 246 if (NULL == event) { 247 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 248 "NULL event passed into %s", __func__); 249 QDF_ASSERT(0); 250 return QDF_STATUS_E_FAULT; 251 } 252 253 /* check if cookie is same as that of initialized event */ 254 if (LINUX_EVENT_COOKIE != event->cookie) { 255 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 256 "Uninitialized event passed into %s", __func__); 257 QDF_ASSERT(0); 258 return QDF_STATUS_E_INVAL; 259 } 260 261 /* update the timeout if its on a emaulation platform */ 262 timeout *= qdf_timer_get_multiplier(); 263 if (timeout) { 264 long ret; 265 266 ret = wait_for_completion_timeout(&event->complete, 267 msecs_to_jiffies(timeout)); 268 if (0 >= ret) 269 return QDF_STATUS_E_TIMEOUT; 270 } else { 271 wait_for_completion(&event->complete); 272 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO, 273 "Signaled for completion %s", __func__); 274 return QDF_STATUS_SUCCESS; 275 } 276 return QDF_STATUS_SUCCESS; 277 } 278 qdf_export_symbol(qdf_wait_single_event); 279 280 /** 281 * qdf_complete_wait_events() - Sets all the events which are in the list. 282 * 283 * This function traverses the list of events and sets all of them. It 284 * sets the flag force_set as TRUE to indicate that these events have 285 * been forcefully set. 286 * 287 * Return: None 288 */ 289 void qdf_complete_wait_events(void) 290 { 291 struct qdf_evt_node *event_node = NULL; 292 qdf_list_node_t *list_node = NULL; 293 QDF_STATUS status; 294 295 if (qdf_list_empty(&qdf_wait_event_list)) 296 return; 297 298 qdf_spin_lock(&qdf_wait_event_lock); 299 qdf_list_peek_front(&qdf_wait_event_list, 300 &list_node); 301 302 while (list_node != NULL) { 303 event_node = qdf_container_of(list_node, 304 struct qdf_evt_node, node); 305 306 event_node->pevent->force_set = true; 307 qdf_event_set(event_node->pevent); 308 309 status = qdf_list_peek_next(&qdf_wait_event_list, 310 &event_node->node, &list_node); 311 312 if (!QDF_IS_STATUS_SUCCESS(status)) 313 break; 314 } 315 qdf_spin_unlock(&qdf_wait_event_lock); 316 } 317 qdf_export_symbol(qdf_complete_wait_events); 318 319 /** 320 * qdf_wait_for_event_completion() - Waits for an event to be set. 321 * 322 * @event: Pointer to an event to wait on. 323 * @timeout: Timeout value (in milliseconds). 324 * 325 * This function adds the event in a list and waits on it until it 326 * is set or the timeout duration elapses. The purpose of waiting 327 * is considered complete only if the event is set and the flag 328 * force_set is FALSE, it returns success in this case. In other 329 * cases it returns appropriate error status. 330 * 331 * Return: QDF status 332 */ 333 QDF_STATUS qdf_wait_for_event_completion(qdf_event_t *event, uint32_t timeout) 334 { 335 struct qdf_evt_node *event_node; 336 QDF_STATUS status; 337 338 if (in_interrupt()) { 339 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 340 "%s cannot be called from interrupt context!!!", 341 __func__); 342 QDF_ASSERT(0); 343 return QDF_STATUS_E_FAULT; 344 } 345 346 /* check for null pointer */ 347 if (NULL == event) { 348 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 349 "NULL event passed into %s", __func__); 350 QDF_ASSERT(0); 351 return QDF_STATUS_E_FAULT; 352 } 353 354 /* check if cookie is same as that of initialized event */ 355 if (LINUX_EVENT_COOKIE != event->cookie) { 356 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 357 "Uninitialized event passed into %s", __func__); 358 QDF_ASSERT(0); 359 return QDF_STATUS_E_INVAL; 360 } 361 362 event_node = qdf_mem_malloc(sizeof(*event_node)); 363 if (NULL == event_node) { 364 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 365 "Could not allocate for event node in %s", __func__); 366 QDF_ASSERT(0); 367 return QDF_STATUS_E_NOMEM; 368 } 369 event_node->pevent = event; 370 371 qdf_spin_lock(&qdf_wait_event_lock); 372 status = qdf_list_insert_back(&qdf_wait_event_list, 373 &event_node->node); 374 qdf_spin_unlock(&qdf_wait_event_lock); 375 376 if (QDF_STATUS_SUCCESS != status) { 377 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 378 "Failed to add event in the list in %s", __func__), 379 status = QDF_STATUS_E_FAULT; 380 goto err_list_add; 381 } 382 383 /* update the timeout if its on a emaulation platform */ 384 timeout *= qdf_timer_get_multiplier(); 385 if (timeout) { 386 long ret; 387 388 ret = wait_for_completion_timeout(&event->complete, 389 msecs_to_jiffies(timeout)); 390 if (0 >= ret) 391 /* Timeout occurred */ 392 status = QDF_STATUS_E_TIMEOUT; 393 else { 394 if (event->force_set) 395 /* Event forcefully completed, return fail */ 396 status = QDF_STATUS_E_FAULT; 397 else 398 status = QDF_STATUS_SUCCESS; 399 } 400 } else { 401 wait_for_completion(&event->complete); 402 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO, 403 "Signaled for completion %s", __func__); 404 if (event->force_set) 405 /* Event forcefully completed, return fail */ 406 status = QDF_STATUS_E_FAULT; 407 else 408 status = QDF_STATUS_SUCCESS; 409 } 410 411 qdf_spin_lock(&qdf_wait_event_lock); 412 qdf_list_remove_node(&qdf_wait_event_list, 413 &event_node->node); 414 qdf_spin_unlock(&qdf_wait_event_lock); 415 err_list_add: 416 qdf_mem_free(event_node); 417 return status; 418 } 419 qdf_export_symbol(qdf_wait_for_event_completion); 420 421 /** 422 * qdf_event_list_init() - Creates a list and spinlock for events. 423 * 424 * This function creates a list for maintaining events on which threads 425 * wait for completion. A spinlock is also created to protect related 426 * oprations. 427 * 428 * Return: None 429 */ 430 void qdf_event_list_init(void) 431 { 432 qdf_list_create(&qdf_wait_event_list, MAX_WAIT_EVENTS); 433 qdf_spinlock_create(&qdf_wait_event_lock); 434 } 435 qdf_export_symbol(qdf_event_list_init); 436 437 /** 438 * qdf_event_list_destroy() - Destroys list and spinlock created for events. 439 * 440 * This function destroys the list and spinlock created for events on which 441 * threads wait for completion. 442 * 443 * Return: None 444 */ 445 void qdf_event_list_destroy(void) 446 { 447 qdf_list_destroy(&qdf_wait_event_list); 448 qdf_spinlock_destroy(&qdf_wait_event_lock); 449 } 450 qdf_export_symbol(qdf_event_list_destroy); 451 452 QDF_STATUS qdf_exit_thread(QDF_STATUS status) 453 { 454 if (status == QDF_STATUS_SUCCESS) 455 do_exit(0); 456 else 457 do_exit(SIGKILL); 458 459 return QDF_STATUS_SUCCESS; 460 } 461 qdf_export_symbol(qdf_exit_thread); 462 463 #ifdef CONFIG_MCL 464 void qdf_register_self_recovery_callback(qdf_self_recovery_callback callback) 465 { 466 self_recovery_cb = callback; 467 } 468 469 void qdf_trigger_self_recovery(void) 470 { 471 if (!self_recovery_cb) { 472 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO, 473 "No self recovery callback registered %s", __func__); 474 return; 475 } 476 self_recovery_cb(QDF_REASON_UNSPECIFIED); 477 } 478 #endif 479