1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/*=============================================================================
29VideoCore OS Abstraction Layer - event flags implemented via mutexes
30=============================================================================*/
31
32#include "interface/vcos/vcos.h"
33#include "interface/vcos/generic/vcos_generic_event_flags.h"
34
35#include <stddef.h>
36
37/** A structure created by a thread that waits on the event flags
38 * for a particular combination of flags to arrive.
39 */
40typedef struct VCOS_EVENT_WAITER_T
41{
42 VCOS_UNSIGNED requested_events; /**< The events wanted */
43 VCOS_UNSIGNED actual_events; /**< Actual events found */
44 VCOS_UNSIGNED op; /**< The event operation to be used */
45 VCOS_STATUS_T return_status; /**< The return status the waiter should pass back */
46 VCOS_EVENT_FLAGS_T *flags; /**< Pointer to the original 'flags' structure */
47 VCOS_THREAD_T *thread; /**< Thread waiting */
48 struct VCOS_EVENT_WAITER_T *next;
49} VCOS_EVENT_WAITER_T;
50
51#ifndef NDEBUG
52static int waiter_list_valid(VCOS_EVENT_FLAGS_T *flags);
53#endif
54static void event_flags_timer_expired(void *cxt);
55
56VCOS_STATUS_T vcos_generic_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name)
57{
58 VCOS_STATUS_T rc;
59 if ((rc=vcos_mutex_create(&flags->lock, name)) != VCOS_SUCCESS)
60 {
61 return rc;
62 }
63
64 flags->events = 0;
65 flags->waiters.head = flags->waiters.tail = 0;
66 return rc;
67}
68
69void vcos_generic_event_flags_set(VCOS_EVENT_FLAGS_T *flags,
70 VCOS_UNSIGNED bitmask,
71 VCOS_OPTION op)
72{
73 vcos_assert(flags);
74 vcos_mutex_lock(&flags->lock);
75 if (op == VCOS_OR)
76 {
77 flags->events |= bitmask;
78 }
79 else if (op == VCOS_AND)
80 {
81 flags->events &= bitmask;
82 }
83 else
84 {
85 vcos_assert(0);
86 }
87
88 /* Now wake up any threads that have now become signalled. */
89 if (flags->waiters.head != NULL)
90 {
91 VCOS_UNSIGNED consumed_events = 0;
92 VCOS_EVENT_WAITER_T **pcurrent_waiter = &flags->waiters.head;
93 VCOS_EVENT_WAITER_T *prev_waiter = NULL;
94
95 /* Walk the chain of tasks suspend on this event flag group to determine
96 * if any of their requests can be satisfied.
97 */
98 while ((*pcurrent_waiter) != NULL)
99 {
100 VCOS_EVENT_WAITER_T *curr_waiter = *pcurrent_waiter;
101
102 /* Determine if this request has been satisfied */
103
104 /* First, find the event flags in common. */
105 VCOS_UNSIGNED waiter_satisfied = flags->events & curr_waiter->requested_events;
106
107 /* Second, determine if all the event flags must match */
108 if (curr_waiter->op & VCOS_AND)
109 {
110 /* All requested events must be present */
111 waiter_satisfied = (waiter_satisfied == curr_waiter->requested_events);
112 }
113
114 /* Wake this one up? */
115 if (waiter_satisfied)
116 {
117
118 if (curr_waiter->op & VCOS_CONSUME)
119 {
120 consumed_events |= curr_waiter->requested_events;
121 }
122
123 /* remove this block from the list, taking care at the end */
124 *pcurrent_waiter = curr_waiter->next;
125 if (curr_waiter->next == NULL)
126 flags->waiters.tail = prev_waiter;
127
128 vcos_assert(waiter_list_valid(flags));
129
130 curr_waiter->return_status = VCOS_SUCCESS;
131 curr_waiter->actual_events = flags->events;
132
133 _vcos_thread_sem_post(curr_waiter->thread);
134 }
135 else
136 {
137 /* move to next element in the list */
138 prev_waiter = *pcurrent_waiter;
139 pcurrent_waiter = &(curr_waiter->next);
140 }
141 }
142
143 flags->events &= ~consumed_events;
144
145 }
146
147 vcos_mutex_unlock(&flags->lock);
148}
149
150void vcos_generic_event_flags_delete(VCOS_EVENT_FLAGS_T *flags)
151{
152 vcos_mutex_delete(&flags->lock);
153}
154
155extern VCOS_STATUS_T vcos_generic_event_flags_get(VCOS_EVENT_FLAGS_T *flags,
156 VCOS_UNSIGNED bitmask,
157 VCOS_OPTION op,
158 VCOS_UNSIGNED suspend,
159 VCOS_UNSIGNED *retrieved_bits)
160{
161 VCOS_EVENT_WAITER_T waitreq;
162 VCOS_STATUS_T rc = VCOS_EAGAIN;
163 int satisfied = 0;
164
165 vcos_assert(flags);
166
167 /* default retrieved bits to 0 */
168 *retrieved_bits = 0;
169
170 vcos_mutex_lock(&flags->lock);
171 switch (op & VCOS_EVENT_FLAG_OP_MASK)
172 {
173 case VCOS_AND:
174 if ((flags->events & bitmask) == bitmask)
175 {
176 *retrieved_bits = flags->events;
177 rc = VCOS_SUCCESS;
178 satisfied = 1;
179 if (op & VCOS_CONSUME)
180 flags->events &= ~bitmask;
181 }
182 break;
183
184 case VCOS_OR:
185 if (flags->events & bitmask)
186 {
187 *retrieved_bits = flags->events;
188 rc = VCOS_SUCCESS;
189 satisfied = 1;
190 if (op & VCOS_CONSUME)
191 flags->events &= ~bitmask;
192 }
193 break;
194
195 default:
196 vcos_assert(0);
197 rc = VCOS_EINVAL;
198 break;
199 }
200
201 if (!satisfied && suspend)
202 {
203 /* Have to go to sleep.
204 *
205 * Append to tail so we get FIFO ordering.
206 */
207 waitreq.requested_events = bitmask;
208 waitreq.op = op;
209 waitreq.return_status = VCOS_EAGAIN;
210 waitreq.flags = flags;
211 waitreq.actual_events = 0;
212 waitreq.thread = vcos_thread_current();
213 waitreq.next = 0;
214 vcos_assert(waitreq.thread != (VCOS_THREAD_T*)-1);
215 VCOS_QUEUE_APPEND_TAIL(&flags->waiters, &waitreq);
216
217 if (suspend != (VCOS_UNSIGNED)-1)
218 _vcos_task_timer_set(event_flags_timer_expired, &waitreq, suspend);
219
220 vcos_mutex_unlock(&flags->lock);
221 /* go to sleep and wait to be signalled or timeout */
222
223 _vcos_thread_sem_wait();
224
225 *retrieved_bits = waitreq.actual_events;
226 rc = waitreq.return_status;
227
228 /* cancel the timer - do not do this while holding the mutex as it
229 * might be waiting for the timeout function to complete, which will
230 * try to take the mutex.
231 */
232 if (suspend != (VCOS_UNSIGNED)-1)
233 _vcos_task_timer_cancel();
234 }
235 else
236 {
237 vcos_mutex_unlock(&flags->lock);
238 }
239
240 return rc;
241}
242
243
244/** Called when a get call times out. Remove this thread's
245 * entry from the waiting queue, then resume the thread.
246 */
247static void event_flags_timer_expired(void *cxt)
248{
249 VCOS_EVENT_WAITER_T *waitreq = (VCOS_EVENT_WAITER_T *)cxt;
250 VCOS_EVENT_FLAGS_T *flags = waitreq->flags;
251 VCOS_EVENT_WAITER_T **plist;
252 VCOS_EVENT_WAITER_T *prev = NULL;
253 VCOS_THREAD_T *thread = 0;
254
255 vcos_assert(flags);
256
257 vcos_mutex_lock(&flags->lock);
258
259 /* walk the list of waiting threads on this event group, and remove
260 * the one that has expired.
261 *
262 * FIXME: could use doubly-linked list if lots of threads are found
263 * to be waiting on a single event flag instance.
264 */
265 plist = &flags->waiters.head;
266 while (*plist != NULL)
267 {
268 if (*plist == waitreq)
269 {
270 int at_end;
271 /* found it */
272 thread = (*plist)->thread;
273 at_end = ((*plist)->next == NULL);
274
275 /* link past */
276 *plist = (*plist)->next;
277 if (at_end)
278 flags->waiters.tail = prev;
279
280 break;
281 }
282 prev = *plist;
283 plist = &(*plist)->next;
284 }
285 vcos_assert(waiter_list_valid(flags));
286
287 vcos_mutex_unlock(&flags->lock);
288
289 if (thread)
290 {
291 _vcos_thread_sem_post(thread);
292 }
293}
294
295#ifndef NDEBUG
296
297static int waiter_list_valid(VCOS_EVENT_FLAGS_T *flags)
298{
299 int valid;
300 /* Either both head and tail are NULL, or neither are NULL */
301 if (flags->waiters.head == NULL)
302 {
303 valid = (flags->waiters.tail == NULL);
304 }
305 else
306 {
307 valid = (flags->waiters.tail != NULL);
308 }
309
310 /* If head and tail point at the same non-NULL element, then there
311 * is only one element in the list.
312 */
313 if (flags->waiters.head && (flags->waiters.head == flags->waiters.tail))
314 {
315 valid = (flags->waiters.head->next == NULL);
316 }
317 return valid;
318}
319
320#endif
321