blob: 76d99c25d62b8d24fccb953e162e43adbcbbcaf6 [file] [log] [blame]
Benjamin Walshacc68c12017-01-29 18:57:45 -05001/*
2 * Copyright (c) 2017 Wind River Systems, Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7/**
8 * @file
9 *
10 * @brief Kernel asynchronous event polling interface.
11 *
12 * This polling mechanism allows waiting on multiple events concurrently,
13 * either events triggered directly, or from kernel objects or other kernel
14 * constructs.
15 */
16
17#include <kernel.h>
18#include <kernel_structs.h>
19#include <wait_q.h>
20#include <ksched.h>
21#include <misc/slist.h>
22#include <misc/dlist.h>
23#include <misc/__assert.h>
24
Kumar Galacc334c72017-04-21 10:55:34 -050025void k_poll_event_init(struct k_poll_event *event, u32_t type,
Benjamin Walshacc68c12017-01-29 18:57:45 -050026 int mode, void *obj)
27{
28 __ASSERT(mode == K_POLL_MODE_NOTIFY_ONLY,
29 "only NOTIFY_ONLY mode is supported\n");
30 __ASSERT(type < (1 << _POLL_NUM_TYPES), "invalid type\n");
31 __ASSERT(obj, "must provide an object\n");
32
33 event->poller = NULL;
Benjamin Walsh969d4a72017-02-02 11:25:11 -050034 /* event->tag is left uninitialized: the user will set it if needed */
Benjamin Walshacc68c12017-01-29 18:57:45 -050035 event->type = type;
36 event->state = K_POLL_STATE_NOT_READY;
37 event->mode = mode;
38 event->unused = 0;
39 event->obj = obj;
40}
41
42/* must be called with interrupts locked */
43static inline void set_polling_state(struct k_thread *thread)
44{
45 _mark_thread_as_polling(thread);
46}
47
48/* must be called with interrupts locked */
49static inline void clear_polling_state(struct k_thread *thread)
50{
51 _mark_thread_as_not_polling(thread);
52}
53
54/* must be called with interrupts locked */
Benjamin Walsh47503e32017-02-09 14:52:21 -050055static inline int is_polling(void)
Benjamin Walshacc68c12017-01-29 18:57:45 -050056{
Benjamin Walsh47503e32017-02-09 14:52:21 -050057 return _is_thread_polling(_current);
Benjamin Walshacc68c12017-01-29 18:57:45 -050058}
59
60/* must be called with interrupts locked */
Kumar Galacc334c72017-04-21 10:55:34 -050061static inline int is_condition_met(struct k_poll_event *event, u32_t *state)
Benjamin Walshacc68c12017-01-29 18:57:45 -050062{
63 switch (event->type) {
64 case K_POLL_TYPE_SEM_AVAILABLE:
65 if (k_sem_count_get(event->sem) > 0) {
66 *state = K_POLL_STATE_SEM_AVAILABLE;
67 return 1;
68 }
69 break;
Luiz Augusto von Dentze5ed88f2017-02-21 15:27:20 +020070 case K_POLL_TYPE_DATA_AVAILABLE:
71 if (!k_queue_is_empty(event->queue)) {
Benjamin Walshacc68c12017-01-29 18:57:45 -050072 *state = K_POLL_STATE_FIFO_DATA_AVAILABLE;
73 return 1;
74 }
75 break;
76 case K_POLL_TYPE_SIGNAL:
77 if (event->signal->signaled) {
78 *state = K_POLL_STATE_SIGNALED;
79 return 1;
80 }
81 break;
82 case K_POLL_TYPE_IGNORE:
83 return 0;
84 default:
85 __ASSERT(0, "invalid event type (0x%x)\n", event->type);
86 break;
87 }
88
89 return 0;
90}
91
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +030092static inline void add_event(sys_dlist_t *events, struct k_poll_event *event,
93 struct _poller *poller)
94{
95 struct k_poll_event *pending;
96
97 pending = (struct k_poll_event *)sys_dlist_peek_tail(events);
98 if (!pending || _is_t1_higher_prio_than_t2(pending->poller->thread,
99 poller->thread)) {
100 sys_dlist_append(events, &event->_node);
101 return;
102 }
103
104 SYS_DLIST_FOR_EACH_CONTAINER(events, pending, _node) {
105 if (_is_t1_higher_prio_than_t2(poller->thread,
106 pending->poller->thread)) {
107 sys_dlist_insert_before(events, &pending->_node,
108 &event->_node);
109 return;
110 }
111 }
112
113 sys_dlist_append(events, &event->_node);
114}
115
Benjamin Walshacc68c12017-01-29 18:57:45 -0500116/* must be called with interrupts locked */
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300117static inline int register_event(struct k_poll_event *event,
118 struct _poller *poller)
Benjamin Walshacc68c12017-01-29 18:57:45 -0500119{
120 switch (event->type) {
121 case K_POLL_TYPE_SEM_AVAILABLE:
122 __ASSERT(event->sem, "invalid semaphore\n");
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300123 add_event(&event->sem->poll_events, event, poller);
Benjamin Walshacc68c12017-01-29 18:57:45 -0500124 break;
Luiz Augusto von Dentze5ed88f2017-02-21 15:27:20 +0200125 case K_POLL_TYPE_DATA_AVAILABLE:
126 __ASSERT(event->queue, "invalid queue\n");
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300127 add_event(&event->queue->poll_events, event, poller);
Benjamin Walshacc68c12017-01-29 18:57:45 -0500128 break;
129 case K_POLL_TYPE_SIGNAL:
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300130 __ASSERT(event->signal, "invalid poll signal\n");
131 add_event(&event->signal->poll_events, event, poller);
Benjamin Walshacc68c12017-01-29 18:57:45 -0500132 break;
133 case K_POLL_TYPE_IGNORE:
134 /* nothing to do */
135 break;
136 default:
137 __ASSERT(0, "invalid event type\n");
138 break;
139 }
140
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300141 event->poller = poller;
142
Benjamin Walshacc68c12017-01-29 18:57:45 -0500143 return 0;
144}
145
146/* must be called with interrupts locked */
147static inline void clear_event_registration(struct k_poll_event *event)
148{
149 event->poller = NULL;
150
151 switch (event->type) {
152 case K_POLL_TYPE_SEM_AVAILABLE:
153 __ASSERT(event->sem, "invalid semaphore\n");
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300154 sys_dlist_remove(&event->_node);
Benjamin Walshacc68c12017-01-29 18:57:45 -0500155 break;
Luiz Augusto von Dentze5ed88f2017-02-21 15:27:20 +0200156 case K_POLL_TYPE_DATA_AVAILABLE:
157 __ASSERT(event->queue, "invalid queue\n");
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300158 sys_dlist_remove(&event->_node);
Benjamin Walshacc68c12017-01-29 18:57:45 -0500159 break;
160 case K_POLL_TYPE_SIGNAL:
161 __ASSERT(event->signal, "invalid poll signal\n");
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300162 sys_dlist_remove(&event->_node);
Benjamin Walshacc68c12017-01-29 18:57:45 -0500163 break;
164 case K_POLL_TYPE_IGNORE:
165 /* nothing to do */
166 break;
167 default:
168 __ASSERT(0, "invalid event type\n");
169 break;
170 }
171}
172
173/* must be called with interrupts locked */
174static inline void clear_event_registrations(struct k_poll_event *events,
175 int last_registered,
176 unsigned int key)
177{
178 for (; last_registered >= 0; last_registered--) {
179 clear_event_registration(&events[last_registered]);
180 irq_unlock(key);
181 key = irq_lock();
182 }
183}
184
Kumar Galacc334c72017-04-21 10:55:34 -0500185static inline void set_event_ready(struct k_poll_event *event, u32_t state)
Benjamin Walshacc68c12017-01-29 18:57:45 -0500186{
187 event->poller = NULL;
188 event->state |= state;
189}
190
Kumar Galacc334c72017-04-21 10:55:34 -0500191int k_poll(struct k_poll_event *events, int num_events, s32_t timeout)
Benjamin Walshacc68c12017-01-29 18:57:45 -0500192{
193 __ASSERT(!_is_in_isr(), "");
194 __ASSERT(events, "NULL events\n");
195 __ASSERT(num_events > 0, "zero events\n");
196
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300197 int last_registered = -1, rc;
Benjamin Walshacc68c12017-01-29 18:57:45 -0500198 unsigned int key;
199
200 key = irq_lock();
201 set_polling_state(_current);
202 irq_unlock(key);
203
Benjamin Walshacc68c12017-01-29 18:57:45 -0500204 struct _poller poller = { .thread = _current };
205
206 /* find events whose condition is already fulfilled */
207 for (int ii = 0; ii < num_events; ii++) {
Kumar Galacc334c72017-04-21 10:55:34 -0500208 u32_t state;
Benjamin Walshacc68c12017-01-29 18:57:45 -0500209
210 key = irq_lock();
211 if (is_condition_met(&events[ii], &state)) {
212 set_event_ready(&events[ii], state);
213 clear_polling_state(_current);
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300214 } else if (timeout != K_NO_WAIT && is_polling()) {
215 rc = register_event(&events[ii], &poller);
Benjamin Walshacc68c12017-01-29 18:57:45 -0500216 if (rc == 0) {
Benjamin Walshacc68c12017-01-29 18:57:45 -0500217 ++last_registered;
Benjamin Walshacc68c12017-01-29 18:57:45 -0500218 } else {
219 __ASSERT(0, "unexpected return code\n");
220 }
221 }
222 irq_unlock(key);
223 }
224
225 key = irq_lock();
226
227 /*
228 * If we're not polling anymore, it means that at least one event
229 * condition is met, either when looping through the events here or
230 * because one of the events registered has had its state changed, or
231 * that one of the objects we wanted to poll on already had a thread
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300232 * polling on it.
Benjamin Walshacc68c12017-01-29 18:57:45 -0500233 */
Benjamin Walsh47503e32017-02-09 14:52:21 -0500234 if (!is_polling()) {
Benjamin Walshacc68c12017-01-29 18:57:45 -0500235 clear_event_registrations(events, last_registered, key);
236 irq_unlock(key);
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300237 return 0;
Benjamin Walshacc68c12017-01-29 18:57:45 -0500238 }
239
240 clear_polling_state(_current);
241
242 if (timeout == K_NO_WAIT) {
243 irq_unlock(key);
244 return -EAGAIN;
245 }
246
247 _wait_q_t wait_q = _WAIT_Q_INIT(&wait_q);
248
249 _pend_current_thread(&wait_q, timeout);
250
251 int swap_rc = _Swap(key);
252
253 /*
254 * Clear all event registrations. If events happen while we're in this
255 * loop, and we already had one that triggered, that's OK: they will
256 * end up in the list of events that are ready; if we timed out, and
257 * events happen while we're in this loop, that is OK as well since
258 * we've already know the return code (-EAGAIN), and even if they are
259 * added to the list of events that occurred, the user has to check the
260 * return code first, which invalidates the whole list of event states.
261 */
262 key = irq_lock();
263 clear_event_registrations(events, last_registered, key);
264 irq_unlock(key);
265
266 return swap_rc;
267}
268
269/* must be called with interrupts locked */
Kumar Galacc334c72017-04-21 10:55:34 -0500270static int _signal_poll_event(struct k_poll_event *event, u32_t state,
Benjamin Walshacc68c12017-01-29 18:57:45 -0500271 int *must_reschedule)
272{
273 *must_reschedule = 0;
274
275 if (!event->poller) {
276 goto ready_event;
277 }
278
279 struct k_thread *thread = event->poller->thread;
280
281 __ASSERT(event->poller->thread, "poller should have a thread\n");
282
283 clear_polling_state(thread);
284
285 if (!_is_thread_pending(thread)) {
286 goto ready_event;
287 }
288
289 if (_is_thread_timeout_expired(thread)) {
290 return -EAGAIN;
291 }
292
293 _unpend_thread(thread);
294 _abort_thread_timeout(thread);
Luiz Augusto von Dentzfc775a02017-10-17 15:33:32 +0300295 _set_thread_return_value(thread,
296 state == K_POLL_STATE_NOT_READY ? -EINTR : 0);
Benjamin Walshacc68c12017-01-29 18:57:45 -0500297
298 if (!_is_thread_ready(thread)) {
299 goto ready_event;
300 }
301
302 _add_thread_to_ready_q(thread);
303 *must_reschedule = !_is_in_isr() && _must_switch_threads();
304
305ready_event:
306 set_event_ready(event, state);
307 return 0;
308}
309
310/* returns 1 if a reschedule must take place, 0 otherwise */
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300311int _handle_obj_poll_events(sys_dlist_t *events, u32_t state)
Benjamin Walshacc68c12017-01-29 18:57:45 -0500312{
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300313 struct k_poll_event *poll_event;
Benjamin Walshacc68c12017-01-29 18:57:45 -0500314 int must_reschedule;
315
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300316 poll_event = (struct k_poll_event *)sys_dlist_get(events);
317 if (!poll_event) {
318 return 0;
319 }
320
Benjamin Walshacc68c12017-01-29 18:57:45 -0500321 (void)_signal_poll_event(poll_event, state, &must_reschedule);
322 return must_reschedule;
323}
324
Benjamin Walsha304f162017-02-02 16:46:09 -0500325void k_poll_signal_init(struct k_poll_signal *signal)
326{
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300327 sys_dlist_init(&signal->poll_events);
Benjamin Walsha304f162017-02-02 16:46:09 -0500328 signal->signaled = 0;
329 /* signal->result is left unitialized */
330}
331
Benjamin Walshacc68c12017-01-29 18:57:45 -0500332int k_poll_signal(struct k_poll_signal *signal, int result)
333{
334 unsigned int key = irq_lock();
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300335 struct k_poll_event *poll_event;
Benjamin Walshacc68c12017-01-29 18:57:45 -0500336 int must_reschedule;
337
338 signal->result = result;
Benjamin Walsh3c1ab5d2017-02-09 15:36:29 -0500339 signal->signaled = 1;
Benjamin Walshacc68c12017-01-29 18:57:45 -0500340
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300341 poll_event = (struct k_poll_event *)sys_dlist_get(&signal->poll_events);
342 if (!poll_event) {
Benjamin Walshacc68c12017-01-29 18:57:45 -0500343 irq_unlock(key);
344 return 0;
345 }
346
Luiz Augusto von Dentz7d01c5e2017-08-21 10:49:29 +0300347 int rc = _signal_poll_event(poll_event, K_POLL_STATE_SIGNALED,
Benjamin Walshacc68c12017-01-29 18:57:45 -0500348 &must_reschedule);
349
350 if (must_reschedule) {
351 (void)_Swap(key);
352 } else {
353 irq_unlock(key);
354 }
355
356 return rc;
357}