xref: /wlan-driver/qca-wifi-host-cmn/qdf/linux/src/qdf_event.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1 /*
2  * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /**
21  * DOC: qdf_event.c
22  *
23  * This source file contains linux specific definitions for QDF event APIs
24  * The APIs mentioned in this file are used for initializing, setting,
25  * resetting, destroying an event and waiting on an occurrence of an event
26  * among multiple events.
27  */
28 
29 /* Include Files */
30 #include "qdf_event.h"
31 #include "qdf_mc_timer.h"
32 #include "qdf_timer.h"
33 #include <qdf_module.h>
34 
35 struct qdf_evt_node {
36 	qdf_list_node_t node;
37 	qdf_event_t *pevent;
38 };
39 
40 #define MAX_WAIT_EVENTS 10
41 
42 static qdf_list_t qdf_wait_event_list;
43 static qdf_spinlock_t qdf_wait_event_lock;
44 
45 /* Function Definitions and Documentation */
46 
47 /**
48  * qdf_event_create() - initializes a QDF event
49  * @event: Pointer to the opaque event object to initialize
50  *
51  * The qdf_event_create() function initializes the specified event. Upon
52  * successful initialization, the state of the event becomes initialized
53  * and not signalled.
54  *
55  * An event must be initialized before it may be used in any other event
56  * functions.
57  * Attempting to initialize an already initialized event results in
58  * a failure.
59  *
60  * Return: QDF status
61  */
qdf_event_create(qdf_event_t * event)62 QDF_STATUS qdf_event_create(qdf_event_t *event)
63 {
64 	QDF_BUG(event);
65 	if (!event)
66 		return QDF_STATUS_E_FAULT;
67 
68 	/* check for 'already initialized' event */
69 	QDF_BUG(event->cookie != LINUX_EVENT_COOKIE);
70 	if (event->cookie == LINUX_EVENT_COOKIE)
71 		return QDF_STATUS_E_BUSY;
72 
73 	/* initialize new event */
74 	init_completion(&event->complete);
75 	event->cookie = LINUX_EVENT_COOKIE;
76 
77 	return QDF_STATUS_SUCCESS;
78 }
79 qdf_export_symbol(qdf_event_create);
80 
qdf_event_set(qdf_event_t * event)81 QDF_STATUS qdf_event_set(qdf_event_t *event)
82 {
83 	QDF_BUG(event);
84 	if (!event)
85 		return QDF_STATUS_E_FAULT;
86 
87 	/* ensure event is initialized */
88 	QDF_BUG(event->cookie == LINUX_EVENT_COOKIE);
89 	if (event->cookie != LINUX_EVENT_COOKIE)
90 		return QDF_STATUS_E_INVAL;
91 
92 	event->done = true;
93 	complete(&event->complete);
94 
95 	return QDF_STATUS_SUCCESS;
96 }
97 
98 qdf_export_symbol(qdf_event_set);
99 
qdf_event_set_all(qdf_event_t * event)100 QDF_STATUS qdf_event_set_all(qdf_event_t *event)
101 {
102 	QDF_BUG(event);
103 	if (!event)
104 		return QDF_STATUS_E_FAULT;
105 
106 	/* ensure event is initialized */
107 	QDF_BUG(event->cookie == LINUX_EVENT_COOKIE);
108 	if (event->cookie != LINUX_EVENT_COOKIE)
109 		return QDF_STATUS_E_INVAL;
110 
111 	event->done = true;
112 	complete_all(&event->complete);
113 
114 	return QDF_STATUS_SUCCESS;
115 }
116 
117 qdf_export_symbol(qdf_event_set_all);
118 
119 /**
120  * qdf_event_reset() - resets a QDF event
121  * @event: The event to set to the NOT signalled state
122  *
123  * This function isn't required for Linux. Therefore, it doesn't do much.
124  *
125  * The state of the specified event is set to 'NOT signalled' by calling
126  * qdf_event_reset().  The state of the event remains NOT signalled until an
127  * explicit call to qdf_event_set().
128  *
129  * This function sets the event to a NOT signalled state even if the event was
130  * signalled multiple times before being signaled.
131  *
132  * Return: QDF status
133  */
qdf_event_reset(qdf_event_t * event)134 QDF_STATUS qdf_event_reset(qdf_event_t *event)
135 {
136 	QDF_BUG(event);
137 	if (!event)
138 		return QDF_STATUS_E_FAULT;
139 
140 	/* ensure event is initialized */
141 	QDF_BUG(event->cookie == LINUX_EVENT_COOKIE);
142 	if (event->cookie != LINUX_EVENT_COOKIE)
143 		return QDF_STATUS_E_INVAL;
144 
145 	/* (re)initialize event */
146 	event->done = false;
147 	event->force_set = false;
148 	INIT_COMPLETION(event->complete);
149 
150 	return QDF_STATUS_SUCCESS;
151 }
152 qdf_export_symbol(qdf_event_reset);
153 
154 /**
155  * qdf_event_destroy() - Destroys a QDF event
156  * @event: The event object to be destroyed.
157  *
158  * This function doesn't do much in Linux. There is no need for the caller
159  * to explicitly destroy an event after use.
160  *
161  * The os_event_destroy() function shall destroy the event object
162  * referenced by event.  After a successful return from qdf_event_destroy()
163  * the event object becomes, in effect, uninitialized.
164  *
165  * A destroyed event object can be reinitialized using qdf_event_create();
166  * the results of otherwise referencing the object after it has been destroyed
167  * are undefined.  Calls to QDF event functions to manipulate the lock such
168  * as qdf_event_set() will fail if the event is destroyed.  Therefore,
169  * don't use the event after it has been destroyed until it has
170  * been re-initialized.
171  *
172  * Return: QDF status
173  */
qdf_event_destroy(qdf_event_t * event)174 QDF_STATUS qdf_event_destroy(qdf_event_t *event)
175 {
176 	QDF_BUG(event);
177 	if (!event)
178 		return QDF_STATUS_E_FAULT;
179 
180 	/* ensure event is initialized */
181 	QDF_BUG(event->cookie == LINUX_EVENT_COOKIE);
182 	if (event->cookie != LINUX_EVENT_COOKIE)
183 		return QDF_STATUS_E_INVAL;
184 
185 	/* make sure nobody is waiting on the event */
186 	complete_all(&event->complete);
187 
188 	/* destroy the event */
189 	memset(event, 0, sizeof(qdf_event_t));
190 
191 	return QDF_STATUS_SUCCESS;
192 }
193 qdf_export_symbol(qdf_event_destroy);
194 
195 /**
196  * qdf_wait_single_event() - Waits for a single event to be set.
197  * This API waits for the event to be set.
198  *
199  * @event: Pointer to an event to wait on.
200  * @timeout: Timeout value (in milliseconds).  This function returns
201  * if this interval elapses, regardless if any of the events have
202  * been set.  An input value of 0 for this timeout parameter means
203  * to wait infinitely, meaning a timeout will never occur.
204  *
205  * Return: QDF status
206  */
qdf_wait_single_event(qdf_event_t * event,uint32_t timeout)207 QDF_STATUS qdf_wait_single_event(qdf_event_t *event, uint32_t timeout)
208 {
209 	QDF_BUG(!in_interrupt());
210 	if (in_interrupt())
211 		return QDF_STATUS_E_FAULT;
212 
213 	QDF_BUG(event);
214 	if (!event)
215 		return QDF_STATUS_E_FAULT;
216 
217 	/* ensure event is initialized */
218 	QDF_BUG(event->cookie == LINUX_EVENT_COOKIE);
219 	if (event->cookie != LINUX_EVENT_COOKIE)
220 		return QDF_STATUS_E_INVAL;
221 
222 	if (timeout) {
223 		long ret;
224 
225 		ret = wait_for_completion_timeout(
226 				&event->complete,
227 				__qdf_scaled_msecs_to_jiffies(timeout));
228 
229 		if (ret <= 0)
230 			return QDF_STATUS_E_TIMEOUT;
231 	} else {
232 		wait_for_completion(&event->complete);
233 	}
234 
235 	return QDF_STATUS_SUCCESS;
236 }
237 qdf_export_symbol(qdf_wait_single_event);
238 
239 /**
240  * qdf_complete_wait_events() - Sets all the events which are in the list.
241  *
242  * This function traverses the list of events and sets all of them. It
243  * sets the flag force_set as TRUE to indicate that these events have
244  * been forcefully set.
245  *
246  * Return: None
247  */
qdf_complete_wait_events(void)248 void qdf_complete_wait_events(void)
249 {
250 	struct qdf_evt_node *event_node = NULL;
251 	qdf_list_node_t *list_node = NULL;
252 	QDF_STATUS status;
253 
254 	if (qdf_list_empty(&qdf_wait_event_list))
255 		return;
256 
257 	qdf_spin_lock(&qdf_wait_event_lock);
258 	qdf_list_peek_front(&qdf_wait_event_list,
259 			    &list_node);
260 
261 	while (list_node) {
262 		event_node = qdf_container_of(list_node,
263 						struct qdf_evt_node, node);
264 
265 		if (!event_node->pevent->done) {
266 			event_node->pevent->force_set = true;
267 			qdf_event_set(event_node->pevent);
268 		}
269 
270 		status = qdf_list_peek_next(&qdf_wait_event_list,
271 					&event_node->node, &list_node);
272 
273 		if (!QDF_IS_STATUS_SUCCESS(status))
274 			break;
275 	}
276 	qdf_spin_unlock(&qdf_wait_event_lock);
277 }
278 qdf_export_symbol(qdf_complete_wait_events);
279 
280 /**
281  * qdf_wait_for_event_completion() - Waits for an event to be set.
282  *
283  * @event: Pointer to an event to wait on.
284  * @timeout: Timeout value (in milliseconds).
285  *
286  * This function adds the event in a list and waits on it until it
287  * is set or the timeout duration elapses. The purpose of waiting
288  * is considered complete only if the event is set and the flag
289  * force_set is FALSE, it returns success in this case. In other
290  * cases it returns appropriate error status.
291  *
292  * Return: QDF status
293  */
qdf_wait_for_event_completion(qdf_event_t * event,uint32_t timeout)294 QDF_STATUS qdf_wait_for_event_completion(qdf_event_t *event, uint32_t timeout)
295 {
296 	struct qdf_evt_node *event_node;
297 	QDF_STATUS status;
298 
299 	QDF_BUG(!in_interrupt());
300 	if (in_interrupt())
301 		return QDF_STATUS_E_FAULT;
302 
303 	QDF_BUG(event);
304 	if (!event)
305 		return QDF_STATUS_E_FAULT;
306 
307 	/* ensure event is initialized */
308 	QDF_BUG(event->cookie == LINUX_EVENT_COOKIE);
309 	if (event->cookie != LINUX_EVENT_COOKIE)
310 		return QDF_STATUS_E_INVAL;
311 
312 	event_node = qdf_mem_malloc(sizeof(*event_node));
313 	if (!event_node)
314 		return QDF_STATUS_E_NOMEM;
315 
316 	event_node->pevent = event;
317 
318 	qdf_spin_lock(&qdf_wait_event_lock);
319 	status = qdf_list_insert_back(&qdf_wait_event_list, &event_node->node);
320 	qdf_spin_unlock(&qdf_wait_event_lock);
321 
322 	if (QDF_STATUS_SUCCESS != status) {
323 		qdf_err("Failed to insert event into tracking list");
324 		goto free_node;
325 	}
326 
327 	if (timeout) {
328 		long ret;
329 
330 		/* update the timeout if it's on an emulation platform */
331 		ret = wait_for_completion_timeout(&event->complete,
332 						  __qdf_scaled_msecs_to_jiffies(timeout));
333 
334 		if (ret <= 0) {
335 			status = QDF_STATUS_E_TIMEOUT;
336 			goto list_remove;
337 		}
338 	} else {
339 		wait_for_completion(&event->complete);
340 	}
341 
342 	/* if event was forcefully completed, return failure */
343 	if (event->force_set)
344 		status = QDF_STATUS_E_FAULT;
345 
346 list_remove:
347 	qdf_spin_lock(&qdf_wait_event_lock);
348 	qdf_list_remove_node(&qdf_wait_event_list, &event_node->node);
349 	qdf_spin_unlock(&qdf_wait_event_lock);
350 
351 free_node:
352 	qdf_mem_free(event_node);
353 
354 	return status;
355 }
356 qdf_export_symbol(qdf_wait_for_event_completion);
357 
358 /**
359  * qdf_event_list_init() - Creates a list and spinlock for events.
360  *
361  * This function creates a list for maintaining events on which threads
362  * wait for completion. A spinlock is also created to protect related
363  * oprations.
364  *
365  * Return: None
366  */
qdf_event_list_init(void)367 void qdf_event_list_init(void)
368 {
369 	qdf_list_create(&qdf_wait_event_list, MAX_WAIT_EVENTS);
370 	qdf_spinlock_create(&qdf_wait_event_lock);
371 }
372 qdf_export_symbol(qdf_event_list_init);
373 
374 /**
375  * qdf_event_list_destroy() - Destroys list and spinlock created for events.
376  *
377  * This function destroys the list and spinlock created for events on which
378  * threads wait for completion.
379  *
380  * Return: None
381  */
qdf_event_list_destroy(void)382 void qdf_event_list_destroy(void)
383 {
384 	qdf_list_destroy(&qdf_wait_event_list);
385 	qdf_spinlock_destroy(&qdf_wait_event_lock);
386 }
387 qdf_export_symbol(qdf_event_list_destroy);
388