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