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