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 /** 81 * qdf_event_set() - sets a QDF event 82 * @event: The event to set to the signalled state 83 * 84 * The state of the specified event is set to signalled by calling 85 * qdf_event_set(). 86 * 87 * Any threads waiting on the event as a result of a qdf_event_wait() will 88 * be unblocked and available to be scheduled for execution when the event 89 * is signaled by a call to qdf_event_set(). 90 * 91 * Return: QDF status 92 */ 93 QDF_STATUS qdf_event_set(qdf_event_t *event) 94 { 95 QDF_BUG(event); 96 if (!event) 97 return QDF_STATUS_E_FAULT; 98 99 /* ensure event is initialized */ 100 QDF_BUG(event->cookie == LINUX_EVENT_COOKIE); 101 if (event->cookie != LINUX_EVENT_COOKIE) 102 return QDF_STATUS_E_INVAL; 103 104 event->done = true; 105 complete(&event->complete); 106 107 return QDF_STATUS_SUCCESS; 108 } 109 qdf_export_symbol(qdf_event_set); 110 111 /** 112 * qdf_event_reset() - resets a QDF event 113 * @event: The event to set to the NOT signalled state 114 * 115 * This function isn't required for Linux. Therefore, it doesn't do much. 116 * 117 * The state of the specified event is set to 'NOT signalled' by calling 118 * qdf_event_reset(). The state of the event remains NOT signalled until an 119 * explicit call to qdf_event_set(). 120 * 121 * This function sets the event to a NOT signalled state even if the event was 122 * signalled multiple times before being signaled. 123 * 124 * Return: QDF status 125 */ 126 QDF_STATUS qdf_event_reset(qdf_event_t *event) 127 { 128 QDF_BUG(event); 129 if (!event) 130 return QDF_STATUS_E_FAULT; 131 132 /* ensure event is initialized */ 133 QDF_BUG(event->cookie == LINUX_EVENT_COOKIE); 134 if (event->cookie != LINUX_EVENT_COOKIE) 135 return QDF_STATUS_E_INVAL; 136 137 /* (re)initialize event */ 138 event->done = false; 139 event->force_set = false; 140 INIT_COMPLETION(event->complete); 141 142 return QDF_STATUS_SUCCESS; 143 } 144 qdf_export_symbol(qdf_event_reset); 145 146 /** 147 * qdf_event_destroy() - Destroys a QDF event 148 * @event: The event object to be destroyed. 149 * 150 * This function doesn't do much in Linux. There is no need for the caller 151 * to explicitly destroy an event after use. 152 * 153 * The os_event_destroy() function shall destroy the event object 154 * referenced by event. After a successful return from qdf_event_destroy() 155 * the event object becomes, in effect, uninitialized. 156 * 157 * A destroyed event object can be reinitialized using qdf_event_create(); 158 * the results of otherwise referencing the object after it has been destroyed 159 * are undefined. Calls to QDF event functions to manipulate the lock such 160 * as qdf_event_set() will fail if the event is destroyed. Therefore, 161 * don't use the event after it has been destroyed until it has 162 * been re-initialized. 163 * 164 * Return: QDF status 165 */ 166 QDF_STATUS qdf_event_destroy(qdf_event_t *event) 167 { 168 QDF_BUG(event); 169 if (!event) 170 return QDF_STATUS_E_FAULT; 171 172 /* ensure event is initialized */ 173 QDF_BUG(event->cookie == LINUX_EVENT_COOKIE); 174 if (event->cookie != LINUX_EVENT_COOKIE) 175 return QDF_STATUS_E_INVAL; 176 177 /* make sure nobody is waiting on the event */ 178 complete_all(&event->complete); 179 180 /* destroy the event */ 181 memset(event, 0, sizeof(qdf_event_t)); 182 183 return QDF_STATUS_SUCCESS; 184 } 185 qdf_export_symbol(qdf_event_destroy); 186 187 /** 188 * qdf_wait_single_event() - Waits for a single event to be set. 189 * This API waits for the event to be set. 190 * 191 * @event: Pointer to an event to wait on. 192 * @timeout: Timeout value (in milliseconds). This function returns 193 * if this interval elapses, regardless if any of the events have 194 * been set. An input value of 0 for this timeout parameter means 195 * to wait infinitely, meaning a timeout will never occur. 196 * 197 * Return: QDF status 198 */ 199 QDF_STATUS qdf_wait_single_event(qdf_event_t *event, uint32_t timeout) 200 { 201 QDF_BUG(!in_interrupt()); 202 if (in_interrupt()) 203 return QDF_STATUS_E_FAULT; 204 205 QDF_BUG(event); 206 if (!event) 207 return QDF_STATUS_E_FAULT; 208 209 /* ensure event is initialized */ 210 QDF_BUG(event->cookie == LINUX_EVENT_COOKIE); 211 if (event->cookie != LINUX_EVENT_COOKIE) 212 return QDF_STATUS_E_INVAL; 213 214 if (timeout) { 215 long ret; 216 217 ret = wait_for_completion_timeout( 218 &event->complete, 219 __qdf_scaled_msecs_to_jiffies(timeout)); 220 221 if (ret <= 0) 222 return QDF_STATUS_E_TIMEOUT; 223 } else { 224 wait_for_completion(&event->complete); 225 } 226 227 return QDF_STATUS_SUCCESS; 228 } 229 qdf_export_symbol(qdf_wait_single_event); 230 231 /** 232 * qdf_complete_wait_events() - Sets all the events which are in the list. 233 * 234 * This function traverses the list of events and sets all of them. It 235 * sets the flag force_set as TRUE to indicate that these events have 236 * been forcefully set. 237 * 238 * Return: None 239 */ 240 void qdf_complete_wait_events(void) 241 { 242 struct qdf_evt_node *event_node = NULL; 243 qdf_list_node_t *list_node = NULL; 244 QDF_STATUS status; 245 246 if (qdf_list_empty(&qdf_wait_event_list)) 247 return; 248 249 qdf_spin_lock(&qdf_wait_event_lock); 250 qdf_list_peek_front(&qdf_wait_event_list, 251 &list_node); 252 253 while (list_node) { 254 event_node = qdf_container_of(list_node, 255 struct qdf_evt_node, node); 256 257 if (!event_node->pevent->done) { 258 event_node->pevent->force_set = true; 259 qdf_event_set(event_node->pevent); 260 } 261 262 status = qdf_list_peek_next(&qdf_wait_event_list, 263 &event_node->node, &list_node); 264 265 if (!QDF_IS_STATUS_SUCCESS(status)) 266 break; 267 } 268 qdf_spin_unlock(&qdf_wait_event_lock); 269 } 270 qdf_export_symbol(qdf_complete_wait_events); 271 272 /** 273 * qdf_wait_for_event_completion() - Waits for an event to be set. 274 * 275 * @event: Pointer to an event to wait on. 276 * @timeout: Timeout value (in milliseconds). 277 * 278 * This function adds the event in a list and waits on it until it 279 * is set or the timeout duration elapses. The purpose of waiting 280 * is considered complete only if the event is set and the flag 281 * force_set is FALSE, it returns success in this case. In other 282 * cases it returns appropriate error status. 283 * 284 * Return: QDF status 285 */ 286 QDF_STATUS qdf_wait_for_event_completion(qdf_event_t *event, uint32_t timeout) 287 { 288 struct qdf_evt_node *event_node; 289 QDF_STATUS status; 290 291 QDF_BUG(!in_interrupt()); 292 if (in_interrupt()) 293 return QDF_STATUS_E_FAULT; 294 295 QDF_BUG(event); 296 if (!event) 297 return QDF_STATUS_E_FAULT; 298 299 /* ensure event is initialized */ 300 QDF_BUG(event->cookie == LINUX_EVENT_COOKIE); 301 if (event->cookie != LINUX_EVENT_COOKIE) 302 return QDF_STATUS_E_INVAL; 303 304 event_node = qdf_mem_malloc(sizeof(*event_node)); 305 if (!event_node) 306 return QDF_STATUS_E_NOMEM; 307 308 event_node->pevent = event; 309 310 qdf_spin_lock(&qdf_wait_event_lock); 311 status = qdf_list_insert_back(&qdf_wait_event_list, &event_node->node); 312 qdf_spin_unlock(&qdf_wait_event_lock); 313 314 if (QDF_STATUS_SUCCESS != status) { 315 qdf_err("Failed to insert event into tracking list"); 316 goto free_node; 317 } 318 319 if (timeout) { 320 long ret; 321 322 /* update the timeout if it's on an emulation platform */ 323 ret = wait_for_completion_timeout(&event->complete, 324 __qdf_scaled_msecs_to_jiffies(timeout)); 325 326 if (ret <= 0) { 327 status = QDF_STATUS_E_TIMEOUT; 328 goto list_remove; 329 } 330 } else { 331 wait_for_completion(&event->complete); 332 } 333 334 /* if event was forcefully completed, return failure */ 335 if (event->force_set) 336 status = QDF_STATUS_E_FAULT; 337 338 list_remove: 339 qdf_spin_lock(&qdf_wait_event_lock); 340 qdf_list_remove_node(&qdf_wait_event_list, &event_node->node); 341 qdf_spin_unlock(&qdf_wait_event_lock); 342 343 free_node: 344 qdf_mem_free(event_node); 345 346 return status; 347 } 348 qdf_export_symbol(qdf_wait_for_event_completion); 349 350 /** 351 * qdf_event_list_init() - Creates a list and spinlock for events. 352 * 353 * This function creates a list for maintaining events on which threads 354 * wait for completion. A spinlock is also created to protect related 355 * oprations. 356 * 357 * Return: None 358 */ 359 void qdf_event_list_init(void) 360 { 361 qdf_list_create(&qdf_wait_event_list, MAX_WAIT_EVENTS); 362 qdf_spinlock_create(&qdf_wait_event_lock); 363 } 364 qdf_export_symbol(qdf_event_list_init); 365 366 /** 367 * qdf_event_list_destroy() - Destroys list and spinlock created for events. 368 * 369 * This function destroys the list and spinlock created for events on which 370 * threads wait for completion. 371 * 372 * Return: None 373 */ 374 void qdf_event_list_destroy(void) 375 { 376 qdf_list_destroy(&qdf_wait_event_list); 377 qdf_spinlock_destroy(&qdf_wait_event_lock); 378 } 379 qdf_export_symbol(qdf_event_list_destroy); 380 381 QDF_STATUS qdf_exit_thread(QDF_STATUS status) 382 { 383 if (status == QDF_STATUS_SUCCESS) 384 do_exit(0); 385 else 386 do_exit(SIGKILL); 387 388 return QDF_STATUS_SUCCESS; 389 } 390 qdf_export_symbol(qdf_exit_thread); 391