Forked to avoid changing the publioc repo

Dependencies:   BLE_API mbed-dev-bin nRF51822

Fork of microbit-dal by Lancaster University

Committer:
bluetooth_mdw
Date:
Thu Apr 20 13:42:44 2017 +0000
Revision:
74:4c7db2d5ca9a
Parent:
41:da05ec75cd5d
Example app showing how to include the Bluetooth accelerometer service in your micro:bit application code.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Jonathan Austin 1:8aa5cdb4ab67 1 /*
Jonathan Austin 1:8aa5cdb4ab67 2 The MIT License (MIT)
Jonathan Austin 1:8aa5cdb4ab67 3
Jonathan Austin 1:8aa5cdb4ab67 4 Copyright (c) 2016 British Broadcasting Corporation.
Jonathan Austin 1:8aa5cdb4ab67 5 This software is provided by Lancaster University by arrangement with the BBC.
Jonathan Austin 1:8aa5cdb4ab67 6
Jonathan Austin 1:8aa5cdb4ab67 7 Permission is hereby granted, free of charge, to any person obtaining a
Jonathan Austin 1:8aa5cdb4ab67 8 copy of this software and associated documentation files (the "Software"),
Jonathan Austin 1:8aa5cdb4ab67 9 to deal in the Software without restriction, including without limitation
Jonathan Austin 1:8aa5cdb4ab67 10 the rights to use, copy, modify, merge, publish, distribute, sublicense,
Jonathan Austin 1:8aa5cdb4ab67 11 and/or sell copies of the Software, and to permit persons to whom the
Jonathan Austin 1:8aa5cdb4ab67 12 Software is furnished to do so, subject to the following conditions:
Jonathan Austin 1:8aa5cdb4ab67 13
Jonathan Austin 1:8aa5cdb4ab67 14 The above copyright notice and this permission notice shall be included in
Jonathan Austin 1:8aa5cdb4ab67 15 all copies or substantial portions of the Software.
Jonathan Austin 1:8aa5cdb4ab67 16
Jonathan Austin 1:8aa5cdb4ab67 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Jonathan Austin 1:8aa5cdb4ab67 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Jonathan Austin 1:8aa5cdb4ab67 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
Jonathan Austin 1:8aa5cdb4ab67 20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Jonathan Austin 1:8aa5cdb4ab67 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
Jonathan Austin 1:8aa5cdb4ab67 22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
Jonathan Austin 1:8aa5cdb4ab67 23 DEALINGS IN THE SOFTWARE.
Jonathan Austin 1:8aa5cdb4ab67 24 */
Jonathan Austin 1:8aa5cdb4ab67 25
Jonathan Austin 1:8aa5cdb4ab67 26 /**
Jonathan Austin 1:8aa5cdb4ab67 27 * Class definition for the MicroBitMessageBus.
Jonathan Austin 1:8aa5cdb4ab67 28 *
Jonathan Austin 1:8aa5cdb4ab67 29 * The MicroBitMessageBus is the common mechanism to deliver asynchronous events on the
Jonathan Austin 1:8aa5cdb4ab67 30 * MicroBit platform. It serves a number of purposes:
Jonathan Austin 1:8aa5cdb4ab67 31 *
Jonathan Austin 1:8aa5cdb4ab67 32 * 1) It provides an eventing abstraction that is independent of the underlying substrate.
Jonathan Austin 1:8aa5cdb4ab67 33 *
Jonathan Austin 1:8aa5cdb4ab67 34 * 2) It provides a mechanism to decouple user code from trusted system code
Jonathan Austin 1:8aa5cdb4ab67 35 * i.e. the basis of a message passing nano kernel.
Jonathan Austin 1:8aa5cdb4ab67 36 *
Jonathan Austin 1:8aa5cdb4ab67 37 * 3) It allows a common high level eventing abstraction across a range of hardware types.e.g. buttons, BLE...
Jonathan Austin 1:8aa5cdb4ab67 38 *
Jonathan Austin 1:8aa5cdb4ab67 39 * 4) It provides a mechanim for extensibility - new devices added via I/O pins can have OO based
Jonathan Austin 1:8aa5cdb4ab67 40 * drivers and communicate via the message bus with minima impact on user level languages.
Jonathan Austin 1:8aa5cdb4ab67 41 *
Jonathan Austin 1:8aa5cdb4ab67 42 * 5) It allows for the possiblility of event / data aggregation, which in turn can save energy.
Jonathan Austin 1:8aa5cdb4ab67 43 *
Jonathan Austin 1:8aa5cdb4ab67 44 * It has the following design principles:
Jonathan Austin 1:8aa5cdb4ab67 45 *
Jonathan Austin 1:8aa5cdb4ab67 46 * 1) Maintain a low RAM footprint where possible
Jonathan Austin 1:8aa5cdb4ab67 47 *
Jonathan Austin 1:8aa5cdb4ab67 48 * 2) Make few assumptions about the underlying platform, but allow optimizations where possible.
Jonathan Austin 1:8aa5cdb4ab67 49 */
Jonathan Austin 1:8aa5cdb4ab67 50 #include "MicroBitConfig.h"
Jonathan Austin 1:8aa5cdb4ab67 51 #include "MicroBitMessageBus.h"
Jonathan Austin 1:8aa5cdb4ab67 52 #include "MicroBitFiber.h"
Jonathan Austin 1:8aa5cdb4ab67 53 #include "ErrorNo.h"
Jonathan Austin 1:8aa5cdb4ab67 54
Jonathan Austin 1:8aa5cdb4ab67 55 /**
Jonathan Austin 1:8aa5cdb4ab67 56 * Default constructor.
Jonathan Austin 1:8aa5cdb4ab67 57 *
Jonathan Austin 1:8aa5cdb4ab67 58 * Adds itself as a fiber component, and also configures itself to be the
Jonathan Austin 1:8aa5cdb4ab67 59 * default EventModel if defaultEventBus is NULL.
Jonathan Austin 1:8aa5cdb4ab67 60 */
Jonathan Austin 1:8aa5cdb4ab67 61 MicroBitMessageBus::MicroBitMessageBus()
Jonathan Austin 1:8aa5cdb4ab67 62 {
Jonathan Austin 1:8aa5cdb4ab67 63 this->listeners = NULL;
Jonathan Austin 1:8aa5cdb4ab67 64 this->evt_queue_head = NULL;
Jonathan Austin 1:8aa5cdb4ab67 65 this->evt_queue_tail = NULL;
Jonathan Austin 1:8aa5cdb4ab67 66 this->queueLength = 0;
Jonathan Austin 1:8aa5cdb4ab67 67
Jonathan Austin 1:8aa5cdb4ab67 68 fiber_add_idle_component(this);
Jonathan Austin 1:8aa5cdb4ab67 69
Jonathan Austin 1:8aa5cdb4ab67 70 if(EventModel::defaultEventBus == NULL)
Jonathan Austin 1:8aa5cdb4ab67 71 EventModel::defaultEventBus = this;
Jonathan Austin 1:8aa5cdb4ab67 72 }
Jonathan Austin 1:8aa5cdb4ab67 73
Jonathan Austin 1:8aa5cdb4ab67 74 /**
Jonathan Austin 1:8aa5cdb4ab67 75 * Invokes a callback on a given MicroBitListener
Jonathan Austin 1:8aa5cdb4ab67 76 *
Jonathan Austin 1:8aa5cdb4ab67 77 * Internal wrapper function, used to enable
Jonathan Austin 1:8aa5cdb4ab67 78 * parameterised callbacks through the fiber scheduler.
Jonathan Austin 1:8aa5cdb4ab67 79 */
Jonathan Austin 1:8aa5cdb4ab67 80 void async_callback(void *param)
Jonathan Austin 1:8aa5cdb4ab67 81 {
Jonathan Austin 1:8aa5cdb4ab67 82 MicroBitListener *listener = (MicroBitListener *)param;
Jonathan Austin 1:8aa5cdb4ab67 83
Jonathan Austin 1:8aa5cdb4ab67 84 // OK, now we need to decide how to behave depending on our configuration.
Jonathan Austin 1:8aa5cdb4ab67 85 // If this a fiber f already active within this listener then check our
Jonathan Austin 1:8aa5cdb4ab67 86 // configuration to determine the correct course of action.
Jonathan Austin 1:8aa5cdb4ab67 87 //
Jonathan Austin 1:8aa5cdb4ab67 88
Jonathan Austin 1:8aa5cdb4ab67 89 if (listener->flags & MESSAGE_BUS_LISTENER_BUSY)
Jonathan Austin 1:8aa5cdb4ab67 90 {
Jonathan Austin 1:8aa5cdb4ab67 91 // Drop this event, if that's how we've been configured.
Jonathan Austin 1:8aa5cdb4ab67 92 if (listener->flags & MESSAGE_BUS_LISTENER_DROP_IF_BUSY)
Jonathan Austin 1:8aa5cdb4ab67 93 return;
Jonathan Austin 1:8aa5cdb4ab67 94
Jonathan Austin 1:8aa5cdb4ab67 95 // Queue this event up for later, if that's how we've been configured.
Jonathan Austin 1:8aa5cdb4ab67 96 if (listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY)
Jonathan Austin 1:8aa5cdb4ab67 97 {
Jonathan Austin 1:8aa5cdb4ab67 98 listener->queue(listener->evt);
Jonathan Austin 1:8aa5cdb4ab67 99 return;
Jonathan Austin 1:8aa5cdb4ab67 100 }
Jonathan Austin 1:8aa5cdb4ab67 101 }
Jonathan Austin 1:8aa5cdb4ab67 102
Jonathan Austin 1:8aa5cdb4ab67 103 // Determine the calling convention for the callback, and invoke...
Jonathan Austin 1:8aa5cdb4ab67 104 // C++ is really bad at this! Especially as the ARM compiler is yet to support C++ 11 :-/
Jonathan Austin 1:8aa5cdb4ab67 105
Jonathan Austin 1:8aa5cdb4ab67 106 // Record that we have a fiber going into this listener...
Jonathan Austin 1:8aa5cdb4ab67 107 listener->flags |= MESSAGE_BUS_LISTENER_BUSY;
Jonathan Austin 1:8aa5cdb4ab67 108
Jonathan Austin 1:8aa5cdb4ab67 109 while (1)
Jonathan Austin 1:8aa5cdb4ab67 110 {
Jonathan Austin 1:8aa5cdb4ab67 111 // Firstly, check for a method callback into an object.
Jonathan Austin 1:8aa5cdb4ab67 112 if (listener->flags & MESSAGE_BUS_LISTENER_METHOD)
Jonathan Austin 1:8aa5cdb4ab67 113 listener->cb_method->fire(listener->evt);
Jonathan Austin 1:8aa5cdb4ab67 114
Jonathan Austin 1:8aa5cdb4ab67 115 // Now a parameterised C function
Jonathan Austin 1:8aa5cdb4ab67 116 else if (listener->flags & MESSAGE_BUS_LISTENER_PARAMETERISED)
Jonathan Austin 1:8aa5cdb4ab67 117 listener->cb_param(listener->evt, listener->cb_arg);
Jonathan Austin 1:8aa5cdb4ab67 118
Jonathan Austin 1:8aa5cdb4ab67 119 // We must have a plain C function
Jonathan Austin 1:8aa5cdb4ab67 120 else
Jonathan Austin 1:8aa5cdb4ab67 121 listener->cb(listener->evt);
Jonathan Austin 1:8aa5cdb4ab67 122
Jonathan Austin 1:8aa5cdb4ab67 123 // If there are more events to process, dequeue the next one and process it.
Jonathan Austin 1:8aa5cdb4ab67 124 if ((listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY) && listener->evt_queue)
Jonathan Austin 1:8aa5cdb4ab67 125 {
Jonathan Austin 1:8aa5cdb4ab67 126 MicroBitEventQueueItem *item = listener->evt_queue;
Jonathan Austin 1:8aa5cdb4ab67 127
Jonathan Austin 1:8aa5cdb4ab67 128 listener->evt = item->evt;
Jonathan Austin 1:8aa5cdb4ab67 129 listener->evt_queue = listener->evt_queue->next;
Jonathan Austin 1:8aa5cdb4ab67 130 delete item;
Jonathan Austin 1:8aa5cdb4ab67 131
Jonathan Austin 1:8aa5cdb4ab67 132 // We spin the scheduler here, to preven any particular event handler from continuously holding onto resources.
Jonathan Austin 1:8aa5cdb4ab67 133 schedule();
Jonathan Austin 1:8aa5cdb4ab67 134 }
Jonathan Austin 1:8aa5cdb4ab67 135 else
Jonathan Austin 1:8aa5cdb4ab67 136 break;
Jonathan Austin 1:8aa5cdb4ab67 137 }
Jonathan Austin 1:8aa5cdb4ab67 138
Jonathan Austin 1:8aa5cdb4ab67 139 // The fiber of exiting... clear our state.
Jonathan Austin 1:8aa5cdb4ab67 140 listener->flags &= ~MESSAGE_BUS_LISTENER_BUSY;
Jonathan Austin 1:8aa5cdb4ab67 141 }
Jonathan Austin 1:8aa5cdb4ab67 142
Jonathan Austin 1:8aa5cdb4ab67 143 /**
Jonathan Austin 1:8aa5cdb4ab67 144 * Queue the given event for processing at a later time.
Jonathan Austin 1:8aa5cdb4ab67 145 * Add the given event at the tail of our queue.
Jonathan Austin 1:8aa5cdb4ab67 146 *
Jonathan Austin 1:8aa5cdb4ab67 147 * @param The event to queue.
Jonathan Austin 1:8aa5cdb4ab67 148 */
Jonathan Austin 1:8aa5cdb4ab67 149 void MicroBitMessageBus::queueEvent(MicroBitEvent &evt)
Jonathan Austin 1:8aa5cdb4ab67 150 {
Jonathan Austin 1:8aa5cdb4ab67 151 int processingComplete;
Jonathan Austin 1:8aa5cdb4ab67 152
Jonathan Austin 1:8aa5cdb4ab67 153 MicroBitEventQueueItem *prev = evt_queue_tail;
Jonathan Austin 1:8aa5cdb4ab67 154
Jonathan Austin 1:8aa5cdb4ab67 155 // Now process all handler regsitered as URGENT.
Jonathan Austin 1:8aa5cdb4ab67 156 // These pre-empt the queue, and are useful for fast, high priority services.
Jonathan Austin 1:8aa5cdb4ab67 157 processingComplete = this->process(evt, true);
Jonathan Austin 1:8aa5cdb4ab67 158
Jonathan Austin 1:8aa5cdb4ab67 159 // If we've already processed all event handlers, we're all done.
Jonathan Austin 1:8aa5cdb4ab67 160 // No need to queue the event.
Jonathan Austin 1:8aa5cdb4ab67 161 if (processingComplete)
Jonathan Austin 1:8aa5cdb4ab67 162 return;
Jonathan Austin 1:8aa5cdb4ab67 163
Jonathan Austin 1:8aa5cdb4ab67 164 // If we need to queue, but there is no space, then there's nothg we can do.
Jonathan Austin 1:8aa5cdb4ab67 165 if (queueLength >= MESSAGE_BUS_LISTENER_MAX_QUEUE_DEPTH)
Jonathan Austin 1:8aa5cdb4ab67 166 return;
Jonathan Austin 1:8aa5cdb4ab67 167
Jonathan Austin 1:8aa5cdb4ab67 168 // Otherwise, we need to queue this event for later processing...
Jonathan Austin 1:8aa5cdb4ab67 169 // We queue this event at the tail of the queue at the point where we entered queueEvent()
Jonathan Austin 1:8aa5cdb4ab67 170 // This is important as the processing above *may* have generated further events, and
Jonathan Austin 1:8aa5cdb4ab67 171 // we want to maintain ordering of events.
Jonathan Austin 1:8aa5cdb4ab67 172 MicroBitEventQueueItem *item = new MicroBitEventQueueItem(evt);
Jonathan Austin 1:8aa5cdb4ab67 173
Jonathan Austin 1:8aa5cdb4ab67 174 // The queue was empty when we entered this function, so queue our event at the start of the queue.
Jonathan Austin 1:8aa5cdb4ab67 175 __disable_irq();
Jonathan Austin 1:8aa5cdb4ab67 176
Jonathan Austin 1:8aa5cdb4ab67 177 if (prev == NULL)
Jonathan Austin 1:8aa5cdb4ab67 178 {
Jonathan Austin 1:8aa5cdb4ab67 179 item->next = evt_queue_head;
Jonathan Austin 1:8aa5cdb4ab67 180 evt_queue_head = item;
Jonathan Austin 1:8aa5cdb4ab67 181 }
Jonathan Austin 1:8aa5cdb4ab67 182 else
Jonathan Austin 1:8aa5cdb4ab67 183 {
Jonathan Austin 1:8aa5cdb4ab67 184 item->next = prev->next;
Jonathan Austin 1:8aa5cdb4ab67 185 prev->next = item;
Jonathan Austin 1:8aa5cdb4ab67 186 }
Jonathan Austin 1:8aa5cdb4ab67 187
Jonathan Austin 1:8aa5cdb4ab67 188 if (item->next == NULL)
Jonathan Austin 1:8aa5cdb4ab67 189 evt_queue_tail = item;
Jonathan Austin 1:8aa5cdb4ab67 190
Jonathan Austin 1:8aa5cdb4ab67 191 queueLength++;
Jonathan Austin 1:8aa5cdb4ab67 192
Jonathan Austin 1:8aa5cdb4ab67 193 __enable_irq();
Jonathan Austin 1:8aa5cdb4ab67 194 }
Jonathan Austin 1:8aa5cdb4ab67 195
Jonathan Austin 1:8aa5cdb4ab67 196 /**
Jonathan Austin 1:8aa5cdb4ab67 197 * Extract the next event from the front of the event queue (if present).
Jonathan Austin 1:8aa5cdb4ab67 198 *
Jonathan Austin 1:8aa5cdb4ab67 199 * @return a pointer to the MicroBitEventQueueItem that is at the head of the list.
Jonathan Austin 1:8aa5cdb4ab67 200 */
Jonathan Austin 1:8aa5cdb4ab67 201 MicroBitEventQueueItem* MicroBitMessageBus::dequeueEvent()
Jonathan Austin 1:8aa5cdb4ab67 202 {
Jonathan Austin 1:8aa5cdb4ab67 203 MicroBitEventQueueItem *item = NULL;
Jonathan Austin 1:8aa5cdb4ab67 204
Jonathan Austin 1:8aa5cdb4ab67 205 __disable_irq();
Jonathan Austin 1:8aa5cdb4ab67 206
Jonathan Austin 1:8aa5cdb4ab67 207 if (evt_queue_head != NULL)
Jonathan Austin 1:8aa5cdb4ab67 208 {
Jonathan Austin 1:8aa5cdb4ab67 209 item = evt_queue_head;
Jonathan Austin 1:8aa5cdb4ab67 210 evt_queue_head = item->next;
Jonathan Austin 1:8aa5cdb4ab67 211
Jonathan Austin 1:8aa5cdb4ab67 212 if (evt_queue_head == NULL)
Jonathan Austin 1:8aa5cdb4ab67 213 evt_queue_tail = NULL;
Jonathan Austin 1:8aa5cdb4ab67 214
Jonathan Austin 1:8aa5cdb4ab67 215 queueLength--;
Jonathan Austin 1:8aa5cdb4ab67 216 }
Jonathan Austin 1:8aa5cdb4ab67 217
Jonathan Austin 1:8aa5cdb4ab67 218 __enable_irq();
Jonathan Austin 1:8aa5cdb4ab67 219
Jonathan Austin 1:8aa5cdb4ab67 220
Jonathan Austin 1:8aa5cdb4ab67 221 return item;
Jonathan Austin 1:8aa5cdb4ab67 222 }
Jonathan Austin 1:8aa5cdb4ab67 223
Jonathan Austin 1:8aa5cdb4ab67 224 /**
Jonathan Austin 1:8aa5cdb4ab67 225 * Cleanup any MicroBitListeners marked for deletion from the list.
Jonathan Austin 1:8aa5cdb4ab67 226 *
Jonathan Austin 1:8aa5cdb4ab67 227 * @return The number of listeners removed from the list.
Jonathan Austin 1:8aa5cdb4ab67 228 */
Jonathan Austin 1:8aa5cdb4ab67 229 int MicroBitMessageBus::deleteMarkedListeners()
Jonathan Austin 1:8aa5cdb4ab67 230 {
Jonathan Austin 1:8aa5cdb4ab67 231 MicroBitListener *l, *p;
Jonathan Austin 1:8aa5cdb4ab67 232 int removed = 0;
Jonathan Austin 1:8aa5cdb4ab67 233
Jonathan Austin 1:8aa5cdb4ab67 234 l = listeners;
Jonathan Austin 1:8aa5cdb4ab67 235 p = NULL;
Jonathan Austin 1:8aa5cdb4ab67 236
Jonathan Austin 1:8aa5cdb4ab67 237 // Walk this list of event handlers. Delete any that match the given listener.
Jonathan Austin 1:8aa5cdb4ab67 238 while (l != NULL)
Jonathan Austin 1:8aa5cdb4ab67 239 {
LancasterUniversity 37:b624ae5e94a5 240 if ((l->flags & MESSAGE_BUS_LISTENER_DELETING) && !(l->flags & MESSAGE_BUS_LISTENER_BUSY))
Jonathan Austin 1:8aa5cdb4ab67 241 {
Jonathan Austin 1:8aa5cdb4ab67 242 if (p == NULL)
Jonathan Austin 1:8aa5cdb4ab67 243 listeners = l->next;
Jonathan Austin 1:8aa5cdb4ab67 244 else
Jonathan Austin 1:8aa5cdb4ab67 245 p->next = l->next;
Jonathan Austin 1:8aa5cdb4ab67 246
Jonathan Austin 1:8aa5cdb4ab67 247 // delete the listener.
Jonathan Austin 1:8aa5cdb4ab67 248 MicroBitListener *t = l;
Jonathan Austin 1:8aa5cdb4ab67 249 l = l->next;
Jonathan Austin 1:8aa5cdb4ab67 250
Jonathan Austin 1:8aa5cdb4ab67 251 delete t;
Jonathan Austin 1:8aa5cdb4ab67 252 removed++;
Jonathan Austin 1:8aa5cdb4ab67 253
Jonathan Austin 1:8aa5cdb4ab67 254 continue;
Jonathan Austin 1:8aa5cdb4ab67 255 }
Jonathan Austin 1:8aa5cdb4ab67 256
Jonathan Austin 1:8aa5cdb4ab67 257 p = l;
Jonathan Austin 1:8aa5cdb4ab67 258 l = l->next;
Jonathan Austin 1:8aa5cdb4ab67 259 }
Jonathan Austin 1:8aa5cdb4ab67 260
Jonathan Austin 1:8aa5cdb4ab67 261 return removed;
Jonathan Austin 1:8aa5cdb4ab67 262 }
Jonathan Austin 1:8aa5cdb4ab67 263
Jonathan Austin 1:8aa5cdb4ab67 264 /**
Jonathan Austin 1:8aa5cdb4ab67 265 * Periodic callback from MicroBit.
Jonathan Austin 1:8aa5cdb4ab67 266 *
Jonathan Austin 1:8aa5cdb4ab67 267 * Process at least one event from the event queue, if it is not empty.
Jonathan Austin 1:8aa5cdb4ab67 268 * We then continue processing events until something appears on the runqueue.
Jonathan Austin 1:8aa5cdb4ab67 269 */
Jonathan Austin 1:8aa5cdb4ab67 270 void MicroBitMessageBus::idleTick()
Jonathan Austin 1:8aa5cdb4ab67 271 {
Jonathan Austin 1:8aa5cdb4ab67 272 // Clear out any listeners marked for deletion
Jonathan Austin 1:8aa5cdb4ab67 273 this->deleteMarkedListeners();
Jonathan Austin 1:8aa5cdb4ab67 274
Jonathan Austin 1:8aa5cdb4ab67 275 MicroBitEventQueueItem *item = this->dequeueEvent();
Jonathan Austin 1:8aa5cdb4ab67 276
Jonathan Austin 1:8aa5cdb4ab67 277 // Whilst there are events to process and we have no useful other work to do, pull them off the queue and process them.
Jonathan Austin 1:8aa5cdb4ab67 278 while (item)
Jonathan Austin 1:8aa5cdb4ab67 279 {
Jonathan Austin 1:8aa5cdb4ab67 280 // send the event to all standard event listeners.
Jonathan Austin 1:8aa5cdb4ab67 281 this->process(item->evt);
Jonathan Austin 1:8aa5cdb4ab67 282
Jonathan Austin 1:8aa5cdb4ab67 283 // Free the queue item.
Jonathan Austin 1:8aa5cdb4ab67 284 delete item;
Jonathan Austin 1:8aa5cdb4ab67 285
Jonathan Austin 1:8aa5cdb4ab67 286 // If we have created some useful work to do, we stop processing.
Jonathan Austin 1:8aa5cdb4ab67 287 // This helps to minimise the number of blocked fibers we create at any point in time, therefore
Jonathan Austin 1:8aa5cdb4ab67 288 // also reducing the RAM footprint.
Jonathan Austin 1:8aa5cdb4ab67 289 if(!scheduler_runqueue_empty())
Jonathan Austin 1:8aa5cdb4ab67 290 break;
Jonathan Austin 1:8aa5cdb4ab67 291
Jonathan Austin 1:8aa5cdb4ab67 292 // Pull the next event to process, if there is one.
Jonathan Austin 1:8aa5cdb4ab67 293 item = this->dequeueEvent();
Jonathan Austin 1:8aa5cdb4ab67 294 }
Jonathan Austin 1:8aa5cdb4ab67 295 }
Jonathan Austin 1:8aa5cdb4ab67 296
Jonathan Austin 1:8aa5cdb4ab67 297 /**
Jonathan Austin 1:8aa5cdb4ab67 298 * Queues the given event to be sent to all registered recipients.
Jonathan Austin 1:8aa5cdb4ab67 299 *
Jonathan Austin 1:8aa5cdb4ab67 300 * @param evt The event to send.
Jonathan Austin 1:8aa5cdb4ab67 301 *
Jonathan Austin 1:8aa5cdb4ab67 302 * @code
Jonathan Austin 1:8aa5cdb4ab67 303 * MicroBitMessageBus bus;
Jonathan Austin 1:8aa5cdb4ab67 304 *
Jonathan Austin 1:8aa5cdb4ab67 305 * // Creates and sends the MicroBitEvent using bus.
Jonathan Austin 1:8aa5cdb4ab67 306 * MicrobitEvent evt(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK);
Jonathan Austin 1:8aa5cdb4ab67 307 *
Jonathan Austin 1:8aa5cdb4ab67 308 * // Creates the MicrobitEvent, but delays the sending of that event.
Jonathan Austin 1:8aa5cdb4ab67 309 * MicrobitEvent evt1(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, CREATE_ONLY);
Jonathan Austin 1:8aa5cdb4ab67 310 *
Jonathan Austin 1:8aa5cdb4ab67 311 * bus.send(evt1);
Jonathan Austin 1:8aa5cdb4ab67 312 *
Jonathan Austin 1:8aa5cdb4ab67 313 * // This has the same effect!
Jonathan Austin 1:8aa5cdb4ab67 314 * evt1.fire()
Jonathan Austin 1:8aa5cdb4ab67 315 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 316 */
Jonathan Austin 1:8aa5cdb4ab67 317 int MicroBitMessageBus::send(MicroBitEvent evt)
Jonathan Austin 1:8aa5cdb4ab67 318 {
Jonathan Austin 1:8aa5cdb4ab67 319 // We simply queue processing of the event until we're scheduled in normal thread context.
Jonathan Austin 1:8aa5cdb4ab67 320 // We do this to avoid the possibility of executing event handler code in IRQ context, which may bring
Jonathan Austin 1:8aa5cdb4ab67 321 // hidden race conditions to kids code. Queuing all events ensures causal ordering (total ordering in fact).
Jonathan Austin 1:8aa5cdb4ab67 322 this->queueEvent(evt);
Jonathan Austin 1:8aa5cdb4ab67 323 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 324 }
Jonathan Austin 1:8aa5cdb4ab67 325
Jonathan Austin 1:8aa5cdb4ab67 326 /**
Jonathan Austin 1:8aa5cdb4ab67 327 * Internal function, used to deliver the given event to all relevant recipients.
Jonathan Austin 1:8aa5cdb4ab67 328 * Normally, this is called once an event has been removed from the event queue.
Jonathan Austin 1:8aa5cdb4ab67 329 *
Jonathan Austin 1:8aa5cdb4ab67 330 * @param evt The event to send.
Jonathan Austin 1:8aa5cdb4ab67 331 *
Jonathan Austin 1:8aa5cdb4ab67 332 * @param urgent The type of listeners to process (optional). If set to true, only listeners defined as urgent and non-blocking will be processed
Jonathan Austin 1:8aa5cdb4ab67 333 * otherwise, all other (standard) listeners will be processed. Defaults to false.
Jonathan Austin 1:8aa5cdb4ab67 334 *
Jonathan Austin 1:8aa5cdb4ab67 335 * @return 1 if all matching listeners were processed, 0 if further processing is required.
Jonathan Austin 1:8aa5cdb4ab67 336 *
Jonathan Austin 1:8aa5cdb4ab67 337 * @note It is recommended that all external code uses the send() function instead of this function,
Jonathan Austin 1:8aa5cdb4ab67 338 * or the constructors provided by MicrobitEvent.
Jonathan Austin 1:8aa5cdb4ab67 339 */
Jonathan Austin 1:8aa5cdb4ab67 340 int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent)
Jonathan Austin 1:8aa5cdb4ab67 341 {
Jonathan Austin 1:8aa5cdb4ab67 342 MicroBitListener *l;
Jonathan Austin 1:8aa5cdb4ab67 343 int complete = 1;
Jonathan Austin 1:8aa5cdb4ab67 344 bool listenerUrgent;
Jonathan Austin 1:8aa5cdb4ab67 345
Jonathan Austin 1:8aa5cdb4ab67 346 l = listeners;
Jonathan Austin 1:8aa5cdb4ab67 347 while (l != NULL)
Jonathan Austin 1:8aa5cdb4ab67 348 {
Jonathan Austin 1:8aa5cdb4ab67 349 if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY))
Jonathan Austin 1:8aa5cdb4ab67 350 {
LancasterUniversity 5:f0f1cecd65d8 351 // If we're running under the fiber scheduler, then derive the THREADING_MODE for the callback based on the
LancasterUniversity 5:f0f1cecd65d8 352 // metadata in the listener itself.
LancasterUniversity 5:f0f1cecd65d8 353 if (fiber_scheduler_running())
LancasterUniversity 5:f0f1cecd65d8 354 listenerUrgent = (l->flags & MESSAGE_BUS_LISTENER_IMMEDIATE) == MESSAGE_BUS_LISTENER_IMMEDIATE;
LancasterUniversity 5:f0f1cecd65d8 355 else
LancasterUniversity 5:f0f1cecd65d8 356 listenerUrgent = true;
LancasterUniversity 5:f0f1cecd65d8 357
LancasterUniversity 5:f0f1cecd65d8 358 // If we should process this event hander in this pass, then activate the listener.
Jonathan Austin 1:8aa5cdb4ab67 359 if(listenerUrgent == urgent && !(l->flags & MESSAGE_BUS_LISTENER_DELETING))
Jonathan Austin 1:8aa5cdb4ab67 360 {
Jonathan Austin 1:8aa5cdb4ab67 361 l->evt = evt;
Jonathan Austin 1:8aa5cdb4ab67 362
Jonathan Austin 1:8aa5cdb4ab67 363 // OK, if this handler has regisitered itself as non-blocking, we just execute it directly...
Jonathan Austin 1:8aa5cdb4ab67 364 // This is normally only done for trusted system components.
Jonathan Austin 1:8aa5cdb4ab67 365 // Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber
Jonathan Austin 1:8aa5cdb4ab67 366 // should the event handler attempt a blocking operation, but doesn't have the overhead
Jonathan Austin 1:8aa5cdb4ab67 367 // of creating a fiber needlessly. (cool huh?)
LancasterUniversity 25:27299423d813 368 if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || !fiber_scheduler_running())
LancasterUniversity 25:27299423d813 369 async_callback(l);
LancasterUniversity 25:27299423d813 370 else
LancasterUniversity 25:27299423d813 371 invoke(async_callback, l);
Jonathan Austin 1:8aa5cdb4ab67 372 }
Jonathan Austin 1:8aa5cdb4ab67 373 else
Jonathan Austin 1:8aa5cdb4ab67 374 {
Jonathan Austin 1:8aa5cdb4ab67 375 complete = 0;
Jonathan Austin 1:8aa5cdb4ab67 376 }
Jonathan Austin 1:8aa5cdb4ab67 377 }
Jonathan Austin 1:8aa5cdb4ab67 378
Jonathan Austin 1:8aa5cdb4ab67 379 l = l->next;
Jonathan Austin 1:8aa5cdb4ab67 380 }
Jonathan Austin 1:8aa5cdb4ab67 381
Jonathan Austin 1:8aa5cdb4ab67 382 return complete;
Jonathan Austin 1:8aa5cdb4ab67 383 }
Jonathan Austin 1:8aa5cdb4ab67 384
Jonathan Austin 1:8aa5cdb4ab67 385 /**
Jonathan Austin 1:8aa5cdb4ab67 386 * Add the given MicroBitListener to the list of event handlers, unconditionally.
Jonathan Austin 1:8aa5cdb4ab67 387 *
Jonathan Austin 1:8aa5cdb4ab67 388 * @param listener The MicroBitListener to add.
Jonathan Austin 1:8aa5cdb4ab67 389 *
Jonathan Austin 1:8aa5cdb4ab67 390 * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise.
Jonathan Austin 1:8aa5cdb4ab67 391 */
Jonathan Austin 1:8aa5cdb4ab67 392 int MicroBitMessageBus::add(MicroBitListener *newListener)
Jonathan Austin 1:8aa5cdb4ab67 393 {
Jonathan Austin 1:8aa5cdb4ab67 394 MicroBitListener *l, *p;
Jonathan Austin 1:8aa5cdb4ab67 395 int methodCallback;
Jonathan Austin 1:8aa5cdb4ab67 396
Jonathan Austin 1:8aa5cdb4ab67 397 //handler can't be NULL!
Jonathan Austin 1:8aa5cdb4ab67 398 if (newListener == NULL)
Jonathan Austin 1:8aa5cdb4ab67 399 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 400
Jonathan Austin 1:8aa5cdb4ab67 401 l = listeners;
Jonathan Austin 1:8aa5cdb4ab67 402
Jonathan Austin 1:8aa5cdb4ab67 403 // Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler
Jonathan Austin 1:8aa5cdb4ab67 404 // registered in a that will already capture these events. If we do, silently ignore.
Jonathan Austin 1:8aa5cdb4ab67 405
Jonathan Austin 1:8aa5cdb4ab67 406 // We always check the ID, VALUE and CB_METHOD fields.
Jonathan Austin 1:8aa5cdb4ab67 407 // If we have a callback to a method, check the cb_method class. Otherwise, the cb function point is sufficient.
Jonathan Austin 1:8aa5cdb4ab67 408 while (l != NULL)
Jonathan Austin 1:8aa5cdb4ab67 409 {
Jonathan Austin 1:8aa5cdb4ab67 410 methodCallback = (newListener->flags & MESSAGE_BUS_LISTENER_METHOD) && (l->flags & MESSAGE_BUS_LISTENER_METHOD);
Jonathan Austin 1:8aa5cdb4ab67 411
Jonathan Austin 1:8aa5cdb4ab67 412 if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb))
Jonathan Austin 1:8aa5cdb4ab67 413 {
Jonathan Austin 1:8aa5cdb4ab67 414 // We have a perfect match for this event listener already registered.
Jonathan Austin 1:8aa5cdb4ab67 415 // If it's marked for deletion, we simply resurrect the listener, and we're done.
Jonathan Austin 1:8aa5cdb4ab67 416 // Either way, we return an error code, as the *new* listener should be released...
Jonathan Austin 1:8aa5cdb4ab67 417 if(l->flags & MESSAGE_BUS_LISTENER_DELETING)
Jonathan Austin 1:8aa5cdb4ab67 418 l->flags &= ~MESSAGE_BUS_LISTENER_DELETING;
Jonathan Austin 1:8aa5cdb4ab67 419
Jonathan Austin 1:8aa5cdb4ab67 420 return MICROBIT_NOT_SUPPORTED;
Jonathan Austin 1:8aa5cdb4ab67 421 }
Jonathan Austin 1:8aa5cdb4ab67 422
Jonathan Austin 1:8aa5cdb4ab67 423 l = l->next;
Jonathan Austin 1:8aa5cdb4ab67 424 }
Jonathan Austin 1:8aa5cdb4ab67 425
Jonathan Austin 1:8aa5cdb4ab67 426 // We have a valid, new event handler. Add it to the list.
Jonathan Austin 1:8aa5cdb4ab67 427 // if listeners is null - we can automatically add this listener to the list at the beginning...
Jonathan Austin 1:8aa5cdb4ab67 428 if (listeners == NULL)
Jonathan Austin 1:8aa5cdb4ab67 429 {
Jonathan Austin 1:8aa5cdb4ab67 430 listeners = newListener;
Jonathan Austin 1:8aa5cdb4ab67 431 MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id);
Jonathan Austin 1:8aa5cdb4ab67 432
Jonathan Austin 1:8aa5cdb4ab67 433 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 434 }
Jonathan Austin 1:8aa5cdb4ab67 435
Jonathan Austin 1:8aa5cdb4ab67 436 // We maintain an ordered list of listeners.
Jonathan Austin 1:8aa5cdb4ab67 437 // The chain is held stictly in increasing order of ID (first level), then value code (second level).
Jonathan Austin 1:8aa5cdb4ab67 438 // Find the correct point in the chain for this event.
Jonathan Austin 1:8aa5cdb4ab67 439 // Adding a listener is a rare occurance, so we just walk the list...
Jonathan Austin 1:8aa5cdb4ab67 440
Jonathan Austin 1:8aa5cdb4ab67 441 p = listeners;
Jonathan Austin 1:8aa5cdb4ab67 442 l = listeners;
Jonathan Austin 1:8aa5cdb4ab67 443
Jonathan Austin 1:8aa5cdb4ab67 444 while (l != NULL && l->id < newListener->id)
Jonathan Austin 1:8aa5cdb4ab67 445 {
Jonathan Austin 1:8aa5cdb4ab67 446 p = l;
Jonathan Austin 1:8aa5cdb4ab67 447 l = l->next;
Jonathan Austin 1:8aa5cdb4ab67 448 }
Jonathan Austin 1:8aa5cdb4ab67 449
Jonathan Austin 1:8aa5cdb4ab67 450 while (l != NULL && l->id == newListener->id && l->value < newListener->value)
Jonathan Austin 1:8aa5cdb4ab67 451 {
Jonathan Austin 1:8aa5cdb4ab67 452 p = l;
Jonathan Austin 1:8aa5cdb4ab67 453 l = l->next;
Jonathan Austin 1:8aa5cdb4ab67 454 }
Jonathan Austin 1:8aa5cdb4ab67 455
Jonathan Austin 1:8aa5cdb4ab67 456 //add at front of list
Jonathan Austin 1:8aa5cdb4ab67 457 if (p == listeners && (newListener->id < p->id || (p->id == newListener->id && p->value > newListener->value)))
Jonathan Austin 1:8aa5cdb4ab67 458 {
Jonathan Austin 1:8aa5cdb4ab67 459 newListener->next = p;
Jonathan Austin 1:8aa5cdb4ab67 460
Jonathan Austin 1:8aa5cdb4ab67 461 //this new listener is now the front!
Jonathan Austin 1:8aa5cdb4ab67 462 listeners = newListener;
Jonathan Austin 1:8aa5cdb4ab67 463 }
Jonathan Austin 1:8aa5cdb4ab67 464
Jonathan Austin 1:8aa5cdb4ab67 465 //add after p
Jonathan Austin 1:8aa5cdb4ab67 466 else
Jonathan Austin 1:8aa5cdb4ab67 467 {
Jonathan Austin 1:8aa5cdb4ab67 468 newListener->next = p->next;
Jonathan Austin 1:8aa5cdb4ab67 469 p->next = newListener;
Jonathan Austin 1:8aa5cdb4ab67 470 }
Jonathan Austin 1:8aa5cdb4ab67 471
Jonathan Austin 1:8aa5cdb4ab67 472 MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id);
Jonathan Austin 1:8aa5cdb4ab67 473 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 474 }
Jonathan Austin 1:8aa5cdb4ab67 475
Jonathan Austin 1:8aa5cdb4ab67 476 /**
Jonathan Austin 1:8aa5cdb4ab67 477 * Remove the given MicroBitListener from the list of event handlers.
Jonathan Austin 1:8aa5cdb4ab67 478 *
Jonathan Austin 1:8aa5cdb4ab67 479 * @param listener The MicroBitListener to remove.
Jonathan Austin 1:8aa5cdb4ab67 480 *
Jonathan Austin 1:8aa5cdb4ab67 481 * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise.
Jonathan Austin 1:8aa5cdb4ab67 482 */
Jonathan Austin 1:8aa5cdb4ab67 483 int MicroBitMessageBus::remove(MicroBitListener *listener)
Jonathan Austin 1:8aa5cdb4ab67 484 {
Jonathan Austin 1:8aa5cdb4ab67 485 MicroBitListener *l;
Jonathan Austin 1:8aa5cdb4ab67 486 int removed = 0;
Jonathan Austin 1:8aa5cdb4ab67 487
Jonathan Austin 1:8aa5cdb4ab67 488 //handler can't be NULL!
Jonathan Austin 1:8aa5cdb4ab67 489 if (listener == NULL)
Jonathan Austin 1:8aa5cdb4ab67 490 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 491
Jonathan Austin 1:8aa5cdb4ab67 492 l = listeners;
Jonathan Austin 1:8aa5cdb4ab67 493
Jonathan Austin 1:8aa5cdb4ab67 494 // Walk this list of event handlers. Delete any that match the given listener.
Jonathan Austin 1:8aa5cdb4ab67 495 while (l != NULL)
Jonathan Austin 1:8aa5cdb4ab67 496 {
Jonathan Austin 1:8aa5cdb4ab67 497 if ((listener->flags & MESSAGE_BUS_LISTENER_METHOD) == (l->flags & MESSAGE_BUS_LISTENER_METHOD))
Jonathan Austin 1:8aa5cdb4ab67 498 {
Jonathan Austin 1:8aa5cdb4ab67 499 if(((listener->flags & MESSAGE_BUS_LISTENER_METHOD) && (*l->cb_method == *listener->cb_method)) ||
Jonathan Austin 1:8aa5cdb4ab67 500 ((!(listener->flags & MESSAGE_BUS_LISTENER_METHOD) && l->cb == listener->cb)))
Jonathan Austin 1:8aa5cdb4ab67 501 {
Jonathan Austin 1:8aa5cdb4ab67 502 if ((listener->id == MICROBIT_ID_ANY || listener->id == l->id) && (listener->value == MICROBIT_EVT_ANY || listener->value == l->value))
Jonathan Austin 1:8aa5cdb4ab67 503 {
Jonathan Austin 1:8aa5cdb4ab67 504 // Found a match. mark this to be removed from the list.
Jonathan Austin 1:8aa5cdb4ab67 505 l->flags |= MESSAGE_BUS_LISTENER_DELETING;
Jonathan Austin 1:8aa5cdb4ab67 506 removed++;
Jonathan Austin 1:8aa5cdb4ab67 507 }
Jonathan Austin 1:8aa5cdb4ab67 508 }
Jonathan Austin 1:8aa5cdb4ab67 509 }
Jonathan Austin 1:8aa5cdb4ab67 510
Jonathan Austin 1:8aa5cdb4ab67 511 l = l->next;
Jonathan Austin 1:8aa5cdb4ab67 512 }
Jonathan Austin 1:8aa5cdb4ab67 513
Jonathan Austin 1:8aa5cdb4ab67 514 if (removed > 0)
Jonathan Austin 1:8aa5cdb4ab67 515 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 516 else
Jonathan Austin 1:8aa5cdb4ab67 517 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 518 }
Jonathan Austin 1:8aa5cdb4ab67 519
Jonathan Austin 1:8aa5cdb4ab67 520 /**
Jonathan Austin 1:8aa5cdb4ab67 521 * Returns the microBitListener with the given position in our list.
Jonathan Austin 1:8aa5cdb4ab67 522 *
Jonathan Austin 1:8aa5cdb4ab67 523 * @param n The position in the list to return.
Jonathan Austin 1:8aa5cdb4ab67 524 *
Jonathan Austin 1:8aa5cdb4ab67 525 * @return the MicroBitListener at postion n in the list, or NULL if the position is invalid.
Jonathan Austin 1:8aa5cdb4ab67 526 */
Jonathan Austin 1:8aa5cdb4ab67 527 MicroBitListener* MicroBitMessageBus::elementAt(int n)
Jonathan Austin 1:8aa5cdb4ab67 528 {
Jonathan Austin 1:8aa5cdb4ab67 529 MicroBitListener *l = listeners;
Jonathan Austin 1:8aa5cdb4ab67 530
Jonathan Austin 1:8aa5cdb4ab67 531 while (n > 0)
Jonathan Austin 1:8aa5cdb4ab67 532 {
Jonathan Austin 1:8aa5cdb4ab67 533 if (l == NULL)
Jonathan Austin 1:8aa5cdb4ab67 534 return NULL;
Jonathan Austin 1:8aa5cdb4ab67 535
Jonathan Austin 1:8aa5cdb4ab67 536 n--;
Jonathan Austin 1:8aa5cdb4ab67 537 l = l->next;
Jonathan Austin 1:8aa5cdb4ab67 538 }
Jonathan Austin 1:8aa5cdb4ab67 539
Jonathan Austin 1:8aa5cdb4ab67 540 return l;
Jonathan Austin 1:8aa5cdb4ab67 541 }
Jonathan Austin 1:8aa5cdb4ab67 542
Jonathan Austin 1:8aa5cdb4ab67 543 /**
Jonathan Austin 1:8aa5cdb4ab67 544 * Destructor for MicroBitMessageBus, where we deregister this instance from the array of fiber components.
Jonathan Austin 1:8aa5cdb4ab67 545 */
Jonathan Austin 1:8aa5cdb4ab67 546 MicroBitMessageBus::~MicroBitMessageBus()
Jonathan Austin 1:8aa5cdb4ab67 547 {
Jonathan Austin 1:8aa5cdb4ab67 548 fiber_remove_idle_component(this);
LancasterUniversity 5:f0f1cecd65d8 549 }