xref: /wlan-dirver/qca-wifi-host-cmn/qdf/linux/src/qdf_event.c (revision d78dedc9dd8c4ee677ac1649d1d42f2a7c3cc1b7)
1 /*
2  * Copyright (c) 2014-2018 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 #ifdef CONFIG_MCL
34 static qdf_self_recovery_callback self_recovery_cb;
35 #endif
36 
37 struct qdf_evt_node {
38 	qdf_list_node_t node;
39 	qdf_event_t *pevent;
40 };
41 
42 #define MAX_WAIT_EVENTS 10
43 
44 static qdf_list_t qdf_wait_event_list;
45 static qdf_spinlock_t qdf_wait_event_lock;
46 
47 /* Function Definitions and Documentation */
48 
49 /**
50  * qdf_event_create() - initializes a QDF event
51  * @event: Pointer to the opaque event object to initialize
52  *
53  * The qdf_event_create() function initializes the specified event. Upon
54  * successful initialization, the state of the event becomes initialized
55  * and not signalled.
56  *
57  * An event must be initialized before it may be used in any other event
58  * functions.
59  * Attempting to initialize an already initialized event results in
60  * a failure.
61  *
62  * Return: QDF status
63  */
64 QDF_STATUS qdf_event_create(qdf_event_t *event)
65 {
66 	/* check for null pointer */
67 	if (NULL == event) {
68 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
69 			  "NULL event passed into %s", __func__);
70 		QDF_ASSERT(0);
71 		return QDF_STATUS_E_FAULT;
72 	}
73 
74 	/* check for 'already initialized' event */
75 	if (LINUX_EVENT_COOKIE == event->cookie) {
76 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
77 			  "Initialized event passed into %s", __func__);
78 		QDF_ASSERT(0);
79 		return QDF_STATUS_E_BUSY;
80 	}
81 
82 	/* initialize new event */
83 	init_completion(&event->complete);
84 	event->cookie = LINUX_EVENT_COOKIE;
85 
86 	return QDF_STATUS_SUCCESS;
87 }
88 qdf_export_symbol(qdf_event_create);
89 
90 /**
91  * qdf_event_set() - sets a QDF event
92  * @event: The event to set to the signalled state
93  *
94  * The state of the specified event is set to signalled by calling
95  * qdf_event_set().
96  *
97  * Any threads waiting on the event as a result of a qdf_event_wait() will
98  * be unblocked and available to be scheduled for execution when the event
99  * is signaled by a call to qdf_event_set().
100  *
101  * Return: QDF status
102  */
103 QDF_STATUS qdf_event_set(qdf_event_t *event)
104 {
105 	/* check for null pointer */
106 	if (NULL == event) {
107 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
108 			  "NULL event passed into %s", __func__);
109 		QDF_ASSERT(0);
110 		return QDF_STATUS_E_FAULT;
111 	}
112 
113 	/* check if event refers to an initialized object */
114 	if (LINUX_EVENT_COOKIE != event->cookie) {
115 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
116 			  "Uninitialized event passed into %s", __func__);
117 		QDF_ASSERT(0);
118 		return QDF_STATUS_E_INVAL;
119 	}
120 
121 	complete(&event->complete);
122 
123 	return QDF_STATUS_SUCCESS;
124 }
125 qdf_export_symbol(qdf_event_set);
126 
127 /**
128  * qdf_event_reset() - resets a QDF event
129  * @event: The event to set to the NOT signalled state
130  *
131  * This function isn't required for Linux. Therefore, it doesn't do much.
132  *
133  * The state of the specified event is set to 'NOT signalled' by calling
134  * qdf_event_reset().  The state of the event remains NOT signalled until an
135  * explicit call to qdf_event_set().
136  *
137  * This function sets the event to a NOT signalled state even if the event was
138  * signalled multiple times before being signaled.
139  *
140  * Return: QDF status
141  */
142 QDF_STATUS qdf_event_reset(qdf_event_t *event)
143 {
144 	/* check for null pointer */
145 	if (NULL == event) {
146 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
147 			  "NULL event passed into %s", __func__);
148 		QDF_ASSERT(0);
149 		return QDF_STATUS_E_FAULT;
150 	}
151 
152 	/* check to make sure it is an 'already initialized' event */
153 	if (LINUX_EVENT_COOKIE != event->cookie) {
154 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
155 			  "Uninitialized event passed into %s", __func__);
156 		QDF_ASSERT(0);
157 		return QDF_STATUS_E_INVAL;
158 	}
159 
160 	/* (re)initialize event */
161 	INIT_COMPLETION(event->complete);
162 	return QDF_STATUS_SUCCESS;
163 }
164 qdf_export_symbol(qdf_event_reset);
165 
166 /**
167  * qdf_event_destroy() - Destroys a QDF event
168  * @event: The event object to be destroyed.
169  *
170  * This function doesn't do much in Linux. There is no need for the caller
171  * to explicitly destroy an event after use.
172  *
173  * The os_event_destroy() function shall destroy the event object
174  * referenced by event.  After a successful return from qdf_event_destroy()
175  * the event object becomes, in effect, uninitialized.
176  *
177  * A destroyed event object can be reinitialized using qdf_event_create();
178  * the results of otherwise referencing the object after it has been destroyed
179  * are undefined.  Calls to QDF event functions to manipulate the lock such
180  * as qdf_event_set() will fail if the event is destroyed.  Therefore,
181  * don't use the event after it has been destroyed until it has
182  * been re-initialized.
183  *
184  * Return: QDF status
185  */
186 QDF_STATUS qdf_event_destroy(qdf_event_t *event)
187 {
188 	/* check for null pointer */
189 	if (NULL == event) {
190 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
191 			  "NULL event passed into %s", __func__);
192 		QDF_ASSERT(0);
193 		return QDF_STATUS_E_FAULT;
194 	}
195 
196 	/* check to make sure it is an 'already initialized' event */
197 	if (LINUX_EVENT_COOKIE != event->cookie) {
198 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
199 			  "Uninitialized event passed into %s", __func__);
200 		QDF_ASSERT(0);
201 		return QDF_STATUS_E_INVAL;
202 	}
203 
204 	/* make sure nobody is waiting on the event */
205 	complete_all(&event->complete);
206 
207 	/* destroy the event */
208 	memset(event, 0, sizeof(qdf_event_t));
209 
210 	return QDF_STATUS_SUCCESS;
211 }
212 qdf_export_symbol(qdf_event_destroy);
213 
214 /**
215  * qdf_wait_single_event() - Waits for a single event to be set.
216  * This API waits for the event to be set.
217  *
218  * @event: Pointer to an event to wait on.
219  * @timeout: Timeout value (in milliseconds).  This function returns
220  * if this interval elapses, regardless if any of the events have
221  * been set.  An input value of 0 for this timeout parameter means
222  * to wait infinitely, meaning a timeout will never occur.
223  *
224  * Return: QDF status
225  */
226 QDF_STATUS qdf_wait_single_event(qdf_event_t *event, uint32_t timeout)
227 {
228 	if (in_interrupt()) {
229 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
230 			  "%s cannot be called from interrupt context!!!",
231 			  __func__);
232 		QDF_ASSERT(0);
233 		return QDF_STATUS_E_FAULT;
234 	}
235 
236 	/* check for null pointer */
237 	if (NULL == event) {
238 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
239 			  "NULL event passed into %s", __func__);
240 		QDF_ASSERT(0);
241 		return QDF_STATUS_E_FAULT;
242 	}
243 
244 	/* check if cookie is same as that of initialized event */
245 	if (LINUX_EVENT_COOKIE != event->cookie) {
246 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
247 			  "Uninitialized event passed into %s", __func__);
248 		QDF_ASSERT(0);
249 		return QDF_STATUS_E_INVAL;
250 	}
251 
252 	/* update the timeout if its on a emaulation platform */
253 	timeout *= qdf_timer_get_multiplier();
254 	if (timeout) {
255 		long ret;
256 
257 		ret = wait_for_completion_timeout(&event->complete,
258 						  msecs_to_jiffies(timeout));
259 		if (0 >= ret)
260 			return QDF_STATUS_E_TIMEOUT;
261 	} else {
262 		wait_for_completion(&event->complete);
263 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO,
264 			  "Signaled for completion %s", __func__);
265 		return QDF_STATUS_SUCCESS;
266 	}
267 	return QDF_STATUS_SUCCESS;
268 }
269 qdf_export_symbol(qdf_wait_single_event);
270 
271 /**
272  * qdf_complete_wait_events() - Sets all the events which are in the list.
273  *
274  * This function traverses the list of events and sets all of them. It
275  * sets the flag force_set as TRUE to indicate that these events have
276  * been forcefully set.
277  *
278  * Return: None
279  */
280 void qdf_complete_wait_events(void)
281 {
282 	struct qdf_evt_node *event_node = NULL;
283 	qdf_list_node_t *list_node = NULL;
284 	QDF_STATUS status;
285 
286 	if (qdf_list_empty(&qdf_wait_event_list))
287 		return;
288 
289 	qdf_spin_lock(&qdf_wait_event_lock);
290 	qdf_list_peek_front(&qdf_wait_event_list,
291 			    &list_node);
292 
293 	while (list_node != NULL) {
294 		event_node = qdf_container_of(list_node,
295 						struct qdf_evt_node, node);
296 
297 		event_node->pevent->force_set = true;
298 		qdf_event_set(event_node->pevent);
299 
300 		status = qdf_list_peek_next(&qdf_wait_event_list,
301 					&event_node->node, &list_node);
302 
303 		if (!QDF_IS_STATUS_SUCCESS(status))
304 			break;
305 	}
306 	qdf_spin_unlock(&qdf_wait_event_lock);
307 }
308 qdf_export_symbol(qdf_complete_wait_events);
309 
310 /**
311  * qdf_wait_for_event_completion() - Waits for an event to be set.
312  *
313  * @event: Pointer to an event to wait on.
314  * @timeout: Timeout value (in milliseconds).
315  *
316  * This function adds the event in a list and waits on it until it
317  * is set or the timeout duration elapses. The purpose of waiting
318  * is considered complete only if the event is set and the flag
319  * force_set is FALSE, it returns success in this case. In other
320  * cases it returns appropriate error status.
321  *
322  * Return: QDF status
323  */
324 QDF_STATUS qdf_wait_for_event_completion(qdf_event_t *event, uint32_t timeout)
325 {
326 	struct qdf_evt_node *event_node;
327 	QDF_STATUS status;
328 
329 	if (in_interrupt()) {
330 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
331 			  "%s cannot be called from interrupt context!!!",
332 			  __func__);
333 		QDF_ASSERT(0);
334 		return QDF_STATUS_E_FAULT;
335 	}
336 
337 	/* check for null pointer */
338 	if (NULL == event) {
339 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
340 			  "NULL event passed into %s", __func__);
341 		QDF_ASSERT(0);
342 		return QDF_STATUS_E_FAULT;
343 	}
344 
345 	/* check if cookie is same as that of initialized event */
346 	if (LINUX_EVENT_COOKIE != event->cookie) {
347 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
348 			  "Uninitialized event passed into %s", __func__);
349 		QDF_ASSERT(0);
350 		return QDF_STATUS_E_INVAL;
351 	}
352 
353 	event_node = qdf_mem_malloc(sizeof(*event_node));
354 	if (NULL == event_node) {
355 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
356 			  "Could not allocate for event node in %s", __func__);
357 		QDF_ASSERT(0);
358 		return QDF_STATUS_E_NOMEM;
359 	}
360 	event_node->pevent = event;
361 
362 	qdf_spin_lock(&qdf_wait_event_lock);
363 	status = qdf_list_insert_back(&qdf_wait_event_list,
364 			&event_node->node);
365 	qdf_spin_unlock(&qdf_wait_event_lock);
366 
367 	if (QDF_STATUS_SUCCESS != status) {
368 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
369 			"Failed to add event in the list in %s", __func__),
370 		status = QDF_STATUS_E_FAULT;
371 		goto err_list_add;
372 	}
373 
374 	/* update the timeout if its on a emaulation platform */
375 	timeout *= qdf_timer_get_multiplier();
376 	if (timeout) {
377 		long ret;
378 
379 		ret = wait_for_completion_timeout(&event->complete,
380 						  msecs_to_jiffies(timeout));
381 		if (0 >= ret)
382 			/* Timeout occurred */
383 			status = QDF_STATUS_E_TIMEOUT;
384 		else {
385 			if (event->force_set)
386 				/* Event forcefully completed, return fail */
387 				status = QDF_STATUS_E_FAULT;
388 			else
389 				status = QDF_STATUS_SUCCESS;
390 		}
391 	} else {
392 		wait_for_completion(&event->complete);
393 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO,
394 			"Signaled for completion %s", __func__);
395 		if (event->force_set)
396 			/* Event forcefully completed, return fail */
397 			status = QDF_STATUS_E_FAULT;
398 		else
399 			status = QDF_STATUS_SUCCESS;
400 	}
401 
402 	qdf_spin_lock(&qdf_wait_event_lock);
403 	qdf_list_remove_node(&qdf_wait_event_list,
404 			&event_node->node);
405 	qdf_spin_unlock(&qdf_wait_event_lock);
406 err_list_add:
407 	qdf_mem_free(event_node);
408 	return status;
409 }
410 qdf_export_symbol(qdf_wait_for_event_completion);
411 
412 /**
413  * qdf_event_list_init() - Creates a list and spinlock for events.
414  *
415  * This function creates a list for maintaining events on which threads
416  * wait for completion. A spinlock is also created to protect related
417  * oprations.
418  *
419  * Return: None
420  */
421 void qdf_event_list_init(void)
422 {
423 	qdf_list_create(&qdf_wait_event_list, MAX_WAIT_EVENTS);
424 	qdf_spinlock_create(&qdf_wait_event_lock);
425 }
426 qdf_export_symbol(qdf_event_list_init);
427 
428 /**
429  * qdf_event_list_destroy() - Destroys list and spinlock created for events.
430  *
431  * This function destroys the list and spinlock created for events on which
432  * threads wait for completion.
433  *
434  * Return: None
435  */
436 void qdf_event_list_destroy(void)
437 {
438 	qdf_list_destroy(&qdf_wait_event_list);
439 	qdf_spinlock_destroy(&qdf_wait_event_lock);
440 }
441 qdf_export_symbol(qdf_event_list_destroy);
442 
443 QDF_STATUS qdf_exit_thread(QDF_STATUS status)
444 {
445 	if (status == QDF_STATUS_SUCCESS)
446 		do_exit(0);
447 	else
448 		do_exit(SIGKILL);
449 
450 	return QDF_STATUS_SUCCESS;
451 }
452 qdf_export_symbol(qdf_exit_thread);
453 
454 #ifdef CONFIG_MCL
455 void qdf_register_self_recovery_callback(qdf_self_recovery_callback callback)
456 {
457 	self_recovery_cb = callback;
458 }
459 
460 void qdf_trigger_self_recovery(void)
461 {
462 	if (!self_recovery_cb) {
463 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO,
464 			  "No self recovery callback registered %s", __func__);
465 		return;
466 	}
467 	self_recovery_cb(QDF_REASON_UNSPECIFIED);
468 }
469 #endif
470