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_module.h> 32 33 struct qdf_evt_node { 34 qdf_list_node_t node; 35 qdf_event_t *pevent; 36 }; 37 38 #define MAX_WAIT_EVENTS 10 39 40 static qdf_list_t qdf_wait_event_list; 41 static qdf_spinlock_t qdf_wait_event_lock; 42 43 /* Function Definitions and Documentation */ 44 45 /** 46 * qdf_event_create() - initializes a QDF event 47 * @event: Pointer to the opaque event object to initialize 48 * 49 * The qdf_event_create() function initializes the specified event. Upon 50 * successful initialization, the state of the event becomes initialized 51 * and not signalled. 52 * 53 * An event must be initialized before it may be used in any other event 54 * functions. 55 * Attempting to initialize an already initialized event results in 56 * a failure. 57 * 58 * Return: QDF status 59 */ 60 QDF_STATUS qdf_event_create(qdf_event_t *event) 61 { 62 QDF_BUG(event); 63 if (!event) 64 return QDF_STATUS_E_FAULT; 65 66 /* check for 'already initialized' event */ 67 QDF_BUG(event->cookie != LINUX_EVENT_COOKIE); 68 if (event->cookie == LINUX_EVENT_COOKIE) 69 return QDF_STATUS_E_BUSY; 70 71 /* initialize new event */ 72 init_completion(&event->complete); 73 event->cookie = LINUX_EVENT_COOKIE; 74 75 return QDF_STATUS_SUCCESS; 76 } 77 qdf_export_symbol(qdf_event_create); 78 79 /** 80 * qdf_event_set() - sets a QDF event 81 * @event: The event to set to the signalled state 82 * 83 * The state of the specified event is set to signalled by calling 84 * qdf_event_set(). 85 * 86 * Any threads waiting on the event as a result of a qdf_event_wait() will 87 * be unblocked and available to be scheduled for execution when the event 88 * is signaled by a call to qdf_event_set(). 89 * 90 * Return: QDF status 91 */ 92 QDF_STATUS qdf_event_set(qdf_event_t *event) 93 { 94 QDF_BUG(event); 95 if (!event) 96 return QDF_STATUS_E_FAULT; 97 98 /* ensure event is initialized */ 99 QDF_BUG(event->cookie == LINUX_EVENT_COOKIE); 100 if (event->cookie != LINUX_EVENT_COOKIE) 101 return QDF_STATUS_E_INVAL; 102 103 complete(&event->complete); 104 105 return QDF_STATUS_SUCCESS; 106 } 107 qdf_export_symbol(qdf_event_set); 108 109 /** 110 * qdf_event_reset() - resets a QDF event 111 * @event: The event to set to the NOT signalled state 112 * 113 * This function isn't required for Linux. Therefore, it doesn't do much. 114 * 115 * The state of the specified event is set to 'NOT signalled' by calling 116 * qdf_event_reset(). The state of the event remains NOT signalled until an 117 * explicit call to qdf_event_set(). 118 * 119 * This function sets the event to a NOT signalled state even if the event was 120 * signalled multiple times before being signaled. 121 * 122 * Return: QDF status 123 */ 124 QDF_STATUS qdf_event_reset(qdf_event_t *event) 125 { 126 QDF_BUG(event); 127 if (!event) 128 return QDF_STATUS_E_FAULT; 129 130 /* ensure event is initialized */ 131 QDF_BUG(event->cookie == LINUX_EVENT_COOKIE); 132 if (event->cookie != LINUX_EVENT_COOKIE) 133 return QDF_STATUS_E_INVAL; 134 135 /* (re)initialize event */ 136 event->force_set = false; 137 INIT_COMPLETION(event->complete); 138 139 return QDF_STATUS_SUCCESS; 140 } 141 qdf_export_symbol(qdf_event_reset); 142 143 /** 144 * qdf_event_destroy() - Destroys a QDF event 145 * @event: The event object to be destroyed. 146 * 147 * This function doesn't do much in Linux. There is no need for the caller 148 * to explicitly destroy an event after use. 149 * 150 * The os_event_destroy() function shall destroy the event object 151 * referenced by event. After a successful return from qdf_event_destroy() 152 * the event object becomes, in effect, uninitialized. 153 * 154 * A destroyed event object can be reinitialized using qdf_event_create(); 155 * the results of otherwise referencing the object after it has been destroyed 156 * are undefined. Calls to QDF event functions to manipulate the lock such 157 * as qdf_event_set() will fail if the event is destroyed. Therefore, 158 * don't use the event after it has been destroyed until it has 159 * been re-initialized. 160 * 161 * Return: QDF status 162 */ 163 QDF_STATUS qdf_event_destroy(qdf_event_t *event) 164 { 165 QDF_BUG(event); 166 if (!event) 167 return QDF_STATUS_E_FAULT; 168 169 /* ensure event is initialized */ 170 QDF_BUG(event->cookie == LINUX_EVENT_COOKIE); 171 if (event->cookie != LINUX_EVENT_COOKIE) 172 return QDF_STATUS_E_INVAL; 173 174 /* make sure nobody is waiting on the event */ 175 complete_all(&event->complete); 176 177 /* destroy the event */ 178 memset(event, 0, sizeof(qdf_event_t)); 179 180 return QDF_STATUS_SUCCESS; 181 } 182 qdf_export_symbol(qdf_event_destroy); 183 184 /** 185 * qdf_wait_single_event() - Waits for a single event to be set. 186 * This API waits for the event to be set. 187 * 188 * @event: Pointer to an event to wait on. 189 * @timeout: Timeout value (in milliseconds). This function returns 190 * if this interval elapses, regardless if any of the events have 191 * been set. An input value of 0 for this timeout parameter means 192 * to wait infinitely, meaning a timeout will never occur. 193 * 194 * Return: QDF status 195 */ 196 QDF_STATUS qdf_wait_single_event(qdf_event_t *event, uint32_t timeout) 197 { 198 QDF_BUG(!in_interrupt()); 199 if (in_interrupt()) 200 return QDF_STATUS_E_FAULT; 201 202 QDF_BUG(event); 203 if (!event) 204 return QDF_STATUS_E_FAULT; 205 206 /* ensure event is initialized */ 207 QDF_BUG(event->cookie == LINUX_EVENT_COOKIE); 208 if (event->cookie != LINUX_EVENT_COOKIE) 209 return QDF_STATUS_E_INVAL; 210 211 if (timeout) { 212 long ret; 213 214 /* update the timeout if it's on an emulation platform */ 215 timeout *= qdf_timer_get_multiplier(); 216 ret = wait_for_completion_timeout(&event->complete, 217 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 != NULL) { 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