/*
ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
/**
* @addtogroup Events
* @{
*/
#include
#ifdef CH_USE_EVENTS
/**
* Registers an Event Listener on an Event Source.
* @param esp pointer to the \p EventSource structure
* @param elp pointer to the \p EventListener structure
* @param emask the mask of event flags to be pended to the thread when the
* event source is broadcasted
* @note Multiple Event Listeners can specify the same bits to be pended.
*/
void chEvtRegisterMask(EventSource *esp, EventListener *elp, eventmask_t emask) {
chSysLock();
elp->el_next = esp->es_next;
esp->es_next = elp;
elp->el_listener = currp;
elp->el_mask = emask;
chSysUnlock();
}
/**
* Unregisters an Event Listener from its Event Source.
* @param esp pointer to the \p EventSource structure
* @param elp pointer to the \p EventListener structure
* @note If the event listener is not registered on the specified event source
* then the function does nothing.
* @note For optimal performance it is better to perform the unregister
* operations in inverse order of the register operations (elements are
* found on top of the list).
*/
void chEvtUnregister(EventSource *esp, EventListener *elp) {
EventListener *p = (EventListener *)esp;
chSysLock();
while (p->el_next != (EventListener *)esp) {
if (p->el_next == elp) {
p->el_next = elp->el_next;
break;
}
p = p->el_next;
}
chSysUnlock();
}
/**
* Clears the pending events specified in the mask.
* @param mask the events to be cleared
* @return The pending events that were cleared.
*/
eventmask_t chEvtClear(eventmask_t mask) {
eventmask_t m;
chSysLock();
m = currp->p_epending & mask;
currp->p_epending &= ~mask;
chSysUnlock();
return m;
}
/**
* Makes an events mask pending in the current thread, this is \b much faster than
* using \p chEvtBreadcast().
* @param mask the events to be pended
* @return The current pending events mask.
*/
eventmask_t chEvtPend(eventmask_t mask) {
chSysLock();
mask = (currp->p_epending |= mask);
chSysUnlock();
return mask;
}
/**
* Signals all the Event Listeners registered on the specified Event Source.
* @param esp pointer to the \p EventSource structure
*/
void chEvtBroadcast(EventSource *esp) {
chSysLock();
chEvtBroadcastI(esp);
chSchRescheduleS();
chSysUnlock();
}
/**
* Signals all the Event Listeners registered on the specified Event Source.
* @param esp pointer to the \p EventSource structure
* @note This function does not reschedule.
*/
void chEvtBroadcastI(EventSource *esp) {
EventListener *elp;
elp = esp->es_next;
while (elp != (EventListener *)esp) {
Thread *tp = elp->el_listener;
tp->p_epending |= elp->el_mask;
/* Test on the AND/OR conditions wait states.*/
if (((tp->p_state == PRWTOREVT) && ((tp->p_epending & tp->p_ewmask) != 0)) ||
((tp->p_state == PRWTANDEVT) && ((tp->p_epending & tp->p_ewmask) == tp->p_ewmask)))
chSchReadyI(tp)->p_rdymsg = RDY_OK;
elp = elp->el_next;
}
}
/**
* Invokes the event handlers associated with a mask.
* @param mask mask of the events to be dispatched
* @param handlers an array of \p evhandler_t. The array must be
* have indexes from zero up the higher registered event
* identifier.
*/
void chEvtDispatch(const evhandler_t handlers[], eventmask_t mask) {
eventid_t eid;
eid = 0;
while (mask) {
if (mask & EVENT_MASK(eid)) {
mask &= ~EVENT_MASK(eid);
handlers[eid](eid);
}
eid++;
}
}
#if defined(CH_OPTIMIZE_SPEED) || !defined(CH_USE_EVENTS_TIMEOUT)
/**
* A pending event among those specified in \p ewmask is selected, cleared and
* its mask returned.
* @param ewmask mask of the events that the function should wait for,
* \p ALL_EVENTS enables all the events
* @return The mask of the lowest id served and cleared event.
* @note Only a single event is served in the function, the one with the
* lowest event id. The function is meant to be invoked into a loop in
* order to serve all the pending events.
* This means that Event Listeners with a lower event identifier have
* an higher priority.
*/
eventmask_t chEvtWaitOne(eventmask_t ewmask) {
eventmask_t m;
chSysLock();
if ((m = (currp->p_epending & ewmask)) == 0) {
currp->p_ewmask = ewmask;
chSchGoSleepS(PRWTOREVT);
m = currp->p_epending & ewmask;
}
m &= -m;
currp->p_epending &= ~m;
chSysUnlock();
return m;
}
/**
* Waits for any of the specified events.
* The function waits for any event among those specified in \p ewmask to
* become pending then the events are cleared and returned.
* @param ewmask mask of the events that the function should wait for,
* \p ALL_EVENTS enables all the events
* @return The mask of the served and cleared events.
*/
eventmask_t chEvtWaitAny(eventmask_t ewmask) {
eventmask_t m;
chSysLock();
if ((m = (currp->p_epending & ewmask)) == 0) {
currp->p_ewmask = ewmask;
chSchGoSleepS(PRWTOREVT);
m = currp->p_epending & ewmask;
}
currp->p_epending &= ~m;
chSysUnlock();
return m;
}
/**
* Waits for all the specified event flags then clears them.
* The function waits for all the events specified in \p ewmask to become
* pending then the events are cleared and returned.
* @param ewmask mask of the event ids that the function should wait for
* @return The mask of the served and cleared events.
*/
eventmask_t chEvtWaitAll(eventmask_t ewmask) {
chSysLock();
if ((currp->p_epending & ewmask) != ewmask) {
currp->p_ewmask = ewmask;
chSchGoSleepS(PRWTANDEVT);
}
currp->p_epending &= ~ewmask;
chSysUnlock();
return ewmask;
}
/**
* The function waits for an event and returns the event identifier, if an
* event handler is specified then the handler is executed before returning.
* @param ewmask mask of the events that should be served by the function,
* \p ALL_EVENTS enables all the sources
* @param handlers an array of \p evhandler_t. The array must be
* have indexes from zero up the higher registered event
* identifier. The array can be \p NULL or contain \p NULL
* elements (no callbacks specified).
* @return The event identifier.
* @note Only a single event is served in the function, the one with the
* lowest event id. The function is meant to be invoked into a loop so
* that all events are received and served.
* This means that Event Listeners with a lower event identifier have
* an higher priority.
* @deprecated Please use \p chEvtWaitOne() and \p chEvtDispatch() instead,
* this function will be removed in version 1.0.0.
*/
eventid_t chEvtWait(eventmask_t ewmask,
const evhandler_t handlers[]) {
eventid_t i;
eventmask_t m;
chSysLock();
if ((currp->p_epending & ewmask) == 0) {
currp->p_ewmask = ewmask;
chSchGoSleepS(PRWTOREVT);
}
i = 0, m = 1;
while ((currp->p_epending & ewmask & m) == 0)
i += 1, m <<= 1;
currp->p_epending &= ~m;
chSysUnlock();
if (handlers && handlers[i])
handlers[i](i);
return i;
}
#endif /* defined(CH_OPTIMIZE_SPEED) || !defined(CH_USE_EVENTS_TIMEOUT) */
#ifdef CH_USE_EVENTS_TIMEOUT
/**
* Waits for a single event.
* A pending event among those specified in \p ewmask is selected, cleared and
* its mask returned.
* @param ewmask mask of the events that the function should wait for,
* \p ALL_EVENTS enables all the events
* @param time the number of ticks before the operation timouts
* @return The mask of the lowest id served and cleared event.
* @retval 0 if the specified timeout expired
* @note Only a single event is served in the function, the one with the
* lowest event id. The function is meant to be invoked into a loop in
* order to serve all the pending events.
* This means that Event Listeners with a lower event identifier have
* an higher priority.
*/
eventmask_t chEvtWaitOneTimeout(eventmask_t ewmask, systime_t time) {
eventmask_t m;
chSysLock();
if ((m = (currp->p_epending & ewmask)) == 0) {
currp->p_ewmask = ewmask;
if (chSchGoSleepTimeoutS(PRWTOREVT, time) < RDY_OK)
return (eventmask_t)0;
m = currp->p_epending & ewmask;
}
m &= -m;
currp->p_epending &= ~m;
chSysUnlock();
return m;
}
/**
* Waits for any of the specified events.
* The function waits for any event among those specified in \p ewmask to
* become pending then the events are cleared and returned.
* @param ewmask mask of the events that the function should wait for,
* \p ALL_EVENTS enables all the events
* @param time the number of ticks before the operation timouts
* @return The mask of the served and cleared events.
* @retval 0 if the specified timeout expired
*/
eventmask_t chEvtWaitAnyTimeout(eventmask_t ewmask, systime_t time) {
eventmask_t m;
chSysLock();
if ((m = (currp->p_epending & ewmask)) == 0) {
currp->p_ewmask = ewmask;
if (chSchGoSleepTimeoutS(PRWTOREVT, time) < RDY_OK)
return (eventmask_t)0;
m = currp->p_epending & ewmask;
}
currp->p_epending &= ~m;
chSysUnlock();
return m;
}
/**
* Waits for all the specified event flags then clears them.
* The function waits for all the events specified in \p ewmask to become
* pending then the events are cleared and returned.
* @param ewmask mask of the event ids that the function should wait for
* @param time the number of ticks before the operation timouts
* @return The mask of the served and cleared events.
* @retval 0 if the specified timeout expired
*/
eventmask_t chEvtWaitAllTimeout(eventmask_t ewmask, systime_t time) {
chSysLock();
if ((currp->p_epending & ewmask) != ewmask) {
currp->p_ewmask = ewmask;
if (chSchGoSleepTimeoutS(PRWTANDEVT, time) < RDY_OK)
return (eventmask_t)0;
}
currp->p_epending &= ~ewmask;
chSysUnlock();
return ewmask;
}
/**
* The function waits for an event or the specified timeout then returns the
* event identifier, if an event handler is specified then the handler is
* executed before returning.
* @param ewmask mask of the events that should be served by the function,
* \p ALL_EVENTS enables all the sources
* @param handlers an array of \p evhandler_t. The array must be
* have indexes from zero up the higher registered event
* identifier. The array can be NULL or contain NULL elements
* (no callback specified).
* @param time the number of ticks before the operation timouts
* @return The event identifier.
* @retval RDY_TIMEOUT if the specified timeout expired.
* @note Only a single event is served in the function, the one with the
* lowest event id. The function is meant to be invoked into a loop so
* that all events are received and served.
* This means that Event Listeners with a lower event identifier have
* an higher priority.
* @deprecated Please use \p chEvtWaitOneTimeout() and \p chEvtDispatch()
* instead, this function will be removed in version 1.0.0.
*/
eventid_t chEvtWaitTimeout(eventmask_t ewmask,
const evhandler_t handlers[],
systime_t time) {
eventid_t i;
eventmask_t m;
chSysLock();
if ((currp->p_epending & ewmask) == 0) {
currp->p_ewmask = ewmask;
if (chSchGoSleepTimeoutS(PRWTOREVT, time) < RDY_OK)
return RDY_TIMEOUT;
}
i = 0, m = 1;
while ((currp->p_epending & ewmask & m) == 0)
i += 1, m <<= 1;
currp->p_epending &= ~m;
chSysUnlock();
if (handlers && handlers[i])
handlers[i](i);
return i;
}
#endif /* CH_USE_EVENTS_TIMEOUT */
#endif /* CH_USE_EVENTS */
/** @} */