From 316a4830a67fe85f0ee1916a22856fb4ad486994 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Thu, 29 Oct 2020 19:39:14 +0000 Subject: [PATCH 01/43] Close potential race condition in Serial::send() --- source/driver-models/Serial.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/driver-models/Serial.cpp b/source/driver-models/Serial.cpp index ad8e3ce4..10c227fa 100644 --- a/source/driver-models/Serial.cpp +++ b/source/driver-models/Serial.cpp @@ -82,6 +82,9 @@ int Serial::setTxInterrupt(uint8_t *string, int len, SerialMode mode) { int copiedBytes = 0; + if(mode != SYNC_SPINWAIT) + fiber_wake_on_event(DEVICE_ID_NOTIFY, CODAL_SERIAL_EVT_TX_EMPTY); + for(copiedBytes = 0; copiedBytes < len; copiedBytes++) { uint16_t nextHead = (txBuffHead + 1) % txBuffSize; @@ -94,9 +97,6 @@ int Serial::setTxInterrupt(uint8_t *string, int len, SerialMode mode) break; } - if(mode != SYNC_SPINWAIT) - fiber_wake_on_event(DEVICE_ID_NOTIFY, CODAL_SERIAL_EVT_TX_EMPTY); - //set the TX interrupt enableInterrupt(TxInterrupt); From 33da548ab2181982eb8ad14cf3b85ca0efc9c89a Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Tue, 10 Nov 2020 17:50:56 +0000 Subject: [PATCH 02/43] Introduce Pin::getPulseUs() and helper classes - Add synchronous getPulse() method in Pin interface specification. When overrided, this method should return the period of hte next pulse on the relevant pin. - Add PulseIn helper class and component ID, which can be used to implement getPulse() operation on tiop of existing codal APIs. --- inc/core/CodalComponent.h | 1 + inc/driver-models/Pin.h | 14 ++++ inc/drivers/PulseIn.h | 83 ++++++++++++++++++++++++ source/drivers/PulseIn.cpp | 129 +++++++++++++++++++++++++++++++++++++ 4 files changed, 227 insertions(+) create mode 100644 inc/drivers/PulseIn.h create mode 100644 source/drivers/PulseIn.cpp diff --git a/inc/core/CodalComponent.h b/inc/core/CodalComponent.h index d3934240..bdeb65f4 100644 --- a/inc/core/CodalComponent.h +++ b/inc/core/CodalComponent.h @@ -66,6 +66,7 @@ DEALINGS IN THE SOFTWARE. #define DEVICE_ID_JACDAC_CONTROL_SERVICE 32 #define DEVICE_ID_JACDAC_CONFIGURATION_SERVICE 33 #define DEVICE_ID_SYSTEM_ADC 34 +#define DEVICE_ID_PULSE_IN 35 #define DEVICE_ID_IO_P0 100 // IDs 100-227 are reserved for I/O Pin IDs. diff --git a/inc/driver-models/Pin.h b/inc/driver-models/Pin.h index 59558f92..773bf676 100644 --- a/inc/driver-models/Pin.h +++ b/inc/driver-models/Pin.h @@ -388,6 +388,20 @@ namespace codal return DEVICE_OK; } + /** + * Measures the period of the next digital pulse on this pin. + * The polarity of the detected pulse is defined by setPolarity(). + * The calling fiber is blocked until a pulse is received or the specified + * timeout passes. + * + * @param timeout The maximum period of time in microseconds to wait for a pulse. + * @return the period of the pulse in microseconds, or DEVICE_CANCELLED on timeout. + */ + virtual int getPulseUs(int timeout) + { + return DEVICE_NOT_IMPLEMENTED; + } + /** * Configures the events generated by this Pin instance. * diff --git a/inc/drivers/PulseIn.h b/inc/drivers/PulseIn.h new file mode 100644 index 00000000..b77cb97a --- /dev/null +++ b/inc/drivers/PulseIn.h @@ -0,0 +1,83 @@ +/* +The MIT License (MIT) + +Copyright (c) 2020 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#include "Event.h" +#include "Pin.h" +#include "CodalFiber.h" + +#ifndef CODAL_PULSE_IN_H +#define CODAL_PULSE_IN_H + +#define DEVICE_EVT_PULSE_IN_TIMEOUT 10000 + +namespace codal +{ + +class PulseIn +{ + Pin &pin; + uint32_t lastPeriod; + FiberLock lock; + CODAL_TIMESTAMP timeout; + static bool timeoutGeneratorStarted; + + public: + uint32_t lastEdge; + + /** + * Creates a new instance of a synchronous pulse detector ont he given pin. + * + * @param pin The pin to observe for pulse events + * @return the period of the next pulse in microseconds, or XXX if the given timeout expires. + */ + PulseIn(Pin &pin); + + /** + * Synchronously await a pulse, and return the period of the pulse. + * + * @param timeout The maximum amount of time to wait for a pulse, in microseconds. Set to zero to wait indefinitely. + * @return The period of the next pulse, in microseconds, or DEVICE_CANCELLED if the timeout passes. + */ + int awaitPulse(int timeout = 0); + + /** + * Event handler called when a pulse is detected. + */ + void + onPulse(Event e); + + /** + * Event handler called when a timeout event is generated. + */ + void + onTimeout(Event e); + + /** + * Destructor + */ + ~PulseIn(); +}; + +} +#endif diff --git a/source/drivers/PulseIn.cpp b/source/drivers/PulseIn.cpp new file mode 100644 index 00000000..dc0c9519 --- /dev/null +++ b/source/drivers/PulseIn.cpp @@ -0,0 +1,129 @@ +/* +The MIT License (MIT) + +Copyright (c) 2020 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#include "PulseIn.h" +#include "Timer.h" +#include "CodalDmesg.h" + +using namespace codal; + +bool PulseIn::timeoutGeneratorStarted = false; + +/** + * Creates a new instance of a synchronous pulse detector ont he given pin. + * + * @param pin The pin to observe for pulse events + * @return the period of the next pulse in microseconds, or XXX if the given timeout expires. + */ +PulseIn::PulseIn(Pin &p) : pin(p) +{ + lastPeriod = 0; + lastEdge = 0; + + // Configure the requested pin to supply pulse events. + pin.eventOn(DEVICE_PIN_EVENT_ON_PULSE); + EventModel::defaultEventBus->listen(pin.id, pin.getPolarity() ? DEVICE_PIN_EVT_PULSE_HI : DEVICE_PIN_EVT_PULSE_LO, this, &PulseIn::onPulse); + EventModel::defaultEventBus->listen(DEVICE_ID_PULSE_IN, DEVICE_EVT_PULSE_IN_TIMEOUT, this, &PulseIn::onTimeout); + + if (!timeoutGeneratorStarted) + { + system_timer_event_every_us(10000, DEVICE_ID_PULSE_IN, DEVICE_EVT_PULSE_IN_TIMEOUT); + timeoutGeneratorStarted = true; + } + + lock.wait(); +} + +/** + * Synchronously await a pulse, and return the period of the pulse. + * + * @param timeout The maximum amount of time to wait for a pulse, in microseconds. Set to zero to wait indefinitely. + * @return The period of the next pulse, in microseconds, or DEVICE_CANCELLED if the timeout passes. + */ +int +PulseIn::awaitPulse(int timeout) +{ + if (timeout) + this->timeout = system_timer_current_time_us() + timeout; + else + this->timeout = 0; + + lastPeriod = 0; + + lock.wait(); + + if (lastPeriod != 0) + return lastPeriod; + else + return DEVICE_CANCELLED; +} + +/** + * Event handler called when a pulse is detected. + */ +void +PulseIn::onPulse(Event e) +{ + timeout = 0; + lastPeriod = (uint32_t) e.timestamp; + + // Wake any blocked fibers and reset the lock. + if (lock.getWaitCount()) + { + lock.notify(); + lock.wait(); + } +} + +/** + * Event handler called when a timeout event is generated. + */ +void +PulseIn::onTimeout(Event e) +{ + if (timeout && system_timer_current_time_us() > timeout) + { + timeout = 0; + lastPeriod = 0; + + if (lock.getWaitCount()) + { + lock.notify(); + lock.wait(); + } + } +} + +/** + * Destructor + */ +PulseIn::~PulseIn() +{ + EventModel::defaultEventBus->ignore(pin.id, pin.getPolarity() ? DEVICE_PIN_EVT_PULSE_HI : DEVICE_PIN_EVT_PULSE_LO, this, &PulseIn::onPulse); + EventModel::defaultEventBus->ignore(DEVICE_ID_PULSE_IN, DEVICE_EVT_PULSE_IN_TIMEOUT, this, &PulseIn::onTimeout); + pin.eventOn(DEVICE_PIN_EVENT_NONE); + timeout = 0; + lastPeriod = 0; + lock.notifyAll(); +} \ No newline at end of file From 2ed19d64ec3359143409a4b0a9758b4aeef3ee7d Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Thu, 12 Nov 2020 11:56:59 +0000 Subject: [PATCH 03/43] Update non-volatile memory APIs to no longer assume on chip address space - update NVMController to use logical not physical addresses - update KeyValueStorage to use new API - update KeyValueStorage to perform read operations via API rather than direct memory access operations. --- inc/driver-models/NVMController.h | 57 ++++++++-- inc/drivers/KeyValueStorage.h | 85 ++++++--------- source/drivers/KeyValueStorage.cpp | 165 ++++++++++++----------------- 3 files changed, 151 insertions(+), 156 deletions(-) diff --git a/inc/driver-models/NVMController.h b/inc/driver-models/NVMController.h index 26479377..294dd4b5 100644 --- a/inc/driver-models/NVMController.h +++ b/inc/driver-models/NVMController.h @@ -12,40 +12,83 @@ namespace codal NVMController(){}; - virtual uint32_t* getFlashEnd() + /** + * Determines the logical address of the start of non-volatile memory region + * + * @return The logical address of the first valid logical address in the region of non-volatile memory + */ + virtual uint32_t getFlashStart() { - return NULL; + return 0; } - virtual uint32_t* getFlashStart() + /** + * Determines the logical address of the end of the non-volatile memory region + * + * @return The logical address of the first invalid logical address beyond + * the non-volatile memory. + */ + virtual uint32_t getFlashEnd() { - return NULL; + return 0; } + /** + * Determines the size of a non-volatile memory page. A page is defined as + * the block of memory the can be efficiently erased without impacted on + * other regions of memory. + * + * @return The size of a single page in bytes. + */ virtual uint32_t getPageSize() { return 0; } + /** + * Determines the amount of available storage. + * + * @return the amount of available storage, in bytes. + */ virtual uint32_t getFlashSize() { return 0; } - virtual int copy(uint32_t* dest, uint32_t* source, uint32_t size) + /** + * Reads a block of memory from non-volatile memory into RAM + * + * @param dest The address in RAM in which to store the result of the read operation + * @param source The logical address in non-voltile memory to read from + * @param size The number 32-bit words to read. + */ + virtual int read(uint32_t* dest, uint32_t source, uint32_t size) { return DEVICE_NOT_IMPLEMENTED; } - virtual int erase(uint32_t* page) + /** + * Writes a block of memory to non-volatile memory. + * + * @param dest The logical address in non-voltile memory to write to + * @param source The address in RAM of the data to write to non-volatile memory + * @param size The number 32-bit words to write. + */ + virtual int write(uint32_t dest, uint32_t* source, uint32_t size) { return DEVICE_NOT_IMPLEMENTED; } - virtual int write(uint32_t* dst, uint32_t* source, uint32_t size) + /** + * Erases a given page in non-volatile memory. + * + * @param page The address of the page to erase (logical address of the start of the page). + */ + virtual int erase(uint32_t page) { return DEVICE_NOT_IMPLEMENTED; } + }; } diff --git a/inc/drivers/KeyValueStorage.h b/inc/drivers/KeyValueStorage.h index 0661e4eb..c00092ea 100644 --- a/inc/drivers/KeyValueStorage.h +++ b/inc/drivers/KeyValueStorage.h @@ -32,7 +32,7 @@ DEALINGS IN THE SOFTWARE. #include "NVMController.h" #ifndef DEVICE_KEY_VALUE_STORE_OFFSET -#define DEVICE_KEY_VALUE_STORE_OFFSET 4 +#define DEVICE_KEY_VALUE_STORE_OFFSET -4 #endif #define KEY_VALUE_STORAGE_MAGIC 0xC0DA @@ -92,63 +92,23 @@ namespace codal */ class KeyValueStorage { - uint32_t* flashPagePtr; - NVMController& controller; - uint32_t scratch[KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE]; - /** - * Function for copying words from one location to another. - * - * @param from the address to copy data from. - * - * @param to the address to copy the data to. - * - * @param sizeInWords the number of words to copy - */ - void flashCopy(uint32_t* from, uint32_t* to, int sizeInWords); - - /** - * Function for populating the scratch page with a KeyValueStore. - * - * @param store the KeyValueStore struct to write to the scratch page. - */ - void scratchKeyValueStore(KeyValueStore store); - - /** - * Function for populating the scratch page with a KeyValuePair. - * - * @param pair the KeyValuePair struct to write to the scratch page. - * - * @param flashPointer the pointer in flash where this KeyValuePair resides. This pointer - * is used to determine the offset into the scratch page, where the KeyValuePair should - * be written. - */ - void scratchKeyValuePair(KeyValuePair pair, int scratchOffset); + uint32_t flashPagePtr; + NVMController& controller; + uint32_t scratch[KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE]; public: /** - * Default constructor. + * Constructor. * * Creates an instance of KeyValueStorage which acts like a KeyValueStore * that allows the retrieval, addition and deletion of KeyValuePairs. + * + * @param controller The non-volatile storage controller to use + * @param pageNumber The logical page number for this KeyValueStorage. + * Optionally use negative number to count from end of address space. */ - KeyValueStorage(NVMController& controller); - - /** - * Method for erasing a page in flash. - * - * @param page_address Address of the first word in the page to be erased. - */ - void flashPageErase(uint32_t * page_address); - - /** - * Method for writing a word of data in flash with a value. - * - * @param address Address of the word to change. - * - * @param value Value to be written to flash. - */ - void flashWordWrite(uint32_t * address, uint32_t value); + KeyValueStorage(NVMController& controller, int pageNumber = DEVICE_KEY_VALUE_STORE_OFFSET); /** * Places a given key, and it's corresponding value into flash at the earliest @@ -233,7 +193,32 @@ namespace codal */ int size(); + /** + * Erase all contents of this KeyValue store + */ int wipe(); + + private: + + /** + * Function for populating the scratch page with a KeyValueStore. + * + * @param store the KeyValueStore struct to write to the scratch page. + */ + void scratchKeyValueStore(KeyValueStore store); + + /** + * Function for populating the scratch page with a KeyValuePair. + * + * @param pair the KeyValuePair struct to write to the scratch page. + * + * @param flashPointer the pointer in flash where this KeyValuePair resides. This pointer + * is used to determine the offset into the scratch page, where the KeyValuePair should + * be written. + */ + void scratchKeyValuePair(KeyValuePair pair, int scratchOffset); + + }; } diff --git a/source/drivers/KeyValueStorage.cpp b/source/drivers/KeyValueStorage.cpp index 38105d27..ef59fd33 100644 --- a/source/drivers/KeyValueStorage.cpp +++ b/source/drivers/KeyValueStorage.cpp @@ -33,85 +33,33 @@ DEALINGS IN THE SOFTWARE. #include "CodalCompat.h" #include "CodalDmesg.h" -#ifndef DEVICE_KEY_VALUE_STORE_OFFSET -#define DEVICE_KEY_VALUE_STORE_OFFSET 4 -#endif - using namespace codal; /** - * Default constructor. + * Constructor. * * Creates an instance of KeyValueStorage which acts like a KeyValueStore * that allows the retrieval, addition and deletion of KeyValuePairs. + * + * @param controller The non-volatile storage controller to use + * @param pageNumber The logical page number for this KeyValueStorage + * Optionally use negative number to count from end of address space. */ -KeyValueStorage::KeyValueStorage(NVMController& controller) : controller(controller) -{ - memset(scratch, 0, KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE * 4); - this->flashPagePtr = controller.getFlashEnd() - (controller.getPageSize() * DEVICE_KEY_VALUE_STORE_OFFSET); - size(); -} - -/** - * Method for erasing a page in flash. - * - * @param page_address Address of the first word in the page to be erased. - */ -void KeyValueStorage::flashPageErase(uint32_t * page_address) +KeyValueStorage::KeyValueStorage(NVMController& controller, int pageNumber) : controller(controller) { - controller.erase(page_address); -} + // Determine the logival address of the start of the key/value storage page + if (pageNumber < 0) + flashPagePtr = controller.getFlashEnd() - (controller.getPageSize() * pageNumber); + else + flashPagePtr = controller.getFlashStart() + (controller.getPageSize() * pageNumber); -/** - * Function for copying words from one location to another. - * - * @param from the address to copy data from. - * - * @param to the address to copy the data to. - * - * @param sizeInWords the number of words to copy - */ -void KeyValueStorage::flashCopy(uint32_t* from, uint32_t* to, int sizeInWords) -{ - controller.write(to, from, sizeInWords); -} + // Erase our scratch buffer + memset(scratch, 0, KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE * 4); -/** - * Method for writing a word of data in flash with a value. - * - * @param address Address of the word to change. - * - * @param value Value to be written to flash. - */ -void KeyValueStorage::flashWordWrite(uint32_t * address, uint32_t value) -{ - controller.write(&value, address, 1); + size(); } -/** - * Function for populating the scratch page with a KeyValueStore. - * - * @param store the KeyValueStore struct to write to the scratch page. - */ -void KeyValueStorage::scratchKeyValueStore(KeyValueStore store) -{ - memcpy(this->scratch, &store, sizeof(KeyValueStore)); -} - -/** - * Function for populating the scratch page with a KeyValuePair. - * - * @param pair the KeyValuePair struct to write to the scratch page. - * - * @param flashPointer the pointer in flash where this KeyValuePair resides. This pointer - * is used to determine the offset into the scratch page, where the KeyValuePair should - * be written. - */ -void KeyValueStorage::scratchKeyValuePair(KeyValuePair pair, int scratchOffset) -{ - memcpy(this->scratch + scratchOffset, &pair, sizeof(KeyValuePair)); -} /** * Places a given key, and it's corresponding value into flash at the earliest @@ -148,17 +96,13 @@ int KeyValueStorage::put(const char *key, uint8_t *data, int dataSize) memcpy(pair.key, key, keySize); memcpy(pair.value, data, dataSize); - //calculate our various offsets. - uint32_t kvStoreSize = sizeof(KeyValueStore) / 4; - uint32_t kvPairSize = sizeof(KeyValuePair) / 4; - - uint32_t* flashPointer = this->flashPagePtr; + uint32_t flashPointer = flashPagePtr; int storeSize = size(); //our KeyValueStore struct is always at 0 - flashPointer += kvStoreSize; - int scratchPtr = kvStoreSize; + flashPointer += sizeof(KeyValueStore); + int scratchPtr = sizeof(KeyValueStore) / 4; KeyValuePair storedPair = KeyValuePair(); @@ -170,7 +114,7 @@ int KeyValueStorage::put(const char *key, uint8_t *data, int dataSize) //iterate through key value pairs in flash, writing them to the scratch page. for(int i = 0; i < storeSize; i++) { - memcpy(&storedPair, flashPointer, sizeof(KeyValuePair)); + controller.read((uint32_t *)&storedPair, flashPointer, sizeof(KeyValuePair)/4); //check if the keys match... if(strcmp((char *)storedPair.key, (char *)pair.key) == 0) @@ -185,8 +129,8 @@ int KeyValueStorage::put(const char *key, uint8_t *data, int dataSize) scratchKeyValuePair(storedPair, scratchPtr); } - flashPointer += kvPairSize; - scratchPtr += kvPairSize; + flashPointer += sizeof(KeyValuePair); + scratchPtr += sizeof(KeyValuePair) / 4; } if(!found) @@ -203,10 +147,10 @@ int KeyValueStorage::put(const char *key, uint8_t *data, int dataSize) } //erase our storage page - flashPageErase(this->flashPagePtr); + controller.erase(flashPagePtr); //copy from scratch to storage. - flashCopy(this->scratch, flashPagePtr, KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE); + controller.write(flashPagePtr, scratch, KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE); return DEVICE_OK; } @@ -248,10 +192,10 @@ KeyValuePair* KeyValueStorage::get(const char* key) if(storeSize == 0) return NULL; - uint32_t* flashPtr = this->flashPagePtr; + uint32_t flashPtr = this->flashPagePtr; //our KeyValueStore struct is always at 0 - flashPtr += sizeof(KeyValueStore) / 4; + flashPtr += sizeof(KeyValueStore); KeyValuePair *pair = new KeyValuePair(); @@ -260,12 +204,12 @@ KeyValuePair* KeyValueStorage::get(const char* key) //iterate through flash until we have a match, or drop out. for(i = 0; i < storeSize; i++) { - memcpy(pair, flashPtr, sizeof(KeyValuePair)); + controller.read((uint32_t *)pair, flashPtr, sizeof(KeyValuePair)/4); // DMESG("k %s value: %d %d %d %d",pair->key, pair->value[0], pair->value[1], pair->value[2], pair->value[3]); if(strcmp(key,(char *)pair->key) == 0) break; - flashPtr += sizeof(KeyValuePair) / 4; + flashPtr += sizeof(KeyValuePair); } //clean up @@ -303,20 +247,17 @@ KeyValuePair* KeyValueStorage::get(ManagedString key) */ int KeyValueStorage::remove(const char* key) { - uint32_t kvStoreSize = sizeof(KeyValueStore) / 4; - uint32_t kvPairSize = sizeof(KeyValuePair) / 4; - int storeSize = size(); //if we have no data, we have nothing to do. if(storeSize == 0) return DEVICE_NO_DATA; - uint32_t* flashPointer = this->flashPagePtr; + uint32_t flashPointer = this->flashPagePtr; //our KeyValueStore struct is always at 0 - flashPointer += kvStoreSize; - int scratchPointer = kvStoreSize; + flashPointer += sizeof(KeyValueStore); + int scratchPointer = sizeof(KeyValueStore) / 4; KeyValuePair storedPair = KeyValuePair(); @@ -330,7 +271,7 @@ int KeyValueStorage::remove(const char* key) //iterate through our flash copy pairs to scratch, unless there is a key patch for(int i = 0; i < storeSize; i++) { - memcpy(&storedPair, flashPointer, sizeof(KeyValuePair)); + controller.read((uint32_t *)&storedPair, flashPointer, sizeof(KeyValuePair)/4); //if we have a match, don't increment our scratchPointer if(strcmp((char *)storedPair.key, (char *)key) == 0) @@ -346,7 +287,7 @@ int KeyValueStorage::remove(const char* key) scratchPointer += sizeof(KeyValuePair) / 4; } - flashPointer += sizeof(KeyValuePair) / 4; + flashPointer += sizeof(KeyValuePair); } //if we haven't got a match, write our old KeyValueStore struct @@ -357,9 +298,9 @@ int KeyValueStorage::remove(const char* key) } //copy scratch to our storage page - flashPageErase((uint32_t *)flashPagePtr); - flashCopy(scratch, flashPagePtr, kvStoreSize + (storeSize * kvPairSize)); - + controller.erase(flashPagePtr); + controller.write(flashPagePtr, scratch, (sizeof(KeyValueStore) / 4) + (storeSize * (sizeof(KeyValuePair) / 4))); + return DEVICE_OK; } @@ -386,8 +327,7 @@ int KeyValueStorage::size() KeyValueStore store = KeyValueStore(); //read our data! - memcpy(&store, flashPagePtr, sizeof(KeyValueStore)); - + controller.read((uint32_t *)&store, flashPagePtr, sizeof(KeyValueStore)/4); //if we haven't used flash before, we need to configure it if(store.magic != KEY_VALUE_STORAGE_MAGIC) @@ -399,16 +339,43 @@ int KeyValueStorage::size() scratchKeyValueStore(store); //erase flash, and copy the scratch page over - flashPageErase((uint32_t *)flashPagePtr); - flashCopy(scratch, flashPagePtr, KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE); + controller.erase(flashPagePtr); + controller.write(flashPagePtr, scratch, KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE); } return store.size; } +/** + * Erase all contents of this KeyValue store + */ int KeyValueStorage::wipe() { - flashPageErase(this->flashPagePtr); + controller.erase(flashPagePtr); size(); return DEVICE_OK; -} \ No newline at end of file +} + +/** + * Function for populating the scratch page with a KeyValueStore. + * + * @param store the KeyValueStore struct to write to the scratch page. + */ +void KeyValueStorage::scratchKeyValueStore(KeyValueStore store) +{ + memcpy(this->scratch, &store, sizeof(KeyValueStore)); +} + +/** + * Function for populating the scratch page with a KeyValuePair. + * + * @param pair the KeyValuePair struct to write to the scratch page. + * + * @param flashPointer the pointer in flash where this KeyValuePair resides. This pointer + * is used to determine the offset into the scratch page, where the KeyValuePair should + * be written. + */ +void KeyValueStorage::scratchKeyValuePair(KeyValuePair pair, int scratchOffset) +{ + memcpy(this->scratch + scratchOffset, &pair, sizeof(KeyValuePair)); +} From 3c2533d387d0a579a7b2da2e74db2a5e03ab9095 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Thu, 12 Nov 2020 20:43:55 +0000 Subject: [PATCH 04/43] Correct MAGIC constant and remove legacy debug statements --- inc/drivers/KeyValueStorage.h | 2 +- source/drivers/KeyValueStorage.cpp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/inc/drivers/KeyValueStorage.h b/inc/drivers/KeyValueStorage.h index c00092ea..0b7807b4 100644 --- a/inc/drivers/KeyValueStorage.h +++ b/inc/drivers/KeyValueStorage.h @@ -35,7 +35,7 @@ DEALINGS IN THE SOFTWARE. #define DEVICE_KEY_VALUE_STORE_OFFSET -4 #endif -#define KEY_VALUE_STORAGE_MAGIC 0xC0DA +#define KEY_VALUE_STORAGE_MAGIC 0xC0DA1 #define KEY_VALUE_STORAGE_BLOCK_SIZE 48 #define KEY_VALUE_STORAGE_KEY_SIZE 16 diff --git a/source/drivers/KeyValueStorage.cpp b/source/drivers/KeyValueStorage.cpp index ef59fd33..27479da9 100644 --- a/source/drivers/KeyValueStorage.cpp +++ b/source/drivers/KeyValueStorage.cpp @@ -31,7 +31,6 @@ DEALINGS IN THE SOFTWARE. #include "CodalConfig.h" #include "KeyValueStorage.h" #include "CodalCompat.h" -#include "CodalDmesg.h" using namespace codal; @@ -205,7 +204,6 @@ KeyValuePair* KeyValueStorage::get(const char* key) for(i = 0; i < storeSize; i++) { controller.read((uint32_t *)pair, flashPtr, sizeof(KeyValuePair)/4); - // DMESG("k %s value: %d %d %d %d",pair->key, pair->value[0], pair->value[1], pair->value[2], pair->value[3]); if(strcmp(key,(char *)pair->key) == 0) break; From fb16846dbcb3e929f680e9e5a48e121c9d6a9e04 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Thu, 12 Nov 2020 21:03:01 +0000 Subject: [PATCH 05/43] Update KeyValueStorage to use lazy instantiation of buffers --- inc/drivers/KeyValueStorage.h | 7 ++++++- source/drivers/KeyValueStorage.cpp | 24 +++++++++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/inc/drivers/KeyValueStorage.h b/inc/drivers/KeyValueStorage.h index 0b7807b4..fb67c7d8 100644 --- a/inc/drivers/KeyValueStorage.h +++ b/inc/drivers/KeyValueStorage.h @@ -94,7 +94,7 @@ namespace codal { uint32_t flashPagePtr; NVMController& controller; - uint32_t scratch[KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE]; + uint32_t *scratch; public: @@ -200,6 +200,11 @@ namespace codal private: + /** + * Function to lazily instatiate a scratch buffer + */ + void scratchReset(); + /** * Function for populating the scratch page with a KeyValueStore. * diff --git a/source/drivers/KeyValueStorage.cpp b/source/drivers/KeyValueStorage.cpp index 27479da9..d9cfb5e1 100644 --- a/source/drivers/KeyValueStorage.cpp +++ b/source/drivers/KeyValueStorage.cpp @@ -47,15 +47,14 @@ using namespace codal; */ KeyValueStorage::KeyValueStorage(NVMController& controller, int pageNumber) : controller(controller) { + scratch = NULL; + // Determine the logival address of the start of the key/value storage page if (pageNumber < 0) flashPagePtr = controller.getFlashEnd() - (controller.getPageSize() * pageNumber); else flashPagePtr = controller.getFlashStart() + (controller.getPageSize() * pageNumber); - // Erase our scratch buffer - memset(scratch, 0, KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE * 4); - size(); } @@ -104,11 +103,9 @@ int KeyValueStorage::put(const char *key, uint8_t *data, int dataSize) int scratchPtr = sizeof(KeyValueStore) / 4; KeyValuePair storedPair = KeyValuePair(); - int found = 0; - //erase our scratch page - memset(scratch, 0, sizeof(scratch)); + scratchReset(); //iterate through key value pairs in flash, writing them to the scratch page. for(int i = 0; i < storeSize; i++) @@ -261,7 +258,7 @@ int KeyValueStorage::remove(const char* key) int found = 0; - memset(scratch, 0, KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE * 4); + scratchReset(); // scratch the old size (it will be updated later if required). scratchKeyValueStore(KeyValueStore(KEY_VALUE_STORAGE_MAGIC, storeSize)); @@ -334,6 +331,7 @@ int KeyValueStorage::size() store.size = 0; //erase the scratch page and write our new KeyValueStore + scratchReset(); scratchKeyValueStore(store); //erase flash, and copy the scratch page over @@ -354,6 +352,18 @@ int KeyValueStorage::wipe() return DEVICE_OK; } +/** + * Function to lazily instatiate a scratch buffer + */ +void KeyValueStorage::scratchReset() +{ + if (scratch == NULL) + scratch = (uint32_t *)malloc(KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE * 4); + + memset(scratch, 0, KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE * 4); +} + + /** * Function for populating the scratch page with a KeyValueStore. * From 3200597c985fad0876e3f61137106c73df85f78f Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sun, 15 Nov 2020 17:45:46 +0000 Subject: [PATCH 06/43] Add missing LICENSE in MemorySource class --- inc/streams/MemorySource.h | 24 ++++++++++++++++++++++++ source/streams/MemorySource.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/inc/streams/MemorySource.h b/inc/streams/MemorySource.h index 8fb2e388..4161607c 100644 --- a/inc/streams/MemorySource.h +++ b/inc/streams/MemorySource.h @@ -1,3 +1,27 @@ +/* +The MIT License (MIT) + +Copyright (c) 2020 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + #include "CodalConfig.h" #include "DataStream.h" diff --git a/source/streams/MemorySource.cpp b/source/streams/MemorySource.cpp index 7a7687ba..ca7890d7 100644 --- a/source/streams/MemorySource.cpp +++ b/source/streams/MemorySource.cpp @@ -1,3 +1,27 @@ +/* +The MIT License (MIT) + +Copyright (c) 2020 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + #include "MemorySource.h" #include "CodalDmesg.h" From 7132daedac411f636df46447025c1e4fcb7bc1e6 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Mon, 16 Nov 2020 15:01:02 +0000 Subject: [PATCH 07/43] Remove status bitfield clash between LSM303 drivers and Compass/Accelerometer base classes --- inc/drivers/LSM303Accelerometer.h | 4 ++-- inc/drivers/LSM303Magnetometer.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/inc/drivers/LSM303Accelerometer.h b/inc/drivers/LSM303Accelerometer.h index 534986ee..2f17822c 100644 --- a/inc/drivers/LSM303Accelerometer.h +++ b/inc/drivers/LSM303Accelerometer.h @@ -91,8 +91,8 @@ DEALINGS IN THE SOFTWARE. /** * LSM303 Status flags */ -#define LSM303_A_STATUS_ENABLED 0x01 -#define LSM303_A_STATUS_SLEEPING 0x02 +#define LSM303_A_STATUS_ENABLED 0x0100 +#define LSM303_A_STATUS_SLEEPING 0x0200 namespace codal { diff --git a/inc/drivers/LSM303Magnetometer.h b/inc/drivers/LSM303Magnetometer.h index f5281368..4142f2a0 100644 --- a/inc/drivers/LSM303Magnetometer.h +++ b/inc/drivers/LSM303Magnetometer.h @@ -83,8 +83,8 @@ DEALINGS IN THE SOFTWARE. /** * LSM303 Status flags */ -#define LSM303_M_STATUS_ENABLED 0x01 -#define LSM303_M_STATUS_SLEEPING 0x02 +#define LSM303_M_STATUS_ENABLED 0x0100 +#define LSM303_M_STATUS_SLEEPING 0x0200 /** * Class definition for LSM303_M. From 7611fc2dfbef828d138655fb2aed51f368f52700 Mon Sep 17 00:00:00 2001 From: James Devine Date: Wed, 18 Nov 2020 13:56:34 +0000 Subject: [PATCH 08/43] remove outdated JACDAC code --- inc/JACDAC/JACDAC.h | 179 ---- inc/JACDAC/JDPhysicalLayer.h | 331 ------ inc/JACDAC/JDService.h | 166 --- inc/JACDAC/JDServiceClasses.h | 26 - inc/JACDAC/control/JDCRC.h | 24 - inc/JACDAC/control/JDConfigurationService.h | 45 - inc/JACDAC/control/JDControlService.h | 185 ---- inc/JACDAC/control/JDDeviceManager.h | 64 -- inc/JACDAC/control/JDGPIOIdentification.h | 21 - inc/JACDAC/control/JDRNGService.h | 30 - inc/JACDAC/control/JDSoundIdentification.h | 22 - inc/JACDAC/services/JDAccelerometerService.h | 46 - inc/JACDAC/services/JDConsoleService.h | 41 - inc/JACDAC/services/JDMessageBusService.h | 87 -- inc/JACDAC/services/JDPacketSniffer.h | 33 - inc/JACDAC/services/JDTestService.h | 22 - inc/drivers/USBJACDAC.h | 66 -- source/JACDAC/JACDAC.cpp | 235 ----- source/JACDAC/JDPhysicalLayer.cpp | 953 ------------------ source/JACDAC/JDService.cpp | 109 -- source/JACDAC/control/JDCRC.cpp | 39 - .../JACDAC/control/JDConfigurationService.cpp | 84 -- source/JACDAC/control/JDControlService.cpp | 625 ------------ source/JACDAC/control/JDDeviceManager.cpp | 150 --- .../JACDAC/control/JDGPIOIdentification.cpp | 29 - source/JACDAC/control/JDRNGService.cpp | 32 - .../JACDAC/control/JDSoundIdentification.cpp | 34 - source/JACDAC/jacdac_status_flags.py | 34 - source/JACDAC/jacdac_status_log.py | 65 -- .../services/JDAccelerometerService.cpp | 82 -- source/JACDAC/services/JDConsoleService.cpp | 84 -- .../JACDAC/services/JDMessageBusService.cpp | 124 --- source/JACDAC/services/JDPacketSniffer.cpp | 69 -- source/JACDAC/services/JDTestService.cpp | 23 - source/drivers/USBJACDAC.cpp | 165 --- 35 files changed, 4324 deletions(-) delete mode 100644 inc/JACDAC/JACDAC.h delete mode 100644 inc/JACDAC/JDPhysicalLayer.h delete mode 100644 inc/JACDAC/JDService.h delete mode 100644 inc/JACDAC/JDServiceClasses.h delete mode 100644 inc/JACDAC/control/JDCRC.h delete mode 100644 inc/JACDAC/control/JDConfigurationService.h delete mode 100644 inc/JACDAC/control/JDControlService.h delete mode 100644 inc/JACDAC/control/JDDeviceManager.h delete mode 100644 inc/JACDAC/control/JDGPIOIdentification.h delete mode 100644 inc/JACDAC/control/JDRNGService.h delete mode 100644 inc/JACDAC/control/JDSoundIdentification.h delete mode 100644 inc/JACDAC/services/JDAccelerometerService.h delete mode 100644 inc/JACDAC/services/JDConsoleService.h delete mode 100644 inc/JACDAC/services/JDMessageBusService.h delete mode 100644 inc/JACDAC/services/JDPacketSniffer.h delete mode 100644 inc/JACDAC/services/JDTestService.h delete mode 100644 inc/drivers/USBJACDAC.h delete mode 100644 source/JACDAC/JACDAC.cpp delete mode 100644 source/JACDAC/JDPhysicalLayer.cpp delete mode 100644 source/JACDAC/JDService.cpp delete mode 100644 source/JACDAC/control/JDCRC.cpp delete mode 100644 source/JACDAC/control/JDConfigurationService.cpp delete mode 100644 source/JACDAC/control/JDControlService.cpp delete mode 100644 source/JACDAC/control/JDDeviceManager.cpp delete mode 100644 source/JACDAC/control/JDGPIOIdentification.cpp delete mode 100644 source/JACDAC/control/JDRNGService.cpp delete mode 100644 source/JACDAC/control/JDSoundIdentification.cpp delete mode 100644 source/JACDAC/jacdac_status_flags.py delete mode 100644 source/JACDAC/jacdac_status_log.py delete mode 100644 source/JACDAC/services/JDAccelerometerService.cpp delete mode 100644 source/JACDAC/services/JDConsoleService.cpp delete mode 100644 source/JACDAC/services/JDMessageBusService.cpp delete mode 100644 source/JACDAC/services/JDPacketSniffer.cpp delete mode 100644 source/JACDAC/services/JDTestService.cpp delete mode 100644 source/drivers/USBJACDAC.cpp diff --git a/inc/JACDAC/JACDAC.h b/inc/JACDAC/JACDAC.h deleted file mode 100644 index cf62817e..00000000 --- a/inc/JACDAC/JACDAC.h +++ /dev/null @@ -1,179 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2017 Lancaster University. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - -#ifndef CODAL_JACDAC_H -#define CODAL_JACDAC_H - -#include "CodalConfig.h" -#include "CodalComponent.h" -#include "ErrorNo.h" -#include "Event.h" -#include "JDControlService.h" -#include "ManagedString.h" -#include "codal_target_hal.h" - -#define JD_STARTED 0x02 - -#ifndef JD_SERVICE_ARRAY_SIZE -#define JD_SERVICE_ARRAY_SIZE 20 -#endif - -namespace codal -{ - /** - * This class is the composition of the physical and control layers. It process packets - * produced by the JACDAC physical layer and passes them to our high level services using the - * control layer. - * - * This composition forms JACDAC. - **/ - class JACDAC : public CodalComponent - { - friend class JDControlService; - friend class JDPhysicalLayer; - - /** - * Invoked by JACDAC when a packet is received from the serial bus. - * - * This handler is invoked in standard conext. This also means that users can stop the device from handling packets. - * (which might be a bad thing). - * - * This handler continues to pull packets from the queue and iterate over services. - **/ - void onPacketReceived(Event); - - // An instance of the control service responsible for routing packets to services - JDControlService controlService; - - // A pointer to a bridge service (if set). Defaults to NULL. - JDService* bridge; - - public: - - // this array holds all services on the device - static JDService* services[JD_SERVICE_ARRAY_SIZE]; - - // a reference to a the physical bus instance - JDPhysicalLayer& bus; - - // a singleton pointer to the current instance of JACDAC. - static JACDAC* instance; - - /** - * Constructor - * - * @param bus A reference to an instance of JDPhysicalLayer for the transmission of packets. - * - * @param deviceName a ManagedString containing the desired name of the device. - * This can be set using setDeviceName post construction. - *π - * @param id for the message bus, defaults to SERVICE_STATE_ID_JACDAC_PROTOCOL - **/ - JACDAC(JDPhysicalLayer& bus, ManagedString deviceName = ManagedString(), uint16_t id = DEVICE_ID_JACDAC); - - /** - * Sets the bridge member variable to the given JDService pointer. - * - * Bridge services are given all packets received on the bus, the idea being that - * packets can be bridged to another networking medium, i.e. packet radio. - * - * @param bridge the service to forward all packets to another networking medium - * this service will receive all packets via the handlePacket call. If NULL - * is given, the bridge member variable is cleared. - **/ - int setBridge(JDService* bridge); - - /** - * Set the name to be used when enumerated on the bus. - * - * @param s the name to use - **/ - static int setDeviceName(ManagedString s); - - /** - * Get the name to used when enumerated on the bus. - * - * @return the current name being used - **/ - static ManagedString getDeviceName(); - - /** - * Adds a service to the services array. The control service iterates over this array. - * - * @param device a reference to the service to add. - * - * @return DEVICE_OK on success. - **/ - virtual int add(JDService& device); - - /** - * removes a service from the services array. The control service iterates over this array. - * - * @param device a reference to the service to remove. - * - * @return DEVICE_OK on success. - **/ - virtual int remove(JDService& device); - - /** - * A static method to send an entire, premade JDPacket on the bus. - * - * @param pkt the JDPacket struct to send. - * - * @return DEVICE_OK on success. - * - * @note This struct is copied into memory managed by the physical layer. Users - * are responsible for freeing memory allocated to the given JDPacket (pkt). - **/ - static int send(JDPacket* pkt); - - static int triggerRemoteIdentification(uint8_t device_address); - - static int setRemoteDeviceName(uint8_t device_address, ManagedString name); - - static JDDevice* getRemoteDevice(uint8_t device_address); - - /** - * Logs the current state of JACDAC services. - **/ - void logState(); - - /** - * Starts the jacdac bus and enumerates this device (if required). - * - * @return DEVICE_OK on success. - **/ - int start(); - - /** - * Stops the jacdac bus and stops enumeration by the control service (if required). - * - * @return DEVICE_OK on success. - **/ - int stop(); - }; - -} // namespace codal - -#endif diff --git a/inc/JACDAC/JDPhysicalLayer.h b/inc/JACDAC/JDPhysicalLayer.h deleted file mode 100644 index 89b42d57..00000000 --- a/inc/JACDAC/JDPhysicalLayer.h +++ /dev/null @@ -1,331 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2017 Lancaster University. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - -#ifndef CODAL_JD_PHYSICAL_H -#define CODAL_JD_PHYSICAL_H - -#include "CodalConfig.h" -#include "ErrorNo.h" -#include "Pin.h" -#include "Event.h" -#include "DMASingleWireSerial.h" -#include "LowLevelTimer.h" -#include "JDDeviceManager.h" - -#define JD_VERSION 0 - -// various timings in microseconds -// 8 data bits, 1 start bit, 1 stop bit. -#define JD_BYTE_AT_125KBAUD 80 -// the maximum permitted time between bytes -#define JD_MAX_INTERBYTE_SPACING (2 * JD_BYTE_AT_125KBAUD) -// the minimum permitted time between the data packets -#define JD_MIN_INTERFRAME_SPACING (2 * JD_BYTE_AT_125KBAUD) -// the time it takes for the bus to be considered in a normal state -#define JD_BUS_NORMALITY_PERIOD (2 * JD_BYTE_AT_125KBAUD) -// the minimum permitted time between the low pulse and data being received is 40 us -#define JD_MIN_INTERLODATA_SPACING 40 -// max spacing is 3 times 1 byte at minimum baud rate (240 us) -#define JD_MAX_INTERLODATA_SPACING (3 * JD_BYTE_AT_125KBAUD) - -#define JD_SERIAL_MAX_BUFFERS 10 - -#define JD_SERIAL_MAX_SERVICE_NUMBER 15 - -#define JD_SERIAL_RECEIVING 0x0001 -#define JD_SERIAL_RECEIVING_HEADER 0x0002 -#define JD_SERIAL_TRANSMITTING 0x0004 -#define JD_SERIAL_RX_LO_PULSE 0x0008 -#define JD_SERIAL_TX_LO_PULSE 0x0010 - -#define JD_SERIAL_BUS_LO_ERROR 0x0020 -#define JD_SERIAL_BUS_TIMEOUT_ERROR 0x0040 -#define JD_SERIAL_BUS_UART_ERROR 0x0080 -#define JD_SERIAL_ERR_MSK 0x00E0 - -#define JD_SERIAL_BUS_STATE 0x0100 -#define JD_SERIAL_BUS_TOGGLED 0x0200 - -#define JD_SERIAL_DEBUG_BIT 0x8000 - -#define JD_SERIAL_EVT_DATA_READY 1 -#define JD_SERIAL_EVT_BUS_ERROR 2 -#define JD_SERIAL_EVT_CRC_ERROR 3 -#define JD_SERIAL_EVT_DRAIN 4 -#define JD_SERIAL_EVT_RX_TIMEOUT 5 - -#define JD_SERIAL_EVT_BUS_CONNECTED 5 -#define JD_SERIAL_EVT_BUS_DISCONNECTED 6 - -#define JD_SERIAL_HEADER_SIZE 4 -#define JD_SERIAL_CRC_HEADER_SIZE 2 // when computing CRC, we skip the CRC and version fields, so the header size decreases by three. -#define JD_SERIAL_MAX_PAYLOAD_SIZE 255 - -#define JD_SERIAL_MAXIMUM_BUFFERS 10 - -#define JD_SERIAL_DMA_TIMEOUT 2 // 2 callback ~8 ms - -#define JD_SERIAL_MAX_BAUD 1000000 -#define JD_SERIAL_TX_MAX_BACKOFF 1000 - -#ifndef JD_RX_ARRAY_SIZE -#define JD_RX_ARRAY_SIZE 10 -#endif - -#ifndef JD_TX_ARRAY_SIZE -#define JD_TX_ARRAY_SIZE 10 -#endif - -#define JD_SERIAL_BAUD_1M 1 -#define JD_SERIAL_BAUD_500K 2 -#define JD_SERIAL_BAUD_250K 4 -#define JD_SERIAL_BAUD_125K 8 - -#if CONFIG_ENABLED(JD_DEBUG) -#define JD_DMESG codal_dmesg -#else -#define JD_DMESG(...) ((void)0) -#endif - -namespace codal -{ - class JDService; - // a struct containing the various diagnostics of the JACDAC physical layer. - struct JDDiagnostics - { - uint32_t bus_state; - uint32_t bus_lo_error; - uint32_t bus_uart_error; - uint32_t bus_timeout_error; - uint32_t packets_sent; - uint32_t packets_received; - uint32_t packets_dropped; - }; - - struct JDPacket - { - uint16_t crc:12, service_number:4; // crc is stored in the first 12 bits, service number in the final 4 bits - uint8_t device_address; // control is 0, devices are allocated address in the range 1 - 255 - uint8_t size; // the size, address, and crc are not included by the size variable. The size of a packet dictates the size of the data field. - uint8_t data[JD_SERIAL_MAX_PAYLOAD_SIZE]; - uint8_t communication_rate; - } __attribute((__packed__)); - - enum class JDBusState : uint8_t - { - Receiving, - Transmitting, - Error, - Unknown - }; - - enum class JDSerialState : uint8_t - { - ListeningForPulse, - ErrorRecovery, - Off - }; - - /** - * This enumeration defines the low time of the tx pulse, and the transmission speed of - * this JACDAC device on the bus. - **/ - enum class JDBaudRate : uint8_t - { - Baud1M = JD_SERIAL_BAUD_1M, - Baud500K = JD_SERIAL_BAUD_500K, - Baud250K = JD_SERIAL_BAUD_250K, - Baud125K = JD_SERIAL_BAUD_125K - }; - - enum JDBusErrorState : uint16_t - { - Continuation = 0, - BusLoError = JD_SERIAL_BUS_LO_ERROR, - BusTimeoutError = JD_SERIAL_BUS_TIMEOUT_ERROR, - BusUARTError = JD_SERIAL_BUS_UART_ERROR // a different error code, but we want the same behaviour. - }; - - /** - * Class definition for a JACDAC interface. - */ - class JDPhysicalLayer : public CodalComponent - { - friend class USBJACDAC; - - JDBaudRate maxBaud; - JDBaudRate currentBaud; - uint8_t bufferOffset; - - JDService* sniffer; - - protected: - DMASingleWireSerial& sws; - Pin& sp; - LowLevelTimer& timer; - - Pin* busLED; - Pin* commLED; - - bool busLEDActiveLo; - bool commLEDActiveLo; - - JDSerialState state; - - uint32_t startTime; - uint32_t lastBufferedCount; - - void loPulseDetected(uint32_t); - int setState(JDSerialState s); - void dmaComplete(Event evt); - - JDPacket* popRxArray(); - JDPacket* popTxArray(); - int addToTxArray(JDPacket* packet); - int addToRxArray(JDPacket* packet); - void sendPacket(); - void errorState(JDBusErrorState); - - /** - * Sends a packet using the SingleWireSerial instance. This function begins the asynchronous transmission of a packet. - * If an ongoing asynchronous transmission is happening, JD is added to the txQueue. If this is the first packet in a while - * asynchronous transmission is begun. - * - * @returns DEVICE_OK on success, DEVICE_INVALID_PARAMETER if JD is NULL, or DEVICE_NO_RESOURCES if the queue is full. - */ - virtual int queuePacket(JDPacket *p); - - public: - - static JDPhysicalLayer* instance; - - uint8_t txHead; - uint8_t txTail; - uint8_t rxHead; - uint8_t rxTail; - - JDPacket* rxBuf; // holds the pointer to the current rx buffer - JDPacket* txBuf; // holds the pointer to the current tx buffer - JDPacket* rxArray[JD_RX_ARRAY_SIZE]; - JDPacket* txArray[JD_TX_ARRAY_SIZE]; - - /** - * Constructor - * - * @param sws an instance of sws. - * - * @param busStateLED an instance of a pin, used to display the state of the bus. - * - * @param commStateLED an instance of a pin, used to display the state of the bus. - * - * @param baud Defaults to 1mbaud - */ - JDPhysicalLayer(DMASingleWireSerial& sws, LowLevelTimer& timer, Pin* busStateLED = NULL, Pin* commStateLED = NULL, bool busLEDActiveLo = false, bool commLEDActiveLo = false, JDBaudRate baud = JDBaudRate::Baud1M, uint16_t id = DEVICE_ID_JACDAC_PHYS); - - /** - * Retrieves the first packet on the rxQueue regardless of the device_class - * - * @returns the first packet on the rxQueue or NULL - */ - JDPacket *getPacket(); - - /** - * Causes this instance of JACDAC to begin listening for packets transmitted on the serial line. - */ - virtual void start(); - - /** - * Causes this instance of JACDAC to stop listening for packets transmitted on the serial line. - */ - virtual void stop(); - - int send(JDPacket* tx, JDDevice* device, bool computeCRC = true); - - /** - * Sends a packet using the SingleWireSerial instance. This function begins the asynchronous transmission of a packet. - * If an ongoing asynchronous transmission is happening, JD is added to the txQueue. If this is the first packet in a while - * asynchronous transmission is begun. - * - * @param buf the buffer to send. - * - * @param len the length of the buffer to send. - * - * @returns DEVICE_OK on success, DEVICE_INVALID_PARAMETER if buf is NULL or len is invalid, or DEVICE_NO_RESOURCES if the queue is full. - */ - int send(uint8_t* buf, int len, uint8_t service_number, JDDevice* device); - - /** - * Returns a bool indicating whether the JACDAC driver has been started. - * - * @return true if started, false if not. - **/ - bool isRunning(); - - /** - * Returns the current state if the bus. - * - * @return true if connected, false if there's a bad bus condition. - **/ - bool isConnected(); - - /** - * Returns the current state of the bus, either: - * - * * Receiving if the driver is in the process of receiving a packet. - * * Transmitting if the driver is communicating a packet on the bus. - * - * If neither of the previous states are true, then the driver looks at the bus and returns the bus state: - * - * * High, if the line is currently floating high. - * * Lo if something is currently pulling the line low. - **/ - JDBusState getState(); - - uint8_t getErrorState(); - JDDiagnostics getDiagnostics(); - - /** - * Sets the maximum baud rate which is used as a default (if no communication rate is given in any packet), and as - * a maximum reception rate. - * - * @param baudRate the desired baud rate for this jacdac instance, one of: Baud1M, Baud500K, Baud250K, Baud125K - * - * @returns DEVICE_OK on success - **/ - int setMaximumBaud(JDBaudRate baudRate); - - /** - * Returns the current maximum baud rate. - * - * @returns the enumerated baud rate for this jacdac instance, one of: Baud1M, Baud500K, Baud250K, Baud125K - **/ - JDBaudRate getMaximumBaud(); - - void _timerCallback(uint16_t channels); - void _dmaCallback(uint16_t errCode); - void _gpioCallback(int state); - }; -} // namespace codal - -#endif diff --git a/inc/JACDAC/JDService.h b/inc/JACDAC/JDService.h deleted file mode 100644 index f6bf7385..00000000 --- a/inc/JACDAC/JDService.h +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef CODAL_JD_SERVICE_H -#define CODAL_JD_SERVICE_H - -#include "CodalComponent.h" -#include "JDPhysicalLayer.h" -#include "JDServiceClasses.h" -#include "JDDeviceManager.h" - -#define JD_MAX_HOST_SERVICES JD_DEVICE_MAX_HOST_SERVICES - -#define JD_SERVICE_EVT_CONNECTED 65520 -#define JD_SERVICE_EVT_DISCONNECTED 65521 -#define JD_SERVICE_EVT_ERROR 65526 -#define JD_SERVICE_NUMBER_UNINITIALISED_VAL 65535 // used as the service_number when a service is not initialised -#define JD_SERVICE_STATUS_FLAGS_INITIALISED 0x02 // device service is running - -#define JD_SERVICE_INFO_HEADER_SIZE 6 - -#define JD_SERVICE_MODE_CLIENT 1 -#define JD_SERVICE_MODE_HOST 2 -#define JD_SERVICE_MODE_BROADCAST_HOST 3 -#define JD_SERVICE_MODE_CONTROL_LAYER 4 - -namespace codal -{ - // This enumeration specifies that supported configurations that services should utilise. - // Many combinations of flags are supported, but only the ones listed here have been fully implemented. - enum JDServiceMode - { - ClientService = JD_SERVICE_MODE_CLIENT, // the service is seeking the use of another device's resource - HostService = JD_SERVICE_MODE_HOST, // the service is hosting a resource for others to use. - BroadcastHostService = JD_SERVICE_MODE_BROADCAST_HOST, // the service is enumerated with its own address, and receives all packets of the same class (including control packets) - ControlLayerService = JD_SERVICE_MODE_CONTROL_LAYER // this service is not consider an enumerable service. - }; - - struct JDServiceInformation - { - uint32_t service_class; // the class of the service - uint8_t service_flags; - uint8_t advertisement_size; - uint8_t data[]; // optional additional data, maximum of 16 bytes - } __attribute((__packed__)); - - /** - * This class presents a common abstraction for all JDServices. It also contains some default member functions to perform common operations. - * This should be subclassed by any service implementation - **/ - class JDService : public CodalComponent - { - friend class JDControlService; - friend class JACDAC; - // the above need direct access to our member variables and more - - protected: - - // Due to the dynamic nature of JACDAC when a new service is created, this variable is incremented. - // JACDAC id's are allocated from 3000 - 4000 - static uint32_t dynamicId; - - JDServiceMode mode; - uint32_t service_class; - uint16_t service_number; - uint8_t service_flags; - - JDDevice* device; - JDDevice* requiredDevice; - - /** - * Called by the logic service when a new state is connected to the serial bus - * - * @param state an instance of JDServiceState representing the device that has been connected - * - * @return SERVICE_STATE_OK for success - **/ - virtual int hostConnected(); - - /** - * Called by the logic service when this service has been disconnected from the serial bus. - * - * This is only called if a service is in VirtualMode and the virtualised device disappears from the bus. - * - * @return SERVICE_STATE_OK for success - **/ - virtual int hostDisconnected(); - - /** - * A convenience function that calls JACDAC->send with parameters supplied from this instances' JDServiceState - * - * @param buf the data to send - * @param len the length of the data. - * - * @return SERVICE_STATE_OK on success. - **/ - virtual int send(uint8_t* buf, int len); - - public: - - /** - * Constructor - * - * */ - JDService(uint32_t serviceClass, JDServiceMode m); - - /** - * Invoked by the logic service when it is queuing a control packet. - * - * This allows the addition of service specific control packet information and the setting of any additional flags. - * - * @param p A pointer to the packet, where the data field contains a pre-filled control packet. - * - * @return SERVICE_STATE_OK on success - **/ - virtual int addAdvertisementData(uint8_t* data); - - /** - * Invoked by the logic service when a control packet with the address of the service is received. - * - * Control packets are routed by address, or by class in broadcast mode. Services - * can override this function to handle additional payloads in control packet.s - * - * @param p the packet from the serial bus. Services should cast p->data to a JDControlPacket. - * - * @return SERVICE_STATE_OK to signal that the packet has been handled, or SERVICE_STATE_CANCELLED to indicate the logic service - * should continue to search for a service. - **/ - virtual int handleServiceInformation(JDDevice* device, JDServiceInformation* info); - - /** - * Invoked by the Protocol service when a standard packet with the address of the service is received. - * - * @param p the packet from the serial bus. Services should cast p->data to their agreed upon structure.. - * - * @return SERVICE_STATE_OK to signal that the packet has been handled, or SERVICE_STATE_CANCELLED to indicate the logic service - * should continue to search for a service. - **/ - virtual int handlePacket(JDPacket* p); - - /** - * Returns the current connected state of this service instance. - * - * @return true for connected, false for disconnected - **/ - virtual bool isConnected(); - - /** - * Retrieves the device instance of the remote device - * - * @return the address. - **/ - JDDevice* getHostDevice(); - - /** - * Retrieves the class of the service. - * - * @return the class. - **/ - uint32_t getServiceClass(); - - /** - * Destructor, removes this service from the services array and deletes the pairedInstance member variable if allocated. - **/ - ~JDService(); - }; -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/JDServiceClasses.h b/inc/JACDAC/JDServiceClasses.h deleted file mode 100644 index e444b37a..00000000 --- a/inc/JACDAC/JDServiceClasses.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef JD_CLASSES_H -#define JD_CLASSES_H - -#define STATIC_CLASS_START 0 -#define STATIC_CLASS_END 0x00FFFFFF -#define DYNAMIC_CLASS_START STATIC_ADDRESS_END -#define DYNAMIC_CLASS_END 0xFFFFFFFF - -#define JD_SERVICE_CLASS_CODAL_START 0 -#define JD_SERVICE_CLASS_CODAL_END 2000 -#define JD_SERVICE_CLASS_MAKECODE_START 2000 -#define JD_SERVICE_CLASS_MAKECODE_END 4000 - -#define JD_SERVICE_CLASS_CONTROL 0 -#define JD_SERVICE_CLASS_CONTROL_RNG 1 -#define JD_SERVICE_CLASS_CONTROL_CONFIGURATION 2 -#define JD_SERVICE_CLASS_CONTROL_TEST 3 - -#define JD_SERVICE_CLASS_JOYSTICK 4 -#define JD_SERVICE_CLASS_MESSAGE_BUS 5 -#define JD_SERVICE_CLASS_BRIDGE 6 -#define JD_SERVICE_CLASS_BUTTON 7 -#define JD_SERVICE_CLASS_ACCELEROMETER 8 -#define JD_SERVICE_CLASS_CONSOLE 9 - -#endif \ No newline at end of file diff --git a/inc/JACDAC/control/JDCRC.h b/inc/JACDAC/control/JDCRC.h deleted file mode 100644 index b198cfac..00000000 --- a/inc/JACDAC/control/JDCRC.h +++ /dev/null @@ -1,24 +0,0 @@ -#include "JDDeviceManager.h" - -#ifndef JD_CRC_H -#define JD_CRC_H - -#define JD_CRC_POLYNOMIAL 0xF13 - -namespace codal -{ - /** - * Computes the crc for a given buffer (data) and length (len) - * - * @param data the pointer to the data buffer - * @param len the size of data - * @param device the JDDevice to computer the crc with. The unique device identifier is used - * when calculating the crc destined for specific services. Device can be NULL, this indicates - * that the crc should not include a udid. - * - * @return the computed crc. - **/ - uint16_t jd_crc(uint8_t *data, uint32_t len, JDDevice* device); -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/control/JDConfigurationService.h b/inc/JACDAC/control/JDConfigurationService.h deleted file mode 100644 index f9332050..00000000 --- a/inc/JACDAC/control/JDConfigurationService.h +++ /dev/null @@ -1,45 +0,0 @@ -#include "JDService.h" -#include "ManagedString.h" - -#ifndef JD_NAMING_SERVICE_H -#define JD_NAMING_SERVICE_H - -#define JD_CONTROL_CONFIGURATION_SERVICE_NUMBER 1 - -#define JD_CONTROL_CONFIGURATION_SERVICE_REQUEST_TYPE_NAME 1 -#define JD_CONTROL_CONFIGURATION_SERVICE_REQUEST_TYPE_IDENTIFY 2 - -#define JD_CONTROL_CONFIGURATION_SERVICE_PACKET_HEADER_SIZE 2 - -#define JD_CONTROL_CONFIGURATION_EVT_NAME 1 -#define JD_CONTROL_CONFIGURATION_EVT_IDENTIFY 2 - -#ifndef JD_DEFAULT_INDICATION_TIME -#define JD_DEFAULT_INDICATION_TIME 5 // time in seconds -#endif - -struct JDConfigurationPacket -{ - uint8_t device_address; - uint8_t request_type; - uint8_t data[]; -} __attribute((__packed__)); - -namespace codal -{ - class JDConfigurationService : public JDService - { - int send(uint8_t* buf, int len) override; - - public: - JDConfigurationService(uint16_t id = DEVICE_ID_JACDAC_CONFIGURATION_SERVICE); - - virtual int handlePacket(JDPacket* p) override; - - int setRemoteDeviceName(uint8_t device_address, ManagedString newName); - - int triggerRemoteIdentification(uint8_t device_address); - }; -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/control/JDControlService.h b/inc/JACDAC/control/JDControlService.h deleted file mode 100644 index 274c7f8e..00000000 --- a/inc/JACDAC/control/JDControlService.h +++ /dev/null @@ -1,185 +0,0 @@ -#ifndef CODAL_JD_CONTROL_SERVICE_H -#define CODAL_JD_CONTROL_SERVICE_H - -#include "JDService.h" -#include "JDRNGService.h" -#include "JDConfigurationService.h" -#include "ManagedString.h" - -#define JD_CONTROL_SERVICE_STATUS_ENUMERATE 0x02 -#define JD_CONTROL_SERVICE_STATUS_ENUMERATING 0x04 -#define JD_CONTROL_SERVICE_STATUS_ENUMERATED 0x08 -#define JD_CONTROL_SERVICE_STATUS_BUS_LO 0x10 - -#define JD_CONTROL_SERVICE_EVT_CHANGED 2 -#define JD_CONTROL_SERVICE_EVT_TIMER_CALLBACK 3 - -#define JD_CONTROL_PACKET_HEADER_SIZE 10 - -#define JD_CONTROL_ROLLING_TIMEOUT_VAL 3 - -namespace codal -{ - /** - * This struct represents a JDControlPacket used by the control service residing on device_address 0 - * with service_number 0. - * - * A control packet provides full information about services on a device, it's most important use is to translates the address used in - * standard packets to the full service information. - **/ - struct JDControlPacket - { - uint64_t unique_device_identifier; // the "unique" serial number of the device. - uint8_t device_address; - uint8_t device_flags; - uint8_t data[]; - } __attribute((__packed__)); - - /** - * This class represents the control service, which is consistent across all JACDAC devices. - * - * It handles addressing and the routing of control packets from the bus to their respective services. - **/ - class JDControlService : public JDService - { - JDDevice* remoteDevices; - JDDevice* controller; - JDControlPacket* enumerationData; - - JDDeviceManager deviceManager; - JDRNGService rngService; - JDConfigurationService configurationService; - ManagedString name; - - - /** - * This member function periodically iterates across all devices and performs various actions. It handles the sending - * of control packets, address assignment for the device, and the connection and disconnection of services as devices - * are added or removed from the bus. - **/ - void timerCallback(Event); - - /** - * This member function disconnects the services attached to the given device. It is invoked - * when the controlService has detected the absence of a device. - * - * @param device the device which has been removed from the bus. - **/ - void deviceDisconnected(JDDevice* device); - - /** - * This member function connects the device struct used to enumerate with HostServices running - * on the device. - **/ - void deviceEnumerated(); - - /** - * This member function composes the control packet data field which includes service information. - * It is responsible for calling addAdvertisementData. - * - * @return the size of the controlPacket. - **/ - int formControlPacket(); - - /** - * This function overrides the default implementation of JDService->send, instead using - * a device_address of 0, a service number of 0, and a device_unique_identifier of NULL. - **/ - int send(uint8_t* buf, int len) override; - - public: - - /** - * Constructor. - * - * Creates a local initialised service and adds itself to the service array. - * - * @param deviceName the device name to use on the bus. - **/ - JDControlService(ManagedString name); - - void routePacket(JDPacket* p); - - /** - * Overridden for future use. It might be useful to control the behaviour of the logic service in the future. - **/ - virtual int handleServiceInformation(JDDevice* device, JDServiceInformation* p) override; - - /** - * Called by the JACDAC when a data packet has address 0. - * - * Packets addressed to zero will always be addressed to a control service. - * - * This function forwards packets for specific service_numbers (such as name and RNG services). - * - * It then iterates over all services routing control packets correctly. Virtual services are - * populated if a packet is not handled by an existing service. - * - * @param p the packet from the serial bus. - **/ - virtual int handlePacket(JDPacket* p) override; - - /** - * Get a device based upon its device_address - * - * @param device_address the device_address of the device to retrieve - * - * @returns NULL if not found, or a ptr to a JDDevice struct. - **/ - JDDevice* getRemoteDevice(uint8_t device_address); - - /** - * Starts the enumeration process of the device only if a service has host services to - * enumerate. - * - * @return DEVICE_OK on success. - **/ - int enumerate(); - - /** - * Returns the current enumeration state. - * - * @returns true if enumerated, false otherwise. - **/ - bool isEnumerated(); - - /** - * Returns a bool whose value is based on whether the device is enumerating. - * - * @returns true if enumerating, false otherwise. - **/ - bool isEnumerating(); - - /** - * The opposite of enumerate, and stops Control Packets being transmitted on the bus. - * - * @return DEVICE_OK on success. - **/ - int disconnect(); - - /** - * Retrieves the current device name. - * - * @returns the device name as a ManagedString. - **/ - ManagedString getDeviceName(); - - /** - * Sets the current device name. - * - * @param name the name for the device. - * - * @returns DEVICE_OK on success. - **/ - int setDeviceName(ManagedString name); - - int setRemoteDeviceName(uint8_t device_address, ManagedString name); - - /** - * - **/ - int triggerRemoteIdentification(uint8_t deviceAddress); - }; -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/control/JDDeviceManager.h b/inc/JACDAC/control/JDDeviceManager.h deleted file mode 100644 index d4e61f35..00000000 --- a/inc/JACDAC/control/JDDeviceManager.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef CODAL_JD_DEVICE_MANAGER_H -#define CODAL_JD_DEVICE_MANAGER_H - -#include "CodalConfig.h" -#include "ErrorNo.h" - -#define JD_DEVICE_FLAGS_NACK 0x08 -#define JD_DEVICE_FLAGS_HAS_NAME 0x04 -#define JD_DEVICE_FLAGS_PROPOSING 0x02 -#define JD_DEVICE_FLAGS_REJECT 0x01 - -#define JD_DEVICE_MAX_HOST_SERVICES 16 - -#ifndef JD_DEVICE_DEFAULT_COMMUNICATION_RATE -#define JD_DEVICE_DEFAULT_COMMUNICATION_RATE JD_SERIAL_BAUD_1M -#endif - -struct JDDevice -{ - uint64_t unique_device_identifier; - uint8_t device_address; - uint8_t device_flags; - uint8_t communication_rate; - uint8_t rolling_counter; - uint16_t servicemap_bitmsk; - uint8_t broadcast_servicemap[JD_DEVICE_MAX_HOST_SERVICES / 2]; // use to map remote broadcast services to local broadcast services. - JDDevice* next; - uint8_t* name; -}; - -namespace codal -{ - class JDControlPacket; - - /** - * This class represents the logic service, which is consistent across all JACDAC devices. - * - * It handles addressing and the routing of control packets from the bus to their respective services. - **/ - class JDDeviceManager - { - JDDevice* devices; - - int initialiseDevice(JDDevice* remoteDevice, JDControlPacket* controlPacket, uint8_t communicationRate); - - public: - - JDDevice* getDeviceList(); - - JDDevice* getDevice(uint8_t device_address); - - JDDevice* getDevice(uint8_t device_address, uint64_t unique_device_identifier); - - JDDevice* addDevice(JDControlPacket* controlPacket, uint8_t communicationRate); - - int updateDevice(JDDevice* remoteDevice, JDControlPacket* controlPacket, uint8_t communicationRate); - - int removeDevice(JDDevice* device); - - JDDeviceManager(); - }; -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/control/JDGPIOIdentification.h b/inc/JACDAC/control/JDGPIOIdentification.h deleted file mode 100644 index 1e201509..00000000 --- a/inc/JACDAC/control/JDGPIOIdentification.h +++ /dev/null @@ -1,21 +0,0 @@ -#include "CodalConfig.h" -#include "JDConfigurationService.h" -#include "Pin.h" - -#ifndef JD_GPIO_IDENTIFICATION_H -#define JD_GPIO_IDENTIFICATION_H - -namespace codal -{ - class JDGPIOIdentification - { - Pin& gpio; - bool identifying; - - public: - JDGPIOIdentification(Pin& gpio); - void identify(Event); - }; -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/control/JDRNGService.h b/inc/JACDAC/control/JDRNGService.h deleted file mode 100644 index f3877a11..00000000 --- a/inc/JACDAC/control/JDRNGService.h +++ /dev/null @@ -1,30 +0,0 @@ -#include "JDService.h" - -#ifndef JD_RNG_SERVICE_H -#define JD_RNG_SERVICE_H - -#define JD_CONTROL_RNG_SERVICE_NUMBER 2 - -#define JD_CONTROL_RNG_SERVICE_REQUEST_TYPE_REQ 1 -#define JD_CONTROL_RNG_SERVICE_REQUEST_TYPE_RESP 2 - -struct JDRNGServicePacket -{ - uint32_t request_type; - uint32_t random; -}; - -namespace codal -{ - class JDRNGService : public JDService - { - int send(uint8_t* buf, int len) override; - - public: - JDRNGService(); - - virtual int handlePacket(JDPacket* p) override; - }; -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/control/JDSoundIdentification.h b/inc/JACDAC/control/JDSoundIdentification.h deleted file mode 100644 index b515450d..00000000 --- a/inc/JACDAC/control/JDSoundIdentification.h +++ /dev/null @@ -1,22 +0,0 @@ -#include "CodalConfig.h" -#include "JDConfigurationService.h" -#include "Pin.h" -#include "Synthesizer.h" - -#ifndef JD_SOUND_IDENTIFICATION_H -#define JD_SOUND_IDENTIFICATION_H - -namespace codal -{ - class JDSoundIdentification - { - Synthesizer& synth; - bool identifying; - - public: - JDSoundIdentification(Synthesizer& synth); - void identify(Event); - }; -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/services/JDAccelerometerService.h b/inc/JACDAC/services/JDAccelerometerService.h deleted file mode 100644 index a387f412..00000000 --- a/inc/JACDAC/services/JDAccelerometerService.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef JD_ACCELEROMETER_SERVICE_H -#define JD_ACCELEROMETER_SERVICE_H - -#include "JACDAC.h" -#include "Accelerometer.h" - -#define JD_ACCEL_EVT_SEND_DATA 1 - -namespace codal -{ - - struct AccelerometerPacket - { - uint8_t packet_type; - int16_t x; - int16_t y; - int16_t z; - }__attribute__((packed)); - - struct AccelerometerGesturePacket - { - uint8_t packet_type; - int16_t event_value; - }__attribute__((packed)); - - class JDAccelerometerService : public JDService - { - Accelerometer* accelerometer; - Sample3D latest; - - void sendData(Event); - void forwardEvent(Event evt); - - public: - JDAccelerometerService(Accelerometer& accel); - JDAccelerometerService(); - - int getX(); - int getY(); - int getZ(); - - virtual int handlePacket(JDPacket* p); - }; -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/services/JDConsoleService.h b/inc/JACDAC/services/JDConsoleService.h deleted file mode 100644 index d3d1a18b..00000000 --- a/inc/JACDAC/services/JDConsoleService.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef JD_CONSOLE_SERVICE_H -#define JD_CONSOLE_SERVICE_H - -#include "JACDAC.h" -#include "ManagedString.h" - -#define JD_CONSOLE_LOG_PRIORITY_LOG 1 -#define JD_CONSOLE_LOG_PRIORITY_INFO 2 -#define JD_CONSOLE_LOG_PRIORITY_DEBUG 3 -#define JD_CONSOLE_LOG_PRIORITY_ERROR 4 - -namespace codal -{ - enum JDConsoleLogPriority - { - Log = JD_CONSOLE_LOG_PRIORITY_LOG, - Info = JD_CONSOLE_LOG_PRIORITY_INFO, - Debug = JD_CONSOLE_LOG_PRIORITY_DEBUG, - Error = JD_CONSOLE_LOG_PRIORITY_ERROR - }; - - struct JDConsolePacket - { - uint8_t priority; - uint8_t message[]; // no size is needed, null terminator is included in the message. - } __attribute((__packed__)); - - class JDConsoleService : public JDService - { - public: - JDConsoleService(bool receiver); - - int handlePacket(JDPacket* pkt) override; - - int log(JDConsoleLogPriority priority, ManagedString message); - - void setMinimumPriority(JDConsoleLogPriority priority); - }; -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/services/JDMessageBusService.h b/inc/JACDAC/services/JDMessageBusService.h deleted file mode 100644 index 181e96cb..00000000 --- a/inc/JACDAC/services/JDMessageBusService.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef JD_MESSAGE_BUS_SERVICE_H -#define JD_MESSAGE_BUS_SERVICE_H - -#include "JACDAC.h" -#include "MessageBus.h" - -#define JD_MESSAGEBUS_TYPE_EVENT 0x01 -#define JD_MESSAGEBUS_TYPE_LISTEN 0x02 - -namespace codal -{ - class JDMessageBusService : public JDService - { - void eventReceived(Event e); - bool suppressForwarding; - - public: - JDMessageBusService(); - - /** - * Associates the given event with the serial channel. - * - * Once registered, all events matching the given registration sent to this device's - * default EventModel will be automatically retransmitted on the serial bus. - * - * @param id The id of the event to register. - * - * @param value the value of the event to register. - * - * @return DEVICE_OK on success, or DEVICE_NO_RESOURCES if no default EventModel is available. - * - * @note The wildcards DEVICE_ID_ANY and DEVICE_EVT_ANY can also be in place of the - * id and value fields. - */ - int listen(uint16_t id, uint16_t value); - - /** - * Associates the given event with the serial channel. - * - * Once registered, all events matching the given registration sent to the given - * EventModel will be automatically retransmitted on the serial bus. - * - * @param id The id of the events to register. - * - * @param value the value of the event to register. - * - * @param eventBus The EventModel to listen for events on. - * - * @return DEVICE_OK on success. - * - * @note The wildcards DEVICE_ID_ANY and DEVICE_EVT_ANY can also be in place of the - * id and value fields. - */ - int listen(uint16_t id, uint16_t value, EventModel &eventBus); - - /** - * Disassociates the given event with the serial channel. - * - * @param id The id of the events to deregister. - * - * @param value The value of the event to deregister. - * - * @return DEVICE_OK on success, or DEVICE_INVALID_PARAMETER if the default message bus does not exist. - * - * @note DEVICE_EVT_ANY can be used to deregister all event values matching the given id. - */ - int ignore(uint16_t id, uint16_t value); - /** - * Disassociates the given events with the serial channel. - * - * @param id The id of the events to deregister. - * - * @param value The value of the event to deregister. - * - * @param eventBus The EventModel to deregister on. - * - * @return DEVICE_OK on success. - * - * @note DEVICE_EVT_ANY can be used to deregister all event values matching the given id. - */ - int ignore(uint16_t id, uint16_t value, EventModel &eventBus); - - virtual int handlePacket(JDPacket* p); - }; -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/services/JDPacketSniffer.h b/inc/JACDAC/services/JDPacketSniffer.h deleted file mode 100644 index 7574b331..00000000 --- a/inc/JACDAC/services/JDPacketSniffer.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef JD_PACKET_LOGGER_H -#define JD_PACKET_LOGGER_H - -#include "JACDAC.h" -#include "Event.h" - -namespace codal -{ - - struct JDSnifferServiceMap - { - uint8_t device_address; - uint32_t services[JD_DEVICE_MAX_HOST_SERVICES]; - }; - - class JDPacketSniffer: public JDService - { - JDDeviceManager deviceManager; - - void timerCallback(Event); - - public: - JDPacketSniffer(); - - virtual int handlePacket(JDPacket* p) override; - - JDDevice* getDeviceList(); - - void logDevices(); - }; -} - -#endif \ No newline at end of file diff --git a/inc/JACDAC/services/JDTestService.h b/inc/JACDAC/services/JDTestService.h deleted file mode 100644 index cd279e6b..00000000 --- a/inc/JACDAC/services/JDTestService.h +++ /dev/null @@ -1,22 +0,0 @@ -#include "JDService.h" -#include "ManagedString.h" - -#ifndef JD_TEST_SERVICE_H -#define JD_TEST_SERVICE_H - -namespace codal -{ - class JDTestService : public JDService - { - ManagedString name; - - public: - JDTestService(ManagedString serviceName, JDServiceMode m); - - virtual int handlePacket(JDPacket* p) override; - - void sendTestPacket(uint32_t value); - }; -} - -#endif \ No newline at end of file diff --git a/inc/drivers/USBJACDAC.h b/inc/drivers/USBJACDAC.h deleted file mode 100644 index b73ae93d..00000000 --- a/inc/drivers/USBJACDAC.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2017 Lancaster University. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - -#ifndef DEVICE_USB_JACDAC_H -#define DEVICE_USB_JACDAC_H - -#include "CodalUSB.h" -#include "JACDAC.h" - -#define USB_JACDAC_BUFFER_SIZE 2048 - -#define JACDAC_USB_STATUS_CLEAR_TO_SEND 0x02 - -#if CONFIG_ENABLED(DEVICE_USB) - -namespace codal -{ - class USBJACDAC : public CodalUSBInterface, public JDService - { - uint8_t inBuf[USB_JACDAC_BUFFER_SIZE]; - uint8_t outBuf[USB_JACDAC_BUFFER_SIZE]; - - uint16_t inBuffPtr; - uint16_t outBuffPtr; - - JDPhysicalLayer* phys; - - public: - USBJACDAC(); - - int setPhysicalLayer(JDPhysicalLayer &phys); - virtual int classRequest(UsbEndpointIn &ctrl, USBSetup& setup) override; - virtual int endpointRequest() override; - virtual const InterfaceInfo *getInterfaceInfo() override; - virtual int handlePacket(JDPacket*) override; - - virtual bool enableWebUSB() override { return true; } - - virtual void idleCallback() override; - }; -} - -#endif - -#endif \ No newline at end of file diff --git a/source/JACDAC/JACDAC.cpp b/source/JACDAC/JACDAC.cpp deleted file mode 100644 index 91156985..00000000 --- a/source/JACDAC/JACDAC.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2017 Lancaster University. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - -#include "CodalConfig.h" -#include "ErrorNo.h" -#include "Event.h" -#include "EventModel.h" -#include "JACDAC.h" -#include "Timer.h" -#include "CodalDmesg.h" - -using namespace codal; - -JDService* JACDAC::services[JD_SERVICE_ARRAY_SIZE] = { 0 }; - -JACDAC* JACDAC::instance = NULL; - -void JACDAC::onPacketReceived(Event) -{ - JDPacket* pkt = NULL; - - while((pkt = bus.getPacket()) != NULL) - { - DMESG("PKT: a %d sn: %d",pkt->device_address, pkt->service_number); - controlService.routePacket(pkt); - - // if we have a bridge service, route all packets to it. - if (bridge) - bridge->handlePacket(pkt); - - free(pkt); - } -} - -JACDAC::JACDAC(JDPhysicalLayer& bus, ManagedString name, uint16_t id) : controlService(name), bridge(NULL), bus(bus) -{ - this->id = id; - - if (instance == NULL) - instance = this; - - memset(this->services, 0, sizeof(JDService*) * JD_SERVICE_ARRAY_SIZE); - - add(controlService); - - // packets are queued, and should be processed in normal context. - if (EventModel::defaultEventBus) - EventModel::defaultEventBus->listen(bus.id, JD_SERIAL_EVT_DATA_READY, this, &JACDAC::onPacketReceived); -} - -int JACDAC::add(JDService& service) -{ - int i; - - // check for duplicates first - for (i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - if (services[i] == &service) - return DEVICE_OK; - - for (i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - { - target_disable_irq(); - if (services[i] == NULL) - { - services[i] = &service; - target_enable_irq(); - break; - } - target_enable_irq(); - } - - if (i == JD_SERVICE_ARRAY_SIZE) - return DEVICE_NO_RESOURCES; - - return DEVICE_OK; -} - -int JACDAC::remove(JDService& service) -{ - target_disable_irq(); - for (int i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - { - if (services[i] == &service) - { - services[i] = NULL; - break; - } - } - target_enable_irq(); - - return DEVICE_OK; -} - -int JACDAC::setBridge(JDService* bridge) -{ - this->bridge = bridge; - return DEVICE_OK; -} - -int JACDAC::send(JDPacket* pkt) -{ - if (instance) - return instance->bus.send(pkt, NULL, true); - - return DEVICE_NO_RESOURCES; -} - -int JACDAC::setDeviceName(ManagedString s) -{ - if (JACDAC::instance == NULL) - return DEVICE_INVALID_STATE; - - return JACDAC::instance->controlService.setDeviceName(s); -} - -ManagedString JACDAC::getDeviceName() -{ - if (JACDAC::instance == NULL) - return ManagedString(); - - return JACDAC::instance->controlService.getDeviceName(); -} - -int JACDAC::triggerRemoteIdentification(uint8_t device_address) -{ - if (JACDAC::instance == NULL) - return DEVICE_INVALID_STATE; - - return JACDAC::instance->controlService.triggerRemoteIdentification(device_address); -} - -int JACDAC::setRemoteDeviceName(uint8_t device_address, ManagedString name) -{ - if (JACDAC::instance == NULL) - return DEVICE_INVALID_STATE; - - return JACDAC::instance->controlService.setRemoteDeviceName(device_address, name); -} - -JDDevice* JACDAC::getRemoteDevice(uint8_t device_address) -{ - if (JACDAC::instance == NULL) - return NULL; - - return JACDAC::instance->controlService.getRemoteDevice(device_address); -} - -void JACDAC::logState() -{ - if (JACDAC::instance == NULL) - return; - - DMESG("Enabled: %d", JACDAC::instance->bus.isRunning()); - - - JDBusState busState = JACDAC::instance->bus.getState(); - - const char* busStateStr = ""; - - switch(busState) - { - case JDBusState::Receiving: - busStateStr = "Receiving"; - break; - - case JDBusState::Transmitting: - busStateStr = "Transmitting"; - break; - - case JDBusState::Error: - busStateStr = "Error"; - break; - - case JDBusState::Unknown: - busStateStr = "Unknown"; - break; - } - - DMESG("Bus state: %s", busStateStr); - - for (int i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - { - JDService* current = JACDAC::instance->services[i]; - - if (current) - DMESG("Service %d initialised[%d] device_address[%d] serial[%d] class[%d], mode[%s%s%s]", i, current->isConnected(), (current->device) ? current->device->device_address : -1, (current->device) ? current->device->unique_device_identifier : -1, current->service_class, current->mode == BroadcastHostService ? "B" : "", current->mode == HostService ? "H" : "", current->mode == ClientService ? "C" : ""); - } -} - -int JACDAC::start() -{ - if (this->status & JD_STARTED) - return DEVICE_OK; - - controlService.enumerate(); - - bus.start(); - - this->status |= JD_STARTED; - return DEVICE_OK; -} - -int JACDAC::stop() -{ - if (!(this->status & JD_STARTED)) - return DEVICE_OK; - - bus.stop(); - - controlService.disconnect(); - - this->status &= ~JD_STARTED; - return DEVICE_OK; -} diff --git a/source/JACDAC/JDPhysicalLayer.cpp b/source/JACDAC/JDPhysicalLayer.cpp deleted file mode 100644 index 9a81dc24..00000000 --- a/source/JACDAC/JDPhysicalLayer.cpp +++ /dev/null @@ -1,953 +0,0 @@ -#include "JDPhysicalLayer.h" -#include "Event.h" -#include "EventModel.h" -#include "codal_target_hal.h" -#include "CodalDmesg.h" -#include "CodalFiber.h" -#include "SingleWireSerial.h" -#include "Timer.h" -#include "JACDAC.h" -#include "JDCRC.h" - -// #define GPIO_DEBUG - -#ifdef GPIO_DEBUG -extern void set_gpio(int); -extern void set_gpio1(int); -//unused -extern void set_gpio2(int); -// error -extern void set_gpio3(int); - -#define SET_GPIO(val)(set_gpio(val)) -#define SET_GPIO1(val)(set_gpio1(val)) -#define SET_GPIO2(val)(set_gpio2(val)) -#define SET_GPIO3(val)(set_gpio3(val)) -#else -#define SET_GPIO(...)((void)0) -#define SET_GPIO1(...)((void)0) -#define SET_GPIO2(...)((void)0) -#define SET_GPIO3(...)((void)0) -#endif - -// #define TRACK_STATE - -#ifdef TRACK_STATE - -// #define PHYS_STATE_SIZE 128 -#define PHYS_STATE_SIZE 512 - -#warning TRACK_STATE_ON - -uint32_t phys_state[PHYS_STATE_SIZE] = {0}; -uint32_t phys_pointer = 0; - -#define JD_SET_FLAGS(flags) do { \ - target_disable_irq(); \ - test_status |= (flags); \ - phys_state[phys_pointer] = test_status | (__LINE__ << 16) | 1 << 31; \ - target_enable_irq(); \ - phys_pointer = (phys_pointer + 1) % PHYS_STATE_SIZE; \ - }while(0) - -#define JD_UNSET_FLAGS(flags) do { \ - target_disable_irq(); \ - test_status &= ~(flags); \ - phys_state[phys_pointer] = test_status | (__LINE__ << 16); \ - target_enable_irq(); \ - phys_pointer = (phys_pointer + 1) % PHYS_STATE_SIZE; \ - }while(0) - -#else -#define JD_SET_FLAGS(flags) do { \ - test_status |= (flags); \ - }while(0) - -#define JD_UNSET_FLAGS(flags) do { \ - test_status &= ~(flags); \ - }while(0) -#endif - -#define TIMEOUT_CC 0 -#define TX_CALLBACK_CC 0 // reuse the above channel - -#define TIMER_CHANNELS_REQUIRED 2 - -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) - -#define COMM_LED_LO ((this->commLEDActiveLo) ? 1 : 0) -#define COMM_LED_HI ((this->commLEDActiveLo) ? 0 : 1) - -#define BUS_LED_LO ((this->busLEDActiveLo) ? 1 : 0) -#define BUS_LED_HI ((this->busLEDActiveLo) ? 0 : 1) - -using namespace codal; - -volatile uint32_t test_status = 0; - -JDDiagnostics diagnostics; -JDPhysicalLayer* JDPhysicalLayer::instance = NULL; - -struct BaudByte -{ - uint32_t baud; - uint32_t time_per_byte; -}; - -/** - * A simple look up for getting the time per byte given a baud rate. - * - * JDBaudRate index - 1 should be used to index this array. - * - * i.e. baudToByteMap[(uint8_t)Baud1M - 1].baud = 1000000 - **/ -const BaudByte baudToByteMap[] = -{ - {1000000, 10}, - {500000, 20}, - {0, 0}, - {250000, 40}, - {0, 0}, - {0, 0}, - {0, 0}, - {125000, 80}, -}; - -// https://graphics.stanford.edu/~seander/bithacks.html -// <3 -inline uint32_t ceil_pow2(uint32_t v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; -} - -void jacdac_gpio_irq(int state) -{ - if (JDPhysicalLayer::instance) - JDPhysicalLayer::instance->_gpioCallback(state); -} - - -void jacdac_timer_irq(uint16_t channels) -{ - if (JDPhysicalLayer::instance) - JDPhysicalLayer::instance->_timerCallback(channels); -} - -void jacdac_sws_dma_irq(uint16_t errCode) -{ - if (JDPhysicalLayer::instance) - JDPhysicalLayer::instance->_dmaCallback(errCode); -} - -void JDPhysicalLayer::_gpioCallback(int state) -{ - uint32_t now = timer.captureCounter(); - - SET_GPIO(1); - if (state) - { - JD_SET_FLAGS(JD_SERIAL_BUS_STATE); - SET_GPIO1(1); - if (test_status & JD_SERIAL_ERR_MSK) - { - // all flags - CODAL_ASSERT(!(test_status & (JD_SERIAL_RECEIVING | JD_SERIAL_RECEIVING_HEADER | JD_SERIAL_TRANSMITTING)),test_status) - - startTime = now; - timer.setCompare(TIMEOUT_CC, startTime + JD_BYTE_AT_125KBAUD); - } - else if (test_status & JD_SERIAL_RX_LO_PULSE) - { - CODAL_ASSERT(!(test_status & (JD_SERIAL_RECEIVING | JD_SERIAL_RECEIVING_HEADER | JD_SERIAL_TRANSMITTING)),test_status) - JD_UNSET_FLAGS(JD_SERIAL_RX_LO_PULSE); - SET_GPIO(1); - loPulseDetected((now > startTime) ? now - startTime : startTime - now); - } - } - else - { - SET_GPIO1(0); - JD_UNSET_FLAGS(JD_SERIAL_BUS_STATE); - startTime = now; - - if (!(test_status & JD_SERIAL_ERR_MSK)) - { - timer.setCompare(TIMEOUT_CC, startTime + (2 * JD_BYTE_AT_125KBAUD)); - JD_SET_FLAGS(JD_SERIAL_RX_LO_PULSE); - } - else - { - // JD_DMESG("B"); - } - } - SET_GPIO(0); -} - -void JDPhysicalLayer::errorState(JDBusErrorState es) -{ - JD_DMESG("ERROR! %d",es); - // first time entering the error state? - if (es != JDBusErrorState::Continuation) - { - JD_DMESG("ERROR! %d",es); - SET_GPIO(0); - SET_GPIO3(1); - setState(JDSerialState::Off); - JD_UNSET_FLAGS(JD_SERIAL_RECEIVING | JD_SERIAL_RECEIVING_HEADER | JD_SERIAL_RX_LO_PULSE | JD_SERIAL_TRANSMITTING | JD_SERIAL_BUS_STATE); - - if (es == JD_SERIAL_BUS_TIMEOUT_ERROR) - diagnostics.bus_timeout_error++; - - if (es == JD_SERIAL_BUS_UART_ERROR) - diagnostics.bus_uart_error++; - - if (es == JD_SERIAL_BUS_LO_ERROR) - diagnostics.bus_lo_error++; - - if (es == JD_SERIAL_BUS_TIMEOUT_ERROR || es == JD_SERIAL_BUS_UART_ERROR) - { - JD_DMESG("ABRT"); - sws.abortDMA(); - sws.setMode(SingleWireDisconnected); - - if (commLED) - commLED->setDigitalValue(COMM_LED_LO); - } - - JD_UNSET_FLAGS(JD_SERIAL_BUS_STATE); - // update the bus state before enabling our IRQ. - JD_SET_FLAGS(es | (sp.getDigitalValue(PullMode::Up) ? JD_SERIAL_BUS_STATE : 0)); - // JD_DMESG("EST %d",test_status); - startTime = timer.captureCounter(); - - timer.setCompare(TIMEOUT_CC, startTime + JD_BYTE_AT_125KBAUD); - setState(JDSerialState::ErrorRecovery); - // Event(this->id, JD_SERIAL_EVT_BUS_ERROR); - - if (busLED) - busLED->setDigitalValue(BUS_LED_LO); - return; - } - - // in error mode we detect if there is activity on the bus (we do this by resetting start time when the bus is toggled, - // until the bus becomes idle defined by a period of JD_BUS_NORMALITY_PERIOD) - uint32_t endTime = timer.captureCounter(); - - if (test_status & JD_SERIAL_BUS_STATE && endTime - startTime >= JD_BUS_NORMALITY_PERIOD) - { - if (busLED) - busLED->setDigitalValue(BUS_LED_HI); - // JD_DMESG("B4 %d",test_status); - JD_UNSET_FLAGS(JD_SERIAL_ERR_MSK); - // resume normality - setState(JDSerialState::ListeningForPulse); - - // setup tx interrupt - timer.setCompare(TX_CALLBACK_CC, timer.captureCounter() + (JD_MIN_INTERFRAME_SPACING + target_random(JD_SERIAL_TX_MAX_BACKOFF))); - // JD_DMESG("EXITE %d",test_status); - SET_GPIO3(0); - return; - } - - timer.setCompare(TIMEOUT_CC, timer.captureCounter() + JD_BYTE_AT_125KBAUD); -} - -void JDPhysicalLayer::_timerCallback(uint16_t channels) -{ - target_disable_irq(); - // JD_SET_FLAGS(JD_SERIAL_DEBUG_BIT); - SET_GPIO2(1); - // JD_DMESG("TC %d",test_status); - if (test_status & JD_SERIAL_ERR_MSK) - { - // JD_DMESG("CONT ERR %d",test_status); - errorState(JDBusErrorState::Continuation); - SET_GPIO2(0); - JD_UNSET_FLAGS(JD_SERIAL_DEBUG_BIT); - target_enable_irq(); - return; - } - - if (test_status & JD_SERIAL_TX_LO_PULSE) - { - sendPacket(); - target_enable_irq(); - return; - } - - if (test_status & (JD_SERIAL_RECEIVING | JD_SERIAL_RECEIVING_HEADER | JD_SERIAL_TRANSMITTING)) - { - target_enable_irq(); - uint32_t comparison = ((test_status & JD_SERIAL_RECEIVING_HEADER && lastBufferedCount == 0) ? JD_MAX_INTERLODATA_SPACING : JD_MAX_INTERBYTE_SPACING); - uint32_t endTime = timer.captureCounter(); - uint32_t byteCount = (test_status & JD_SERIAL_TRANSMITTING) ? sws.getBytesTransmitted() : sws.getBytesReceived(); - JD_DMESG("lbc: %d br: %d",lastBufferedCount, byteCount); - - // if we've not received data since last check - // we break up the if statements to ensure that startTime is not updated if we're waiting for bytes. - if (lastBufferedCount == byteCount) - { - if (endTime - startTime >= comparison) - { - JD_DMESG("%d",test_status); - errorState(JDBusErrorState::BusTimeoutError); - SET_GPIO2(0); - JD_UNSET_FLAGS(JD_SERIAL_DEBUG_BIT); - return; - } - } - // if we have received data. - else - { - startTime = endTime; - lastBufferedCount = byteCount; - } - - timer.setCompare(TIMEOUT_CC, endTime + JD_BYTE_AT_125KBAUD); - SET_GPIO2(0); - JD_UNSET_FLAGS(JD_SERIAL_DEBUG_BIT); - return; - } - - if (test_status & JD_SERIAL_RX_LO_PULSE) - { - errorState(JDBusErrorState::BusLoError); - SET_GPIO2(0); - JD_UNSET_FLAGS(JD_SERIAL_DEBUG_BIT); - target_enable_irq(); - return; - } - - // if we reach here, we are neither transmitting, receiving or in an error state - // check the flag one last time as we can be preempted by the GPIO interrupt - if ((this->txHead != this->txTail || txBuf)) - { - if (setState(JDSerialState::Off) == 0) - { - JD_DMESG("BUS LO"); - - if (busLED) - busLED->setDigitalValue(BUS_LED_LO); - - if (commLED) - commLED->setDigitalValue(COMM_LED_LO); - - JD_DMESG("TXLO ERR"); - errorState(JDBusErrorState::BusLoError); - } - else - sendPacket(); - - target_enable_irq(); - return; - } - SET_GPIO2(0); - // JD_UNSET_FLAGS(JD_SERIAL_DEBUG_BIT); - timer.setCompare(TX_CALLBACK_CC, timer.captureCounter() + 100 + target_random(JD_SERIAL_TX_MAX_BACKOFF)); - target_enable_irq(); -} - -void JDPhysicalLayer::_dmaCallback(uint16_t errCode) -{ - SET_GPIO(1); - timer.clearCompare(TIMEOUT_CC); - - JD_DMESG("DMA %d", errCode); - CODAL_ASSERT(errCode != 0, 0xF00); - - // rx complete, queue packet for later handling - if (errCode == SWS_EVT_DATA_RECEIVED) - { - if (test_status & JD_SERIAL_RECEIVING_HEADER) - { - JD_DMESG("HEAD"); - JD_UNSET_FLAGS(JD_SERIAL_RECEIVING_HEADER); - if (rxBuf->size) - { - // here we start a new dma transaction, reset lastBufferedCount... - lastBufferedCount = 0; - sws.receiveDMA(((uint8_t*)rxBuf) + JD_SERIAL_HEADER_SIZE, rxBuf->size); - timer.setCompare(TIMEOUT_CC, startTime + JD_MAX_INTERBYTE_SPACING); - CODAL_ASSERT(!(test_status & JD_SERIAL_RECEIVING), test_status); - JD_SET_FLAGS(JD_SERIAL_RECEIVING); - SET_GPIO(0); - // JD_DMESG("RXH %d",rxBuf->size); - return; - } - // JD_DMESG("RXH %d",rxBuf->size); - } - else if (test_status & JD_SERIAL_RECEIVING) - { - JD_UNSET_FLAGS(JD_SERIAL_RECEIVING); - - // CRC is computed at the control layer. - rxBuf->communication_rate = (uint8_t)currentBaud; - // move rxbuf to rxArray and allocate new buffer. - int ret = addToRxArray(rxBuf); - if (ret == DEVICE_OK) - { - JD_DMESG("RXD[%d,%d]",this->rxHead, this->rxTail); - rxBuf = (JDPacket*)malloc(sizeof(JDPacket)); - diagnostics.packets_received++; - } - else - diagnostics.packets_dropped++; - - Event(id, JD_SERIAL_EVT_DATA_READY); - // SET_GPIO1(0); - SET_GPIO(0); - } - } - - if (errCode == SWS_EVT_DATA_SENT) - { - JD_UNSET_FLAGS(JD_SERIAL_TRANSMITTING); - free(txBuf); - txBuf = NULL; - diagnostics.packets_sent++; - // JD_DMESG("DMA TXD"); - } - - // JD_DMESG("DMAC"); - if (errCode == SWS_EVT_ERROR) - { - SET_GPIO(0); - // SET_GPIO1(0); - // we should never have the lo pulse flag set here. - CODAL_ASSERT(!(test_status & JD_SERIAL_RX_LO_PULSE), test_status); - JD_DMESG("BUART ERR %d",test_status); - errorState(JDBusErrorState::BusUARTError); - return; - } - - SET_GPIO(0); - sws.setMode(SingleWireDisconnected); - - // force transition to output so that the pin is reconfigured. - // also drive the bus high for a little bit. - sp.setDigitalValue(1); - setState(JDSerialState::ListeningForPulse); - - timer.setCompare(TX_CALLBACK_CC, timer.captureCounter() + (JD_MIN_INTERFRAME_SPACING + target_random(JD_SERIAL_TX_MAX_BACKOFF))); - - if (commLED) - commLED->setDigitalValue(COMM_LED_LO); -} - -void JDPhysicalLayer::loPulseDetected(uint32_t pulseTime) -{ - // JD_DMESG("LO: %d %d", (test_status & JD_SERIAL_RECEIVING) ? 1 : 0, (test_status & JD_SERIAL_TRANSMITTING) ? 1 : 0); - // guard against repeat events. - if (test_status & (JD_SERIAL_RECEIVING | JD_SERIAL_RECEIVING_HEADER | JD_SERIAL_TRANSMITTING) || !(test_status & DEVICE_COMPONENT_RUNNING)) - return; - - // JD_DMESG("TS: %d", pulseTime); - pulseTime = ceil(pulseTime / 10); - pulseTime = ceil_pow2(pulseTime); - - JD_DMESG("TS: %d", pulseTime); - - // we support 1, 2, 4, 8 as our powers of 2. - if (pulseTime < (uint8_t)this->maxBaud || pulseTime > 8) - { - SET_GPIO2(0); - errorState(JDBusErrorState::BusUARTError); - return; - } - - // 1 us to here - if ((JDBaudRate)pulseTime != this->currentBaud) - { - JD_DMESG("SB: %d",baudToByteMap[pulseTime - 1].baud); - sws.setBaud(baudToByteMap[pulseTime - 1].baud); - this->currentBaud = (JDBaudRate)pulseTime; - } - - // 1 more us - setState(JDSerialState::Off); - - // 10 us - // JD_DMESG("LO"); - JD_SET_FLAGS(JD_SERIAL_RECEIVING_HEADER); - - lastBufferedCount = 0; - sws.receiveDMA((uint8_t*)rxBuf, JD_SERIAL_HEADER_SIZE); - - // 14 more us - timer.setCompare(TIMEOUT_CC, startTime + JD_BYTE_AT_125KBAUD); - - if (commLED) - commLED->setDigitalValue(COMM_LED_HI); - // JD_DMESG("RXSTRT"); -} - -int JDPhysicalLayer::setState(JDSerialState state) -{ - uint32_t eventType = 0; - - if (state == JDSerialState::ListeningForPulse) - eventType = DEVICE_PIN_INTERRUPT_ON_EDGE; - - if (state == JDSerialState::ErrorRecovery) - eventType = DEVICE_PIN_INTERRUPT_ON_EDGE; - - if (state == JDSerialState::Off) - eventType = DEVICE_PIN_EVENT_NONE; - - sp.eventOn(eventType); - - this->state = state; - return sp.getDigitalValue(PullMode::Up); -} - -/** - * Constructor - * - * @param p the transmission pin to use - * - * @param sws an instance of sws created using p. - */ -JDPhysicalLayer::JDPhysicalLayer(DMASingleWireSerial& sws, LowLevelTimer& timer, Pin* busLED, Pin* commStateLED, bool busLEDActiveLo, bool commLEDActiveLo, JDBaudRate maxBaudRate, uint16_t id) : sws(sws), sp(sws.p), timer(timer), busLED(busLED), commLED(commStateLED) -{ - this->id = id; - this->maxBaud = maxBaudRate; - this->sniffer = NULL; - - instance = this; - - rxBuf = NULL; - txBuf = NULL; - memset(rxArray, 0, sizeof(JDPacket*) * JD_RX_ARRAY_SIZE); - memset(txArray, 0, sizeof(JDPacket*) * JD_TX_ARRAY_SIZE); - - this->txTail = 0; - this->txHead = 0; - - this->rxTail = 0; - this->rxHead = 0; - - this->busLEDActiveLo = busLEDActiveLo; - this->commLEDActiveLo = commLEDActiveLo; - - test_status = 0; - - // 32 bit 1 us timer. - timer.setBitMode(BitMode32); - timer.setClockSpeed(1000); - timer.setIRQ(jacdac_timer_irq); - timer.setIRQPriority(1); - timer.enable(); - - sp.setIRQ(jacdac_gpio_irq); - sp.getDigitalValue(PullMode::None); - - sws.setIRQ(jacdac_sws_dma_irq); -} - -/** - * Retrieves the first packet on the rxQueue irregardless of the device_class - * - * @returns the first packet on the rxQueue or NULL - */ -JDPacket* JDPhysicalLayer::getPacket() -{ - JDPacket* pkt = popRxArray(); - - if (pkt && this->sniffer) - sniffer->handlePacket(pkt); - - return pkt; -} - -/** - * Causes this instance of JDPhysicalLayer to begin listening for packets transmitted on the serial line. - */ -void JDPhysicalLayer::start() -{ - if (isRunning()) - return; - - if (rxBuf == NULL) - rxBuf = (JDPacket*)malloc(sizeof(JDPacket)); - - JD_SET_FLAGS(DEVICE_COMPONENT_RUNNING); - - // check if the bus is lo here and change our led - sp.getDigitalValue(PullMode::Up); - setState(JDSerialState::ListeningForPulse); - - timer.setCompare(TX_CALLBACK_CC, timer.captureCounter() + 100 + target_random(JD_SERIAL_TX_MAX_BACKOFF)); - - if (busLED) - busLED->setDigitalValue(BUS_LED_HI); - - Event(this->id, JD_SERIAL_EVT_BUS_CONNECTED); -} - -/** - * Causes this instance of JDPhysicalLayer to stop listening for packets transmitted on the serial line. - */ -void JDPhysicalLayer::stop() -{ - if (!isRunning()) - return; - - JD_UNSET_FLAGS(DEVICE_COMPONENT_RUNNING); - if (rxBuf) - { - free(rxBuf); - rxBuf = NULL; - } - - setState(JDSerialState::Off); - - if (busLED) - busLED->setDigitalValue(BUS_LED_LO); - - Event(this->id, JD_SERIAL_EVT_BUS_DISCONNECTED); -} - -void JDPhysicalLayer::sendPacket() -{ - // if we're not transmitting and we have stuff to transmit - if (!(test_status & (JD_SERIAL_TX_LO_PULSE | JD_SERIAL_TRANSMITTING))) - { - // If we get here, we assume we have control of the bus. - // check if we have stuff to send, txBuf will be set if a previous send failed. - JD_DMESG("TX B"); - JD_SET_FLAGS(JD_SERIAL_TX_LO_PULSE); - - // we may have a packet that we previously tried to send, but was aborted for some reason. - if (txBuf == NULL) - txBuf = popTxArray(); - - sp.setDigitalValue(0); - target_wait_us(baudToByteMap[(uint8_t)txBuf->communication_rate - 1].time_per_byte); - sp.setDigitalValue(1); - - if (txBuf->communication_rate != (uint8_t)currentBaud) - { - sws.setBaud(baudToByteMap[(uint8_t)txBuf->communication_rate - 1].baud); - currentBaud = (JDBaudRate)txBuf->communication_rate; - } - - // configure to come back after the minimum lo_data gap has been observed. - timer.setCompare(TIMEOUT_CC, timer.captureCounter() + JD_MIN_INTERLODATA_SPACING); - JD_DMESG("LO"); - return; - } - - // we've returned after a DMA transfer has been flagged (above)... start - if (test_status & JD_SERIAL_TX_LO_PULSE) - { - JD_UNSET_FLAGS(JD_SERIAL_TX_LO_PULSE); - JD_SET_FLAGS(JD_SERIAL_TRANSMITTING); - startTime = timer.captureCounter(); - lastBufferedCount = 0; - sws.sendDMA((uint8_t *)txBuf, txBuf->size + JD_SERIAL_HEADER_SIZE); - timer.setCompare(TIMEOUT_CC, startTime + JD_BYTE_AT_125KBAUD); - if (commLED) - commLED->setDigitalValue(COMM_LED_HI); - return; - } -} - -int JDPhysicalLayer::queuePacket(JDPacket* tx) -{ - if (tx == NULL) - return DEVICE_INVALID_PARAMETER; - - // don't queue packets if JDPhysicalLayer is not running - if (!isRunning()) - return DEVICE_INVALID_STATE; - - uint8_t nextTail = (this->txTail + 1) % JD_TX_ARRAY_SIZE; - - if (nextTail == this->txHead) - return DEVICE_NO_RESOURCES; - - JDPacket* pkt = (JDPacket *)malloc(sizeof(JDPacket)); - memset(pkt, 0, sizeof(JDPacket)); - memcpy(pkt, tx, sizeof(JDPacket)); - - int ret = addToTxArray(pkt); - - if (ret == DEVICE_OK && this->sniffer) - sniffer->handlePacket(pkt); - - return ret; -} - -/** - * Sends a packet using the SingleWireSerial instance. This function begins the asynchronous transmission of a packet. - * If an ongoing asynchronous transmission is happening, JD is added to the txQueue. If this is the first packet in a while - * asynchronous transmission is begun. - * - * @param JD the packet to send. - * - * @returns DEVICE_OK on success, DEVICE_INVALID_PARAMETER if JD is NULL, or DEVICE_NO_RESOURCES if the queue is full. - */ -int JDPhysicalLayer::send(JDPacket* tx, JDDevice* device, bool computeCRC) -{ - JD_DMESG("SEND"); - if (tx == NULL) - return DEVICE_INVALID_PARAMETER; - - if (tx->communication_rate < (uint8_t) maxBaud) - tx->communication_rate = (uint8_t)maxBaud; - - // crc is calculated from the address field onwards - if (computeCRC) - { - uint8_t* addressPointer = (uint8_t*)&tx->device_address; - tx->crc = jd_crc(addressPointer, tx->size + JD_SERIAL_CRC_HEADER_SIZE, device); - } - - return queuePacket(tx); -} - -/** - * Sends a packet using the SingleWireSerial instance. This function begins the asynchronous transmission of a packet. - * If an ongoing asynchronous transmission is happening, pkt is added to the txQueue. If this is the first packet in a while - * asynchronous transmission is begun. - * - * @param buf the buffer to send. - * - * @param len the length of the buffer to send. - * - * @returns DEVICE_OK on success, DEVICE_INVALID_PARAMETER if buf is NULL or len is invalid, or DEVICE_NO_RESOURCES if the queue is full. - */ -int JDPhysicalLayer::send(uint8_t* buf, int len, uint8_t service_number, JDDevice* device) -{ - if (buf == NULL || len <= 0 || len > JD_SERIAL_MAX_PAYLOAD_SIZE || service_number > JD_SERIAL_MAX_SERVICE_NUMBER) - { - JD_DMESG("pkt TOO BIG: %d ",len); - return DEVICE_INVALID_PARAMETER; - } - - JDPacket pkt; - memset(&pkt, 0, sizeof(JDPacket)); - - if (device) - { - pkt.device_address = device->device_address; - pkt.communication_rate = device->communication_rate; - } - else - { - pkt.device_address = 0; - pkt.communication_rate = (uint8_t)this->getMaximumBaud(); - } - - pkt.crc = 0; - pkt.service_number = service_number; - pkt.size = len; - - memcpy(pkt.data, buf, len); - - return send(&pkt, device, true); -} - -/** - * Returns a bool indicating whether the JDPhysicalLayer driver has been started. - * - * @return true if started, false if not. - **/ -bool JDPhysicalLayer::isRunning() -{ - return (test_status & DEVICE_COMPONENT_RUNNING) ? true : false; -} - -/** - * Returns the current state if the bus. - * - * @return true if connected, false if there's a bad bus condition. - **/ -bool JDPhysicalLayer::isConnected() -{ - if (test_status & JD_SERIAL_ERR_MSK) - return false; - - return true; -} - -/** - * Returns the current state of the bus, either: - * - * * Receiving if the driver is in the process of receiving a packet. - * * Transmitting if the driver is communicating a packet on the bus. - * - * If neither of the previous states are true, then the driver looks at the bus and returns the bus state: - * - * * High, if the line is currently floating high. - * * Lo if something is currently pulling the line low. - **/ -JDBusState JDPhysicalLayer::getState() -{ - - if (test_status & (JD_SERIAL_RECEIVING | JD_SERIAL_RECEIVING_HEADER)) - return JDBusState::Receiving; - - if (test_status & JD_SERIAL_TRANSMITTING) - return JDBusState::Transmitting; - - return JDBusState::Unknown; -} - - -uint8_t JDPhysicalLayer::getErrorState() -{ - return (test_status & JD_SERIAL_ERR_MSK) >> 4; -} - -JDDiagnostics JDPhysicalLayer::getDiagnostics() -{ - diagnostics.bus_state = getErrorState(); - return diagnostics; -} - -int JDPhysicalLayer::setMaximumBaud(JDBaudRate baud) -{ - this->maxBaud = baud; - return DEVICE_OK; -} - -JDBaudRate JDPhysicalLayer::getMaximumBaud() -{ - return this->maxBaud; -} - -/** - * Our arrays are FIFO circular buffers. - * - * Example: - * txHead txTail - * [item, item, item, item, NULL, NULL, NULL] - * - * Remove: - * txHead txTail - * [NULL, item, item, item, NULL, NULL, NULL] - * - * Add: - * txHead txTail - * [NULL, item, item, item, item, NULL, NULL] - **/ -JDPacket* JDPhysicalLayer::popRxArray() -{ - // nothing to pop - if (this->rxTail == this->rxHead) - return NULL; - - target_disable_irq(); - uint8_t nextHead = (this->rxHead + 1) % JD_RX_ARRAY_SIZE; - JDPacket* p = rxArray[this->rxHead]; - this->rxArray[this->rxHead] = NULL; - this->rxHead = nextHead; - target_enable_irq(); - - // JD_DMESG("POP[%d,%d]",this->rxHead, this->rxTail); - - return p; -} - -/** - * Our arrays are FIFO circular buffers. - * - * Example: - * txHead txTail - * [item, item, item, item, NULL, NULL, NULL] - * - * Remove: - * txHead txTail - * [NULL, item, item, item, NULL, NULL, NULL] - * - * Add: - * txHead txTail - * [NULL, item, item, item, item, NULL, NULL] - **/ -JDPacket* JDPhysicalLayer::popTxArray() -{ - // nothing to pop - if (this->txTail == this->txHead) - return NULL; - - uint8_t nextHead = (this->txHead + 1) % JD_TX_ARRAY_SIZE; - JDPacket* p = txArray[this->txHead]; - this->txArray[this->txHead] = NULL; - target_disable_irq(); - this->txHead = nextHead; - target_enable_irq(); - - return p; -} - -/** - * Our arrays are FIFO circular buffers. - * - * Example: - * txHead txTail - * [item, item, item, item, NULL, NULL, NULL] - * - * Remove: - * txHead txTail - * [NULL, item, item, item, NULL, NULL, NULL] - * - * Add: - * txHead txTail - * [NULL, item, item, item, item, NULL, NULL] - **/ -int JDPhysicalLayer::addToTxArray(JDPacket* packet) -{ - uint8_t nextTail = (this->txTail + 1) % JD_TX_ARRAY_SIZE; - - if (nextTail == this->txHead) - return DEVICE_NO_RESOURCES; - - // add our buffer to the array before updating the head - // this ensures atomicity. - this->txArray[this->txTail] = packet; - target_disable_irq(); - this->txTail = nextTail; - target_enable_irq(); - - return DEVICE_OK; -} - -/** - * Our arrays are FIFO circular buffers. - * - * Example: - * txHead txTail - * [item, item, item, item, NULL, NULL, NULL] - * - * Remove: - * txHead txTail - * [NULL, item, item, item, NULL, NULL, NULL] - * - * Add: - * txHead txTail - * [NULL, item, item, item, item, NULL, NULL] - **/ -int JDPhysicalLayer::addToRxArray(JDPacket* packet) -{ - uint8_t nextTail = (this->rxTail + 1) % JD_RX_ARRAY_SIZE; - - if (nextTail == this->rxHead) - return DEVICE_NO_RESOURCES; - - // add our buffer to the array before updating the head - // this ensures atomicity. - this->rxArray[this->rxTail] = packet; - target_disable_irq(); - this->rxTail = nextTail; - target_enable_irq(); - - return DEVICE_OK; -} \ No newline at end of file diff --git a/source/JACDAC/JDService.cpp b/source/JACDAC/JDService.cpp deleted file mode 100644 index 941bb570..00000000 --- a/source/JACDAC/JDService.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2017 Lancaster University. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ -#include "JDService.h" -#include "CodalDmesg.h" -#include "codal_target_hal.h" -#include "EventModel.h" -#include "CodalFiber.h" -#include "JACDAC.h" - -using namespace codal; - -uint32_t JDService::dynamicId = DEVICE_ID_JD_DYNAMIC_ID; - -int JDService::addAdvertisementData(uint8_t* data) -{ - // by default, the control service will fill in the required information. - // any additional information should be added here.... - return 0; -} - -int JDService::send(uint8_t* buf, int len) -{ - if (JACDAC::instance && this->device) - return JACDAC::instance->bus.send(buf, len, this->service_number, this->device); - - return DEVICE_NO_RESOURCES; -} - -JDService::JDService(uint32_t service_class, JDServiceMode m) -{ - // we use a dynamic id for the message bus for simplicity. - // with the dynamic nature of JACDAC, it would be hard to maintain a consistent id. - this->id = dynamicId++; - this->mode = m; - this->device = NULL; - this->requiredDevice = NULL; - this->service_class = service_class; - this->service_number = JD_SERVICE_NUMBER_UNINITIALISED_VAL; - - if (JACDAC::instance) - JACDAC::instance->add(*this); -} - -bool JDService::isConnected() -{ - return (this->status & JD_SERVICE_STATUS_FLAGS_INITIALISED) ? true : false; -} - -int JDService::hostConnected() -{ - // DMESG("CONNB a:%d sn:%d cl:%d",device.address,device.serial_number, device.service_class); - this->status |= JD_SERVICE_STATUS_FLAGS_INITIALISED; - Event(this->id, JD_SERVICE_EVT_CONNECTED); - return DEVICE_OK; -} - -int JDService::hostDisconnected() -{ - // DMESG("DISCB a:%d sn:%d cl: %d",device.address,device.serial_number, device.service_class); - this->status &= ~(JD_SERVICE_STATUS_FLAGS_INITIALISED); - Event(this->id, JD_SERVICE_EVT_DISCONNECTED); - return DEVICE_OK; -} - -JDDevice* JDService::getHostDevice() -{ - return this->device; -} - -uint32_t JDService::getServiceClass() -{ - return this->service_class; -} - -int JDService::handleServiceInformation(JDDevice* device, JDServiceInformation* info) -{ - return DEVICE_OK; -} - -int JDService::handlePacket(JDPacket* p) -{ - return DEVICE_OK; -} - -JDService::~JDService() -{ - JACDAC::instance->remove(*this); -} \ No newline at end of file diff --git a/source/JACDAC/control/JDCRC.cpp b/source/JACDAC/control/JDCRC.cpp deleted file mode 100644 index a8cf25ff..00000000 --- a/source/JACDAC/control/JDCRC.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "JDCRC.h" - -uint16_t codal::jd_crc(uint8_t *data, uint32_t len, JDDevice* device) -{ - uint16_t crc = 0xfff; - - int i = 0; - if (device != NULL) - { - uint8_t* udidPtr = (uint8_t*)&device->unique_device_identifier; - while (i < 8) - { - crc ^= (*udidPtr++ << 8); - for (int i = 0; i < 8; ++i) - { - if (crc & 0x800) - crc = crc << 1 ^ JD_CRC_POLYNOMIAL; - else - crc = crc << 1; - } - i++; - } - } - - while (len--) - { - crc ^= (*data++ << 8); - for (int i = 0; i < 8; ++i) - { - if (crc & 0x800) - crc = crc << 1 ^ JD_CRC_POLYNOMIAL; - else - crc = crc << 1; - } - } - - return crc & 0xFFF; -} - diff --git a/source/JACDAC/control/JDConfigurationService.cpp b/source/JACDAC/control/JDConfigurationService.cpp deleted file mode 100644 index 3825c5bd..00000000 --- a/source/JACDAC/control/JDConfigurationService.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "JDConfigurationService.h" -#include "JACDAC.h" -#include "CodalDmesg.h" - -using namespace codal; - -int JDConfigurationService::send(uint8_t* buf, int len) -{ - if (JACDAC::instance) - return JACDAC::instance->bus.send(buf, len, this->service_number, NULL); - - return DEVICE_NO_RESOURCES; -} - -JDConfigurationService::JDConfigurationService(uint16_t id) : JDService(JD_SERVICE_CLASS_CONTROL_CONFIGURATION, ControlLayerService) -{ - this->service_number = JD_CONTROL_CONFIGURATION_SERVICE_NUMBER; - this->id = id; -} - -int JDConfigurationService::handlePacket(JDPacket* p) -{ - JDConfigurationPacket* pkt = (JDConfigurationPacket *)p->data; - JD_DMESG("CFG size: %d", p->size); - - if (this->device) - { - if (pkt->device_address == this->device->device_address) - { - if (pkt->request_type == JD_CONTROL_CONFIGURATION_SERVICE_REQUEST_TYPE_NAME) - { - uint8_t* namePtr = pkt->data; - int len = *namePtr++; - JACDAC::instance->setDeviceName(ManagedString((char *)namePtr, len)); - Event(this->id, JD_CONTROL_CONFIGURATION_EVT_NAME); - } - - if (pkt->request_type == JD_CONTROL_CONFIGURATION_SERVICE_REQUEST_TYPE_IDENTIFY) - Event(this->id, JD_CONTROL_CONFIGURATION_EVT_IDENTIFY); - } - } - - return DEVICE_OK; -} - -int JDConfigurationService::setRemoteDeviceName(uint8_t device_address, ManagedString newName) -{ - int len = newName.length(); - - if (len > JD_SERIAL_MAX_PAYLOAD_SIZE) - return DEVICE_INVALID_PARAMETER; - - int size = JD_CONTROL_CONFIGURATION_SERVICE_PACKET_HEADER_SIZE + len + 1; - - JDConfigurationPacket* cfg = (JDConfigurationPacket *)malloc(JD_CONTROL_CONFIGURATION_SERVICE_PACKET_HEADER_SIZE + len + 1); // add one for the size byte - - cfg->request_type = JD_CONTROL_CONFIGURATION_SERVICE_REQUEST_TYPE_NAME; - cfg->device_address = device_address; - - *cfg->data = len; - - memcpy(cfg->data + 1, newName.toCharArray(), len); - - send((uint8_t *)cfg, size); - - free(cfg); - - return DEVICE_OK; -} - -int JDConfigurationService::triggerRemoteIdentification(uint8_t device_address) -{ - if (device_address == 0) - return DEVICE_INVALID_PARAMETER; - - JDConfigurationPacket cfg; - - cfg.device_address = device_address; - cfg.request_type = JD_CONTROL_CONFIGURATION_SERVICE_REQUEST_TYPE_IDENTIFY; - - send((uint8_t *)&cfg, JD_CONTROL_CONFIGURATION_SERVICE_PACKET_HEADER_SIZE); - - return DEVICE_OK; -} \ No newline at end of file diff --git a/source/JACDAC/control/JDControlService.cpp b/source/JACDAC/control/JDControlService.cpp deleted file mode 100644 index 049a8cad..00000000 --- a/source/JACDAC/control/JDControlService.cpp +++ /dev/null @@ -1,625 +0,0 @@ -#include "CodalConfig.h" -#include "JDControlService.h" -#include "CodalDmesg.h" -#include "Timer.h" -#include "JDCRC.h" -#include "JACDAC.h" -#include "CodalUtil.h" - -using namespace codal; - -uint64_t generate_eui64(uint64_t device_identifier) -{ - uint64_t unique_device_identifier; - - unique_device_identifier = device_identifier; - - uint8_t* bytePtr = (uint8_t *)&unique_device_identifier; - - // set the address to locally administered (it wasn't assigned as globally unique, it's made up). - // https://community.cisco.com/t5/networking-documents/understanding-ipv6-eui-64-bit-address/ta-p/3116953 - bytePtr[6] &= ~0x02; - - return unique_device_identifier; -} - -void JDControlService::deviceDisconnected(JDDevice* device) -{ - // iterate over services on the device and provide connect / disconnect events. - for (int i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - { - JDService* current = JACDAC::instance->services[i]; - - // don't disconnect control layer services - if (current == NULL || current->device != device || current->mode == ControlLayerService) - continue; - - current->device = NULL; - current->service_number = JD_SERVICE_NUMBER_UNINITIALISED_VAL; - current->hostDisconnected(); - } -} - -void JDControlService::deviceEnumerated() -{ - // iterate over services on the device and provide connect / disconnect events. - for (int i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - { - JDService* current = JACDAC::instance->services[i]; - - if (current == NULL || current == this || current->mode == ClientService) - continue; - - current->device = this->device; - current->hostConnected(); - } -} - -int JDControlService::formControlPacket() -{ - uint16_t size = 0; - - int nsNameLen = this->name.length(); - - // copy the name into the control packet (if we have one) - if (nsNameLen) - { - this->device->device_flags |= JD_DEVICE_FLAGS_HAS_NAME; - uint8_t* name = enumerationData->data; - name[0] = nsNameLen; - memcpy(name + 1, this->name.toCharArray(), name[0]); - size += name[0] + 1; - } - - enumerationData->unique_device_identifier = this->device->unique_device_identifier; - enumerationData->device_address = this->device->device_address; - enumerationData->device_flags = this->device->device_flags; - - // compile the list of host services for control packet - JDServiceInformation* info = (JDServiceInformation *)(enumerationData->data + size); - - int service_number = 0; - for (int i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - { - JDService* current = JACDAC::instance->services[i]; - - if (current == NULL || current->mode == ControlLayerService || current->mode == ClientService) - continue; - - // the device has modified its service numbers whilst enumerated. - if (current->service_number != JD_SERVICE_NUMBER_UNINITIALISED_VAL && current->service_number != service_number) - target_panic(DEVICE_JACDAC_ERROR); - - current->service_number = service_number; - - info = (JDServiceInformation *)(enumerationData->data + size); - - // DMESG("IPTR: %p %d",info, (unsigned long)info % 4); - - info->service_flags = current->service_flags; - info->service_class = current->service_class; - info->advertisement_size = current->addAdvertisementData(info->data); - - size += info->advertisement_size + JD_SERVICE_INFO_HEADER_SIZE; - service_number++; - } - - size += JD_CONTROL_PACKET_HEADER_SIZE; - - CODAL_ASSERT(size <= JD_SERIAL_MAX_PAYLOAD_SIZE, DEVICE_JACDAC_ERROR); - return size; -} - -/** - * Timer callback every 500 ms - **/ -void JDControlService::timerCallback(Event) -{ - // no sense continuing if we dont have a bus to transmit on... - if (!JACDAC::instance || !JACDAC::instance->bus.isRunning()) - return; - - // handle enumeration - if (this->status & JD_CONTROL_SERVICE_STATUS_ENUMERATE) - { - if (this->status & JD_CONTROL_SERVICE_STATUS_ENUMERATING) - { - this->device->rolling_counter++; - - if (this->device->rolling_counter >= JD_CONTROL_ROLLING_TIMEOUT_VAL) - { - this->status &= ~JD_CONTROL_SERVICE_STATUS_ENUMERATING; - this->status |= JD_CONTROL_SERVICE_STATUS_ENUMERATED; - this->device->device_flags &= ~JD_DEVICE_FLAGS_PROPOSING; - this->deviceEnumerated(); - } - } - else - { - if (JACDAC::instance->bus.isConnected() == false) - { - this->device->rolling_counter++; - - if (this->device->rolling_counter > 3) - { - this->status |= JD_CONTROL_SERVICE_STATUS_BUS_LO; - this->deviceDisconnected(this->device); - return; - } - } - else - { - if (this->status & JD_CONTROL_SERVICE_STATUS_BUS_LO) - { - this->deviceEnumerated(); - this->status &= ~JD_CONTROL_SERVICE_STATUS_BUS_LO; - } - - this->device->rolling_counter = 0; - } - } - - // queue a control packet if we have host services. - enumerationData->unique_device_identifier = this->device->unique_device_identifier; - enumerationData->device_address = this->device->device_address; - enumerationData->device_flags = this->device->device_flags; - - send((uint8_t*)enumerationData, formControlPacket()); - } - - // now check to see if remote devices have timed out. - JDDevice* head = this->deviceManager.getDeviceList(); - - while (head) - { - JDDevice* dev = head; - head = head->next; - dev->rolling_counter++; - - if (dev->rolling_counter >= JD_CONTROL_ROLLING_TIMEOUT_VAL) - { - this->deviceManager.removeDevice(dev); - this->deviceDisconnected(dev); - free(dev->name); - free(dev); - } - } -} - -int JDControlService::enumerate() -{ - if (this->status & JD_CONTROL_SERVICE_STATUS_ENUMERATE) - return DEVICE_INVALID_STATE; - - // these services aren't automatically added at construction as jacdac is being initialised. - JACDAC::instance->add(this->configurationService); - JACDAC::instance->add(this->rngService); - - if (enumerationData == NULL) - { - this->enumerationData = (JDControlPacket*)malloc(JD_SERIAL_MAX_PAYLOAD_SIZE); - memset(this->enumerationData, 0, JD_SERIAL_MAX_PAYLOAD_SIZE); - } - - if (device == NULL) - { - this->device = (JDDevice*)malloc(sizeof(JDDevice)); - - this->device->unique_device_identifier = generate_eui64(target_get_serial()); - - // naiive implementation for now... we can sniff the bus for a little before enumerating to - // get a good first address in the future. - this->device->device_address = 1 + random(254); - - // set the device state for the control service. - this->device->device_flags = JD_DEVICE_FLAGS_PROPOSING; - - this->device->communication_rate = JD_DEVICE_DEFAULT_COMMUNICATION_RATE; - - this->device->rolling_counter = 0; - this->device->next = NULL; - this->device->name = NULL; - } - - int hostServiceCount = 0; - - for (int i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - { - JDService* current = JACDAC::instance->services[i]; - - if (current == NULL || current->mode == ClientService || current->mode == ControlLayerService) - continue; - - hostServiceCount++; - } - - if (hostServiceCount > 0) - { - this->status |= (JD_CONTROL_SERVICE_STATUS_ENUMERATING | JD_CONTROL_SERVICE_STATUS_ENUMERATE); - return DEVICE_OK; - } - - // free enumerationData if we aren't enumerating (no host services) - free(this->enumerationData); - this->enumerationData = NULL; - - if (this->device->device_flags & JD_DEVICE_FLAGS_HAS_NAME) - free(this->device->name); - - free(this->device); - this->device = NULL; - - return DEVICE_INVALID_STATE; -} - -/** - * - **/ -bool JDControlService::isEnumerated() -{ - return this->status & JD_CONTROL_SERVICE_STATUS_ENUMERATED; -} - -/** - * - **/ -bool JDControlService::isEnumerating() -{ - return this->status & JD_CONTROL_SERVICE_STATUS_ENUMERATING; -} - -int JDControlService::disconnect() -{ - if (!(this->status & JD_CONTROL_SERVICE_STATUS_ENUMERATE)) - return DEVICE_INVALID_STATE; - - this->deviceDisconnected(this->device); - - this->status &= ~JD_CONTROL_SERVICE_STATUS_ENUMERATE; - - return DEVICE_OK; -} - -JDControlService::JDControlService(ManagedString name) : JDService(JD_SERVICE_CLASS_CONTROL, ControlLayerService), configurationService() -{ - this->name = name; - this->device = NULL; - this->remoteDevices = NULL; - this->enumerationData = NULL; - - status = 0; - status |= DEVICE_COMPONENT_RUNNING; - - if (EventModel::defaultEventBus) - { - EventModel::defaultEventBus->listen(this->id, JD_CONTROL_SERVICE_EVT_TIMER_CALLBACK, this, &JDControlService::timerCallback); - system_timer_event_every(500, this->id, JD_CONTROL_SERVICE_EVT_TIMER_CALLBACK); - } -} - -int JDControlService::send(uint8_t* buf, int len) -{ - if (JACDAC::instance) - return JACDAC::instance->bus.send(buf, len, 0, NULL); - - return DEVICE_NO_RESOURCES; -} - -int JDControlService::handleServiceInformation(JDDevice* device, JDServiceInformation* p) -{ - // nop for now... could be useful in the future for controlling the mode of the logic service? - return DEVICE_OK; -} - -void JDControlService::routePacket(JDPacket* pkt) -{ - JD_DMESG("pkt REC AD: %d sno: %d SZ:%d",pkt->device_address, pkt->service_number, pkt->size); - - JDDevice* device = NULL; - - if (pkt->device_address == this->device->device_address) - device = this->device; - else - device = this->getRemoteDevice(pkt->device_address); - - uint8_t* addressPointer = (uint8_t*)&pkt->device_address; - uint16_t crc = jd_crc(addressPointer, pkt->size + 2, device); // include size and address in the checksum. - - bool crcCheck = (crc == pkt->crc); - - if (crcCheck) - { - if (device == NULL) - { - this->handlePacket(pkt); - } - else - { - // map from device broadcast map to potentially the service number of one of our enumerated broadcast hosts - int16_t host_service_number = -1; - - if (device->servicemap_bitmsk & 1 << pkt->service_number) - { - uint8_t sn = device->broadcast_servicemap[pkt->service_number / 2]; - - if (pkt->service_number % 2 == 0) - host_service_number = sn & 0x0F; - else - host_service_number = sn & 0xF0 >> 4; - - // we now match on the control service device looking for host services. - device = this->device; - } - - bool broadcast = (host_service_number >= 0); - - // we matched a broadcast host, route to all broadcast hosts on the device. - if (broadcast) - { - uint32_t broadcast_class = 0; - - for (int i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - { - JDService* service = JACDAC::instance->services[i]; - - if (!service || !service->device || service->mode == ClientService || service->mode == ControlLayerService) - continue; - - if (service->device->device_address == device->device_address && service->service_number == host_service_number) { - JD_DMESG("BROADCAST MATCH CL: %d", service->service_class); - broadcast_class = service->service_class; - break; - } - } - - for (int i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - { - JDService* service = JACDAC::instance->services[i]; - - if (!service || !service->device || service->mode != BroadcastHostService) - continue; - - if (service->service_class == broadcast_class) - // break if DEVICE_OK is returned (indicates the packet has been handled) - if (service->handlePacket(pkt) == DEVICE_OK) - break; - } - } - else - { - for (int i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - { - JDService* service = JACDAC::instance->services[i]; - - if (!service || !service->device || service->mode == ControlLayerService) - continue; - - JD_DMESG("DRIV a:%d sn:%d c:%d i:%d f %d", service->state.device_address, service->state.serial_number, service->state.service_class, service->state.flags & JD_DEVICE_FLAGS_INITIALISED ? 1 : 0, service->state.flags); - - if (service->device->device_address == device->device_address && service->service_number == pkt->service_number) - if (service->handlePacket(pkt) == DEVICE_OK) - break; - } - } - } - } - else - { - DMESG("CRC ERR: %d %d", pkt->device_address, pkt->service_number); - } - -} - -/** - * Given a control packet, finds the associated service, or if no associated device, associates a remote device with a service. - **/ -int JDControlService::handlePacket(JDPacket* pkt) -{ - DMESG("HP %d %d", pkt->device_address, pkt->service_number); - // if the driver has not started yet, drain packets. - if (!(this->status & JD_CONTROL_SERVICE_STATUS_ENUMERATE)) - return DEVICE_OK; - - if (pkt->service_number == this->rngService.service_number) - { - DMESG("RNG SERV"); - this->rngService.handlePacket(pkt); - return DEVICE_OK; - } - - if (pkt->service_number == this->configurationService.service_number) - { - DMESG("CONF SERV"); - this->configurationService.handlePacket(pkt); - return DEVICE_OK; - } - - JDControlPacket *cp = (JDControlPacket *)pkt->data; - - // address collision check - if (this->status & (JD_CONTROL_SERVICE_STATUS_ENUMERATING | JD_CONTROL_SERVICE_STATUS_ENUMERATED) && device->device_address == cp->device_address) - { - // a different device is using our address!! - if (device->unique_device_identifier != cp->unique_device_identifier) - { - // if the device is proposing, we can reject (as per the spec) - if (cp->device_flags & JD_DEVICE_FLAGS_PROPOSING) - { - // if we're proposing too, the remote device has won the address - if (this->device->device_flags & JD_DEVICE_FLAGS_PROPOSING) - { - this->device->rolling_counter = 0; - this->device->device_address = 1 + target_random(254); - } - // if our address is established, reject the proposal - else - { - JDControlPacket rejectCP; - - rejectCP.device_address = cp->device_address; - rejectCP.unique_device_identifier = cp->unique_device_identifier; - rejectCP.device_flags = JD_DEVICE_FLAGS_REJECT; - send((uint8_t *)&rejectCP, JD_CONTROL_PACKET_HEADER_SIZE); - JD_DMESG("ASK OTHER TO REASSIGN"); - } - - return DEVICE_OK; // no further processing required. - } - } - // someone has flagged a conflict with our device address, re-enumerate - else if (cp->device_flags & JD_DEVICE_FLAGS_REJECT) - { - this->device->rolling_counter = 0; - this->device->device_address = 1 + target_random(254); - return DEVICE_OK; - } - } - - // the device has not got a confirmed address (enumerating)... if there was a collision it would be handled above - if (cp->device_flags & JD_DEVICE_FLAGS_PROPOSING) - return DEVICE_OK; - - // if a service is relying on a remote device, the control service is maintaining the state. - JDDevice* remoteDevice = this->deviceManager.getDevice(cp->device_address, cp->unique_device_identifier); - - if (remoteDevice) - this->deviceManager.updateDevice(remoteDevice, cp, pkt->communication_rate); - - // if here, address validation has completed successfully... process service information - uint8_t* dataPointer = cp->data; - int service_number = 0; - uint8_t nameSize = 0; - // skip name: - // the size of the name is the first byte of the data payload (if present) - if (cp->device_flags & JD_DEVICE_FLAGS_HAS_NAME) - { - JD_DMESG("HAS NAME %d", *dataPointer); - nameSize = (*dataPointer) + 1; // plus one for the size byte itself. - } - - dataPointer += nameSize; - uint8_t* dpStart = dataPointer; - - JD_DMESG("USDEV: a %d, s %d, c %d, i %d, t %c%c%c", this->device->device_address, this->device->unique_device_identifier, this->service_class, this->status & JD_SERVICE_STATUS_FLAGS_INITIALISED ? 1 : 0, this->mode == BroadcastHostService ? 'B' : ' ', this->mode == HostService ? 'H' : ' ', this->mode == ClientService ? 'C' : ' '); - while (dataPointer < dpStart + (pkt->size - JD_CONTROL_PACKET_HEADER_SIZE - nameSize)) - { - JDServiceInformation* serviceInfo = (JDServiceInformation *)dataPointer; - JD_DMESG("SI: addr %d, sn %d, class %d", cp->device_address, service_number, serviceInfo->service_class); - for (int i = 0; i < JD_SERVICE_ARRAY_SIZE; i++) - { - JDService* current = JACDAC::instance->services[i]; - - if (current == NULL || current->mode == ControlLayerService) - continue; - - bool class_check = current->service_class == serviceInfo->service_class; - - // if the service is running, route the packet. - if (current->status & JD_SERVICE_STATUS_FLAGS_INITIALISED) - { - bool address_check = current->device->device_address == cp->device_address && current->service_number == service_number; - bool serial_check = cp->unique_device_identifier == current->device->unique_device_identifier; - - // this boolean is used to override stringent address checks (not needed for broadcast services as they receive all packets) to prevent code duplication - bool broadcast_override = current->mode == BroadcastHostService; - JD_DMESG("INITDSer: a %d, s %d, c %d, i %d, t %c%c%c", current->device->device_address, current->device->unique_device_identifier, current->service_class, current->status & JD_SERVICE_STATUS_FLAGS_INITIALISED ? 1 : 0, current->mode == BroadcastHostService ? 'B' : ' ', current->mode == HostService ? 'H' : ' ', current->mode == ClientService ? 'C' : ' '); - - // check if applicable - if ((address_check && serial_check && class_check) || (class_check && broadcast_override)) - { - // we are receiving a packet from a remote device for a service in broadcast mode. - if (broadcast_override && cp->device_address != this->device->device_address) - { - // create a device representation if none exists - if (!remoteDevice) - remoteDevice = this->deviceManager.addDevice(cp, pkt->communication_rate); - - int idx = service_number / 2; - - remoteDevice->servicemap_bitmsk |= 1 << service_number; - - // set the service map for this broadcast service. - if (idx % 2 == 0) - remoteDevice->broadcast_servicemap[idx] = (remoteDevice->broadcast_servicemap[idx] & 0xF0) | current->service_number; - else - remoteDevice->broadcast_servicemap[idx] = current->service_number << 4 | (remoteDevice->broadcast_servicemap[idx] & 0x0F); - } - - // if the service has handled the packet it will return DEVICE_OK. - // any non zero return value will cause packet routing to continue - if (current->handleServiceInformation(remoteDevice, serviceInfo) == DEVICE_OK) - { - JD_DMESG("uS ABSORBED %d %d", current->device->device_address, current->service_class); - break; - } - } - } - else if (class_check && current->mode == ClientService) - { - JD_DMESG("UNINITDSer a %d, s %d, c %d, t %c%c%c", current->device->device_address, current->device->unique_device_identifier, current->service_class, current->mode == BroadcastHostService ? 'B' : ' ', current->mode == HostService? 'H' : ' ', current->mode == ClientService ? 'C' : ' '); - - // this service instance is looking for a specific device (either a unique_device_identifier or name) - if (current->requiredDevice) - { - if ((current->requiredDevice->unique_device_identifier > 0 && current->requiredDevice->unique_device_identifier != cp->unique_device_identifier)) - continue; - - // this service is looking for a device with a name, further check required. - if (current->requiredDevice->device_flags & JD_DEVICE_FLAGS_HAS_NAME) - { - if (!(cp->device_flags & JD_DEVICE_FLAGS_HAS_NAME)) - continue; - - // the size of the name is the first byte of the data payload (if present) - uint8_t len = *cp->data; - - if (ManagedString((char *)cp->data + 1, len) != ManagedString((char *)current->requiredDevice->name)) - continue; - } - } - - JD_DMESG("FOUND NEW: %d %d %d", current->device->device_address, current->service_class); - remoteDevice = this->deviceManager.addDevice(cp, pkt->communication_rate); - - if (current->handleServiceInformation(remoteDevice, (JDServiceInformation*)dataPointer) == DEVICE_OK) - { - current->device = remoteDevice; - current->service_number = service_number; - current->hostConnected(); - Event(this->id, JD_CONTROL_SERVICE_EVT_CHANGED); - break; - } - } - } - service_number++; - dataPointer += JD_SERVICE_INFO_HEADER_SIZE + serviceInfo->advertisement_size; - } - - return DEVICE_OK; -} - -int JDControlService::setDeviceName(ManagedString name) -{ - this->name = name; - return DEVICE_OK; -} - -ManagedString JDControlService::getDeviceName() -{ - return this->name; -} - -int JDControlService::setRemoteDeviceName(uint8_t device_address, ManagedString name) -{ - return this->configurationService.setRemoteDeviceName(device_address, name); -} - -int JDControlService::triggerRemoteIdentification(uint8_t deviceAddress) -{ - return this->configurationService.triggerRemoteIdentification(deviceAddress); -} - -JDDevice* JDControlService::getRemoteDevice(uint8_t device_address) -{ - return this->deviceManager.getDevice(device_address); -} \ No newline at end of file diff --git a/source/JACDAC/control/JDDeviceManager.cpp b/source/JACDAC/control/JDDeviceManager.cpp deleted file mode 100644 index b5c3c667..00000000 --- a/source/JACDAC/control/JDDeviceManager.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "JDDeviceManager.h" -#include "JDControlService.h" -#include "CodalDmesg.h" - -using namespace codal; - -int JDDeviceManager::initialiseDevice(JDDevice* remoteDevice, JDControlPacket* controlPacket, uint8_t communicationRate) -{ - remoteDevice->device_address = controlPacket->device_address; - remoteDevice->unique_device_identifier = controlPacket->unique_device_identifier; - remoteDevice->device_flags = controlPacket->device_flags; - remoteDevice->communication_rate = communicationRate; - remoteDevice->rolling_counter = 0; - - if (controlPacket->device_flags & JD_DEVICE_FLAGS_HAS_NAME) - { - uint8_t len = *controlPacket->data; - - if (remoteDevice->name) - { - if (len == strlen((char *)remoteDevice->name) && memcmp(controlPacket->data + 1, remoteDevice->name, len)) - return DEVICE_OK; - - free(remoteDevice->name); - remoteDevice->name = NULL; - } - - remoteDevice->name = (uint8_t *)malloc(len + 1); - memcpy(remoteDevice->name, controlPacket->data + 1, len); - remoteDevice->name[len] = 0; - } - - return DEVICE_OK; -} - -JDDeviceManager::JDDeviceManager() -{ - this->devices = NULL; -} - -JDDevice* JDDeviceManager::getDeviceList() -{ - return this->devices; -} - -JDDevice* JDDeviceManager::getDevice(uint8_t device_address) -{ - JDDevice* head = this->devices; - - while(head) - { - if (head->device_address == device_address) - return head; - - head = head->next; - } - - return NULL; -} - -JDDevice* JDDeviceManager::getDevice(uint8_t device_address, uint64_t unique_device_identifier) -{ - JDDevice* head = this->devices; - - while(head) - { - if (head->device_address == device_address && head->unique_device_identifier == unique_device_identifier) - return head; - - head = head->next; - } - - return NULL; -} - -JDDevice* JDDeviceManager::addDevice(JDControlPacket* controlPacket, uint8_t communicationRate) -{ - JDDevice* newRemote = (JDDevice *) malloc(sizeof(JDDevice)); - - newRemote->next = NULL; - newRemote->name = NULL; - newRemote->servicemap_bitmsk = 0; - - initialiseDevice(newRemote, controlPacket, communicationRate); - - if (this->devices == NULL) - this->devices = newRemote; - else - { - JDDevice* head = this->devices; - JDDevice* prev = this->devices; - - while(head) - { - // guard against duplicates. - if (head->device_address == newRemote->device_address && head->unique_device_identifier == newRemote->unique_device_identifier) - { - if (newRemote->device_flags & JD_DEVICE_FLAGS_HAS_NAME) - free (newRemote->name); - - free(newRemote); - return head; - } - - prev = head; - head = head->next; - } - - prev->next = newRemote; - } - - return newRemote; -} - -int JDDeviceManager::updateDevice(JDDevice* remoteDevice, JDControlPacket* controlPacket, uint8_t communicationRate) -{ - return initialiseDevice(remoteDevice, controlPacket, communicationRate); -} - -int JDDeviceManager::removeDevice(JDDevice* device) -{ - if (this->devices == NULL) - return DEVICE_INVALID_PARAMETER; - - JDDevice* curr = this->devices; - JDDevice* prev = NULL; - - if (device == curr) - this->devices = curr->next; - else - { - prev = curr; - curr = curr->next; - - while (curr) - { - // found!! - if (curr->device_address == device->device_address && curr->unique_device_identifier == device->unique_device_identifier) - { - prev->next = curr->next; - return DEVICE_OK; - } - - prev = curr; - curr = curr->next; - } - } - - return DEVICE_INVALID_PARAMETER; -} \ No newline at end of file diff --git a/source/JACDAC/control/JDGPIOIdentification.cpp b/source/JACDAC/control/JDGPIOIdentification.cpp deleted file mode 100644 index 866d861e..00000000 --- a/source/JACDAC/control/JDGPIOIdentification.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "JDGPIOIdentification.h" -#include "MessageBus.h" - -using namespace codal; - -JDGPIOIdentification::JDGPIOIdentification(Pin& gpio) : gpio(gpio) -{ - this->identifying = false; - - if (EventModel::defaultEventBus) - EventModel::defaultEventBus->listen(DEVICE_ID_JACDAC_CONFIGURATION_SERVICE, JD_CONTROL_CONFIGURATION_EVT_IDENTIFY, this, &JDGPIOIdentification::identify); -} - -void JDGPIOIdentification::identify(Event) -{ - if (this->identifying) - return; - - this->identifying = true; - - int state = 0; - for (int i = 0; i < JD_DEFAULT_INDICATION_TIME * 10; i++) - { - gpio.setDigitalValue(state = !state); - fiber_sleep(100); - } - - this->identifying = false; -} \ No newline at end of file diff --git a/source/JACDAC/control/JDRNGService.cpp b/source/JACDAC/control/JDRNGService.cpp deleted file mode 100644 index ead07590..00000000 --- a/source/JACDAC/control/JDRNGService.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "JDRNGService.h" -#include "JACDAC.h" - -using namespace codal; - -int JDRNGService::send(uint8_t* buf, int len) -{ - if (JACDAC::instance) - return JACDAC::instance->bus.send(buf, len, this->service_number, NULL); - - return DEVICE_NO_RESOURCES; -} - -JDRNGService::JDRNGService() : JDService(JD_SERVICE_CLASS_CONTROL_RNG, ControlLayerService) -{ - this->service_number = JD_CONTROL_RNG_SERVICE_NUMBER; -} - -int JDRNGService::handlePacket(JDPacket* p) -{ - JDRNGServicePacket* rng = (JDRNGServicePacket*)p->data; - JDRNGServicePacket resp; - - if (rng->request_type == JD_CONTROL_RNG_SERVICE_REQUEST_TYPE_REQ) - { - resp.request_type = JD_CONTROL_RNG_SERVICE_REQUEST_TYPE_RESP; - resp.random = target_random(0xFFFFFFFF); - send((uint8_t*)&resp, sizeof(JDRNGServicePacket)); - } - - return DEVICE_OK; -} \ No newline at end of file diff --git a/source/JACDAC/control/JDSoundIdentification.cpp b/source/JACDAC/control/JDSoundIdentification.cpp deleted file mode 100644 index e07c0914..00000000 --- a/source/JACDAC/control/JDSoundIdentification.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "JDSoundIdentification.h" -#include "MessageBus.h" - -using namespace codal; - -JDSoundIdentification::JDSoundIdentification(Synthesizer& synth) : synth(synth) -{ - this->identifying = false; - - if (EventModel::defaultEventBus) - EventModel::defaultEventBus->listen(DEVICE_ID_JACDAC_CONFIGURATION_SERVICE, JD_CONTROL_CONFIGURATION_EVT_IDENTIFY, this, &JDSoundIdentification::identify); -} - -void JDSoundIdentification::identify(Event) -{ - if (this->identifying) - return; - - this->identifying = true; - - // C4 E4 G4 C5 - float frequencies[5] = {261.63, 329.63, 392.00, 532.25}; - - for (int j = 0; j < JD_DEFAULT_INDICATION_TIME; j++) - for (int i = 0; i < 5; i++) - { - synth.setFrequency(frequencies[i],100); - fiber_sleep(100); - } - - synth.setFrequency(0); - - this->identifying = false; -} \ No newline at end of file diff --git a/source/JACDAC/jacdac_status_flags.py b/source/JACDAC/jacdac_status_flags.py deleted file mode 100644 index 2c1efa0f..00000000 --- a/source/JACDAC/jacdac_status_flags.py +++ /dev/null @@ -1,34 +0,0 @@ -import sys - -def process_flags(num): - flags = { - "JD_SERIAL_RECEIVING": 0x0001, - "JD_SERIAL_RECEIVING_HEADER": 0x0002, - "JD_SERIAL_TRANSMITTING": 0x0004, - "JD_SERIAL_RX_LO_PULSE": 0x0008, - "JD_SERIAL_TX_LO_PULSE": 0x0010, - - "JD_SERIAL_BUS_LO_ERROR": 0x0020, - "JD_SERIAL_BUS_TIMEOUT_ERROR": 0x0040, - "JD_SERIAL_BUS_UART_ERROR": 0x0080, - "JD_SERIAL_ERR_MSK": 0x00E0, - - "JD_SERIAL_BUS_STATE": 0x0100, - "JD_SERIAL_BUS_TOGGLED": 0x0200, - - "DEVICE_COMPONENT_RUNNING": 0x1000, - "JD_SERIAL_DEBUG_BIT": 0x8000 - } - - for flag in flags.keys(): - if flags[flag] & num: - print(flag + "\r\n") - -if len(sys.argv) == 2: - process_flags(int(sys.argv[1])) -else: - print("Invalid args, this script expects on number") - exit(1) - - - diff --git a/source/JACDAC/jacdac_status_log.py b/source/JACDAC/jacdac_status_log.py deleted file mode 100644 index 807b8bf9..00000000 --- a/source/JACDAC/jacdac_status_log.py +++ /dev/null @@ -1,65 +0,0 @@ -import sys - -PHYS_STATE_SIZE = 128 - -log = [14749952, 2162725120, 15339776, 2186481924, 2187759876, 40374532, 11407360, 2160398656, 2160693568, 13308224, 2163249472, - 15864128, 14749952, 2162725120, 15339776, 2186481924, 2187759876, 40374532, 11407360, 2160398656, 2160693568, 13308224, - 2163249472, 15864128, 14749952, 2162725120, 15339776, 2186481924, 2187759876, 40374532, 11407360, 2160398656, 2160693568, - 13308224, 2163249472, 15864128, 14749952, 2162725120, 15339776, 2186481924, 2187759876, 40374532, 11407360, 2160398656, - 2160693568, 13308224, 2163249472, 15864128, 14749952, 2162725120, 15339776, 2186481924, 2187759876, 40374532, 11407360, - 2160398656, 2160693568, 13308224, 2163249472, 15864128, 14749952, 2162725120, 15339776, 2160398656, 2160693568, 13308224, - 2163249472, 15864128, 14749952, 2162725120, 15339776, 2186481924, 2187759876, 40374532, 11407360, 2160398656, 2160693568, - 13308224, 2163249472, 15864128, 14749952, 2162725120, 15339776, 2186481924, 2187759876, 40374532, 11407360, 2160398656, - 2160693568, 13308224, 2163249472, 15864128, 14749952, 2162725120, 15339776, 2186481924, 2187759876, 40374532, 11407360, - 2160398656, 2160693568, 13308224, 2163249472, 15864128, 14749952, 2162725120, 15339776, 2186481924, 2187759876, 40374532,11407360, 2160398656, 2160693568, 13308224, 2163249472, 15864128, 14749952, 2162725120, 15339776, 2186481924, 2187759876, - 40374532, 11407360, 2160398656, 2160693568, 13308224, 2163249472, 15864128] -log_start = 85 - -def process_flags(num): - flags = { - "JD_SERIAL_RECEIVING": 0x0001, - "JD_SERIAL_RECEIVING_HEADER": 0x0002, - "JD_SERIAL_TRANSMITTING": 0x0004, - "JD_SERIAL_RX_LO_PULSE": 0x0008, - "JD_SERIAL_TX_LO_PULSE": 0x0010, - - "JD_SERIAL_BUS_LO_ERROR": 0x0020, - "JD_SERIAL_BUS_TIMEOUT_ERROR": 0x0040, - "JD_SERIAL_BUS_UART_ERROR": 0x0080, - "JD_SERIAL_ERR_MSK": 0x00E0, - - "JD_SERIAL_BUS_STATE": 0x0100, - "JD_SERIAL_BUS_TOGGLED": 0x0200, - - "DEVICE_COMPONENT_RUNNING": 0x1000, - "JD_SERIAL_DEBUG_BIT": 0x8000 - } - - lineno = (num & 0x7FFF0000) >> 16 - isSet = num & (1 << 31) - logflags = num & 0x0000FFFF - - flag_str = "" - - if isSet > 0: - flag_str = "S: " - else: - flag_str = "U: " - - flag_str += str(lineno) + " " + hex(logflags) + " " - for flag in flags.keys(): - if flags[flag] & logflags: - flag_str += flag + " " - - print(flag_str) - -iterator = log_start -log_end = iterator - 1 - -while iterator != log_end: - process_flags(log[iterator]) - iterator = (iterator + 1) % PHYS_STATE_SIZE - - - - diff --git a/source/JACDAC/services/JDAccelerometerService.cpp b/source/JACDAC/services/JDAccelerometerService.cpp deleted file mode 100644 index f24e81a9..00000000 --- a/source/JACDAC/services/JDAccelerometerService.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "JDAccelerometerService.h" -#include "CodalDmesg.h" -#include "Timer.h" -#include "EventModel.h" - -using namespace codal; - -void JDAccelerometerService::sendData(Event) -{ - this->latest = this->accelerometer->getSample(); - - AccelerometerPacket p; - // raw accel type - p.packet_type = 0; - p.x = latest.x; - p.y = latest.y; - p.z = latest.z; - - send((uint8_t*)&p, sizeof(AccelerometerPacket)); -} - -void JDAccelerometerService::forwardEvent(Event evt) -{ - if (evt.value == ACCELEROMETER_EVT_3G || evt.value == ACCELEROMETER_EVT_6G || evt.value == ACCELEROMETER_EVT_8G || evt.value == ACCELEROMETER_EVT_DATA_UPDATE) - return; - - AccelerometerGesturePacket p; - // gesture type - p.packet_type = 1; - p.event_value = evt.value; - - send((uint8_t*)&p, sizeof(AccelerometerGesturePacket)); -} - -JDAccelerometerService::JDAccelerometerService(Accelerometer& accel) : JDService(JD_SERVICE_CLASS_ACCELEROMETER, HostService), accelerometer(&accel) -{ - system_timer_event_every(50, this->id, JD_ACCEL_EVT_SEND_DATA); - - if (EventModel::defaultEventBus) - { - EventModel::defaultEventBus->listen(this->id, JD_ACCEL_EVT_SEND_DATA, this, &JDAccelerometerService::sendData); - EventModel::defaultEventBus->listen(DEVICE_ID_GESTURE, DEVICE_EVT_ANY, this, &JDAccelerometerService::forwardEvent); - } -} - -JDAccelerometerService::JDAccelerometerService() : JDService(JD_SERVICE_CLASS_ACCELEROMETER, ClientService), accelerometer(NULL) -{ -} - -int JDAccelerometerService::getX() -{ - return latest.x; -} -int JDAccelerometerService::getY() -{ - return latest.y; -} -int JDAccelerometerService::getZ() -{ - return latest.z; -} - -int JDAccelerometerService::handlePacket(JDPacket* p) -{ - AccelerometerPacket* data = (AccelerometerPacket*)p->data; - - if (data->packet_type == 0) - { - latest.x = data->x; - latest.y = data->y; - latest.z = data->z; - } - - if (data->packet_type == 1) - { - AccelerometerGesturePacket* gesture = (AccelerometerGesturePacket*)p->data; - Event(this->id, gesture->event_value); - } - - - return DEVICE_OK; -} \ No newline at end of file diff --git a/source/JACDAC/services/JDConsoleService.cpp b/source/JACDAC/services/JDConsoleService.cpp deleted file mode 100644 index 2765797f..00000000 --- a/source/JACDAC/services/JDConsoleService.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "JDConsoleService.h" -#include "CodalDmesg.h" - -using namespace codal; - -JDConsoleService::JDConsoleService(bool receiver) : - JDService(JD_SERVICE_CLASS_CONSOLE, (receiver) ? BroadcastHostService : HostService) -{ - status = 0; -} - -int JDConsoleService::handlePacket(JDPacket* pkt) -{ - if (mode == BroadcastHostService) - { - // this is a bit rubbish, but for now we just log to DMESG. - // in the future there should be a function ptr. - JDConsolePacket* consolePkt = (JDConsolePacket*)pkt->data; - - if (consolePkt->priority < ((this->status & 0xF0) >> 4)) - return DEVICE_OK; - - char* priorityMsg = (char *)""; - - switch ((JDConsoleLogPriority)consolePkt->priority) - { - case Log: - priorityMsg = (char *)"log"; - break; - - case Info: - priorityMsg = (char *)"info"; - break; - - case Debug: - priorityMsg = (char *)"debug"; - break; - - case Error: - priorityMsg = (char *)"error"; - break; - } - - JDDevice* device = JACDAC::instance->getRemoteDevice(pkt->device_address); - - char* deviceName = (char*)"UNKNOWN"; - - if (device) - { - if (device->name) - deviceName = (char *)device->name; - else - deviceName = (char*)"UNNAMED"; - } - - DMESG("[%d,%s] %s: %s", pkt->device_address, deviceName, priorityMsg, consolePkt->message); - } - - return DEVICE_OK; -} - -void JDConsoleService::setMinimumPriority(JDConsoleLogPriority priority) -{ - this->status = priority << 4 | (this->status & 0x0F); -} - -int JDConsoleService::log(JDConsoleLogPriority priority, ManagedString message) -{ - // one for priority, the other for null terminator - if (message.length() + 2 > JD_SERIAL_MAX_PAYLOAD_SIZE || message.length() == 0) - return DEVICE_INVALID_PARAMETER; - - // one for priority, the other for null terminator - JDConsolePacket* console = (JDConsolePacket*)malloc(message.length() + 2); - - console->priority = priority; - memcpy(console->message, message.toCharArray(), message.length() + 1); - - send((uint8_t*) console, message.length() + 2); - - free(console); - - return DEVICE_OK; -} \ No newline at end of file diff --git a/source/JACDAC/services/JDMessageBusService.cpp b/source/JACDAC/services/JDMessageBusService.cpp deleted file mode 100644 index 62ebdd98..00000000 --- a/source/JACDAC/services/JDMessageBusService.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "JDMessageBusService.h" -#include "CodalDmesg.h" - -using namespace codal; - -JDMessageBusService::JDMessageBusService() : JDService(JD_SERVICE_CLASS_MESSAGE_BUS, BroadcastHostService) -{ - suppressForwarding = false; -} - -/** - * Associates the given event with the serial channel. - * - * Once registered, all events matching the given registration sent to this device's - * default EventModel will be automatically retransmitted on the serial bus. - * - * @param id The id of the event to register. - * - * @param value the value of the event to register. - * - * @return DEVICE_OK on success, or DEVICE_NO_RESOURCES if no default EventModel is available. - * - * @note The wildcards DEVICE_ID_ANY and DEVICE_EVT_ANY can also be in place of the - * id and value fields. - */ -int JDMessageBusService::listen(uint16_t id, uint16_t value) -{ - if (EventModel::defaultEventBus) - return listen(id, value, *EventModel::defaultEventBus); - - return DEVICE_NO_RESOURCES; -} - -/** - * Associates the given event with the serial channel. - * - * Once registered, all events matching the given registration sent to the given - * EventModel will be automatically retransmitted on the serial bus. - * - * @param id The id of the events to register. - * - * @param value the value of the event to register. - * - * @param eventBus The EventModel to listen for events on. - * - * @return DEVICE_OK on success. - * - * @note The wildcards DEVICE_ID_ANY and DEVICE_EVT_ANY can also be in place of the - * id and value fields. - */ -int JDMessageBusService::listen(uint16_t id, uint16_t value, EventModel &eventBus) -{ - return eventBus.listen(id, value, this, &JDMessageBusService::eventReceived, MESSAGE_BUS_LISTENER_IMMEDIATE); -} - -/** - * Disassociates the given event with the serial channel. - * - * @param id The id of the events to deregister. - * - * @param value The value of the event to deregister. - * - * @return DEVICE_OK on success, or DEVICE_INVALID_PARAMETER if the default message bus does not exist. - * - * @note DEVICE_EVT_ANY can be used to deregister all event values matching the given id. - */ -int JDMessageBusService::ignore(uint16_t id, uint16_t value) -{ - if (EventModel::defaultEventBus) - return ignore(id, value, *EventModel::defaultEventBus); - - return DEVICE_INVALID_PARAMETER; -} - -/** - * Disassociates the given events with the serial channel. - * - * @param id The id of the events to deregister. - * - * @param value The value of the event to deregister. - * - * @param eventBus The EventModel to deregister on. - * - * @return DEVICE_OK on success. - * - * @note DEVICE_EVT_ANY can be used to deregister all event values matching the given id. - */ -int JDMessageBusService::ignore(uint16_t id, uint16_t value, EventModel &eventBus) -{ - return eventBus.ignore(id, value, this, &JDMessageBusService::eventReceived); -} - - -/** - * Protocol handler callback. This is called when the serial bus receives a packet marked as using the event protocol. - * - * This function process this packet, and fires the event contained inside onto the default EventModel. - */ -int JDMessageBusService::handlePacket(JDPacket* p) -{ - Event *e = (Event *) p->data; - - suppressForwarding = true; - e->fire(); - suppressForwarding = false; - - return DEVICE_OK; -} - -/** - * Event handler callback. This is called whenever an event is received matching one of those registered through - * the registerEvent() method described above. Upon receiving such an event, it is wrapped into - * a serial bus packet and transmitted to any other devices in the same group. - */ -void JDMessageBusService::eventReceived(Event e) -{ - // DMESG("EVENT"); - if(suppressForwarding) - return; - - // DMESG("PACKET QUEUED: %d %d %d", e.source, e.value, sizeof(Event)); - send((uint8_t *)&e, sizeof(Event)); - // DMESG("RET %d",ret); -} \ No newline at end of file diff --git a/source/JACDAC/services/JDPacketSniffer.cpp b/source/JACDAC/services/JDPacketSniffer.cpp deleted file mode 100644 index 6c48deff..00000000 --- a/source/JACDAC/services/JDPacketSniffer.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "JDPacketSniffer.h" -#include "JDControlService.h" -#include "CodalDmesg.h" -#include "Timer.h" - -using namespace codal; - -void JDPacketSniffer::timerCallback(Event) -{ - JDDevice* head = this->deviceManager.getDeviceList(); - - while (head) - { - JDDevice* dev = head; - head = head->next; - dev->rolling_counter++; - - if (dev->rolling_counter > 3) - { - this->deviceManager.removeDevice(dev); - free(dev->name); - free(dev); - } - } -} - -JDPacketSniffer::JDPacketSniffer() : JDService(JD_SERVICE_CLASS_BRIDGE, ClientService) -{ - if (EventModel::defaultEventBus) - { - EventModel::defaultEventBus->listen(this->id, JD_CONTROL_SERVICE_EVT_TIMER_CALLBACK, this, &JDPacketSniffer::timerCallback); - system_timer_event_every(500, this->id, JD_CONTROL_SERVICE_EVT_TIMER_CALLBACK); - } - -} - -JDDevice* JDPacketSniffer::getDeviceList() -{ - return this->deviceManager.getDeviceList(); -} - -int JDPacketSniffer::handlePacket(JDPacket* p) -{ - if (p->device_address == 0) - { - if (p->service_number == 0) - { - JDControlPacket* cp = (JDControlPacket *)p->data; - if (cp->device_flags & (JD_DEVICE_FLAGS_REJECT | JD_DEVICE_FLAGS_PROPOSING)) - return DEVICE_OK; - - JDDevice* remote = this->deviceManager.addDevice((JDControlPacket*)p->data, p->communication_rate); - remote->rolling_counter = 0; - } - } - - return DEVICE_OK; -} - -void JDPacketSniffer::logDevices() -{ - JDDevice* head = this->deviceManager.getDeviceList(); - - while (head) - { - DMESG("A: %d, unique_device_identifierL: %d N: %s CR: %d", head->device_address, (uint32_t)head->unique_device_identifier, head->name ? head->name : 0, head->communication_rate); - head = head->next; - } -} \ No newline at end of file diff --git a/source/JACDAC/services/JDTestService.cpp b/source/JACDAC/services/JDTestService.cpp deleted file mode 100644 index 4f078fe6..00000000 --- a/source/JACDAC/services/JDTestService.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "JDTestService.h" -#include "CodalDmesg.h" - -using namespace codal; - -JDTestService::JDTestService(ManagedString serviceName, JDServiceMode m) : JDService(JD_SERVICE_CLASS_CONTROL_TEST, m), name(serviceName) -{ -} - -int JDTestService::handlePacket(JDPacket* p) -{ - uint32_t *data = (uint32_t *)p->data; - DMESG("REC N: %d F: %d->%d N: %s M: %c%c%c", *data, p->device_address, p->service_number, this->name.toCharArray(), (this->mode == ClientService) ? "C":"", (this->mode == HostService) ? "H":"", (this->mode == BroadcastHostService) ? "B":""); - return DEVICE_OK; -} - -void JDTestService::sendTestPacket(uint32_t value) -{ - if (!this->device) - return; - DMESG("SEND: %d FROM: %d", value, this->device->device_address); - send((uint8_t*)&value, sizeof(uint32_t)); -} \ No newline at end of file diff --git a/source/drivers/USBJACDAC.cpp b/source/drivers/USBJACDAC.cpp deleted file mode 100644 index 4cd03c8c..00000000 --- a/source/drivers/USBJACDAC.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "CodalConfig.h" -#include "USBJACDAC.h" -#include "CodalDmesg.h" - -#define USB_JACDAC_REQ_INIT 0x11 -#define USB_JACDAC_REQ_DIAGNOSTICS 0x22 - -#if CONFIG_ENABLED(DEVICE_USB) - -bool is_setup = false; - -using namespace codal; - -static const InterfaceInfo ifaceInfo = { - NULL, // No supplemental descriptor - 0, // ditto - 2, // two endpoints - { - 2, // numEndpoints - 0xDC, /// class code - diagnostic device - 0x08, // subclass (undefined) - 0x00, // undefined - 0x00, // iface string - 0x00, // alt setting - }, - {USB_EP_TYPE_BULK, 0}, - {USB_EP_TYPE_BULK, 0}, -}; - -struct USBJACDACDiagnostics { - uint32_t bus_state; - uint32_t bus_lo_error; - uint32_t bus_uart_error; - uint32_t bus_timeout_error; - uint32_t packets_sent; - uint32_t packets_received; - uint32_t packets_dropped; -}; - -USBJACDAC::USBJACDAC() : JDService(JD_SERVICE_CLASS_BRIDGE, ClientService) -{ - inBuffPtr = 0; - outBuffPtr = 0; - - this->phys = NULL; - this->status = DEVICE_COMPONENT_STATUS_IDLE_TICK | DEVICE_COMPONENT_RUNNING; - - if (JACDAC::instance) - setPhysicalLayer(JACDAC::instance->bus); -} - -void USBJACDAC::idleCallback() -{ - if (inBuffPtr >= sizeof(JDPacket) && (this->status & JACDAC_USB_STATUS_CLEAR_TO_SEND)) - { - DMESG("IBFPTR %d",inBuffPtr); - in->write(inBuf, sizeof(JDPacket)); - inBuffPtr -= sizeof(JDPacket); - - if (inBuffPtr > 0) - memmove(inBuf, inBuf + sizeof(JDPacket), inBuffPtr); - DMESG("IBFPTR AF %d",inBuffPtr); - } - - if (outBuffPtr >= sizeof(JDPacket)) - { - DMESG("OBFPTR %d", outBuffPtr); - JDPacket* tx = (JDPacket*) outBuf; - - // we expect any stack will already calculate the crc etc. so just place the packet on the bus. - if (this->phys) - { - this->phys->send(tx, NULL, false); - - JDPacket* pkt = (JDPacket *)malloc(sizeof(JDPacket)); - memcpy(pkt, tx, sizeof(JDPacket)); - - // queue locally on the device as well (if we can) - int ret = this->phys->addToRxArray(pkt); - - if (ret == DEVICE_OK) - Event(this->phys->id, JD_SERIAL_EVT_DATA_READY); - else - free(pkt); - } - - outBuffPtr -= sizeof(JDPacket); - - if (outBuffPtr > 0) - memmove(outBuf, outBuf + sizeof(JDPacket), outBuffPtr); - DMESG("OBFPTR AF %d",outBuffPtr); - } -} - -int USBJACDAC::setPhysicalLayer(JDPhysicalLayer &phys) -{ - this->phys = &phys; - this->phys->sniffer = this; - return DEVICE_OK; -} - -int USBJACDAC::classRequest(UsbEndpointIn &ctrl, USBSetup& setup) -{ - uint8_t dummy = 0; - USBJACDACDiagnostics diags; - JDDiagnostics jdDiags; - - switch(setup.bRequest) - { - // INIT request signals that the host is listening for data so - // writes will be received and not infinitely blocked. - // a non-zero value begins streaming. - case USB_JACDAC_REQ_INIT: - if (setup.wValueL) - this->status |= JACDAC_USB_STATUS_CLEAR_TO_SEND; - else - this->status &= ~JACDAC_USB_STATUS_CLEAR_TO_SEND; - - ctrl.write(&dummy, 0); - return DEVICE_OK; - - case USB_JACDAC_REQ_DIAGNOSTICS: - if (this->phys) - { - diags.bus_state = this->phys->getErrorState(); - jdDiags = this->phys->getDiagnostics(); - memcpy(&diags, &jdDiags, sizeof(JDDiagnostics)); - ctrl.write(&diags, sizeof(USBJACDACDiagnostics)); - return DEVICE_OK; - } - } - - return DEVICE_NOT_SUPPORTED; -} - -int USBJACDAC::endpointRequest() -{ - uint8_t buf[64]; - int len = out->read(buf, sizeof(buf)); - - if (len <= 0 || outBuffPtr + len > USB_JACDAC_BUFFER_SIZE) - return len; - - memcpy(&this->outBuf[outBuffPtr], buf, len); - outBuffPtr += len; - - return len; -} - -const InterfaceInfo *USBJACDAC::getInterfaceInfo() -{ - return &ifaceInfo; -} - -int USBJACDAC::handlePacket(JDPacket* packet) -{ - if (inBuffPtr + sizeof(JDPacket) > USB_JACDAC_BUFFER_SIZE) - return DEVICE_OK; - - memcpy(&this->inBuf[inBuffPtr], (uint8_t*)packet, sizeof(JDPacket)); - inBuffPtr += sizeof(JDPacket); - - return DEVICE_OK; -} -#endif \ No newline at end of file From c77bc53cd891b2e41c3c3da733a75dec4fa0019f Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Mon, 23 Nov 2020 15:46:32 +0000 Subject: [PATCH 09/43] Add backward compatibility wrapper for I2C::read() and I2C::write() --- inc/driver-models/I2C.h | 2 ++ source/driver-models/I2C.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/inc/driver-models/I2C.h b/inc/driver-models/I2C.h index 458dc303..0b136d5d 100644 --- a/inc/driver-models/I2C.h +++ b/inc/driver-models/I2C.h @@ -115,6 +115,7 @@ class I2C * @return DEVICE_OK on success, DEVICE_I2C_ERROR if the the write request failed. */ virtual int write(uint16_t address, uint8_t *data, int len, bool repeated = false); + int write(int address, char *data, int len, bool repeated = false); /** * Performs a typical register write operation to the I2C slave device provided. @@ -153,6 +154,7 @@ class I2C * @return DEVICE_OK on success, DEVICE_I2C_ERROR if the the read request failed. */ virtual int read(uint16_t address, uint8_t *data, int len, bool repeated = false); + int read(int address, char *data, int len, bool repeated = false); /** * Performs a typical register read operation to the I2C slave device provided. diff --git a/source/driver-models/I2C.cpp b/source/driver-models/I2C.cpp index 06803be3..4acd924a 100644 --- a/source/driver-models/I2C.cpp +++ b/source/driver-models/I2C.cpp @@ -269,5 +269,15 @@ namespace codal return (result == DEVICE_OK) ? (int)data : result; } + + int I2C::write(int address, char *data, int len, bool repeated) + { + return write((uint16_t)address, (uint8_t *)data, len, repeated); + } + + int I2C::read(int address, char *data, int len, bool repeated) + { + return read((uint16_t)address, (uint8_t *)data, len, repeated); + } } From 879ec3d8dac8b510c49d57421046f493a640684a Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Mon, 23 Nov 2020 19:37:17 +0000 Subject: [PATCH 10/43] Add Pin destructor --- inc/driver-models/Pin.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/inc/driver-models/Pin.h b/inc/driver-models/Pin.h index 773bf676..255d6a4b 100644 --- a/inc/driver-models/Pin.h +++ b/inc/driver-models/Pin.h @@ -513,6 +513,10 @@ namespace codal virtual void disconnect() { } + + virtual ~Pin() + { + } }; } From 147c24ce8641983dfd6ed584926b2a6a27abc017 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Tue, 1 Dec 2020 21:01:34 +0000 Subject: [PATCH 11/43] Use lazy initialisation in Pulsein - introduce enable flag - initialise underlying timers and even handler only when needed - only destruct if needed, and protect against accidental multiple calls to destructor --- inc/drivers/PulseIn.h | 1 + source/drivers/PulseIn.cpp | 45 ++++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/inc/drivers/PulseIn.h b/inc/drivers/PulseIn.h index b77cb97a..ac6e4546 100644 --- a/inc/drivers/PulseIn.h +++ b/inc/drivers/PulseIn.h @@ -41,6 +41,7 @@ class PulseIn FiberLock lock; CODAL_TIMESTAMP timeout; static bool timeoutGeneratorStarted; + bool enabled; public: uint32_t lastEdge; diff --git a/source/drivers/PulseIn.cpp b/source/drivers/PulseIn.cpp index dc0c9519..b84bbb00 100644 --- a/source/drivers/PulseIn.cpp +++ b/source/drivers/PulseIn.cpp @@ -40,17 +40,7 @@ PulseIn::PulseIn(Pin &p) : pin(p) { lastPeriod = 0; lastEdge = 0; - - // Configure the requested pin to supply pulse events. - pin.eventOn(DEVICE_PIN_EVENT_ON_PULSE); - EventModel::defaultEventBus->listen(pin.id, pin.getPolarity() ? DEVICE_PIN_EVT_PULSE_HI : DEVICE_PIN_EVT_PULSE_LO, this, &PulseIn::onPulse); - EventModel::defaultEventBus->listen(DEVICE_ID_PULSE_IN, DEVICE_EVT_PULSE_IN_TIMEOUT, this, &PulseIn::onTimeout); - - if (!timeoutGeneratorStarted) - { - system_timer_event_every_us(10000, DEVICE_ID_PULSE_IN, DEVICE_EVT_PULSE_IN_TIMEOUT); - timeoutGeneratorStarted = true; - } + enabled = false; lock.wait(); } @@ -64,6 +54,23 @@ PulseIn::PulseIn(Pin &p) : pin(p) int PulseIn::awaitPulse(int timeout) { + // perform lazy initialisation of our dependencies + if (!enabled) + { + // Configure the requested pin to supply pulse events. + pin.eventOn(DEVICE_PIN_EVENT_ON_PULSE); + EventModel::defaultEventBus->listen(pin.id, pin.getPolarity() ? DEVICE_PIN_EVT_PULSE_HI : DEVICE_PIN_EVT_PULSE_LO, this, &PulseIn::onPulse, MESSAGE_BUS_LISTENER_IMMEDIATE); + EventModel::defaultEventBus->listen(DEVICE_ID_PULSE_IN, DEVICE_EVT_PULSE_IN_TIMEOUT, this, &PulseIn::onTimeout, MESSAGE_BUS_LISTENER_IMMEDIATE); + + if (!timeoutGeneratorStarted) + { + system_timer_event_every_us(10000, DEVICE_ID_PULSE_IN, DEVICE_EVT_PULSE_IN_TIMEOUT); + timeoutGeneratorStarted = true; + } + + enabled = true; + } + if (timeout) this->timeout = system_timer_current_time_us() + timeout; else @@ -120,10 +127,14 @@ PulseIn::onTimeout(Event e) */ PulseIn::~PulseIn() { - EventModel::defaultEventBus->ignore(pin.id, pin.getPolarity() ? DEVICE_PIN_EVT_PULSE_HI : DEVICE_PIN_EVT_PULSE_LO, this, &PulseIn::onPulse); - EventModel::defaultEventBus->ignore(DEVICE_ID_PULSE_IN, DEVICE_EVT_PULSE_IN_TIMEOUT, this, &PulseIn::onTimeout); - pin.eventOn(DEVICE_PIN_EVENT_NONE); - timeout = 0; - lastPeriod = 0; - lock.notifyAll(); + if (enabled) + { + enabled = false; + EventModel::defaultEventBus->ignore(pin.id, pin.getPolarity() ? DEVICE_PIN_EVT_PULSE_HI : DEVICE_PIN_EVT_PULSE_LO, this, &PulseIn::onPulse); + EventModel::defaultEventBus->ignore(DEVICE_ID_PULSE_IN, DEVICE_EVT_PULSE_IN_TIMEOUT, this, &PulseIn::onTimeout); + //pin.eventOn(DEVICE_PIN_EVENT_NONE); + timeout = 0; + lastPeriod = 0; + lock.notifyAll(); + } } \ No newline at end of file From b027a15b6cfc3c766b3b38ef1bd8c4879308ecbd Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Wed, 2 Dec 2020 19:38:42 +0000 Subject: [PATCH 12/43] Update FiberLock to perform safely when used in IRQ context - IRQ handler can now much more safely call notify() on a FiberLock - Calls to wait from IRQ context should not be permitted --- inc/core/CodalFiber.h | 2 +- source/core/CodalFiber.cpp | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/inc/core/CodalFiber.h b/inc/core/CodalFiber.h index a16b119e..b83b7138 100644 --- a/inc/core/CodalFiber.h +++ b/inc/core/CodalFiber.h @@ -335,7 +335,7 @@ namespace codal class FiberLock { private: - bool locked; + int locked; Fiber *queue; public: diff --git a/source/core/CodalFiber.cpp b/source/core/CodalFiber.cpp index 2c979052..9f9292a0 100644 --- a/source/core/CodalFiber.cpp +++ b/source/core/CodalFiber.cpp @@ -1018,7 +1018,9 @@ void FiberLock::wait() if (!fiber_scheduler_running()) return; - if (locked) + int l = ++locked; + + if (l > 1) { // wait() is a blocking call, so if we're in a fork on block context, // it's time to spawn a new fiber... @@ -1030,11 +1032,22 @@ void FiberLock::wait() // Add fiber to the sleep queue. We maintain strict ordering here to reduce lookup times. queue_fiber(f, &queue); + // Check if we've been raced by something running in interrupt context. + // Note this is safe, as no IRQ can wait() and as we are non-preemptive, neither could any other fiber. + // It is possible that and IRQ has performed a notify() operation however. + // If so, put ourself back on the run queue and spin the scheduler (in case we performed a fork-on-block) + if (locked < l) + { + // Remove fiber from the run queue + dequeue_fiber(f); + + // Add fiber to the sleep queue. We maintain strict ordering here to reduce lookup times. + queue_fiber(f, &runQueue); + } + // Finally, enter the scheduler. schedule(); } - - locked = true; } /** @@ -1049,7 +1062,9 @@ void FiberLock::notify() dequeue_fiber(f); queue_fiber(f, &runQueue); } - locked = false; + + if (locked > 0) + locked--; } /** @@ -1066,7 +1081,7 @@ void FiberLock::notifyAll() f = queue; } - locked = false; + locked = 0; } From 3143d1307fcc351857e97c75e4c27de0ead08039 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Wed, 2 Dec 2020 19:53:39 +0000 Subject: [PATCH 13/43] Protect FiberLock::wait() with IRQ mutex --- source/core/CodalFiber.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/core/CodalFiber.cpp b/source/core/CodalFiber.cpp index 9f9292a0..15beb30b 100644 --- a/source/core/CodalFiber.cpp +++ b/source/core/CodalFiber.cpp @@ -1036,6 +1036,7 @@ void FiberLock::wait() // Note this is safe, as no IRQ can wait() and as we are non-preemptive, neither could any other fiber. // It is possible that and IRQ has performed a notify() operation however. // If so, put ourself back on the run queue and spin the scheduler (in case we performed a fork-on-block) + target_disable_irq(); if (locked < l) { // Remove fiber from the run queue @@ -1044,6 +1045,7 @@ void FiberLock::wait() // Add fiber to the sleep queue. We maintain strict ordering here to reduce lookup times. queue_fiber(f, &runQueue); } + target_enable_irq(); // Finally, enter the scheduler. schedule(); From 1b0a04a374b48a9044258b2763d7a81a2cbf590e Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Wed, 2 Dec 2020 20:09:22 +0000 Subject: [PATCH 14/43] Ensure atomic increment in FiberLock::wait() --- source/core/CodalFiber.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/core/CodalFiber.cpp b/source/core/CodalFiber.cpp index 15beb30b..6cf1629b 100644 --- a/source/core/CodalFiber.cpp +++ b/source/core/CodalFiber.cpp @@ -1018,7 +1018,9 @@ void FiberLock::wait() if (!fiber_scheduler_running()) return; + target_disable_irq(); int l = ++locked; + target_enable_irq(); if (l > 1) { From 624048d8588a2d2ca3e5b30c9635d41e999ce0b7 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Thu, 3 Dec 2020 12:36:26 +0000 Subject: [PATCH 15/43] Ensure all fibers blocked on PulseIn are correctly awoken - Support multiple fibers blocked on lock - Ensure lock is always correctly reset --- source/drivers/PulseIn.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/source/drivers/PulseIn.cpp b/source/drivers/PulseIn.cpp index b84bbb00..3d2b06fe 100644 --- a/source/drivers/PulseIn.cpp +++ b/source/drivers/PulseIn.cpp @@ -96,11 +96,8 @@ PulseIn::onPulse(Event e) lastPeriod = (uint32_t) e.timestamp; // Wake any blocked fibers and reset the lock. - if (lock.getWaitCount()) - { - lock.notify(); - lock.wait(); - } + lock.notifyAll(); + lock.wait(); } /** @@ -114,11 +111,9 @@ PulseIn::onTimeout(Event e) timeout = 0; lastPeriod = 0; - if (lock.getWaitCount()) - { - lock.notify(); - lock.wait(); - } + // Wake any blocked fibers and reset the lock. + lock.notifyAll(); + lock.wait(); } } From a12d37133d0e594de067c0b90f35227e91690970 Mon Sep 17 00:00:00 2001 From: Steve Kemp Date: Sun, 13 Dec 2020 15:40:48 +0200 Subject: [PATCH 16/43] Fixed typo: availiable -> available --- inc/core/CodalHeapAllocator.h | 2 +- inc/streams/DataStream.h | 4 ++-- inc/streams/Mixer.h | 2 +- inc/types/ManagedString.h | 2 +- source/core/CodalFiber.cpp | 8 ++++---- source/core/CodalHeapAllocator.cpp | 2 +- source/streams/DataStream.cpp | 4 ++-- source/types/ManagedString.cpp | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/inc/core/CodalHeapAllocator.h b/inc/core/CodalHeapAllocator.h index 5e16a521..09e6935b 100644 --- a/inc/core/CodalHeapAllocator.h +++ b/inc/core/CodalHeapAllocator.h @@ -42,7 +42,7 @@ DEALINGS IN THE SOFTWARE. * what these are, and consider the tradeoffs against simplicity... * * @note The need for this should be reviewed in the future, if a different memory allocator is - * made availiable in the mbed platform. + * made available in the mbed platform. * * TODO: Consider caching recently freed blocks to improve allocation time. */ diff --git a/inc/streams/DataStream.h b/inc/streams/DataStream.h index ad4866f8..b1027ea3 100644 --- a/inc/streams/DataStream.h +++ b/inc/streams/DataStream.h @@ -137,14 +137,14 @@ namespace codal /** * Define a downstream component for data stream. * - * @sink The component that data will be delivered to, when it is availiable + * @sink The component that data will be delivered to, when it is available */ virtual void connect(DataSink &sink) override; /** * Define a downstream component for data stream. * - * @sink The component that data will be delivered to, when it is availiable + * @sink The component that data will be delivered to, when it is available */ virtual void disconnect() override; diff --git a/inc/streams/Mixer.h b/inc/streams/Mixer.h index a469560f..9678db70 100644 --- a/inc/streams/Mixer.h +++ b/inc/streams/Mixer.h @@ -75,7 +75,7 @@ class Mixer : public DataSource, public DataSink /** * Define a downstream component for data stream. * - * @sink The component that data will be delivered to, when it is availiable + * @sink The component that data will be delivered to, when it is available */ virtual void connect(DataSink &sink); }; diff --git a/inc/types/ManagedString.h b/inc/types/ManagedString.h index 8e07ad6b..1fbffc70 100644 --- a/inc/types/ManagedString.h +++ b/inc/types/ManagedString.h @@ -50,7 +50,7 @@ namespace codal * such as Touch Develop. * * Written from first principles here, for several reasons: - * 1) std::shared_ptr is not yet availiable on the ARMCC compiler + * 1) std::shared_ptr is not yet available on the ARMCC compiler * * 2) to reduce memory footprint - we don't need many of the other features in the std library * diff --git a/source/core/CodalFiber.cpp b/source/core/CodalFiber.cpp index 6cf1629b..616baca5 100644 --- a/source/core/CodalFiber.cpp +++ b/source/core/CodalFiber.cpp @@ -154,7 +154,7 @@ Fiber * codal::get_fiber_list() } /** - * Allocates a fiber from the fiber pool if availiable. Otherwise, allocates a new one from the heap. + * Allocates a fiber from the fiber pool if available. Otherwise, allocates a new one from the heap. */ Fiber *getFiberContext() { @@ -355,7 +355,7 @@ static Fiber* handle_fob() // it's time to spawn a new fiber... if (f->flags & DEVICE_FIBER_FLAG_FOB) { - // Allocate a TCB from the new fiber. This will come from the tread pool if availiable, + // Allocate a TCB from the new fiber. This will come from the tread pool if available, // else a new one will be allocated on the heap. if (!forkedFiber) @@ -662,7 +662,7 @@ Fiber *__create_fiber(uint32_t ep, uint32_t cp, uint32_t pm, int parameterised) if (ep == 0 || cp == 0) return NULL; - // Allocate a TCB from the new fiber. This will come from the fiber pool if availiable, + // Allocate a TCB from the new fiber. This will come from the fiber pool if available, // else a new one will be allocated on the heap. Fiber *newFiber = getFiberContext(); @@ -1104,4 +1104,4 @@ int FiberLock::getWaitCount() } return count; -} \ No newline at end of file +} diff --git a/source/core/CodalHeapAllocator.cpp b/source/core/CodalHeapAllocator.cpp index f5ebfe05..09568217 100644 --- a/source/core/CodalHeapAllocator.cpp +++ b/source/core/CodalHeapAllocator.cpp @@ -42,7 +42,7 @@ DEALINGS IN THE SOFTWARE. * what these are, and consider the tradeoffs against simplicity... * * @note The need for this should be reviewed in the future, if a different memory allocator is - * made availiable in the mbed platform. + * made available in the mbed platform. * * TODO: Consider caching recently freed blocks to improve allocation time. */ diff --git a/source/streams/DataStream.cpp b/source/streams/DataStream.cpp index 42604079..4647307c 100644 --- a/source/streams/DataStream.cpp +++ b/source/streams/DataStream.cpp @@ -158,7 +158,7 @@ bool DataStream::isReadOnly() /** * Define a downstream component for data stream. * - * @sink The component that data will be delivered to, when it is availiable + * @sink The component that data will be delivered to, when it is available */ void DataStream::connect(DataSink &sink) { @@ -177,7 +177,7 @@ int DataStream::getFormat() /** * Define a downstream component for data stream. * - * @sink The component that data will be delivered to, when it is availiable + * @sink The component that data will be delivered to, when it is available */ void DataStream::disconnect() { diff --git a/source/types/ManagedString.cpp b/source/types/ManagedString.cpp index 44ba355a..11457d6e 100644 --- a/source/types/ManagedString.cpp +++ b/source/types/ManagedString.cpp @@ -31,7 +31,7 @@ DEALINGS IN THE SOFTWARE. * such as Touch Develop. * * Written from first principles here, for several reasons: - * 1) std::shared_ptr is not yet availiable on the ARMCC compiler + * 1) std::shared_ptr is not yet available on the ARMCC compiler * * 2) to reduce memory footprint - we don't need many of the other features in the std library * From f484d8d500b454e6fa288b208df41f20f0200315 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Mon, 14 Dec 2020 17:44:30 +0000 Subject: [PATCH 17/43] MessageBus.cpp : Honour changes in cb_arg when recycling event listeners --- source/drivers/MessageBus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/drivers/MessageBus.cpp b/source/drivers/MessageBus.cpp index e5f77191..438f0030 100644 --- a/source/drivers/MessageBus.cpp +++ b/source/drivers/MessageBus.cpp @@ -418,7 +418,7 @@ int MessageBus::add(Listener *newListener) { methodCallback = (newListener->flags & MESSAGE_BUS_LISTENER_METHOD) && (l->flags & MESSAGE_BUS_LISTENER_METHOD); - if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb)) + if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb) && newListener->cb_arg == l->cb_arg) { // We have a perfect match for this event listener already registered. // If it's marked for deletion, we simply resurrect the listener, and we're done. From e6b73051ed273a288536b523227915d24be01b79 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Wed, 16 Dec 2020 19:56:45 +0000 Subject: [PATCH 18/43] Optimize serial write oprerations - Unify codepaths for string, buffer and single character writes - Pipeline write operations so that blocking calls return as soon as data is scheduled - Remove obsolete internal send(SerialMode) method - Fix bug affecting ASYNC read operations that caused calling fiber to be incorrectly blocked - Ensure receiver is enabled when isReadable() is called --- inc/driver-models/Serial.h | 2 - source/driver-models/Serial.cpp | 85 +++++++++++---------------------- 2 files changed, 27 insertions(+), 60 deletions(-) diff --git a/inc/driver-models/Serial.h b/inc/driver-models/Serial.h index 89060789..dbe11abb 100644 --- a/inc/driver-models/Serial.h +++ b/inc/driver-models/Serial.h @@ -111,8 +111,6 @@ namespace codal void circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition); - void send(SerialMode mode = DEVICE_DEFAULT_SERIAL_MODE); - int setTxInterrupt(uint8_t *string, int len, SerialMode mode); public: diff --git a/source/driver-models/Serial.cpp b/source/driver-models/Serial.cpp index 10c227fa..462f9639 100644 --- a/source/driver-models/Serial.cpp +++ b/source/driver-models/Serial.cpp @@ -82,19 +82,27 @@ int Serial::setTxInterrupt(uint8_t *string, int len, SerialMode mode) { int copiedBytes = 0; - if(mode != SYNC_SPINWAIT) - fiber_wake_on_event(DEVICE_ID_NOTIFY, CODAL_SERIAL_EVT_TX_EMPTY); - - for(copiedBytes = 0; copiedBytes < len; copiedBytes++) + while(copiedBytes < len) { uint16_t nextHead = (txBuffHead + 1) % txBuffSize; - if(nextHead != txBuffTail) + + if(nextHead == txBuffTail) { - this->txBuff[txBuffHead] = string[copiedBytes]; - txBuffHead = nextHead; + enableInterrupt(TxInterrupt); + + if(mode == SYNC_SLEEP) + fiber_wait_for_event(DEVICE_ID_NOTIFY, CODAL_SERIAL_EVT_TX_EMPTY); + + if(mode == SYNC_SPINWAIT) + while(txBufferedSize() > 0); + + if(mode == ASYNC) + break; } - else - break; + + this->txBuff[txBuffHead] = string[copiedBytes]; + txBuffHead = nextHead; + copiedBytes++; } //set the TX interrupt @@ -198,21 +206,6 @@ int Serial::initialiseTx() return DEVICE_OK; } -/** - * An internal method that either spin waits if mode is set to SYNC_SPINWAIT - * or puts the fiber to sleep if the mode is set to SYNC_SLEEP - * - * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP - */ -void Serial::send(SerialMode mode) -{ - if(mode == SYNC_SPINWAIT) - while(txBufferedSize() > 0); - - if(mode == SYNC_SLEEP) - schedule(); -} - /** * An internal method that copies values from a circular buffer to a linear buffer. * @@ -310,29 +303,7 @@ Serial::Serial(Pin& tx, Pin& rx, uint8_t rxBufferSize, uint8_t txBufferSize, uin */ int Serial::sendChar(char c, SerialMode mode) { - if(txInUse()) - return DEVICE_SERIAL_IN_USE; - - lockTx(); - - //lazy initialisation of our tx buffer - if(!(status & CODAL_SERIAL_STATUS_TX_BUFF_INIT)) - { - int result = initialiseTx(); - - if(result != DEVICE_OK) - return result; - } - - uint8_t toTransmit[2] = { c, '\0'}; - - int bytesWritten = setTxInterrupt(toTransmit, 1, mode); - - send(mode); - - unlockTx(); - - return bytesWritten; + return send((uint8_t *)&c, 1, mode); } /** @@ -409,17 +380,7 @@ int Serial::send(uint8_t *buffer, int bufferLen, SerialMode mode) return result; } - bool complete = false; - int bytesWritten = 0; - - while(!complete) - { - bytesWritten += setTxInterrupt(buffer + bytesWritten, bufferLen - bytesWritten, mode); - send(mode); - - if(mode == ASYNC || bytesWritten >= bufferLen) - complete = true; - } + int bytesWritten = setTxInterrupt(buffer, bufferLen, mode); unlockTx(); @@ -979,6 +940,14 @@ int Serial::eventOn(ManagedString delimeters, SerialMode mode) */ int Serial::isReadable() { + if(!(status & CODAL_SERIAL_STATUS_RX_BUFF_INIT)) + { + int result = initialiseRx(); + + if(result != DEVICE_OK) + return result; + } + return (rxBuffTail != rxBuffHead) ? 1 : 0; } From 7d772a1a9f1e2b9af22980da2c5cdde67f00fc8c Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sun, 7 Mar 2021 17:04:52 +0000 Subject: [PATCH 19/43] Close possible race condition in Serial receive handling --- source/driver-models/Serial.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/source/driver-models/Serial.cpp b/source/driver-models/Serial.cpp index 462f9639..a3372fa4 100644 --- a/source/driver-models/Serial.cpp +++ b/source/driver-models/Serial.cpp @@ -883,12 +883,16 @@ int Serial::eventAfter(int len, SerialMode mode) if(mode == SYNC_SPINWAIT) return DEVICE_INVALID_PARAMETER; + // Schedule this fiber to wake on an event from the serial port, if necessary + if(mode == SYNC_SLEEP) + fiber_wake_on_event(this->id, CODAL_SERIAL_EVT_HEAD_MATCH); + //configure our head match... this->rxBuffHeadMatch = (rxBuffHead + len) % rxBuffSize; - //block! + // Deschedule this fiber, if necessary if(mode == SYNC_SLEEP) - fiber_wait_for_event(this->id, CODAL_SERIAL_EVT_HEAD_MATCH); + schedule(); return DEVICE_OK; } From 4fa8275e2069434f83cdf967098ddd53644abf03 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sun, 7 Mar 2021 18:08:12 +0000 Subject: [PATCH 20/43] Protect against buffer underflow in Serial::readUntil() (codal-microbit-v2#79) - Correct apply wrap around of circular buffer --- source/driver-models/Serial.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/driver-models/Serial.cpp b/source/driver-models/Serial.cpp index a3372fa4..fa00fe19 100644 --- a/source/driver-models/Serial.cpp +++ b/source/driver-models/Serial.cpp @@ -769,6 +769,8 @@ ManagedString Serial::readUntil(ManagedString delimeters, SerialMode mode) eventOn(delimeters, mode); foundIndex = rxBuffHead - 1; + if (foundIndex < 0) + foundIndex += rxBuffSize; this->delimeters = ManagedString(); } From 8c0e1e475b97dedbdb43d76a015220ff091cfba9 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Mon, 8 Mar 2021 14:25:31 +0000 Subject: [PATCH 21/43] Ensure Serial buffer sizes are valid (codal-microbit-v2#74) - Add validation to prevent overflow in Serial::setTxBufferSize() and Serial::setRxBufferSize() --- source/driver-models/Serial.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/driver-models/Serial.cpp b/source/driver-models/Serial.cpp index fa00fe19..d72d6bcc 100644 --- a/source/driver-models/Serial.cpp +++ b/source/driver-models/Serial.cpp @@ -986,7 +986,10 @@ int Serial::setRxBufferSize(uint8_t size) lockRx(); // + 1 so there is a usable buffer size, of the size the user requested. - this->rxBuffSize = size + 1; + if (size != 255) + size++; + + this->rxBuffSize = size; int result = initialiseRx(); @@ -1011,7 +1014,10 @@ int Serial::setTxBufferSize(uint8_t size) lockTx(); // + 1 so there is a usable buffer size, of the size the user requested. - this->txBuffSize = size + 1; + if (size != 255) + size++; + + this->txBuffSize = size; int result = initialiseTx(); From 37d6f5e4c42fa6e65a2763bd89df22bb6a9a8c62 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sat, 13 Mar 2021 20:09:26 +0000 Subject: [PATCH 22/43] Ensure DEVICE_BUTTON_STATE_HOLD_TRIGGERED state is cleared in Button class (codal-microbit-v2#53) - Clear DEVICE_BUTTON_STATE_HOLD_TRIGGERED state when button is released - Allows DEVICE_BUTTON_EVT_HOLDevet to be raised more than once per pin --- source/drivers/Button.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/source/drivers/Button.cpp b/source/drivers/Button.cpp index 6e410ccb..da76d034 100644 --- a/source/drivers/Button.cpp +++ b/source/drivers/Button.cpp @@ -131,6 +131,7 @@ void Button::periodicCallback() if(sigma < DEVICE_BUTTON_SIGMA_THRESH_LO && (status & DEVICE_BUTTON_STATE)) { status &= ~DEVICE_BUTTON_STATE; + status &= ~DEVICE_BUTTON_STATE_HOLD_TRIGGERED; Event evt(id,DEVICE_BUTTON_EVT_UP); if (eventConfiguration == DEVICE_BUTTON_ALL_EVENTS) From f1dd42498e8efb71a7601b155c0ce7c30f722c57 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sun, 14 Mar 2021 11:47:15 +0000 Subject: [PATCH 23/43] Make zero initialisation of ManagedBuffers optional Non-zero initialisation is sometimes useful for low level operations, such as allocation of DMA buffers. - Introduce BufferInitialize enumeration - Prmeterise managedBuffer(length) constructor to allow user to optionally if zero initialisation is needed - Add default parameter value of BufferInitialize::Zero to maintain backwards compatibility --- inc/types/ManagedBuffer.h | 11 +++++++++-- source/types/ManagedBuffer.cpp | 12 +++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/inc/types/ManagedBuffer.h b/inc/types/ManagedBuffer.h index 4e1fd0df..13cba31b 100644 --- a/inc/types/ManagedBuffer.h +++ b/inc/types/ManagedBuffer.h @@ -36,6 +36,12 @@ namespace codal uint8_t payload[0]; // ManagedBuffer data }; + enum class BufferInitialize : uint8_t + { + None = 0, + Zero + }; + /** * Class definition for a ManagedBuffer. * A ManagedBuffer holds a series of bytes for general purpose use. @@ -69,7 +75,7 @@ namespace codal * ManagedBuffer p(16); // Creates a ManagedBuffer 16 bytes long. * @endcode */ - ManagedBuffer(int length); + ManagedBuffer(int length, BufferInitialize initialize = BufferInitialize::Zero); /** * Constructor. @@ -120,9 +126,10 @@ namespace codal * * @param data The data with which to fill the buffer. * @param length The length of the buffer to create. + * @param initialize The initialization mode to use for the allocted memory in the buffer * */ - void init(uint8_t *data, int length); + void init(uint8_t *data, int length, BufferInitialize initialize); /** * Destructor. diff --git a/source/types/ManagedBuffer.cpp b/source/types/ManagedBuffer.cpp index 0cd788a1..28a9f92a 100644 --- a/source/types/ManagedBuffer.cpp +++ b/source/types/ManagedBuffer.cpp @@ -69,9 +69,9 @@ ManagedBuffer::ManagedBuffer() * ManagedBuffer p(16); // Creates a ManagedBuffer 16 bytes long. * @endcode */ -ManagedBuffer::ManagedBuffer(int length) +ManagedBuffer::ManagedBuffer(int length, BufferInitialize initialize) { - this->init(NULL, length); + this->init(NULL, length, initialize); } /** @@ -90,7 +90,7 @@ ManagedBuffer::ManagedBuffer(int length) */ ManagedBuffer::ManagedBuffer(uint8_t *data, int length) { - this->init(data, length); + this->init(data, length, BufferInitialize::None); } /** @@ -128,9 +128,10 @@ ManagedBuffer::ManagedBuffer(BufferData *p) * * @param data The data with which to fill the buffer. * @param length The length of the buffer to create. + * @param initialize The initialization mode to use for the allocted memory in the buffer * */ -void ManagedBuffer::init(uint8_t *data, int length) +void ManagedBuffer::init(uint8_t *data, int length, BufferInitialize initialize) { if (length <= 0) { initEmpty(); @@ -145,7 +146,8 @@ void ManagedBuffer::init(uint8_t *data, int length) // Copy in the data buffer, if provided. if (data) memcpy(ptr->payload, data, length); - else + + if (initialize == BufferInitialize::Zero) memset(ptr->payload, 0, length); } From caee1151f0fdfb3a6678f9c269e7b33c9163bfea Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sun, 14 Mar 2021 23:22:27 +0000 Subject: [PATCH 24/43] Update RefCounted types to use atomic operations on reference count - Enable ManagedBuffer to be safely used in IRQ contexts --- inc/types/RefCounted.h | 2 +- source/types/RefCounted.cpp | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/inc/types/RefCounted.h b/inc/types/RefCounted.h index e84d987a..48a583a0 100644 --- a/inc/types/RefCounted.h +++ b/inc/types/RefCounted.h @@ -43,7 +43,7 @@ namespace codal * Should never be even or one (object should be deleted then). * When it's set to 0xffff, it means the object sits in flash and should not be counted. */ - uint16_t refCount; + volatile uint16_t refCount; #if CONFIG_ENABLED(DEVICE_TAG) uint16_t tag; diff --git a/source/types/RefCounted.cpp b/source/types/RefCounted.cpp index 36e085d9..37a70b36 100644 --- a/source/types/RefCounted.cpp +++ b/source/types/RefCounted.cpp @@ -71,7 +71,7 @@ bool RefCounted::isReadOnly() void RefCounted::incr() { if (!isReadOnlyInline(this)) - refCount += 2; + __sync_fetch_and_add(&refCount, 2); } /** @@ -82,11 +82,7 @@ void RefCounted::decr() if (isReadOnlyInline(this)) return; - refCount -= 2; - if (refCount == 1) { - // if we just call plain free(), the write to refCount will - // be optimized away, and it will stay '3'; this way we make - // sure to get a panic on next incr()/decr() + if (__sync_fetch_and_add(&refCount, -2) == 3 ) { destroy(); } } From c8547baf94649b20a80457fffb279078e9d32a0d Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Thu, 18 Mar 2021 10:06:57 +0000 Subject: [PATCH 25/43] Default atomic operations for MCU without hardware support (#132) * Introduce default atomic operations for reference counted types - support for CPUs without hardware supported exclusivity instructions, such as Cortex M0. --- inc/core/codal_target_hal.h | 11 ++++++++++ source/core/codal_default_target_hal.cpp | 26 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/inc/core/codal_target_hal.h b/inc/core/codal_target_hal.h index 835c036a..dbd52ffe 100644 --- a/inc/core/codal_target_hal.h +++ b/inc/core/codal_target_hal.h @@ -79,6 +79,17 @@ extern "C" PROCESSOR_WORD_TYPE tcb_get_sp(void* tcb); void tcb_configure_args(void* tcb, PROCESSOR_WORD_TYPE ep, PROCESSOR_WORD_TYPE cp, PROCESSOR_WORD_TYPE pm); + + /** + * Default implementation of atomic fetch and add opertaion. + * GCC provides this where possible, but this is not supported on some CPU architectures... + * + * @param ptr pointer to the memory to access. + * @param value the value to add to the memory location. + * @return the value of th ememory location BEFORE the add operation took place. + */ + short unsigned int __sync_fetch_and_add_2 (volatile void *ptr, short unsigned int value); + } diff --git a/source/core/codal_default_target_hal.cpp b/source/core/codal_default_target_hal.cpp index d5e23b30..4e84efbc 100644 --- a/source/core/codal_default_target_hal.cpp +++ b/source/core/codal_default_target_hal.cpp @@ -38,3 +38,29 @@ __attribute__((weak)) void target_deepsleep() // if not implemented, default to WFI target_wait_for_event(); } + +/** + * Default implementation of atomic fetch and add opertaion. + * GCC provides this where possible, but this is not supported on some CPU architectures... + * + * @param ptr pointer to the memory to access. + * @param value the value to add to the memory location. + * @return the value of th ememory location BEFORE the add operation took place. + */ +__attribute__((weak)) short unsigned int __sync_fetch_and_add_2 (volatile void *ptr, short unsigned int value) +{ + +#if CONFIG_ENABLED(DISABLE_IRQ_FOR_SOFTWARE_ATOMICS) + target_disable_irq(); +#endif + + uint16_t *p = (uint16_t *)ptr; + uint16_t old = *p; + *p += value; + +#if CONFIG_ENABLED(DISABLE_IRQ_FOR_SOFTWARE_ATOMICS) + target_enable_irq(); +#endif + + return old; +} From cc2924d9a3f1a83f8eafd79551f8c21b3012fbfa Mon Sep 17 00:00:00 2001 From: James Devine Date: Thu, 22 Apr 2021 13:54:31 +0100 Subject: [PATCH 26/43] add events to CodalUSB --- inc/core/CodalComponent.h | 1 + inc/driver-models/CodalUSB.h | 10 ++++++++-- source/driver-models/CodalUSB.cpp | 19 +++++++++++-------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/inc/core/CodalComponent.h b/inc/core/CodalComponent.h index bdeb65f4..43be4e3e 100644 --- a/inc/core/CodalComponent.h +++ b/inc/core/CodalComponent.h @@ -67,6 +67,7 @@ DEALINGS IN THE SOFTWARE. #define DEVICE_ID_JACDAC_CONFIGURATION_SERVICE 33 #define DEVICE_ID_SYSTEM_ADC 34 #define DEVICE_ID_PULSE_IN 35 +#define DEVICE_ID_USB 36 #define DEVICE_ID_IO_P0 100 // IDs 100-227 are reserved for I/O Pin IDs. diff --git a/inc/driver-models/CodalUSB.h b/inc/driver-models/CodalUSB.h index 06566543..22e3adf2 100644 --- a/inc/driver-models/CodalUSB.h +++ b/inc/driver-models/CodalUSB.h @@ -26,9 +26,15 @@ DEALINGS IN THE SOFTWARE. #define CODAL_USB_H #include "CodalConfig.h" +#include "CodalComponent.h" #if CONFIG_ENABLED(DEVICE_USB) +// define usb events for the CODAL message bus +#define USB_EVT_CONNECTED 1 // powered usb cable has been plugged into the device +#define USB_EVT_REMOVED 2 // powered usb cable has been removed from the device +#define USB_EVT_READY 3 // powered usb cable with data connection has been plugged into the device, and the usb peripheral is ready! + #include #include "ErrorNo.h" @@ -261,7 +267,7 @@ class CodalDummyUSBInterface : public CodalUSBInterface { virtual const InterfaceInfo *getInterfaceInfo(); }; -class CodalUSB +class CodalUSB : public codal::CodalComponent { uint8_t endpointsUsed; uint8_t startDelayCount; @@ -284,7 +290,7 @@ class CodalUSB UsbEndpointIn *ctrlIn; UsbEndpointOut *ctrlOut; - CodalUSB(); + CodalUSB(uint16_t id = DEVICE_ID_USB); int add(CodalUSBInterface &interface); diff --git a/source/driver-models/CodalUSB.cpp b/source/driver-models/CodalUSB.cpp index 6061bec0..9d1f7d2c 100644 --- a/source/driver-models/CodalUSB.cpp +++ b/source/driver-models/CodalUSB.cpp @@ -182,8 +182,11 @@ const InterfaceInfo *CodalDummyUSBInterface::getInterfaceInfo() return &codalDummyIfaceInfo; } -CodalUSB::CodalUSB() +CodalUSB::CodalUSB(uint16_t id) { + // Store our identifiers. + this->id = id; + this->status = 0; usbInstance = this; endpointsUsed = 1; // CTRL endpoint ctrlIn = NULL; @@ -448,7 +451,7 @@ void CodalUSB::setupRequest(USBSetup &setup) LOG("SETUP Req=%x type=%x val=%x:%x idx=%x len=%d", setup.bRequest, setup.bmRequestType, setup.wValueH, setup.wValueL, setup.wIndex, setup.wLength); - int status = DEVICE_OK; + int transactionStatus = DEVICE_OK; // Standard Requests uint16_t wValue = (setup.wValueH << 8) | setup.wValueL; @@ -504,7 +507,7 @@ void CodalUSB::setupRequest(USBSetup &setup) break; case USB_REQ_GET_DESCRIPTOR: LOG("GET DESC"); - status = sendDescriptors(setup); + transactionStatus = sendDescriptors(setup); break; case USB_REQ_SET_DESCRIPTOR: LOG("SET DESC"); @@ -524,7 +527,7 @@ void CodalUSB::setupRequest(USBSetup &setup) sendzlp(); } else - status = DEVICE_NOT_SUPPORTED; + transactionStatus = DEVICE_NOT_SUPPORTED; break; } } @@ -536,7 +539,7 @@ void CodalUSB::setupRequest(USBSetup &setup) case VENDOR_MS20: if (numWebUSBInterfaces == 0) { - status = DEVICE_NOT_SUPPORTED; + transactionStatus = DEVICE_NOT_SUPPORTED; } else { @@ -565,17 +568,17 @@ void CodalUSB::setupRequest(USBSetup &setup) case VENDOR_WEBUSB: // this is the place for the WebUSB landing page, if we ever want to do that - status = DEVICE_NOT_IMPLEMENTED; + transactionStatus = DEVICE_NOT_IMPLEMENTED; break; } } #endif else { - status = interfaceRequest(setup, true); + transactionStatus = interfaceRequest(setup, true); } - if (status < 0) + if (transactionStatus < 0) stall(); // sending response clears this - make sure we did From 30c99d9e04e6331fc60af9db9b846479ab7cf36c Mon Sep 17 00:00:00 2001 From: James Devine Date: Fri, 23 Apr 2021 16:29:44 +0100 Subject: [PATCH 27/43] ifguard uf2_info in GhostFAT implementation --- source/drivers/GhostFAT.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/drivers/GhostFAT.cpp b/source/drivers/GhostFAT.cpp index 685b5638..9dd06d1f 100644 --- a/source/drivers/GhostFAT.cpp +++ b/source/drivers/GhostFAT.cpp @@ -296,6 +296,7 @@ void GhostFAT::readBlocks(int blockAddr, int numBlocks) finishReadWrite(); } +#ifdef BOOTLOADER_START_ADDR void GhostFAT::writeBlocks(int blockAddr, int numBlocks) { uint8_t buf[512]; @@ -329,6 +330,7 @@ void GhostFAT::writeBlocks(int blockAddr, int numBlocks) finishReadWrite(); } +#endif GhostFAT::GhostFAT() { @@ -467,7 +469,9 @@ static void readDMesg(GFATEntry *ent, unsigned blockAddr, char *dst) void GhostFAT::addFiles() { +#ifdef BOOTLOADER_START_ADDR addStringFile(uf2_info(), "info_uf2.txt"); +#endif addFile(readCurrentUF2, this, "current.uf2", internalFlashSize() * 2); #if DEVICE_DMESG_BUFFER_SIZE > 0 addFile(readDMesg, this, "dmesg.txt", DEVICE_DMESG_BUFFER_SIZE); @@ -475,4 +479,4 @@ void GhostFAT::addFiles() } } -#endif +#endif \ No newline at end of file From 939f944296aa4c808af7da0daef763cff6a216eb Mon Sep 17 00:00:00 2001 From: James Devine Date: Fri, 23 Apr 2021 16:35:34 +0100 Subject: [PATCH 28/43] fixup GhostFAT uf2 bootloader ifdef --- source/drivers/GhostFAT.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/drivers/GhostFAT.cpp b/source/drivers/GhostFAT.cpp index 9dd06d1f..54fd2598 100644 --- a/source/drivers/GhostFAT.cpp +++ b/source/drivers/GhostFAT.cpp @@ -296,9 +296,10 @@ void GhostFAT::readBlocks(int blockAddr, int numBlocks) finishReadWrite(); } -#ifdef BOOTLOADER_START_ADDR + void GhostFAT::writeBlocks(int blockAddr, int numBlocks) { +#ifdef BOOTLOADER_START_ADDR uint8_t buf[512]; bool handoverSupported = false; @@ -327,10 +328,10 @@ void GhostFAT::writeBlocks(int blockAddr, int numBlocks) } blockAddr++; } +#endif finishReadWrite(); } -#endif GhostFAT::GhostFAT() { From f56a98351d70386c11dff123a4d9872a04f3eb29 Mon Sep 17 00:00:00 2001 From: James Devine Date: Tue, 4 May 2021 15:07:07 +0100 Subject: [PATCH 29/43] add USB_EP_TIMEOUT flag --- inc/driver-models/CodalUSB.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/inc/driver-models/CodalUSB.h b/inc/driver-models/CodalUSB.h index 22e3adf2..cad90e27 100644 --- a/inc/driver-models/CodalUSB.h +++ b/inc/driver-models/CodalUSB.h @@ -38,6 +38,8 @@ DEALINGS IN THE SOFTWARE. #include #include "ErrorNo.h" +#define USB_EP_TIMEOUT 0x02 + #define USB_CONFIG_POWERED_MASK 0x40 #define USB_CONFIG_BUS_POWERED 0x80 #define USB_CONFIG_SELF_POWERED 0xC0 From 7a834f5b951992094e1486d2033b54e9a1f8bfb0 Mon Sep 17 00:00:00 2001 From: James Devine Date: Tue, 4 May 2021 21:53:27 +0100 Subject: [PATCH 30/43] unset EP_TIMEOUT flag on read (OUT token) --- source/driver-models/CodalUSB.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/driver-models/CodalUSB.cpp b/source/driver-models/CodalUSB.cpp index 9d1f7d2c..56528ef2 100644 --- a/source/driver-models/CodalUSB.cpp +++ b/source/driver-models/CodalUSB.cpp @@ -588,7 +588,11 @@ void CodalUSB::setupRequest(USBSetup &setup) void CodalUSB::interruptHandler() { for (CodalUSBInterface *iface = interfaces; iface; iface = iface->next) + { + if (iface->in) + iface->in->flags &= USB_EP_TIMEOUT; iface->endpointRequest(); + } } void CodalUSB::initEndpoints() From 08e08b606a468dbbd7094be2d3a9a507ec44cc40 Mon Sep 17 00:00:00 2001 From: James Devine Date: Thu, 6 May 2021 10:38:19 +0100 Subject: [PATCH 31/43] correct incorrect bitmsk unset --- source/driver-models/CodalUSB.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/driver-models/CodalUSB.cpp b/source/driver-models/CodalUSB.cpp index 56528ef2..552f0d3c 100644 --- a/source/driver-models/CodalUSB.cpp +++ b/source/driver-models/CodalUSB.cpp @@ -590,7 +590,7 @@ void CodalUSB::interruptHandler() for (CodalUSBInterface *iface = interfaces; iface; iface = iface->next) { if (iface->in) - iface->in->flags &= USB_EP_TIMEOUT; + iface->in->flags &= ~USB_EP_TIMEOUT; iface->endpointRequest(); } } From 1076c9a4388809a4e2c262d62b0064108066ab19 Mon Sep 17 00:00:00 2001 From: James Devine Date: Thu, 13 May 2021 15:26:57 +0100 Subject: [PATCH 32/43] remove EP_TIMEOUT flag Too tricky to track state --- inc/driver-models/CodalUSB.h | 2 -- source/driver-models/CodalUSB.cpp | 4 ---- 2 files changed, 6 deletions(-) diff --git a/inc/driver-models/CodalUSB.h b/inc/driver-models/CodalUSB.h index cad90e27..22e3adf2 100644 --- a/inc/driver-models/CodalUSB.h +++ b/inc/driver-models/CodalUSB.h @@ -38,8 +38,6 @@ DEALINGS IN THE SOFTWARE. #include #include "ErrorNo.h" -#define USB_EP_TIMEOUT 0x02 - #define USB_CONFIG_POWERED_MASK 0x40 #define USB_CONFIG_BUS_POWERED 0x80 #define USB_CONFIG_SELF_POWERED 0xC0 diff --git a/source/driver-models/CodalUSB.cpp b/source/driver-models/CodalUSB.cpp index 552f0d3c..9d1f7d2c 100644 --- a/source/driver-models/CodalUSB.cpp +++ b/source/driver-models/CodalUSB.cpp @@ -588,11 +588,7 @@ void CodalUSB::setupRequest(USBSetup &setup) void CodalUSB::interruptHandler() { for (CodalUSBInterface *iface = interfaces; iface; iface = iface->next) - { - if (iface->in) - iface->in->flags &= ~USB_EP_TIMEOUT; iface->endpointRequest(); - } } void CodalUSB::initEndpoints() From 111fc68a06a6d0b05c3e8551890dc11f014c1dc9 Mon Sep 17 00:00:00 2001 From: Martin Williams Date: Tue, 15 Jun 2021 12:13:07 +0100 Subject: [PATCH 33/43] Deep sleep 2 (#138) Introduce Framework for Power Efficient Deep Sleep --- inc/core/CodalComponent.h | 30 +++ inc/core/CodalFiber.h | 25 +- inc/core/ErrorNo.h | 3 + inc/core/EventModel.h | 2 +- inc/core/NotifyEvents.h | 1 + inc/core/codal_target_hal.h | 2 + inc/driver-models/LowLevelTimer.h | 2 + inc/driver-models/Pin.h | 24 ++ inc/driver-models/Serial.h | 3 +- inc/driver-models/Timer.h | 113 +++++++- inc/drivers/Button.h | 25 ++ inc/drivers/MessageBus.h | 2 - source/core/CodalComponent.cpp | 64 ++++- source/core/CodalFiber.cpp | 35 ++- source/core/codal_default_target_hal.cpp | 6 + source/driver-models/Timer.cpp | 323 ++++++++++++++++++++--- source/drivers/Button.cpp | 32 +++ source/drivers/LSM303Accelerometer.cpp | 6 +- source/drivers/LSM303Magnetometer.cpp | 6 +- 19 files changed, 631 insertions(+), 73 deletions(-) diff --git a/inc/core/CodalComponent.h b/inc/core/CodalComponent.h index 43be4e3e..c9e0b0fc 100644 --- a/inc/core/CodalComponent.h +++ b/inc/core/CodalComponent.h @@ -184,6 +184,36 @@ namespace codal */ static void setAllSleep(bool doSleep); + typedef enum deepSleepCallbackReason + { + deepSleepCallbackPrepare, //Prepare for sleep + deepSleepCallbackBegin, //Puts the component in sleep (low power) mode. + deepSleepCallbackBeginWithWakeUps, //and enable wake-up sources + deepSleepCallbackEnd, //Brings the component out of sleep (low power) mode. + deepSleepCallbackEndWithWakeUps, //and disable wake-up sources + deepSleepCallbackCountWakeUps, //Count deep sleep wake-up sources. + deepSleepCallbackClearWakeUps //Clear deep sleep wake up sources + } deepSleepCallbackReason; + + typedef struct deepSleepCallbackData + { + int count; + + void init() { count = 0; } + + deepSleepCallbackData() { init(); } + } deepSleepCallbackData; + + /** + * Perform functions related to deep sleep. + */ + virtual int deepSleepCallback( deepSleepCallbackReason reason, deepSleepCallbackData *data); + + /** + * Perform functions related to deep sleep. + */ + static void deepSleepAll( deepSleepCallbackReason reason, deepSleepCallbackData *data); + /** * If you have added your component to the idle or system tick component arrays, * you must remember to remove your component from them if your component is destructed. diff --git a/inc/core/CodalFiber.h b/inc/core/CodalFiber.h index b83b7138..822e0332 100644 --- a/inc/core/CodalFiber.h +++ b/inc/core/CodalFiber.h @@ -44,6 +44,7 @@ DEALINGS IN THE SOFTWARE. // Fiber Scheduler Flags #define DEVICE_SCHEDULER_RUNNING 0x01 #define DEVICE_SCHEDULER_IDLE 0x02 +#define DEVICE_SCHEDULER_DEEPSLEEP 0x04 // Fiber Flags #define DEVICE_FIBER_FLAG_FOB 0x01 @@ -56,6 +57,7 @@ DEALINGS IN THE SOFTWARE. #define DEVICE_GET_FIBER_LIST_AVAILABLE 1 + namespace codal { /** @@ -91,7 +93,7 @@ namespace codal /** * Determines if the fiber scheduler is operational. * - * @return 1 if the fber scheduler is running, 0 otherwise. + * @return 1 if the fiber scheduler is running, 0 otherwise. */ int fiber_scheduler_running(); @@ -298,6 +300,13 @@ namespace codal */ int scheduler_runqueue_empty(); + /** + * Determines if any fibers are waiting for events. + * + * @return 1 if there are no fibers currently waiting for events; otherwise 0 + */ + int scheduler_waitqueue_empty(); + /** * Utility function to add the currenty running fiber to the given queue. * @@ -332,6 +341,20 @@ namespace codal */ void idle_task(); + /** + * Determines if deep sleep is pending. + * + * @return 1 if deep sleep is pending, 0 otherwise. + */ + int fiber_scheduler_get_deepsleep_pending(); + + /** + * Flag if deep sleep is pending. + * + * @param pending 1 if deep sleep is pending, 0 otherwise. + */ + void fiber_scheduler_set_deepsleep_pending( int pending); + class FiberLock { private: diff --git a/inc/core/ErrorNo.h b/inc/core/ErrorNo.h index 4f483e7f..63840341 100644 --- a/inc/core/ErrorNo.h +++ b/inc/core/ErrorNo.h @@ -97,6 +97,9 @@ enum PanicCode{ // Non-recoverable error in the JACDAC stack DEVICE_JACDAC_ERROR = 60, + // CPU SDK faults + DEVICE_CPU_SDK = 70, + // hardware incorrect configuration DEVICE_HARDWARE_CONFIGURATION_ERROR = 90, }; diff --git a/inc/core/EventModel.h b/inc/core/EventModel.h index a3025f68..157514d4 100644 --- a/inc/core/EventModel.h +++ b/inc/core/EventModel.h @@ -108,7 +108,7 @@ namespace codal * * @return This default implementation simply returns NULL. */ - Listener *elementAt(int) + virtual Listener *elementAt(int) { return NULL; } diff --git a/inc/core/NotifyEvents.h b/inc/core/NotifyEvents.h index 477d2213..12601b6f 100644 --- a/inc/core/NotifyEvents.h +++ b/inc/core/NotifyEvents.h @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. #define CODAL_SERIAL_EVT_TX_EMPTY 2 #define BLE_EVT_SERIAL_TX_EMPTY 3 #define ARCADE_PLAYER_JOIN_RESULT 4 +#define POWER_EVT_CANCEL_DEEPSLEEP 5 // Any values after 1024 are available for application use #define DEVICE_NOTIFY_USER_EVENT_BASE 1024 diff --git a/inc/core/codal_target_hal.h b/inc/core/codal_target_hal.h index dbd52ffe..96804879 100644 --- a/inc/core/codal_target_hal.h +++ b/inc/core/codal_target_hal.h @@ -45,6 +45,8 @@ extern "C" uint64_t target_get_serial(); + void target_scheduler_idle(); + void target_wait_for_event(); void target_deepsleep(); diff --git a/inc/driver-models/LowLevelTimer.h b/inc/driver-models/LowLevelTimer.h index c7845783..bd758d0d 100644 --- a/inc/driver-models/LowLevelTimer.h +++ b/inc/driver-models/LowLevelTimer.h @@ -5,6 +5,8 @@ #include "CodalComponent.h" #include "codal_target_hal.h" +#define CODAL_LOWLEVELTIMER_STATUS_SLEEP_IRQENABLE 0x01 + namespace codal { diff --git a/inc/driver-models/Pin.h b/inc/driver-models/Pin.h index 255d6a4b..fbc25c04 100644 --- a/inc/driver-models/Pin.h +++ b/inc/driver-models/Pin.h @@ -37,6 +37,7 @@ DEALINGS IN THE SOFTWARE. #define IO_STATUS_EVENT_PULSE_ON_EDGE 0x0040 // Pin will generate events on pin change #define IO_STATUS_INTERRUPT_ON_EDGE 0x0080 // Pin will generate events on pin change #define IO_STATUS_ACTIVE_HI 0x0100 // Pin is ACTIVE_HI if set, or ACTIVE_LO if clear +#define IO_STATUS_WAKE_ON_ACTIVE 0x0200 // Pin should trigger power manager wake-up #define DEVICE_PIN_MAX_OUTPUT 1023 @@ -507,6 +508,29 @@ namespace codal setPolarity(0); } + /** + * Sets whether the pin should trigger power manager wake-up. + * + * @param wake The action of the pin - either 1 to trigger wake-up or 0 for no wake-up + */ + void wakeOnActive(int wake) + { + if (wake) + status |= IO_STATUS_WAKE_ON_ACTIVE; + else + status &= ~IO_STATUS_WAKE_ON_ACTIVE; + } + + /** + * Deternine if the pin should trigger power manager wake-up. + * + * @param wake The action of the pin - either 1 to trigger wake up or 0 for no + */ + int isWakeOnActive() + { + return (status & IO_STATUS_WAKE_ON_ACTIVE) ? 1 : 0; + } + /** * Disconnect any attached peripherals from this pin. */ diff --git a/inc/driver-models/Serial.h b/inc/driver-models/Serial.h index dbe11abb..807689d8 100644 --- a/inc/driver-models/Serial.h +++ b/inc/driver-models/Serial.h @@ -41,7 +41,8 @@ DEALINGS IN THE SOFTWARE. #define CODAL_SERIAL_STATUS_TX_IN_USE 0x02 #define CODAL_SERIAL_STATUS_RX_BUFF_INIT 0x04 #define CODAL_SERIAL_STATUS_TX_BUFF_INIT 0x08 -#define CODAL_SERIAL_STATUS_RXD 0x10 +#define CODAL_SERIAL_STATUS_RXD 0x10 +#define CODAL_SERIAL_STATUS_DEEPSLEEP 0x20 namespace codal diff --git a/inc/driver-models/Timer.h b/inc/driver-models/Timer.h index aa09bc0f..3d9d488b 100644 --- a/inc/driver-models/Timer.h +++ b/inc/driver-models/Timer.h @@ -33,28 +33,41 @@ DEALINGS IN THE SOFTWARE. #define CODAL_TIMER_DEFAULT_EVENT_LIST_SIZE 10 #endif +// +// TimerEvent flags +// +#define CODAL_TIMER_EVENT_FLAGS_NONE 0 +#define CODAL_TIMER_EVENT_FLAGS_WAKEUP 0x01 + namespace codal { struct TimerEvent { - uint16_t id; - uint16_t value; CODAL_TIMESTAMP period; CODAL_TIMESTAMP timestamp; + uint16_t id; + uint16_t value; + uint32_t flags; // We only need one byte, but sizeof(TimerEvent) is still 24 - void set(CODAL_TIMESTAMP timestamp, CODAL_TIMESTAMP period, uint16_t id, uint16_t value) + void set(CODAL_TIMESTAMP timestamp, CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags = CODAL_TIMER_EVENT_FLAGS_NONE) { this->timestamp = timestamp; this->period = period; this->id = id; this->value = value; + this->flags = flags; } }; class Timer { +#if CONFIG_ENABLED(CODAL_TIMER_32BIT) + uint32_t sigma; + uint32_t delta; +#else uint16_t sigma; uint16_t delta; +#endif LowLevelTimer& timer; /** @@ -115,8 +128,10 @@ namespace codal * @param id the ID to be used in event generation. * * @param value the value to place into the Events' value field. + * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. */ - int eventAfter(CODAL_TIMESTAMP period, uint16_t id, uint16_t value); + int eventAfter(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags = CODAL_TIMER_EVENT_FLAGS_NONE); /** * Configures this Timer instance to fire an event after period @@ -127,8 +142,10 @@ namespace codal * @param id the ID to be used in event generation. * * @param value the value to place into the Events' value field. + * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. */ - int eventAfterUs(CODAL_TIMESTAMP period, uint16_t id, uint16_t value); + int eventAfterUs(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags = CODAL_TIMER_EVENT_FLAGS_NONE); /** * Configures this Timer instance to fire an event every period @@ -139,8 +156,10 @@ namespace codal * @param id the ID to be used in event generation. * * @param value the value to place into the Events' value field. + * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. */ - int eventEvery(CODAL_TIMESTAMP period, uint16_t id, uint16_t value); + int eventEvery(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags = CODAL_TIMER_EVENT_FLAGS_NONE); /** * Configures this Timer instance to fire an event every period @@ -151,8 +170,10 @@ namespace codal * @param id the ID to be used in event generation. * * @param value the value to place into the Events' value field. + * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. */ - int eventEveryUs(CODAL_TIMESTAMP period, uint16_t id, uint16_t value); + int eventEveryUs(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags = CODAL_TIMER_EVENT_FLAGS_NONE); /** * Cancels any events matching the given id and value. @@ -173,6 +194,28 @@ namespace codal */ void trigger(bool isFallback); + /** + * Called from power manager before sleep. + * @param counter reference to a variable to receive the current timer counter + * + * @return the current time since power on in microseconds + */ + CODAL_TIMESTAMP deepSleepBegin( CODAL_TIMESTAMP &counter); + + /** + * Called from power manager after sleep. + * @param counter the current timer counter + * @param micros time elapsed since deepSleepBegin + */ + void deepSleepEnd( CODAL_TIMESTAMP counter, CODAL_TIMESTAMP micros); + + /** + * Determine the time of the next wake up event. + * @param timestamp reference to a variable to receive the time. + * @return true if there is an event. + */ + bool deepSleepWakeUpTime( CODAL_TIMESTAMP ×tamp); + /** * Enables interrupts for this timer instance. */ @@ -194,7 +237,8 @@ namespace codal TimerEvent *getTimerEvent(); void releaseTimerEvent(TimerEvent *event); - int setEvent(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, bool repeat); + int setEvent(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, bool repeat, uint32_t flags); + TimerEvent *deepSleepWakeUpEvent(); }; /* @@ -224,9 +268,11 @@ namespace codal * * @param the value to fire against the current system_timer id. * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. + * * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. */ - int system_timer_event_every_us(CODAL_TIMESTAMP period, uint16_t id, uint16_t value); + int system_timer_event_every_us(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags = CODAL_TIMER_EVENT_FLAGS_NONE); /** * Configure an event to occur every given number of milliseconds. @@ -235,9 +281,11 @@ namespace codal * * @param the value to fire against the current system_timer id. * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. + * * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. */ - int system_timer_event_every(CODAL_TIMESTAMP period, uint16_t id, uint16_t value); + int system_timer_event_every(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags = CODAL_TIMER_EVENT_FLAGS_NONE); /** * Configure an event to occur after a given number of microseconds. @@ -246,9 +294,11 @@ namespace codal * * @param the value to fire against the current system_timer id. * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. + * * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. */ - int system_timer_event_after(CODAL_TIMESTAMP period, uint16_t id, uint16_t value); + int system_timer_event_after(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags = CODAL_TIMER_EVENT_FLAGS_NONE); /** * Configure an event to occur after a given number of milliseconds. @@ -257,9 +307,11 @@ namespace codal * * @param the value to fire against the current system_timer id. * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. + * * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. */ - int system_timer_event_after_us(CODAL_TIMESTAMP period, uint16_t id, uint16_t value); + int system_timer_event_after_us(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags = CODAL_TIMER_EVENT_FLAGS_NONE); /** * Cancels any events matching the given id and value. @@ -312,6 +364,43 @@ namespace codal */ int system_timer_wait_ms(uint32_t period); + /** + * Determine the current time and the corresponding timer counter, + * to enable the caller to take over tracking time. + * + * @param counter reference to a variable to receive the current timer counter + * + * @return the current time since power on in microseconds + */ + CODAL_TIMESTAMP system_timer_deepsleep_begin( CODAL_TIMESTAMP &counter); + + /** + * After taking over time tracking with system_timer_deepsleep_begin, + * hand back control by supplying a new timer counter value with + * corresponding elapsed time since taking over tracking. + * + * The counter and elapsed time may be zero if the time has been maintained + * meanwhile by calling system_timer_current_time_us(). + * + * Event timestamps are are shifted towards the present. + * "after" and "every" events that would have fired during deep sleep + * will fire once as if firing late, then "every" events will + * resume the same relative timings. + * + * @param counter the current timer counter. + * @param micros time elapsed since system_timer_deepsleep_begin + * + * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. + */ + int system_timer_deepsleep_end( CODAL_TIMESTAMP counter, CODAL_TIMESTAMP micros); + + /** + * Determine the time of the next wake-up event. + * @param timestamp reference to a variable to receive the time. + * @return true if there is an event. + */ + bool system_timer_deepsleep_wakeup_time( CODAL_TIMESTAMP ×tamp); + extern Timer* system_timer; } diff --git a/inc/drivers/Button.h b/inc/drivers/Button.h index b4206c89..7ef0a61c 100644 --- a/inc/drivers/Button.h +++ b/inc/drivers/Button.h @@ -105,6 +105,26 @@ namespace codal */ void periodicCallback(); + /** + * Sets whether the button should trigger power manager wake-up. + * + * @param wake The action of the button - either 1 to trigger wake-up or 0 for no wake-up + */ + void wakeOnActive(int wake) + { + _pin.wakeOnActive( wake); + } + + /** + * Deternine if the button should trigger power manager wake-up. + * + * @param wake The action of the button - either 1 to trigger wake up or 0 for no + */ + int isWakeOnActive() + { + return _pin.isWakeOnActive(); + } + /** * Destructor for Button, where we deregister this instance from the array of fiber components. */ @@ -117,6 +137,11 @@ namespace codal */ virtual int buttonActive(); + /** + * Puts the component in (or out of) sleep (low power) mode. + */ + virtual int setSleep(bool doSleep) override; + }; } diff --git a/inc/drivers/MessageBus.h b/inc/drivers/MessageBus.h index 7eb0769b..0f316331 100644 --- a/inc/drivers/MessageBus.h +++ b/inc/drivers/MessageBus.h @@ -142,8 +142,6 @@ namespace codal */ virtual int remove(Listener *newListener); - - private: Listener *listeners; // Chain of active listeners. diff --git a/source/core/CodalComponent.cpp b/source/core/CodalComponent.cpp index 8529f740..8e4d8c47 100644 --- a/source/core/CodalComponent.cpp +++ b/source/core/CodalComponent.cpp @@ -123,20 +123,64 @@ void CodalComponent::removeComponent() * Puts all components in (or out of) sleep (low power) mode. */ void CodalComponent::setAllSleep(bool doSleep) +{ + deepSleepAll( doSleep ? deepSleepCallbackBegin : deepSleepCallbackEnd, NULL); +} + +/** + * Perform functions related to deep sleep. + */ +int CodalComponent::deepSleepCallback( deepSleepCallbackReason reason, deepSleepCallbackData *data) +{ + switch ( reason) + { + case deepSleepCallbackBegin: + case deepSleepCallbackBeginWithWakeUps: + setSleep(true); + break; + + case deepSleepCallbackEnd: + case deepSleepCallbackEndWithWakeUps: + setSleep(false); + break; + + default: + break; + } + + return DEVICE_OK; +} + +/** + * Perform functions related to deep sleep. + */ +void CodalComponent::deepSleepAll( deepSleepCallbackReason reason, deepSleepCallbackData *data) { // usually, dependencies of component X are added before X itself, // so iterate backwards (so from high-level components to low-level) // when putting stuff to sleep, and forwards when waking up - if (doSleep) - { - for (int i = DEVICE_COMPONENT_COUNT - 1; i >= 0; i--) - if (components[i]) - components[i]->setSleep(true); - } - else + + switch ( reason) { - for (unsigned i = 0; i < DEVICE_COMPONENT_COUNT; i++) - if (components[i]) - components[i]->setSleep(false); + case deepSleepCallbackPrepare: + case deepSleepCallbackBegin: + case deepSleepCallbackBeginWithWakeUps: + case deepSleepCallbackCountWakeUps: + for (unsigned i = 0; i < DEVICE_COMPONENT_COUNT; i++) + { + if (components[i]) + components[i]->deepSleepCallback( reason, data); + } + break; + + case deepSleepCallbackEnd: + case deepSleepCallbackEndWithWakeUps: + case deepSleepCallbackClearWakeUps: + for (int i = DEVICE_COMPONENT_COUNT - 1; i >= 0; i--) + { + if (components[i]) + components[i]->deepSleepCallback( reason, data); + } + break; } } diff --git a/source/core/CodalFiber.cpp b/source/core/CodalFiber.cpp index 616baca5..1f6352b2 100644 --- a/source/core/CodalFiber.cpp +++ b/source/core/CodalFiber.cpp @@ -851,6 +851,16 @@ int codal::scheduler_runqueue_empty() return (runQueue == NULL); } +/** + * Determines if any fibers are waiting for events. + * + * @return 1 if there are no fibers currently waiting for events; otherwise 0 + */ +int codal::scheduler_waitqueue_empty() +{ + return (waitQueue == NULL); +} + /** * Calls the Fiber scheduler. * The calling Fiber will likely be blocked, and control given to another waiting fiber. @@ -982,7 +992,7 @@ void codal::idle() // because we enforce MESSAGE_BUS_LISTENER_IMMEDIATE for listeners placed // on the scheduler. fiber_flags &= ~DEVICE_SCHEDULER_IDLE; - target_wait_for_event(); + target_scheduler_idle(); } } @@ -1000,6 +1010,29 @@ void codal::idle_task() } } +/** + * Determines if deep sleep is pending. + * + * @return 1 if deep sleep is pending, 0 otherwise. + */ +int codal::fiber_scheduler_get_deepsleep_pending() +{ + return fiber_flags & DEVICE_SCHEDULER_DEEPSLEEP ? 1 : 0; +} + +/** + * Flag if deep sleep is pending. + * + * @param penfing 1 if deep sleep is pending, 0 otherwise. + */ +void codal::fiber_scheduler_set_deepsleep_pending( int pending) +{ + if ( pending) + fiber_flags |= DEVICE_SCHEDULER_DEEPSLEEP; + else + fiber_flags &= ~DEVICE_SCHEDULER_DEEPSLEEP; +} + /** * Create a new lock that can be used for mutual exclusion and condition synchronisation. */ diff --git a/source/core/codal_default_target_hal.cpp b/source/core/codal_default_target_hal.cpp index 4e84efbc..d55adc1f 100644 --- a/source/core/codal_default_target_hal.cpp +++ b/source/core/codal_default_target_hal.cpp @@ -33,6 +33,12 @@ __attribute__((weak)) void target_panic(int statusCode) } } +__attribute__((weak)) void target_scheduler_idle() +{ + // if not implemented, default to WFI + target_wait_for_event(); +} + __attribute__((weak)) void target_deepsleep() { // if not implemented, default to WFI diff --git a/source/driver-models/Timer.cpp b/source/driver-models/Timer.cpp index 3429b24c..c5366d1f 100644 --- a/source/driver-models/Timer.cpp +++ b/source/driver-models/Timer.cpp @@ -40,6 +40,8 @@ DEALINGS IN THE SOFTWARE. #include "ErrorNo.h" #include "codal_target_hal.h" #include "CodalDmesg.h" +#include "CodalFiber.h" +#include "NotifyEvents.h" using namespace codal; @@ -116,6 +118,7 @@ Timer::Timer(LowLevelTimer& t, uint8_t ccPeriodChannel, uint8_t ccEventChannel) delta = 0; sigma = timer.captureCounter(); + timer.enableIRQ(); system_timer_calibrate_cycles(); } @@ -156,13 +159,13 @@ int Timer::enableInterrupts() return DEVICE_OK; } -int Timer::setEvent(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, bool repeat) +int Timer::setEvent(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, bool repeat, uint32_t flags) { TimerEvent *evt = getTimerEvent(); if (evt == NULL) return DEVICE_NO_RESOURCES; - evt->set(getTimeUs() + period, repeat ? period: 0, id, value); + evt->set(getTimeUs() + period, repeat ? period: 0, id, value, flags); target_disable_irq(); @@ -220,10 +223,12 @@ int Timer::cancel(uint16_t id, uint16_t value) * @param id the ID to be used in event generation. * * @param value the value to place into the Events' value field. + * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. */ -int Timer::eventAfter(CODAL_TIMESTAMP period, uint16_t id, uint16_t value) +int Timer::eventAfter(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags) { - return eventAfterUs(period*1000, id, value); + return eventAfterUs(period*1000, id, value, flags); } /** @@ -235,10 +240,12 @@ int Timer::eventAfter(CODAL_TIMESTAMP period, uint16_t id, uint16_t value) * @param id the ID to be used in event generation. * * @param value the value to place into the Events' value field. + * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. */ -int Timer::eventAfterUs(CODAL_TIMESTAMP period, uint16_t id, uint16_t value) +int Timer::eventAfterUs(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags) { - return setEvent(period, id, value, false); + return setEvent(period, id, value, false, flags); } /** @@ -250,10 +257,12 @@ int Timer::eventAfterUs(CODAL_TIMESTAMP period, uint16_t id, uint16_t value) * @param id the ID to be used in event generation. * * @param value the value to place into the Events' value field. + * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. */ -int Timer::eventEvery(CODAL_TIMESTAMP period, uint16_t id, uint16_t value) +int Timer::eventEvery(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags) { - return eventEveryUs(period*1000, id, value); + return eventEveryUs(period*1000, id, value, flags); } /** @@ -265,10 +274,12 @@ int Timer::eventEvery(CODAL_TIMESTAMP period, uint16_t id, uint16_t value) * @param id the ID to be used in event generation. * * @param value the value to place into the Events' value field. + * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. */ -int Timer::eventEveryUs(CODAL_TIMESTAMP period, uint16_t id, uint16_t value) +int Timer::eventEveryUs(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags) { - return setEvent(period, id, value, true); + return setEvent(period, id, value, true, flags); } /** @@ -284,8 +295,13 @@ void Timer::sync() uint32_t val = timer.captureCounter(); uint32_t elapsed = 0; +#if CONFIG_ENABLED(CODAL_TIMER_32BIT) + // assume at least 32 bit counter; note that this also works when the timer overflows + elapsed = (uint32_t)(val - sigma); +#else // assume at least 16 bit counter; note that this also works when the timer overflows elapsed = (uint16_t)(val - sigma); +#endif sigma = val; // advance main timer @@ -343,28 +359,39 @@ void Timer::trigger(bool isFallback) for (int i = 0; iid != 0 && currentTimeUs >= e->timestamp) + if (e->id != 0) { - uint16_t id = e->id; - uint16_t value = e->value; - - // Release before triggering event. Otherwise, an immediate event handler - // can cancel this event, another event might be put in its place - // and we end up releasing (or repeating) a completely different event. - if (e->period == 0) - releaseTimerEvent(e); - else - e->timestamp += e->period; - - // We need to trigger this event. -#if CONFIG_ENABLED(LIGHTWEIGHT_EVENTS) - Event evt(id, value, currentTime); -#else - Event evt(id, value, currentTimeUs); -#endif - - // TODO: Handle rollover case above... - eventsFired++; + if (currentTimeUs >= e->timestamp) + { + uint16_t id = e->id; + uint16_t value = e->value; + + // Release before triggering event. Otherwise, an immediate event handler + // can cancel this event, another event might be put in its place + // and we end up releasing (or repeating) a completely different event. + if (e->period == 0) + releaseTimerEvent(e); + else + e->timestamp += e->period; + + // We need to trigger this event. + #if CONFIG_ENABLED(LIGHTWEIGHT_EVENTS) + Event evt(id, value, currentTime); + #else + Event evt(id, value, currentTimeUs); + #endif + + // TODO: Handle rollover case above... + eventsFired++; + } + else if ( e->flags & CODAL_TIMER_EVENT_FLAGS_WAKEUP && fiber_scheduler_get_deepsleep_pending() && e->timestamp < currentTimeUs + 100000) + { + #if CONFIG_ENABLED(LIGHTWEIGHT_EVENTS) + Event evt(DEVICE_ID_NOTIFY, POWER_EVT_CANCEL_DEEPSLEEP, currentTime); + #else + Event evt(DEVICE_ID_NOTIFY, POWER_EVT_CANCEL_DEEPSLEEP, currentTimeUs); + #endif + } } e++; } @@ -375,6 +402,170 @@ void Timer::trigger(bool isFallback) recomputeNextTimerEvent(); } +/** + * Find the next event with the WAKEUP flag + */ +TimerEvent *Timer::deepSleepWakeUpEvent() +{ + TimerEvent *wakeUpEvent = NULL; + + TimerEvent *eNext = timerEventList + eventListSize; + for ( TimerEvent *e = timerEventList; e < eNext; e++) + { + if ( e->id != 0 && e->flags & CODAL_TIMER_EVENT_FLAGS_WAKEUP) + { + if ( wakeUpEvent == NULL || (e->timestamp < wakeUpEvent->timestamp)) + wakeUpEvent = e; + } + } + + return wakeUpEvent; +} + +/** + * Determine the current time and the corresponding timer counter, + * to enable the caller to take over tracking time. + * + * @param counter reference to a variable to receive the current timer counter + * + * @return the current time since power on in microseconds + */ +CODAL_TIMESTAMP Timer::deepSleepBegin( CODAL_TIMESTAMP &counter) +{ + // Need to disable all IRQs - for example if SPI IRQ is triggered during + // sync(), it might call into getTimeUs(), which would call sync() + target_disable_irq(); + + uint32_t val = timer.captureCounter(); + uint32_t elapsed = 0; + +#if CONFIG_ENABLED(CODAL_TIMER_32BIT) + // assume at least 32 bit counter; note that this also works when the timer overflows + elapsed = (uint32_t)(val - sigma); +#else + // assume at least 16 bit counter; note that this also works when the timer overflows + elapsed = (uint16_t)(val - sigma); +#endif + sigma = val; + + // advance main timer + currentTimeUs += elapsed; + + // the 64 bit division is ~150 cycles + // this is called at least every few ms, and quite possibly much more often + delta += elapsed; + while (delta >= 1000) { + currentTime++; + delta -= 1000; + } + + timer.disableIRQ(); + target_enable_irq(); + + counter = val; + return currentTimeUs; +} + +/** + * After taking over time tracking with system_timer_deepsleep_begin, + * hand back control by supplying a new timer counter value with + * corresponding elapsed time since taking over tracking. + * + * The counter and elapsed time may be zero if the time has been maintained + * meanwhile by calling system_timer_current_time_us(). + * + * Event timestamps are are shifted towards the present. + * "after" and "every" events that would have fired during deep sleep + * will fire once as if firing late, then "every" events will + * resume the same relative timings. + * + * @param counter the current timer counter. + * @param micros time elapsed since system_timer_deepsleep_begin + * + * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. + */ +void Timer::deepSleepEnd( CODAL_TIMESTAMP counter, CODAL_TIMESTAMP micros) +{ + // On entry, the timer IRQ is disabled and must not be enabled + target_disable_irq(); + + if ( micros > 0) + { + currentTimeUs += micros; + + CODAL_TIMESTAMP millis = micros / 1000; + micros -= millis * 1000; + + currentTime += millis; + + delta += micros; + while (delta >= 1000) { + currentTime++; + delta -= 1000; + } + +#if CONFIG_ENABLED(CODAL_TIMER_32BIT) + sigma = (uint32_t) counter; +#else + sigma = (uint16_t) counter; +#endif + } + + sync(); + + // Bring any past events to the present and find the next event + // All events that would have fired during deep sleep will fire once, but obviously late. + // For some periodic events that will mean some events are dropped, + // but subsequent events will be on the same schedule as before deep sleep. + CODAL_TIMESTAMP present = currentTimeUs + CODAL_TIMER_MINIMUM_PERIOD; + nextTimerEvent = NULL; + TimerEvent *eNext = timerEventList + eventListSize; + for ( TimerEvent *e = timerEventList; e < eNext; e++) + { + if ( e->id != 0) + { + if ( e->period == 0) + { + if ( e->timestamp < present) + e->timestamp = present; + } + else + { + while ( e->timestamp + e->period < present) + e->timestamp += e->period; + } + + if ( nextTimerEvent == NULL || nextTimerEvent->timestamp > e->timestamp) + nextTimerEvent = e; + } + } + + uint32_t counterNow = timer.captureCounter(); + + timer.setCompare(ccPeriodChannel, counterNow + 10000000); + + if (nextTimerEvent) + timer.setCompare( ccEventChannel, counterNow + CODAL_TIMER_MINIMUM_PERIOD); + + target_enable_irq(); +} + +/** + * Determine the time of the next wake-up event. + * @param timestamp reference to a variable to receive the time. + * @return true if there is an event. + */ +bool Timer::deepSleepWakeUpTime( CODAL_TIMESTAMP ×tamp) +{ + TimerEvent *wakeUpEvent = deepSleepWakeUpEvent(); + + if ( wakeUpEvent == NULL) + return false; + + timestamp = wakeUpEvent->timestamp; + return true; +} + /** * Destructor for this Timer instance */ @@ -421,14 +612,16 @@ CODAL_TIMESTAMP codal::system_timer_current_time_us() * * @param the value to fire against the current system_timer id. * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. + * * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. */ -int codal::system_timer_event_every_us(CODAL_TIMESTAMP period, uint16_t id, uint16_t value) +int codal::system_timer_event_every_us(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags) { if(system_timer == NULL) return DEVICE_NOT_SUPPORTED; - return system_timer->eventEveryUs(period, id, value); + return system_timer->eventEveryUs(period, id, value, flags); } /** @@ -438,14 +631,16 @@ int codal::system_timer_event_every_us(CODAL_TIMESTAMP period, uint16_t id, uint * * @param the value to fire against the current system_timer id. * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. + * * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. */ -int codal::system_timer_event_after_us(CODAL_TIMESTAMP period, uint16_t id, uint16_t value) +int codal::system_timer_event_after_us(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags) { if(system_timer == NULL) return DEVICE_NOT_SUPPORTED; - return system_timer->eventAfterUs(period, id, value); + return system_timer->eventAfterUs(period, id, value, flags); } /** @@ -455,14 +650,16 @@ int codal::system_timer_event_after_us(CODAL_TIMESTAMP period, uint16_t id, uint * * @param the value to fire against the current system_timer id. * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. + * * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. */ -int codal::system_timer_event_every(CODAL_TIMESTAMP period, uint16_t id, uint16_t value) +int codal::system_timer_event_every(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags) { if(system_timer == NULL) return DEVICE_NOT_SUPPORTED; - return system_timer->eventEvery(period, id, value); + return system_timer->eventEvery(period, id, value, flags); } /** @@ -472,14 +669,16 @@ int codal::system_timer_event_every(CODAL_TIMESTAMP period, uint16_t id, uint16_ * * @param the value to fire against the current system_timer id. * + * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. + * * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. */ -int codal::system_timer_event_after(CODAL_TIMESTAMP period, uint16_t id, uint16_t value) +int codal::system_timer_event_after(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags) { if(system_timer == NULL) return DEVICE_NOT_SUPPORTED; - return system_timer->eventAfter(period, id, value); + return system_timer->eventAfter(period, id, value, flags); } /** @@ -577,3 +776,49 @@ int codal::system_timer_wait_ms(uint32_t period) { return system_timer_wait_us(period * 1000); } + +/** + * Called from power manager before deep sleep. + * @param counter reference to a variable to receive the current timer counter + * + * @return the current time since power on in microseconds + */ +CODAL_TIMESTAMP codal::system_timer_deepsleep_begin( CODAL_TIMESTAMP &counter) +{ + if(system_timer == NULL) + { + counter = 0; + return 0; + } + return system_timer->deepSleepBegin( counter); +} + +/** + * Called from power manager after deep sleep. + * @param counter pointer to a variable to receive the current timer counter + * @param micros time elapsed since system_timer_deepsleep_begin + * + * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. + */ +int codal::system_timer_deepsleep_end( CODAL_TIMESTAMP counter, CODAL_TIMESTAMP micros) +{ + if(system_timer == NULL) + return DEVICE_NOT_SUPPORTED; + + system_timer->deepSleepEnd( counter, micros); + return DEVICE_OK; +} + +/** + * Determine the time of the next wake up event. + * @param timestamp reference to a variable to receive the time. + * @return true if there is an event. + */ +bool codal::system_timer_deepsleep_wakeup_time( CODAL_TIMESTAMP ×tamp) +{ + if(system_timer == NULL) + return false; + + return system_timer->deepSleepWakeUpTime(timestamp); +} + diff --git a/source/drivers/Button.cpp b/source/drivers/Button.cpp index da76d034..fb573918 100644 --- a/source/drivers/Button.cpp +++ b/source/drivers/Button.cpp @@ -26,6 +26,7 @@ DEALINGS IN THE SOFTWARE. #include "Button.h" #include "Timer.h" #include "EventModel.h" +#include "CodalDmesg.h" using namespace codal; @@ -52,6 +53,7 @@ Button::Button(Pin &pin, uint16_t id, ButtonEventConfiguration eventConfiguratio this->sigma = 0; this->polarity = polarity; + pin.setPolarity( polarity == ACTIVE_HIGH ? 1 : 0); pin.setPull(mode); this->status |= DEVICE_COMPONENT_STATUS_SYSTEM_TICK; @@ -176,3 +178,33 @@ int Button::isPressed() Button::~Button() { } + +/** + * Puts the component in (or out of) sleep (low power) mode. + */ +int Button::setSleep(bool doSleep) +{ + if (doSleep) + { + status &= ~DEVICE_BUTTON_STATE; + status &= ~DEVICE_BUTTON_STATE_HOLD_TRIGGERED; + clickCount = 0; + sigma = 0; + } + else + { + if ( isWakeOnActive()) + { + if ( buttonActive()) + { + sigma = DEVICE_BUTTON_SIGMA_THRESH_LO + 1; + status |= DEVICE_BUTTON_STATE; + Event evt(id,DEVICE_BUTTON_EVT_DOWN); + clickCount = 1; + downStartTime = system_timer_current_time(); + } + } + } + + return DEVICE_OK; +} diff --git a/source/drivers/LSM303Accelerometer.cpp b/source/drivers/LSM303Accelerometer.cpp index e89dedfd..872e93fc 100644 --- a/source/drivers/LSM303Accelerometer.cpp +++ b/source/drivers/LSM303Accelerometer.cpp @@ -149,7 +149,7 @@ int LSM303Accelerometer::requestUpdate() { bool awaitSample = false; - if ((status & (LSM303_A_STATUS_ENABLED | LSM303_A_STATUS_SLEEPING)) == 0x00) + if ((status & LSM303_A_STATUS_ENABLED) == 0x00) { // If we get here without being enabled, applicaiton code has requested // functionlity from this component. Perform on demand activation. @@ -230,6 +230,7 @@ int LSM303Accelerometer::setSleep(bool doSleep) { if (doSleep && (status & LSM303_A_STATUS_ENABLED)) { + status &= ~DEVICE_COMPONENT_STATUS_IDLE_TICK; status |= LSM303_A_STATUS_SLEEPING; status &= ~LSM303_A_STATUS_ENABLED; configure(); @@ -237,9 +238,8 @@ int LSM303Accelerometer::setSleep(bool doSleep) if (!doSleep && (status & LSM303_A_STATUS_SLEEPING)) { - status |= LSM303_A_STATUS_ENABLED; + status |= DEVICE_COMPONENT_STATUS_IDLE_TICK; status &= ~LSM303_A_STATUS_SLEEPING; - configure(); } return DEVICE_OK; diff --git a/source/drivers/LSM303Magnetometer.cpp b/source/drivers/LSM303Magnetometer.cpp index 46938e72..82137dae 100644 --- a/source/drivers/LSM303Magnetometer.cpp +++ b/source/drivers/LSM303Magnetometer.cpp @@ -128,7 +128,7 @@ int LSM303Magnetometer::requestUpdate() { bool awaitSample = false; - if ((status & (LSM303_M_STATUS_ENABLED | LSM303_M_STATUS_SLEEPING)) == 0x00) + if ((status & LSM303_M_STATUS_ENABLED) == 0x00) { // If we get here without being enabled, applicaiton code has requested // functionlity from this component. Perform on demand activation. @@ -205,6 +205,7 @@ int LSM303Magnetometer::setSleep(bool doSleep) { if (doSleep && (status & LSM303_M_STATUS_ENABLED)) { + status &= ~DEVICE_COMPONENT_STATUS_IDLE_TICK; status |= LSM303_M_STATUS_SLEEPING; status &= ~LSM303_M_STATUS_ENABLED; configure(); @@ -212,9 +213,8 @@ int LSM303Magnetometer::setSleep(bool doSleep) if (!doSleep && (status & LSM303_M_STATUS_SLEEPING)) { - status |= LSM303_M_STATUS_ENABLED; + status |= DEVICE_COMPONENT_STATUS_IDLE_TICK; status &= ~LSM303_M_STATUS_SLEEPING; - configure(); } return DEVICE_OK; From e2efc4cb034b04e5db5d7f6669667194b4a49024 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Wed, 30 Jun 2021 21:13:38 +0100 Subject: [PATCH 34/43] Optimize and fix logical error in inline ManagedString != method --- source/types/ManagedString.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/types/ManagedString.cpp b/source/types/ManagedString.cpp index 11457d6e..33aa114f 100644 --- a/source/types/ManagedString.cpp +++ b/source/types/ManagedString.cpp @@ -375,7 +375,7 @@ bool ManagedString::operator== (const ManagedString& s) */ bool ManagedString::operator!= (const ManagedString& s) { - return ((length() != s.length()) && (strcmp(toCharArray(),s.toCharArray())!=0)); + return !(*this == s); } /** From 27e16d43b6f97ee2ef993faab23827480ce35545 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Mon, 20 Sep 2021 13:37:37 -0700 Subject: [PATCH 35/43] Remove sample endpointRequest() impl (crashes when no out endpoint) --- inc/drivers/HID.h | 1 - source/drivers/HID.cpp | 16 ---------------- 2 files changed, 17 deletions(-) diff --git a/inc/drivers/HID.h b/inc/drivers/HID.h index b7a747be..8f71f240 100644 --- a/inc/drivers/HID.h +++ b/inc/drivers/HID.h @@ -55,7 +55,6 @@ namespace codal virtual int classRequest(UsbEndpointIn &ctrl, USBSetup& setup); virtual int stdRequest(UsbEndpointIn &ctrl, USBSetup& setup); - virtual int endpointRequest(); virtual const InterfaceInfo *getInterfaceInfo(); }; } diff --git a/source/drivers/HID.cpp b/source/drivers/HID.cpp index c2cd265b..bd7c2724 100644 --- a/source/drivers/HID.cpp +++ b/source/drivers/HID.cpp @@ -95,22 +95,6 @@ int USBHID::stdRequest(UsbEndpointIn &ctrl, USBSetup &setup) return DEVICE_NOT_SUPPORTED; } -int USBHID::endpointRequest() -{ - uint8_t buf[64]; - int len = out->read(buf, sizeof(buf)); - if (len <= 0) - return len; - - for (int i = 1; i < 4; ++i) - { - buf[i] ^= 'a' - 'A'; - } - - // this should echo-back serial - return in->write(buf, sizeof(buf)); -} - const InterfaceInfo *USBHID::getInterfaceInfo() { return &ifaceInfo; From 0e7b078f72ff8aaaac93613bdec390e6b5f5f8e8 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Mon, 20 Sep 2021 13:37:50 -0700 Subject: [PATCH 36/43] Don't crash when not initialized --- source/drivers/HIDJoystick.cpp | 3 +++ source/drivers/HIDKeyboard.cpp | 3 +++ source/drivers/HIDMouse.cpp | 3 +++ 3 files changed, 9 insertions(+) diff --git a/source/drivers/HIDJoystick.cpp b/source/drivers/HIDJoystick.cpp index c393e738..3954c2e5 100644 --- a/source/drivers/HIDJoystick.cpp +++ b/source/drivers/HIDJoystick.cpp @@ -189,6 +189,9 @@ int USBHIDJoystick::setThrottle(uint8_t num, uint8_t val) int USBHIDJoystick::sendReport() { + if (!in) + return DEVICE_INVALID_STATE; + uint8_t report[sizeof(HIDJoystickState)]; memcpy(report, &joystickState, sizeof(HIDJoystickState)); diff --git a/source/drivers/HIDKeyboard.cpp b/source/drivers/HIDKeyboard.cpp index cfbf75c9..0178cffb 100644 --- a/source/drivers/HIDKeyboard.cpp +++ b/source/drivers/HIDKeyboard.cpp @@ -203,6 +203,9 @@ int USBHIDKeyboard::updateReport(HIDKeyboardReport* report) if(report == NULL) return DEVICE_INVALID_PARAMETER; + if (!in) + return DEVICE_INVALID_STATE; + uint8_t reportBuf[report->reportSize + 1] = {report->reportID}; memcpy(reportBuf + 1, report->keyState, report->reportSize); diff --git a/source/drivers/HIDMouse.cpp b/source/drivers/HIDMouse.cpp index 264c7a84..bf454d4e 100644 --- a/source/drivers/HIDMouse.cpp +++ b/source/drivers/HIDMouse.cpp @@ -153,6 +153,9 @@ int USBHIDMouse::moveWheel(int8_t w) int USBHIDMouse::sendReport() { + if (!in) + return DEVICE_INVALID_STATE; + uint8_t report[sizeof(HIDMouseState)]; memcpy(report, &mouseState, sizeof(HIDMouseState)); From b209655f5ebb7915df52938c81a13102b5377a7e Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Mon, 27 Sep 2021 15:55:08 -0700 Subject: [PATCH 37/43] Add REAL_TIME_FUNC attribute --- inc/core/codal_target_hal.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/inc/core/codal_target_hal.h b/inc/core/codal_target_hal.h index 96804879..92364662 100644 --- a/inc/core/codal_target_hal.h +++ b/inc/core/codal_target_hal.h @@ -94,5 +94,14 @@ extern "C" } +// This is re-defined in targets with external flash, that require certain functions to be placed in RAM +#ifndef REAL_TIME_FUNC +#define REAL_TIME_FUNC /* */ +#endif + +// This is for cycle-precise wait even in presence of flash caches (forces function to sit in RAM) +#ifndef FORCE_RAM_FUNC +#define FORCE_RAM_FUNC __attribute__((noinline, long_call, section(".data"))) +#endif #endif From 4ed39fbe28ee97aa922012e0a43e608253e98058 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Mon, 27 Sep 2021 15:56:20 -0700 Subject: [PATCH 38/43] Put REAL_TIME_FUNC on functions that disable IRQs or are used from ISRs --- source/core/CodalFiber.cpp | 5 +++++ source/core/CodalHeapAllocator.cpp | 3 +++ source/core/codal_default_target_hal.cpp | 1 + source/driver-models/Timer.cpp | 10 ++++++++++ source/drivers/MessageBus.cpp | 2 ++ 5 files changed, 21 insertions(+) diff --git a/source/core/CodalFiber.cpp b/source/core/CodalFiber.cpp index 1f6352b2..8fe73f38 100644 --- a/source/core/CodalFiber.cpp +++ b/source/core/CodalFiber.cpp @@ -82,6 +82,7 @@ using namespace codal; * * @param queue The run queue to add the fiber to. */ +REAL_TIME_FUNC void codal::queue_fiber(Fiber *f, Fiber **queue) { target_disable_irq(); @@ -119,6 +120,7 @@ void codal::queue_fiber(Fiber *f, Fiber **queue) * * @param f the fiber to remove. */ +REAL_TIME_FUNC void codal::dequeue_fiber(Fiber *f) { // If this fiber is already dequeued, nothing the there's nothing to do. @@ -156,6 +158,7 @@ Fiber * codal::get_fiber_list() /** * Allocates a fiber from the fiber pool if available. Otherwise, allocates a new one from the heap. */ +REAL_TIME_FUNC Fiber *getFiberContext() { Fiber *f; @@ -737,6 +740,7 @@ void codal::release_fiber(void *) * * Any fiber reaching the end of its entry function will return here for recycling. */ +REAL_TIME_FUNC void codal::release_fiber(void) { if (!fiber_scheduler_running()) @@ -1045,6 +1049,7 @@ FiberLock::FiberLock() /** * Block the calling fiber until the lock is available **/ +REAL_TIME_FUNC void FiberLock::wait() { // If the scheduler is not running, then simply exit, as we're running monothreaded. diff --git a/source/core/CodalHeapAllocator.cpp b/source/core/CodalHeapAllocator.cpp index 09568217..f5870b0d 100644 --- a/source/core/CodalHeapAllocator.cpp +++ b/source/core/CodalHeapAllocator.cpp @@ -191,6 +191,7 @@ uint32_t device_heap_size(uint8_t heap_index) * * @return A pointer to the allocated memory, or NULL if insufficient memory is available. */ +REAL_TIME_FUNC void *device_malloc_in(size_t size, HeapDefinition &heap) { PROCESSOR_WORD_TYPE blockSize = 0; @@ -281,6 +282,7 @@ void *device_malloc_in(size_t size, HeapDefinition &heap) * * @return A pointer to the allocated memory, or NULL if insufficient memory is available. */ +REAL_TIME_FUNC void* device_malloc (size_t size) { static uint8_t initialised = 0; @@ -340,6 +342,7 @@ void* device_malloc (size_t size) * * @param mem The memory area to release. */ +REAL_TIME_FUNC void device_free (void *mem) { PROCESSOR_WORD_TYPE *memory = (PROCESSOR_WORD_TYPE *)mem; diff --git a/source/core/codal_default_target_hal.cpp b/source/core/codal_default_target_hal.cpp index d55adc1f..8c7a7994 100644 --- a/source/core/codal_default_target_hal.cpp +++ b/source/core/codal_default_target_hal.cpp @@ -8,6 +8,7 @@ __attribute__((weak)) void target_wait(uint32_t milliseconds) codal::system_timer_wait_ms(milliseconds); } +REAL_TIME_FUNC __attribute__((weak)) void target_wait_us(uint32_t us) { codal::system_timer_wait_us(us); diff --git a/source/driver-models/Timer.cpp b/source/driver-models/Timer.cpp index c5366d1f..cee92aef 100644 --- a/source/driver-models/Timer.cpp +++ b/source/driver-models/Timer.cpp @@ -62,6 +62,7 @@ void timer_callback(uint16_t chan) } } +REAL_TIME_FUNC void Timer::triggerIn(CODAL_TIMESTAMP t) { if (t < CODAL_TIMER_MINIMUM_PERIOD) t = CODAL_TIMER_MINIMUM_PERIOD; @@ -141,6 +142,7 @@ CODAL_TIMESTAMP Timer::getTime() * * @return the timestamp in microseconds */ +REAL_TIME_FUNC CODAL_TIMESTAMP Timer::getTimeUs() { sync(); @@ -159,6 +161,7 @@ int Timer::enableInterrupts() return DEVICE_OK; } +REAL_TIME_FUNC int Timer::setEvent(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, bool repeat, uint32_t flags) { TimerEvent *evt = getTimerEvent(); @@ -188,6 +191,7 @@ int Timer::setEvent(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, bool re * * @param value the value that was given upon a previous call to eventEvery / eventAfter */ +REAL_TIME_FUNC int Timer::cancel(uint16_t id, uint16_t value) { int res = DEVICE_INVALID_PARAMETER; @@ -286,6 +290,7 @@ int Timer::eventEveryUs(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uin * Callback from physical timer implementation code. * @param t Indication that t time units (typically microsends) have elapsed. */ +REAL_TIME_FUNC void Timer::sync() { // Need to disable all IRQs - for example if SPI IRQ is triggered during @@ -430,6 +435,7 @@ TimerEvent *Timer::deepSleepWakeUpEvent() * * @return the current time since power on in microseconds */ +REAL_TIME_FUNC CODAL_TIMESTAMP Timer::deepSleepBegin( CODAL_TIMESTAMP &counter) { // Need to disable all IRQs - for example if SPI IRQ is triggered during @@ -484,6 +490,7 @@ CODAL_TIMESTAMP Timer::deepSleepBegin( CODAL_TIMESTAMP &counter) * * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. */ +REAL_TIME_FUNC void Timer::deepSleepEnd( CODAL_TIMESTAMP counter, CODAL_TIMESTAMP micros) { // On entry, the timer IRQ is disabled and must not be enabled @@ -725,6 +732,7 @@ int codal::system_timer_calibrate_cycles() * @param cycles the number of nops to execute * @return DEVICE_OK */ +FORCE_RAM_FUNC void codal::system_timer_wait_cycles(uint32_t cycles) { __asm__ __volatile__( @@ -748,6 +756,7 @@ void codal::system_timer_wait_cycles(uint32_t cycles) * @note this provides a good starting point for non-timing critical applications. For more accurate timings, * please use a cycle-based wait approach (see system_timer_wait_cycles) */ +REAL_TIME_FUNC int codal::system_timer_wait_us(uint32_t period) { if(system_timer == NULL) @@ -772,6 +781,7 @@ int codal::system_timer_wait_us(uint32_t period) * @note this provides a good starting point for non-timing critical applications. For more accurate timings, * please use a cycle-based wait approach (see system_timer_wait_cycles) */ +REAL_TIME_FUNC int codal::system_timer_wait_ms(uint32_t period) { return system_timer_wait_us(period * 1000); diff --git a/source/drivers/MessageBus.cpp b/source/drivers/MessageBus.cpp index 438f0030..6eb53ea3 100644 --- a/source/drivers/MessageBus.cpp +++ b/source/drivers/MessageBus.cpp @@ -153,6 +153,7 @@ void async_callback(void *param) * * @param The event to queue. */ +REAL_TIME_FUNC void MessageBus::queueEvent(Event &evt) { int processingComplete; @@ -205,6 +206,7 @@ void MessageBus::queueEvent(Event &evt) * * @return a pointer to the EventQueueItem that is at the head of the list. */ +REAL_TIME_FUNC EventQueueItem* MessageBus::dequeueEvent() { EventQueueItem *item = NULL; From 4ec6950b3825513326a678bb3020d67a5cc95465 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Mon, 27 Sep 2021 15:56:30 -0700 Subject: [PATCH 39/43] Use new FORCE_RAM_FUNC attribute --- inc/driver-models/Timer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/driver-models/Timer.h b/inc/driver-models/Timer.h index 3d9d488b..d55b964e 100644 --- a/inc/driver-models/Timer.h +++ b/inc/driver-models/Timer.h @@ -342,7 +342,7 @@ namespace codal * * @note the amount of cycles per iteration will vary between CPUs. */ - __attribute__((noinline, long_call, section(".data"))) + FORCE_RAM_FUNC void system_timer_wait_cycles(uint32_t cycles); /** From 251ff683b225f5e6a9decce1db4777b1e893183a Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Mon, 27 Sep 2021 15:57:11 -0700 Subject: [PATCH 40/43] Shorten IRQ-disabled period in DMESG --- source/core/CodalDmesg.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/source/core/CodalDmesg.cpp b/source/core/CodalDmesg.cpp index 89d90fe5..44b7d7e8 100644 --- a/source/core/CodalDmesg.cpp +++ b/source/core/CodalDmesg.cpp @@ -35,24 +35,30 @@ using namespace codal; static void logwrite(const char *msg); +REAL_TIME_FUNC static void logwriten(const char *msg, int l) { + target_disable_irq(); + if (codalLogStore.ptr + l >= sizeof(codalLogStore.buffer)) { - const int jump = sizeof(codalLogStore.buffer) / 4; - codalLogStore.ptr -= jump; - memmove(codalLogStore.buffer, codalLogStore.buffer + jump, codalLogStore.ptr); - // zero-out the rest so it looks OK in the debugger - memset(codalLogStore.buffer + codalLogStore.ptr, 0, sizeof(codalLogStore.buffer) - codalLogStore.ptr); - } - if (l + codalLogStore.ptr >= sizeof(codalLogStore.buffer)) - { - logwrite("DMESG line too long!\n"); - return; + *(uint32_t *)codalLogStore.buffer = 0x0a2e2e2e; // "...\n" + codalLogStore.ptr = 4; + if (l >= sizeof(codalLogStore.buffer) - 5) + { + msg = "DMESG line too long!\n"; + l = 21; + } } - memcpy(codalLogStore.buffer + codalLogStore.ptr, msg, l); + + char *dst = &codalLogStore.buffer[codalLogStore.ptr]; + int tmp = l; + while (tmp--) + *dst++ = *msg++; + *dst = 0; codalLogStore.ptr += l; - codalLogStore.buffer[codalLogStore.ptr] = 0; + + target_enable_irq(); } static void logwrite(const char *msg) @@ -133,7 +139,6 @@ void codal_vdmesg(const char *format, bool crlf, va_list ap) { const char *end = format; - target_disable_irq(); while (*end) { if (*end++ == '%') @@ -173,8 +178,6 @@ void codal_vdmesg(const char *format, bool crlf, va_list ap) if (crlf) logwrite("\r\n"); - - target_enable_irq(); } #endif From ab99d299e25f40509c3dc79b510b4d57f0b595d3 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 28 Sep 2021 10:21:14 -0700 Subject: [PATCH 41/43] Fix warnings --- source/core/CodalDmesg.cpp | 2 +- source/drivers/HIDKeyboard.cpp | 4 ++-- source/drivers/LEDMatrix.cpp | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/source/core/CodalDmesg.cpp b/source/core/CodalDmesg.cpp index 44b7d7e8..81f2b2e3 100644 --- a/source/core/CodalDmesg.cpp +++ b/source/core/CodalDmesg.cpp @@ -44,7 +44,7 @@ static void logwriten(const char *msg, int l) { *(uint32_t *)codalLogStore.buffer = 0x0a2e2e2e; // "...\n" codalLogStore.ptr = 4; - if (l >= sizeof(codalLogStore.buffer) - 5) + if (l >= (int)sizeof(codalLogStore.buffer) - 5) { msg = "DMESG line too long!\n"; l = 21; diff --git a/source/drivers/HIDKeyboard.cpp b/source/drivers/HIDKeyboard.cpp index 0178cffb..ef76249f 100644 --- a/source/drivers/HIDKeyboard.cpp +++ b/source/drivers/HIDKeyboard.cpp @@ -203,8 +203,8 @@ int USBHIDKeyboard::updateReport(HIDKeyboardReport* report) if(report == NULL) return DEVICE_INVALID_PARAMETER; - if (!in) - return DEVICE_INVALID_STATE; + if (!in) + return DEVICE_INVALID_STATE; uint8_t reportBuf[report->reportSize + 1] = {report->reportID}; memcpy(reportBuf + 1, report->keyState, report->reportSize); diff --git a/source/drivers/LEDMatrix.cpp b/source/drivers/LEDMatrix.cpp index c49f953c..c58260f8 100644 --- a/source/drivers/LEDMatrix.cpp +++ b/source/drivers/LEDMatrix.cpp @@ -151,8 +151,11 @@ void LEDMatrix::render() matrixMap.rowPins[strobeRow]->setDigitalValue(1); //timer does not have enough resolution for brightness of 1. 23.53 us + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wtype-limits" if(brightness <= LED_MATRIX_MAXIMUM_BRIGHTNESS && brightness > LED_MATRIX_MINIMUM_BRIGHTNESS) system_timer_event_after_us(frameTimeout, id, LED_MATRIX_EVT_FRAME_TIMEOUT); + #pragma GCC diagnostic pop //this will take around 23us to execute if(brightness <= LED_MATRIX_MINIMUM_BRIGHTNESS) From c91fff71b71589351be665145679860224c75d16 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 5 Oct 2021 11:57:11 -0700 Subject: [PATCH 42/43] Mark more functions real-time (ones used from ISRs) --- source/core/CodalFiber.cpp | 1 + source/driver-models/Timer.cpp | 4 ++++ source/drivers/MessageBus.cpp | 3 +++ source/types/Event.cpp | 3 +++ 4 files changed, 11 insertions(+) diff --git a/source/core/CodalFiber.cpp b/source/core/CodalFiber.cpp index 8fe73f38..7e0fe033 100644 --- a/source/core/CodalFiber.cpp +++ b/source/core/CodalFiber.cpp @@ -255,6 +255,7 @@ void codal::scheduler_init(EventModel &_messageBus) * * @return 1 if the fber scheduler is running, 0 otherwise. */ +REAL_TIME_FUNC int codal::fiber_scheduler_running() { if (fiber_flags & DEVICE_SCHEDULER_RUNNING) diff --git a/source/driver-models/Timer.cpp b/source/driver-models/Timer.cpp index cee92aef..8537bc5f 100644 --- a/source/driver-models/Timer.cpp +++ b/source/driver-models/Timer.cpp @@ -72,6 +72,7 @@ void Timer::triggerIn(CODAL_TIMESTAMP t) target_enable_irq(); } +REAL_TIME_FUNC TimerEvent *Timer::getTimerEvent() { // Find the first unused slot, and assign it. @@ -247,6 +248,7 @@ int Timer::eventAfter(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint3 * * @param flags CODAL_TIMER_EVENT_FLAGS_WAKEUP for event to trigger deep sleep wake-up. */ +REAL_TIME_FUNC int Timer::eventAfterUs(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags) { return setEvent(period, id, value, false, flags); @@ -323,6 +325,7 @@ void Timer::sync() target_enable_irq(); } +REAL_TIME_FUNC void Timer::recomputeNextTimerEvent() { nextTimerEvent = NULL; @@ -642,6 +645,7 @@ int codal::system_timer_event_every_us(CODAL_TIMESTAMP period, uint16_t id, uint * * @return DEVICE_OK or DEVICE_NOT_SUPPORTED if no timer has been registered. */ +REAL_TIME_FUNC int codal::system_timer_event_after_us(CODAL_TIMESTAMP period, uint16_t id, uint16_t value, uint32_t flags) { if(system_timer == NULL) diff --git a/source/drivers/MessageBus.cpp b/source/drivers/MessageBus.cpp index 6eb53ea3..ef86550d 100644 --- a/source/drivers/MessageBus.cpp +++ b/source/drivers/MessageBus.cpp @@ -83,6 +83,7 @@ MessageBus::MessageBus() * Internal wrapper function, used to enable * parameterised callbacks through the fiber scheduler. */ +REAL_TIME_FUNC void async_callback(void *param) { Listener *listener = (Listener *)param; @@ -323,6 +324,7 @@ void MessageBus::idle(Event) * evt1.fire() * @endcode */ +REAL_TIME_FUNC int MessageBus::send(Event evt) { // We simply queue processing of the event until we're scheduled in normal thread context. @@ -346,6 +348,7 @@ int MessageBus::send(Event evt) * @note It is recommended that all external code uses the send() function instead of this function, * or the constructors provided by Event. */ +REAL_TIME_FUNC int MessageBus::process(Event &evt, bool urgent) { Listener *l; diff --git a/source/types/Event.cpp b/source/types/Event.cpp index 19e0d853..59873cbd 100644 --- a/source/types/Event.cpp +++ b/source/types/Event.cpp @@ -55,6 +55,7 @@ EventModel* EventModel::defaultEventBus = NULL; * Event evt(id,DEVICE_BUTTON_EVT_CLICK,CREATE_AND_FIRE); * @endcode */ +REAL_TIME_FUNC Event::Event(uint16_t source, uint16_t value, EventLaunchMode mode) { this->source = source; @@ -91,6 +92,7 @@ Event::Event(uint16_t source, uint16_t value, EventLaunchMode mode) * Event evt(id,DEVICE_BUTTON_EVT_CLICK,CREATE_AND_FIRE); * @endcode */ + REAL_TIME_FUNC Event::Event(uint16_t source, uint16_t value, CODAL_TIMESTAMP currentTimeUs, EventLaunchMode mode) { this->source = source; @@ -120,6 +122,7 @@ Event::Event() /** * Fires this Event onto the Default EventModel, or a custom one! */ +REAL_TIME_FUNC void Event::fire() { if(EventModel::defaultEventBus) From 42445dfe65a61ebf4af55f9a13e709e298896a10 Mon Sep 17 00:00:00 2001 From: Dr John Vidler Date: Thu, 13 Jan 2022 16:28:05 +0000 Subject: [PATCH 43/43] FORCE_RAM_FUNC now correctly uses .data.ramfuncs rather than plain .data, to work with newer gcc versions. See issue #33 at https://github.com/lancaster-university/microbit-v2-samples/issues/33 --- inc/core/codal_target_hal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/core/codal_target_hal.h b/inc/core/codal_target_hal.h index 92364662..115aa307 100644 --- a/inc/core/codal_target_hal.h +++ b/inc/core/codal_target_hal.h @@ -101,7 +101,7 @@ extern "C" // This is for cycle-precise wait even in presence of flash caches (forces function to sit in RAM) #ifndef FORCE_RAM_FUNC -#define FORCE_RAM_FUNC __attribute__((noinline, long_call, section(".data"))) +#define FORCE_RAM_FUNC __attribute__((noinline, long_call, section(".data.ramfuncs"))) #endif #endif