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