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
master
Sam Kent 5 years ago committed by GitHub
parent 2cff906f01
commit 3413d5487d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      inc/bluetooth/MicroBitBLEManager.h
  2. 106
      inc/bluetooth/MicroBitPartialFlashingService.h
  3. 13
      inc/core/MicroBitComponent.h
  4. 18
      inc/core/MicroBitConfig.h
  5. 2
      inc/drivers/MicroBitFlash.h
  6. 123
      inc/drivers/MicroBitMemoryMap.h
  7. 2
      module.json
  8. 3
      source/CMakeLists.txt
  9. 86
      source/bluetooth/MicroBitBLEManager.cpp
  10. 322
      source/bluetooth/MicroBitPartialFlashingService.cpp
  11. 17
      source/drivers/MicroBitFlash.cpp
  12. 179
      source/drivers/MicroBitMemoryMap.cpp

@ -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

@ -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

@ -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()
{

@ -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
//

@ -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

@ -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

@ -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": [

@ -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)

@ -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;
}

@ -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
};

@ -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);
}

@ -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, &region.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;
}
}
}
}
Loading…
Cancel
Save