Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:1c7da5f83647, committed 2016-11-29
- Comitter:
- sarahmarshy
- Date:
- Tue Nov 29 06:29:10 2016 +0000
- Commit message:
- Initial commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Tue Nov 29 06:29:10 2016 +0000 @@ -0,0 +1,2 @@ +# EddystoneBeacon +Google Open Source image for Eddystone Beacon supporting UID / URL / TLM / EID frames and GATT service
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/config.json Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,20 @@
+{
+ "nordic": {
+ "softdevice": "S130"
+ },
+ "nrf51822": {
+ "ram_size": "32K"
+ },
+ "mbed": {
+ "max-filehandles": 4
+ },
+ "platform" : {
+ "reset_button" : "p17",
+ "config_led" : "p13",
+ "shutdown_led" : "p14",
+ "led_off" : 0
+ },
+ "mbedtls": {
+ "user-config-file": "\"source/mbedtls_config.h\""
+ }
+}
Binary file img/app_start.png has changed
Binary file img/edit_url.png has changed
Binary file img/open_configuration.png has changed
Binary file img/result.png has changed
Binary file img/save_url.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-os.lib Tue Nov 29 06:29:10 2016 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/mbed-os/#aeabcc947294f2a6cbe1e808c030779c7c460f10
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app.json Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,12 @@
+{
+ "macros": [
+ "NDEBUG=1",
+ "MBEDTLS_USER_CONFIG_FILE=\"mbedtls_config.h\"",
+ "OS_MAINSTKSIZE=1024"
+ ],
+ "target_overrides": {
+ "*": {
+ "platform.stdio-flush-at-exit": false
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/readme.md Tue Nov 29 06:29:10 2016 +0000 @@ -0,0 +1,90 @@ +Eddystone beacons broadcast a small amount of information, like URLs, to nearby BLE devices. + +The Eddystone Beacon sample application runs in two stages: + +1. On startup, the Configuration Service (which allows [modification of the beacon](https://github.com/google/eddystone/blob/master/eddystone-url/docs/config-service-spec.md)) runs for a user-defined period (default - 30 seconds). + +1. When the Configuration Service period ends, the Eddystone Service broadcasts advertisement packets. + + + +# Running the application + +## Requirements + +You should install the *Physical Web* application on your phone: + +- [Android version](https://play.google.com/store/apps/details?id=physical_web.org.physicalweb) + +- [iOS version](https://itunes.apple.com/us/app/physical-web/id927653608?mt=8) + + +**Note:** It is also possible to use a regular scanner to interract with your Eddystone beacon but it requires +knowledge about BLE and Eddystone beacon specification out of the scope of this document. + + +Hardware requirements are in the [main readme](https://github.com/ARMmbed/ble-examples/blob/master/README.md). + +## Building instructions + +Building instructions for all samples are in the [main readme](https://github.com/ARMmbed/ble-examples/blob/master/README.md). + +### Working with nRF51-based 16K targets + +Because of memory constraints, you can't use the SoftDevice 130 (S130) to build for nRF51-based 16K targets. If you are using these targets, then before building: + +1. Open the ``config.json`` file in this sample. +1. Change ``soft device`` to ``S110``. +1. Save. + +You can now build for nRF51-based 16K targets. + +## Setting up the beacon + +By default, the beacon directs to the url ``http://mbed.org``. You can change this to your own URL in two ways: + +1. Manually edit the code in ``main.cpp`` in your copy of the sample. + +1. Build and run the application's default code as explained in the building instructions. When the beacon starts up, the Configuration Service runs for 30 seconds (this is the default value; you can change it in ``main.cpp``). While the Configuration Service runs, you can use a BLE scanner on your phone to edit the values the service presents. + +## Checking for success + +1. Build the application and install it on your board as explained in the building instructions. + +1. Open the *Physical Web* application on your phone. It will start to search for nearby beacons. + +  + + **figure 1** Start of the *Physical Web* application version 0.1.856 on Android + +1. When the beacon starts up, the Configuration Service runs for 30 seconds. +During this time it is possible to change the URL advertised by the beacon. +It is also important to note that during these 30 seconds, your device will not advertise any URL. + +  + + **figure 2** How to open the beacon configuration view using the *Physical Web* application version 0.1.856 on Android + + +1. Edit the URL advertised by your beacon. + +  + + **figure 3** How to edit the URL advertised by your beacon using the *Physical Web* application version 0.1.856 on Android + + +1. Save the URL which will be advertised by your beacon. + +  + + **figure 4** How to save your beacon configuration and start advertising URL using the *Physical Web* application version 0.1.856 on Android. + + +1. Find your device; it should advertise the URL you have set. + +  + + **figure 5** Display of URL advertised by your beacon using the *Physical Web* application version 0.1.856 on Android. + + +**Note:** You can use the [Eddystone Observer](https://github.com/ARMmbed/ble-examples/tree/master/BLE_EddystoneObserver) sample instead of a phone application.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EIDFrame.cpp Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2016, Google Inc, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EIDFrame.h"
+#include "EddystoneService.h"
+#include "EntropySource/EntropySource.h"
+
+EIDFrame::EIDFrame()
+{
+ mbedtls_entropy_init(&entropy);
+ // init entropy source
+ eddystoneRegisterEntropySource(&entropy);
+ // init Random
+ mbedtls_ctr_drbg_init(&ctr_drbg);
+}
+
+void EIDFrame::clearFrame(uint8_t* frame) {
+ frame[FRAME_LEN_OFFSET] = 0; // Set frame length to zero to clear it
+}
+
+
+void EIDFrame::setData(uint8_t *rawFrame, int8_t advTxPower, const uint8_t* eidData)
+{
+ size_t index = 0;
+ rawFrame[index++] = EDDYSTONE_UUID_SIZE + EID_FRAME_LEN; // EID length + overhead of four bytes below
+ rawFrame[index++] = EDDYSTONE_UUID[0]; // 16-bit Eddystone UUID
+ rawFrame[index++] = EDDYSTONE_UUID[1];
+ rawFrame[index++] = FRAME_TYPE_EID; // 1B Type
+ rawFrame[index++] = advTxPower; // 1B Power @ 0meter
+
+ memcpy(rawFrame + index, eidData, EID_LENGTH); // EID = 8 BYTE ID
+}
+
+uint8_t* EIDFrame::getData(uint8_t* rawFrame)
+{
+ return &(rawFrame[EID_DATA_OFFSET]);
+}
+
+uint8_t EIDFrame::getDataLength(uint8_t* rawFrame)
+{
+ return rawFrame[FRAME_LEN_OFFSET] - EDDYSTONE_UUID_LEN;
+}
+
+uint8_t* EIDFrame::getAdvFrame(uint8_t* rawFrame)
+{
+ return &(rawFrame[ADV_FRAME_OFFSET]);
+}
+
+uint8_t EIDFrame::getAdvFrameLength(uint8_t* rawFrame)
+{
+ return rawFrame[FRAME_LEN_OFFSET];
+}
+
+uint8_t* EIDFrame::getEid(uint8_t* rawFrame)
+{
+ return &(rawFrame[EID_VALUE_OFFSET]);
+}
+
+uint8_t EIDFrame::getEidLength(uint8_t* rawFrame)
+{
+ return rawFrame[FRAME_LEN_OFFSET] - EID_HEADER_LEN;
+}
+
+void EIDFrame::setAdvTxPower(uint8_t* rawFrame, int8_t advTxPower)
+{
+ rawFrame[EID_TXPOWER_OFFSET] = advTxPower;
+}
+
+// Mote: This is only called after the rotation period is due, or on writing/creating a new eidIdentityKey
+void EIDFrame::update(uint8_t* rawFrame, uint8_t* eidIdentityKey, uint8_t rotationPeriodExp, uint32_t timeSecs)
+{
+ // Calculate the temporary key datastructure 1
+ uint8_t ts[4]; // big endian representation of time
+ ts[0] = (timeSecs >> 24) & 0xff;
+ ts[1] = (timeSecs >> 16) & 0xff;
+
+ uint8_t tmpEidDS1[16] = { 0,0,0,0,0,0,0,0,0,0,0, SALT, 0, 0, ts[0], ts[1] };
+
+ // Perform the aes encryption to generate the final temporary key.
+ uint8_t tmpKey[16];
+ aes128Encrypt(eidIdentityKey, tmpEidDS1, tmpKey);
+
+ // Compute the EID
+ uint8_t eid[16];
+ uint32_t scaledTime = (timeSecs >> rotationPeriodExp) << rotationPeriodExp;
+ ts[0] = (scaledTime >> 24) & 0xff;
+ ts[1] = (scaledTime >> 16) & 0xff;
+ ts[2] = (scaledTime >> 8) & 0xff;
+ ts[3] = scaledTime & 0xff;
+ uint8_t tmpEidDS2[16] = { 0,0,0,0,0,0,0,0,0,0,0, rotationPeriodExp, ts[0], ts[1], ts[2], ts[3] };
+ aes128Encrypt(tmpKey, tmpEidDS2, eid);
+
+ // copy the leading 8 bytes of the eid result (full result length = 16) into the ADV frame
+ memcpy(rawFrame + 5, eid, EID_LENGTH);
+
+}
+
+/** AES128 encrypts a 16-byte input array with a key, resulting in a 16-byte output array */
+void EIDFrame::aes128Encrypt(uint8_t key[], uint8_t input[], uint8_t output[]) {
+ mbedtls_aes_context ctx;
+ mbedtls_aes_init(&ctx);
+ mbedtls_aes_setkey_enc(&ctx, key, 8 * sizeof(Lock_t));
+ mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, input, output);
+ mbedtls_aes_free(&ctx);
+}
+
+int EIDFrame::genBeaconKeys(PrivateEcdhKey_t beaconPrivateEcdhKey, PublicEcdhKey_t beaconPublicEcdhKey) {
+ mbedtls_ecdh_init( &ecdh_ctx );
+
+ int i = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
+ if (i != 0) {
+ return i; // return EID_RND_FAIL;
+ }
+
+ if (mbedtls_ecp_group_load(&ecdh_ctx.grp, MBEDTLS_ECP_DP_CURVE25519) != 0) {
+ return EID_GRP_FAIL;
+ }
+ if (mbedtls_ecdh_gen_public(&ecdh_ctx.grp, &ecdh_ctx.d, &ecdh_ctx.Q, mbedtls_ctr_drbg_random, &ctr_drbg) != 0) {
+ return EID_GENKEY_FAIL;
+ }
+
+ mbedtls_mpi_write_binary(&ecdh_ctx.d, beaconPrivateEcdhKey, sizeof(PrivateEcdhKey_t));
+ mbedtls_mpi_write_binary(&ecdh_ctx.Q.X, beaconPublicEcdhKey, sizeof(PublicEcdhKey_t));
+
+ mbedtls_ecdh_free( &ecdh_ctx );
+ return EID_SUCCESS;
+}
+
+int EIDFrame::genEcdhSharedKey(PrivateEcdhKey_t beaconPrivateEcdhKey, PublicEcdhKey_t beaconPublicEcdhKey, PublicEcdhKey_t serverPublicEcdhKey, EidIdentityKey_t eidIdentityKey) {
+ int16_t ret = 0;
+ uint8_t tmp[32];
+ // initialize context
+ mbedtls_ecdh_init( &ecdh_ctx );
+ mbedtls_ecp_group_load( &ecdh_ctx.grp, MBEDTLS_ECP_DP_CURVE25519 );
+
+ // copy binary beacon private key (previously generated!) into context
+ // Note: As the PrivateKey is generated locally, it is Big Endian
+ ret = mbedtls_mpi_read_binary( &ecdh_ctx.d, beaconPrivateEcdhKey, sizeof(PrivateEcdhKey_t) );
+
+ // copy server-public-key (received through GATT characteristic 10) into context
+ ret = mbedtls_mpi_lset( &ecdh_ctx.Qp.Z, 1 );
+ EddystoneService::swapEndianArray(serverPublicEcdhKey, tmp, 32); // To make it Big Endian
+ ret = mbedtls_mpi_read_binary( &ecdh_ctx.Qp.X, tmp , sizeof(PublicEcdhKey_t) );
+
+ // ECDH point multiplication
+ size_t olen; // actual size of shared secret
+ uint8_t sharedSecret[32]; // shared ECDH secret
+ memset(sharedSecret, 0, 32);
+ ret = mbedtls_ecdh_calc_secret( &ecdh_ctx, &olen, sharedSecret, sizeof(sharedSecret), NULL, NULL );
+ LOG(("size of olen= %d ret=%x\r\n", olen, ret));
+ EddystoneService::swapEndianArray(sharedSecret, tmp, 32);
+ memcpy(sharedSecret, tmp, 32);
+ LOG(("Shared secret=")); EddystoneService::logPrintHex(sharedSecret, 32);
+ if (olen != sizeof(sharedSecret)) {
+ return EID_GENKEY_FAIL;
+ }
+ if (ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA) {
+ return EID_RC_SS_IS_ZERO;
+ }
+
+ // Convert the shared secret to key material using HKDF-SHA256. HKDF is used with
+ // the salt set to a concatenation of the resolver's public key and beacon's
+ // public key, with a null context.
+
+ // build HKDF key
+ unsigned char k[ 64 ];
+ EddystoneService::swapEndianArray(beaconPublicEcdhKey, tmp, 32);
+ memcpy( &k[0], serverPublicEcdhKey, sizeof(PublicEcdhKey_t) );
+ memcpy( &k[32], tmp, sizeof(PublicEcdhKey_t) );
+
+ // compute HKDF: see https://tools.ietf.org/html/rfc5869
+ // mbedtls_md_context_t md_ctx;
+ mbedtls_md_init( &md_ctx );
+ mbedtls_md_setup( &md_ctx, mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ), 1 );
+ mbedtls_md_hmac_starts( &md_ctx, k, sizeof( k ) );
+ mbedtls_md_hmac_update( &md_ctx, sharedSecret, sizeof(sharedSecret) );
+ unsigned char prk[ 32 ];
+ mbedtls_md_hmac_finish( &md_ctx, prk );
+ mbedtls_md_hmac_starts( &md_ctx, prk, sizeof( prk ) );
+ const unsigned char const1[] = { 0x01 };
+ mbedtls_md_hmac_update( &md_ctx, const1, sizeof( const1 ) );
+ unsigned char t[ 32 ];
+ mbedtls_md_hmac_finish( &md_ctx, t );
+
+ //Truncate the key material to 16 bytes (128 bits) to convert it to an AES-128 secret key.
+ memcpy( eidIdentityKey, t, sizeof(EidIdentityKey_t) );
+ LOG(("\r\nEIDIdentityKey=")); EddystoneService::logPrintHex(t, 32); LOG(("\r\n"));
+
+ mbedtls_md_free( &md_ctx );
+ mbedtls_ecdh_free( &ecdh_ctx );
+ return EID_SUCCESS;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EIDFrame.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2016, Google Inc, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EIDFRAME_H__
+#define __EIDFRAME_H__
+
+#include <string.h>
+#include "EddystoneTypes.h"
+#include "mbedtls/aes.h"
+#include "mbedtls/ecdh.h"
+#include "mbedtls/md.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+#include "aes_eax.h"
+
+/**
+ * Class that encapsulates data that belongs to the Eddystone-EID frame. For
+ * more information refer to https://github.com/google/eddystone/tree/master/eddystone-EID.
+ */
+class EIDFrame
+{
+public:
+ static const uint8_t SALT = 0xff;
+ static const uint8_t EID_LENGTH = 8;
+ static const int EID_SUCCESS = 0;
+ static const int EID_RC_SS_IS_ZERO = -1;
+ static const int EID_RND_FAIL = -2;
+ static const int EID_GRP_FAIL = -3;
+ static const int EID_GENKEY_FAIL = -4;
+
+ /**
+ * Construct a new instance of this class.
+ */
+ EIDFrame();
+
+ /**
+ * Clear frame (internally represented by length = 0 )
+ */
+ void clearFrame(uint8_t* frame);
+
+ /**
+ * Construct the raw bytes of the Eddystone-EID frame that will be directly
+ * used in the advertising packets.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ * @param[in] advPowerLevel
+ * Power level value included in the raw frame.
+ * @param[in] eidData
+ * The actual 16-byte EID data in the raw frame.
+ */
+ void setData(uint8_t* rawFrame, int8_t advTxPower, const uint8_t* eidData);
+
+ /**
+ * Get the EID frame data from the Eddystone-EID frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return A pointer to the bytes of the Eddystone-EID frame data.
+ */
+ uint8_t* getData(uint8_t* rawFrame);
+
+ /**
+ * Get the length of the EID frame data from the Eddystone-EID frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the Eddystone-EID frame.
+ */
+ uint8_t getDataLength(uint8_t* rawFrame);
+
+ /**
+ * Get the EID Adv data from the Eddystone-EID frame.
+ * This is the full service data included in the BLE service data params
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return A pointer to the bytes of the Eddystone-EID Adv frame data.
+ */
+ uint8_t* getAdvFrame(uint8_t* rawFrame);
+
+ /**
+ * Get the length of the EID Adv data from the Eddystone-EID frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the Eddystone-EID Adv frame data.
+ */
+ uint8_t getAdvFrameLength(uint8_t* rawFrame);
+
+ /**
+ * Get just the EID data from the Eddystone-EID frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return A pointer to the bytes of the EID in the Eddystone-EID frame.
+ */
+ uint8_t* getEid(uint8_t* rawFrame);
+
+ /**
+ * Get the length of just the EID data from the Eddystone-EID frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the EID in the Eddystone-EID frame.
+ */
+ uint8_t getEidLength(uint8_t* rawFrame);
+
+ /**
+ * Set the Adv TX Power in the frame. This is necessary because the adv
+ * Tx Power might be updated independent of the data bytes
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ * @param[in] advPowerLevel
+ * Power level value included in the raw frame.
+ *
+ */
+ void setAdvTxPower(uint8_t* rawFrame, int8_t advTxPower);
+
+
+ /**
+ * Generate the beacon private and public keys. This should be called on
+ * every restart of the beacon.
+ *
+ * @param[out] beaconPrivateEcdhKey
+ * Pointer to the beacon private key array.
+ * @param[out] beaconPublicEcdhKey
+ * Pointer to the beacon public key array.
+ *
+ */
+ int genBeaconKeys(PrivateEcdhKey_t beaconPrivateEcdhKey, PublicEcdhKey_t beaconPublicEcdhKey);
+
+ /**
+ * Update the EID frame. Tests if its time to rotate the EID payload, and if due, calculates and establishes the new value
+ *
+ * @param[in] *rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ * @param[in] *eidIdentityKey
+ * Eid key used to regenerate the EID id.
+ * @param[in] rotationPeriodExp
+ * EID rotation time as an exponent k : 2^k seconds
+ * @param[in] timeSecs
+ * time in seconds
+ *
+ */
+ void update(uint8_t* rawFrame, uint8_t* eidIdentityKey, uint8_t rotationPeriodExp, uint32_t timeSecs);
+
+ /**
+ * genEcdhSharedKey generates the eik value for inclusion in the EID ADV packet
+ *
+ * @param[in] beaconPrivateEcdhKey
+ * The beacon's private ECDH key, generated by genBeaconKeys()
+ * @param[in] beaconPublicEcdhKey
+ * The beacon's public ECDH key, generated by genBeaconKeys()
+ * @param[in] serverPublicEcdhKey
+ * The server's public ECDH key
+ * @param[out] eidIdentityKey
+ * Identity key for this beacon and server combination
+ */
+ int genEcdhSharedKey(PrivateEcdhKey_t beaconPrivateEcdhKey, PublicEcdhKey_t beaconPublicEcdhKey, PublicEcdhKey_t serverPublicEcdhKey, EidIdentityKey_t eidIdentityKey);
+
+ /**
+ * The byte ID of an Eddystone-EID frame.
+ */
+ static const uint8_t FRAME_TYPE_EID = 0x30;
+
+private:
+
+ // Declare context for crypto functions
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+ mbedtls_ecdh_context ecdh_ctx;
+ mbedtls_md_context_t md_ctx;
+
+ /**
+ * The size (in bytes) of an Eddystone-EID frame.
+ * This is the some of the Eddystone UUID(2 bytes), FrameType, AdvTxPower,
+ * EID Value
+ */
+ static const uint8_t EID_FRAME_LEN = 18;
+ static const uint8_t FRAME_LEN_OFFSET = 0;
+ static const uint8_t EDDYSTONE_UUID_LEN = 2;
+ static const uint8_t EID_DATA_OFFSET = 3;
+ static const uint8_t ADV_FRAME_OFFSET = 1;
+ static const uint8_t EID_VALUE_OFFSET = 5;
+ static const uint8_t EID_HEADER_LEN = 4;
+ static const uint8_t EID_TXPOWER_OFFSET = 4;
+
+ /**
+ * AES128 ECB Encrypts a 16-byte input array with a key, to an output array
+ *
+ * @param[in] *key
+ * The encryption key
+ * @param[in] *input
+ * The input array
+ * @param[in] *output
+ * The output array (contains the encrypted data)
+ */
+ void aes128Encrypt(uint8_t *key, uint8_t *input, uint8_t *output);
+
+};
+
+#endif /* __EIDFRAME_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EddystoneService.cpp Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,1331 @@
+/*
+ * Copyright (c) 2006-2016 Google Inc, All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EddystoneService.h"
+#include "PersistentStorageHelper/ConfigParamsPersistence.h"
+#include "EntropySource/EntropySource.h"
+
+/* Use define zero for production, 1 for testing to allow connection at any time */
+#define DEFAULT_REMAIN_CONNECTABLE 0x01
+
+const char * const EddystoneService::slotDefaultUrls[] = EDDYSTONE_DEFAULT_SLOT_URLS;
+
+// Static timer used as time since boot
+Timer EddystoneService::timeSinceBootTimer;
+
+/*
+ * CONSTRUCTOR #1 Used on 1st boot (after reflash)
+ */
+EddystoneService::EddystoneService(BLE &bleIn,
+ const PowerLevels_t &advTxPowerLevelsIn,
+ const PowerLevels_t &radioTxPowerLevelsIn,
+ event_queue_t &evQ,
+ uint32_t advConfigIntervalIn) :
+ ble(bleIn),
+ operationMode(EDDYSTONE_MODE_NONE),
+ uidFrame(),
+ urlFrame(),
+ tlmFrame(),
+ eidFrame(),
+ tlmBatteryVoltageCallback(NULL),
+ tlmBeaconTemperatureCallback(NULL),
+ radioManagerCallbackHandle(NULL),
+ deviceName(DEFAULT_DEVICE_NAME),
+ eventQueue(evQ),
+ nextEidSlot(0)
+{
+ LOG(("1st Boot: "));
+ LOG((BUILD_VERSION_STR));
+ if (advConfigIntervalIn != 0) {
+ if (advConfigIntervalIn < ble.gap().getMinAdvertisingInterval()) {
+ advConfigInterval = ble.gap().getMinAdvertisingInterval();
+ } else if (advConfigIntervalIn > ble.gap().getMaxAdvertisingInterval()) {
+ advConfigInterval = ble.gap().getMaxAdvertisingInterval();
+ } else {
+ advConfigInterval = advConfigIntervalIn;
+ }
+ }
+ memcpy(radioTxPowerLevels, radioTxPowerLevelsIn, sizeof(PowerLevels_t));
+ memcpy(advTxPowerLevels, advTxPowerLevelsIn, sizeof(PowerLevels_t));
+
+ // 1st Boot so reset everything to factory values
+ LOG(("1st BOOT: "));
+ doFactoryReset(); // includes genBeaconKeys
+
+ LOG(("After FactoryReset: 1st Boot Init: genBeaconKeyRC=%d\r\n", genBeaconKeyRC));
+
+ /* Set the device name at startup */
+ ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceName));
+}
+
+/*
+ * Constuctor #2: Used on 2nd+ boot: EddystoneService parameters derived from persistent storage
+ */
+EddystoneService::EddystoneService(BLE &bleIn,
+ EddystoneParams_t ¶msIn,
+ const PowerLevels_t &radioTxPowerLevelsIn,
+ event_queue_t &evQ,
+ uint32_t advConfigIntervalIn) :
+ ble(bleIn),
+ operationMode(EDDYSTONE_MODE_NONE),
+ uidFrame(),
+ urlFrame(),
+ tlmFrame(),
+ eidFrame(),
+ tlmBatteryVoltageCallback(NULL),
+ tlmBeaconTemperatureCallback(NULL),
+ radioManagerCallbackHandle(NULL),
+ deviceName(DEFAULT_DEVICE_NAME),
+ eventQueue(evQ),
+ nextEidSlot(0)
+{
+ LOG(("2nd (>=) Boot: "));
+ LOG((BUILD_VERSION_STR));
+ // Init time Params
+ LOG(("Init Params\r\n"));
+ timeSinceBootTimer.start();
+ memcpy(&timeParams, &(paramsIn.timeParams), sizeof(TimeParams_t));
+ LOG(("2nd Boot: Time:"));
+ LOG(("PriorBoots=%lu, SinceBoot=%lu\r\n", timeParams.timeInPriorBoots, timeParams.timeSinceLastBoot));
+ timeParams.timeInPriorBoots = timeParams.timeInPriorBoots + timeParams.timeSinceLastBoot;
+ timeParams.timeSinceLastBoot = getTimeSinceLastBootMs() / 1000;
+ nvmSaveTimeParams();
+
+ // Init gneeral params
+ memcpy(capabilities, paramsIn.capabilities, sizeof(Capability_t));
+ activeSlot = paramsIn.activeSlot;
+ memcpy(radioTxPowerLevels, radioTxPowerLevelsIn, sizeof(PowerLevels_t));
+ memcpy(slotRadioTxPowerLevels, paramsIn.slotRadioTxPowerLevels, sizeof(SlotTxPowerLevels_t));
+ memcpy(advTxPowerLevels, paramsIn.advTxPowerLevels, sizeof(PowerLevels_t));
+ memcpy(slotAdvTxPowerLevels, paramsIn.slotAdvTxPowerLevels, sizeof(SlotTxPowerLevels_t));
+ memcpy(slotAdvIntervals, paramsIn.slotAdvIntervals, sizeof(SlotAdvIntervals_t));
+ lockState = paramsIn.lockState;
+ memcpy(unlockKey, paramsIn.unlockKey, sizeof(Lock_t));
+ memcpy(unlockToken, paramsIn.unlockToken, sizeof(Lock_t));
+ memcpy(challenge, paramsIn.challenge, sizeof(Lock_t));
+ memset(slotCallbackHandles, 0, sizeof(SlotCallbackHandles_t));
+ memcpy(slotStorage, paramsIn.slotStorage, sizeof(SlotStorage_t));
+ memcpy(slotFrameTypes, paramsIn.slotFrameTypes, sizeof(SlotFrameTypes_t));
+ memcpy(slotEidRotationPeriodExps, paramsIn.slotEidRotationPeriodExps, sizeof(SlotEidRotationPeriodExps_t));
+ memcpy(slotEidIdentityKeys, paramsIn.slotEidIdentityKeys, sizeof(SlotEidIdentityKeys_t));
+ // Zero next EID slot rotation times to enforce rotation of each slot on restart
+ memset(slotEidNextRotationTimes, 0, sizeof(SlotEidNextRotationTimes_t));
+ remainConnectable = paramsIn.remainConnectable;
+
+ if (advConfigIntervalIn != 0) {
+ if (advConfigIntervalIn < ble.gap().getMinAdvertisingInterval()) {
+ advConfigInterval = ble.gap().getMinAdvertisingInterval();
+ } else if (advConfigIntervalIn > ble.gap().getMaxAdvertisingInterval()) {
+ advConfigInterval = ble.gap().getMaxAdvertisingInterval();
+ } else {
+ advConfigInterval = advConfigIntervalIn;
+ }
+ }
+
+ // Generate fresh private and public ECDH keys for EID
+ genEIDBeaconKeys();
+
+ // Recompute EID Slot Data
+ for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
+ uint8_t* frame = slotToFrame(slot);
+ switch (slotFrameTypes[slot]) {
+ case EDDYSTONE_FRAME_EID:
+ nextEidSlot = slot;
+ eidFrame.setData(frame, slotAdvTxPowerLevels[slot], nullEid);
+ eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], getTimeSinceFirstBootSecs());
+ break;
+ }
+ }
+
+ /* Set the device name at startup */
+ ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceName));
+}
+
+// Regenerate the beacon keys
+void EddystoneService::genEIDBeaconKeys(void) {
+ genBeaconKeyRC = -1;
+#ifdef GEN_BEACON_KEYS_AT_INIT
+ memset(privateEcdhKey, 0, 32);
+ memset(publicEcdhKey, 0, 32);
+ genBeaconKeyRC = eidFrame.genBeaconKeys(privateEcdhKey, publicEcdhKey);
+ swapEndianArray(publicEcdhKey, publicEcdhKeyLE, 32);
+#endif
+}
+
+/**
+ * Factory reset all parmeters: used at initial boot, and activated from Char 11
+ */
+void EddystoneService::doFactoryReset(void)
+{
+ // Init Time tracking
+ timeSinceBootTimer.start();
+ timeParams.timeInPriorBoots = 0;
+ timeParams.timeSinceLastBoot = getTimeSinceLastBootMs() / 1000;
+ nvmSaveTimeParams();
+ // Init callbacks
+ memset(slotCallbackHandles, 0, sizeof(SlotCallbackHandles_t));
+ radioManagerCallbackHandle = NULL;
+ memcpy(capabilities, CAPABILITIES_DEFAULT, CAP_HDR_LEN);
+ // Line above leaves powerlevels blank; Line below fills them in
+ memcpy(capabilities + CAP_HDR_LEN, radioTxPowerLevels, sizeof(PowerLevels_t));
+ activeSlot = DEFAULT_SLOT;
+ // Intervals
+ uint16_t buf1[] = EDDYSTONE_DEFAULT_SLOT_INTERVALS;
+ for (int i = 0; i < MAX_ADV_SLOTS; i++) {
+ // Ensure all slot periods are in range
+ buf1[i] = correctAdvertisementPeriod(buf1[i]);
+ }
+ memcpy(slotAdvIntervals, buf1, sizeof(SlotAdvIntervals_t));
+ // Radio and Adv TX Power
+ int8_t buf2[] = EDDYSTONE_DEFAULT_SLOT_TX_POWERS;
+ for (int i = 0; i< MAX_ADV_SLOTS; i++) {
+ slotRadioTxPowerLevels[i] = buf2[i];
+ slotAdvTxPowerLevels[i] = advTxPowerLevels[radioTxPowerToIndex(buf2[i])];
+ }
+ // Lock
+ lockState = UNLOCKED;
+ uint8_t defKeyBuf[] = EDDYSTONE_DEFAULT_UNLOCK_KEY;
+ memcpy(unlockKey, defKeyBuf, sizeof(Lock_t));
+ memset(unlockToken, 0, sizeof(Lock_t));
+ memset(challenge, 0, sizeof(Lock_t)); // NOTE: challenge is randomized on first unlockChar read;
+
+ // Generate ECDH Beacon Key Pair (Private/Public)
+ genEIDBeaconKeys();
+
+ memcpy(slotEidIdentityKeys, slotDefaultEidIdentityKeys, sizeof(SlotEidIdentityKeys_t));
+ uint8_t buf4[] = EDDYSTONE_DEFAULT_SLOT_EID_ROTATION_PERIOD_EXPS;
+ memcpy(slotEidRotationPeriodExps, buf4, sizeof(SlotEidRotationPeriodExps_t));
+ memset(slotEidNextRotationTimes, 0, sizeof(SlotEidNextRotationTimes_t));
+ // Slot Data Type Defaults
+ uint8_t buf3[] = EDDYSTONE_DEFAULT_SLOT_TYPES;
+ memcpy(slotFrameTypes, buf3, sizeof(SlotFrameTypes_t));
+ // Initialize Slot Data Defaults
+ int eidSlot;
+ for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
+ uint8_t* frame = slotToFrame(slot);
+ switch (slotFrameTypes[slot]) {
+ case EDDYSTONE_FRAME_UID:
+ uidFrame.setData(frame, slotAdvTxPowerLevels[slot], reinterpret_cast<const uint8_t*>(slotDefaultUids[slot]));
+ break;
+ case EDDYSTONE_FRAME_URL:
+ urlFrame.setUnencodedUrlData(frame, slotAdvTxPowerLevels[slot], slotDefaultUrls[slot]);
+ break;
+ case EDDYSTONE_FRAME_TLM:
+ tlmFrame.setTLMData(TLMFrame::DEFAULT_TLM_VERSION);
+ tlmFrame.setData(frame);
+ eidSlot = getEidSlot();
+ if (eidSlot != NO_EID_SLOT_SET) {
+ LOG(("EID slot Set in FactoryReset\r\n"));
+ tlmFrame.encryptData(frame, slotEidIdentityKeys[eidSlot], slotEidRotationPeriodExps[eidSlot], getTimeSinceFirstBootSecs());
+ }
+ break;
+ case EDDYSTONE_FRAME_EID:
+ nextEidSlot = slot;
+ eidFrame.setData(frame, slotAdvTxPowerLevels[slot], nullEid);
+ eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], getTimeSinceFirstBootSecs());
+ break;
+ }
+ }
+
+#ifdef DONT_REMAIN_CONNECTABLE
+ remainConnectable = REMAIN_CONNECTABLE_UNSET;
+#else
+ remainConnectable = REMAIN_CONNECTABLE_SET;
+#endif
+ factoryReset = false;
+}
+
+/* Setup callback to update BatteryVoltage in TLM frame */
+void EddystoneService::onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn)
+{
+ tlmBatteryVoltageCallback = tlmBatteryVoltageCallbackIn;
+}
+
+/* Setup callback to update BeaconTemperature in TLM frame */
+void EddystoneService::onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn)
+{
+ tlmBeaconTemperatureCallback = tlmBeaconTemperatureCallbackIn;
+}
+
+EddystoneService::EddystoneError_t EddystoneService::startEddystoneBeaconAdvertisements(void)
+{
+ stopEddystoneBeaconAdvertisements();
+
+ bool intervalValidFlag = false;
+ for (int i = 0; i < MAX_ADV_SLOTS; i++) {
+ if (slotAdvIntervals[i] != 0) {
+ intervalValidFlag = true;
+ }
+ }
+
+ if (!intervalValidFlag) {
+ /* Nothing to do, the period is 0 for all frames */
+ return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL;
+ }
+
+ // In case left over from Config Adv Mode
+ ble.gap().clearScanResponse();
+
+ operationMode = EDDYSTONE_MODE_BEACON;
+
+ /* Configure advertisements initially at power of active slot*/
+ ble.gap().setTxPower(slotRadioTxPowerLevels[activeSlot]);
+
+ if (remainConnectable) {
+ ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+ } else {
+ ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
+ }
+ ble.gap().setAdvertisingInterval(ble.gap().getMaxAdvertisingInterval());
+
+ /* Make sure the queue is currently empty */
+ advFrameQueue.reset();
+ /* Setup callbacks to periodically add frames to be advertised to the queue and
+ * add initial frame so that we have something to advertise on startup */
+ for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
+ uint8_t* frame = slotToFrame(slot);
+ if (slotAdvIntervals[slot] && testValidFrame(frame)) {
+ advFrameQueue.push(slot);
+ slotCallbackHandles[slot] = eventQueue.post_every(
+ &EddystoneService::enqueueFrame, this, slot,
+ slotAdvIntervals[slot] /* ms */
+ );
+ }
+ }
+ /* Start advertising */
+ manageRadio();
+
+ return EDDYSTONE_ERROR_NONE;
+}
+
+ble_error_t EddystoneService::setCompleteDeviceName(const char *deviceNameIn)
+{
+ /* Make sure the device name is safe */
+ ble_error_t error = ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceNameIn));
+ if (error == BLE_ERROR_NONE) {
+ deviceName = deviceNameIn;
+ if (operationMode == EDDYSTONE_MODE_CONFIG) {
+ /* Need to update the advertising packets to the new name */
+ setupEddystoneConfigScanResponse();
+ }
+ }
+
+ return error;
+}
+
+/* It is not the responsibility of the Eddystone implementation to store
+ * the configured parameters in persistent storage since this is
+ * platform-specific. So we provide this function that returns the
+ * configured values that need to be stored and the main application
+ * takes care of storing them.
+ */
+void EddystoneService::getEddystoneParams(EddystoneParams_t ¶ms)
+{
+ // Time
+ timeParams.timeSinceLastBoot = getTimeSinceLastBootMs() / 1000;
+ memcpy(&(params.timeParams), &timeParams, sizeof(TimeParams_t));
+ // Capabilities
+ memcpy(params.capabilities, capabilities, sizeof(Capability_t));
+ // Active Slot
+ params.activeSlot = activeSlot;
+ // Intervals
+ memcpy(params.slotAdvIntervals, slotAdvIntervals, sizeof(SlotAdvIntervals_t));
+ // Power Levels
+ memcpy(params.radioTxPowerLevels, radioTxPowerLevels, sizeof(PowerLevels_t));
+ memcpy(params.advTxPowerLevels, advTxPowerLevels, sizeof(PowerLevels_t));
+ // Slot Power Levels
+ memcpy(params.slotRadioTxPowerLevels, slotRadioTxPowerLevels, sizeof(MAX_ADV_SLOTS));
+ memcpy(params.slotAdvTxPowerLevels, slotAdvTxPowerLevels, sizeof(MAX_ADV_SLOTS));
+ // Lock
+ params.lockState = lockState;
+ memcpy(params.unlockKey, unlockKey, sizeof(Lock_t));
+ memcpy(params.unlockToken, unlockToken, sizeof(Lock_t));
+ memcpy(params.challenge, challenge, sizeof(Lock_t));
+ // Slots
+ memcpy(params.slotFrameTypes, slotFrameTypes, sizeof(SlotFrameTypes_t));
+ memcpy(params.slotStorage, slotStorage, sizeof(SlotStorage_t));
+ memcpy(params.slotEidRotationPeriodExps, slotEidRotationPeriodExps, sizeof(SlotEidRotationPeriodExps_t));
+ memcpy(params.slotEidIdentityKeys, slotEidIdentityKeys, sizeof(SlotEidIdentityKeys_t));
+ // Testing and Management
+ params.remainConnectable = remainConnectable;
+}
+
+void EddystoneService::swapAdvertisedFrame(int slot)
+{
+ uint8_t* frame = slotToFrame(slot);
+ uint8_t frameType = slotFrameTypes[slot];
+ uint32_t timeSecs = getTimeSinceFirstBootSecs();
+ switch (frameType) {
+ case EDDYSTONE_FRAME_UID:
+ updateAdvertisementPacket(uidFrame.getAdvFrame(frame), uidFrame.getAdvFrameLength(frame));
+ break;
+ case EDDYSTONE_FRAME_URL:
+ updateAdvertisementPacket(urlFrame.getAdvFrame(frame), urlFrame.getAdvFrameLength(frame));
+ break;
+ case EDDYSTONE_FRAME_TLM:
+ updateRawTLMFrame(frame);
+ updateAdvertisementPacket(tlmFrame.getAdvFrame(frame), tlmFrame.getAdvFrameLength(frame));
+ break;
+ case EDDYSTONE_FRAME_EID:
+ // only update the frame if the rotation period is due
+ if (timeSecs >= slotEidNextRotationTimes[slot]) {
+ eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSecs);
+ slotEidNextRotationTimes[slot] = timeSecs + (1 << slotEidRotationPeriodExps[slot]);
+ // select a new random MAC address so the beacon is not trackable
+ setRandomMacAddress();
+ // Store in NVM in case the beacon loses power
+ nvmSaveTimeParams();
+ LOG(("EID ROTATED: Time=%lu\r\n", timeSecs));
+ }
+ updateAdvertisementPacket(eidFrame.getAdvFrame(frame), eidFrame.getAdvFrameLength(frame));
+ break;
+ default:
+ //Some error occurred
+ error("Frame to swap in does not specify a valid type");
+ break;
+ }
+ ble.gap().setTxPower(slotRadioTxPowerLevels[slot]);
+}
+
+
+/* Helper function that calls user-defined functions to update Battery Voltage and Temperature (if available),
+ * then updates the raw frame data and finally updates the actual advertised packet. This operation must be
+ * done fairly often because the TLM frame TimeSinceBoot must have a 0.1 secs resolution according to the
+ * Eddystone specification.
+ */
+void EddystoneService::updateRawTLMFrame(uint8_t* frame)
+{
+ if (tlmBeaconTemperatureCallback != NULL) {
+ tlmFrame.updateBeaconTemperature((*tlmBeaconTemperatureCallback)(tlmFrame.getBeaconTemperature()));
+ }
+ if (tlmBatteryVoltageCallback != NULL) {
+ tlmFrame.updateBatteryVoltage((*tlmBatteryVoltageCallback)(tlmFrame.getBatteryVoltage()));
+ }
+ tlmFrame.updateTimeSinceLastBoot(getTimeSinceLastBootMs());
+ tlmFrame.setData(frame);
+ int slot = getEidSlot();
+ LOG(("TLMHelper Method slot=%d\r\n", slot));
+ if (slot != NO_EID_SLOT_SET) {
+ LOG(("TLMHelper: Before Encrypting TLM\r\n"));
+ tlmFrame.encryptData(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], getTimeSinceFirstBootSecs());
+ LOG(("TLMHelper: Before Encrypting TLM\r\n"));
+ }
+}
+
+void EddystoneService::updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength)
+{
+ ble.gap().clearAdvertisingPayload();
+ ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
+ ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID));
+ ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, rawFrameLength);
+}
+
+uint8_t* EddystoneService::slotToFrame(int slot)
+{
+ return reinterpret_cast<uint8_t *>(&slotStorage[slot * sizeof(Slot_t)]);
+}
+
+void EddystoneService::enqueueFrame(int slot)
+{
+ advFrameQueue.push(slot);
+ if (!radioManagerCallbackHandle) {
+ /* Advertising stopped and there is not callback posted in the event queue. Just
+ * execute the manager to resume advertising */
+ manageRadio();
+ }
+}
+
+void EddystoneService::manageRadio(void)
+{
+ uint8_t slot;
+ uint64_t startTimeManageRadio = getTimeSinceLastBootMs();
+
+ /* Signal that there is currently no callback posted */
+ radioManagerCallbackHandle = NULL;
+
+ if (advFrameQueue.pop(slot)) {
+ /* We have something to advertise */
+ if (ble.gap().getState().advertising) {
+ ble.gap().stopAdvertising();
+ }
+ swapAdvertisedFrame(slot);
+ ble.gap().startAdvertising();
+
+ /* Increase the advertised packet count in TLM frame */
+ tlmFrame.updatePduCount();
+
+ /* Post a callback to itself to stop the advertisement or pop the next
+ * frame from the queue. However, take into account the time taken to
+ * swap in this frame. */
+ radioManagerCallbackHandle = eventQueue.post_in(
+ &EddystoneService::manageRadio, this,
+ ble.gap().getMinNonConnectableAdvertisingInterval() - (getTimeSinceLastBootMs() - startTimeManageRadio) /* ms */
+ );
+ } else if (ble.gap().getState().advertising) {
+ /* Nothing else to advertise, stop advertising and do not schedule any callbacks */
+ ble.gap().stopAdvertising();
+ }
+}
+
+void EddystoneService::startEddystoneConfigService(void)
+{
+ uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
+ int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot];
+ int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
+ uint8_t* slotData = slotToFrame(activeSlot) + 1;
+ aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
+
+ capabilitiesChar = new ReadOnlyArrayGattCharacteristic<uint8_t, sizeof(Capability_t)>(UUID_CAPABILITIES_CHAR, capabilities);
+ activeSlotChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_ACTIVE_SLOT_CHAR, &activeSlot);
+ advIntervalChar = new ReadWriteGattCharacteristic<uint16_t>(UUID_ADV_INTERVAL_CHAR, &beAdvInterval);
+ radioTxPowerChar = new ReadWriteGattCharacteristic<int8_t>(UUID_RADIO_TX_POWER_CHAR, &radioTxPower);
+ advTxPowerChar = new ReadWriteGattCharacteristic<int8_t>(UUID_ADV_TX_POWER_CHAR, &advTxPower);
+ lockStateChar = new GattCharacteristic(UUID_LOCK_STATE_CHAR, &lockState, sizeof(uint8_t), sizeof(LockState_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
+ unlockChar = new ReadWriteArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_UNLOCK_CHAR, unlockToken);
+ publicEcdhKeyChar = new GattCharacteristic(UUID_PUBLIC_ECDH_KEY_CHAR, publicEcdhKey, 0, sizeof(PublicEcdhKey_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
+ eidIdentityKeyChar = new GattCharacteristic(UUID_EID_IDENTITY_KEY_CHAR, encryptedEidIdentityKey, 0, sizeof(EidIdentityKey_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
+ advSlotDataChar = new GattCharacteristic(UUID_ADV_SLOT_DATA_CHAR, slotData, 0, 34, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
+ factoryResetChar = new WriteOnlyGattCharacteristic<uint8_t>(UUID_FACTORY_RESET_CHAR, &factoryReset);
+ remainConnectableChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_REMAIN_CONNECTABLE_CHAR, &remainConnectable);
+
+ // CHAR-1 capabilities (READ ONLY)
+ capabilitiesChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
+ // CHAR-2 Active Slot
+ activeSlotChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
+ activeSlotChar->setWriteAuthorizationCallback(this, &EddystoneService::writeActiveSlotAuthorizationCallback<uint8_t>);
+ // CHAR-3 Adv Interval
+ advIntervalChar->setReadAuthorizationCallback(this, &EddystoneService::readAdvIntervalAuthorizationCallback);
+ advIntervalChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint16_t>);
+ // CHAR-4 Radio TX Power
+ radioTxPowerChar->setReadAuthorizationCallback(this, &EddystoneService::readRadioTxPowerAuthorizationCallback);
+ radioTxPowerChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint8_t>);
+ // CHAR-5
+ advTxPowerChar->setReadAuthorizationCallback(this, &EddystoneService::readAdvTxPowerAuthorizationCallback);
+ advTxPowerChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint8_t>);
+ // CHAR-6 Lock State
+ lockStateChar->setWriteAuthorizationCallback(this, &EddystoneService::writeLockStateAuthorizationCallback);
+ // CHAR-7 Unlock
+ unlockChar->setReadAuthorizationCallback(this, &EddystoneService::readUnlockAuthorizationCallback);
+ unlockChar->setWriteAuthorizationCallback(this, &EddystoneService::writeUnlockAuthorizationCallback);
+ // CHAR-8 Public Ecdh Key (READ ONLY)
+ publicEcdhKeyChar->setReadAuthorizationCallback(this, &EddystoneService::readPublicEcdhKeyAuthorizationCallback);
+ // CHAR-9 EID Identity Key (READ ONLY)
+ eidIdentityKeyChar->setReadAuthorizationCallback(this, &EddystoneService::readEidIdentityAuthorizationCallback);
+ // CHAR-10 Adv Slot Data
+ advSlotDataChar->setReadAuthorizationCallback(this, &EddystoneService::readDataAuthorizationCallback);
+ advSlotDataChar->setWriteAuthorizationCallback(this, &EddystoneService::writeVarLengthDataAuthorizationCallback);
+ // CHAR-11 Factory Reset
+ factoryResetChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
+ factoryResetChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<bool>);
+ // CHAR-12 Remain Connectable
+ remainConnectableChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
+ remainConnectableChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<bool>);
+
+ // Create pointers to all characteristics in the GATT service
+ charTable[0] = capabilitiesChar;
+ charTable[1] = activeSlotChar;
+ charTable[2] = advIntervalChar;
+ charTable[3] = radioTxPowerChar;
+ charTable[4] = advTxPowerChar;
+ charTable[5] = lockStateChar;
+ charTable[6] = unlockChar;
+ charTable[7] = publicEcdhKeyChar;
+ charTable[8] = eidIdentityKeyChar;
+ charTable[9] = advSlotDataChar;
+ charTable[10] = factoryResetChar;
+ charTable[11] = remainConnectableChar;
+
+ GattService configService(UUID_ES_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
+
+ ble.gattServer().addService(configService);
+ ble.gattServer().onDataWritten(this, &EddystoneService::onDataWrittenCallback);
+ updateCharacteristicValues();
+}
+
+
+void EddystoneService::freeConfigCharacteristics(void)
+{
+ delete capabilitiesChar;
+ delete activeSlotChar;
+ delete advIntervalChar;
+ delete radioTxPowerChar;
+ delete advTxPowerChar;
+ delete lockStateChar;
+ delete unlockChar;
+ delete publicEcdhKeyChar;
+ delete eidIdentityKeyChar;
+ delete advSlotDataChar;
+ delete factoryResetChar;
+ delete remainConnectableChar;
+}
+
+void EddystoneService::stopEddystoneBeaconAdvertisements(void)
+{
+ /* Unschedule callbacks */
+
+ for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
+ if (slotCallbackHandles[slot]) {
+ eventQueue.cancel(slotCallbackHandles[slot]);
+ slotCallbackHandles[slot] = NULL;
+ }
+ }
+
+ if (radioManagerCallbackHandle) {
+ eventQueue.cancel(radioManagerCallbackHandle);
+ radioManagerCallbackHandle = NULL;
+ }
+
+ /* Stop any current Advs (ES Config or Beacon) */
+ BLE::Instance().gap().stopAdvertising();
+}
+
+/*
+ * Internal helper function used to update the GATT database following any
+ * change to the internal state of the service object.
+ */
+void EddystoneService::updateCharacteristicValues(void)
+{
+ // Init variables for update
+ uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
+ int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot];
+ int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
+ uint8_t* frame = slotToFrame(activeSlot);
+ uint8_t slotLength = 0;
+ uint8_t* slotData = NULL;
+ memset(encryptedEidIdentityKey, 0, sizeof(encryptedEidIdentityKey));
+
+ switch(slotFrameTypes[activeSlot]) {
+ case EDDYSTONE_FRAME_UID:
+ slotLength = uidFrame.getDataLength(frame);
+ slotData = uidFrame.getData(frame);
+ break;
+ case EDDYSTONE_FRAME_URL:
+ slotLength = urlFrame.getDataLength(frame);
+ slotData = urlFrame.getData(frame);
+ break;
+ case EDDYSTONE_FRAME_TLM:
+ updateRawTLMFrame(frame);
+ slotLength = tlmFrame.getDataLength(frame);
+ slotData = tlmFrame.getData(frame);
+ break;
+ case EDDYSTONE_FRAME_EID:
+ slotLength = eidFrame.getDataLength(frame);
+ slotData = eidFrame.getData(frame);
+ aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
+ break;
+ }
+
+ ble.gattServer().write(capabilitiesChar->getValueHandle(), reinterpret_cast<uint8_t *>(capabilities), sizeof(Capability_t));
+ ble.gattServer().write(activeSlotChar->getValueHandle(), &activeSlot, sizeof(uint8_t));
+ ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t));
+ ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t));
+ ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t));
+ ble.gattServer().write(lockStateChar->getValueHandle(), &lockState, sizeof(uint8_t));
+ ble.gattServer().write(unlockChar->getValueHandle(), unlockToken, sizeof(Lock_t));
+ ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(publicEcdhKey), sizeof(PublicEcdhKey_t));
+ ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(encryptedEidIdentityKey), sizeof(EidIdentityKey_t));
+ ble.gattServer().write(advSlotDataChar->getValueHandle(), slotData, slotLength);
+ ble.gattServer().write(factoryResetChar->getValueHandle(), &factoryReset, sizeof(uint8_t));
+ ble.gattServer().write(remainConnectableChar->getValueHandle(), &remainConnectable, sizeof(uint8_t));
+}
+
+EddystoneService::EddystoneError_t EddystoneService::startEddystoneConfigAdvertisements(void)
+{
+ stopEddystoneBeaconAdvertisements();
+
+ if (advConfigInterval == 0) {
+ // Nothing to do, the advertisement interval is 0
+ return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL;
+ }
+
+ operationMode = EDDYSTONE_MODE_CONFIG;
+
+ ble.gap().clearAdvertisingPayload();
+
+ /* Accumulate the new payload */
+ // Add the Flags param
+ ble.gap().accumulateAdvertisingPayload(
+ GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE
+ );
+#ifdef INCLUDE_CONFIG_URL
+ // Add the Eddystone 16-bit Service ID
+ ble.gap().accumulateAdvertisingPayload(
+ GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS,
+ EDDYSTONE_UUID,
+ sizeof(EDDYSTONE_UUID)
+ );
+#endif
+ /* UUID is in different order in the ADV frame (!) */
+ uint8_t reversedServiceUUID[sizeof(UUID_ES_BEACON_SERVICE)];
+ for (size_t i = 0; i < sizeof(UUID_ES_BEACON_SERVICE); i++) {
+ reversedServiceUUID[i] = UUID_ES_BEACON_SERVICE[sizeof(UUID_ES_BEACON_SERVICE) - i - 1];
+ }
+ ble.gap().accumulateAdvertisingPayload(
+ GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
+ reversedServiceUUID,
+ sizeof(reversedServiceUUID)
+ );
+ // Add Generic Appearance Tag
+ ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
+ setupEddystoneConfigScanResponse();
+
+ ble.gap().setTxPower(radioTxPowerLevels[sizeof(PowerLevels_t)-1]); // Max Power for Config
+ ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+ ble.gap().setAdvertisingInterval(advConfigInterval);
+ ble.gap().startAdvertising();
+
+ return EDDYSTONE_ERROR_NONE;
+}
+
+void EddystoneService::setupEddystoneConfigScanResponse(void)
+{
+ ble.gap().clearScanResponse();
+ // Add LOCAL NAME (indicating the Eddystone Version)
+ ble.gap().accumulateScanResponse(
+ GapAdvertisingData::COMPLETE_LOCAL_NAME,
+ reinterpret_cast<const uint8_t *>(deviceName),
+ strlen(deviceName)
+ );
+#ifdef INCLUDE_CONFIG_URL
+ // Add SERVICE DATA for a PhyWeb Config URL
+ uint8_t configFrame[URLFrame::ENCODED_BUF_SIZE];
+ int encodedUrlLen = URLFrame::encodeURL(configFrame + CONFIG_FRAME_HDR_LEN, EDDYSTONE_CONFIG_URL);
+ uint8_t advPower = advTxPowerLevels[sizeof(PowerLevels_t)-1] & 0xFF;
+ uint8_t configFrameHdr[CONFIG_FRAME_HDR_LEN] = {0, 0, URLFrame::FRAME_TYPE_URL, advPower};
+ // ++ Fill in the Eddystone Service UUID in the HDR
+ memcpy(configFrameHdr, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID));
+ // ++ Copy the HDR to the config frame
+ memcpy(configFrame, configFrameHdr, CONFIG_FRAME_HDR_LEN);
+ ble.gap().accumulateScanResponse(
+ GapAdvertisingData::SERVICE_DATA,
+ configFrame,
+ CONFIG_FRAME_HDR_LEN + encodedUrlLen
+ );
+#else
+ // Add TRANSMIT POWER
+ ble.gap().accumulateScanResponse(
+ GapAdvertisingData::TX_POWER_LEVEL,
+ reinterpret_cast<uint8_t *>(&advTxPowerLevels[sizeof(PowerLevels_t)-1]),
+ sizeof(uint8_t)
+ );
+#endif
+}
+
+/* WRITE AUTHORIZATION */
+
+void EddystoneService::writeUnlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
+{
+ if (lockState == UNLOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
+ } else if (authParams->len != sizeof(Lock_t)) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
+ } else if (authParams->offset != 0) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
+ } else if (memcmp(authParams->data, unlockToken, sizeof(Lock_t)) != 0) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
+ } else {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+ }
+}
+
+void EddystoneService::writeVarLengthDataAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
+{
+ if (lockState == LOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
+ } else if (authParams->len > 34) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
+ } else {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+ }
+}
+
+
+void EddystoneService::writeLockStateAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
+{
+ if (lockState == LOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
+ } else if ((authParams->len != sizeof(uint8_t)) && (authParams->len != (sizeof(uint8_t) + sizeof(Lock_t)))) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
+ } else if (authParams->offset != 0) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
+ } else {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+ }
+}
+
+template <typename T>
+void EddystoneService::writeBasicAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
+{
+ if (lockState == LOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
+ } else if (authParams->len != sizeof(T)) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
+ } else if (authParams->offset != 0) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
+ } else {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+ }
+}
+
+template <typename T>
+void EddystoneService::writeActiveSlotAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
+{
+ if (lockState == LOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
+ } else if (authParams->len != sizeof(T)) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
+ } else if (*(authParams->data) > MAX_ADV_SLOTS -1) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
+ } else if (authParams->offset != 0) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
+ } else {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+ }
+}
+
+/* READ AUTHORIZTION */
+
+void EddystoneService::readBasicTestLockAuthorizationCallback(GattReadAuthCallbackParams *authParams)
+{
+ LOG(("\r\nDO READ BASIC TEST LOCK slot=%d\r\n", activeSlot));
+ if (lockState == LOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
+ } else {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+ }
+}
+
+void EddystoneService::readEidIdentityAuthorizationCallback(GattReadAuthCallbackParams *authParams)
+{
+ LOG(("\r\nDO READ EID IDENTITY slot=%d\r\n", activeSlot));
+ aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
+ int sum = 0;
+ // Test if the IdentityKey is all zeros for this slot
+ for (uint8_t i = 0; i < sizeof(EidIdentityKey_t); i++) {
+ sum = sum + slotEidIdentityKeys[activeSlot][i];
+ }
+ ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), encryptedEidIdentityKey, sizeof(EidIdentityKey_t));
+
+ // When the array is all zeros, the key has not been set, so return fault
+ if ((lockState == LOCKED) || (sum == 0)) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
+ } else {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+ }
+}
+
+void EddystoneService::readPublicEcdhKeyAuthorizationCallback(GattReadAuthCallbackParams *authParams)
+{
+ LOG(("\r\nDO READ BEACON PUBLIC ECDH KEY (LE) slot=%d\r\n", activeSlot));
+
+ ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), publicEcdhKeyLE, sizeof(PublicEcdhKey_t));
+
+ // When the array is all zeros, the key has not been set, so return fault
+ if (lockState == LOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
+ } else {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+ }
+}
+
+void EddystoneService::readDataAuthorizationCallback(GattReadAuthCallbackParams *authParams)
+{
+ LOG(("\r\nDO READ ADV-DATA : slot=%d\r\n", activeSlot));
+ uint8_t frameType = slotFrameTypes[activeSlot];
+ uint8_t* frame = slotToFrame(activeSlot);
+ uint8_t slotLength = 1;
+ uint8_t buf[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ uint8_t* slotData = buf;
+
+ if (lockState == LOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
+ return;
+ }
+ LOG(("IN READ ADV-DATA AFTER LOCK TEST frameType=%d\r\n", frameType));
+ if (testValidFrame(frame) ) { // Check the frame has valid data before proceeding
+ switch(frameType) {
+ case EDDYSTONE_FRAME_UID:
+ LOG(("READ ADV-DATA UID SLOT DATA slot=%d\r\n", activeSlot));
+ slotLength = uidFrame.getDataLength(frame);
+ slotData = uidFrame.getData(frame);
+ break;
+ case EDDYSTONE_FRAME_URL:
+ LOG(("READ ADV-DATA URL SLOT DATA slot=%d\r\n", activeSlot));
+ slotLength = urlFrame.getDataLength(frame);
+ slotData = urlFrame.getData(frame);
+ break;
+ case EDDYSTONE_FRAME_TLM:
+ LOG(("READ ADV-DATA TLM SLOT DATA slot=%d\r\n", activeSlot));
+ updateRawTLMFrame(frame);
+ slotLength = tlmFrame.getDataLength(frame);
+ slotData = tlmFrame.getData(frame);
+ LOG(("READ ADV-DATA AFTER T/E TLM length=%d\r\n", slotLength));
+ LOG(("Data=")); logPrintHex(slotData, 18);
+ break;
+ case EDDYSTONE_FRAME_EID:
+ LOG(("READ ADV-DATA EID SLOT DATA slot=%d\r\n", activeSlot));
+ slotLength = 14;
+ buf[0] = EIDFrame::FRAME_TYPE_EID;
+ buf[1] = slotEidRotationPeriodExps[activeSlot];
+ // Add time as a big endian 32 bit number
+ uint32_t timeSecs = getTimeSinceFirstBootSecs();
+ buf[2] = (timeSecs >> 24) & 0xff;
+ buf[3] = (timeSecs >> 16) & 0xff;
+ buf[4] = (timeSecs >> 8) & 0xff;
+ buf[5] = timeSecs & 0xff;
+ memcpy(buf + 6, eidFrame.getEid(frame), 8);
+ slotData = buf;
+ break;
+ }
+ }
+ LOG(("IN READ ADV-DATA AFTER FRAME PROCESSING slot=%d\r\n", activeSlot));
+ ble.gattServer().write(advSlotDataChar->getValueHandle(), slotData, slotLength);
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+}
+
+bool EddystoneService::testValidFrame(uint8_t* frame) {
+ return (frame[0] != 0 ) ? true : false;
+}
+
+void EddystoneService::readUnlockAuthorizationCallback(GattReadAuthCallbackParams *authParams)
+{
+ LOG(("\r\nDO READ UNLOCK slot=%d\r\n", activeSlot));
+ if (lockState == UNLOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
+ return;
+ }
+ // Update the challenge ready for the characteristic read
+ generateRandom(challenge, sizeof(Lock_t));
+ aes128Encrypt(unlockKey, challenge, unlockToken);
+ ble.gattServer().write(unlockChar->getValueHandle(), reinterpret_cast<uint8_t *>(challenge), sizeof(Lock_t));
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+}
+
+void EddystoneService::readAdvIntervalAuthorizationCallback(GattReadAuthCallbackParams *authParams)
+{
+ LOG(("\r\nDO READ ADV INTERVAL slot=%d\r\n", activeSlot));
+ if (lockState == LOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
+ return;
+ }
+ uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
+ ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t));
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+}
+
+void EddystoneService::readRadioTxPowerAuthorizationCallback(GattReadAuthCallbackParams *authParams)
+{
+ LOG(("\r\nDO READ RADIO TXPOWER slot=%d\r\n", activeSlot));
+ if (lockState == LOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
+ return;
+ }
+ int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot];
+ ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t));
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+}
+
+void EddystoneService::readAdvTxPowerAuthorizationCallback(GattReadAuthCallbackParams *authParams)
+{
+ LOG(("\r\nDO READ ADV TXPOWER slot=%d\r\n", activeSlot));
+ if (lockState == LOCKED) {
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
+ return;
+ }
+ int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
+ ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t));
+ authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
+}
+
+/*
+ * This callback is invoked when a GATT client attempts to modify any of the
+ * characteristics of this service. Attempts to do so are also applied to
+ * the internal state of this service object.
+ */
+void EddystoneService::onDataWrittenCallback(const GattWriteCallbackParams *writeParams)
+{
+ uint16_t handle = writeParams->handle;
+ LOG(("\r\nDO WRITE: Handle=%d Len=%d\r\n", handle, writeParams->len));
+ // CHAR-1 CAPABILITIES
+ /* capabilitySlotChar is READ ONLY */
+ // CHAR-2 ACTIVE SLOT
+ if (handle == activeSlotChar->getValueHandle()) {
+ LOG(("Write: Active Slot Handle=%d\r\n", handle));
+ uint8_t slot = *(writeParams->data);
+ LOG(("Active Slot=%d\r\n", slot));
+ // Ensure slot does not exceed limit, or set highest slot
+ if (slot < MAX_ADV_SLOTS) {
+ activeSlot = slot;
+ }
+ ble.gattServer().write(activeSlotChar->getValueHandle(), &activeSlot, sizeof(uint8_t));
+ // CHAR-3 ADV INTERVAL
+ } else if (handle == advIntervalChar->getValueHandle()) {
+ LOG(("Write: Interval Handle=%d\r\n", handle));
+ uint16_t interval = correctAdvertisementPeriod(swapEndian(*((uint16_t *)(writeParams->data))));
+ slotAdvIntervals[activeSlot] = interval; // Store this value for reading
+ uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
+ ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t));
+ // CHAR-4 RADIO TX POWER
+ } else if (handle == radioTxPowerChar->getValueHandle()) {
+ LOG(("Write: RADIO Power Handle=%d\r\n", handle));
+ int8_t radioTxPower = *(writeParams->data);
+ uint8_t index = radioTxPowerToIndex(radioTxPower);
+ radioTxPower = radioTxPowerLevels[index]; // Power now corrected to nearest allowed power
+ slotRadioTxPowerLevels[activeSlot] = radioTxPower; // Store by slot number
+ int8_t advTxPower = advTxPowerLevels[index]; // Determine adv power equivalent
+ slotAdvTxPowerLevels[activeSlot] = advTxPower;
+ setFrameTxPower(activeSlot, advTxPower); // Set the actual frame radio TxPower for this slot
+ ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t));
+ // CHAR-5 ADV TX POWER
+ } else if (handle == advTxPowerChar->getValueHandle()) {
+ LOG(("Write: ADV Power Handle=%d\r\n", handle));
+ int8_t advTxPower = *(writeParams->data);
+ slotAdvTxPowerLevels[activeSlot] = advTxPower;
+ setFrameTxPower(activeSlot, advTxPower); // Update the actual frame Adv TxPower for this slot
+ ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t));
+ // CHAR-6 LOCK STATE
+ } else if (handle == lockStateChar->getValueHandle()) {
+ LOG(("Write: Lock State Handle=%d\r\n", handle));
+ uint8_t newLockState = *(writeParams->data);
+ if ((writeParams->len == sizeof(uint8_t)) || (writeParams->len == sizeof(uint8_t) + sizeof(Lock_t))) {
+ if ((newLockState == LOCKED) || (newLockState == UNLOCKED) || (newLockState == UNLOCKED_AUTO_RELOCK_DISABLED)) {
+ lockState = newLockState;
+ }
+ }
+ if ((newLockState == LOCKED) && (writeParams->len == (sizeof(uint8_t) + sizeof(Lock_t))) ) {
+ // And sets the new secret lock code if present
+ uint8_t encryptedNewKey[sizeof(Lock_t)];
+ uint8_t newKey[sizeof(Lock_t)];
+ memcpy(encryptedNewKey, (writeParams->data)+1, sizeof(Lock_t));
+ // Decrypt the new key
+ aes128Decrypt(unlockKey, encryptedNewKey, newKey);
+ memcpy(unlockKey, newKey, sizeof(Lock_t));
+ }
+ ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(uint8_t));
+ // CHAR-7 UNLOCK
+ } else if (handle == unlockChar->getValueHandle()) {
+ LOG(("Write: Unlock Handle=%d\r\n", handle));
+ // NOTE: Actual comparison with unlock code is done in:
+ // writeUnlockAuthorizationCallback(...) which is executed before this method call.
+ lockState = UNLOCKED;
+ // Regenerate challenge and expected unlockToken for Next unlock operation
+ generateRandom(challenge, sizeof(Lock_t));
+ aes128Encrypt(unlockKey, challenge, unlockToken);
+ // Update Chars
+ ble.gattServer().write(unlockChar->getValueHandle(), reinterpret_cast<uint8_t *>(challenge), sizeof(Lock_t)); // Update the challenge
+ ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(uint8_t)); // Update the lock
+ // CHAR-8 PUBLIC ECDH KEY
+ /* PublicEchdChar is READ ONLY */
+ // CHAR-9 EID INDENTITY KEY
+ /* EidIdentityChar is READ ONLY */
+ // CHAR-10 ADV DATA
+ } else if (handle == advSlotDataChar->getValueHandle()) {
+ LOG(("Write: Adv Slot DATA Handle=%d\r\n", handle));
+ uint8_t* frame = slotToFrame(activeSlot);
+ int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
+ uint8_t writeFrameFormat = *(writeParams->data);
+ uint8_t writeFrameLen = (writeParams->len);
+ uint8_t writeData[34];
+ uint8_t serverPublicEcdhKey[32];
+
+ if (writeFrameLen != 0) {
+ writeFrameLen--; // Remove the Format byte from the count
+ } else {
+ writeFrameFormat = UNDEFINED_FRAME_FORMAT; // Undefined format
+ }
+
+ memcpy(writeData, (writeParams->data) + 1, writeFrameLen);
+ LOG(("ADV Data Write=%d,%d\r\n", writeFrameFormat, writeFrameLen));
+ switch(writeFrameFormat) {
+ case UIDFrame::FRAME_TYPE_UID:
+ if (writeFrameLen == 16) {
+ uidFrame.setData(frame, advTxPower,reinterpret_cast<const uint8_t *>((writeParams->data) + 1));
+ slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_UID;
+ } else if (writeFrameLen == 0) {
+ uidFrame.clearFrame(frame);
+ }
+ break;
+ case URLFrame::FRAME_TYPE_URL:
+ if (writeFrameLen <= 18) {
+ urlFrame.setData(frame, advTxPower, reinterpret_cast<const uint8_t*>((writeParams->data) + 1), writeFrameLen );
+ slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_URL;
+ } else if (writeFrameLen == 0) {
+ urlFrame.clearFrame(frame);
+ }
+ break;
+ case TLMFrame::FRAME_TYPE_TLM:
+ if (writeFrameLen == 0) {
+ updateRawTLMFrame(frame);
+ tlmFrame.setData(frame);
+ int slot = getEidSlot();
+ LOG(("WRITE: Testing if TLM or ETLM=%d\r\n", slot));
+ if (slot != NO_EID_SLOT_SET) {
+ LOG(("WRITE: Configuring ETLM Slot time(S)=%lu\r\n", getTimeSinceFirstBootSecs() ));
+ tlmFrame.encryptData(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], getTimeSinceFirstBootSecs() );
+ }
+ slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_TLM;
+ }
+ break;
+ case EIDFrame::FRAME_TYPE_EID:
+ LOG(("EID Len=%d\r\n", writeFrameLen));
+ if (writeFrameLen == 17) {
+ // Least secure
+ LOG(("EID Insecure branch\r\n"));
+ aes128Decrypt(unlockKey, writeData, slotEidIdentityKeys[activeSlot]);
+ slotEidRotationPeriodExps[activeSlot] = writeData[16]; // index 16 is the exponent
+ ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&writeData), sizeof(EidIdentityKey_t));
+ } else if (writeFrameLen == 33 ) {
+ // Most secure
+ memcpy(serverPublicEcdhKey, writeData, 32);
+ ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&serverPublicEcdhKey), sizeof(PublicEcdhKey_t));
+ LOG(("ServerPublicEcdhKey=")); logPrintHex(serverPublicEcdhKey, 32);
+ slotEidRotationPeriodExps[activeSlot] = writeData[32]; // index 32 is the exponent
+ LOG(("Exponent=%i\r\n", writeData[32]));
+ LOG(("genBeaconKeyRC=%x\r\n", genBeaconKeyRC));
+ LOG(("BeaconPrivateEcdhKey=")); logPrintHex(privateEcdhKey, 32);
+ LOG(("BeaconPublicEcdhKey=")); logPrintHex(publicEcdhKey, 32);
+ LOG(("genECDHShareKey\r\n"));
+ int rc = eidFrame.genEcdhSharedKey(privateEcdhKey, publicEcdhKey, serverPublicEcdhKey, slotEidIdentityKeys[activeSlot]);
+ LOG(("Gen Keys RC = %x\r\n", rc));
+ LOG(("Generated eidIdentityKey=")); logPrintHex(slotEidIdentityKeys[activeSlot], 16);
+ aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
+ LOG(("encryptedEidIdentityKey=")); logPrintHex(encryptedEidIdentityKey, 16);
+ ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&encryptedEidIdentityKey), sizeof(EidIdentityKey_t));
+ } else if (writeFrameLen == 0) {
+ // Reset eidFrame
+ eidFrame.clearFrame(frame);
+ break;
+ } else {
+ break; // Do nothing, this is not a recognized Frame length
+ }
+ // Establish the new frame type
+ slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_EID;
+ nextEidSlot = activeSlot; // This was the last one updated
+ LOG(("update Eid Frame\r\n"));
+ // Generate EID ADV frame packet
+ eidFrame.setData(frame, advTxPower, nullEid);
+ // Fill in the correct EID Value from the Identity Key/exp/clock
+ eidFrame.update(frame, slotEidIdentityKeys[activeSlot], slotEidRotationPeriodExps[activeSlot], getTimeSinceFirstBootSecs() );
+ LOG(("END update Eid Frame\r\n"));
+ break;
+ default:
+ frame[0] = 0; // Frame format unknown so clear the entire frame by writing 0 to its length
+ break;
+ }
+ // Read takes care of setting the Characteristic Value
+ // CHAR-11 FACTORY RESET
+ } else if (handle == factoryResetChar->getValueHandle() && (*((uint8_t *)writeParams->data) != 0)) {
+ LOG(("Write: Factory Reset: Handle=%d\r\n", handle));
+ // Reset params to default values
+ doFactoryReset();
+ // Update all characteristics based on params
+ updateCharacteristicValues();
+ // CHAR-12 REMAIN CONNECTABLE
+ } else if (handle == remainConnectableChar->getValueHandle()) {
+ LOG(("Write: Remain Connectable Handle=%d\r\n", handle));
+ remainConnectable = *(writeParams->data);
+ ble.gattServer().write(remainConnectableChar->getValueHandle(), &remainConnectable, sizeof(uint8_t));
+ }
+
+}
+
+void EddystoneService::setFrameTxPower(uint8_t slot, int8_t advTxPower) {
+ uint8_t* frame = slotToFrame(slot);
+ uint8_t frameType = slotFrameTypes[slot] << 4; // Converting the enum to an actual frame type
+ switch (frameType) {
+ case UIDFrame::FRAME_TYPE_UID:
+ uidFrame.setAdvTxPower(frame, advTxPower);
+ break;
+ case URLFrame::FRAME_TYPE_URL:
+ urlFrame.setAdvTxPower(frame, advTxPower);
+ break;
+ case EIDFrame::FRAME_TYPE_EID:
+ eidFrame.setAdvTxPower(frame, advTxPower);
+ break;
+ }
+}
+
+uint8_t EddystoneService::radioTxPowerToIndex(int8_t txPower) {
+ // NOTE: txPower is an 8-bit signed number
+ uint8_t size = sizeof(PowerLevels_t);
+ // Look for the value in range (or next biggest value)
+ for (uint8_t i = 0; i < size; i++) {
+ if (txPower <= radioTxPowerLevels[i]) {
+ return i;
+ }
+ }
+ return size - 1;
+}
+
+/** AES128 encrypts a 16-byte input array with a key, resulting in a 16-byte output array */
+void EddystoneService::aes128Encrypt(uint8_t key[], uint8_t input[], uint8_t output[]) {
+ mbedtls_aes_context ctx;
+ mbedtls_aes_init(&ctx);
+ mbedtls_aes_setkey_enc(&ctx, key, 8 * sizeof(Lock_t));
+ mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, input, output);
+ mbedtls_aes_free(&ctx);
+}
+
+/** AES128 decrypts a 16-byte input array with a key, resulting in a 16-byte output array */
+void EddystoneService::aes128Decrypt(uint8_t key[], uint8_t input[], uint8_t output[]) {
+ mbedtls_aes_context ctx;
+ mbedtls_aes_init(&ctx);
+ mbedtls_aes_setkey_dec(&ctx, key, 8 * sizeof(Lock_t));
+ mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_DECRYPT, input, output);
+ mbedtls_aes_free(&ctx);
+}
+
+
+
+#ifdef HARDWARE_RANDOM_NUM_GENERATOR
+// Generates a set of random values in byte array[size] based on hardware source
+void EddystoneService::generateRandom(uint8_t ain[], int size) {
+ mbedtls_entropy_context entropy;
+ mbedtls_entropy_init(&entropy);
+ // init entropy source
+ eddystoneRegisterEntropySource(&entropy);
+ mbedtls_ctr_drbg_context ctr_drbg;
+ mbedtls_ctr_drbg_init(&ctr_drbg);
+ mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
+ mbedtls_ctr_drbg_random(&ctr_drbg, ain, size);
+ mbedtls_ctr_drbg_free(&ctr_drbg);
+ mbedtls_entropy_free(&entropy);
+ return;
+}
+#else
+// Generates a set of random values in byte array[size] seeded by the clock(ms)
+void EddystoneService::generateRandom(uint8_t ain[], int size) {
+ int i;
+ // Random seed based on boot time in milliseconds
+ srand(getTimeSinceLastBootMs());
+ for (i = 0; i < size; i++) {
+ ain[i] = rand() % 256;
+ }
+ return;
+}
+#endif
+
+/** Reverse Even sized Array endianess: Big to Little or Little to Big */
+void EddystoneService::swapEndianArray(uint8_t ptrIn[], uint8_t ptrOut[], int size) {
+ int i;
+ for (i = 0; i < size; i++) {
+ ptrOut[i] = ptrIn[size - i - 1];
+ }
+ return;
+}
+
+/** Reverse endianess: Big to Little or Little to Big */
+uint16_t EddystoneService::swapEndian(uint16_t arg) {
+ return (arg / 256) + (arg % 256) * 256;
+}
+
+uint16_t EddystoneService::correctAdvertisementPeriod(uint16_t beaconPeriodIn) const
+{
+ /* Re-map beaconPeriod to within permissible bounds if necessary. */
+ if (beaconPeriodIn != 0) {
+ if (beaconPeriodIn < ble.gap().getMinNonConnectableAdvertisingInterval()) {
+ return ble.gap().getMinNonConnectableAdvertisingInterval();
+ } else if (beaconPeriodIn > ble.gap().getMaxAdvertisingInterval()) {
+ return ble.gap().getMaxAdvertisingInterval();
+ }
+ }
+ return beaconPeriodIn;
+}
+
+void EddystoneService::logPrintHex(uint8_t* a, int len) {
+ for (int i = 0; i < len; i++) {
+ LOG(("%x%x", a[i] >> 4, a[i] & 0x0f ));
+ }
+ LOG(("\r\n"));
+}
+
+void EddystoneService::setRandomMacAddress(void) {
+#ifdef EID_RANDOM_MAC
+ uint8_t macAddress[6]; // 48 bit Mac Address
+ generateRandom(macAddress, 6);
+ macAddress[5] |= 0xc0; // Ensure upper two bits are 11's for Random Add
+ ble.setAddress(BLEProtocol::AddressType::RANDOM_STATIC, macAddress);
+#endif
+}
+
+int EddystoneService::getEidSlot(void) {
+ int eidSlot = NO_EID_SLOT_SET; // by default;
+ for (int i = 0; i < MAX_ADV_SLOTS; i++) {
+ if (slotFrameTypes[nextEidSlot] == EDDYSTONE_FRAME_EID) {
+ eidSlot = nextEidSlot;
+ nextEidSlot = (nextEidSlot-1) % MAX_ADV_SLOTS;
+ break;
+ }
+ nextEidSlot = (nextEidSlot-1) % MAX_ADV_SLOTS; // ensure the slot numbers wrap
+ }
+ return eidSlot;
+}
+
+bool EddystoneService::isLocked(void) {
+ if (lockState == LOCKED) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Time : Stable Storage
+ */
+
+/**
+ * Returns the time since FIRST Boot (Time in Prior Boots + Time since Last Boot) in SECONDS
+ */
+uint32_t EddystoneService::getTimeSinceFirstBootSecs(void) {
+ timeParams.timeSinceLastBoot = getTimeSinceLastBootMs() / 1000;
+ uint32_t totalTimeSinceFirstBoot = timeParams.timeSinceLastBoot + timeParams.timeInPriorBoots;
+ // Timer Overflow condition = 136 years (32 bits in seconds) so no need for wrap check
+ return totalTimeSinceFirstBoot;
+}
+
+/**
+ * Returns the time since last boot in MILLISECONDS
+ * NOTE: This solution is needed as a stopgap until the Timer API is updated to 64-bit
+ */
+uint64_t EddystoneService::getTimeSinceLastBootMs(void) {
+ static uint64_t time64bit = 0;
+ time64bit += timeSinceBootTimer.read_ms();
+ timeSinceBootTimer.reset();
+ return time64bit;
+}
+
+/**
+ * Store only the time params in Pstorage(e.g. NVM), to maintain time between boots
+ * NOTE: Platform-specific implementation for persistence on the nRF5x. Based on the
+ * pstorage module provided by the Nordic SDK.
+ */
+void EddystoneService::nvmSaveTimeParams(void) {
+ LOG(("Time NVM: "));
+ LOG(("PriorBoots=%lu, SinceBoot=%lu\r\n", timeParams.timeInPriorBoots, timeParams.timeSinceLastBoot));
+ saveEddystoneTimeParams(&timeParams);
+}
+
+/*
+ * Establish constant arrays
+ */
+const uint8_t EddystoneService::slotDefaultUids[MAX_ADV_SLOTS][16] = EDDYSTONE_DEFAULT_SLOT_UIDS;
+
+const uint8_t EddystoneService::slotDefaultEidIdentityKeys[MAX_ADV_SLOTS][16] = EDDYSTONE_DEFAULT_SLOT_EID_IDENTITY_KEYS;
+
+const uint8_t EddystoneService::nullEid[8] = {0,0,0,0,0,0,0,0};
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EddystoneService.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,1198 @@
+/*
+ * Copyright (c) 2006-2016 Google Inc, All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EDDYSTONESERVICE_H__
+#define __EDDYSTONESERVICE_H__
+//
+// 2016-03 Eddystone Unified GATT
+//
+#include "EventQueue/EventQueue.h"
+#include "ble/BLE.h"
+#include "EddystoneTypes.h"
+#include "UIDFrame.h"
+#include "URLFrame.h"
+#include "TLMFrame.h"
+#include "EIDFrame.h"
+#include <string.h>
+#include "mbedtls/aes.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+// #include "PersistentStorageHelper/ConfigParamsPersistence.h"
+
+#ifdef YOTTA_CFG_MBED_OS
+ #include "mbed-drivers/mbed.h"
+ #include "mbed-drivers/CircularBuffer.h"
+#else
+ #include "mbed.h"
+ #include "CircularBuffer.h"
+#endif
+
+#include "stdio.h"
+#include "Eddystone_config.h"
+#include "pstorage_platform.h"
+
+/**
+ * This class implements the Eddystone-URL Config Service and the Eddystone
+ * Protocol Specification as defined in the publicly available specification at
+ * https://github.com/google/eddystone/blob/master/protocol-specification.md.
+ */
+class EddystoneService
+{
+public:
+ /**
+ * Total number of GATT Characteristics in the Eddystonei-URL Configuration
+ * Service.
+ */
+ static const uint16_t TOTAL_CHARACTERISTICS = 12;
+
+ /**
+ * Max data that can be written to the data characteristic
+ */
+ static const uint8_t MAX_DATA_WRITE = 34; // FrameType+32B(IdentityKey)+Exp
+
+ /**
+ * Default interval for advertising packets for the Eddystone-URL
+ * Configuration Service.
+ */
+ static const uint32_t DEFAULT_CONFIG_PERIOD_MSEC = EDDYSTONE_DEFAULT_CONFIG_ADV_INTERVAL;
+
+ /**
+ * Enumeration that defines the various operation modes of the
+ * EddystoneService.
+ *
+ * @note The main app can change the mode of EddystoneService at any point
+ * of time by calling startConfigService() or startBeaconService().
+ * Resources from the previous mode will be freed.
+ *
+ * @note It is currently NOT possible to force EddystoneService back into
+ * EDDYSTONE_MODE_NONE.
+ */
+ enum OperationModes {
+ /**
+ * NONE: EddystoneService has been initialized but no memory has been
+ * dynamically allocated. Additionally, no services are running
+ * nothing is being advertised.
+ */
+ EDDYSTONE_MODE_NONE,
+ /**
+ * CONFIG: EddystoneService has been initialized, the configuration
+ * service started and memory has been allocated for BLE
+ * characteristics. Memory consumption peaks during CONFIG
+ * mode.
+ */
+ EDDYSTONE_MODE_CONFIG,
+ /**
+ * BEACON: Eddystone service is running as a beacon advertising URL,
+ * UID and/or TLM frames depending on how it is configured.
+ */
+ EDDYSTONE_MODE_BEACON
+ };
+
+ /**
+ * Structure that encapsulates the Eddystone configuration parameters. This
+ * structure is particularly useful when storing the parameters to
+ * persistent storage.
+ */
+ struct EddystoneParams_t {
+ /**
+ *
+ */
+ TimeParams_t timeParams;
+ /**
+ * A buffer describing the capabilities of the beacon
+ */
+ Capability_t capabilities;
+
+ /**
+ * Defines the slot that advInterval, radioPower, advPower, advSlotData operate on
+ */
+ uint8_t activeSlot;
+
+ /**
+ * The Beacon interval for each beacon slot
+ *
+ * @note A value of zero disables Eddystone-URL frame trasmissions.
+ */
+ SlotAdvIntervals_t slotAdvIntervals;
+
+ /**
+ * The Radio TX Powers supported by this beacon
+ */
+ PowerLevels_t radioTxPowerLevels;
+
+ /**
+ * The Radio TX Power set for each slot
+ */
+ SlotTxPowerLevels_t slotRadioTxPowerLevels;
+
+ /**
+ * The Calibrated Adv TX Powers supported by this beacon (one for each radio power)
+ */
+ PowerLevels_t advTxPowerLevels;
+
+ /**
+ * The Adv TX Power set for each slot
+ */
+ SlotTxPowerLevels_t slotAdvTxPowerLevels;
+
+ /**
+ * The value of the Eddystone-URL Configuration Service Lock State
+ * characteristic.
+ */
+ uint8_t lockState;
+
+ /**
+ * The value of the Eddystone-URL Configuration Service Unlock
+ * characteristic that can be used to unlock the beacon and clear the
+ * single-use lock-code.
+ */
+ Lock_t unlockToken;
+
+ /**
+ * An array holding the 128-bit unlockKey (big endian)
+ */
+ Lock_t unlockKey;
+
+ /**
+ * An array holding the 128-bit challenge (big endian) in the
+ * challenge/response unlock protocol
+ */
+ Lock_t challenge;
+
+ /**
+ * EID: An array holding the slot rotation period exponents
+ */
+ SlotEidRotationPeriodExps_t slotEidRotationPeriodExps;
+
+ /**
+ * EID: An array holding the slot 128-bit EID Identity Key (big endian)
+ */
+ SlotEidIdentityKeys_t slotEidIdentityKeys;
+
+ /**
+ * Specifies the type of each frame indexed by slot
+ */
+ SlotFrameTypes_t slotFrameTypes;
+
+ /**
+ * A buffer that contains all slot frames, 32-bytes allocated to each frame
+ */
+ SlotStorage_t slotStorage;
+
+ /**
+ * The state of the recently invoked Factory Reset characteristic
+ */
+ uint8_t factoryReset;
+
+ /**
+ * The state of the recently invoked Remain Connectable characteristic
+ */
+ uint8_t remainConnectable;
+ };
+
+ /**
+ * Enumeration that defines the various error codes for EddystoneService.
+ */
+ enum EddystoneError_t {
+ /**
+ * No error occurred.
+ */
+ EDDYSTONE_ERROR_NONE,
+ /**
+ * The supplied advertising interval is invalid. The interval may be
+ * too short/long for the type of advertising packets being broadcast.
+ *
+ * @note For the acceptable range of advertising interval refer to the
+ * following functions in mbed BLE API:
+ * - Gap::getMinNonConnectableAdvertisingInterval()
+ * - Gap::getMinAdvertisingInterval()
+ * - Gap::getMaxAdvertisingInterval()
+ */
+ EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL,
+ /**
+ * The result of executing a call when the the EddystoneService is in
+ * the incorrect operation mode.
+ */
+ EDDYSTONE_ERROR_INVALID_STATE
+ };
+
+ /**
+ * Enumeration that defines the available frame types within Eddystone
+ * advertising packets.
+ */
+ enum FrameType {
+ /**
+ * The Eddystone-UID frame. Refer to
+ * https://github.com/google/eddystone/tree/master/eddystone-uid.
+ */
+ EDDYSTONE_FRAME_UID,
+ /**
+ * The Eddystone-URL frame. Refer to
+ * https://github.com/google/eddystone/tree/master/eddystone-url.
+ */
+ EDDYSTONE_FRAME_URL,
+ /**
+ * The Eddystone-TLM frame. Refer to
+ * https://github.com/google/eddystone/tree/master/eddystone-tlm.
+ */
+ EDDYSTONE_FRAME_TLM,
+ /**
+ * The Eddystone-EID frame. Refer to
+ * https://github.com/google/eddystone/tree/master/eddystone-eid.
+ */
+ EDDYSTONE_FRAME_EID,
+ /**
+ * The total number Eddystone frame types.
+ */
+ NUM_EDDYSTONE_FRAMES
+ };
+
+ typedef eq::EventQueue event_queue_t;
+
+ /**
+ * Constructor that Initializes the EddystoneService using parameters from
+ * the supplied EddystoneParams_t. This constructor is particularly useful
+ * for configuring the EddystoneService with parameters fetched from
+ * persistent storage.
+ *
+ * @param[in] bleIn
+ * The BLE instance.
+ * @param[in] paramIn
+ * The input Eddystone configuration parameters.
+ * @param[in] radioPowerLevelsIn
+ * The value set internally into the radion tx power.
+ * @param[in] eventQueue
+ * The event queue used by the service to schedule tasks.
+ * @param[in] advConfigIntervalIn
+ * The advertising interval for advertising packets of the
+ * Eddystone-URL Configuration Service.
+ */
+ EddystoneService(BLE &bleIn,
+ EddystoneParams_t ¶msIn,
+ const PowerLevels_t &radioPowerLevelsIn,
+ event_queue_t &eventQueue,
+ uint32_t advConfigIntervalIn = DEFAULT_CONFIG_PERIOD_MSEC);
+
+ /**
+ * Constructor to initialize the EddystoneService to default values.
+ *
+ * @param[in] bleIn
+ * The BLE instance.
+ * @param[in] advPowerLevelsIn
+ * The value of the Eddystone-URL Configuration Service TX
+ * Power Mode characteristic.
+ * @param[in] radioPowerLevelsIn
+ * The value set internally into the radion tx power.
+ * @param[in] eventQueue
+ * The event queue used by the service to schedule tasks.
+ * @param[in] advConfigIntervalIn
+ * The advertising interval for advertising packets of the
+ * Eddystone-URL Configuration Service.
+ *
+ * @note When using this constructor the setURLData(), setTMLData() and
+ * setUIDData() and setEIDData() functions must be called to initialize
+ * EddystoneService manually.
+ */
+ EddystoneService(BLE &bleIn,
+ const PowerLevels_t &advPowerLevelsIn,
+ const PowerLevels_t &radioPowerLevelsIn,
+ event_queue_t &eventQueue,
+ uint32_t advConfigIntervalIn = DEFAULT_CONFIG_PERIOD_MSEC);
+
+
+ /**
+ * Generate the EID Beacon Random ECHD Keys (private and Public)
+ */
+ void genEIDBeaconKeys(void);
+
+ /**
+ * Factory Reset all parameters in the beacon
+ */
+ void doFactoryReset(void);
+
+ /**
+ * Setup callback to update BatteryVoltage in Eddystone-TLM frames
+ *
+ * @param[in] tlmBatteryVoltageCallbackIn
+ * The callback being registered.
+ */
+ void onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn);
+
+ /**
+ * Setup callback to update BeaconTemperature in Eddystone-TLM frames
+ *
+ * @param[in] tlmBeaconTemperatureCallbackIn
+ * The callback being registered.
+ */
+ void onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn);
+
+ /**
+ * Change the EddystoneService OperationMode to EDDYSTONE_MODE_CONFIG.
+ *
+ * @retval EDDYSTONE_ERROR_NONE if the operation succeeded.
+ * @retval EDDYSONE_ERROR_INVALID_ADVERTISING_INTERVAL if the configured
+ * advertising interval is zero.
+ *
+ * @note If EddystoneService was previously in EDDYSTONE_MODE_BEACON, then
+ * the resources allocated to that mode of operation such as memory
+ * are freed and the BLE instance shutdown before the new operation
+ * mode is configured.
+ */
+ EddystoneError_t startConfigService(void);
+
+ /**
+ * Change the EddystoneService to start transmitting Eddystone beacons
+ * operationMode = EDDYSTONE_MODE_BEACON
+ *
+ * @retval EDDYSTONE_ERROR_NONE if the operation succeeded.
+ * @retval EDDYSONE_ERROR_INVALID_ADVERTISING_INTERVAL if the configured
+ * advertising interval is zero.
+ *
+ * @note If EddystoneService was previously in EDDYSTONE_MODE_CONFIG, then
+ * the resources allocated to that mode of operation such as memory
+ * are freed and the BLE instance shutdown before the new operation
+ * mode is configured.
+ */
+ EddystoneError_t startEddystoneBeaconAdvertisements(void);
+
+ /**
+ * Set the Comple Local Name for the BLE device. This not only updates
+ * the value of the Device Name Characteristic, it also updates the scan
+ * response payload if the EddystoneService is currently in
+ * EDDYSTONE_MODE_CONFIG.
+ *
+ * @param[in] deviceNameIn
+ * A pointer to a null terminated string containing the new
+ * device name.
+ *
+ * @return BLE_ERROR_NONE if the name was successfully set. Otherwise an
+ * appropriate error.
+ *
+ * @note EddystoneService does not make an internal copy of the string
+ * pointed to by @p deviceNameIn. Therefore, the user is responsible
+ * for ensuring that the string persists in memory as long as it is
+ * in use by the EddystoneService.
+ *
+ * @note The device name is not considered an Eddystone configuration
+ * parameter; therefore, it is not contained within the
+ * EddystoneParams_t structure and must be stored to persistent
+ * storage separately.
+ */
+ ble_error_t setCompleteDeviceName(const char *deviceNameIn);
+
+ /**
+ * Get the Eddystone Configuration parameters. This is particularly useful
+ * for storing the configuration parameters in persistent storage.
+ * It is not the responsibility of the Eddystone implementation to store
+ * the configured parameters in persistent storage since this is
+ * platform-specific.
+ *
+ * @param[out] params
+ * A reference to an EddystoneParams_t structure with the
+ * configured parameters of the EddystoneService.
+ */
+ void getEddystoneParams(EddystoneParams_t ¶ms);
+
+ /**
+ * Start advertising packets indicating the Eddystone Configuration state
+ * operationMode = EDDYSTONE_MODE_CONFIG
+ */
+ EddystoneService::EddystoneError_t startEddystoneConfigAdvertisements(void);
+
+ /**
+ * Free the resources acquired by a call to setupBeaconService() and
+ * cancel all pending callbacks that operate the radio and frame queue.
+ *
+ * @note This call will not modify the current state of the BLE device.
+ * EddystoneService::stopBeaconService should only be called after
+ * a call to BLE::shutdown().
+ */
+ void stopEddystoneBeaconAdvertisements(void);
+
+ /**
+ * Initialize and start the BLE Eddystone Configuration Service
+ * This will create the 12-characteristics of the service and make them
+ * available when a client connects
+ */
+ void startEddystoneConfigService();
+
+ /**
+ * Stops the Eddystone Configuration Service and frees its resources
+ * and cancels all pending callbacks that operate the radio and frame queue.
+ *
+ * @note This call will not modify the current state of the BLE device.
+ * EddystoneService::stopBeaconService should only be called after
+ * a call to BLE::shutdown().
+ */
+ void stopEddystoneConfigService();
+
+ /**
+ * Tests if the beacon is locked or not
+ *
+ * @return bool
+ */
+ bool isLocked();
+
+ /**
+ * Print an array as a set of hex values
+ *
+ * @param[in] a
+ * The array to be printed.
+ *
+ * @param[in] len
+ * The length of the array.
+ *
+ * @return void
+ *
+ */
+ static void logPrintHex(uint8_t* a, int len);
+
+ /**
+ * Swaps the endianess of an array ptrIn[size] to ptrOut[size]
+ *
+ * @param[in] *ptrIn
+ * The input array
+ * @param[in] *ptrOut
+ * The output array
+ * @param[in] size
+ * The sizes of the arrays (num bytes to be reversed)
+ */
+ static void swapEndianArray(uint8_t *ptrIn, uint8_t *ptrOut, int size);
+
+ /**
+ * Generate a random array of bytes of length size
+ *
+ * @param[in] *ain
+ * The input/output array
+ * @param[in] size
+ * The size of the array in bytes
+ */
+ static void generateRandom(uint8_t *ain, int size);
+
+ /**
+ * Timer that keeps track of the time since boot.
+ */
+ static Timer timeSinceBootTimer;
+
+private:
+
+ static const uint8_t NO_EID_SLOT_SET = 0xff;
+
+ static const uint8_t UNDEFINED_FRAME_FORMAT = 0xff;
+
+ static const uint8_t REMAIN_CONNECTABLE_SET = 0x01;
+
+ static const uint8_t REMAIN_CONNECTABLE_UNSET = 0x00;
+
+ static const uint8_t CONFIG_FRAME_HDR_LEN = 4;
+
+ /**
+ * Helper funtion that will be registered as an initialization complete
+ * callback when BLE::shutdown() is called. This is necessary when changing
+ * Eddystone OperationModes. Once the BLE initialization is complete, this
+ * callback will initialize all the necessary resource to operate
+ * Eddystone service in the selected mode.
+ *
+ * @param[in] initContext
+ * The context provided by BLE API when initialization
+ * completes.
+ */
+ void bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext);
+
+ /**
+ * When in EDDYSTONE_MODE_BEACON this function is called to update the
+ * advertising payload to contain the information related to the specified
+ * FrameType.
+ *
+ * @param[in] slot
+ * The slot to populate the advertising payload with.
+ */
+ void swapAdvertisedFrame(int slot);
+
+ /**
+ * Helper function that manages the BLE radio that is used to broadcast
+ * advertising packets. To advertise frames at the configured intervals
+ * the actual advertising interval of the BLE instance is set to the value
+ * returned by Gap::getMaxAdvertisingInterval() from the BLE API. When a
+ * frame needs to be advertised, the enqueueFrame() callbacks add the frame
+ * type to the advFrameQueue and post a manageRadio() callback. When the
+ * callback is executed, the frame is dequeued and advertised using the
+ * radio (by updating the advertising payload). manageRadio() also posts a
+ * callback to itself Gap::getMinNonConnectableAdvertisingInterval()
+ * milliseconds later. In this callback, manageRadio() will advertise the
+ * next frame in the queue, yet if there is none it calls
+ * Gap::stopAdvertising() and does not post any further callbacks.
+ */
+ void manageRadio(void);
+
+ /**
+ * Regular callbacks posted at the rate of slotAdvPeriod[slot] milliseconds
+ * enqueue frames to be advertised. If the
+ * frame queue is currently empty, then this function directly calls
+ * manageRadio() to broadcast the required FrameType.
+ *
+ * @param[in] frameType
+ * The FrameType to enqueue for broadcasting.
+ */
+ void enqueueFrame(int slot);
+
+ /**
+ * Helper function that updates the advertising payload when in
+ * EDDYSTONE_MODE_BEACON to contain a new frame.
+ *
+ * @param[in] rawFrame
+ * The raw bytes of the frame to advertise.
+ * @param[in] rawFrameLength
+ * The length in bytes of the array pointed to by @p rawFrame.
+ */
+ void updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength);
+
+ /**
+ * Helper function that updates the information in the Eddystone-TLM frames
+ * Internally, this function executes the registered callbacks to update
+ * beacon Battery Voltage and Temperature (if available). Furthermore, this
+ * function updates the raw frame data. This operation must be done fairly
+ * often because the Eddystone-TLM frame Time Since Boot must have a 0.1
+ * seconds resolution according to the Eddystone specification.
+ */
+ void updateRawTLMFrame(uint8_t* frame);
+
+ /**
+ * Calculate the Frame pointer from the slot number
+ */
+ uint8_t* slotToFrame(int slot);
+
+ /**
+ * Free the characteric resources acquired by a call to
+ * startEddystoneConfigService().
+ */
+ void freeConfigCharacteristics(void);
+
+ /**
+ * Helper function used to update the GATT database following any
+ * change to the internal state of the service object.
+ */
+ void updateCharacteristicValues(void);
+
+ /**
+ * Helper function to setup the payload of scan response packets for
+ * Eddystone-URL Configuration Service.
+ */
+ void setupEddystoneConfigScanResponse(void);
+
+ /**
+ * Callback registered to the BLE API to authorize write operations to the
+ * Eddystone Configuration Service Lock characteristic.
+ *
+ * @param[in] authParams
+ * Write authentication information.
+ */
+ void writeLockAuthorizationCallback(GattWriteAuthCallbackParams *authParams);
+
+ /**
+ * Callback registered to the BLE API to authorize write operations to the
+ * Eddystone Configuration Service Unlock characteristic.
+ *
+ * @param[in] authParams
+ * Write authentication information.
+ */
+ void writeUnlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams);
+
+ /**
+ * Callback registered to the BLE API to authorize write operations to the
+ * Eddystone Configuration Service advSlotData characteristic.
+ *
+ * @param[in] authParams
+ * Write authentication information.
+ */
+ void writeVarLengthDataAuthorizationCallback(GattWriteAuthCallbackParams *authParams);
+
+ /**
+ * Callback registered to the BLE API to authorize write operations to the
+ * lockState characteristic which can be 1 byte or 17 bytes long.
+ *
+ * @param[in] authParams
+ * Write authentication information.
+ */
+ void writeLockStateAuthorizationCallback(GattWriteAuthCallbackParams *authParams);
+
+ /**
+ * Callback registered to the BLE API to authorize write operations to simple fixed length
+ * value characteristic types.
+ *
+ * @param[in] authParams
+ * Write authentication information.
+ */
+ template <typename T>
+ void writeBasicAuthorizationCallback(GattWriteAuthCallbackParams *authParams);
+
+ /**
+ * This callback is invoked when a GATT client attempts to write to the
+ * Active Slot characteristic of the service.
+ *
+ * @param[in] authParams
+ * Information about the values that are being read.
+ */
+ template <typename T>
+ void writeActiveSlotAuthorizationCallback(GattWriteAuthCallbackParams *authParams);
+
+ /**
+ * READ AUTHORIZATIONS
+ */
+
+ /**
+ * This callback is invoked when a GATT client attempts to read from a
+ * basic characteristic of the Eddystone Configuration Service, which
+ * is blocked if the beacon lock is set to LOCKED.
+ *
+ * @param[in] authParams
+ * Information about the values that are being read.
+ */
+ void readBasicTestLockAuthorizationCallback(GattReadAuthCallbackParams *authParams);
+
+ /**
+ * This callback is invoked when a GATT client attempts to read from the
+ * EidIdentityKey characteristic of the Eddystone Configuration Service,
+ * which is blocked if the beacon lock is set to LOCKED, or the key has not
+ * been set/initialized.
+ *
+ * @param[in] authParams
+ * Information about the values that are being read.
+ */
+ void readEidIdentityAuthorizationCallback(GattReadAuthCallbackParams *authParams);
+
+ /**
+ * This callback is invoked when a GATT client attempts to read from the
+ * PublicEcdhKey characteristic of the Eddystone Configuration Service,
+ * which is blocked if the beacon lock is set to LOCKED, or the key has not
+ * been set/initialized.
+ *
+ * @param[in] authParams
+ * Information about the values that are being read.
+ */
+ void readPublicEcdhKeyAuthorizationCallback(GattReadAuthCallbackParams *authParams);
+
+
+ /**
+ * This callback is invoked when a GATT client attempts to read from the
+ * Adv Slot Data characteristic of the Eddystone Configuration Service,
+ * which isblocked if the beacon lock is set to LOCKED.
+ *
+ * @param[in] authParams
+ * Information about the values that are being read.
+ */
+ void readDataAuthorizationCallback(GattReadAuthCallbackParams *authParams);
+
+ /**
+ * Checks if this is valid frame data (i.e. length > 0)
+ *
+ * @param[in] frame
+ * The frame being tested
+ * @returns frame is valid or not.
+ */
+ bool testValidFrame(uint8_t* frame);
+
+ /**
+ * This callback is invoked when a GATT client attempts to read the challenge
+ * from the Unlock characteristic of the Eddystone Configuration Service,
+ * which is blocked if the beacon lock is set to UNLOCKED.
+ *
+ * @param[in] authParams
+ * Information about the values that are being read.
+ */
+ void readUnlockAuthorizationCallback(GattReadAuthCallbackParams *authParams);
+
+ /**
+ * This callback is invoked when a GATT client attempts to read from the
+ * Radio Tx Power characteristic of the Eddystone Configuration Service,
+ * which is blocked if the beacon lock is set to LOCKED.
+ *
+ * @param[in] authParams
+ * Information about the values that are being read.
+ */
+ void readRadioTxPowerAuthorizationCallback(GattReadAuthCallbackParams *authParams);
+
+ /**
+ * This callback is invoked when a GATT client attempts to read from the
+ * Radio Tx Power characteristic of the Eddystone Configuration Service,
+ * which is blocked if the beacon lock is set to LOCKED.
+ *
+ * @param[in] authParams
+ * Information about the values that are being read.
+ */
+ void readAdvTxPowerAuthorizationCallback(GattReadAuthCallbackParams *authParams);
+
+ /**
+ * This callback is invoked when a GATT client attempts to read from the
+ * Adv Interval characteristic of the Eddystone Configuration Service,
+ * which is blocked if the beacon lock is set to LOCKED.
+ *
+ * @param[in] authParams
+ * Information about the values that are being read.
+ */
+ void readAdvIntervalAuthorizationCallback(GattReadAuthCallbackParams *authParams);
+
+ /**
+ * Calculates the index in the radio power levels array which can be used
+ * to index into the adv power levels array to find the calibrated adv power
+ * used in the adv frame.
+ */
+ uint8_t radioTxPowerToIndex(int8_t txPower);
+
+ /**
+ * This callback is invoked when a GATT client attempts to modify any of the
+ * characteristics of this service. Attempts to do so are also applied to
+ * the internal state of this service object.
+ *
+ * @param[in] writeParams
+ * Information about the values that are being written.
+ */
+ void onDataWrittenCallback(const GattWriteCallbackParams *writeParams);
+
+ /**
+ * Sets the power for the frame in a particular slot using the
+ * adv tx power parmeter
+ *
+ * @param[in] slot
+ * The the current slot number being considered
+ * @param[in] advTxPower
+ * The adv power required in a frame
+ */
+ void setFrameTxPower(uint8_t slot, int8_t advTxPower);
+
+ /**
+ * AES128 ECB Encrypts a 16-byte input array with a key, to an output array
+ *
+ * @param[in] *key
+ * The encryption key
+ * @param[in] *input
+ * The input array
+ * @param[in] *output
+ * The output array (contains the encrypted data)
+ */
+ void aes128Encrypt(uint8_t *key, uint8_t *input, uint8_t *output);
+
+ /**
+ * AES128 ECB Deccrypts a 16-byte input array with a key, to an output array
+ *
+ * @param[in] *key
+ * The decryption key
+ * @param[in] *input
+ * The input array
+ * @param[in] *output
+ * The output array (containing the decrypted data)
+ */
+ void aes128Decrypt(uint8_t *key, uint8_t *input, uint8_t *output);
+
+
+
+ /**
+ * Swaps the endianess of a 16-bit unsigned int
+ *
+ * @param[in] arg
+ * The value with the byte order to be reversed
+ *
+ * @return The resulting 16-bit value with byte order reversed
+ */
+ uint16_t swapEndian(uint16_t arg);
+
+ /**
+ * Correct the advertising interval for non-connectable packets.
+ *
+ * @param[in] beaconPeriodIn
+ * The input interval in milliseconds.
+ *
+ * @return The corrected interval in milliseconds.
+ *
+ * @note For the acceptable range of advertising interval refer to the
+ * following functions in mbed BLE API:
+ * - Gap::getMinNonConnectableAdvertisingInterval()
+ * - Gap::getMaxAdvertisingInterval()
+ */
+ uint16_t correctAdvertisementPeriod(uint16_t beaconPeriodIn) const;
+
+ /**
+ * Swaps the endianess of a 16-bit unsigned int
+ *
+ * @param[in] arg
+ * The value with the byte order to be reversed
+ *
+ * @return The resulting 16-bit value with byte order reversed
+ */
+ void setRandomMacAddress(void);
+
+ /**
+ * Finds the first EID slot set
+ *
+ * @return slot number (and if not, returns NO_EID_SLOT_SET = -1)
+ */
+ int getEidSlot(void);
+
+ /**
+ * Returns the current time in Secs (Prior Time + Time since boot)
+ *
+ * @return time
+ */
+ uint32_t getTimeSinceFirstBootSecs(void);
+
+
+ /**
+ * Returns the time since boot in Milliseconds
+ *
+ * @return time
+ */
+ static uint64_t getTimeSinceLastBootMs(void);
+
+ /**
+ * Saves only the Time Params in pStorage (a subset of all the Eddsytone Params)
+ * This is more efficient than periodically saving all state (its just 8 bytes)
+ */
+ void nvmSaveTimeParams(void);
+
+ /**
+ * BLE instance that EddystoneService will operate on.
+ */
+ BLE &ble;
+
+ /**
+ * The advertising interval for Eddystone-URL Config Service advertising
+ * packets.
+ */
+ uint32_t advConfigInterval;
+ /**
+ * Current EddystoneServce operation mode.
+ */
+ uint8_t operationMode;
+
+ /**
+ * Parameter to consistently record the return code when generating Beacon Keys
+ */
+ int genBeaconKeyRC;
+
+ /**
+ * Keeps track of time in prior boots and current/last boot
+ */
+ TimeParams_t timeParams;
+
+ /**
+ * GATT Service Variables
+ */
+
+ /**
+ * An array describing the capabilites of the beacon.
+ */
+ Capability_t capabilities;
+
+ /**
+ * The currenty defined active slot.
+ */
+ uint8_t activeSlot;
+
+ /**
+ * An array containing all the adv intervals for each slot index
+ */
+ SlotAdvIntervals_t slotAdvIntervals;
+
+ /**
+ * The value of the Eddystone Configuration Service radioTX Power
+ * characteristic.
+ */
+ SlotTxPowerLevels_t slotRadioTxPowerLevels;
+
+ /**
+ * An array containing the supported radio tx power levels for this beacon
+ */
+ PowerLevels_t radioTxPowerLevels;
+
+ /**
+ * An array containing all possible values for advertised tx power in Eddystone
+ * slots.
+ */
+ SlotTxPowerLevels_t slotAdvTxPowerLevels;
+
+ /**
+ * An array containing the supported adv tx power levels for this beacon
+ */
+ PowerLevels_t advTxPowerLevels;
+
+ /**
+ * The value of the Eddystone Configuration Service Lock State
+ * characteristic.
+ */
+ uint8_t lockState;
+
+
+ /**
+ * The value of the Eddystone Configuration Service Lock State
+ * buffer
+ */
+ LockState_t lockStateBuf;
+
+ /**
+ * The value of the Eddystone Configuration Service unlock key
+ */
+ Lock_t unlockKey;
+
+ /**
+ * The value of the Eddystone Configuration Service unlock challenge
+ */
+ Lock_t challenge;
+
+ /**
+ * The value of the Eddystone Configuration Service unlock token. A write
+ * to the unlock characteristic must contain this token to unlock the beacon
+ */
+ Lock_t unlockToken;
+
+
+ /**
+ * EID: An array holding the 256-bit private Ecdh Key (big endian)
+ */
+ PrivateEcdhKey_t privateEcdhKey;
+
+ /**
+ * EID: An array holding the 256-bit public Ecdh Key (big endian)
+ */
+ PublicEcdhKey_t publicEcdhKey;
+
+ /**
+ * EID: An array holding the 256-bit public Ecdh Key (little endian)
+ */
+ PublicEcdhKey_t publicEcdhKeyLE;
+
+ /**
+ * EID: An array holding the slot rotation period exponents
+ */
+ SlotEidRotationPeriodExps_t slotEidRotationPeriodExps;
+
+ /**
+ * EID: An array holding the slot Eid Identity Keys
+ */
+ SlotEidIdentityKeys_t slotEidIdentityKeys;
+
+ /**
+ * EID: An array holding the slot Eid Public Ecdh Keys
+ */
+ //SlotEidPublicEcdhKeys_t slotEidPublicEcdhKeys;
+
+ /**
+ * Instance of the UID frame.
+ */
+ UIDFrame uidFrame;
+
+ /**
+ * Instance of the URL frame.
+ */
+ URLFrame urlFrame;
+
+ /**
+ * Instance of the TLM frame.
+ */
+ TLMFrame tlmFrame;
+
+ /**
+ * Instance of the EID frame.
+ */
+ EIDFrame eidFrame;
+
+ /**
+ * The value of the Eddystone Configuration Service reset
+ * characteristic.
+ */
+ uint8_t factoryReset;
+
+ /**
+ * The value of the Eddystone Configuration Service Remain Connectable
+ * characteristic.
+ */
+ uint8_t remainConnectable;
+
+ /**
+ * CHARACTERISTIC STORAGE
+ */
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone
+ * Configuration Service Capabilities characteristic.
+ */
+ ReadOnlyArrayGattCharacteristic<uint8_t, sizeof(Capability_t)> *capabilitiesChar;
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone
+ * Configuration Service Active Slot characteristic.
+ */
+ ReadWriteGattCharacteristic<uint8_t> *activeSlotChar;
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone
+ * Configuration Service Adv Interval characteristic.
+ */
+ ReadWriteGattCharacteristic<uint16_t> *advIntervalChar;
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone
+ * Configuration Service Radio Tx Power characteristic.
+ */
+ ReadWriteGattCharacteristic<int8_t> *radioTxPowerChar;
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone
+ * Configuration Service Adv Tx Power characteristic.
+ */
+ ReadWriteGattCharacteristic<int8_t> *advTxPowerChar;
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone
+ * Configuration Service Lock State characteristic.
+ */
+ GattCharacteristic *lockStateChar;
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone
+ * Configuration Service Unlock characteristic.
+ */
+ ReadWriteArrayGattCharacteristic<uint8_t, sizeof(Lock_t)> *unlockChar;
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone
+ * Configuration Service Public ECDH Key characteristic.
+ */
+ GattCharacteristic *publicEcdhKeyChar;
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone-URL
+ * Configuration Service EID Identity Key characteristic.
+ */
+ GattCharacteristic *eidIdentityKeyChar;
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone-URL
+ * Configuration Service Adv Slot Data characteristic.
+ */
+ GattCharacteristic *advSlotDataChar;
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone-URL
+ * Configuration Service Factory Reset characteristic.
+ */
+ WriteOnlyGattCharacteristic<uint8_t> *factoryResetChar;
+
+ /**
+ * Pointer to the BLE API characteristic encapsulation for the Eddystone-GATT
+ * Configuration Service Remain Connectable characteristic.
+ */
+ ReadWriteGattCharacteristic<uint8_t> *remainConnectableChar;
+
+ /**
+ * END OF GATT CHARACTERISTICS
+ */
+
+ /**
+ * EID: An array holding the slot next rotation times
+ */
+ SlotEidNextRotationTimes_t slotEidNextRotationTimes;
+
+ /**
+ * EID: Storage for the current slot encrypted EID Identity Key
+ */
+ EidIdentityKey_t encryptedEidIdentityKey;
+
+ /*
+ * Storage for all the slots / frames
+ */
+ SlotStorage_t slotStorage;
+
+ /**
+ * An array that defines the frame type of each slot using the slot number
+ * as an index.
+ */
+ SlotFrameTypes_t slotFrameTypes;
+
+ /**
+ * Circular buffer that represents of Eddystone frames to be advertised.
+ */
+ CircularBuffer<uint8_t, MAX_ADV_SLOTS> advFrameQueue;
+
+ /**
+ * The registered callback to update the Eddystone-TLM frame Battery
+ * Voltage.
+ */
+ TlmUpdateCallback_t tlmBatteryVoltageCallback;
+
+ /**
+ * The registered callback to update the Eddystone-TLM frame Beacon
+ * Temperature.
+ */
+ TlmUpdateCallback_t tlmBeaconTemperatureCallback;
+
+ /**
+ * Type for the array of callback handles for all the slot timers
+ */
+ typedef event_queue_t::event_handle_t SlotCallbackHandles_t[MAX_ADV_SLOTS];
+
+ /**
+ * An array of all the slot timer callbacks handles
+ */
+ SlotCallbackHandles_t slotCallbackHandles;
+
+ /**
+ * Callback handle to keep track of manageRadio() callbacks.
+ */
+ event_queue_t::event_handle_t radioManagerCallbackHandle;
+
+ /**
+ * GattCharacteristic table used to populate the BLE ATT table in the
+ * GATT Server.
+ */
+ GattCharacteristic *charTable[TOTAL_CHARACTERISTICS];
+
+ /**
+ * Pointer to the device name currently being used.
+ */
+ const char *deviceName;
+
+ /**
+ * Defines an array of string constants (a container) used to initialise any URL slots
+ */
+ static const char* const slotDefaultUrls[];
+
+ /**
+ * Defines an array of UIDs to initialize UID slots
+ */
+ static const uint8_t slotDefaultUids[MAX_ADV_SLOTS][16];
+
+ /**
+ * Defines an array of EID (Identity keys) to initialize EID slots
+ */
+ static const uint8_t slotDefaultEidIdentityKeys[MAX_ADV_SLOTS][16];
+
+ /**
+ * Defines default EID payload before being updated with the first EID rotation value
+ */
+ static const uint8_t nullEid[8];
+
+ /**
+ * Reference to the event queue used to post tasks
+ */
+ event_queue_t& eventQueue;
+
+ /**
+ * Next EID slot frame that will be transmitted
+ */
+ uint8_t nextEidSlot;
+};
+
+#endif /* __EDDYSTONESERVICE_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EddystoneTypes.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2006-2016 Google Inc, All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EDDYSTONETYPES_H__
+#define __EDDYSTONETYPES_H__
+
+#include <stdint.h>
+#include <stddef.h>
+#include "Eddystone_config.h"
+
+/**
+ * Macro to expand a 16-bit Eddystone UUID to 128-bit UUID.
+ */
+#define UUID_ES_BEACON(FIRST, SECOND) { \
+ 0xa3, 0x0c8, FIRST, SECOND, 0x8e, 0xd3, 0x4b, 0xdf, \
+ 0x8a, 0x39, 0xa0, 0x1b, 0xeb, 0xed, 0xe2, 0x95, \
+}
+
+/**
+ * Eddystone 16-bit UUID.
+ */
+const uint8_t EDDYSTONE_UUID[] = {0xAA, 0xFE};
+
+/**
+ * Size of Eddystone UID. Needed to construct all frames raw bytes.
+ */
+const uint16_t EDDYSTONE_UUID_SIZE = sizeof(EDDYSTONE_UUID);
+
+/** BEGINING OF CHARACTERISTICS */
+
+/**
+ * 128-bit UUID for Eddystone-GATT Configuration Service.
+ */
+const uint8_t UUID_ES_BEACON_SERVICE[] = UUID_ES_BEACON(0x75, 0x00);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service Capabilities
+ * characteristic.
+ */
+const uint8_t UUID_CAPABILITIES_CHAR[] = UUID_ES_BEACON(0x75, 0x01);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service Active Slot
+ * characteristic.
+ */
+const uint8_t UUID_ACTIVE_SLOT_CHAR[] = UUID_ES_BEACON(0x75, 0x02);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service Advertising Interval
+ * characteristic.
+ */
+const uint8_t UUID_ADV_INTERVAL_CHAR[] = UUID_ES_BEACON(0x75, 0x03);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service Radio Tx Power
+ * characteristic.
+ */
+const uint8_t UUID_RADIO_TX_POWER_CHAR[] = UUID_ES_BEACON(0x75, 0x04);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service Adv Tx Power
+ * characteristic.
+ */
+const uint8_t UUID_ADV_TX_POWER_CHAR[] = UUID_ES_BEACON(0x75, 0x05);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service Lock State
+ * characteristic.
+ */
+const uint8_t UUID_LOCK_STATE_CHAR[] = UUID_ES_BEACON(0x75, 0x06);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service Unlock
+ * characteristic.
+ */
+const uint8_t UUID_UNLOCK_CHAR[] = UUID_ES_BEACON(0x75, 0x07);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service Public ECDH Key
+ * characteristic.
+ */
+const uint8_t UUID_PUBLIC_ECDH_KEY_CHAR[] = UUID_ES_BEACON(0x75, 0x08);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service EID Identity Key
+ * characteristic.
+ */
+const uint8_t UUID_EID_IDENTITY_KEY_CHAR[] = UUID_ES_BEACON(0x75, 0x09);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service Adv Slot Data
+ * characteristic.
+ */
+const uint8_t UUID_ADV_SLOT_DATA_CHAR[] = UUID_ES_BEACON(0x75, 0x0a);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service Reset
+ * characteristic.
+ */
+const uint8_t UUID_FACTORY_RESET_CHAR[] = UUID_ES_BEACON(0x75, 0x0b);
+
+/**
+ * 128-bit UUID for Eddystone-URL Configuration Service Remain Connectable
+ * characteristic.
+ */
+const uint8_t UUID_REMAIN_CONNECTABLE_CHAR[] = UUID_ES_BEACON(0x75, 0x0c);
+
+/** END OF CHARACTERISTICS */
+
+/**
+ * Default Lock State used by EddystoneService.
+ */
+const uint8_t DEFAULT_LOCK_STATE_DATA[] = {DEFAULT_LOCK_STATE};
+
+/**
+ * A type defining the size of the READ ONLY capability characteristic
+ */
+typedef uint8_t Capability_t[CAP_HDR_LEN + NUM_POWER_MODES];
+
+/**
+ * Type for the 128-bit for Eddystone-URL Configuration Service Lock and Unlock
+ * characteristic value.
+ */
+typedef uint8_t Lock_t[16];
+
+/**
+ * Type for the 128-bit for Eddystone-URL Configuration Service Advertised TX
+ * Power Levels characteristic value.
+ */
+typedef int8_t PowerLevels_t[NUM_POWER_MODES];
+
+/**
+ * Type representing the power level set for each slot
+ */
+typedef int8_t SlotTxPowerLevels_t[MAX_ADV_SLOTS];
+
+/**
+ * Type representing the adv interval set for each slot
+ */
+typedef uint16_t SlotAdvIntervals_t[MAX_ADV_SLOTS];
+
+/**
+ * Type representing the buffer used to represent the LockState and potentially
+ * an updated key
+ */
+typedef uint8_t LockState_t[17];
+
+/**
+ * Type representing the EID private ECDH Key
+ */
+typedef uint8_t PrivateEcdhKey_t[32];
+
+/**
+ * Type representing the EID public ECDH Key
+ */
+typedef uint8_t PublicEcdhKey_t[32];
+
+/**
+ * Type representing the EID Identity Key
+ */
+typedef uint8_t EidIdentityKey_t[16];
+
+/**
+ * Type representing the storage for a single slot
+ */
+typedef uint8_t Slot_t[32];
+
+/**
+ * Type representing the storage for all slots given MAX_ADV_SLOTS
+ */
+typedef uint8_t SlotStorage_t[MAX_ADV_SLOTS * sizeof(Slot_t)];
+
+/**
+ * Type representing the current frame types if each slot
+ */
+typedef uint8_t SlotFrameTypes_t[MAX_ADV_SLOTS];
+
+/**
+ * Type representing the EID rotation period exp for each slot
+ */
+typedef uint8_t SlotEidRotationPeriodExps_t[MAX_ADV_SLOTS];
+
+/**
+ * Type representing the EID next rotation time for each slot
+ */
+typedef uint32_t SlotEidNextRotationTimes_t[MAX_ADV_SLOTS];
+
+/**
+ * Type representing the EID identity keys for each slot
+ */
+typedef EidIdentityKey_t SlotEidIdentityKeys_t[MAX_ADV_SLOTS];
+
+/**
+ * Size in bytes of UID namespace ID.
+ */
+const size_t UID_NAMESPACEID_SIZE = 10;
+
+/**
+ * Type for the UID namespace ID.
+ */
+typedef uint8_t UIDNamespaceID_t[UID_NAMESPACEID_SIZE];
+
+/**
+ * Size in bytes of UID instance ID.
+ */
+const size_t UID_INSTANCEID_SIZE = 6;
+
+/**
+ * Type for the UID instance ID.
+ */
+typedef uint8_t UIDInstanceID_t[UID_INSTANCEID_SIZE];
+
+/**
+ * Type for callbacks to update Eddystone-TLM frame Batery Voltage and Beacon
+ * Temperature.
+ */
+typedef uint16_t (*TlmUpdateCallback_t) (uint16_t);
+
+// END OF PROTOTYPES
+
+typedef struct {
+ uint32_t timeInPriorBoots;
+ uint32_t timeSinceLastBoot;
+} TimeParams_t;
+
+#endif /* __EDDYSTONETYPES_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/Eddystone_config.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2016, Google, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EDDYSTONE_CONFIG_H_
+#define EDDYSTONE_CONFIG_H_
+
+// Version printed out on virtual terminal (independent of logging flag below)
+#define BUILD_VERSION_STR "EID Version 1.00 2016-11-19:15:00\r\n"
+
+/**
+ * Platform Target (if not set, default is nRF51-DK or nRF51-dongle or nRF52-DK)
+ * NOTE1: All targets are assumed to be 32K (in target.json) and S110 (in config.h)
+ * NOTE2: Only enable one target below (default is nRF_DK).
+ */
+// #define MinewTech51
+#define MinewTech52
+// #define nRF_DK
+
+/**
+ * DECLARE YOUR TARGET'S PARAMETERS
+ * If you are adding a new target, append to end of elif chain
+ *
+ * LED_OFF: value for an LED off state: 1 or 0
+ * CONFIG_LED: which LED to blink when in Configuration Mode
+ * On power up will go into configuration mode and eventually time out
+ * SHUTDOWN_LED: which LED to blink when shutting down (only used if RESET_BUTTON is defined)
+ * RESET_BUTTON: Which button to use. If defined, adds code to handle button presses
+ * Button press will toggle between configuration mode and off
+ * Configuration mode will eventually timeout and broadcast default values
+ * This will shutdown after initial power up! Assumes shipping with a battery in an off state
+ * EDDYSTONE_DEFAULT_RADIO_TX_POWER_LEVELS: Which power levels to offer
+ * EDDYSTONE_DEFAULT_ADV_TX_POWER_LEVELS: What to advertise these levels (as antennas always loose some power)
+ */
+#ifdef MinewTech51
+ #define LED_OFF 0
+ #define CONFIG_LED p15
+ #define SHUTDOWN_LED p16
+ #define RESET_BUTTON p18
+ #define EDDYSTONE_DEFAULT_RADIO_TX_POWER_LEVELS { -30, -16, -4, 4 }
+ #define EDDYSTONE_DEFAULT_ADV_TX_POWER_LEVELS { -42, -30, -25, -13 }
+
+#elif defined MinewTech52
+ #define LED_OFF 0
+ #define CONFIG_LED LED3
+ #define SHUTDOWN_LED LED2
+ #define RESET_BUTTON BUTTON1
+ #define EDDYSTONE_DEFAULT_RADIO_TX_POWER_LEVELS { -40, -20, -8, 4 }
+ #define EDDYSTONE_DEFAULT_ADV_TX_POWER_LEVELS { -50, -30, -18, -6 }
+
+#else
+ // *** nRF_DK or USB Dongle PIN defines ***
+ #define LED_OFF 1
+ #define CONFIG_LED LED3
+ // Uncomment the defines below if you want the DK board to behave like a
+ // Beacon target with shutdown on power up, and a mode button
+ // #define SHUTDOWN_LED LED2
+ // #define RESET_BUTTON BUTTON1
+ #define EDDYSTONE_DEFAULT_RADIO_TX_POWER_LEVELS { -30, -16, -4, 4 }
+ #define EDDYSTONE_DEFAULT_ADV_TX_POWER_LEVELS { -42, -30, -25, -13 }
+#endif
+
+/**
+ * DEBUG OPTIONS
+ * For production: all defines below should be UNCOMMENTED:
+ * Key
+ * GEN_BEACON_KEYS_AT_INIT: Debugging flag to help test entropy source
+ * HARDWARE_RANDOM_NUM_GENERATOR: include if the target supports a hardware RNG
+ * EID_RANDOM_MAC: include if you want to randomize the mac address for each eid rotation
+ * INCLUDE_CONFIG_URL: Includes configuration url when in Configuration Mode
+ * DONT_REMAIN_CONNECTABLE: Debugging flag; remain connectable during beaconing for easy testing
+ * NO_4SEC_START_DELAY: Debugging flag to pause 4s before starting; allow time to connect virtual terminal
+ * NO_EAX_TEST: Debugging flag: when not define, test will check x = EAX_DECRYPT(EAX_ENCRYPT(x)), output in LOG
+ * NO_LOGGING: Debugging flag; controls logging to virtual terminal
+ */
+#define GEN_BEACON_KEYS_AT_INIT
+#define HARDWARE_RANDOM_NUM_GENERATOR
+#define EID_RANDOM_MAC
+#define INCLUDE_CONFIG_URL
+#define DONT_REMAIN_CONNECTABLE
+#define NO_4SEC_START_DELAY
+#define NO_EAX_TEST
+#define NO_LOGGING
+
+/* Default enable printf logging, unless explicitly NO_LOGGING */
+#ifdef NO_LOGGING
+ #define LOG_PRINT 0
+#else
+ #define LOG_PRINT 1
+#endif
+
+#define LOG(x) do { if (LOG_PRINT) printf x; } while (0)
+
+/**
+ * GENERIC BEACON BEHAVIORS DEFINED
+ * Note: If the CONFIG_URL is enabled (DEFINE above)
+ * The size of the DEVICE_NAME + Encoded Length of the CONFIG_URL
+ * must be LESS THAN OR EQUAL to 23
+ */
+#define EDDYSTONE_CONFIG_URL "http://c.pw3b.com"
+#define EDDYSTONE_CFG_DEFAULT_DEVICE_NAME "Eddystone v3.0"
+#define EDDYSTONE_DEFAULT_MAX_ADV_SLOTS 3
+#define EDDYSTONE_DEFAULT_CONFIG_ADV_INTERVAL 1000
+#define EDDYSTONE_DEFAULT_CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS 60
+
+#define EDDYSTONE_DEFAULT_UNLOCK_KEY { \
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF \
+}
+
+#define EDDYSTONE_DEFAULT_SLOT_URLS { \
+ "http://c.pw3b.com", \
+ "https://www.mbed.com/", \
+ "https://www.github.com/" \
+}
+
+#define EDDYSTONE_DEFAULT_SLOT_UIDS { \
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }, \
+ { 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0 }, \
+ { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF } \
+}
+
+#define EDDYSTONE_DEFAULT_SLOT_EID_IDENTITY_KEYS { \
+ { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF }, \
+ { 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF }, \
+ { 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF } \
+}
+
+#define EDDYSTONE_DEFAULT_SLOT_EID_ROTATION_PERIOD_EXPS { 10, 10, 10 }
+
+// The following frame/slot types are supported: URL, UID, TLM, EID. The defaults set URL x2 and EID
+#define EDDYSTONE_DEFAULT_SLOT_TYPES { \
+ EDDYSTONE_FRAME_URL, \
+ EDDYSTONE_FRAME_URL, \
+ EDDYSTONE_FRAME_EID \
+}
+
+#define EDDYSTONE_DEFAULT_SLOT_INTERVALS { 700, 0, 0 }
+
+#define EDDYSTONE_DEFAULT_SLOT_TX_POWERS { -8, -8, -8 }
+
+/**
+ * Lock constants
+ */
+#define LOCKED 0
+#define UNLOCKED 1
+#define UNLOCKED_AUTO_RELOCK_DISABLED 2
+
+#define DEFAULT_LOCK_STATE UNLOCKED
+
+/**
+ * Set default number of adv slots
+ */
+const uint8_t MAX_ADV_SLOTS = EDDYSTONE_DEFAULT_MAX_ADV_SLOTS;
+
+/**
+ * Slot and Power and Interval Constants
+ */
+const uint8_t DEFAULT_SLOT = 0;
+
+/**
+ * Number of radio power modes supported
+ */
+const uint8_t NUM_POWER_MODES = 4;
+
+/**
+ * Default name for the BLE Device Name characteristic.
+ */
+const char DEFAULT_DEVICE_NAME[] = EDDYSTONE_CFG_DEFAULT_DEVICE_NAME;
+
+/**
+ * ES GATT Capability Constants (6 values)
+ */
+const uint8_t CAP_HDR_LEN = 6; // The six constants below
+const uint8_t ES_GATT_VERSION = 0;
+const uint8_t MAX_EIDS = MAX_ADV_SLOTS;
+const uint8_t CAPABILITIES = 0x03; // Per slot variable interval and variable Power
+const uint8_t SUPPORTED_FRAMES_H = 0x00;
+const uint8_t SUPPORTED_FRAMES_L = 0x0F;
+
+/**
+ * ES GATT Capability Constant Array storing the capability constants
+ */
+const uint8_t CAPABILITIES_DEFAULT[] = {ES_GATT_VERSION, MAX_ADV_SLOTS, MAX_EIDS, CAPABILITIES, \
+ SUPPORTED_FRAMES_H, SUPPORTED_FRAMES_L};
+
+#endif /* EDDYSTONE_CONFIG_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EntropySource/EntropySource.cpp Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,33 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "EntropySource.h"
+
+#if !defined(TARGET_NRF51822) && !defined(TARGET_MCU_NRF52832) /* Persistent storage supported on nrf51 platforms */
+ /**
+ * When not using an nRF51-based target then entropy source is currently unimplemented.
+ */
+ #error "INSECURE CONFIGURATION - YOU MUST IMPLEMENT AN ENTROPY SOURCE"
+
+ int eddystoneRegisterEntropySource( mbedtls_entropy_context* ctx) {
+ return 1;
+ }
+
+ int eddystoneEntropyPoll( void *data,
+ unsigned char *output, size_t len, size_t *olen )
+ {
+ return( 1 );
+ }
+#endif /* #ifdef TARGET_NRF51822 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/EntropySource/EntropySource.h Tue Nov 29 06:29:10 2016 +0000 @@ -0,0 +1,11 @@ +#ifndef __ENTROPY_SOURCE_H__ +#define __ENTROPY_SOURCE_H__ +#include <stddef.h> +#include <mbedtls/entropy.h> + +int eddystoneRegisterEntropySource( mbedtls_entropy_context* ctx); + +int eddystoneEntropyPoll( void *data, + unsigned char *output, size_t len, size_t *olen ); + +#endif // __ENTROPY_SOURCE_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EntropySource/nRFEntropySource/nRFEntropySource.cpp Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,67 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "../EntropySource.h"
+
+#if defined(TARGET_NRF51822) || defined(TARGET_MCU_NRF52832) /* Persistent storage supported on nrf51 platforms */
+
+#include "nrf_soc.h"
+#include "nrf_error.h"
+#include "mbed.h"
+#include <mbedtls/entropy.h>
+
+/*
+ * nRF51 has a TRNG that we can access using SoftDevice.
+ */
+int eddystoneEntropyPoll(void *data, unsigned char *output, size_t len, size_t *olen)
+{
+ uint8_t bytes_available = 0;
+
+ // get the number of random bytes available
+ if (sd_rand_application_bytes_available_get(&bytes_available) != NRF_SUCCESS) {
+ return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
+ }
+
+ // if there is more bytes available that what is requested,
+ // truncate the number of bytes in output to len, otherwise use the total
+ // of bytes available.
+ const uint8_t output_len = bytes_available > len ? len : bytes_available;
+
+ if (output_len) {
+ // transfer "output_len" random bytes to output.
+ if (sd_rand_application_vector_get(output, output_len) != NRF_SUCCESS) {
+ return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
+ }
+ }
+
+ // Everything went fine, commit the output_len to the output parameter
+ *olen = output_len;
+ return 0;
+}
+
+int eddystoneRegisterEntropySource( mbedtls_entropy_context* ctx) {
+ uint8_t pool_capacity;
+ sd_rand_application_pool_capacity_get(&pool_capacity);
+
+ return mbedtls_entropy_add_source(
+ ctx,
+ eddystoneEntropyPoll, // entropy source function
+ NULL, // entropy source data, NULL in this case
+ pool_capacity, // minimum number of bytes the entropy pool should wait on from this callback before releasing entropy
+ MBEDTLS_ENTROPY_SOURCE_STRONG
+ );
+}
+
+#endif /* #ifdef TARGET_NRF51822 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/AlignedStorage.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_DETAIL_ALIGNEDSTORAGE_H_
+#define EVENTQUEUE_DETAIL_ALIGNEDSTORAGE_H_
+
+#include <cstddef>
+
+namespace eq {
+
+class AnonymousDeclaration;
+
+/**
+ * Provide aligned raw storage for holding a type T.
+ * This class is useful to delay the construction of objects while reserving
+ * space in memory for them.
+ * For instance, it can be use with static or global variable which can not
+ * be constructed before main. It can also be used in class member which
+ * do not have or can't be constructed at parent construction time.
+ *
+ * Once the storage has been reserved, it is possible explicitly construct the
+ * object of T by using placement new syntax.
+ * @code
+ * AlignedStorage<Foo> foo;
+ * new (foo.get_storage()) Foo(...);
+ * @endcode
+ * Then it is possible to get a reference to the object by calling the member
+ * function get.
+ * @code
+ * foo.get().doSomething();
+ * @endcode
+ * Once the object needs to be destroyed it is possible to call the destructor
+ * directly
+ * @code
+ * foo.get().~Foo();
+ * @endcode
+ * After this point, their is no instance of T in the storage and the function
+ * get remains unusable until an object is again initialised in the storage.
+ */
+template<typename T>
+class AlignedStorage {
+public:
+ /**
+ * Initialisation of the storage, does **not** zeroed its memory.
+ */
+ AlignedStorage() {}
+ /**
+ * Provide the raw pointer to the address of the storage.
+ */
+ void* get_storage() {
+ return data;
+ }
+
+ /**
+ * Provide the raw pointer to the const address of the storage.
+ */
+ const void* get_storage() const {
+ return data;
+ }
+
+ /**
+ * Return a reference to the element T in this storage.
+ */
+ T& get() {
+ return *static_cast<T*>(get_storage());
+ }
+
+ /**
+ * Return a reference to the const element of T in this storage.
+ */
+ const T& get() const {
+ return *static_cast<const T*>(get_storage());
+ }
+
+private:
+ // it doesn't make sense to allow copy construction of copy assignement for
+ // this kind of object.
+ AlignedStorage(const AlignedStorage&);
+ AlignedStorage& operator=(const AlignedStorage&);
+ // storage. Can be improved by metaprogramming to be the best fit.
+ union {
+ char char_storage;
+ short int short_int_storage;
+ int int_storage;
+ long int long_int_storage;
+ float float_storage;
+ double double_storage;
+ long double long_double_storage;
+ void* pointer_storage;
+ AnonymousDeclaration (*function_pointer_storage)(AnonymousDeclaration);
+ AnonymousDeclaration* AnonymousDeclaration::*data_member_storage ;
+ AnonymousDeclaration (AnonymousDeclaration::*function_member_storage)(AnonymousDeclaration);
+ char data[sizeof(T)];
+ };
+};
+
+/**
+ * Provide aligned raw storage for holding an array of type T.
+ * This is a specialisation of AlignedStorage for arrays of T.
+ * With this class, it is possible to reserve space for a given number of
+ * elements of type T and delay their construction to a latter point. This
+ * feature can be really useful when building generic container which
+ * embed memory for their elements. Instead of default constructing them,
+ * the construction of an element can be made when it is really needed, by
+ * copy. It is the same for the destruction, only objects which have been
+ * constructed needs to be destructed.
+ * Those properties improve generic containers because only the operations
+ * which have to be made are made. It also allow generic container to hold
+ * types which are not DefaultConstructible.
+ *
+ * Once the storage has been reserved, it is possible explicitly construct an
+ * object of T at a given index by using placement new syntax.
+ * @code
+ * AlignedStorage<Foo[10]> foo;
+ * //construct object at index 0 then at index 1.
+ * new (foo.get_storage(0)) Foo(...);
+ * new (foo.get_storage(1)) Foo(...);
+ * @endcode
+ * Then it is possible to get a reference to an object at a given index by
+ * calling the member function get.
+ * @code
+ * // do something with object at index 1
+ * foo.get(1).doSomething();
+ * @endcode
+ * Once the object needs to be destroyed it is possible to call the destructor
+ * directly
+ * @code
+ * // destroy object at index 1.
+ * foo.get(1).~Foo();
+ * @endcode
+ * After this point, their is no instance of T at index 1 in the storage and
+ * trying to use the object at this index will lead to undefined behavior until
+ * an object is again initialised at this index.
+ */
+template<typename T, std::size_t ArraySize>
+struct AlignedStorage<T[ArraySize]> {
+ /**
+ * Initialisation of the storage, does **not** zeroed its memory.
+ */
+ AlignedStorage() {}
+
+ /**
+ * Return raw pointer to the address of element at a given index
+ */
+ void* get_storage(std::size_t index) {
+ return &get(index);
+ }
+
+ /**
+ * const version of void* get_storage(std::size_t).
+ */
+ const void* get_storage(std::size_t index) const {
+ return &get(index);
+ }
+
+ /**
+ * Return reference to the element stored atindex.
+ */
+ T& get(std::size_t index) {
+ return reinterpret_cast<T*>(data)[index];
+ }
+
+ /**
+ * const version of T& get(std::size_t).
+ */
+ const T& get(std::size_t index) const {
+ return reinterpret_cast<const T*>(data)[index];
+ }
+
+private:
+ // it doesn't make sense to allow copy construction of copy assignement for
+ // this kind of object.
+ AlignedStorage(const AlignedStorage&);
+ AlignedStorage& operator=(const AlignedStorage&);
+ // storage. Can be improved by metaprogramming to be the best fit.
+ union {
+ char char_storage;
+ short int short_int_storage;
+ int int_storage;
+ long int long_int_storage;
+ float float_storage;
+ double double_storage;
+ long double long_double_storage;
+ void* pointer_storage;
+ AnonymousDeclaration (*function_pointer_storage)(AnonymousDeclaration);
+ AnonymousDeclaration* AnonymousDeclaration::*data_member_storage ;
+ AnonymousDeclaration (AnonymousDeclaration::*function_member_storage)(AnonymousDeclaration);
+ char data[sizeof(T[ArraySize])];
+ };
+};
+
+} // namespace eq
+
+#endif /* EVENTQUEUE_DETAIL_ALIGNEDSTORAGE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/EventQueue.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_EVENTQUEUE_H_
+#define EVENTQUEUE_EVENTQUEUE_H_
+
+#include <stdio.h>
+#include "Thunk.h"
+#include "MakeThunk.h"
+
+namespace eq {
+
+class EventQueue {
+
+public:
+ /// typedef for callable type.
+ /// the callable type used should support the same operations
+ /// supported by a void(*)() function pointer.
+ typedef Thunk function_t;
+
+ /// handle to a posted event which will be executed later.
+ /// model after a void* pointer.
+ typedef void* event_handle_t;
+
+ /// type used for time
+ typedef std::size_t ms_time_t;
+
+ /// Construct an empty event queue
+ EventQueue() { }
+
+ virtual ~EventQueue() { }
+
+ /**
+ * Post a callable to the event queue.
+ * It will be executed during the next dispatch cycle.
+ * @param f The callbable to be executed by the event queue.
+ * @return the handle to the event.
+ */
+ template<typename F>
+ event_handle_t post(const F& fn) {
+ return do_post(fn);
+ }
+
+ /**
+ * Bind a callable and an argument then post a callable to the event queue.
+ * It will be executed during the next dispatch cycle.
+ * @param f The callbable to be bound with arg0.
+ * @param arg0 The first argument to bind to f.
+ * @return the handle to the event.
+ */
+ template<typename F, typename Arg0>
+ event_handle_t post(const F& fn, const Arg0& arg0) {
+ return do_post(make_thunk(fn, arg0));
+ }
+
+ template<typename F, typename Arg0, typename Arg1>
+ event_handle_t post(const F& fn, const Arg0& arg0, const Arg1& arg1) {
+ return do_post(make_thunk(fn, arg0, arg1));
+ }
+
+ template<typename F, typename Arg0, typename Arg1, typename Arg2>
+ event_handle_t post(const F& fn, const Arg0& arg0, const Arg1& arg1, const Arg2& arg2) {
+ return do_post(make_thunk(fn, arg0, arg1, arg2));
+ }
+
+ template<typename F>
+ event_handle_t post_in(const F& fn, ms_time_t ms_delay) {
+ return do_post(fn, ms_delay);
+ }
+
+ template<typename F, typename Arg0>
+ event_handle_t post_in(const F& fn, const Arg0& arg0, ms_time_t ms_delay) {
+ return do_post(make_thunk(fn, arg0), ms_delay);
+ }
+
+ template<typename F, typename Arg0, typename Arg1>
+ event_handle_t post_in(const F& fn, const Arg0& arg0, const Arg1& arg1, ms_time_t ms_delay) {
+ return do_post(make_thunk(fn, arg0, arg1), ms_delay);
+ }
+
+ template<typename F, typename Arg0, typename Arg1, typename Arg2>
+ event_handle_t post_in(const F& fn, const Arg0& arg0, const Arg1& arg1, const Arg2& arg2, ms_time_t ms_delay) {
+ return do_post(make_thunk(fn, arg0, arg1, arg2), ms_delay);
+ }
+
+ template<typename F>
+ event_handle_t post_every(const F& fn, ms_time_t ms_delay) {
+ return do_post(fn, ms_delay, true);
+ }
+
+ template<typename F, typename Arg0>
+ event_handle_t post_every(const F& fn, const Arg0& arg0, ms_time_t ms_delay) {
+ return do_post(make_thunk(fn, arg0), ms_delay, true);
+ }
+
+ template<typename F, typename Arg0, typename Arg1>
+ event_handle_t post_every(const F& fn, const Arg0& arg0, const Arg1& arg1, ms_time_t ms_delay) {
+ return do_post(make_thunk(fn, arg0, arg1), ms_delay, true);
+ }
+
+ template<typename F, typename Arg0, typename Arg1, typename Arg2>
+ event_handle_t post_every(const F& fn, const Arg0& arg0, const Arg1& arg1, const Arg2& arg2, ms_time_t ms_delay) {
+ return do_post(make_thunk(fn, arg0, arg1, arg2), ms_delay, true);
+ }
+
+ virtual bool cancel(event_handle_t event_handle) = 0;
+
+private:
+ virtual event_handle_t do_post(const function_t& fn, ms_time_t ms_delay = 0, bool repeat = false) = 0;
+};
+
+} // namespace eq
+
+#endif /* EVENTQUEUE_EVENTQUEUE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/EventQueueClassic.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef BLE_API_SOURCE_MBEDCLASSICEVENTQUEUE_H_
+#define BLE_API_SOURCE_MBEDCLASSICEVENTQUEUE_H_
+
+#include <cmsis.h>
+#include "PriorityQueue.h"
+#include "Ticker.h"
+#include "Timer.h"
+#include <stdio.h>
+#include "Thunk.h"
+#include "MakeThunk.h"
+#include "EventQueue.h"
+
+#include <util/CriticalSectionLock.h>
+typedef ::mbed::util::CriticalSectionLock CriticalSection;
+
+namespace eq {
+
+template<std::size_t EventCount>
+class EventQueueClassic: public EventQueue {
+
+ /// Describe an event.
+ /// An event is composed of a function f to execute after a a time t.
+ /// Optionnaly, the event can be periodic and in this case the function f
+ /// is executed after each period p.
+ struct Event {
+ /// construct an event
+ /// @param f The function to execute when this event occur
+ /// @param ms_remaining_time remaining time before this event occurence
+ /// @param ms_repeat_period If the event is periodic, this parameter is the
+ /// period between to occurence of this event.
+ Event(const function_t& f, ms_time_t ms_remaining_time, ms_time_t ms_repeat_period = 0) :
+ _f(f),
+ _ms_remaining_time(ms_remaining_time),
+ _ms_repeat_period(ms_repeat_period) {
+ }
+
+ /// call the inner function within an event
+ void operator()() {
+ _f();
+ }
+
+ /// return a reference to the inner function
+ const function_t& get_function() const {
+ return _f;
+ }
+
+ /// comparison operator used by the priority queue.
+ /// comaprare remaining time between two events
+ friend bool operator<(const Event& lhs, const Event& rhs) {
+ return lhs._ms_remaining_time < rhs._ms_remaining_time;
+ }
+
+ /// return the time remaining when this event was inserted into the priority queue.
+ ms_time_t get_ms_remaining_time() const {
+ return _ms_remaining_time;
+ }
+
+ /// update the remaining time for this event
+ void set_ms_remaining_time(ms_time_t new_remaining_time) {
+ _ms_remaining_time = new_remaining_time;
+ }
+
+ /// If an event is periodic, return the time between two occurence
+ ms_time_t get_ms_repeat_period() const {
+ return _ms_repeat_period;
+ }
+
+ private:
+ function_t _f;
+ ms_time_t _ms_remaining_time;
+ const ms_time_t _ms_repeat_period;
+ };
+
+ /// type of the internal queue
+ typedef PriorityQueue<Event, EventCount> priority_queue_t;
+
+ /// iterator for the queue type
+ typedef typename priority_queue_t::iterator q_iterator_t;
+
+ /// node type in the queue
+ typedef typename priority_queue_t::Node q_node_t;
+
+public:
+ /// Construct an empty event queue
+ EventQueueClassic() :
+ _events_queue(), _ticker(), _timer(), _timed_event_pending(false) {
+ }
+
+ virtual ~EventQueueClassic() { }
+
+ virtual bool cancel(event_handle_t event_handle) {
+ CriticalSection critical_section;
+ bool success = _events_queue.erase(static_cast<q_node_t*>(event_handle));
+ if (success) {
+ // update the timers and events remaining time
+ updateTime();
+ }
+ return success;
+ }
+
+ void dispatch() {
+ while(true) {
+ function_t f;
+ // pick a task from the queue/ or leave
+ {
+ CriticalSection cs;
+ q_iterator_t event_it = _events_queue.begin();
+ if(event_it != _events_queue.end() && event_it->get_ms_remaining_time() == 0) {
+ f = event_it->get_function();
+ // if the event_it should be repeated, reschedule it
+ if (event_it->get_ms_repeat_period()) {
+ reschedule_event(event_it);
+ } else {
+ _events_queue.pop();
+ }
+ } else {
+ break;
+ }
+ }
+ f();
+ }
+ }
+
+private:
+
+ void update_ticker(ms_time_t ms_delay) {
+ _timed_event_pending = true;
+ _ticker.detach();
+ _ticker.attach(this, &EventQueueClassic::updateTime, ((float) ms_delay / 1000));
+ }
+
+ void update_ticker(q_node_t* ref, ms_time_t ms_delay) {
+ // look if the node inserted is the first node with a delay
+ for (q_iterator_t it = _events_queue.begin(); it != _events_queue.end(); ++it) {
+ if(it->get_ms_remaining_time()) {
+ if (it.get_node() == ref) {
+ // update the ticker to ms_delay if the first event
+ // with a delay is the one inserted
+ update_ticker(ms_delay);
+ }
+ break;
+ }
+ }
+ }
+
+ void update_events_remaining_time(ms_time_t elapsed_time) {
+ bool ticker_updated = false;
+
+ for (q_iterator_t it = _events_queue.begin();
+ it != _events_queue.end(); ++it) {
+ ms_time_t remaining_time = it->get_ms_remaining_time();
+ if(remaining_time) {
+ if(remaining_time <= elapsed_time) {
+ it->set_ms_remaining_time(0);
+ } else {
+ it->set_ms_remaining_time(remaining_time - elapsed_time);
+ if (!ticker_updated) {
+ update_ticker(it->get_ms_remaining_time());
+ _timer.start();
+ ticker_updated = true;
+ }
+ }
+ }
+ }
+ }
+
+ void updateTime() {
+ CriticalSection critical_section;
+ ms_time_t elapsed_time = _timer.read_ms();
+ _timed_event_pending = false;
+ _timer.stop();
+ _timer.reset();
+ _ticker.detach();
+ update_events_remaining_time(elapsed_time);
+ }
+
+ void reschedule_event(q_iterator_t& event_it) {
+ ms_time_t ms_period = event_it->get_ms_repeat_period();
+
+ if (_timed_event_pending == false) {
+ update_ticker(ms_period);
+ _timer.start();
+ event_it->set_ms_remaining_time(ms_period);
+ _events_queue.update(event_it);
+ } else {
+ int elapsed_time = _timer.read_ms();
+ event_it->set_ms_remaining_time(elapsed_time + ms_period);
+ _events_queue.update(event_it);
+ update_ticker(event_it.get_node(), ms_period);
+ }
+ }
+
+ virtual event_handle_t do_post(const function_t& fn, ms_time_t ms_delay = 0, bool repeat = false) {
+ if(repeat && (ms_delay == 0)) {
+ return NULL;
+ }
+
+ Event event(fn, ms_delay, repeat ? ms_delay : 0);
+
+ CriticalSection critical_section;
+ if (_events_queue.full()) {
+ return NULL;
+ }
+
+ // there is no need to update timings if ms_delay == 0
+ if (!ms_delay) {
+ return _events_queue.push(event).get_node();
+ }
+
+ // if there is no pending timed event, just add this one and start timers
+ if (_timed_event_pending == false) {
+ update_ticker(ms_delay);
+ _timer.start();
+ return _events_queue.push(event).get_node();
+ }
+
+ int elapsed_time = _timer.read_ms();
+
+ // update remaining time and post the event
+ event.set_ms_remaining_time(ms_delay + elapsed_time);
+ event_handle_t handle = _events_queue.push(event).get_node();
+ update_ticker(static_cast<q_node_t*>(handle), ms_delay);
+
+ return handle;
+ }
+
+ priority_queue_t _events_queue;
+ mbed::Ticker _ticker;
+ mbed::Timer _timer;
+ bool _timed_event_pending;
+};
+
+} // namespace eq
+
+#endif /* BLE_API_SOURCE_MBEDCLASSICEVENTQUEUE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/EventQueueMinar.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_EVENTQUEUEMINAR_H_
+#define EVENTQUEUE_EVENTQUEUEMINAR_H_
+
+#include <minar/minar.h>
+#include "EventQueue.h"
+
+namespace eq {
+
+class EventQueueMinar: public EventQueue {
+
+public:
+ /// Construct an empty event queue
+ EventQueueMinar() { }
+
+ virtual ~EventQueueMinar() { }
+
+ virtual bool cancel(event_handle_t event_handle) {
+ return minar::Scheduler::cancelCallback(event_handle);
+ }
+
+private:
+
+ virtual event_handle_t do_post(const function_t& fn, ms_time_t ms_delay = 0, bool repeat = false) {
+ // convert ms to minar time
+ minar::tick_t tick = minar::milliseconds(ms_delay);
+
+ // convert thunk to minar FunctionPointerBind
+ mbed::util::Event func(
+ mbed::util::FunctionPointer1<void, function_t>(
+ free_func_thunk_call
+ ).bind(fn)
+ );
+
+ if (ms_delay == 0) {
+ return minar::Scheduler::postCallback(func).getHandle();
+ }
+
+ if (repeat == false) {
+ return minar::Scheduler::postCallback(func).delay(tick).tolerance(0).getHandle();
+ } else {
+ return minar::Scheduler::postCallback(func).period(tick).tolerance(0).getHandle();
+ }
+ }
+
+ // due to design limitations in function pointer classes, it is not possible
+ // to use reference here ...
+ static void free_func_thunk_call(function_t fn) {
+ fn();
+ }
+};
+
+} // namespace eq
+
+#endif /* EVENTQUEUE_EVENTQUEUEMINAR_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/MakeThunk.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_MAKETHUNK_H
+#define EVENTQUEUE_MAKETHUNK_H
+
+#include "detail/MemberFunctionAdaptor.h"
+#include "detail/FunctionAdaptor.h"
+#include "detail/Thunks.h"
+
+namespace eq {
+
+/**
+ * Make a thunk from an F.
+ * When this function only takes an F then F it is expected that F is already
+ * a callable and therefore a kind of thunk.
+ * @tparam F The type of callable in input.
+ * @param fn the function to turn into a thunk.
+ * @return fn
+ */
+template<typename F>
+const F& make_thunk(const F& fn) {
+ return fn;
+}
+
+/**
+ * Bind fn and arg0 into a thunk.
+ * @tparam F the type of the function to bind. It can be a function pointer,
+ * a function like object or a pointer to a member function.
+ * @tparam Arg0 The type of the first argument of F.
+ * @param fn the function to bind.
+ * @param arg0 the first argument to bind.
+ * @return a thunk binding F and arg0.
+ */
+template<typename F, typename Arg0>
+detail::Thunk_1<typename detail::FunctionAdaptor<F>::type, Arg0>
+make_thunk(const F& fn, const Arg0& arg0) {
+ typedef typename detail::FunctionAdaptor<F>::type fn_adaptor_t;
+ return detail::Thunk_1<fn_adaptor_t, Arg0>(
+ fn_adaptor_t(fn),
+ arg0
+ );
+}
+
+/**
+ * Bind fn, arg0 and arg1 into a thunk.
+ * @tparam F the type of the function to bind. It can be a function pointer,
+ * a function like object or a pointer to a member function.
+ * @tparam Arg0 The type of the first argument of F.
+ * @tparam Arg1 The type of the second argument of F.
+ * @param fn the function to bind.
+ * @param arg0 the first argument to bind.
+ * @param arg1 the second argument to bind.
+ * @return a thunk binding F, arg0 and arg1.
+ */
+template<typename F, typename Arg0, typename Arg1>
+detail::Thunk_2<typename detail::FunctionAdaptor<F>::type, Arg0, Arg1>
+make_thunk(const F& fn, const Arg0& arg0, const Arg1& arg1) {
+ typedef typename detail::FunctionAdaptor<F>::type fn_adaptor_t;
+ return detail::Thunk_2<fn_adaptor_t, Arg0, Arg1>(
+ fn_adaptor_t(fn),
+ arg0,
+ arg1
+ );
+}
+
+/**
+ * Bind fn, arg0, arg1 and arg2 into a thunk.
+ * @tparam F the type of the function to bind. It can be a function pointer,
+ * a function like object or a pointer to a member function.
+ * @tparam Arg0 The type of the first argument of F.
+ * @tparam Arg1 The type of the second argument of F.
+ * @tparam Arg2 The type of the third argument of F.
+ * @param fn the function to bind.
+ * @param arg0 the first argument to bind.
+ * @param arg1 the second argument to bind.
+ * @param arg1 the third argument to bind.
+ * @return a thunk binding F, arg0, arg1 and arg2.
+ */
+template<typename F, typename Arg0, typename Arg1, typename Arg2>
+detail::Thunk_3<typename detail::FunctionAdaptor<F>::type, Arg0, Arg1, Arg2>
+make_thunk(const F& fn, const Arg0& arg0, const Arg1& arg1, const Arg2& arg2) {
+ typedef typename detail::FunctionAdaptor<F>::type fn_adaptor_t;
+ return detail::Thunk_3<fn_adaptor_t, Arg0, Arg1, Arg2>(
+ fn_adaptor_t(fn),
+ arg0,
+ arg1,
+ arg2
+ );
+}
+
+} // namespace eq
+
+#endif /* EVENTQUEUE_MAKETHUNK_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/PriorityQueue.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_STACKPRIORITYQUEUE_H_
+#define EVENTQUEUE_STACKPRIORITYQUEUE_H_
+
+#include <cstddef>
+#include "AlignedStorage.h"
+
+namespace eq {
+
+/**
+ * Priority queue of Ts.
+ * Ts are ordered from the smaller to the bigger ( < ).
+ * Elements in the queue are mutable (this is a design choice).
+ * After a mutation the function update should be called to ensure that the
+ * queue is still properly sorted.
+ * @tparam T type of elements in this queue
+ * @param capacity Number of elements that this queue can contain
+ */
+template<typename T, std::size_t Capacity>
+class PriorityQueue {
+
+public:
+ /**
+ * Type of the nodes in this queue.
+ */
+ struct Node {
+ AlignedStorage<T> storage; /// storage for the T
+ Node* next; /// pointer to the next node
+ };
+
+ /**
+ * Iterator for elements of the queue.
+ */
+ class Iterator {
+ friend PriorityQueue;
+
+ /// Construct an iterator from a Node.
+ /// This constructor is private and can only be invoked from the PriorityQueue.
+ Iterator(Node* current) :
+ _current(current) {
+ }
+
+ public:
+
+ /// Indirection operator.
+ /// return a reference to the inner T
+ T& operator*() {
+ return _current->storage.get();
+ }
+
+ /// Const version of indirection operator.
+ /// return a reference to the inner T
+ const T& operator*() const {
+ return _current->storage.get();
+ }
+
+ /// dereference operator.
+ /// Will invoke the operation on the inner T
+ T* operator->() {
+ return &(_current->storage.get());
+ }
+
+ /// const dereference operator.
+ /// Will invoke the operation on the inner T
+ const T* operator->() const {
+ return &(_current->storage.get());
+ }
+
+ /// pre incrementation to the next T in the list
+ Iterator& operator++() {
+ _current = _current->next;
+ return *this;
+ }
+
+ /// post incrementation to the next T in the list
+ Iterator operator++(int) {
+ Iterator tmp(*this);
+ _current = _current->next;
+ return tmp;
+ }
+
+ /// Equality operator
+ friend bool operator==(const Iterator& lhs, const Iterator& rhs) {
+ return lhs._current == rhs._current;
+ }
+
+ /// Unequality operator
+ friend bool operator!=(const Iterator& lhs, const Iterator& rhs) {
+ return !(lhs == rhs);
+ }
+
+ /// return the internal node.
+ Node* get_node() {
+ return _current;
+ }
+
+ private:
+ Node* _current;
+ };
+
+ typedef Iterator iterator;
+
+ /// Construct an empty priority queue.
+ PriorityQueue() : nodes(), free_nodes(NULL), head(NULL), used_nodes_count(0) {
+ initialize();
+ }
+
+ /// Copy construct a priority queue.
+ /// The queue will have the same content has other.
+ PriorityQueue(const PriorityQueue& other) :
+ nodes(), free_nodes(NULL), head(NULL), used_nodes_count(0) {
+ initialize();
+ copy(other);
+ }
+
+ /// destroy a priority queue.
+ ~PriorityQueue() {
+ clear();
+ }
+
+ /// Copy assignemnent from another priority queue.
+ /// The content of the queue will be destroyed then the content from
+ /// other will be copied.
+ PriorityQueue& operator=(const PriorityQueue& other) {
+ if (&other == this) {
+ return *this;
+ }
+ copy(other);
+ return *this;
+ }
+
+ /// Push a new element to the queue.
+ /// It will be added before the first element p in the queue where
+ /// element < p == true.
+ /// @return An iterator to the inserted element.
+ iterator push(const T& element) {
+ if (full()) {
+ return NULL;
+ }
+
+ // get a free node
+ Node* new_node = free_nodes;
+ free_nodes = free_nodes->next;
+ new_node->next = NULL;
+
+ ++used_nodes_count;
+
+ // copy content
+ new (new_node->storage.get_storage()) T(element);
+
+ // if there is no node in the queue, just link the head
+ // to the new node and return
+ if (head == NULL) {
+ head = new_node;
+ return new_node;
+ }
+
+ // if the new node has an higher priority than the node in head
+ // just link it as the head
+ if (element < head->storage.get()) {
+ new_node->next = head;
+ head = new_node;
+ return new_node;
+ }
+
+ // insert the node after head
+ insert_after(head, new_node);
+
+ return new_node;
+ }
+
+ /// pop the head of the queue.
+ bool pop() {
+ if (!head) {
+ return false;
+ }
+
+ Node* target = head;
+ target->storage.get().~T();
+ head = target->next;
+ target->next = free_nodes;
+ free_nodes = target;
+ --used_nodes_count;
+ return true;
+ }
+
+ /// If the content of an element is updated is updated after the insertion
+ /// then, the list can be in an unordered state.
+ /// This function help; it update the position of an iterator in the list.
+ void update(iterator it) {
+ Node* target = it.get_node();
+ Node* hint = head;
+
+ if (target == NULL) {
+ return;
+ }
+
+ // remove the node from the list
+ if (target == head) {
+ // if it is the only node in the list, just return
+ // it is not needed to update its position
+ if (target->next == NULL) {
+ return;
+ }
+
+ // if the order is already correct, just return
+ if (target->storage.get() < target->next->storage.get()) {
+ return;
+ }
+
+ // otherwise remove the node from the list
+ // and update the hint
+ head = target->next;
+ hint = head;
+ } else {
+ bool node_found = false;
+ for (Node* current = head; current != NULL; current = current->next) {
+ if (current->next == target) {
+ // check if it is needed to move the node
+ if (current->storage.get() < target->storage.get()) {
+ if (target->next == NULL) {
+ return;
+ }
+
+ if (target->storage.get() < target->next->storage.get()) {
+ return;
+ }
+
+ // there is no need to iterate again the whole list
+ // just mark the hint has the current node
+ hint = current;
+ }
+
+ // remove the node from the list and break out of the loop
+ current->next = target->next;
+ node_found = true;
+ break;
+ }
+ }
+
+ // the node in parameter doesn't belong to this queue
+ if (!node_found) {
+ return;
+ }
+ }
+
+ // insert the node after hint
+ insert_after(hint, target);
+ }
+
+ /// return an iterator to the begining of the queue.
+ iterator begin() {
+ return head;
+ }
+
+ /// return an iterator to the end of the queue.
+ /// @note can't be dereferenced
+ iterator end() {
+ return NULL;
+ }
+
+ /// erase an iterator from the list
+ bool erase(iterator it) {
+ return erase(it.get_node());
+ }
+
+ /// erase a node from the list
+ bool erase(Node* n) {
+ if (n == NULL) {
+ return false;
+ }
+
+ if (head == n) {
+ return pop();
+ }
+
+ Node* current = head;
+ while (current->next) {
+ if (current->next == n) {
+ current->next = n->next;
+ n->storage.get().~T();
+ n->next = free_nodes;
+ free_nodes = n;
+ --used_nodes_count;
+ return true;
+ }
+ current = current->next;
+ }
+ return false;
+ }
+
+ /**
+ * Indicate if the queue is empty or not.
+ * @return true if the queue is empty and false otherwise.
+ * @invariant the queue remains untouched.
+ */
+ bool empty() const {
+ return head == NULL;
+ }
+
+ /**
+ * Indicate if the true is full or not.
+ * @return true if the queue is full and false otherwise.
+ * @invariant the queue remains untouched.
+ */
+ bool full() const {
+ return free_nodes == NULL;
+ }
+
+ /**
+ * Indicate the number of elements in the queue.
+ * @return the number of elements currently held by the queue.
+ * @invariant the queue remains untouched.
+ */
+ std::size_t size() const {
+ return used_nodes_count;
+ }
+
+ /**
+ * Expose the capacity of the queue in terms of number of elements the
+ * queue can hold.
+ * @return the capacity of the queue.
+ * @invariant this function should always return Capacity.
+ */
+ std::size_t capacity() const {
+ return Capacity;
+ }
+
+ /**
+ * Clear the queue from all its elements.
+ */
+ void clear() {
+ while (head) {
+ head->storage.get().~T();
+ Node* tmp = head;
+ head = head->next;
+ tmp->next = free_nodes;
+ free_nodes = tmp;
+ }
+ used_nodes_count = 0;
+ }
+
+private:
+ void initialize() {
+ /// link all the nodes together
+ for (std::size_t i = 0; i < (Capacity - 1); ++i) {
+ nodes[i].next = &nodes[i + 1];
+ }
+ /// the last node does not have a next node
+ nodes[Capacity - 1].next = NULL;
+ /// set all the nodes as free
+ free_nodes = nodes;
+ }
+
+ void copy(const PriorityQueue& other) {
+ if (empty() == false) {
+ clear();
+ }
+
+ Node *to_copy = other.head;
+ Node *previous = NULL;
+ while (to_copy) {
+ // pick a free node
+ Node* new_node = free_nodes;
+ free_nodes = free_nodes->next;
+ new_node->next = NULL;
+
+ // copy content
+ new (new_node->storage.get_storage()) T(to_copy->storage.get());
+
+ // link into the queue or update head then update previous pointer
+ if (previous) {
+ previous->next = new_node;
+ } else {
+ head = new_node;
+ }
+ previous = new_node;
+
+ // update the node to copy
+ to_copy = to_copy->next;
+ }
+ used_nodes_count = other.used_nodes_count;
+ }
+
+ void insert_after(Node* prev, Node* to_insert) {
+ for (; prev != NULL; prev = prev->next) {
+ if (prev->next == NULL || to_insert->storage.get() < prev->next->storage.get()) {
+ to_insert->next = prev->next;
+ prev->next = to_insert;
+ break;
+ }
+ }
+ }
+
+ Node nodes[Capacity]; //< Nodes of the queue
+ Node *free_nodes; //< entry point for the list of free nodes
+ Node *head; //< head of the queue
+ std::size_t used_nodes_count; // number of nodes used
+};
+
+} // namespace eq
+
+#endif /* EVENTQUEUE_STACKPRIORITYQUEUE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/Thunk.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_THUNK_H_
+#define EVENTQUEUE_THUNK_H_
+
+#include "AlignedStorage.h"
+#include "detail/ThunkVTable.h"
+
+namespace eq {
+
+// forward declaration of ThunkVTableGenerator
+namespace detail {
+template<typename T>
+class ThunkVTableGenerator;
+}
+
+/**
+ * A Thunk is a container holding any kind of nullary callable.
+ * It wrap value semantic and function call operations of the inner callable
+ * held.
+ * \note Thunk of callable bound to arguments should be generated by the
+ * function make_thunk.
+ */
+class Thunk {
+ // Size for the internal buffer of the Thunk
+ static const std::size_t BufferSize = 24;
+
+ template<typename T>
+ friend class detail::ThunkVTableGenerator;
+
+public:
+
+ /**
+ * Thunk Empty constructor.
+ * When this thunk is called, if does nothing.
+ */
+ Thunk();
+
+ /**
+ * Construct a Thunk from a nullary callable of type F.
+ * When the call operator is invoked, it call a copy of f ( f() ).
+ */
+ template<typename F>
+ Thunk(const F& f);
+
+ /**
+ * Special constructor for pointer to function.
+ * Allow references to functions to gracefully decay into pointer to function.
+ * Otherwise, reference to function are not copy constructible (their is no
+ * constructible function type in C++).
+ * When the call operator is invoked, it call a copy of f ( f() ).
+ */
+ Thunk(void (*f)());
+
+ /**
+ * Copy construction of a thunk.
+ * Take care that the inner F is correctly copied.
+ */
+ Thunk(const Thunk& other) : _storage(), _vtable() {
+ other._vtable->copy(*this, other);
+ }
+
+ /**
+ * Destruction of the Thunk correctly call the destructor of the
+ * inner callable.
+ */
+ ~Thunk() {
+ _vtable->destroy(*this);
+ }
+
+ /**
+ * Copy assignement from another thunk.
+ * Ensure that the callable held is correctly destroyed then copy
+ * the correctly copy the new one.
+ */
+ Thunk& operator=(const Thunk& other) {
+ if (this == &other) {
+ return *this;
+ }
+ _vtable->destroy(*this);
+ other._vtable->copy(*this, other);
+ return *this;
+ }
+
+ /**
+ * Call operator. Invoke the inner callable.
+ */
+ void operator()() const {
+ _vtable->call(*this);
+ }
+
+private:
+ static void empty_thunk() { }
+
+ AlignedStorage<char[BufferSize]> _storage;
+ const detail::ThunkVTable* _vtable;
+};
+
+} // namespace eq
+
+#include "detail/Thunk.impl.h"
+
+#endif /* EVENTQUEUE_THUNK_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/detail/FunctionAdaptor.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_DETAIL_FUNCTIONADAPTOR_H_
+#define EVENTQUEUE_DETAIL_FUNCTIONADAPTOR_H_
+
+#include "MemberFunctionAdaptor.h"
+
+namespace eq {
+namespace detail {
+
+/**
+ * In C++, several types can be used as function:
+ * - function pointer
+ * - member functions
+ * - function like object
+ * While function pointer and function like object can be used with the function
+ * call syntax, the function call syntax can't be applied for function pointers.
+ * This meta function yield takes a callable type F in input and as a result
+ * return a type which can be constructed from F and used with the function call
+ * syntax.
+ *
+ * \code
+ * class Foo;
+ *
+ * Foo foo;
+ * typedef void (Foo::*foo_function_t)();
+ * foo_function_t foo_function = &Foo::some_function;
+ *
+ * //The following will fail:
+ * //foo_function(foo)
+ *
+ * typedef FunctionAdaptor<foo_function_t>::type foo_function_adaptor_t;
+ * foo_function_adaptor_t foo_function_adapted(foo_function);
+ * foo_function_adapted(foo);
+ *
+ * \endcode
+ *
+ * \tparam F The type of the object to adapt.
+ */
+template<typename F>
+struct FunctionAdaptor {
+ /**
+ * Common case (function pointer and function like object).
+ * Yield itself, no addaptation needed.
+ */
+ typedef F type;
+};
+
+/**
+ * Partial specializetion for member function with no arguments
+ */
+template<typename T>
+struct FunctionAdaptor<void(T::*)()> {
+ /**
+ * Yield a member function adaptor.
+ */
+ typedef MemberFunctionAdaptor0<T> type;
+};
+
+/**
+ * Partial specializetion for member function with one argument
+ */
+template<typename T, typename Arg0>
+struct FunctionAdaptor<void(T::*)(Arg0)> {
+ /**
+ * Yield a member function adaptor.
+ */
+ typedef MemberFunctionAdaptor1<T, Arg0> type;
+};
+
+/**
+ * Partial specializetion for member function with two arguments
+ */
+template<typename T, typename Arg0, typename Arg1>
+struct FunctionAdaptor<void(T::*)(Arg0, Arg1)> {
+ /**
+ * Yield a member function adaptor.
+ */
+ typedef MemberFunctionAdaptor2<T, Arg0, Arg1> type;
+};
+
+} // namespace detail
+} // namespace eq
+
+#endif /* EVENTQUEUE_DETAIL_FUNCTIONADAPTOR_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/detail/MemberFunctionAdaptor.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_DETAIL_MEMBERFUNCTIONADAPTOR_H_
+#define EVENTQUEUE_DETAIL_MEMBERFUNCTIONADAPTOR_H_
+
+namespace eq {
+namespace detail {
+
+/**
+ * Adaptor for member function without argument.
+ * It wrap member function into a function like object to make it usable like
+ * a regular function.
+ * \tparam T the type the class/struct holding the member function.
+ * \code
+ * struct Foo {
+ * void fn();
+ * };
+ *
+ * Foo foo;
+ * MemberFunctionAdaptor0<Foo> fn_adapted(&Foo::fn);
+ *
+ * fn_adapted(foo); // work
+ * fn_adapted(&foo); // work
+ * \endcode
+ */
+template<typename T>
+struct MemberFunctionAdaptor0 {
+ /**
+ * Construct a member function adaptor.
+ * \param fn The member function to addapt.
+ */
+ MemberFunctionAdaptor0(void (T::*fn)()) :
+ _fn(fn) {
+ }
+
+ /**
+ * Call operator for pointer of T
+ */
+ void operator()(T* self) const {
+ (self->*_fn)();
+ }
+
+ /**
+ * Call operator for reference of T
+ */
+ void operator()(T& self) const {
+ (self.*_fn)();
+ }
+
+private:
+ void (T::* const _fn)();
+};
+
+
+/**
+ * Adaptor for member function with one argument.
+ * It wrap member function into a function like object to make it usable like
+ * a regular function.
+ * \tparam T the type the class/struct holding the member function.
+ * \code
+ * struct Foo {
+ * void fn(int);
+ * };
+ *
+ * Foo foo;
+ * MemberFunctionAdaptor1<Foo> fn_adapted(&Foo::fn);
+ *
+ * fn_adapted(foo, 42); // work
+ * fn_adapted(&foo, 42); // work
+ * \endcode
+ */
+template<typename T, typename Arg0>
+struct MemberFunctionAdaptor1 {
+ /**
+ * Construct a member function adaptor.
+ * \param fn The member function to addapt.
+ */
+ MemberFunctionAdaptor1(void (T::*fn)(Arg0)) :
+ _fn(fn) {
+ }
+
+ /**
+ * Call operator for pointer of T
+ */
+ void operator()(T* self, Arg0 arg0) const {
+ (self->*_fn)(arg0);
+ }
+
+ /**
+ * Call operator for reference of T
+ */
+ void operator()(T& self, Arg0 arg0) const {
+ (self.*_fn)(arg0);
+ }
+
+private:
+ void (T::* const _fn)(Arg0);
+};
+
+
+/**
+ * Adaptor for member function with two arguments.
+ * It wrap member function into a function like object to make it usable like
+ * a regular function.
+ * \tparam T the type the class/struct holding the member function.
+ * \code
+ * struct Foo {
+ * void fn(int, const char*);
+ * };
+ *
+ * Foo foo;
+ * MemberFunctionAdaptor2<Foo> fn_adapted(&Foo::fn);
+ *
+ * fn_adapted(foo, 42, "toto"); // work
+ * fn_adapted(&foo, 42, "toto"); // work
+ * \endcode
+ */
+template<typename T, typename Arg0, typename Arg1>
+struct MemberFunctionAdaptor2 {
+ /**
+ * Construct a member function adaptor.
+ * \param fn The member function to addapt.
+ */
+ MemberFunctionAdaptor2(void (T::*fn)(Arg0, Arg1)) : _fn(fn) { }
+
+ /**
+ * Call operator for pointer of T
+ */
+ void operator()(T* self, Arg0 arg0, Arg1 arg1) const {
+ (self->*_fn)(arg0, arg1);
+ }
+
+ /**
+ * Call operator for reference of T
+ */
+ void operator()(T& self, Arg0 arg0, Arg1 arg1) const {
+ (self.*_fn)(arg0, arg1);
+ }
+
+private:
+ void (T::* const _fn)(Arg0, Arg1);
+};
+
+} // namespace detail
+} // namespace eq
+
+#endif /* EVENTQUEUE_DETAIL_MEMBERFUNCTIONADAPTOR_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/detail/Thunk.impl.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_DETAIL_THUNK_IMPL_H_
+#define EVENTQUEUE_DETAIL_THUNK_IMPL_H_
+
+#include <new>
+#include "ThunkVTableGenerator.h"
+
+namespace eq {
+
+/**
+ * Thunk constructor Implementation.
+ * Due to the way templates and forwarding work in C++, it was not possible to
+ * provide this implementation in Thunk.h
+ */
+template<typename F>
+Thunk::Thunk(const F& f) :
+ _storage(),
+ _vtable(&detail::ThunkVTableGenerator<F>::vtable) {
+ typedef __attribute__((unused)) char F_is_too_big_for_the_Thunk[sizeof(F) <= sizeof(_storage) ? 1 : -1];
+ new(_storage.get_storage(0)) F(f);
+}
+
+/**
+ * Specialization for function pointers.
+ * This overload will be chosen when the tyope in input is a reference to a function.
+ * @param f The function to transform in Thunk.
+ */
+inline Thunk::Thunk(void (*f)()) :
+ _storage(),
+ _vtable(&detail::ThunkVTableGenerator<void(*)()>::vtable) {
+ typedef void(*F)();
+ typedef __attribute__((unused)) char F_is_too_big_for_the_Thunk[sizeof(F) <= sizeof(_storage) ? 1 : -1];
+ new(_storage.get_storage(0)) F(f);
+}
+
+/**
+ * Thunk empty constructor Implementation.
+ * Due to the way templates and forwarding work in C++, it was not possible to
+ * provide this implementation in Thunk.h
+ */
+inline Thunk::Thunk() :
+ _storage(),
+ _vtable(&detail::ThunkVTableGenerator<void(*)()>::vtable) {
+ typedef void(*F)();
+ typedef __attribute__((unused)) char F_is_too_big_for_the_Thunk[sizeof(F) <= sizeof(_storage) ? 1 : -1];
+ new(_storage.get_storage(0)) F(empty_thunk);
+}
+
+} // namespace eq
+
+#endif /* EVENTQUEUE_DETAIL_THUNK_IMPL_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/detail/ThunkVTable.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_DETAIL_THUNKVTABLE_H_
+#define EVENTQUEUE_DETAIL_THUNKVTABLE_H_
+
+namespace eq {
+
+// forward declaration of the Thunk class
+class Thunk;
+
+namespace detail {
+
+/**
+ * This POD is used as a vtable by Thunk implementation.
+ * Thunk is a value type for all type nullary callable and therefore standard
+ * polymorphism is not suitable for that use case.
+ * Instead, the vtable is generated for each type contained in a thunk.
+ * This structure is the prototype of such vtable.
+ * \note see ThunkVTableGenerator for implementation and the generation of
+ * Thunk vtables.
+ */
+struct ThunkVTable {
+ typedef Thunk thunk_t;
+
+ /**
+ * destroy a thunk (act like a destructor).
+ */
+ void (* const destroy)(thunk_t& self);
+
+ /**
+ * Copy self into dest.
+ * It is expected that dest is empty.
+ */
+ void (* const copy)(thunk_t& dest, const thunk_t& self);
+
+ /**
+ * Synthetized call for the inner object of the thunk_t.
+ */
+ void (* const call)(const thunk_t& self);
+};
+
+} // namespace detail
+} // namespace eq
+
+#endif /* EVENTQUEUE_DETAIL_THUNKVTABLE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/detail/ThunkVTableGenerator.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_DETAIL_THUNKVTABLEGENERATOR_H_
+#define EVENTQUEUE_DETAIL_THUNKVTABLEGENERATOR_H_
+
+// imported from Thunk.h
+
+namespace eq {
+namespace detail {
+
+/**
+ * Thunk VTable Generator.
+ * This class generate the vtable of a type F for a Thunk.
+ * \tparam F The type of the callable for which the Thunk vtable should be
+ * generated.
+ */
+template<typename F>
+struct ThunkVTableGenerator {
+ typedef Thunk thunk_t;
+
+ /**
+ * Implementation of destructor for Thunk holding an F.
+ * @param self The thunk to destroy
+ */
+ static void destroy(thunk_t& self) {
+ get_ptr(self)->~F();
+ }
+
+ /**
+ * Implementation of copy (used by copy constructor and copy assignment)
+ * for a Thunk holding an F.
+ * @param dest The thunk receiving the copy.
+ * @param self The thunk to copy.
+ */
+ static void copy(thunk_t& dest, const thunk_t& self) {
+ new (get_ptr(dest)) F(*get_ptr(self));
+ dest._vtable = self._vtable;
+ }
+
+ /**
+ * Implementation of call operator for a Thunk holding an F.
+ * @param self The thunk containing the F to call.
+ */
+ static void call(const thunk_t& self) {
+ (*get_ptr(self))();
+ }
+
+ /**
+ * The Thunk vtable for an F.
+ */
+ static const ThunkVTable vtable;
+
+private:
+ /**
+ * Accessor to the pointer to F contained in the Thunk.
+ */
+ static F* get_ptr(thunk_t& thunk) {
+ return static_cast<F*>(thunk._storage.get_storage(0));
+ }
+
+ /**
+ * Accessor to the const pointer to F contained in the const Thunk.
+ */
+ static const F* get_ptr(const thunk_t& thunk) {
+ return static_cast<const F*>(thunk._storage.get_storage(0));
+ }
+};
+
+/**
+ * Instantiation of the Thunk vtable of F.
+ */
+template<typename F>
+const ThunkVTable ThunkVTableGenerator<F>::vtable = {
+ ThunkVTableGenerator<F>::destroy,
+ ThunkVTableGenerator<F>::copy,
+ ThunkVTableGenerator<F>::call
+};
+
+} // namespace detail
+} // namespace eq
+
+#endif /* EVENTQUEUE_DETAIL_THUNKVTABLEGENERATOR_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/detail/Thunks.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2016, ARM Limited, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef EVENTQUEUE_DETAIL_THUNKS_H_
+#define EVENTQUEUE_DETAIL_THUNKS_H_
+
+namespace eq {
+namespace detail {
+
+/**
+ * Generate a Thunk for a callable of type F with one argument.
+ * This class is a function like object containing the function to call and
+ * its argument. When it is invoked, F is invoked with the argument passed
+ * at construction time.
+ * \tparam F the type of the callable.
+ * \tparam Arg0 type of the first parameter of F to pass to F.
+ */
+template<typename F, typename Arg0>
+struct Thunk_1 {
+ /**
+ * Construct the Thunk and bind its arguments.
+ * \param fn the callable, it will be invoked with arg0.
+ * \param arg0 The first argument to pass to fn when this object is called.
+ * \note member function should be adapted by using FunctionAdaptor
+ */
+ Thunk_1(const F& fn, const Arg0& arg0) :
+ _fn(fn), _arg0(arg0) {
+ }
+
+ /**
+ * Apply arg0 to fn.
+ */
+ void operator()() const {
+ _fn(_arg0);
+ }
+
+private:
+ mutable F _fn;
+ mutable Arg0 _arg0;
+};
+
+/**
+ * Generate a Thunk for a callable of type F with two arguments.
+ * This class is a function like object containing the function to call and
+ * its arguments. When it is invoked, F is invoked with the arguments passed
+ * at construction time.
+ * \tparam F the type of the callable.
+ * \tparam Arg0 type of the first parameter to pass to F.
+ * \tparam Arg1 type of the second parameter to pass to F.
+ */
+template<typename F, typename Arg0, typename Arg1>
+struct Thunk_2 {
+ /**
+ * Construct the Thunk and bind its arguments.
+ * \param fn the callable, it will be invoked with arg0 and arg1.
+ * \param arg0 The first argument to pass to fn when this object is called.
+ * \param arg1 The second argument to pass to fn when this object is called.
+ * \note member function should be adapted by using FunctionAdaptor
+ */
+ Thunk_2(const F& fn, const Arg0& arg0, const Arg1& arg1) :
+ _fn(fn),
+ _arg0(arg0),
+ _arg1(arg1) {
+ }
+
+ /**
+ * Apply arg0 and arg1 to fn.
+ */
+ void operator()() const {
+ _fn(_arg0, _arg1);
+ }
+
+private:
+ mutable F _fn;
+ mutable Arg0 _arg0;
+ mutable Arg1 _arg1;
+};
+
+/**
+ * Generate a Thunk for a callable of type F with three arguments.
+ * This class is a function like object containing the function to call and
+ * its arguments. When it is invoked, F is invoked with the arguments passed
+ * at construction time.
+ * \tparam F the type of the callable.
+ * \tparam Arg0 type of the first parameter to pass to F.
+ * \tparam Arg1 type of the second parameter to pass to F.
+ * \tparam Arg2 type of the third parameter to pass to F.
+ */
+template<typename F, typename Arg0, typename Arg1, typename Arg2>
+struct Thunk_3 {
+ /**
+ * Construct the Thunk and bind its arguments.
+ * \param fn the callable, it will be invoked with arg0, arg1 and arg2.
+ * \param arg0 The first argument to pass to fn when this object is called.
+ * \param arg1 The second argument to pass to fn when this object is called.
+ * \param arg2 The third argument to pass to fn when this object is called.
+ * \note member function should be adapted by using FunctionAdaptor
+ */
+ Thunk_3(const F& fn, const Arg0& arg0, const Arg1& arg1, const Arg2& arg2) :
+ _fn(fn),
+ _arg0(arg0),
+ _arg1(arg1),
+ _arg2(arg2){
+ }
+
+ /**
+ * Apply arg0, arg1 and arg2 to fn.
+ */
+ void operator()() const {
+ _fn(_arg0, _arg1, _arg2);
+ }
+
+private:
+ mutable F _fn;
+ mutable Arg0 _arg0;
+ mutable Arg1 _arg1;
+ mutable Arg2 _arg2;
+};
+
+} // namespace detail
+} // namespace eq
+
+#endif /* EVENTQUEUE_DETAIL_THUNKS_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/EventQueue/util/CriticalSectionLock.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,59 @@
+/*
+ * PackageLicenseDeclared: Apache-2.0
+ * Copyright (c) 2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MBED_UTIL_CRITICAL_SECTION_LOCK_H__
+#define __MBED_UTIL_CRITICAL_SECTION_LOCK_H__
+
+#include <stdint.h>
+#include "cmsis.h"
+
+namespace mbed {
+namespace util {
+
+/** RAII object for disabling, then restoring, interrupt state
+ * Usage:
+ * @code
+ *
+ * void f() {
+ * // some code here
+ * {
+ * CriticalSectionLock lock;
+ * // Code in this block will run with interrupts disabled
+ * }
+ * // interrupts will be restored to their previous state
+ * }
+ * @endcode
+ */
+class CriticalSectionLock {
+public:
+ CriticalSectionLock() {
+ _state = __get_PRIMASK();
+ __disable_irq();
+ }
+
+ ~CriticalSectionLock() {
+ __set_PRIMASK(_state);
+ }
+
+private:
+ uint32_t _state;
+};
+
+} // namespace util
+} // namespace mbed
+
+#endif // #ifndef __MBED_UTIL_CRITICAL_SECTION_LOCK_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/PersistentStorageHelper/ConfigParamsPersistence.cpp Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,55 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConfigParamsPersistence.h"
+
+#if !defined(TARGET_NRF51822) && !defined(TARGET_NRF52832) /* Persistent storage supported on nrf51 platforms */
+ /**
+ * When not using an nRF51-based target then persistent storage is not available.
+ */
+ #warning "EddystoneService is not configured to store configuration data in non-volatile memory"
+
+ bool loadEddystoneServiceConfigParams(EddystoneService::EddystoneParams_t *paramsP)
+ {
+ /* Avoid compiler warnings */
+ (void) paramsP;
+
+ /*
+ * Do nothing and let the main program set Eddystone params to
+ * defaults
+ */
+ return false;
+ }
+
+ void saveEddystoneServiceConfigParams(const EddystoneService::EddystoneParams_t *paramsP)
+ {
+ /* Avoid compiler warnings */
+ (void) paramsP;
+
+ /* Do nothing... */
+ return;
+ }
+
+ void saveEddystoneTimeParams(const TimeParams_t *timeP)
+ {
+ /* Avoid compiler warnings */
+ (void) timeP;
+
+ /* Do nothing... */
+ return;
+ }
+
+#endif /* #ifdef TARGET_NRF51822 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/PersistentStorageHelper/ConfigParamsPersistence.h Tue Nov 29 06:29:10 2016 +0000 @@ -0,0 +1,67 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BLE_CONFIG_PARAMS_PERSISTENCE_H__ +#define __BLE_CONFIG_PARAMS_PERSISTENCE_H__ + +#include "../EddystoneService.h" + +/** + * Generic API to load the Eddystone Service configuration parameters from persistent + * storage. If persistent storage isn't available, the persistenceSignature + * member of params may be left un-initialized to the MAGIC, and this will cause + * a reset to default values. + * + * @param[out] paramsP + * The parameters to be filled in from persistence storage. This + * argument can be NULL if the caller is only interested in + * discovering the persistence status of params. + * + * @return true if params were loaded from persistent storage and have usefully + * initialized fields. + */ +bool loadEddystoneServiceConfigParams(EddystoneService::EddystoneParams_t *paramsP); + +/** + * Generic API to store the Eddystone Service configuration parameters to persistent + * storage. It typically initializes the persistenceSignature member of the + * params to the MAGIC value to indicate persistence. + * + * @param[in,out] paramsP + * The params to be saved; persistenceSignature member gets + * updated if persistence is successful. + * + * @note The save operation may be asynchronous. It may be a short while before + * the request takes affect. Reading back saved configParams may not yield + * correct behaviour if attempted soon after a store. + */ +void saveEddystoneServiceConfigParams(const EddystoneService::EddystoneParams_t *paramsP); + +/** + * Generic API to store the Eddystone TimeParams (a subset of Config Params) for + * speed/power efficiency. + * + * @param[in,out] timeP + * The params to be saved; persistenceSignature member gets + * updated if persistence is successful. + * + * @note The save operation may be asynchronous. It may be a short while before + * the request takes affect. Reading back saved configParams may not yield + * correct behaviour if attempted soon after a store. + */ +void saveEddystoneTimeParams(const TimeParams_t *timeP); + +#endif /* #ifndef __BLE_CONFIG_PARAMS_PERSISTENCE_H__*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/PersistentStorageHelper/nrfPersistentStorageHelper/nrfConfigParamsPersistence.cpp Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,140 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(TARGET_NRF51822) || defined(TARGET_NRF52832) /* Persistent storage supported on nrf51 platforms */
+
+extern "C" {
+ #include "pstorage.h"
+}
+
+#include "nrf_error.h"
+#include "../../EddystoneService.h"
+#include <cstddef>
+
+/**
+ * Nordic specific structure used to store params persistently.
+ * It extends EddystoneService::EddystoneParams_t with a persistence signature.
+ */
+struct PersistentParams_t {
+ EddystoneService::EddystoneParams_t params;
+ uint32_t persistenceSignature; /* This isn't really a parameter, but having the expected
+ * magic value in this field indicates persistence. */
+
+ static const uint32_t MAGIC = 0x1BEAC000; /* Magic that identifies persistence */
+};
+
+/**
+ * The following is a module-local variable to hold configuration parameters for
+ * short periods during flash access. This is necessary because the pstorage
+ * APIs don't copy in the memory provided as data source. The memory cannot be
+ * freed or reused by the application until this flash access is complete. The
+ * load and store operations in this module initialize persistentParams and then
+ * pass it on to the 'pstorage' APIs.
+ */
+static PersistentParams_t persistentParams;
+
+static pstorage_handle_t pstorageHandle;
+
+/**
+ * Dummy callback handler needed by Nordic's pstorage module. This is called
+ * after every flash access.
+ */
+static void pstorageNotificationCallback(pstorage_handle_t *p_handle,
+ uint8_t op_code,
+ uint32_t result,
+ uint8_t *p_data,
+ uint32_t data_len)
+{
+ /* Supress compiler warnings */
+ (void) p_handle;
+ (void) op_code;
+ (void) result;
+ (void) p_data;
+ (void) data_len;
+
+ /* APP_ERROR_CHECK(result); */
+}
+
+/* Platform-specific implementation for persistence on the nRF5x. Based on the
+ * pstorage module provided by the Nordic SDK. */
+bool loadEddystoneServiceConfigParams(EddystoneService::EddystoneParams_t *paramsP)
+{
+ static bool pstorageInitied = false;
+ if (!pstorageInitied) {
+ pstorage_init();
+
+ static pstorage_module_param_t pstorageParams = {
+ .cb = pstorageNotificationCallback,
+ .block_size = sizeof(PersistentParams_t),
+ .block_count = 1
+ };
+ pstorage_register(&pstorageParams, &pstorageHandle);
+ pstorageInitied = true;
+ }
+
+ if ((pstorage_load(reinterpret_cast<uint8_t *>(&persistentParams), &pstorageHandle, sizeof(PersistentParams_t), 0) != NRF_SUCCESS) ||
+ (persistentParams.persistenceSignature != PersistentParams_t::MAGIC)) {
+ // On failure zero out and let the service reset to defaults
+ memset(paramsP, 0, sizeof(EddystoneService::EddystoneParams_t));
+ return false;
+ }
+
+ memcpy(paramsP, &persistentParams.params, sizeof(EddystoneService::EddystoneParams_t));
+ return true;
+}
+
+/* Platform-specific implementation for persistence on the nRF5x. Based on the
+ * pstorage module provided by the Nordic SDK. */
+void saveEddystoneServiceConfigParams(const EddystoneService::EddystoneParams_t *paramsP)
+{
+ memcpy(&persistentParams.params, paramsP, sizeof(EddystoneService::EddystoneParams_t));
+ if (persistentParams.persistenceSignature != PersistentParams_t::MAGIC) {
+ persistentParams.persistenceSignature = PersistentParams_t::MAGIC;
+ pstorage_store(&pstorageHandle,
+ reinterpret_cast<uint8_t *>(&persistentParams),
+ sizeof(PersistentParams_t),
+ 0 /* offset */);
+ } else {
+ pstorage_update(&pstorageHandle,
+ reinterpret_cast<uint8_t *>(&persistentParams),
+ sizeof(PersistentParams_t),
+ 0 /* offset */);
+ }
+}
+
+/* Saves only the TimeParams (a subset of Config Params) for speed/power efficiency
+ * Platform-specific implementation for persistence on the nRF5x. Based on the
+ * pstorage module provided by the Nordic SDK. */
+void saveEddystoneTimeParams(const TimeParams_t *timeP)
+{
+ // Copy the time params object to the main datastructure
+ memcpy(&persistentParams.params.timeParams, timeP, sizeof(TimeParams_t));
+ // Test if this is the first pstorage update, or an update
+ if (persistentParams.persistenceSignature != PersistentParams_t::MAGIC) {
+ persistentParams.persistenceSignature = PersistentParams_t::MAGIC;
+ pstorage_store(&pstorageHandle,
+ reinterpret_cast<uint8_t *>(&persistentParams),
+ sizeof(TimeParams_t),
+ offsetof(PersistentParams_t, params.timeParams) /* offset */);
+ } else {
+ pstorage_update(&pstorageHandle,
+ reinterpret_cast<uint8_t *>(&persistentParams),
+ sizeof(TimeParams_t),
+ offsetof(PersistentParams_t, params.timeParams) /* offset */);
+ }
+}
+
+#endif /* #ifdef TARGET_NRF51822 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/TLMFrame.cpp Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2006-2016 Google Inc, All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TLMFrame.h"
+#include "EddystoneService.h"
+
+TLMFrame::TLMFrame(uint8_t tlmVersionIn,
+ uint16_t tlmBatteryVoltageIn,
+ uint16_t tlmBeaconTemperatureIn,
+ uint32_t tlmPduCountIn,
+ uint32_t tlmTimeSinceBootIn) :
+ tlmVersion(tlmVersionIn),
+ lastTimeSinceBootRead(0),
+ tlmBatteryVoltage(tlmBatteryVoltageIn),
+ tlmBeaconTemperature(tlmBeaconTemperatureIn),
+ tlmPduCount(tlmPduCountIn),
+ tlmTimeSinceBoot(tlmTimeSinceBootIn)
+{
+}
+
+void TLMFrame::setTLMData(uint8_t tlmVersionIn)
+{
+ /* According to the Eddystone spec BatteryVoltage is 0 and
+ * BeaconTemperature is 0x8000 if not supported
+ */
+ tlmVersion = tlmVersionIn;
+ tlmBatteryVoltage = 0;
+ tlmBeaconTemperature = 0x8000;
+ tlmPduCount = 0;
+ tlmTimeSinceBoot = 0;
+}
+
+void TLMFrame::setData(uint8_t *rawFrame) // add eidTime - a 4 byte quantity
+{
+ size_t index = 0;
+ rawFrame[index++] = EDDYSTONE_UUID_SIZE + FRAME_SIZE_TLM; // Length of Frame
+ rawFrame[index++] = EDDYSTONE_UUID[0]; // 16-bit Eddystone UUID
+ rawFrame[index++] = EDDYSTONE_UUID[1];
+ rawFrame[index++] = FRAME_TYPE_TLM; // Eddystone frame type = Telemetry
+ rawFrame[index++] = tlmVersion; // TLM Version Number
+ rawFrame[index++] = (uint8_t)(tlmBatteryVoltage >> 8); // Battery Voltage[0]
+ rawFrame[index++] = (uint8_t)(tlmBatteryVoltage >> 0); // Battery Voltage[1]
+ rawFrame[index++] = (uint8_t)(tlmBeaconTemperature >> 8); // Beacon Temp[0]
+ rawFrame[index++] = (uint8_t)(tlmBeaconTemperature >> 0); // Beacon Temp[1]
+ rawFrame[index++] = (uint8_t)(tlmPduCount >> 24); // PDU Count [0]
+ rawFrame[index++] = (uint8_t)(tlmPduCount >> 16); // PDU Count [1]
+ rawFrame[index++] = (uint8_t)(tlmPduCount >> 8); // PDU Count [2]
+ rawFrame[index++] = (uint8_t)(tlmPduCount >> 0); // PDU Count [3]
+ rawFrame[index++] = (uint8_t)(tlmTimeSinceBoot >> 24); // Time Since Boot [0]
+ rawFrame[index++] = (uint8_t)(tlmTimeSinceBoot >> 16); // Time Since Boot [1]
+ rawFrame[index++] = (uint8_t)(tlmTimeSinceBoot >> 8); // Time Since Boot [2]
+ rawFrame[index++] = (uint8_t)(tlmTimeSinceBoot >> 0); // Time Since Boot [3]
+}
+
+void TLMFrame::encryptData(uint8_t* rawFrame, uint8_t* eidIdentityKey, uint8_t rotationPeriodExp, uint32_t beaconTimeSecs) {
+ // Initialize AES data
+ mbedtls_aes_context ctx;
+ mbedtls_aes_init(&ctx);
+ mbedtls_aes_setkey_enc(&ctx, eidIdentityKey, sizeof(EidIdentityKey_t) *8 );
+ // Change the TLM version number to the encrypted version
+ rawFrame[VERSION_OFFSET] = ETLM_VERSION; // Encrypted TLM Version number
+ // Create EAX Params
+ uint8_t nonce[ETLM_NONCE_LEN];
+ // Calculate the 48-bit nonce
+ generateEtlmNonce(nonce, rotationPeriodExp, beaconTimeSecs);
+
+ uint8_t* input = rawFrame + DATA_OFFSET; // array size 12
+ uint8_t output[ETLM_DATA_LEN]; // array size 16 (4 bytes are added: SALT[2], MIC[2])
+ memset(output, 0, ETLM_DATA_LEN);
+ uint8_t emptyHeader[1]; // Empty header
+ LOG(("EIDIdentityKey=\r\n")); EddystoneService::logPrintHex(eidIdentityKey, 16);
+ LOG(("ETLM Encoder INPUT=\r\n")); EddystoneService::logPrintHex(input, 12);
+ LOG(("ETLM SALT=\r\n")); EddystoneService::logPrintHex(nonce+4, 2);
+ LOG(("ETLM Nonce=\r\n")); EddystoneService::logPrintHex(nonce, 6);
+ // Encrypt the TLM to ETLM
+ eddy_aes_authcrypt_eax(&ctx, MBEDTLS_AES_ENCRYPT, nonce, sizeof(nonce), emptyHeader, 0, TLM_DATA_LEN, input, output, output + MIC_OFFSET, MIC_LEN);
+
+#ifndef NO_EAX_TEST
+ // Part of test code to confirm x == EAX_DECRYPT( EAX_ENCRYPT(x) )
+ uint8_t newinput[ETLM_DATA_LEN];
+ memcpy(newinput, output, ETLM_DATA_LEN);
+#endif
+
+ // Only use first 2 bytes of Nonce
+ output[SALT_OFFSET] = nonce[4]; // Nonce MSB
+ output[SALT_OFFSET+1] = nonce[5]; // Nonce LSB
+ LOG(("ETLM output+SALT=\r\n")); EddystoneService::logPrintHex(output, 16);
+ // copy the encrypted payload to the output
+ memcpy((rawFrame + DATA_OFFSET), output, ETLM_DATA_LEN);
+
+#ifndef NO_EAX_TEST
+ // Perform test to confirm x == EAX_DECRYPT( EAX_ENCRYPT(x) )
+ uint8_t buf[ETLM_DATA_LEN];
+ memset(buf, 0, ETLM_DATA_LEN);
+ int ret = eddy_aes_authcrypt_eax(&ctx, MBEDTLS_AES_DECRYPT, nonce, sizeof(nonce), emptyHeader, 0, TLM_DATA_LEN, newinput, buf, newinput + MIC_OFFSET, MIC_LEN);
+ LOG(("ETLM Decoder OUTPUT ret=%d buf=\r\n", ret)); EddystoneService::logPrintHex(buf, 12);
+#endif
+
+ // fix the frame length to the encrypted length
+ rawFrame[FRAME_LEN_OFFSET] = FRAME_SIZE_ETLM + EDDYSTONE_UUID_SIZE;
+ // Free the AES data struture
+ mbedtls_aes_free(&ctx);
+}
+
+
+size_t TLMFrame::getRawFrameSize(uint8_t* rawFrame)
+{
+ return rawFrame[FRAME_LEN_OFFSET];
+}
+
+uint8_t* TLMFrame::getData(uint8_t* rawFrame)
+{
+ if (rawFrame[VERSION_OFFSET] == TLM_VERSION) {
+ setData(rawFrame);
+ }
+ return &(rawFrame[TLM_DATA_OFFSET]);
+}
+
+uint8_t TLMFrame::getDataLength(uint8_t* rawFrame)
+{
+ return rawFrame[FRAME_LEN_OFFSET] - EDDYSTONE_UUID_LEN;
+}
+
+uint8_t* TLMFrame::getAdvFrame(uint8_t* rawFrame){
+ return &(rawFrame[ADV_FRAME_OFFSET]);
+}
+
+uint8_t TLMFrame::getAdvFrameLength(uint8_t* rawFrame){
+ return rawFrame[FRAME_LEN_OFFSET];
+}
+
+void TLMFrame::updateTimeSinceLastBoot(uint32_t nowInMillis)
+{
+ // Measured in tenths of a second
+ tlmTimeSinceBoot += (nowInMillis - lastTimeSinceBootRead) / 100;
+ lastTimeSinceBootRead = nowInMillis;
+}
+
+void TLMFrame::updateBatteryVoltage(uint16_t tlmBatteryVoltageIn)
+{
+ tlmBatteryVoltage = tlmBatteryVoltageIn;
+}
+
+void TLMFrame::updateBeaconTemperature(uint16_t tlmBeaconTemperatureIn)
+{
+ tlmBeaconTemperature = tlmBeaconTemperatureIn;
+}
+
+void TLMFrame::updatePduCount(void)
+{
+ tlmPduCount++;
+}
+
+uint16_t TLMFrame::getBatteryVoltage(void) const
+{
+ return tlmBatteryVoltage;
+}
+
+uint16_t TLMFrame::getBeaconTemperature(void) const
+{
+ return tlmBeaconTemperature;
+}
+
+uint8_t TLMFrame::getTLMVersion(void) const
+{
+ return tlmVersion;
+}
+
+int TLMFrame::generateEtlmNonce(uint8_t* nonce, uint8_t rotationPeriodExp, uint32_t beaconTimeSecs) {
+ int rc = 0;
+ if (sizeof(nonce) != ETLM_NONCE_LEN) {
+ rc = ETLM_NONCE_INVALID_LEN;
+ }
+ uint32_t scaledTime = (beaconTimeSecs >> rotationPeriodExp) << rotationPeriodExp;
+ int index = 0;
+ nonce[index++] = (scaledTime >> 24) & 0xff;
+ nonce[index++] = (scaledTime >> 16) & 0xff;
+ nonce[index++] = (scaledTime >> 8) & 0xff;
+ nonce[index++] = scaledTime & 0xff;
+ EddystoneService::generateRandom(nonce + index, SALT_LEN);
+ return rc;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/TLMFrame.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2006-2016 Google Inc, All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TLMFRAME_H__
+#define __TLMFRAME_H__
+
+#include "EddystoneTypes.h"
+#include "aes_eax.h"
+
+/**
+ * Class that encapsulates data that belongs to the Eddystone-TLM frame. For
+ * more information refer to https://github.com/google/eddystone/tree/master/eddystone-tlm.
+ */
+class TLMFrame
+{
+public:
+ /**
+ * Construct a new instance of this class.
+ *
+ * @param[in] tlmVersionIn
+ * Eddystone-TLM version number to use.
+ * @param[in] tlmBatteryVoltageIn
+ * Initial value for the Eddystone-TLM Battery Voltage.
+ * @param[in] tlmBeaconTemperatureIn
+ * Initial value for the Eddystone-TLM Beacon Temperature.
+ * @param[in] tlmPduCountIn
+ * Initial value for the Eddystone-TLM Advertising PDU Count.
+ * @param[in] tlmTimeSinceBootIn
+ * Intitial value for the Eddystone-TLM time since boot timer.
+ 8 This timer has a 0.1 second resolution.
+ */
+ TLMFrame(uint8_t tlmVersionIn = 0,
+ uint16_t tlmBatteryVoltageIn = 0,
+ uint16_t tlmBeaconTemperatureIn = 0x8000,
+ uint32_t tlmPduCountIn = 0,
+ uint32_t tlmTimeSinceBootIn = 0);
+
+ /**
+ * Set the Eddystone-TLM version number.
+ */
+ void setTLMData(uint8_t tlmVersionIn = 0);
+
+ /**
+ * Construct the raw bytes of the Eddystone-TLM frame that will be directly
+ * used in the advertising packets.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ */
+ void setData(uint8_t *rawFrame);
+
+ /**
+ * Construct the encrypted bytes of the Eddystone-ETLM frame that will be directly
+ * used in the advertising packets.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ * @param[in] eidIdentityKey
+ * Pointer to the eidIdentityKey in use
+ * @param[in] rotationPeriodExp
+ * Rotation exponent for EID
+ * @param[in] beaconTimeSecs
+ * Time in seconds since beacon boot.
+ */
+ void encryptData(uint8_t* rawFrame, uint8_t* eidIdentityKey, uint8_t rotationPeriodExp, uint32_t beaconTimeSecs);
+
+ /**
+ * Get the size of the Eddystone-TLM frame constructed with the
+ * current state of the TLMFrame object.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the Eddystone-TLM frame.
+ */
+ size_t getRawFrameSize(uint8_t* rawFrame);
+
+
+ /**
+ * Get the TLM frame data from the Eddystone-TLM frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return A pointer to the bytes of the Eddystone-TLM frame data.
+ */
+ uint8_t* getData(uint8_t* rawFrame);
+
+ /**
+ * Get the length of the TLM frame data from the Eddystone-TLM frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the Eddystone-TLM frame.
+ */
+ uint8_t getDataLength(uint8_t* rawFrame);
+
+ /**
+ * Get the TLM Adv data from the Eddystone-TLMframe.
+ * This is the full service data included in the BLE service data params
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return A pointer to the bytes of the Eddystone-TLM Adv frame data.
+ */
+ uint8_t* getAdvFrame(uint8_t* rawFrame);
+
+ /**
+ * Get the length of the TLM Adv data from the Eddystone-TLMframe.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the Eddystone-TLM Adv frame data.
+ */
+ uint8_t getAdvFrameLength(uint8_t* rawFrame);
+
+ /**
+ * Update the time since last boot.
+ *
+ * @param[in] nowInMillis
+ * The time since boot in milliseconds.
+ */
+ void updateTimeSinceLastBoot(uint32_t nowInMillis);
+
+ /**
+ * Update the Battery Voltage.
+ *
+ * @param[in] tlmBatteryVoltageIn
+ * The new Battery Voltage value.
+ */
+ void updateBatteryVoltage(uint16_t tlmBatteryVoltageIn);
+
+ /**
+ * Update the Beacon Temperature.
+ *
+ * @param[in] tlmBeaconTemperatureIn
+ * The new Beacon Temperature value.
+ */
+ void updateBeaconTemperature(uint16_t tlmBeaconTemperatureIn);
+
+ /**
+ * Increment the current PDU counter by 1.
+ */
+ void updatePduCount(void);
+
+ /**
+ * Get the current Battery Voltage.
+ *
+ * @return The Battery Voltage.
+ */
+ uint16_t getBatteryVoltage(void) const;
+
+ /**
+ * Get the current Beacon Temperature.
+ *
+ * @return The Beacon Temperature.
+ */
+ uint16_t getBeaconTemperature(void) const;
+
+ /**
+ * Get the current TLM Version number.
+ *
+ * @return The TLM Version number.
+ */
+ uint8_t getTLMVersion(void) const;
+
+ /**
+ * The byte ID of an Eddystone-TLM frame.
+ */
+ static const uint8_t FRAME_TYPE_TLM = 0x20;
+
+ /**
+ * The verison number of the Telemetry packets being used
+ */
+ static const uint8_t DEFAULT_TLM_VERSION = 0;
+
+ /**
+ * The size of an Eddystone-TLM frame.
+ */
+ static const uint8_t FRAME_SIZE_TLM = 14;
+ /**
+ * The size of an Eddystone-ETLM frame.
+ */
+ static const uint8_t FRAME_SIZE_ETLM = (FRAME_SIZE_TLM + 4);
+
+ // Nonce
+ static const uint8_t ETLM_NONCE_LEN = 6;
+ // Version
+ static const uint8_t VERSION_OFFSET = 4;
+ static const uint8_t TLM_VERSION = 0x00;
+ static const uint8_t ETLM_VERSION = 0x01;
+ // Data
+ static const uint8_t DATA_OFFSET = 5;
+ static const uint8_t TLM_DATA_LEN = 12;
+ static const uint8_t ETLM_DATA_LEN = 16;
+ // Salt
+ static const uint8_t SALT_OFFSET = 12;
+ static const uint8_t SALT_LEN = 2;
+ // Message Integrity Check
+ static const uint8_t MIC_OFFSET = 14;
+ static const uint8_t MIC_LEN = 2;
+ // Return codes
+ static const int ETLM_NONCE_INVALID_LEN = -1;
+
+ /**
+ * Constructs 6 byte (48-bit) Nonce from an empty array, rotationExp and beacon time (secs)
+ *
+ * @param[in] nonce
+ * the input and target nonce[] array
+ * @param[in] rotationPeriodExp
+ * Rotation exponent for EID
+ * @param[in] beaconTimeSecs
+ * Time in seconds since beacon boot.
+ * @return[out] return code (success = 0)
+ */
+ int generateEtlmNonce(uint8_t* nonce, uint8_t rotatePeriodExp, uint32_t beaconTimeSecs);
+
+
+private:
+
+ /**
+ * The size (in bytes) of an Eddystone-EID frame.
+ * This is the some of the Eddystone UUID(2 bytes), FrameType, AdvTxPower,
+ * EID Value
+ */
+ // static const uint8_t TLM_FRAME_LEN = 16;
+ // static const uint8_t ETLM_FRAME_LEN = 20;
+ static const uint8_t FRAME_LEN_OFFSET = 0;
+ static const uint8_t EDDYSTONE_UUID_LEN = 2;
+ static const uint8_t TLM_DATA_OFFSET = 3;
+ static const uint8_t ADV_FRAME_OFFSET = 1;
+
+ /**
+ * Eddystone-TLM version value.
+ */
+ uint8_t tlmVersion;
+ /**
+ * Time since boot in milliseconds.
+ */
+ uint32_t lastTimeSinceBootRead;
+ /**
+ * Eddystone-TLM Battery Voltage value.
+ */
+ uint16_t tlmBatteryVoltage;
+ /**
+ * Eddystone-TLM Beacon temperature value.
+ */
+ uint16_t tlmBeaconTemperature;
+ /**
+ * Eddystone-TLM Advertising PDU Count.
+ */
+ uint32_t tlmPduCount;
+ /**
+ * Eddystone-TLM time since boot with 0.1 second resolution.
+ */
+ uint32_t tlmTimeSinceBoot;
+
+
+};
+#endif /* __TLMFRAME_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/UIDFrame.cpp Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2006-2016 Google Inc, All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UIDFrame.h"
+
+UIDFrame::UIDFrame(void) {
+}
+
+void UIDFrame::clearFrame(uint8_t* frame) {
+ frame[FRAME_LEN_OFFSET] = 0; // Set frame length to zero to clear it
+}
+
+void UIDFrame::setData(uint8_t *rawFrame, int8_t advTxPower, const uint8_t* uidData) {
+ size_t index = 0;
+ rawFrame[index++] = UID_HEADER_LEN + UID_LENGTH; // UID length + overhead of four bytes below
+ rawFrame[index++] = EDDYSTONE_UUID[0]; // LSB 16-bit Eddystone UUID (little endian)
+ rawFrame[index++] = EDDYSTONE_UUID[1]; // MSB
+ rawFrame[index++] = FRAME_TYPE_UID; // 1B Type
+ rawFrame[index++] = advTxPower; // 1B Power @ 0meter
+
+ memcpy(rawFrame + index, uidData, UID_LENGTH); // UID = 10B NamespaceID + 6B InstanceID
+}
+
+uint8_t* UIDFrame::getData(uint8_t* rawFrame) {
+ return &(rawFrame[UID_DATA_OFFSET]);
+}
+
+uint8_t UIDFrame::getDataLength(uint8_t* rawFrame)
+{
+ return rawFrame[FRAME_LEN_OFFSET] - EDDYSTONE_UUID_LEN;
+}
+
+uint8_t* UIDFrame::getAdvFrame(uint8_t* rawFrame)
+{
+ return &(rawFrame[ADV_FRAME_OFFSET]);
+}
+
+uint8_t UIDFrame::getAdvFrameLength(uint8_t* rawFrame)
+{
+ return rawFrame[FRAME_LEN_OFFSET];
+}
+
+uint8_t* UIDFrame::getUid(uint8_t* rawFrame)
+{
+ return &(rawFrame[UID_VALUE_OFFSET]);
+}
+
+uint8_t UIDFrame::getUidLength(uint8_t* rawFrame)
+{
+ return rawFrame[FRAME_LEN_OFFSET] - UID_HEADER_LEN;
+}
+
+void UIDFrame::setAdvTxPower(uint8_t* rawFrame, int8_t advTxPower)
+{
+ rawFrame[UID_TXPOWER_OFFSET] = advTxPower;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/UIDFrame.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2006-2016 Google Inc, All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __UIDFRAME_H__
+#define __UIDFRAME_H__
+
+#include <string.h>
+#include "EddystoneTypes.h"
+
+/**
+ * Class that encapsulates data that belongs to the Eddystone-UID frame. For
+ * more information refer to https://github.com/google/eddystone/tree/master/eddystone-uid.
+ */
+class UIDFrame
+{
+public:
+ static const uint8_t UID_LENGTH = 16;
+
+ /**
+ * Construct a new instance of this class.
+ */
+ UIDFrame(void);
+
+ /**
+ * Clear frame (intervally indicated by length = 0 )
+ */
+ void clearFrame(uint8_t* frame);
+
+ /**
+ * Construct the raw bytes of the Eddystone-UID frame that will be directly
+ * used in the advertising packets.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ * @param[in] advPowerLevel
+ * Power level value included in the raw frame.
+ * @param[in] uidData
+ * The actual 16-byte UID data in the raw frame.
+ */
+ void setData(uint8_t* rawFrame, int8_t advTxPower, const uint8_t* uidData);
+
+ /**
+ * Get the UID frame data from the Eddystone-UID frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return A pointer to the bytes of the Eddystone-UID frame data.
+ */
+ uint8_t* getData(uint8_t* rawFrame);
+
+ /**
+ * Get the length of the UID frame data from the Eddystone-UID frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the Eddystone-UID frame.
+ */
+ uint8_t getDataLength(uint8_t* rawFrame);
+
+ /**
+ * Get the UID Adv data from the Eddystone-UID frame.
+ * This is the full service data included in the BLE service data params
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return A pointer to the bytes of the Eddystone-UID Adv frame data.
+ */
+ uint8_t* getAdvFrame(uint8_t* rawFrame);
+
+ /**
+ * Get the length of the UID Adv data from the Eddystone-UID frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the Eddystone-UID Adv frame data.
+ */
+ uint8_t getAdvFrameLength(uint8_t* rawFrame);
+
+ /**
+ * Get just the UID data from the Eddystone-UID frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return A pointer to the bytes of the UID in the Eddystone-UID frame.
+ */
+ uint8_t* getUid(uint8_t* rawFrame);
+
+ /**
+ * Get the length of just the UID data from the Eddystone-UID frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the UID in the Eddystone-UID frame.
+ */
+ uint8_t getUidLength(uint8_t* rawFrame);
+
+ /**
+ * Set the Adv TX Power in the frame. This is necessary because the adv
+ * Tx Power might be updated independent of the data bytes
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ * @param[in] advPowerLevel
+ * Power level value included in the raw frame.
+ *
+ */
+ void setAdvTxPower(uint8_t* rawFrame, int8_t advTxPower);
+
+ /**
+ * The byte ID of an Eddystone-UID frame.
+ */
+ static const uint8_t FRAME_TYPE_UID = 0x00;
+
+private:
+ static const uint8_t UID_FRAME_LEN = 20;
+ static const uint8_t FRAME_LEN_OFFSET = 0;
+ static const uint8_t EDDYSTONE_UUID_LEN = 2;
+ static const uint8_t UID_DATA_OFFSET = 3;
+ static const uint8_t ADV_FRAME_OFFSET = 1;
+ static const uint8_t UID_VALUE_OFFSET = 5;
+ static const uint8_t UID_HEADER_LEN = 4;
+ static const uint8_t UID_TXPOWER_OFFSET = 4;
+ /**
+ * The size (in bytes) of an Eddystone-UID frame.
+ * This is the some of the Eddystone UUID(2 bytes), FrameType, AdvTxPower,
+ * UID Name Length, and UID Instance Length
+ */
+ static const uint8_t FRAME_SIZE_UID = 20;
+ /**
+ * The size (in bytes) of an Eddystone-UID frame.
+ */
+ static const uint8_t UID_NAMESPACEID_LENGTH = 10;
+ static const uint8_t UID_INSTANCEID_LENGTH = 6;
+
+};
+
+#endif /* __UIDFRAME_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/URLFrame.cpp Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2006-2016 Google Inc, All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "URLFrame.h"
+
+/* CONSTRUCTOR */
+URLFrame::URLFrame(void)
+{
+}
+
+void URLFrame::setUnencodedUrlData(uint8_t* rawFrame, int8_t advTxPower, const char *rawUrl)
+{
+ uint8_t encodedUrl[ENCODED_BUF_SIZE];
+ int encodedUrlLen = encodeURL(encodedUrl, rawUrl);
+ encodedUrlLen = (encodedUrlLen > MAX_URL_DATA) ? MAX_URL_DATA : encodedUrlLen;
+ setData(rawFrame, advTxPower, reinterpret_cast<const uint8_t*>(encodedUrl), encodedUrlLen);
+}
+
+void URLFrame::clearFrame(uint8_t* frame) {
+ frame[FRAME_LEN_OFFSET] = 0; // Set frame length to zero to clear it
+}
+
+void URLFrame::setData(uint8_t* rawFrame, int8_t advTxPower, const uint8_t* encodedUrlData, uint8_t encodedUrlLen)
+{
+ uint8_t index = 0;
+ rawFrame[index++] = URL_HEADER_LEN + encodedUrlLen; // INDEX=0 = Frame Length = encodedURL size + 4 bytes of header below
+ rawFrame[index++] = EDDYSTONE_UUID[0]; // FRAME 16-bit Eddystone UUID Low Byte (little endian)
+ rawFrame[index++] = EDDYSTONE_UUID[1]; // FRAME 16-bit Eddystone UUID High Byte
+ rawFrame[index++] = FRAME_TYPE_URL; // URL Frame Type
+ rawFrame[index++] = advTxPower; // Power @ 0meter
+
+ memcpy(rawFrame + index, encodedUrlData, encodedUrlLen);
+}
+
+uint8_t* URLFrame::getData(uint8_t* rawFrame) {
+ return &(rawFrame[URL_DATA_OFFSET]);
+}
+
+
+uint8_t URLFrame::getDataLength(uint8_t* rawFrame) {
+ return rawFrame[FRAME_LEN_OFFSET] - EDDYSTONE_UUID_LEN;
+}
+
+uint8_t* URLFrame::getAdvFrame(uint8_t* rawFrame)
+{
+ return &(rawFrame[ADV_FRAME_OFFSET]);
+}
+
+uint8_t URLFrame::getAdvFrameLength(uint8_t* rawFrame)
+{
+ return rawFrame[FRAME_LEN_OFFSET];
+}
+
+uint8_t* URLFrame::getEncodedUrl(uint8_t* rawFrame)
+{
+ return &(rawFrame[URL_VALUE_OFFSET]);
+}
+
+uint8_t URLFrame::getEncodedUrlLength(uint8_t* rawFrame)
+{
+ return rawFrame[ADV_FRAME_OFFSET] - URL_HEADER_LEN;
+}
+
+
+uint8_t URLFrame::encodeURL(uint8_t* encodedUrl, const char *rawUrl)
+{
+ uint8_t urlDataLength = 0;
+
+ const char *prefixes[] = {
+ "http://www.",
+ "https://www.",
+ "http://",
+ "https://",
+ };
+ const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
+ const char *suffixes[] = {
+ ".com/",
+ ".org/",
+ ".edu/",
+ ".net/",
+ ".info/",
+ ".biz/",
+ ".gov/",
+ ".com",
+ ".org",
+ ".edu",
+ ".net",
+ ".info",
+ ".biz",
+ ".gov"
+ };
+ const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
+
+ /*
+ * Fill with one more 0 than max url data size to ensure its null terminated
+ * And can be printed out for debug purposes
+ */
+ memset(encodedUrl, 0, MAX_URL_DATA + 1);
+
+ if ((rawUrl == NULL) || (strlen(rawUrl) == 0)) {
+ return urlDataLength;
+ }
+
+ /*
+ * handle prefix
+ */
+ for (size_t i = 0; i < NUM_PREFIXES; i++) {
+ size_t prefixLen = strlen(prefixes[i]);
+ if (strncmp(rawUrl, prefixes[i], prefixLen) == 0) {
+ encodedUrl[urlDataLength++] = i;
+ rawUrl += prefixLen;
+ break;
+ }
+ }
+
+ /*
+ * handle suffixes
+ */
+ while (*rawUrl && (urlDataLength <= MAX_URL_DATA)) {
+ /* check for suffix match */
+ size_t i;
+ for (i = 0; i < NUM_SUFFIXES; i++) {
+ size_t suffixLen = strlen(suffixes[i]);
+ if (strncmp(rawUrl, suffixes[i], suffixLen) == 0) {
+ encodedUrl[urlDataLength++] = i;
+ rawUrl += suffixLen;
+ break; /* from the for loop for checking against suffixes */
+ }
+ }
+ /* This is the default case where we've got an ordinary character which doesn't match a suffix. */
+ if (i == NUM_SUFFIXES) {
+ encodedUrl[urlDataLength++] = *rawUrl;
+ ++rawUrl;
+ }
+ }
+ return urlDataLength;
+}
+
+void URLFrame::setAdvTxPower(uint8_t* rawFrame, int8_t advTxPower)
+{
+ rawFrame[URL_TXPOWER_OFFSET] = advTxPower;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/URLFrame.h Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2006-2016 Google Inc, All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __URLFRAME_H__
+#define __URLFRAME_H__
+
+#include "EddystoneTypes.h"
+#include <string.h>
+
+/**
+ * Class that encapsulates data that belongs to the Eddystone-URL frame. For
+ * more information refer to https://github.com/google/eddystone/tree/master/eddystone-url.
+ */
+class URLFrame
+{
+public:
+ /**
+ * Construct a new instance of this class.
+ */
+ URLFrame(void);
+
+ /**
+ * Construct the raw bytes of the Eddystone-URL frame from an unencoded URL
+ * (a null terminated string) that will be directly used in the advertising
+ * packets.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ * @param[in] advPowerLevel
+ * Power level value included in the raw frame.
+ * @param[in] rawURL
+ * A null terminated string containing the URL
+ */
+ void setUnencodedUrlData(uint8_t* rawFrame, int8_t advTxPower, const char *rawUrl);
+
+ /**
+ * Clear frame (intervally indicated by length = 0 )
+ */
+ void clearFrame(uint8_t* frame);
+
+ /**
+ * Construct the raw bytes of the Eddystone-URL frame from an encoded URL
+ * plus length information
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ * @param[in] advPowerLevel
+ * Power level value included in the raw frame.
+ * @param[in] encodedUrlData
+ * A pointer to the encoded URL bytes.
+ * @param[in] encodedUrlLen
+ * The length in bytes of the encoded URL
+ */
+ void setData(uint8_t* rawFrame, int8_t advPowerLevel, const uint8_t* encodedUrlData, uint8_t encodedUrlLen);
+
+ /**
+ * Get the URL frame data from the Eddystone-URL frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return A pointer to the bytes of the Eddystone-URL frame data.
+ */
+ uint8_t* getData(uint8_t* rawFrame);
+
+ /**
+ * Get the length of the URL frame data from the Eddystone-UID frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the Eddystone-URL frame.
+ */
+ uint8_t getDataLength(uint8_t* rawFrame);
+
+ /**
+ * Get the URL Adv data from the Eddystone-URLframe.
+ * This is the full service data included in the BLE service data params
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return A pointer to the bytes of the Eddystone-URLAdv frame data.
+ */
+ uint8_t* getAdvFrame(uint8_t* rawFrame);
+
+ /**
+ * Get the length of the URLAdv data from the Eddystone-URL frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the Eddystone-URL Adv frame data.
+ */
+ uint8_t getAdvFrameLength(uint8_t* rawFrame);
+
+ /**
+ * Get just the encoded URL data from the Eddystone-URL frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return A pointer to the bytes of the encoded URL in the Eddystone-URL
+ * frame.
+ */
+ uint8_t* getEncodedUrl(uint8_t* rawFrame);
+
+ /**
+ * Get the length of just the encoded URL data from the Eddystone-URL frame.
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ *
+ * @return The size in bytes of the encoded URL in the Eddystone-URL frame.
+ */
+ uint8_t getEncodedUrlLength(uint8_t* rawFrame);
+
+ /**
+ * Set the Adv TX Power in the frame. This is necessary because the adv
+ * Tx Power might be updated independent of the data bytes
+ *
+ * @param[in] rawFrame
+ * Pointer to the location where the raw frame will be stored.
+ * @param[in] advPowerLevel
+ * Power level value included in the raw frame.
+ *
+ */
+ void setAdvTxPower(uint8_t* rawFrame, int8_t advTxPower);
+
+ /**
+ * Helper function that encodes a URL null terminated string into the HTTP
+ * URL Encoding required in Eddystone-URL frames. Refer to
+ * https://github.com/google/eddystone/blob/master/eddystone-url/README.md#eddystone-url-http-url-encoding.
+ *
+ * @param[in] encodedUrlData
+ * The encoded bytes of the URL
+ * @param[in] rawUrl
+ * The null terminated string containing a URL to encode.
+ * @return Length of the encodedData in bytes
+ */
+ static uint8_t encodeURL(uint8_t* encodedUrlData, const char* rawUrl);
+
+ /**
+ * The max size (in bytes) of an Eddystone-URL frame.
+ */
+ static const uint8_t ENCODED_BUF_SIZE = 32;
+ /**
+ * The byte ID of an Eddystone-URL frame.
+ */
+ static const uint8_t FRAME_TYPE_URL = 0x10;
+
+private:
+ static const uint8_t FRAME_LEN_OFFSET = 0;
+ static const uint8_t EDDYSTONE_UUID_LEN = 2;
+ static const uint8_t URL_DATA_OFFSET = 3;
+ static const uint8_t ADV_FRAME_OFFSET = 1;
+ static const uint8_t URL_VALUE_OFFSET = 5;
+ static const uint8_t URL_HEADER_LEN = 4;
+ static const uint8_t URL_TXPOWER_OFFSET = 4;
+
+ /**
+ * The minimum size (in bytes) of an Eddystone-URL frame.
+ */
+ static const uint8_t FRAME_MIN_SIZE_URL = 2;
+
+ /**
+ * Offset for playload in a rawFrame UID
+ */
+ static const uint8_t MAX_URL_DATA = 18;
+};
+
+#endif /* __URLFRAME_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/aes_eax.cpp Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2016, Google Inc, All Rights Reserved
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+// #include "aes_eax.h"
+// set defines before loading aes.h
+#define MBEDTLS_CIPHER_MODE_CBC
+#define MBEDTLS_CIPHER_MODE_CTR
+#include "mbedtls/aes.h"
+
+#define EDDY_ERR_EAX_AUTH_FAILED -0x000F /**< Authenticated decryption failed. */
+
+void gf128_double_( unsigned char val[16] )
+{
+ int i;
+ int carry = val[0] >> 7;
+ int xv = (-carry) & 0x87;
+ for (i = 15; i >= 0; i--) {
+ carry = val[i] >> 7;
+ val[i] = (val[i] << 1) ^ xv;
+ xv = carry;
+ }
+}
+
+int compute_cmac_( mbedtls_aes_context *ctx,
+ const unsigned char *input,
+ size_t length,
+ unsigned char param,
+ unsigned char mac[16] )
+{
+ unsigned char buf[16], iv[16];
+ memset(buf, 0, sizeof(buf));
+ buf[15] = param;
+ memset(iv, 0, sizeof(iv));
+ length += 16;
+
+ unsigned char pad[16];
+ memset(pad, 0, sizeof(pad));
+ mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, pad, pad);
+ gf128_double_(pad);
+ if (length & 15) {
+ gf128_double_(pad);
+ pad[length & 15] ^= 0x80;
+ }
+
+ const unsigned char *tmp_input = buf;
+ while (length > 16) {
+ mbedtls_aes_crypt_cbc(ctx, MBEDTLS_AES_ENCRYPT, 16, iv, tmp_input, buf);
+ if (tmp_input == buf) {
+ tmp_input = input;
+ } else {
+ tmp_input += 16;
+ }
+ length -= 16;
+ }
+
+ size_t i;
+ for (i = 0; i < length; i++)
+ pad[i] ^= tmp_input[i];
+
+ mbedtls_aes_crypt_cbc(ctx, MBEDTLS_AES_ENCRYPT, 16, iv, pad, mac);
+ return 0;
+}
+
+int eddy_aes_authcrypt_eax( mbedtls_aes_context *ctx,
+ int mode,
+ const unsigned char *nonce,
+ size_t nonce_length,
+ const unsigned char *header,
+ size_t header_length,
+ size_t message_length,
+ const unsigned char *input,
+ unsigned char *output,
+ unsigned char *tag,
+ size_t tag_length )
+{
+ unsigned char header_mac[16];
+ unsigned char nonce_mac[16];
+ unsigned char ciphertext_mac[16];
+ uint8_t i;
+ compute_cmac_(ctx, header, header_length, 1, header_mac);
+ compute_cmac_(ctx, nonce, nonce_length, 0, nonce_mac);
+ if (mode == MBEDTLS_AES_DECRYPT) {
+ compute_cmac_(ctx, input, message_length, 2, ciphertext_mac);
+ unsigned char n_ok = 0;
+ for (i = 0; i < tag_length; i++) {
+ ciphertext_mac[i] ^= header_mac[i];
+ ciphertext_mac[i] ^= nonce_mac[i];
+ ciphertext_mac[i] ^= tag[i];
+ n_ok |= ciphertext_mac[i];
+ }
+ if (n_ok)
+ return EDDY_ERR_EAX_AUTH_FAILED;
+ }
+ size_t nc_off = 0;
+ unsigned char nonce_copy[16];
+ memcpy(nonce_copy, nonce_mac, sizeof(nonce_mac));
+ unsigned char sb[16];
+ mbedtls_aes_crypt_ctr(ctx, message_length, &nc_off, nonce_copy, sb, input, output);
+ if (mode == MBEDTLS_AES_ENCRYPT) {
+ compute_cmac_(ctx, output, message_length, 2, ciphertext_mac);
+ for (i = 0; i < tag_length; i++)
+ tag[i] = header_mac[i] ^ nonce_mac[i] ^ ciphertext_mac[i];
+ }
+ return 0;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/aes_eax.h Tue Nov 29 06:29:10 2016 +0000 @@ -0,0 +1,47 @@ +#if !defined(AES_EAX_H__INCLUDED__) +#define AES_EAX_H__INCLUDED__ + +#define MBEDTLS_CIPHER_MODE_CBC +/* + * Copyright (c) 2016, Google Inc, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define MBEDTLS_CIPHER_MODE_CTR +#include "mbedtls/aes.h" + +int compute_cmac_( mbedtls_aes_context *ctx, + const unsigned char *input, + size_t length, + unsigned char param, + unsigned char mac[16] ); + +void gf128_double_( unsigned char val[16] ); + +int eddy_aes_authcrypt_eax( mbedtls_aes_context *ctx, + int mode, /* ENCRYPT/DECRYPT */ + const unsigned char *nonce, /* 48-bit nonce */ + size_t nonce_length, /* = 6 */ + const unsigned char *header, /* Empty buffer */ + size_t header_length, /* = 0 */ + size_t message_length, /* Length of input & output buffers 12 */ + const unsigned char *input, + unsigned char *output, + unsigned char *tag, + size_t tag_length ); /* = 2 */ + + + +#endif /* defined(AES_EAX_H__INCLUDED__) */ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/main.cpp Tue Nov 29 06:29:10 2016 +0000
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2006-2016 Google Inc, All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef YOTTA_CFG_MBED_OS // use minar on mbed OS
+# include "mbed-drivers/mbed.h"
+#else
+# include "mbed.h"
+#endif
+
+#include "ble/BLE.h"
+#include "EddystoneService.h"
+
+#include "PersistentStorageHelper/ConfigParamsPersistence.h"
+#include "stdio.h"
+
+#if (defined(NRF51) || defined(NRF52))
+ #include "nrf_soc.h"
+#endif
+
+// Instantiation of the main event loop for this program
+
+#ifdef YOTTA_CFG_MBED_OS // use minar on mbed OS
+# include "EventQueue/EventQueueMinar.h"
+ typedef eq::EventQueueMinar event_queue_t;
+
+#else // otherwise use the event classic queue
+# include "EventQueue/EventQueueClassic.h"
+ typedef eq::EventQueueClassic<
+ /* event count */ 10
+ > event_queue_t;
+
+#endif
+
+static event_queue_t eventQueue;
+
+EddystoneService *eddyServicePtr;
+
+/* Duration after power-on that config service is available. */
+static const int CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS = EDDYSTONE_DEFAULT_CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS;
+
+/* Values for ADV packets related to firmware levels, calibrated based on measured values at 1m */
+static const PowerLevels_t advTxPowerLevels = EDDYSTONE_DEFAULT_ADV_TX_POWER_LEVELS;
+/* Values for radio power levels, provided by manufacturer. */
+static const PowerLevels_t radioTxPowerLevels = EDDYSTONE_DEFAULT_RADIO_TX_POWER_LEVELS;
+
+DigitalOut configLED(CONFIG_LED, LED_OFF);
+
+static const int BLINKY_MSEC = 500; // How long to cycle config LED on/off
+static event_queue_t::event_handle_t handle = 0; // For the config mode timeout
+static event_queue_t::event_handle_t BlinkyHandle = 0; // For the blinking LED when in config mode
+
+static void blinky(void) { configLED = !configLED; }
+
+static void configLED_on(void) {
+ configLED = !LED_OFF;
+ BlinkyHandle = eventQueue.post_every(blinky, BLINKY_MSEC);
+}
+
+static void configLED_off(void) {
+ configLED = LED_OFF;
+ if (BlinkyHandle) {
+ eventQueue.cancel(BlinkyHandle);
+ BlinkyHandle = NULL;
+ }
+}
+
+/**
+ * Callback triggered some time after application started to switch to beacon mode.
+ */
+static void timeoutToStartEddystoneBeaconAdvertisements(void)
+{
+ Gap::GapState_t state;
+ state = BLE::Instance().gap().getState();
+ if (!state.connected) { /* don't switch if we're in a connected state. */
+ eddyServicePtr->startEddystoneBeaconAdvertisements();
+ configLED_off();
+ }
+}
+
+/**
+ * Callback triggered for a connection event.
+ */
+static void connectionCallback(const Gap::ConnectionCallbackParams_t *cbParams)
+{
+ (void) cbParams;
+ // Stop advertising whatever the current mode
+ eddyServicePtr->stopEddystoneBeaconAdvertisements();
+}
+
+/**
+ * Callback triggered for a disconnection event.
+ */
+static void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *cbParams)
+{
+ (void) cbParams;
+ BLE::Instance().gap().startAdvertising();
+ // Save params in persistent storage
+ EddystoneService::EddystoneParams_t params;
+ eddyServicePtr->getEddystoneParams(params);
+ saveEddystoneServiceConfigParams(¶ms);
+ // Ensure LED is off at the end of Config Mode or during a connection
+ configLED_off();
+ // 0.5 Second callback to rapidly re-establish Beaconing Service
+ // (because it needs to be executed outside of disconnect callback)
+ eventQueue.post_in(timeoutToStartEddystoneBeaconAdvertisements, 500 /* ms */);
+}
+
+// This section defines a simple push button handler to enter config or shutdown the beacon
+// Only compiles if "reset_button" is set in config.json in the "platform" section
+//
+#ifdef RESET_BUTTON
+
+InterruptIn button(RESET_BUTTON);
+DigitalOut shutdownLED(SHUTDOWN_LED, LED_OFF);
+
+static void shutdownLED_on(void) { shutdownLED = !LED_OFF; }
+static void shutdownLED_off(void) { shutdownLED = LED_OFF; }
+
+static bool beaconIsOn = true; // Button handler boolean to switch on or off
+static bool buttonBusy; // semaphore to make prevent switch bounce problems
+
+static void freeButtonBusy(void) { buttonBusy = false; }
+
+// Callback used to handle button presses from thread mode (not IRQ)
+static void button_task(void) {
+ bool locked = eddyServicePtr->isLocked();
+
+ // only shutdown if ON and unlocked
+ if (beaconIsOn && !locked) {
+ eventQueue.cancel(handle); // kill any pending callback tasks
+ beaconIsOn = false;
+ eddyServicePtr->stopEddystoneBeaconAdvertisements();
+ configLED_off(); // just in case it's still running...
+ shutdownLED_on(); // Flash shutdownLED to let user know we're turning off
+ eventQueue.post_in(shutdownLED_off, 1000);
+ // only go into configMode if OFF or locked and not in configMode
+ } else if (!beaconIsOn || (locked && BlinkyHandle == NULL)) {
+ eventQueue.cancel(handle); // kill any pending callback tasks
+ beaconIsOn = true;
+ eddyServicePtr->startEddystoneConfigAdvertisements();
+ configLED_on();
+ handle = eventQueue.post_in(
+ timeoutToStartEddystoneBeaconAdvertisements,
+ CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS * 1000 /* ms */
+ );
+ }
+ eventQueue.post_in(freeButtonBusy, 750 /* ms */);
+}
+
+/**
+ * Raw IRQ handler for the reset button. We don't want to actually do any work here.
+ * Instead, we queue work to happen later using an event queue, by posting a callback.
+ * This has the added avantage of serialising actions, so if the button press happens
+ * during the config->beacon mode transition timeout, the button_task won't happen
+ * until the previous task has finished.
+ *
+ * If your buttons aren't debounced, you should do this in software, or button_task
+ * might get queued multiple times.
+ */
+static void reset_rise(void)
+{
+ if (!buttonBusy) {
+ buttonBusy = true;
+ eventQueue.post(button_task);
+ }
+}
+#endif
+
+static void onBleInitError(BLE::InitializationCompleteCallbackContext* initContext)
+{
+ /* Initialization error handling goes here... */
+ (void) initContext;
+}
+
+
+static void bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext)
+{
+ BLE &ble = initContext->ble;
+ ble_error_t error = initContext->error;
+
+ if (error != BLE_ERROR_NONE) {
+ onBleInitError(initContext);
+ return;
+ }
+
+ ble.gap().onDisconnection(disconnectionCallback);
+
+ ble.gap().onConnection(connectionCallback);
+
+ EddystoneService::EddystoneParams_t params;
+
+ wait_ms(35); // Allow the RNG number generator to collect data
+
+ // Determine if booting directly after re-Flash or not
+ if (loadEddystoneServiceConfigParams(¶ms)) {
+ // 2+ Boot after reflash, so get parms from Persistent Storage
+ eddyServicePtr = new EddystoneService(ble, params, radioTxPowerLevels, eventQueue);
+ } else {
+ // 1st Boot after reflash, so reset everything to defaults
+ /* NOTE: slots are initialized in the constructor from the config.json file */
+ eddyServicePtr = new EddystoneService(ble, advTxPowerLevels, radioTxPowerLevels, eventQueue);
+ }
+
+ // Save Default params in persistent storage ready for next boot event
+ eddyServicePtr->getEddystoneParams(params);
+ saveEddystoneServiceConfigParams(¶ms);
+ // Start the Eddystone Config service - This will never stop (only connectability will change)
+ eddyServicePtr->startEddystoneConfigService();
+
+ /* Start Eddystone config Advertizements (to initialize everything properly) */
+ configLED_on();
+ eddyServicePtr->startEddystoneConfigAdvertisements();
+ handle = eventQueue.post_in(
+ timeoutToStartEddystoneBeaconAdvertisements,
+ CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS * 1000 /* ms */
+ );
+
+#if (defined(NRF51) || defined(NRF52))
+ sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE); // set the DCDC mode for the Nordic chip to lower power consumption
+#endif
+
+ // now shut everything off (used for final beacon that ships w/ battery)
+#ifdef RESET_BUTTON
+ eventQueue.post_in(button_task, 2000 /* ms */);
+#endif
+}
+
+void app_start(int, char *[])
+{
+
+#ifdef NO_LOGGING
+ /* Tell standard C library to not allocate large buffers for these streams */
+ // setbuf(stdout, NULL);
+ // setbuf(stderr, NULL);
+ // setbuf(stdin, NULL);
+#endif
+
+#ifndef NO_4SEC_START_DELAY
+ // delay ~4secs before starting to allow time the nRF51 hardware to settle
+ // Also allows time to attach a virtual terimal to read logging output during init
+ wait_ms(4000);
+#endif
+
+#ifdef RESET_BUTTON
+ beaconIsOn = true; // Booting up, initialize for button handler
+ buttonBusy = false; // software debouncing of the reset button
+ button.rise(&reset_rise); // setup reset button
+#endif
+
+ BLE &ble = BLE::Instance();
+ ble.init(bleInitComplete);
+}
+
+#if !defined(YOTTA_CFG_MBED_OS)
+
+void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
+ eventQueue.post(&BLE::processEvents, &context->ble);
+ }
+
+int main() {
+
+ BLE &ble = BLE::Instance();
+ ble.onEventsToProcess(scheduleBleEventsProcessing);
+
+ app_start(0, NULL);
+
+ while (true) {
+ eventQueue.dispatch();
+ sleep();
+ }
+
+ return 0;
+}
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/mbedtls_config.h Tue Nov 29 06:29:10 2016 +0000 @@ -0,0 +1,112 @@ +#ifndef MBEDTLS_EDDYSTONE_CONFIG_H +#define MBEDTLS_EDDYSTONE_CONFIG_H + +/* System support */ +//#define MBEDTLS_HAVE_ASM +#undef MBEDTLS_HAVE_ASM +#define MBEDTLS_HAVE_TIME +#undef MBEDTLS_HAVE_TIME_DATE + +#define MBEDTLS_CIPHER_MODE_CBC +#define MBEDTLS_CIPHER_MODE_CTR +#undef MBEDTLS_CIPHER_PADDING_PKCS7 +#undef MBEDTLS_REMOVE_ARC4_CIPHERSUITES + +/* mbed TLS feature support */ +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#undef MBEDTLS_ECP_DP_SECP384R1_ENABLED +#define MBEDTLS_ECP_DP_CURVE25519_ENABLED + +#undef MBEDTLS_ECP_NIST_OPTIM +#undef MBEDTLS_ECDSA_DETERMINISTIC +#undef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED +#undef MBEDTLS_ERROR_STRERROR_DUMMY +#undef MBEDTLS_PK_RSA_ALT_SUPPORT +#undef MBEDTLS_PKCS1_V15 +#undef MBEDTLS_PKCS1_V21 +#undef MBEDTLS_SELF_TEST +#undef MBEDTLS_SSL_ALL_ALERT_MESSAGES +#undef MBEDTLS_SSL_ENCRYPT_THEN_MAC +#undef MBEDTLS_SSL_EXTENDED_MASTER_SECRET +#undef MBEDTLS_SSL_RENEGOTIATION +#undef MBEDTLS_SSL_MAX_FRAGMENT_LENGTH +#undef MBEDTLS_SSL_PROTO_TLS1_2 +#undef MBEDTLS_SSL_PROTO_DTLS +#undef MBEDTLS_SSL_ALPN +#undef MBEDTLS_SSL_DTLS_ANTI_REPLAY +#undef MBEDTLS_SSL_DTLS_HELLO_VERIFY +#undef MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE +#undef MBEDTLS_SSL_DTLS_BADMAC_LIMIT +#undef MBEDTLS_SSL_SESSION_TICKETS +#undef MBEDTLS_SSL_EXPORT_KEYS +#undef MBEDTLS_SSL_SESSION_TICKETS +#undef MBEDTLS_SSL_EXPORT_KEYS +#undef MBEDTLS_SSL_SERVER_NAME_INDICATION +#undef MBEDTLS_VERSION_FEATURES +#undef MBEDTLS_X509_CHECK_KEY_USAGE +#undef MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE + +/* mbed TLS modules */ +#define MBEDTLS_AES_C +#undef MBEDTLS_ASN1_PARSE_C +#undef MBEDTLS_ASN1_WRITE_C +#undef MBEDTLS_BASE64_C +#define MBEDTLS_BIGNUM_C +#undef MBEDTLS_CCM_C +#undef MBEDTLS_CERTS_C +#undef MBEDTLS_CIPHER_C +#define MBEDTLS_CTR_DRBG_C +#undef MBEDTLS_DEBUG_C +#define MBEDTLS_ECDH_C +#undef MBEDTLS_ECDSA_C +#define MBEDTLS_ECP_C +#define MBEDTLS_ENTROPY_C +#undef MBEDTLS_ERROR_C +#undef MBEDTLS_GCM_C +#undef MBEDTLS_HMAC_DRBG_C +#define MBEDTLS_MD_C +#undef MBEDTLS_OID_C +#undef MBEDTLS_PEM_PARSE_C +#undef MBEDTLS_PK_C +#undef MBEDTLS_PK_PARSE_C +#undef MBEDTLS_PK_WRITE_C +#undef MBEDTLS_PLATFORM_C +#undef MBEDTLS_RSA_C +#define MBEDTLS_SHA256_C +#undef MBEDTLS_SHA512_C +#undef MBEDTLS_SSL_CACHE_C +#undef MBEDTLS_SSL_COOKIE_C +#undef MBEDTLS_SSL_TICKET_C +#undef MBEDTLS_SSL_CLI_C +#undef MBEDTLS_SSL_SRV_C +#undef MBEDTLS_SSL_TLS_C +#undef MBEDTLS_VERSION_C +#undef MBEDTLS_X509_USE_C +#undef MBEDTLS_X509_CRT_PARSE_C +#undef MBEDTLS_X509_CRL_PARSE_C + +/* Save RAM at the expense of ROM */ +#define MBEDTLS_AES_ROM_TABLES + +/* Save RAM by adjusting to our exact needs */ +#define MBEDTLS_ECP_MAX_BITS 256 +#define MBEDTLS_MPI_MAX_SIZE 32 + +/* Save RAM at the expense of speed, see ecp.h */ +#define MBEDTLS_ECP_WINDOW_SIZE 2 +#define MBEDTLS_ECP_FIXED_POINT_OPTIM 0 + +/* Significant speed benefit at the expense of some ROM */ +#define MBEDTLS_ECP_NIST_OPTIM +/* +* You should adjust this to the exact number of sources you're using: default +* is the "mbedtls_platform_entropy_poll" source, but you may want to add other ones. +* Minimum is 2 for the entropy test suite. +*/ +#define MBEDTLS_ENTROPY_MAX_SOURCES 2 +#define MBEDTLS_NO_PLATFORM_ENTROPY + +#endif /* MBEDTLS_EDDYSTONE_CONFIG_H */