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