From 3413d5487dfd6f769bf831426b8888dc477e2aa8 Mon Sep 17 00:00:00 2001 From: Sam Kent <32453267+microbit-sam@users.noreply.github.com> Date: Mon, 16 Apr 2018 17:27:14 +0100 Subject: [PATCH] Added Partial Flashing Service (#9) * Memory Map + Partial Flash Service * Moved Partial Flash Service to Management Mode * PF Service appears in YT Build not PXT * PF in PXT * Swapped DFU and PF Services * Fixing Flash Writew * write flash via bluetooth * Partial Flash - Fixed Address * Update module.json * Adjust flash pointer * Adjust flash start * Adjust Flash Pointer * Added offset to xfer * Writing to FLASH_END * Writing to FLASH_END * Modified PFS read callback * Reenable BLE Security * removed pf * Check diff * Check diff * Replaced ContextSwitch * PF * Test. Writing Hex File to 0x30000 * Writing to PXT empty space * Reversed byte order * Flash 16 bytes at once * Copy data from static var to local var * blocks -> bytes * Fixed offset * Fixed Offset * Flash to 0x30000 + offset * Bypass scratch page * Changed byte order * Write Without Response * Without Fast BLE * Added fast BLE * Updated Connection Parameters * Iterate through BLE data bytes * Get Hashes From PXT Build * Copy Hash from Flash * Testing FLASH_END * Read Hash From Flash * Read Hash from Flash * Reading correct hash from mem * Endianess * Modified MAGIC * Fix Start Address * Sequence # * Write 0xdeadbeef to 0x36000 on PF Seq # error * Group 4 packet blocks for writing * Added error case * Increased flash write size * Blocks * Fire event to write * Decreased flash time * Reformat * Reduced hash size in MemoryMap * Partial Flashing w/ Notifications * One Characteristic - WRITE/WRITE_WOUT_RESP/NOTIFY * Modified WRITE Notification * Tidied - Moved end of transfer logic * Removed VI Swap files * Added Pairing Mode Event ID * Modified Retransmit behaviour * updateFlash caused stack overflow * Removed flash write from memory map * Instantiate Memory Map where used * Word aligned. Added Status and Reset commands * Fixed: storage. becomes storage-> * Fixed warnings: switch case fall through * Fix Hash * Rebuild Map everytime * Hash Error * Edited animation. Hash testing * persistent memory map issues * Moved EOT * Modified BLE animation. Removed globals from PFS * Updated MM * Added incomplete flash flag * Re added CCS.s --- inc/bluetooth/MicroBitBLEManager.h | 32 +- .../MicroBitPartialFlashingService.h | 106 ++++++ inc/core/MicroBitComponent.h | 13 +- inc/core/MicroBitConfig.h | 18 +- inc/drivers/MicroBitFlash.h | 2 +- inc/drivers/MicroBitMemoryMap.h | 123 +++++++ module.json | 2 +- source/CMakeLists.txt | 3 + source/bluetooth/MicroBitBLEManager.cpp | 86 ++++- .../MicroBitPartialFlashingService.cpp | 322 ++++++++++++++++++ source/drivers/MicroBitFlash.cpp | 17 +- source/drivers/MicroBitMemoryMap.cpp | 179 ++++++++++ 12 files changed, 877 insertions(+), 26 deletions(-) create mode 100644 inc/bluetooth/MicroBitPartialFlashingService.h create mode 100644 inc/drivers/MicroBitMemoryMap.h create mode 100644 source/bluetooth/MicroBitPartialFlashingService.cpp create mode 100644 source/drivers/MicroBitMemoryMap.cpp diff --git a/inc/bluetooth/MicroBitBLEManager.h b/inc/bluetooth/MicroBitBLEManager.h index 9787988c..c90a9d2b 100644 --- a/inc/bluetooth/MicroBitBLEManager.h +++ b/inc/bluetooth/MicroBitBLEManager.h @@ -57,6 +57,7 @@ DEALINGS IN THE SOFTWARE. #include "MicroBitButtonService.h" #include "MicroBitIOPinService.h" #include "MicroBitTemperatureService.h" +#include "MicroBitPartialFlashingService.h" #include "ExternalEvents.h" #include "MicroBitButton.h" #include "MicroBitStorage.h" @@ -78,6 +79,9 @@ DEALINGS IN THE SOFTWARE. #define MICROBIT_BLE_STATUS_STORE_SYSATTR 0x02 #define MICROBIT_BLE_STATUS_DISCONNECT 0x04 +#define MICROBIT_BLE_MODE_PAIRING 0x00 +#define MICROBIT_BLE_MODE_APPLICATION 0x01 + extern const int8_t MICROBIT_BLE_POWER_LEVEL[]; struct BLESysAttribute @@ -146,7 +150,7 @@ class MicroBitBLEManager : MicroBitComponent * @param enableBonding If true, the security manager enabled bonding. * * @code - * bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true); + * bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus); * @endcode */ void init(ManagedString deviceName, ManagedString serialNumber, EventModel &messageBus, bool enableBonding); @@ -220,7 +224,7 @@ class MicroBitBLEManager : MicroBitComponent void stopAdvertising(); /** - * A member function used to defer writes to flash, in order to prevent a write collision with + * A member function used to defer writes to flash, in order to prevent a write collision with * softdevice. * @param handle The handle offered by soft device during pairing. * */ @@ -281,6 +285,19 @@ class MicroBitBLEManager : MicroBitComponent int advertiseEddystoneUid(const char* uid_namespace, const char* uid_instance, int8_t calibratedPower = MICROBIT_BLE_EDDYSTONE_DEFAULT_POWER, bool connectable = true, uint16_t interval = MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL); #endif + /** + * Restarts in BLE Mode + * + */ + void restartInBLEMode(); + + /** + * Get current BLE mode; application, pairing + * #define MICROBIT_BLE_MODE_PAIRING 0x00 + * #define MICROBIT_BLE_MODE_APPLICATION 0x01 + */ + uint8_t getBLEMode(); + private: /** * Displays the device's ID code as a histogram on the provided MicroBitDisplay instance. @@ -289,12 +306,21 @@ class MicroBitBLEManager : MicroBitComponent */ void showNameHistogram(MicroBitDisplay &display); + + /** + * Displays pairing mode animation + * + * @param display The display instance used for displaying the histogram. + */ + void showManagementModeAnimation(MicroBitDisplay &display); + #define MICROBIT_BLE_DISCONNECT_AFTER_PAIRING_DELAY 500 - unsigned long pairing_completed_at_time; + unsigned long pairing_completed_at_time; int pairingStatus; ManagedString passKey; ManagedString deviceName; + uint8_t bleMode = MICROBIT_BLE_MODE_APPLICATION; }; #endif diff --git a/inc/bluetooth/MicroBitPartialFlashingService.h b/inc/bluetooth/MicroBitPartialFlashingService.h new file mode 100644 index 00000000..b714e562 --- /dev/null +++ b/inc/bluetooth/MicroBitPartialFlashingService.h @@ -0,0 +1,106 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 British Broadcasting Corporation. +This software is provided by Lancaster University by arrangement with the BBC. + +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 MICROBIT_PARTIAL_FLASH_SERVICE_H +#define MICROBIT_PARTIAL_FLASH_SERVICE_H + +#include "MicroBitConfig.h" +#include "MicroBitBLEManager.h" +#include "ble/BLE.h" +#include "MicroBitMemoryMap.h" + +#include "MicroBitFlash.h" +#include "MicroBitStorage.h" + +#include "MicroBitComponent.h" +#include "MicroBitEvent.h" +#include "MicroBitListener.h" +#include "EventModel.h" + +#define PARTIAL_FLASHING_VERSION 0x01 + +// UUIDs for our service and characteristics +extern const uint8_t MicroBitPartialFlashingServiceUUID[]; +extern const uint8_t MicroBitPartialFlashingServiceCharacteristicUUID[]; + +/** + * Class definition for the custom MicroBit Partial Flash Service. + * Provides a BLE service to remotely read the memory map and flash the PXT program. + */ +class MicroBitPartialFlashingService +{ + public: + /** + * Constructor. + * Create a representation of the Partial Flash Service + * @param _ble The instance of a BLE device that we're running on. + * @param _memoryMap An instance of MicroBiteMemoryMap to interface with. + */ + MicroBitPartialFlashingService(BLEDevice &_ble, EventModel &_messageBus); + + /** + * Callback. Invoked when any of our attributes are written via BLE. + */ + void onDataWritten(const GattWriteCallbackParams *params); + + /** + * Callback. Invoked when any of our attributes are read via BLE. + */ + void onDataRead(GattReadAuthCallbackParams *params); + + private: + // M:B Bluetooth stack and MessageBus + BLEDevice &ble; + EventModel &messageBus; + + /** + * Writing to flash inside MicroBitEvent rather than in the ISR + */ + void partialFlashingEvent(MicroBitEvent e); + + // The base address to write to. Bit masked: (0xFFFF0000 & region.endAddress) >> 16 + uint8_t baseAddress = 0x3; + + // Handles to access each characteristic when they are held by Soft Device. + GattAttribute::Handle_t partialFlashCharacteristicHandle; + + /** + * Process a Partial Flashing data packet + */ + void flashData(uint8_t *data); + + // Ensure packets are in order + uint8_t packetCount = 0; + uint8_t blockPacketCount = 0; + + // Keep track of blocks of data + uint32_t block[16]; + uint8_t blockNum = 0; + uint16_t offset = 0; + +}; + + +#endif diff --git a/inc/core/MicroBitComponent.h b/inc/core/MicroBitComponent.h index a161aee2..8af4c1c6 100644 --- a/inc/core/MicroBitComponent.h +++ b/inc/core/MicroBitComponent.h @@ -68,6 +68,9 @@ DEALINGS IN THE SOFTWARE. #define MICROBIT_ID_MULTIBUTTON_ATTACH 31 #define MICROBIT_ID_SERIAL 32 +#define MICROBIT_ID_PFLASH_NOTIFICATION 33 +#define MICROBIT_ID_PAIRING_MODE 34 + #define MICROBIT_ID_MESSAGE_BUS_LISTENER 1021 // Message bus indication that a handler for a given ID has been registered. #define MICROBIT_ID_NOTIFY_ONE 1022 // Notfication channel, for general purpose synchronisation #define MICROBIT_ID_NOTIFY 1023 // Notfication channel, for general purpose synchronisation @@ -81,17 +84,17 @@ DEALINGS IN THE SOFTWARE. * * All components should inherit from this class. * - * If a component requires regular updates, then that component can be added to the + * If a component requires regular updates, then that component can be added to the * to the systemTick and/or idleTick queues. This provides a simple, extensible mechanism * for code that requires periodic/occasional background processing but does not warrant - * the complexity of maintaining its own thread. + * the complexity of maintaining its own thread. * - * Two levels of support are available. + * Two levels of support are available. * * systemTick() provides a periodic callback during the * micro:bit's system timer interrupt. This provides a guaranteed periodic callback, but in interrupt context * and is suitable for code with lightweight processing requirements, but strict time constraints. - * + * * idleTick() provides a periodic callback whenever the scheduler is idle. This provides occasional, callbacks * in the main thread context, but with no guarantees of frequency. This is suitable for non-urgent background tasks. * @@ -128,7 +131,7 @@ class MicroBitComponent /** * The idle thread will call this member function once the component has been added to the array - * of idle components using fiber_add_idle_component. + * of idle components using fiber_add_idle_component. */ virtual void idleTick() { diff --git a/inc/core/MicroBitConfig.h b/inc/core/MicroBitConfig.h index 783ccd0c..a395d5a9 100644 --- a/inc/core/MicroBitConfig.h +++ b/inc/core/MicroBitConfig.h @@ -79,15 +79,21 @@ DEALINGS IN THE SOFTWARE. // Defines where in memory persistent data is stored. #ifndef KEY_VALUE_STORE_PAGE -#define KEY_VALUE_STORE_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 17)) +#define KEY_VALUE_STORE_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 17)) #endif -#ifndef BLE_BOND_DATA_PAGE +#ifndef BLE_BOND_DATA_PAGE #define BLE_BOND_DATA_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 18)) #endif +#ifndef MEMORY_MAP_PAGE +#define MEMORY_MAP_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 19)) +#endif + +// Scratch moved from page 19 to page 20 +// MicroBitFileSystem uses DEFAULT_SCRATCH_PAGE to mark end of FileSystem #ifndef DEFAULT_SCRATCH_PAGE -#define DEFAULT_SCRATCH_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 19)) +#define DEFAULT_SCRATCH_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 20)) #endif // Address of the end of the current program in FLASH memory. @@ -130,7 +136,7 @@ extern uint32_t __etext; // For standard S110 builds, this should be word aligned and in the range 0x300 - 0x700. // Any unused memory will be automatically reclaimed as HEAP memory if both MICROBIT_HEAP_REUSE_SD and MICROBIT_HEAP_ALLOCATOR are enabled. #ifndef MICROBIT_SD_GATT_TABLE_SIZE -#define MICROBIT_SD_GATT_TABLE_SIZE 0x300 +#define MICROBIT_SD_GATT_TABLE_SIZE 0x400 #endif // @@ -372,7 +378,7 @@ extern uint32_t __etext; // Defines the logical block size for the file system. // Must be a factor of the physical PAGE_SIZE (ideally a power of two less). // -#ifndef MBFS_BLOCK_SIZE +#ifndef MBFS_BLOCK_SIZE #define MBFS_BLOCK_SIZE 256 #endif @@ -382,7 +388,7 @@ extern uint32_t __etext; // Should be <= MBFS_BLOCK_SIZE. // #ifndef MBFS_CACHE_SIZE -#define MBFS_CACHE_SIZE 0 +#define MBFS_CACHE_SIZE 0 #endif // diff --git a/inc/drivers/MicroBitFlash.h b/inc/drivers/MicroBitFlash.h index edf9f20d..187a3f1d 100644 --- a/inc/drivers/MicroBitFlash.h +++ b/inc/drivers/MicroBitFlash.h @@ -73,7 +73,7 @@ class MicroBitFlash * Erase an entire page. * @param page_address address of first word of page. */ - void erase_page(uint32_t* page_address); + uint8_t erase_page(uint32_t* page_address); /** * Write to flash memory, assuming that a write is valid diff --git a/inc/drivers/MicroBitMemoryMap.h b/inc/drivers/MicroBitMemoryMap.h new file mode 100644 index 00000000..7bc3b2c2 --- /dev/null +++ b/inc/drivers/MicroBitMemoryMap.h @@ -0,0 +1,123 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 British Broadcasting Corporation. +This software is provided by Lancaster University by arrangement with the BBC. + +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 MICROBIT_MEMORY_MAP_H +#define MICROBIT_MEMORY_MAP_H + +#include "mbed.h" +#include "MicroBitConfig.h" +#include "ManagedString.h" +#include "ErrorNo.h" + +#define MICROBIT_MEMORY_MAP_MAGIC 0xCA8E + +#define NUMBER_OF_REGIONS 3 + +/** + * Class definition for the MicroBitMemoryMap class. + * This allows reading and writing of regions within the memory map. + * + * This class maps the different regions used on the flash memory to allow + * a region to updated independently of the others AKA Partial Flashing. + */ +class MicroBitMemoryMap +{ + struct Region + { + uint8_t regionId; + uint32_t startAddress; + uint32_t endAddress; + uint8_t hash[8]; + + Region(uint8_t regionId, uint32_t startAddress, uint32_t endAddress, uint8_t hash[8]) + { + this->regionId = regionId; + this->startAddress = startAddress; + this->endAddress = endAddress; + memmove( this->hash, &hash, 8 ); + } + + Region(){ + this->regionId = 0x0; + this->startAddress = 0x0; + this->endAddress = 0x0; + memset( this->hash, 0xDD, 8 ); + } + + }; + + struct MemoryMapStore + { + Region memoryMap[3]; + }; + + uint8_t regionCount = 0; + + /** + * Function to update the flash with the current MemoryMapStore + * + * @param memoryMapStore The memory map to write to flash + */ + void updateFlash(MemoryMapStore *store); + + public: + + MemoryMapStore memoryMapStore; + + /** + * Default constructor. + * + * Creates an instance of MicroBitMemoryMap + */ + MicroBitMemoryMap(); + + /** + * Function for adding a Region to the end of the MemoryMap + * + * @param region The Region to add to the MemoryMap + * + * @return MICROBIT_OK on success + * + */ + int pushRegion(Region region); + + /** + * Function for updating a Region of the MemoryMap + * + * @param region The Region to update in the MemoryMap. The name is used as the selector. + * + * @return MICROBIT_OK success, MICROBIT_NO_DATA if the region is not found + */ + int updateRegion(Region region); + + /** + * Function to fetch hashes from PXT build + * + * @return int Boolean result of the search. 1 = Hashes Found; 0 = No Hash Found + */ + void findHashes(); +}; + +#endif diff --git a/module.json b/module.json index adf834cc..2ac2befc 100644 --- a/module.json +++ b/module.json @@ -1,6 +1,6 @@ { "name": "microbit-dal", - "version": "2.0.0-rc9", + "version": "2.0.0-pf", "license": "MIT", "description": "The runtime library for the BBC micro:bit, developed by Lancaster University", "keywords": [ diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index da5e93ed..22d8b850 100755 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -45,6 +45,7 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES "drivers/MicroBitFlash.cpp" "drivers/MicroBitFile.cpp" "drivers/MicroBitFileSystem.cpp" + "drivers/MicroBitMemoryMap.cpp" "bluetooth/MicroBitAccelerometerService.cpp" "bluetooth/MicroBitBLEManager.cpp" @@ -57,6 +58,8 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES "bluetooth/MicroBitMagnetometerService.cpp" "bluetooth/MicroBitTemperatureService.cpp" "bluetooth/MicroBitUARTService.cpp" + "bluetooth/MicroBitPartialFlashingService.cpp" + ) execute_process(WORKING_DIRECTORY "../../yotta_modules/${PROJECT_NAME}" COMMAND "git" "log" "--pretty=format:%h" "-n" "1" OUTPUT_VARIABLE git_hash) diff --git a/source/bluetooth/MicroBitBLEManager.cpp b/source/bluetooth/MicroBitBLEManager.cpp index ace0796a..c1d31fac 100644 --- a/source/bluetooth/MicroBitBLEManager.cpp +++ b/source/bluetooth/MicroBitBLEManager.cpp @@ -1,6 +1,4 @@ /* -The MIT License (MIT) - Copyright (c) 2016 British Broadcasting Corporation. This software is provided by Lancaster University by arrangement with the BBC. @@ -271,7 +269,7 @@ void MicroBitBLEManager::advertise() } /** - * A member function used to defer writes to flash, in order to prevent a write collision with + * A member function used to defer writes to flash, in order to prevent a write collision with * softdevice. * @param handle The handle offered by soft device during pairing. * */ @@ -380,11 +378,14 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb // Configure the radio at our default power level setTransmitPower(MICROBIT_BLE_DEFAULT_TX_POWER); + // Bring up core BLE services. #if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE) new MicroBitDFUService(*ble); + new MicroBitPartialFlashingService(*ble, messageBus); #endif + #if CONFIG_ENABLED(MICROBIT_BLE_DEVICE_INFORMATION_SERVICE) DeviceInformationService ble_device_information_service(*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, serialNumber.toCharArray(), MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION); #else @@ -641,12 +642,14 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display, MicroBitButton &a ManagedString namePostfix("]"); ManagedString BLEName = namePrefix + deviceName + namePostfix; - ManagedString msg("PAIRING MODE!"); + ManagedString msg("M M!"); int timeInPairingMode = 0; int brightness = 255; int fadeDirection = 0; + bleMode = MICROBIT_BLE_MODE_PAIRING; + ble->gap().stopAdvertising(); // Clear the whitelist (if we have one), so that we're discoverable by all BLE devices. @@ -673,13 +676,17 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display, MicroBitButton &a // Stop any running animations on the display display.stopAnimation(); - display.scroll(msg); - // Display our name, visualised as a histogram in the display to aid identification. - showNameHistogram(display); + // Replaced by animation TODO remove + //display.scroll(msg); fiber_add_idle_component(this); + showManagementModeAnimation(display); + + // Display our name, visualised as a histogram in the display to aid identification. + showNameHistogram(display); + while (1) { if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST) @@ -765,6 +772,50 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display, MicroBitButton &a } } +/** + * Displays the management mode animation on the provided MicroBitDisplay instance. + * + * @param display The Display instance used for displaying the animation. + */ +void MicroBitBLEManager::showManagementModeAnimation(MicroBitDisplay &display) +{ + // Animation for display object + // https://makecode.microbit.org/93264-81126-90471-58367 + + const int mgmt_animation_w = 20; + const int mgmt_animation_h = 5; + const uint8_t mgmt_animation[] = + { + 1,1,1,1,1, 1,1,1,1,1, 1,1,0,1,1, 1,0,0,0,1, + 1,1,1,1,1, 1,1,0,1,1, 1,0,0,0,1, 0,0,0,0,0, + 1,1,0,1,1, 1,0,0,0,1, 0,0,0,0,0, 0,0,0,0,0, + 1,1,1,1,1, 1,1,0,1,1, 1,0,0,0,1, 0,0,0,0,0, + 1,1,1,1,1, 1,1,1,1,1, 1,1,0,1,1, 1,0,0,0,1 + }; + + MicroBitImage mgmt(mgmt_animation_w,mgmt_animation_h,mgmt_animation); + display.animate(mgmt,100,5); + + const uint8_t bt_icon_raw[] = + { + 255,255,255,0 ,255, + 255,0 ,255,255,0 , + 255,255,255,0 ,0 , + 255,0 ,255,255,0 , + 255,255,255,0 ,255 + }; + + MicroBitImage bt_icon(5,5,bt_icon_raw); + display.print(bt_icon,0,0,0,0); + + for(int i=0; i < 255; i = i + 5){ + display.setBrightness(i); + fiber_sleep(5); + } + fiber_sleep(1000); + +} + /** * Displays the device's ID code as a histogram on the provided MicroBitDisplay instance. * @@ -790,3 +841,24 @@ void MicroBitBLEManager::showNameHistogram(MicroBitDisplay &display) display.image.setPixelValue(MICROBIT_DFU_HISTOGRAM_WIDTH - i - 1, MICROBIT_DFU_HISTOGRAM_HEIGHT - j - 1, 255); } } + +/** + * Restarts in BLE Mode + * + */ + void MicroBitBLEManager::restartInBLEMode(){ + KeyValuePair* BLEMode = storage->get("BLEMode"); + if(BLEMode == NULL){ + uint8_t BLEMode = 0x01; + storage->put("BLEMode", &BLEMode, sizeof(BLEMode)); + delete &BLEMode; + } + microbit_reset(); + } + + /** + * Get BLE mode. Returns the current mode: application, pairing mode + */ +uint8_t MicroBitBLEManager::getBLEMode(){ + return bleMode; +} diff --git a/source/bluetooth/MicroBitPartialFlashingService.cpp b/source/bluetooth/MicroBitPartialFlashingService.cpp new file mode 100644 index 00000000..a20d8451 --- /dev/null +++ b/source/bluetooth/MicroBitPartialFlashingService.cpp @@ -0,0 +1,322 @@ + +/* +The MIT License (MIT) + +Copyright (c) 2016 British Broadcasting Corporation. +This software is provided by Lancaster University by arrangement with the BBC. + +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. +*/ + +/** + * Class definition for the custom MicroBit Partial Flashing service. + * Provides a BLE service to remotely write the user program to the device. + */ +#include "MicroBitConfig.h" +#include "ble/UUID.h" +#include "MicroBitPartialFlashingService.h" + +// BLE PF Control Codes +#define REGION_INFO 0x00 +#define FLASH_DATA 0x01 +#define END_OF_TRANSMISSION 0x02 + +// BLE Utilities +#define MICROBIT_STATUS 0xEE +#define MICROBIT_RESET 0xFF + +/** + * Constructor. + * Create a representation of the PartialFlashService + * @param _ble The instance of a BLE device that we're running on. + * @param _messageBus The instance of a EventModel that we're running on. + */ +MicroBitPartialFlashingService::MicroBitPartialFlashingService(BLEDevice &_ble, EventModel &_messageBus) : + ble(_ble), messageBus(_messageBus) +{ + // Set up partial flashing characteristic + uint8_t initCharacteristicValue = 0x00; + GattCharacteristic partialFlashCharacteristic(MicroBitPartialFlashingServiceCharacteristicUUID, &initCharacteristicValue, sizeof(initCharacteristicValue), + 20, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + + // Set default security requirements + partialFlashCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL); + + // Create Partial Flashing Service + GattCharacteristic *characteristics[] = {&partialFlashCharacteristic}; + GattService service(MicroBitPartialFlashingServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic*) ); + ble.addService(service); + + // Get characteristic handle for future use + partialFlashCharacteristicHandle = partialFlashCharacteristic.getValueHandle(); + ble.gattServer().onDataWritten(this, &MicroBitPartialFlashingService::onDataWritten); + + // Set up listener for SD writing + messageBus.listen(MICROBIT_ID_PFLASH_NOTIFICATION, MICROBIT_EVT_ANY, this, &MicroBitPartialFlashingService::partialFlashingEvent); + + // Set up Memory map + // This will create the Memory Map and store it in flash + MicroBitMemoryMap memoryMap; + +} + + +/** + * Callback. Invoked when any of our attributes are written via BLE. + */ +void MicroBitPartialFlashingService::onDataWritten(const GattWriteCallbackParams *params) +{ + // Get data from BLE callback params + uint8_t *data = (uint8_t *)params->data; + + if(params->handle == partialFlashCharacteristicHandle && params->len > 0) + { + // Switch CONTROL byte + switch(data[0]){ + case REGION_INFO: + { + // Create instance of Memory Map to return info + MicroBitMemoryMap memoryMap; + + uint8_t buffer[18]; + // Response: + // Region and Region # + buffer[0] = 0x00; + buffer[1] = data[1]; + + // Start Address + buffer[2] = (memoryMap.memoryMapStore.memoryMap[data[1]].startAddress & 0xFF000000) >> 24; + buffer[3] = (memoryMap.memoryMapStore.memoryMap[data[1]].startAddress & 0x00FF0000) >> 16; + buffer[4] = (memoryMap.memoryMapStore.memoryMap[data[1]].startAddress & 0x0000FF00) >> 8; + buffer[5] = (memoryMap.memoryMapStore.memoryMap[data[1]].startAddress & 0x000000FF); + + // End Address + buffer[6] = (memoryMap.memoryMapStore.memoryMap[data[1]].endAddress & 0xFF000000) >> 24; + buffer[7] = (memoryMap.memoryMapStore.memoryMap[data[1]].endAddress & 0x00FF0000) >> 16; + buffer[8] = (memoryMap.memoryMapStore.memoryMap[data[1]].endAddress & 0x0000FF00) >> 8; + buffer[9] = (memoryMap.memoryMapStore.memoryMap[data[1]].endAddress & 0x000000FF); + + // Region Hash + for(int i = 0; i < 8; ++i) + buffer[10+i] = memoryMap.memoryMapStore.memoryMap[data[1]].hash[i]; + + // Send BLE Notification + ble.gattServer().notify(partialFlashCharacteristicHandle, (const uint8_t *)buffer, 18); + + // Set offset for writing + baseAddress = (memoryMap.memoryMapStore.memoryMap[data[1]].startAddress & 0xFFFF0000) >> 16; // Offsets are 16 bit + + // Reset packet count + packetCount = 0; + + break; + } + case FLASH_DATA: + { + // Process FLASH data packet + flashData(data); + break; + } + case END_OF_TRANSMISSION: + { + /* Start of embedded source isn't always on a page border so client must + * inform the micro:bit that it's the last packet. + * - Write final packet + * The END_OF_TRANSMISSION packet contains no data. Write any data left in the buffer. + */ + MicroBitEvent evt(MICROBIT_ID_PFLASH_NOTIFICATION, END_OF_TRANSMISSION ,CREATE_AND_FIRE); break; + } + case MICROBIT_STATUS: + { + /* + * Return the version of the Partial Flashing Service and the current BLE mode (application / pairing) + */ + uint8_t flashNotificationBuffer[] = {MICROBIT_STATUS, PARTIAL_FLASHING_VERSION, MicroBitBLEManager::manager->getBLEMode()}; + ble.gattServer().notify(partialFlashCharacteristicHandle, (const uint8_t *)flashNotificationBuffer, sizeof(flashNotificationBuffer)); + break; + } + case MICROBIT_RESET: + { + /* + * data[1] determines which mode to reset into: MICROBIT_BLE_MODE_PAIRING or MICROBIT_BLE_MODE_APPLICATION + */ + switch(data[1]) { + case MICROBIT_BLE_MODE_PAIRING: + { + MicroBitEvent evt(MICROBIT_ID_PFLASH_NOTIFICATION, MICROBIT_RESET ,CREATE_AND_FIRE); + break; + } + case MICROBIT_BLE_MODE_APPLICATION: + { + microbit_reset(); + break; + } + } + break; + } + } + } +} + + + +/** + * @param data - A pointer to the data to process + * + */ +void MicroBitPartialFlashingService::flashData(uint8_t *data) +{ + // Receive 16 bytes per packet + // Buffer 8 packets - 32 uint32_t // 128 bytes per block + // When buffer is full trigger partialFlashingEvent + // When write is complete notify app and repeat + // +-----------+---------+---------+----------+ + // | 1 Byte | 2 Bytes | 1 Byte | 16 Bytes | + // +-----------+---------+---------+----------+ + // | COMMAND | OFFSET | PACKET# | DATA | + // +-----------+---------+---------+----------+ + uint8_t packetNum = data[3]; + /** + * Packets with packet num < packet count + * Ignore as part of retransmitted block + */ + if(packetNum < packetCount) + return; + + /** + * Check packet count + * If the packet count doesn't match send a notification to the client + * and set the packet count to the next block number + */ + if(packetNum != ++packetCount) + { + uint8_t flashNotificationBuffer[] = {FLASH_DATA, 0xAA}; + ble.gattServer().notify(partialFlashCharacteristicHandle, (const uint8_t *)flashNotificationBuffer, sizeof(flashNotificationBuffer)); + packetCount = blockPacketCount + 3; + blockNum = 0; + return; + } + + // Add to block + for(int x = 0; x < 4; x++) + block[(4*blockNum) + x] = data[(4*x) + 4] | data[(4*x) + 5] << 8 | data[(4*x) + 6] << 16 | data[(4*x) + 7] << 24; + + // Actions + switch(blockNum) { + // blockNum is 0, set up offset + case 0: + { + offset = ((data[1] << 8) | data[2]); + blockPacketCount = packetNum; + blockNum++; + break; + } + // blockNum is 7, block is full + case 3: + { + // Fire write event + MicroBitEvent evt(MICROBIT_ID_PFLASH_NOTIFICATION, FLASH_DATA ,CREATE_AND_FIRE); + // Reset blockNum + blockNum = 0; + break; + } + default: + { + blockNum++; + break; + } + } +} + + +/** + * Write Event + * Used the write data to the flash outside of the BLE ISR + */ +void MicroBitPartialFlashingService::partialFlashingEvent(MicroBitEvent e) +{ + MicroBitFlash flash; + + switch(e.value){ + case FLASH_DATA: + { + /* + * Set BLE Mode flag if not already set to boot into BLE mode + * upon a failed flash. + */ + MicroBitStorage storage; + KeyValuePair* flashIncomplete = storage.get("flashIncomplete"); + if(flashIncomplete == NULL){ + uint8_t flashIncomplete = 0x01; + storage.put("flashIncomplete", &flashIncomplete, sizeof(flashIncomplete)); + } + + + // Flash Pointer + uint32_t *flashPointer = (uint32_t *) ((baseAddress << 16) + offset); + + // If the pointer is on a page boundary erase the page + if(!((uint32_t)flashPointer % 0x400)) + flash.erase_page(flashPointer); + + // Create a pointer to the data block + uint32_t *blockPointer; + blockPointer = block; + + // Write to flash + flash.flash_burn(flashPointer, blockPointer, 16); + + // Update flash control buffer to send next packet + uint8_t flashNotificationBuffer[] = {FLASH_DATA, 0xFF}; + ble.gattServer().notify(partialFlashCharacteristicHandle, (const uint8_t *)flashNotificationBuffer, sizeof(flashNotificationBuffer)); + break; + } + case END_OF_TRANSMISSION: + { + // Write one more packet over the next block: if source embed magic was not previously erased, it will be now! + uint32_t *blockPointer; + uint32_t *flashPointer = (uint32_t *) ((baseAddress << 16) + offset + 0x40); + + blockPointer = block; + flash.flash_burn(flashPointer, blockPointer, 16); + + // Once the final packet has been written remove the BLE mode flag and reset + // the micro:bit + MicroBitStorage storage; + storage.remove("flashIncomplete"); + delete &storage; + microbit_reset(); + break; + } + case MICROBIT_RESET: + { + MicroBitBLEManager::manager->restartInBLEMode(); + break; + } + } + +} + +const uint8_t MicroBitPartialFlashingServiceUUID[] = { + 0xe9,0x7d,0xd9,0x1d,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitPartialFlashingServiceCharacteristicUUID[] = { + 0xe9,0x7d,0x3b,0x10,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; diff --git a/source/drivers/MicroBitFlash.cpp b/source/drivers/MicroBitFlash.cpp index 82ee87ae..d6b93088 100644 --- a/source/drivers/MicroBitFlash.cpp +++ b/source/drivers/MicroBitFlash.cpp @@ -55,9 +55,16 @@ extern "C" void btle_set_user_evt_handler(void (*func)(uint32_t)); static bool evt_handler_registered = false; static volatile bool flash_op_complete = false; +/* + * static void SWI1_IRQHandler(uint32_t evt) +{ + flash_op_complete = true; +} +*/ + static void nvmc_event_handler(uint32_t evt) { - if(evt == NRF_EVT_FLASH_OPERATION_SUCCESS) + if(evt == NRF_EVT_FLASH_OPERATION_SUCCESS) flash_op_complete = true; } @@ -101,15 +108,19 @@ int MicroBitFlash::need_erase(uint8_t* source, uint8_t* flash_addr, int len) * Erase an entire page * @param page_address address of first word of page */ -void MicroBitFlash::erase_page(uint32_t* pg_addr) +uint8_t MicroBitFlash::erase_page(uint32_t* pg_addr) { if (ble_running()) { flash_op_complete = false; while(1) { - if (sd_flash_page_erase(((uint32_t)pg_addr)/PAGE_SIZE) == NRF_SUCCESS) + uint8_t ret = sd_flash_page_erase(((uint32_t)pg_addr)/PAGE_SIZE); + if (ret == NRF_SUCCESS) { break; + } else { + return ret; + } wait_ms(10); } diff --git a/source/drivers/MicroBitMemoryMap.cpp b/source/drivers/MicroBitMemoryMap.cpp new file mode 100644 index 00000000..f70a0f19 --- /dev/null +++ b/source/drivers/MicroBitMemoryMap.cpp @@ -0,0 +1,179 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 British Broadcasting Corporation. +This software is provided by Lancaster University by arrangement with the BBC. + +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. +*/ + +/** + * Class definition for the MicroBitMemoryMap class. + * This allows reading and writing of regions within the memory map. + * + * This class maps the different regions used on the flash memory to allow + * a region to updated independently of the others AKA Partial Flashing. +*/ + +#include "MicroBitConfig.h" +#include "MicroBitMemoryMap.h" +#include "MicroBitFlash.h" +#include "MicroBitSerial.h" + +/** + * Default constructor. + * + * Creates an instance of MicroBitMemoryMap + */ +MicroBitMemoryMap::MicroBitMemoryMap() +{ + // Assumes PXT Built program + // SD + pushRegion(Region(0x00, 0x00, 0x18000, 0x00)); // Soft Device + + // DAL + pushRegion(Region(0x01, 0x18000, FLASH_PROGRAM_END, 0x00)); // micro:bit Device Abstractation Layer + + // PXT + // PXT will be on the start of the next page + // padding to next page = 0x400 - (FLASH_PROGRAM_END % 0x400); + pushRegion(Region(0x02, FLASH_PROGRAM_END + (0x400 - (FLASH_PROGRAM_END % 0x400)), 0x3BBFF, 0x00)); // micro:bit PXT + + // Find Hashes if PXT Built Program + findHashes(); + +} + +/** + * Function for adding a Region to the end of the MemoryMap + * + * @param region The Region to add to the MemoryMap + * + * @return MICROBIT_OK on success + * + */ +int MicroBitMemoryMap::pushRegion(Region region) +{ + + // Find next blank Region in map + if(regionCount == NUMBER_OF_REGIONS){ + return MICROBIT_NO_DATA; + } else { + // Add data + memoryMapStore.memoryMap[regionCount].startAddress = region.startAddress; + memoryMapStore.memoryMap[regionCount].endAddress = region.endAddress; + memcpy(&memoryMapStore.memoryMap[regionCount].hash, ®ion.hash, 8); + memoryMapStore.memoryMap[regionCount].regionId = region.regionId; + regionCount++; + return MICROBIT_OK; + } +} + +/** + * Function for updating a Region of the MemoryMap + * + * @param region The Region to update in the MemoryMap. The name is used as the selector. + * + * @return MICROBIT_OK success, MICROBIT_NO_DATA if the region is not found + */ +int MicroBitMemoryMap::updateRegion(Region region) +{ + // Find Region name in map + int i = 0; + while(memoryMapStore.memoryMap[i].regionId != region.regionId && i < NUMBER_OF_REGIONS) i++; + + if(i == NUMBER_OF_REGIONS){ + return MICROBIT_NO_DATA; + } else { + // Add data + memoryMapStore.memoryMap[i] = region; + updateFlash(&memoryMapStore); + return MICROBIT_OK; + } +} + +/** + * Function to update the flash with the current MemoryMapStore + * + * @param memoryMapStore The memory map to write to flash + */ +void MicroBitMemoryMap::updateFlash(MemoryMapStore *store) +{ + MicroBitFlash flash; + flash.flash_write((uint32_t *)MEMORY_MAP_PAGE, store, (sizeof(MemoryMapStore) / 4)); +} + +/* + * Function to fetch the hashes from a PXT generated build + */ +void MicroBitMemoryMap::findHashes() +{ + // Iterate through pages to find magic + for(int x = 0; x < NRF_FICR->CODESIZE - 1; x++) + { + + uint32_t volatile *magicAddress = (uint32_t *)(0x400 * x); + + // Check for first 32 bits of Magic + if(*magicAddress == 0x923b8e70) + { + // Check remaining magic + if( + *(uint32_t *)(magicAddress + 0x1) == 0x41A815C6 && + *(uint32_t *)(magicAddress + 0x2) == 0xC96698C4 && + *(uint32_t *)(magicAddress + 0x3) == 0x9751EE75 + ) + { + // If the magic has been found use the hashes follow + magicAddress = (uint32_t *)(magicAddress + 0x4); + + memoryMapStore.memoryMap[1].hash[0] = (*magicAddress & 0xFF); + memoryMapStore.memoryMap[1].hash[1] = (*magicAddress & 0xFF00) >> 8; + memoryMapStore.memoryMap[1].hash[2] = (*magicAddress & 0xFF0000) >> 16; + memoryMapStore.memoryMap[1].hash[3] = (*magicAddress & 0xFF000000) >> 24; + + magicAddress = (uint32_t *)(magicAddress + 0x1); + + memoryMapStore.memoryMap[1].hash[4] = (*magicAddress & 0xFF); + memoryMapStore.memoryMap[1].hash[5] = (*magicAddress & 0xFF00) >> 8; + memoryMapStore.memoryMap[1].hash[6] = (*magicAddress & 0xFF0000) >> 16; + memoryMapStore.memoryMap[1].hash[7] = (*magicAddress & 0xFF000000) >> 24; + + magicAddress = (uint32_t *)(magicAddress + 0x1); + + memoryMapStore.memoryMap[2].hash[0] = (*magicAddress & 0xFF); + memoryMapStore.memoryMap[2].hash[1] = (*magicAddress & 0xFF00) >> 8; + memoryMapStore.memoryMap[2].hash[2] = (*magicAddress & 0xFF0000) >> 16; + memoryMapStore.memoryMap[2].hash[3] = (*magicAddress & 0xFF000000) >> 24; + + magicAddress = (uint32_t *)(magicAddress + 0x1); + + memoryMapStore.memoryMap[2].hash[4] = (*magicAddress & 0xFF); + memoryMapStore.memoryMap[2].hash[5] = (*magicAddress & 0xFF00) >> 8; + memoryMapStore.memoryMap[2].hash[6] = (*magicAddress & 0xFF0000) >> 16; + memoryMapStore.memoryMap[2].hash[7] = (*magicAddress & 0xFF000000) >> 24; + + return; + + } + + } + + } +}