xref: /wlan-dirver/qca-wifi-host-cmn/qdf/linux/src/qdf_event.c (revision 6ecd284e5a94a1c96e26d571dd47419ac305990d)
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