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