xref: /wlan-dirver/qca-wifi-host-cmn/qdf/linux/src/qdf_event.c (revision 45a38684b07295822dc8eba39e293408f203eec8)
1 /*
2  * Copyright (c) 2014-2020 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 		timeout *= qdf_timer_get_multiplier();
324 		ret = wait_for_completion_timeout(&event->complete,
325 						  msecs_to_jiffies(timeout));
326 
327 		if (ret <= 0) {
328 			status = QDF_STATUS_E_TIMEOUT;
329 			goto list_remove;
330 		}
331 	} else {
332 		wait_for_completion(&event->complete);
333 	}
334 
335 	/* if event was forcefully completed, return failure */
336 	if (event->force_set)
337 		status = QDF_STATUS_E_FAULT;
338 
339 list_remove:
340 	qdf_spin_lock(&qdf_wait_event_lock);
341 	qdf_list_remove_node(&qdf_wait_event_list, &event_node->node);
342 	qdf_spin_unlock(&qdf_wait_event_lock);
343 
344 free_node:
345 	qdf_mem_free(event_node);
346 
347 	return status;
348 }
349 qdf_export_symbol(qdf_wait_for_event_completion);
350 
351 /**
352  * qdf_event_list_init() - Creates a list and spinlock for events.
353  *
354  * This function creates a list for maintaining events on which threads
355  * wait for completion. A spinlock is also created to protect related
356  * oprations.
357  *
358  * Return: None
359  */
360 void qdf_event_list_init(void)
361 {
362 	qdf_list_create(&qdf_wait_event_list, MAX_WAIT_EVENTS);
363 	qdf_spinlock_create(&qdf_wait_event_lock);
364 }
365 qdf_export_symbol(qdf_event_list_init);
366 
367 /**
368  * qdf_event_list_destroy() - Destroys list and spinlock created for events.
369  *
370  * This function destroys the list and spinlock created for events on which
371  * threads wait for completion.
372  *
373  * Return: None
374  */
375 void qdf_event_list_destroy(void)
376 {
377 	qdf_list_destroy(&qdf_wait_event_list);
378 	qdf_spinlock_destroy(&qdf_wait_event_lock);
379 }
380 qdf_export_symbol(qdf_event_list_destroy);
381 
382 QDF_STATUS qdf_exit_thread(QDF_STATUS status)
383 {
384 	if (status == QDF_STATUS_SUCCESS)
385 		do_exit(0);
386 	else
387 		do_exit(SIGKILL);
388 
389 	return QDF_STATUS_SUCCESS;
390 }
391 qdf_export_symbol(qdf_exit_thread);
392