Merge pull request #39 from ARMmbed/master

catchup master
pull/6932/head
Paul Szczepanek 2018-05-09 08:26:50 +01:00 committed by GitHub
commit d36bed389d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1735 changed files with 263960 additions and 26854 deletions

17
.astyleignore Normal file
View File

@ -0,0 +1,17 @@
BUILD
cmsis
features/mbedtls
features/FEATURE_LWIP/lwip
rtos/TARGET_CORTEX/rtx4
features/filesystem/littlefs/littlefs
features/filesystem/fat/ChaN
features/frameworks
features/FEATURE_BLE/targets
features/FEATURE_LWIP/lwip-interface/lwip
features/unsupported/
features/FEATURE_COMMON_PAL/
FEATURE_NANOSTACK/coap-service
FEATURE_NANOSTACK/sal-stack-nanostack
rtos/TARGET_CORTEX/rtx5
targets
tools

34
.astylerc Normal file
View File

@ -0,0 +1,34 @@
# Mbed OS code style definition file for astyle
# Don't create backup files, let git handle it
suffix=none
# K&R style
style=kr
# 1 TBS addition to k&r, add braces to one liners
# Use -j as it was changed in astyle from brackets to braces, this way it is compatible with older astyle versions
-j
# 4 spaces, convert tabs to spaces
indent=spaces=4
convert-tabs
# Indent switches and cases
indent-switches
indent-cases
# Remove spaces in and around parentheses
unpad-paren
# Insert a space after if, while, for, and around operators
pad-header
pad-oper
# Pointer/reference operators go next to the name (on the right)
align-pointer=name
align-reference=name
# Attach { for classes and namespaces
attach-namespaces
attach-classes

View File

@ -1,48 +1,31 @@
Note: This is just a template, so feel free to use/remove the unnecessary things
### Description
- Type: Bug | Enhancement | Question
- Related issue: `#abc`
- Priority: Blocker | Major | Minor
<!--
Required
Add detailed description of what you are reporting.
Good example: https://os.mbed.com/docs/latest/reference/workflow.html
Things to consider sharing:
- What target does this relate to?
- What toolchain (name + version) are you using?
- What tools (name + version - is it mbed-cli, online compiler or IDE) are you using?
- What is the SHA of Mbed OS (git log -n1 --oneline)?
- Steps to reproduce. (Did you publish code or a test case that exhibits the problem?)
-->
---------------------------------------------------------------
## Bug
**Target**
K64F|??
### Issue request type
<!--
Required
Please add only one X to one of the following types. Do not fill multiple types. (Split the issue otherwise.)
Please note this is not a GitHub task list; indenting the boxes or changing the format to add a '.' or '*' in front
of them changes the meaning incorrectly. The only changes to make are to add a description under the
description heading and to add an 'x' to the correct box.
**Toolchain:**
GCC_ARM|ARM|IAR
[X] Question
[ ] Enhancement
[ ] Bug
-->
**Toolchain version:**
[ ] Question
[ ] Enhancement
[ ] Bug
**mbed-cli version:**
(`mbed --version`)
**mbed-os sha:**
(`git log -n1 --oneline`)
**DAPLink version:**
**Expected behavior**
**Actual behavior**
**Steps to reproduce**
----------------------------------------------------------------
## Enhancement
**Reason to enhance or problem with existing solution**
**Suggested enhancement**
**Pros**
**Cons**
-----------------------------------------------------------------
## Question
**How to?**

View File

@ -3,7 +3,7 @@
<!--
Required
Add here detailed changes summary, testing results, dependencies
Good example: https://os.mbed.com/docs/latest/reference/guidelines.html#workflow (Pull request template)
Good example: https://os.mbed.com/docs/latest/reference/workflow.html (Pull request template)
-->
@ -11,17 +11,14 @@
<!--
Required
Please tick only one of the following types. Do not tick more or change the layout.
[X] Fix
Please add only one X to one of the following types. Do not fill multiple types (split the pull request otherwise).
Please note this is not a GitHub task list, indenting the boxes or changing the format to add a '.' or '*' in front
of them would change the meaning incorrectly. The only changes to be made are to add a description text under the
description heading and to add a 'x' to the correct box.
-->
[ ] Fix
[ ] Refactor
[ ] New target
[ ] Feature
[ ] Breaking change
-->
[ ] Fix
[ ] Refactor
[ ] New target
[ ] Feature
[ ] Breaking change

View File

@ -94,6 +94,45 @@ matrix:
# Report success since we have overridden default behaviour
- bash -c "$STATUS" success "Local $NAME testing has passed"
- env:
- NAME=astyle
install:
- wget https://downloads.sourceforge.net/project/astyle/astyle/astyle%203.1/astyle_3.1_linux.tar.gz;
mkdir -p BUILD && tar xf astyle_3.1_linux.tar.gz -C BUILD;
pushd BUILD/astyle/build/gcc;
make;
export PATH=$PWD/bin:$PATH;
popd;
- astyle --version
script:
# only changed files this time
- git diff --name-only $TRAVIS_BRANCH | grep '.*\.\(h\|c\|hpp\|cpp\)' | fgrep -v -f .astyleignore | xargs -n 100 -I {} bash -c "astyle -n --options=.astylerc \"{}\"" > astyle-files-changed.out;
if [ $(cat astyle-files-changed.out | grep Formatted | wc -l) -ne 0 ]; then
git --no-pager diff;
echo "Please fix style issues as shown above";
else
echo "Coding style check OK";
fi
after_success:
# run astyle for all files on the branch
- git checkout -- .
- find -regex '.*\.\(h\|c\|hpp\|cpp\)' -type f | fgrep -v -f .astyleignore | xargs -n 100 -I {} bash -c "astyle -n --options=.astylerc \"{}\"" > astyle-branch.out;
# update status if we succeeded, compare with master if possible
- |
CURR=$(cat astyle-branch.out | grep Formatted | wc -l)
PREV=$(curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \
| jq -re "select(.sha != \"$TRAVIS_COMMIT\")
| .statuses[] | select(.context == \"travis-ci/$NAME\").description
| capture(\", (?<warnings>[0-9]+) warnings\").warnings" \
|| echo 0)
STATUSM="Passed, ${CURR} warnings"
if [ "$PREV" -ne 0 ]
then
STATUSM="$STATUSM ($(python -c "print '%+d' % ($CURR-$PREV)") warnings)"
fi
- bash -c "$STATUS" success "$STATUSM"
- env:
- NAME=events
- EVENTS=events
@ -111,7 +150,7 @@ matrix:
- python tools/make.py -t GCC_ARM -m K64F --source=. --build=BUILD/K64F/GCC_ARM -j0
# Check that example compiles without rtos
- sed -n '/``` cpp/,/```/{/```$/Q;/```/d;p;}' $EVENTS/README.md > main.cpp
- rm -r rtos features/cellular features/netsocket features/frameworks BUILD
- rm -r rtos features/cellular features/netsocket features/nanostack features/frameworks BUILD
- python tools/make.py -t GCC_ARM -m DISCO_F401VC --source=. --build=BUILD/DISCO_F401VC/GCC_ARM -j0
# Run local equeue tests
- make -C $EVENTS/equeue test
@ -121,7 +160,7 @@ matrix:
# update status if we succeeded, compare with master if possible
- |
CURR=$(grep -o '[0-9]\+ cycles' prof | awk '{sum += $1} END {print sum}')
PREV=$(curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \
PREV=$(curl -u "$MBED_BOT" https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \
| jq -re "select(.sha != \"$TRAVIS_COMMIT\")
| .statuses[] | select(.context == \"travis-ci/$NAME\").description
| capture(\"runtime is (?<runtime>[0-9]+)\").runtime" \
@ -193,7 +232,7 @@ matrix:
# update status if we succeeded, compare with master if possible
- |
CURR=$(tail -n1 sizes | awk '{print $1}')
PREV=$(curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \
PREV=$(curl -u "$MBED_BOT" https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \
| jq -re "select(.sha != \"$TRAVIS_COMMIT\")
| .statuses[] | select(.context == \"travis-ci/$NAME\").description
| capture(\"code size is (?<size>[0-9]+)\").size" \

View File

@ -0,0 +1,32 @@
"""
mbed SDK
Copyright (c) 2011-2016 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.
"""
import uuid
from mbed_host_tests import BaseHostTest
class Device_Echo(BaseHostTest):
def _callback_repeat(self, key, value, _):
self.send_kv(key, value)
def setup(self):
self.register_callback("echo", self._callback_repeat)
self.register_callback("echo_count", self._callback_repeat)
def teardown(self):
pass

View File

@ -21,28 +21,49 @@
#include "unity/unity.h"
#include "utest/utest.h"
#define PAYLOAD_LENGTH 36
using namespace utest::v1;
// Fill a buffer with a slice of the ASCII alphabet.
void fill_buffer(char* buffer, unsigned int length, unsigned int index) {
unsigned int start = length * index;
for (int i = 0; i < length - 1; i++) {
buffer[i] = 'a' + ((start + i) % 26);
}
buffer[length - 1] = '\0';
}
// Echo server (echo payload to host)
template<int N>
void test_case_echo_server_x() {
char _key[11] = {};
char _value[128] = {};
char _tx_value[PAYLOAD_LENGTH + 1] = {};
char _rx_value[PAYLOAD_LENGTH + 1] = {};
const int echo_count = N;
const char _key_const[] = "echo_count";
const char _echo_count_key_const[] = "echo_count";
const char _echo_key_const[] = "echo";
int expected_key = 1;
greentea_send_kv(_key_const, echo_count);
// Send up the echo count
greentea_send_kv(_echo_count_key_const, echo_count);
// Handshake with host
do {
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
expected_key = strcmp(_key_const, _key);
greentea_parse_kv(_key, _rx_value, sizeof(_key), sizeof(_rx_value));
// Ensure the key received is "echo_count" and not some old data
expected_key = strcmp(_echo_count_key_const, _key);
} while (expected_key);
TEST_ASSERT_EQUAL_INT(echo_count, atoi(_value));
TEST_ASSERT_EQUAL_INT(echo_count, atoi(_rx_value));
for (int i=0; i < echo_count; ++i) {
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
greentea_send_kv(_key, _value);
fill_buffer(_tx_value, PAYLOAD_LENGTH, i);
greentea_send_kv(_echo_key_const, _tx_value);
do {
greentea_parse_kv(_key, _rx_value, sizeof(_key), sizeof(_rx_value));
// Ensure the key received is "echo" and not some old data
expected_key = strcmp(_echo_key_const, _key);
} while (expected_key);
TEST_ASSERT(strncmp(_tx_value, _rx_value, PAYLOAD_LENGTH) == 0);
}
}
@ -56,7 +77,7 @@ Case cases[] = {
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases) {
GREENTEA_SETUP(30, "echo");
GREENTEA_SETUP(30, "device_echo");
return greentea_test_setup_handler(number_of_cases);
}

View File

@ -20,7 +20,7 @@
#include "greentea-client/test_env.h"
#include "mbed.h"
#include "cmsis.h"
#ifdef TARGET_NRF5 // for all NRF5x targets
#if defined(TARGET_NRF5) || defined(TARGET_NRF5x) // for all NRF5x targets
#include "nrf_nvic.h" // for __NRF_NVIC_APP_IRQS_0 / __NRF_NVIC_APP_IRQS_1
#endif
@ -30,7 +30,7 @@ bool test_are_interrupts_enabled(void)
{
// NRF5x targets don't disable interrupts when in critical section, instead they mask application interrupts this is due to BLE stack
// (BLE to be operational requires some interrupts to be always enabled)
#ifdef TARGET_NRF52_DK
#ifdef TARGET_NRF52
// check if APP interrupts are masked for NRF52_DK board
return (((NVIC->ISER[0] & __NRF_NVIC_APP_IRQS_0) != 0) || ((NVIC->ISER[1] & __NRF_NVIC_APP_IRQS_1) != 0));
#elif TARGET_NRF5

View File

@ -28,7 +28,13 @@
using namespace utest::v1;
#define TEST_CYCLES 10000000
#ifdef TARGET_NRF52
/* The increased tolerance is to account for the imprecise timers on the NRF52. */
#define ALLOWED_DRIFT_PPM (1000000/50000) //5.0%
#else
#define ALLOWED_DRIFT_PPM (1000000/5000) //0.5%
#endif
/*
return values to be checked are documented at:
@ -279,9 +285,7 @@ Case cases[] = {
Case("Flash - erase sector", flash_erase_sector_test),
Case("Flash - program page", flash_program_page_test),
Case("Flash - buffer alignment test", flash_buffer_alignment_test),
#ifndef MCU_NRF52
Case("Flash - clock and cache test", flash_clock_and_cache_test),
#endif
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases) {

View File

@ -25,6 +25,7 @@
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "ticker_api.h"
extern "C" {
#include "rtx_lib.h"
@ -44,7 +45,9 @@ private:
Semaphore _sem;
virtual void handler()
{
increment_tick();
core_util_critical_section_enter();
_increment_tick();
core_util_critical_section_exit();
_sem.release();
}
@ -54,6 +57,11 @@ public:
{
}
SysTimerTest(const ticker_data_t *data) :
SysTimer(data), _sem(0, 1)
{
}
virtual ~SysTimerTest()
{
}
@ -64,6 +72,65 @@ public:
}
};
timestamp_t mock_ticker_timestamp;
void mock_ticker_init()
{
}
uint32_t mock_ticker_read()
{
return mock_ticker_timestamp;
}
void mock_ticker_disable_interrupt()
{
}
void mock_ticker_clear_interrupt()
{
}
void mock_ticker_set_interrupt(timestamp_t timestamp)
{
}
void mock_ticker_fire_interrupt()
{
}
const ticker_info_t *mock_ticker_get_info()
{
static const ticker_info_t mock_ticker_info = {
.frequency = 1000000,
.bits = 32
};
return &mock_ticker_info;
}
ticker_interface_t mock_ticker_interface = {
.init = mock_ticker_init,
.read = mock_ticker_read,
.disable_interrupt = mock_ticker_disable_interrupt,
.clear_interrupt = mock_ticker_clear_interrupt,
.set_interrupt = mock_ticker_set_interrupt,
.fire_interrupt = mock_ticker_fire_interrupt,
.get_info = mock_ticker_get_info,
};
ticker_event_queue_t mock_ticker_event_queue;
const ticker_data_t mock_ticker_data = {
.interface = &mock_ticker_interface,
.queue = &mock_ticker_event_queue
};
void mock_ticker_reset()
{
mock_ticker_timestamp = 0;
memset(&mock_ticker_event_queue, 0, sizeof mock_ticker_event_queue);
}
/** Test tick count is zero upon creation
*
* Given a SysTimer
@ -79,26 +146,29 @@ void test_created_with_zero_tick_count(void)
/** Test tick count is updated correctly
*
* Given a SysTimer
* When @a update_tick method is called immediately after creation
* When the @a suspend and @a resume methods are called immediately after creation
* Then the tick count is not updated
* When @a update_tick is called again after a delay
* When @a suspend and @a resume methods are called again after a delay
* Then the tick count is updated
* and the number of ticks incremented is equal TEST_TICKS - 1
* When @a update_tick is called again without a delay
* When @a suspend and @a resume methods are called again without a delay
* Then the tick count is not updated
*/
void test_update_tick(void)
{
SysTimerTest st;
TEST_ASSERT_EQUAL_UINT32(0, st.update_tick());
mock_ticker_reset();
SysTimerTest st(&mock_ticker_data);
st.suspend(TEST_TICKS * 2);
TEST_ASSERT_EQUAL_UINT32(0, st.resume());
TEST_ASSERT_EQUAL_UINT32(0, st.get_tick());
us_timestamp_t test_ticks_elapsed_ts = st.get_time() + DELAY_US;
while (st.get_time() <= test_ticks_elapsed_ts) {}
TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.update_tick());
st.suspend(TEST_TICKS * 2);
mock_ticker_timestamp = DELAY_US;
TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.resume());
TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.get_tick());
TEST_ASSERT_EQUAL_UINT32(0, st.update_tick());
st.suspend(TEST_TICKS * 2);
TEST_ASSERT_EQUAL_UINT32(0, st.resume());
TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.get_tick());
}
@ -110,12 +180,13 @@ void test_update_tick(void)
*/
void test_get_time(void)
{
SysTimerTest st;
mock_ticker_reset();
SysTimerTest st(&mock_ticker_data);
us_timestamp_t t1 = st.get_time();
wait_us(DELAY_US);
mock_ticker_timestamp = DELAY_US;
us_timestamp_t t2 = st.get_time();
TEST_ASSERT_UINT64_WITHIN(DELAY_DELTA_US, DELAY_US, t2 - t1);
TEST_ASSERT_EQUAL_UINT64(DELAY_US, t2 - t1);
}
/** Test cancel_tick

33
cmsis/RTE_Components.h Normal file
View File

@ -0,0 +1,33 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 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 RTE_COMPONENTS_H
#define RTE_COMPONENTS_H
#define CMSIS_device_header <cmsis.h>
#ifndef MBED_CONF_TZ_PROCESS_STACK_SIZE
#define MBED_CONF_TZ_PROCESS_STACK_SIZE 512
#endif
#ifndef MBED_CONF_TZ_PROCESS_STACK_SLOTS
#define MBED_CONF_TZ_PROCESS_STACK_SLOTS 8
#endif
#define TZ_PROCESS_STACK_SLOTS MBED_CONF_TZ_PROCESS_STACK_SLOTS
#define TZ_PROCESS_STACK_SIZE MBED_CONF_TZ_PROCESS_STACK_SIZE
#endif

View File

@ -0,0 +1,206 @@
/*
* Copyright (c) 2015-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
*
* 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.
*
* ----------------------------------------------------------------------------
*
* $Date: 15. October 2016
* $Revision: 1.1.0
*
* Project: TrustZone for ARMv8-M
* Title: Context Management for ARMv8-M TrustZone - Sample implementation
*
*---------------------------------------------------------------------------*/
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
#include "RTE_Components.h"
#include CMSIS_device_header
#include "tz_context.h"
/// Number of process slots (threads may call secure library code)
#ifndef TZ_PROCESS_STACK_SLOTS
#define TZ_PROCESS_STACK_SLOTS 8U
#endif
/// Stack size of the secure library code
#ifndef TZ_PROCESS_STACK_SIZE
#define TZ_PROCESS_STACK_SIZE 256U
#endif
typedef struct {
uint32_t sp_top; // stack space top
uint32_t sp_limit; // stack space limit
uint32_t sp; // current stack pointer
} stack_info_t;
static stack_info_t ProcessStackInfo [TZ_PROCESS_STACK_SLOTS];
static uint64_t ProcessStackMemory[TZ_PROCESS_STACK_SLOTS][TZ_PROCESS_STACK_SIZE/8U];
static uint32_t ProcessStackFreeSlot = 0xFFFFFFFFU;
/// Initialize secure context memory system
/// \return execution status (1: success, 0: error)
__attribute__((cmse_nonsecure_entry))
uint32_t TZ_InitContextSystem_S (void) {
uint32_t n;
if (__get_IPSR() == 0U) {
return 0U; // Thread Mode
}
for (n = 0U; n < TZ_PROCESS_STACK_SLOTS; n++) {
ProcessStackInfo[n].sp = 0U;
ProcessStackInfo[n].sp_limit = (uint32_t)&ProcessStackMemory[n];
ProcessStackInfo[n].sp_top = (uint32_t)&ProcessStackMemory[n] + TZ_PROCESS_STACK_SIZE;
*((uint32_t *)ProcessStackMemory[n]) = n + 1U;
}
*((uint32_t *)ProcessStackMemory[--n]) = 0xFFFFFFFFU;
ProcessStackFreeSlot = 0U;
// Default process stack pointer and stack limit
__set_PSPLIM((uint32_t)ProcessStackMemory);
__set_PSP ((uint32_t)ProcessStackMemory);
// Privileged Thread Mode using PSP
__set_CONTROL(0x02U);
return 1U; // Success
}
/// Allocate context memory for calling secure software modules in TrustZone
/// \param[in] module identifies software modules called from non-secure mode
/// \return value != 0 id TrustZone memory slot identifier
/// \return value 0 no memory available or internal error
__attribute__((cmse_nonsecure_entry))
TZ_MemoryId_t TZ_AllocModuleContext_S (TZ_ModuleId_t module) {
uint32_t slot;
(void)module; // Ignore (fixed Stack size)
if (__get_IPSR() == 0U) {
return 0U; // Thread Mode
}
if (ProcessStackFreeSlot == 0xFFFFFFFFU) {
return 0U; // No slot available
}
slot = ProcessStackFreeSlot;
ProcessStackFreeSlot = *((uint32_t *)ProcessStackMemory[slot]);
ProcessStackInfo[slot].sp = ProcessStackInfo[slot].sp_top;
return (slot + 1U);
}
/// Free context memory that was previously allocated with \ref TZ_AllocModuleContext_S
/// \param[in] id TrustZone memory slot identifier
/// \return execution status (1: success, 0: error)
__attribute__((cmse_nonsecure_entry))
uint32_t TZ_FreeModuleContext_S (TZ_MemoryId_t id) {
uint32_t slot;
if (__get_IPSR() == 0U) {
return 0U; // Thread Mode
}
if ((id == 0U) || (id > TZ_PROCESS_STACK_SLOTS)) {
return 0U; // Invalid ID
}
slot = id - 1U;
if (ProcessStackInfo[slot].sp == 0U) {
return 0U; // Inactive slot
}
ProcessStackInfo[slot].sp = 0U;
*((uint32_t *)ProcessStackMemory[slot]) = ProcessStackFreeSlot;
ProcessStackFreeSlot = slot;
return 1U; // Success
}
/// Load secure context (called on RTOS thread context switch)
/// \param[in] id TrustZone memory slot identifier
/// \return execution status (1: success, 0: error)
__attribute__((cmse_nonsecure_entry))
uint32_t TZ_LoadContext_S (TZ_MemoryId_t id) {
uint32_t slot;
if ((__get_IPSR() == 0U) || ((__get_CONTROL() & 2U) == 0U)) {
return 0U; // Thread Mode or using Main Stack for threads
}
if ((id == 0U) || (id > TZ_PROCESS_STACK_SLOTS)) {
return 0U; // Invalid ID
}
slot = id - 1U;
if (ProcessStackInfo[slot].sp == 0U) {
return 0U; // Inactive slot
}
// Setup process stack pointer and stack limit
__set_PSPLIM(ProcessStackInfo[slot].sp_limit);
__set_PSP (ProcessStackInfo[slot].sp);
return 1U; // Success
}
/// Store secure context (called on RTOS thread context switch)
/// \param[in] id TrustZone memory slot identifier
/// \return execution status (1: success, 0: error)
__attribute__((cmse_nonsecure_entry))
uint32_t TZ_StoreContext_S (TZ_MemoryId_t id) {
uint32_t slot;
uint32_t sp;
if ((__get_IPSR() == 0U) || ((__get_CONTROL() & 2U) == 0U)) {
return 0U; // Thread Mode or using Main Stack for threads
}
if ((id == 0U) || (id > TZ_PROCESS_STACK_SLOTS)) {
return 0U; // Invalid ID
}
slot = id - 1U;
if (ProcessStackInfo[slot].sp == 0U) {
return 0U; // Inactive slot
}
sp = __get_PSP();
if ((sp < ProcessStackInfo[slot].sp_limit) ||
(sp > ProcessStackInfo[slot].sp_top)) {
return 0U; // SP out of range
}
ProcessStackInfo[slot].sp = sp;
// Default process stack pointer and stack limit
__set_PSPLIM((uint32_t)ProcessStackMemory);
__set_PSP ((uint32_t)ProcessStackMemory);
return 1U; // Success
}
#endif

View File

@ -844,11 +844,15 @@ EXCLUDE_PATTERNS = */tools/* \
*/features/mbedtls/* \
*/features/storage/* \
*/features/unsupported/* \
*/features/FEATURE_COMMON_PAL/* \
*/features/FEATURE_LWIP/* \
*/features/FEATURE_UVISOR/* \
*/features/nanostack/FEATURE_NANOSTACK/sal-stack-nanostack/* \
*/features/nanostack/FEATURE_NANOSTACK/coap-service/* \
*/features/nanostack/sal-stack-nanostack/* \
*/features/nanostack/coap-service/* \
*/mbed-trace/* \
*/mbed-coap/* \
*/nanostack-libservice/* \
*/mbed-client-randlib/* \
*/nanostack/sal-stack-nanostack-eventloop/* \
*/ble/generic/* \
*/ble/pal/*

View File

@ -9,5 +9,6 @@
"PREDEFINED": "DOXYGEN_ONLY DEVICE_ANALOGIN DEVICE_ANALOGOUT DEVICE_CAN DEVICE_ETHERNET DEVICE_EMAC DEVICE_FLASH DEVICE_I2C DEVICE_I2CSLAVE DEVICE_I2C_ASYNCH DEVICE_INTERRUPTIN DEVICE_ITM DEVICE_LOWPOWERTIMER DEVICE_PORTIN DEVICE_PORTINOUT DEVICE_PORTOUT DEVICE_PWMOUT DEVICE_RTC DEVICE_TRNG DEVICE_SERIAL DEVICE_SERIAL_ASYNCH DEVICE_SERIAL_FC DEVICE_SLEEP DEVICE_SPI DEVICE_SPI_ASYNCH DEVICE_SPISLAVE DEVICE_STORAGE \"MBED_DEPRECATED_SINCE(f, g)=\" \"MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, M)=\" \"MBED_DEPRECATED(s)=\"",
"EXPAND_AS_DEFINED": "",
"SKIP_FUNCTION_MACROS": "NO",
"EXCLUDE_PATTERNS": "*/tools/* */targets/* */features/mbedtls/* */features/storage/* */features/unsupported/* */BUILD/* */rtos/TARGET_CORTEX/rtx*/* */cmsis/* */features/FEATURE_COMMON_PAL/* */features/FEATURE_LWIP/* */features/FEATURE_UVISOR/* */features/nanostack/FEATURE_NANOSTACK/sal-stack-nanostack/* */features/nanostack/FEATURE_NANOSTACK/coap-service/* */ble/generic/* */ble/pal/*"
"STRIP_CODE_COMMENTS": "NO",
"EXCLUDE_PATTERNS": "*/tools/* */targets/* */features/mbedtls/* */features/storage/* */features/unsupported/* */BUILD/* */rtos/TARGET_CORTEX/rtx*/* */cmsis/* */features/FEATURE_LWIP/* */features/FEATURE_UVISOR/* */nanostack/sal-stack-nanostack/* */nanostack/coap-service/* */ble/generic/* */ble/pal/* */mbed-trace/* */mbed-coap/* */nanostack-libservice/* */mbed-client-randlib/* */nanostack/sal-stack-nanostack-eventloop/*"
}

View File

@ -305,12 +305,11 @@ void UARTSerial::rx_irq(void)
void UARTSerial::tx_irq(void)
{
bool was_full = _txbuf.full();
char data;
/* Write to the peripheral if there is something to write
* and if the peripheral is available to write. */
while (!_txbuf.empty() && SerialBase::writeable()) {
char data;
_txbuf.pop(data);
while (SerialBase::writeable() && _txbuf.pop(data)) {
SerialBase::_base_putc(data);
}

View File

@ -23,6 +23,15 @@
#include <stdbool.h>
#include "mbed.h"
// Ticker operations
#if MBED_CONF_RTOS_PRESENT
unsigned equeue_tick() {
return osKernelGetTickCount();
}
#else
#if MBED_CONF_EVENTS_USE_LOWPOWER_TIMER_TICKER
#define ALIAS_TIMER LowPowerTimer
#define ALIAS_TICKER LowPowerTicker
@ -33,7 +42,6 @@
#define ALIAS_TIMEOUT Timeout
#endif
// Ticker operations
static bool equeue_tick_inited = false;
static volatile unsigned equeue_minutes = 0;
static unsigned equeue_timer[
@ -77,6 +85,7 @@ unsigned equeue_tick() {
return minutes + ms;
}
#endif
// Mutex operations
int equeue_mutex_create(equeue_mutex_t *m) { return 0; }

View File

@ -23,7 +23,7 @@
"value": 256
},
"use-lowpower-timer-ticker": {
"help": "Enable use of low power timer and ticker classes. May reduce the accuracy of the event queue.",
"help": "Enable use of low power timer and ticker classes in non-RTOS builds. May reduce the accuracy of the event queue. In RTOS builds, the RTOS tick count is used, and this configuration option has no effect.",
"value": 0
}
}

View File

@ -147,6 +147,7 @@ struct GenericGattClient::DiscoveryControlBlock : public ProcedureControlBlock {
const AttErrorResponse& error = static_cast<const AttErrorResponse&>(message);
if (error.error_code != AttErrorResponse::ATTRIBUTE_NOT_FOUND) {
terminate(client);
return;
}
switch (error.request_opcode) {

View File

@ -37,37 +37,6 @@ property of the target.
}
```
### Include prebuilt libraries
Last, the target shall also include the cordio libraries into the build.
Four prebuilt libraries are provided:
* `wscore`: which contains the base component used by the Cordio stack.
* `wsstack`: The BLE stack itself, if contains the GAP and GATT layer as well as
the Security manager implementation.
* `wssec`: The low level implementation of the security layer.
* `wshci`: The HCI layer.
The `wssec` and `wshci` libraries are delivered in feature folders. It allows
vendors to override those library if necessary. This can be required if the port
use specific crypto routine or require an highly modified HCI layer.
To include the default library in the target, the features have to be added in
this list of the features of the target:
* `WSSEC`: Include the default `wssec` library.
* `WSHCI`: Include the default `wshci` library.
The target should also compile the sources of the BLE Cordio port. It is
achieved by adding the string `CORDIO` in the list of the `extra_labels`
property of the target.
```json
"TARGET_NAME": {
"extra_labels": ["target extra labels ...", "CORDIO"],
"features": ["target features ...", "BLE", "WSHCI", "WSSEC"]
}
```
## CordioHCIDriver implementation:
A port shall include an HCI driver for the BLE module used by the target and
@ -93,8 +62,10 @@ More information about the architecture can be found in the
#### HCITransport
> **Note:** If the Bluetooth controller uses an H4 communication interface, this
step can be skipped.
> **Note:** If the Bluetooth controller uses an H4 communication interface and
the host exposes serial flow control in mbed then this step can be skipped and
the class `ble::vendor::cordio::H4TransportDriver` can be used as the transport
driver.
An empty transport driver can be coded as:
@ -518,22 +489,6 @@ ble::vendor::cordio::CordioHCIDriver& ble_cordio_get_hci_driver() {
}
```
## Examples
Implementation examples might be found for:
* [ST BlueNRG module](../../TARGET_NUCLEO_F401RE/BlueNrgHCIDriver.cpp): It uses
a custom transport driver and a vendor specific is sent right after the answer
to the reset command to switch the controller to link layer mode only.
* [EM9301 module](../../TARGET_Maxim/TARGET_MAX32620HSP/EM9301HCIDriver.cpp):
it uses a custom transport driver and doesn't doesn't send the command *Set
Event Mask Page 2* to the controller during the reset sequence because this
command is not supported by the controller.
* [PAN1326](../../TARGET_Maxim/TARGET_MAX32630FTHR/CC2564HCIDriver.cpp): It
uses the H4 transport driver. The reset sequence start by sending a
*service pack* then once the service pack has been transferred it continue
with the regular reset sequence.

View File

@ -14,6 +14,8 @@
* limitations under the License.
*/
#if DEVICE_SERIAL && DEVICE_SERIAL_FC
#include "H4TransportDriver.h"
namespace ble {
@ -68,3 +70,5 @@ void H4TransportDriver::on_controller_irq()
} // namespace cordio
} // namespace vendor
} // namespace ble
#endif

View File

@ -17,6 +17,8 @@
#ifndef CORDIO_H4_TRANSPORT_DRIVER_H_
#define CORDIO_H4_TRANSPORT_DRIVER_H_
#if (DEVICE_SERIAL && DEVICE_SERIAL_FC) || defined(DOXYGEN_ONLY)
#include <stdint.h>
#include "mbed.h"
#include "CordioHCITransportDriver.h"
@ -27,6 +29,9 @@ namespace cordio {
/**
* Implementation of the H4 driver.
*
* @note This HCI transport implementation is not accessible to devices that do
* not expose serial flow control.
*/
class H4TransportDriver : public CordioHCITransportDriver {
public:
@ -73,4 +78,6 @@ private:
} // namespace vendor
} // namespace ble
#endif
#endif /* CORDIO_H4_TRANSPORT_DRIVER_H_ */

View File

@ -0,0 +1,344 @@
# Change Log
## [v2.5.3](https://github.com/ARMmbed/ble-nrf51822/tree/v2.5.3) (2016-02-16)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.5.2...v2.5.3)
**Merged pull requests:**
- Fix for compilation errors with S110 softdevice in btle.cpp [\#109](https://github.com/ARMmbed/ble-nrf51822/pull/109) ([ddavidebor](https://github.com/ddavidebor))
## [v2.5.2](https://github.com/ARMmbed/ble-nrf51822/tree/v2.5.2) (2016-02-16)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.5.1...v2.5.2)
**Merged pull requests:**
- Sync develop against master [\#113](https://github.com/ARMmbed/ble-nrf51822/pull/113) ([pan-](https://github.com/pan-))
- Fix incorrect handles of characteristics descriptors. [\#112](https://github.com/ARMmbed/ble-nrf51822/pull/112) ([pan-](https://github.com/pan-))
## [v2.5.1](https://github.com/ARMmbed/ble-nrf51822/tree/v2.5.1) (2016-01-27)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.5.0...v2.5.1)
**Merged pull requests:**
- Remove Gap::state updates from this module [\#108](https://github.com/ARMmbed/ble-nrf51822/pull/108) ([andresag01](https://github.com/andresag01))
- merge version [\#106](https://github.com/ARMmbed/ble-nrf51822/pull/106) ([pan-](https://github.com/pan-))
## [v2.5.0](https://github.com/ARMmbed/ble-nrf51822/tree/v2.5.0) (2016-01-12)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.4.1...v2.5.0)
**Merged pull requests:**
- Fix access to enum member [\#105](https://github.com/ARMmbed/ble-nrf51822/pull/105) ([pan-](https://github.com/pan-))
- Hotfix dependency [\#104](https://github.com/ARMmbed/ble-nrf51822/pull/104) ([pan-](https://github.com/pan-))
- Finish implementation of getAddressesFromBondTable [\#103](https://github.com/ARMmbed/ble-nrf51822/pull/103) ([andresag01](https://github.com/andresag01))
## [v2.4.1](https://github.com/ARMmbed/ble-nrf51822/tree/v2.4.1) (2016-01-11)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.4.0...v2.4.1)
**Merged pull requests:**
- merge branch develop \(v2.4.0\) [\#100](https://github.com/ARMmbed/ble-nrf51822/pull/100) ([pan-](https://github.com/pan-))
## [v2.4.0](https://github.com/ARMmbed/ble-nrf51822/tree/v2.4.0) (2016-01-10)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.3.1...v2.4.0)
**Merged pull requests:**
- Add implementation of experimental whitelisting API [\#99](https://github.com/ARMmbed/ble-nrf51822/pull/99) ([andresag01](https://github.com/andresag01))
## [v2.3.1](https://github.com/ARMmbed/ble-nrf51822/tree/v2.3.1) (2016-01-07)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.3.0...v2.3.1)
**Merged pull requests:**
- Update yotta module dependencies [\#98](https://github.com/ARMmbed/ble-nrf51822/pull/98) ([pan-](https://github.com/pan-))
## [v2.3.0](https://github.com/ARMmbed/ble-nrf51822/tree/v2.3.0) (2015-12-23)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.2.10...v2.3.0)
**Merged pull requests:**
- Implementation of Characteristic descriptor discovery [\#74](https://github.com/ARMmbed/ble-nrf51822/pull/74) ([pan-](https://github.com/pan-))
## [v2.2.10](https://github.com/ARMmbed/ble-nrf51822/tree/v2.2.10) (2015-12-23)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.2.9...v2.2.10)
**Fixed bugs:**
- nRF5xn::init don't verify if errors have occurred during btle\_init [\#59](https://github.com/ARMmbed/ble-nrf51822/issues/59)
**Closed issues:**
- A call to shutdown does not clear the state of some components of BLE API [\#85](https://github.com/ARMmbed/ble-nrf51822/issues/85)
- Memory allocation issue on the NRF51DK board. [\#76](https://github.com/ARMmbed/ble-nrf51822/issues/76)
- Terrible handling of initLen / minLen and variable length characteristics. [\#56](https://github.com/ARMmbed/ble-nrf51822/issues/56)
**Merged pull requests:**
- Fix shutdown of Gap instance to avoid NULL refs [\#96](https://github.com/ARMmbed/ble-nrf51822/pull/96) ([andresag01](https://github.com/andresag01))
- Add check for return code of ble\_init [\#95](https://github.com/ARMmbed/ble-nrf51822/pull/95) ([andresag01](https://github.com/andresag01))
## [v2.2.9](https://github.com/ARMmbed/ble-nrf51822/tree/v2.2.9) (2015-12-18)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.2.8...v2.2.9)
**Closed issues:**
- Cannot open source input file "system\_nrf51.h" [\#52](https://github.com/ARMmbed/ble-nrf51822/issues/52)
**Merged pull requests:**
- Remove occurrence of deprecated appearance enum [\#92](https://github.com/ARMmbed/ble-nrf51822/pull/92) ([andresag01](https://github.com/andresag01))
## [v2.2.8](https://github.com/ARMmbed/ble-nrf51822/tree/v2.2.8) (2015-12-16)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.2.7...v2.2.8)
## [v2.2.7](https://github.com/ARMmbed/ble-nrf51822/tree/v2.2.7) (2015-12-15)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.2.6...v2.2.7)
**Merged pull requests:**
- Replace deprecated inclusions of mbed.h [\#89](https://github.com/ARMmbed/ble-nrf51822/pull/89) ([andresag01](https://github.com/andresag01))
- Improve shutdown to clear BLE API and not just SD [\#87](https://github.com/ARMmbed/ble-nrf51822/pull/87) ([andresag01](https://github.com/andresag01))
## [v2.2.6](https://github.com/ARMmbed/ble-nrf51822/tree/v2.2.6) (2015-12-15)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.2.5...v2.2.6)
**Merged pull requests:**
- follow the extraction of address related types from Gap.h into BLEProtocol.h [\#88](https://github.com/ARMmbed/ble-nrf51822/pull/88) ([rgrover](https://github.com/rgrover))
## [v2.2.5](https://github.com/ARMmbed/ble-nrf51822/tree/v2.2.5) (2015-12-11)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.2.3...v2.2.5)
**Merged pull requests:**
- Added SecurityManager::setLinkSecurity call for elevating security settings on a particular connection. [\#86](https://github.com/ARMmbed/ble-nrf51822/pull/86) ([marcuschangarm](https://github.com/marcuschangarm))
## [v2.2.3](https://github.com/ARMmbed/ble-nrf51822/tree/v2.2.3) (2015-12-10)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.2.2...v2.2.3)
## [v2.2.2](https://github.com/ARMmbed/ble-nrf51822/tree/v2.2.2) (2015-12-08)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.2.1...v2.2.2)
**Merged pull requests:**
- Add -Wno-unused-function to supress-warnings.cmake [\#83](https://github.com/ARMmbed/ble-nrf51822/pull/83) ([andresag01](https://github.com/andresag01))
## [v2.2.1](https://github.com/ARMmbed/ble-nrf51822/tree/v2.2.1) (2015-12-08)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.2.0...v2.2.1)
**Merged pull requests:**
- WIP: UUID endian change [\#82](https://github.com/ARMmbed/ble-nrf51822/pull/82) ([marcuschangarm](https://github.com/marcuschangarm))
## [v2.2.0](https://github.com/ARMmbed/ble-nrf51822/tree/v2.2.0) (2015-12-02)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.1.4...v2.2.0)
## [v2.1.4](https://github.com/ARMmbed/ble-nrf51822/tree/v2.1.4) (2015-12-02)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.1.3...v2.1.4)
## [v2.1.3](https://github.com/ARMmbed/ble-nrf51822/tree/v2.1.3) (2015-12-02)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.1.2...v2.1.3)
## [v2.1.2](https://github.com/ARMmbed/ble-nrf51822/tree/v2.1.2) (2015-12-02)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.1.1...v2.1.2)
**Merged pull requests:**
- Allow GattAttributes to have variable length [\#81](https://github.com/ARMmbed/ble-nrf51822/pull/81) ([andresag01](https://github.com/andresag01))
## [v2.1.1](https://github.com/ARMmbed/ble-nrf51822/tree/v2.1.1) (2015-12-02)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.1.0...v2.1.1)
**Merged pull requests:**
- Fixed endianness bug in nRF5xServiceDiscovery::processDiscoverUUIDResponse so it is consistent with BLE API. [\#80](https://github.com/ARMmbed/ble-nrf51822/pull/80) ([marcuschangarm](https://github.com/marcuschangarm))
- Fixed bug in nRF5xGap.setAddress where random adresses where not set properly. [\#79](https://github.com/ARMmbed/ble-nrf51822/pull/79) ([marcuschangarm](https://github.com/marcuschangarm))
- Separate concept of minlen and len for BLE chars [\#78](https://github.com/ARMmbed/ble-nrf51822/pull/78) ([andresag01](https://github.com/andresag01))
- Split nordic sdk into its own module [\#75](https://github.com/ARMmbed/ble-nrf51822/pull/75) ([LiyouZhou](https://github.com/LiyouZhou))
## [v2.1.0](https://github.com/ARMmbed/ble-nrf51822/tree/v2.1.0) (2015-11-27)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.0.8...v2.1.0)
**Merged pull requests:**
- Update to sdk 8.1 [\#77](https://github.com/ARMmbed/ble-nrf51822/pull/77) ([LiyouZhou](https://github.com/LiyouZhou))
## [v2.0.8](https://github.com/ARMmbed/ble-nrf51822/tree/v2.0.8) (2015-11-26)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.0.7...v2.0.8)
## [v2.0.7](https://github.com/ARMmbed/ble-nrf51822/tree/v2.0.7) (2015-11-26)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.0.6...v2.0.7)
**Closed issues:**
- test2000 [\#72](https://github.com/ARMmbed/ble-nrf51822/issues/72)
- test1000000 [\#71](https://github.com/ARMmbed/ble-nrf51822/issues/71)
- test4 [\#70](https://github.com/ARMmbed/ble-nrf51822/issues/70)
- test3 [\#69](https://github.com/ARMmbed/ble-nrf51822/issues/69)
- test2 [\#68](https://github.com/ARMmbed/ble-nrf51822/issues/68)
**Merged pull requests:**
- use Extern c around \#include to use nordic sdk headers implemented in C [\#73](https://github.com/ARMmbed/ble-nrf51822/pull/73) ([LiyouZhou](https://github.com/LiyouZhou))
## [v2.0.6](https://github.com/ARMmbed/ble-nrf51822/tree/v2.0.6) (2015-11-17)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.0.5...v2.0.6)
**Closed issues:**
- test [\#66](https://github.com/ARMmbed/ble-nrf51822/issues/66)
**Merged pull requests:**
- add Nordic's license agreement. [\#67](https://github.com/ARMmbed/ble-nrf51822/pull/67) ([rgrover](https://github.com/rgrover))
## [v2.0.5](https://github.com/ARMmbed/ble-nrf51822/tree/v2.0.5) (2015-11-16)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.0.4...v2.0.5)
**Merged pull requests:**
- Post radio notification callback through minar [\#65](https://github.com/ARMmbed/ble-nrf51822/pull/65) ([andresag01](https://github.com/andresag01))
## [v2.0.4](https://github.com/ARMmbed/ble-nrf51822/tree/v2.0.4) (2015-11-13)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.0.3...v2.0.4)
**Merged pull requests:**
- Fix assembly sequence to start bootloader in GCC [\#64](https://github.com/ARMmbed/ble-nrf51822/pull/64) ([andresag01](https://github.com/andresag01))
## [v2.0.3](https://github.com/ARMmbed/ble-nrf51822/tree/v2.0.3) (2015-11-09)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.0.2...v2.0.3)
**Merged pull requests:**
- Added watchdog header file from Nordic SDK 8.1 [\#62](https://github.com/ARMmbed/ble-nrf51822/pull/62) ([marcuschangarm](https://github.com/marcuschangarm))
## [v2.0.2](https://github.com/ARMmbed/ble-nrf51822/tree/v2.0.2) (2015-11-03)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/mbedos-release-15-11...v2.0.2)
## [mbedos-release-15-11](https://github.com/ARMmbed/ble-nrf51822/tree/mbedos-release-15-11) (2015-11-03)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.0.1...mbedos-release-15-11)
## [v2.0.1](https://github.com/ARMmbed/ble-nrf51822/tree/v2.0.1) (2015-11-02)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v2.0.0...v2.0.1)
**Merged pull requests:**
- Ensure that the initialization flags is set to false if the BLE stack is shutdown properly. [\#58](https://github.com/ARMmbed/ble-nrf51822/pull/58) ([pan-](https://github.com/pan-))
## [v2.0.0](https://github.com/ARMmbed/ble-nrf51822/tree/v2.0.0) (2015-11-02)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v1.1.0...v2.0.0)
**Closed issues:**
- Nordic SDK and SoftDevice [\#57](https://github.com/ARMmbed/ble-nrf51822/issues/57)
- shouldn't eab6631cb be merged into master? [\#54](https://github.com/ARMmbed/ble-nrf51822/issues/54)
**Merged pull requests:**
- Introduced changes for memory savings [\#55](https://github.com/ARMmbed/ble-nrf51822/pull/55) ([andresag01](https://github.com/andresag01))
## [v1.1.0](https://github.com/ARMmbed/ble-nrf51822/tree/v1.1.0) (2015-10-28)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v1.0.0...v1.1.0)
**Closed issues:**
- target dependencies in module.json [\#50](https://github.com/ARMmbed/ble-nrf51822/issues/50)
**Merged pull requests:**
- When connecting, if no scanning parameters are passed, use values from Gap parent. [\#53](https://github.com/ARMmbed/ble-nrf51822/pull/53) ([marcuschangarm](https://github.com/marcuschangarm))
## [v1.0.0](https://github.com/ARMmbed/ble-nrf51822/tree/v1.0.0) (2015-10-19)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/mbedos-techcon-oob2...v1.0.0)
## [mbedos-techcon-oob2](https://github.com/ARMmbed/ble-nrf51822/tree/mbedos-techcon-oob2) (2015-10-19)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v0.4.8...mbedos-techcon-oob2)
**Closed issues:**
- rename the bootloader files with \_fota in the name? [\#51](https://github.com/ARMmbed/ble-nrf51822/issues/51)
**Merged pull requests:**
- Update S110 detection macros, again [\#49](https://github.com/ARMmbed/ble-nrf51822/pull/49) ([jpbrucker](https://github.com/jpbrucker))
- Error check number of characteristics [\#48](https://github.com/ARMmbed/ble-nrf51822/pull/48) ([Timmmm](https://github.com/Timmmm))
## [v0.4.8](https://github.com/ARMmbed/ble-nrf51822/tree/v0.4.8) (2015-09-25)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v0.4.7...v0.4.8)
**Closed issues:**
- Error real cause loss in nRF5xGattServer.cpp [\#44](https://github.com/ARMmbed/ble-nrf51822/issues/44)
**Merged pull requests:**
- rgrover patch fixed [\#47](https://github.com/ARMmbed/ble-nrf51822/pull/47) ([fabiencomte](https://github.com/fabiencomte))
- Update S110 detection macros [\#43](https://github.com/ARMmbed/ble-nrf51822/pull/43) ([jpbrucker](https://github.com/jpbrucker))
- remove some unnecessary include paths [\#42](https://github.com/ARMmbed/ble-nrf51822/pull/42) ([autopulated](https://github.com/autopulated))
- Add FOTA bootloader image [\#41](https://github.com/ARMmbed/ble-nrf51822/pull/41) ([jpbrucker](https://github.com/jpbrucker))
## [v0.4.7](https://github.com/ARMmbed/ble-nrf51822/tree/v0.4.7) (2015-08-13)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v0.4.6...v0.4.7)
## [v0.4.6](https://github.com/ARMmbed/ble-nrf51822/tree/v0.4.6) (2015-08-11)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v0.4.5...v0.4.6)
**Closed issues:**
- remove duplication of global static variable BLE\_EVT\_BUFFER [\#39](https://github.com/ARMmbed/ble-nrf51822/issues/39)
- clearScanResponse\(\) [\#30](https://github.com/ARMmbed/ble-nrf51822/issues/30)
- Debug builds fail due to missing bsp.h [\#11](https://github.com/ARMmbed/ble-nrf51822/issues/11)
**Merged pull requests:**
- Disable GattClient features when using S110 SoftDevice [\#38](https://github.com/ARMmbed/ble-nrf51822/pull/38) ([jpbrucker](https://github.com/jpbrucker))
## [v0.4.5](https://github.com/ARMmbed/ble-nrf51822/tree/v0.4.5) (2015-08-10)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v0.4.4...v0.4.5)
## [v0.4.4](https://github.com/ARMmbed/ble-nrf51822/tree/v0.4.4) (2015-08-07)
[Full Changelog](https://github.com/ARMmbed/ble-nrf51822/compare/v0.4.3...v0.4.4)
**Closed issues:**
- nrf51822 hangs after calling sd\_flash\_page\_erase\(\) [\#35](https://github.com/ARMmbed/ble-nrf51822/issues/35)
- nRF5xn::getVersion return \(Unknown\) with version 8 soft device [\#29](https://github.com/ARMmbed/ble-nrf51822/issues/29)
**Merged pull requests:**
- Changed Gap:: to GapAdvertisingParams:: because of change in BLE [\#34](https://github.com/ARMmbed/ble-nrf51822/pull/34) ([jslater8](https://github.com/jslater8))
- Select the clock source dynamically on SoftDevice initialisation [\#32](https://github.com/ARMmbed/ble-nrf51822/pull/32) ([jpbrucker](https://github.com/jpbrucker))
- Add S110 SoftDevice compatibility [\#28](https://github.com/ARMmbed/ble-nrf51822/pull/28) ([jpbrucker](https://github.com/jpbrucker))
## [v0.4.3](https://github.com/ARMmbed/ble-nrf51822/tree/v0.4.3) (2015-07-22)
**Closed issues:**
- Target polling failed [\#24](https://github.com/ARMmbed/ble-nrf51822/issues/24)
- support handling of HVX Events \(notifications and indications\). [\#22](https://github.com/ARMmbed/ble-nrf51822/issues/22)
- provide an implementation for GattServer::areUpdatesEnabled\(\) [\#21](https://github.com/ARMmbed/ble-nrf51822/issues/21)
- getValueHandle\(\) returns characteristicIndex instead of attribute-handle [\#20](https://github.com/ARMmbed/ble-nrf51822/issues/20)
- Clash With Definition And Enum Naming [\#16](https://github.com/ARMmbed/ble-nrf51822/issues/16)
- Errors in GCC build [\#14](https://github.com/ARMmbed/ble-nrf51822/issues/14)
- bring s110 support back [\#10](https://github.com/ARMmbed/ble-nrf51822/issues/10)
- Allow adding a User Description descriptor to a GattCharacteristic. [\#9](https://github.com/ARMmbed/ble-nrf51822/issues/9)
- device\_manager\_peripheral.c includes app\_trace.h [\#7](https://github.com/ARMmbed/ble-nrf51822/issues/7)
- linking esb\_gcc.a \(nrf51822 enhanced shock burst\) with mbed [\#5](https://github.com/ARMmbed/ble-nrf51822/issues/5)
- The app\_timer usage may conflict [\#2](https://github.com/ARMmbed/ble-nrf51822/issues/2)
- Nordic License [\#1](https://github.com/ARMmbed/ble-nrf51822/issues/1)
**Merged pull requests:**
- Develop [\#25](https://github.com/ARMmbed/ble-nrf51822/pull/25) ([zoujixing](https://github.com/zoujixing))
- Remove unnecessary 'compiler\_abstraction.h' to get rid of duplicate '… [\#23](https://github.com/ARMmbed/ble-nrf51822/pull/23) ([adfernandes](https://github.com/adfernandes))
- restructure for minimal yotta compatibility [\#15](https://github.com/ARMmbed/ble-nrf51822/pull/15) ([autopulated](https://github.com/autopulated))
- Fix various GCC compilation issues. [\#12](https://github.com/ARMmbed/ble-nrf51822/pull/12) ([adfernandes](https://github.com/adfernandes))
- Fix for GCC lost in SDK v8.0 update [\#8](https://github.com/ARMmbed/ble-nrf51822/pull/8) ([rosterloh](https://github.com/rosterloh))
- new target DELTA\_DFCM\_NNN40 with nrf51822 chip, config internal RC crystal. [\#6](https://github.com/ARMmbed/ble-nrf51822/pull/6) ([Marcomissyou](https://github.com/Marcomissyou))
- Updated return value for nRF51GattServer::updateValue. Will now report w... [\#4](https://github.com/ARMmbed/ble-nrf51822/pull/4) ([marcuschangarm](https://github.com/marcuschangarm))
- Added optional data and length fields to the return struct for authorize... [\#3](https://github.com/ARMmbed/ble-nrf51822/pull/3) ([marcuschangarm](https://github.com/marcuschangarm))
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

View File

@ -0,0 +1,6 @@
This module contains softdevice which comes with The Nordic Softdevice License Agreement,
a BSD-like licence for binary distributions, offered by Nordic for use in mbed. Some
other files come from the mbed SDK, and are licensed under Apache-2.0. Unless
specifically indicated otherwise in a file, files are licensed under the
Apache 2.0 license, as can be found in: apache-2.0.txt. The Nordic Semiconductor Softdevice
License Agreement can be found in softdevice_nrf51822_licence_agreement.txt.

View File

@ -0,0 +1,13 @@
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.

View File

@ -0,0 +1,36 @@
{
"name": "ble-nrf51822",
"version": "2.7.1",
"description": "Nordic stack and drivers for the mbed BLE API.",
"keywords": [
"Bluetooth",
"BLE",
"mbed",
"mbed-official"
],
"author": "Rohit Grover",
"repository": {
"url": "git@github.com:ARMmbed/ble-nRF51822.git",
"type": "git"
},
"homepage": "https://developer.mbed.org/teams/Nordic-Semiconductor/",
"licenses": [
{
"url": "https://spdx.org/licenses/Apache-2.0",
"type": "Apache-2.0"
},
{
"type": "LicenseRef-softdevice_nrf51822_licence_agreement.txt"
}
],
"dependencies": {
"ble": "^2.6.0",
"nrf51-sdk": "^2.4.0"
},
"extraIncludes": [
"source/btle",
"source/btle/custom",
"source/common"
],
"targetDependencies": {}
}

View File

@ -0,0 +1,30 @@
/*
* S110/S120/S130 License Agreement
*
* Copyright (c) 2015, Nordic Semiconductor ASA, All rights reserved.
*
* Redistribution. Redistribution and use in binary form, without modification,
* are permitted provided that the following conditions are met:
*
* • Redistributions must reproduce the above copyright notice and the following
* disclaimer in the documentation and/or other materials provided with the
* distribution.
* • Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* • No reverse engineering, decompilation, or disassembly of this software is
* permitted.
*
* DISCLAIMER.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* /

View File

@ -0,0 +1,490 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 "common/common.h"
#include "nordic_common.h"
#include "btle.h"
#include "btle_clock.h"
#include "ble_flash.h"
#include "ble_conn_params.h"
#include "btle_gap.h"
#include "custom/custom_helper.h"
#include "ble/GapEvents.h"
#include "nRF5xn.h"
// This is a C++ file, so C11 _Static_assert (works with -std=gnu99 on GCC) won't work
#undef STATIC_ASSERT_SIMPLE
#undef STATIC_ASSERT_MSG
// FIXME : We can't use mbed_assert.h because we're using these macros within functions
#define STATIC_ASSERT_MSG(EXPR, MSG)
#define STATIC_ASSERT_SIMPLE(EXPR)
#warning FIXME : We can't use mbed_assert.h because we're using these within functions
#ifdef S110
#define IS_LEGACY_DEVICE_MANAGER_ENABLED 1
#elif defined(S130) || defined(S132)
#define IS_LEGACY_DEVICE_MANAGER_ENABLED 0
#endif
extern "C" {
#if (IS_LEGACY_DEVICE_MANAGER_ENABLED)
#include "pstorage.h"
#include "device_manager.h"
#else
#include "nrf_fstorage.h"
#include "fds.h"
#include "peer_manager.h"
#include "ble_conn_state.h"
#endif
#include "nrf_sdh.h"
#include "nrf_sdh_ble.h"
}
#include "headers/ble_hci.h"
#include "nRF5XPalGattClient.h"
// Make this volatile at it will be set in interrupt context
volatile bool isEventsSignaled = false;
extern "C" void assert_nrf_callback(uint16_t line_num, const uint8_t *p_file_name);
void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t *p_file_name);
// Before SDK 14, the softdevice handler is implemented within the SDK
// In SDK 14+, we have to implement it as we're using the "polling" mode for the SD
extern "C" void SD_EVT_IRQHandler(void); // export the softdevice event handler for registration by nvic-set-vector.
#if NRF_SDK14PLUS_EVENT_HANDLERS
static void btle_handler(const ble_evt_t *p_ble_evt, void *p_context);
#else
static void btle_handler(ble_evt_t *p_ble_evt);
#endif
#if !NRF_SDK14PLUS_EVENT_HANDLERS
static void sys_evt_dispatch(uint32_t sys_evt)
{
#if (IS_LEGACY_DEVICE_MANAGER_ENABLED)
pstorage_sys_event_handler(sys_evt);
#else
// Forward Softdevice events to the fstorage module
fs_sys_event_handler(sys_evt);
#endif
}
#endif
/**
* This function is called in interrupt context to handle BLE events; i.e. pull
* system and user events out of the pending events-queue of the BLE stack. The
* BLE stack signals the availability of events by the triggering the SWI2
* interrupt, which forwards the handling to this function.
*
* The event processing loop is implemented in intern_softdevice_events_execute().
*
* This function will signal to the user code by calling signalEventsToProcess
* that their is events to process and BLE::processEvents should be called.
*/
static uint32_t signalEvent()
{
if(isEventsSignaled == false) {
isEventsSignaled = true;
nRF5xn::Instance(BLE::DEFAULT_INSTANCE).signalEventsToProcess(BLE::DEFAULT_INSTANCE);
}
return NRF_SUCCESS;
}
error_t btle_init(void)
{
nrf_clock_lf_cfg_t clockConfiguration;
ret_code_t err_code;
// register softdevice handler vector
NVIC_SetVector(SD_EVT_IRQn, (uint32_t) SD_EVT_IRQHandler);
#if (NRF_SD_BLE_API_VERSION >= 5)
err_code = nrf_sdh_enable_request();
ASSERT_STATUS(err_code);
#else
// Configure the LF clock according to values provided by btle_clock.h.
// It is input from the chain of the yotta configuration system.
clockConfiguration.source = LFCLK_CONF_SOURCE;
clockConfiguration.xtal_accuracy = LFCLK_CONF_ACCURACY;
clockConfiguration.rc_ctiv = LFCLK_CONF_RC_CTIV;
clockConfiguration.rc_temp_ctiv = LFCLK_CONF_RC_TEMP_CTIV;
SOFTDEVICE_HANDLER_INIT(&clockConfiguration, signalEvent);
#endif
// Enable BLE stack
#if (NRF_SD_BLE_API_VERSION >= 5)
// Configure softdevice manually
// We could have used nrf_sdh_ble_default_cfg_set() but it's tightly coupled with the macros defined in sdk_config.h
ble_cfg_t ble_cfg;
uint32_t ram_start = 0;
// Recover start address of application's RAM
err_code = nrf_sdh_ble_app_ram_start_get(&ram_start);
ASSERT_STATUS(err_code);
// First configure GAP parameters, including the maximum number of connections
memset(&ble_cfg, 0, sizeof(ble_cfg_t));
ble_cfg.conn_cfg.conn_cfg_tag = NRF_CONNECTION_TAG;
ble_cfg.conn_cfg.params.gap_conn_cfg.conn_count = TOTAL_LINK_COUNT;
ble_cfg.conn_cfg.params.gap_conn_cfg.event_length = NRF_SDH_BLE_GAP_EVENT_LENGTH; // FIXME?
err_code = sd_ble_cfg_set(BLE_CONN_CFG_GAP, &ble_cfg, ram_start);
if(err_code != NRF_SUCCESS) {
return ERROR_INVALID_PARAM;
}
// GAP - Configure the number of peripheral and central links
memset(&ble_cfg, 0, sizeof(ble_cfg_t));
ble_cfg.conn_cfg.conn_cfg_tag = NRF_CONNECTION_TAG;
ble_cfg.gap_cfg.role_count_cfg.periph_role_count = PERIPHERAL_LINK_COUNT;
ble_cfg.gap_cfg.role_count_cfg.central_role_count = CENTRAL_LINK_COUNT;
ble_cfg.gap_cfg.role_count_cfg.central_sec_count = CENTRAL_LINK_COUNT ?
BLE_GAP_ROLE_COUNT_CENTRAL_SEC_DEFAULT : 0;
err_code = sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &ble_cfg, ram_start);
if(err_code != NRF_SUCCESS) {
return ERROR_INVALID_PARAM;
}
// Configure GATT
memset(&ble_cfg, 0, sizeof(ble_cfg_t));
ble_cfg.conn_cfg.conn_cfg_tag = NRF_CONNECTION_TAG;
ble_cfg.conn_cfg.params.gatt_conn_cfg.att_mtu = NRF_SDH_BLE_GATT_MAX_MTU_SIZE;
err_code = sd_ble_cfg_set(BLE_CONN_CFG_GATT, &ble_cfg, ram_start);
if(err_code != NRF_SUCCESS) {
return ERROR_INVALID_PARAM;
}
// Number of custom UUIDs
memset(&ble_cfg, 0, sizeof(ble_cfg_t));
ble_cfg.common_cfg.vs_uuid_cfg.vs_uuid_count = UUID_TABLE_MAX_ENTRIES;
err_code = sd_ble_cfg_set(BLE_COMMON_CFG_VS_UUID, &ble_cfg, ram_start);
if(err_code != NRF_SUCCESS) {
return ERROR_INVALID_PARAM;
}
// GATT Server attribute table size
memset(&ble_cfg, 0, sizeof(ble_cfg_t));
ble_cfg.gatts_cfg.attr_tab_size.attr_tab_size = GATTS_ATTR_TAB_SIZE;
err_code = sd_ble_cfg_set(BLE_GATTS_CFG_ATTR_TAB_SIZE, &ble_cfg, ram_start);
if(err_code != NRF_SUCCESS) {
return ERROR_INVALID_PARAM;
}
// GATT Server, Service Changed characteristic
memset(&ble_cfg, 0, sizeof(ble_cfg_t));
ble_cfg.gatts_cfg.service_changed.service_changed = IS_SRVC_CHANGED_CHARACT_PRESENT;
err_code = sd_ble_cfg_set(BLE_GATTS_CFG_SERVICE_CHANGED, &ble_cfg, ram_start);
if(err_code != NRF_SUCCESS) {
return ERROR_INVALID_PARAM;
}
// Enable BLE stack in softdevice
err_code = nrf_sdh_ble_enable(&ram_start);
if(err_code != NRF_SUCCESS) {
return ERROR_INVALID_PARAM;
}
#else
ble_enable_params_t ble_enable_params;
err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
PERIPHERAL_LINK_COUNT,
&ble_enable_params);
ble_enable_params.gatts_enable_params.attr_tab_size = GATTS_ATTR_TAB_SIZE;
ble_enable_params.gatts_enable_params.service_changed = IS_SRVC_CHANGED_CHARACT_PRESENT;
ble_enable_params.common_enable_params.vs_uuid_count = UUID_TABLE_MAX_ENTRIES;
if(err_code != NRF_SUCCESS) {
return ERROR_INVALID_PARAM;
}
if (softdevice_enable(&ble_enable_params) != NRF_SUCCESS) {
return ERROR_INVALID_PARAM;
}
#endif
// Peer Manger must been initialised prior any other call to its API (this file and btle_security_pm.cpp)
pm_init();
#if (NRF_SD_BLE_API_VERSION <= 2)
ble_gap_addr_t addr;
if (sd_ble_gap_address_get(&addr) != NRF_SUCCESS) {
return ERROR_INVALID_PARAM;
}
if (sd_ble_gap_address_set(BLE_GAP_ADDR_CYCLE_MODE_NONE, &addr) != NRF_SUCCESS) {
return ERROR_INVALID_PARAM;
}
#else
ble_gap_privacy_params_t privacy_params = {0};
privacy_params.privacy_mode = BLE_GAP_PRIVACY_MODE_OFF;
pm_privacy_set(&privacy_params);
#endif
// From SDK 14 onwards event handlers are registered differently
// http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v14.0.0%2Fmigration.html
#if NRF_SDK14PLUS_EVENT_HANDLERS
// Register a handler for our BLE events.
NRF_SDH_BLE_OBSERVER(m_ble_observer, 3 /* default priority for user events */, btle_handler, NULL);
#else
ASSERT_STATUS( softdevice_ble_evt_handler_set(btle_handler));
ASSERT_STATUS( softdevice_sys_evt_handler_set(sys_evt_dispatch));
#endif
return btle_gap_init();
}
#if NRF_SDK14PLUS_EVENT_HANDLERS
static void btle_handler(const ble_evt_t *p_ble_evt, void *p_context)
#else
static void btle_handler(ble_evt_t *p_ble_evt)
#endif
{
#if NRF_SDK14PLUS_EVENT_HANDLERS
(void)p_context; // Keep compiler happy
#endif
using ble::pal::vendor::nordic::nRF5XGattClient;
// In SDK14+, all other modules from the SDK will be registered independently as softdevice events observers
#if !NRF_SDK14PLUS_EVENT_HANDLERS
/* Library service handlers */
#if SDK_CONN_PARAMS_MODULE_ENABLE
ble_conn_params_on_ble_evt(p_ble_evt);
#endif
#if (IS_LEGACY_DEVICE_MANAGER_ENABLED)
dm_ble_evt_handler(p_ble_evt);
#else
// Forward BLE events to the Connection State module.
// This must be called before any event handler that uses this module.
ble_conn_state_on_ble_evt(p_ble_evt);
// Forward BLE events to the Peer Manager
pm_on_ble_evt(p_ble_evt);
#endif
#endif
#if !defined(TARGET_MCU_NRF51_16K_S110) && !defined(TARGET_MCU_NRF51_32K_S110)
nRF5XGattClient::handle_events(p_ble_evt);
#endif
nRF5xn &ble = nRF5xn::Instance(BLE::DEFAULT_INSTANCE);
nRF5xGap &gap = (nRF5xGap &) ble.getGap();
nRF5xGattServer &gattServer = (nRF5xGattServer &) ble.getGattServer();
nRF5xSecurityManager &securityManager = (nRF5xSecurityManager &) ble.getSecurityManager();
/* Custom event handler */
switch (p_ble_evt->header.evt_id) {
case BLE_GAP_EVT_CONNECTED: {
Gap::Handle_t handle = p_ble_evt->evt.gap_evt.conn_handle;
#if defined(TARGET_MCU_NRF51_16K_S110) || defined(TARGET_MCU_NRF51_32K_S110)
/* Only peripheral role is supported by S110 */
Gap::Role_t role = Gap::PERIPHERAL;
#else
Gap::Role_t role = static_cast<Gap::Role_t>(p_ble_evt->evt.gap_evt.params.connected.role);
#endif
gap.setConnectionHandle(handle);
const Gap::ConnectionParams_t *params = reinterpret_cast<const Gap::ConnectionParams_t *>(&(p_ble_evt->evt.gap_evt.params.connected.conn_params));
const ble_gap_addr_t *peer = &p_ble_evt->evt.gap_evt.params.connected.peer_addr;
#if (NRF_SD_BLE_API_VERSION <= 2)
const ble_gap_addr_t *own = &p_ble_evt->evt.gap_evt.params.connected.own_addr;
gap.processConnectionEvent(handle,
role,
static_cast<BLEProtocol::AddressType_t>(peer->addr_type), peer->addr,
static_cast<BLEProtocol::AddressType_t>(own->addr_type), own->addr,
params);
#else
Gap::AddressType_t addr_type;
Gap::Address_t own_address;
gap.getAddress(&addr_type, own_address);
gap.processConnectionEvent(handle,
role,
static_cast<BLEProtocol::AddressType_t>(peer->addr_type), peer->addr,
addr_type, own_address,
params);
#endif
break;
}
case BLE_GAP_EVT_DISCONNECTED: {
Gap::Handle_t handle = p_ble_evt->evt.gap_evt.conn_handle;
// Since we are not in a connection and have not started advertising,
// store bonds
gap.setConnectionHandle (BLE_CONN_HANDLE_INVALID);
Gap::DisconnectionReason_t reason;
switch (p_ble_evt->evt.gap_evt.params.disconnected.reason) {
case BLE_HCI_LOCAL_HOST_TERMINATED_CONNECTION:
reason = Gap::LOCAL_HOST_TERMINATED_CONNECTION;
break;
case BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION:
reason = Gap::REMOTE_USER_TERMINATED_CONNECTION;
break;
case BLE_HCI_CONN_INTERVAL_UNACCEPTABLE:
reason = Gap::CONN_INTERVAL_UNACCEPTABLE;
break;
default:
/* Please refer to the underlying transport library for an
* interpretion of this reason's value. */
reason = static_cast<Gap::DisconnectionReason_t>(p_ble_evt->evt.gap_evt.params.disconnected.reason);
break;
}
#if !defined(TARGET_MCU_NRF51_16K_S110) && !defined(TARGET_MCU_NRF51_32K_S110)
// Close all pending discoveries for this connection
nRF5XGattClient::handle_connection_termination(handle);
#endif
gap.processDisconnectionEvent(handle, reason);
break;
}
#if (NRF_SD_BLE_API_VERSION >= 5)
#ifndef S140
// Handle PHY upgrade request
case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
{
ble_gap_phys_t const phys =
{
/* rx_phys */ BLE_GAP_PHY_AUTO,
/* tx_phys */ BLE_GAP_PHY_AUTO,
};
ASSERT_STATUS_RET_VOID( sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys) );
break;
}
#endif
// Handle Data length negotiation request
case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST:
{
ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
uint8_t const data_length_peer =
p_gap_evt->params.data_length_update_request.peer_params.max_tx_octets;
const uint8_t max_data_length = NRF_SDH_BLE_GATT_MAX_MTU_SIZE + 4 /* L2CAP header size */;
uint8_t const data_length = MIN(max_data_length, data_length_peer);
ble_gap_data_length_params_t const dlp =
{
/* max_rx_octets */ data_length,
/* max_tx_octets */ data_length
};
ASSERT_STATUS_RET_VOID(sd_ble_gap_data_length_update(p_gap_evt->conn_handle, &dlp, NULL));
break;
}
// Handle MTU exchange request
case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST:
{
// Respond with the server MTU
uint16_t conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
ASSERT_STATUS_RET_VOID(sd_ble_gatts_exchange_mtu_reply(conn_handle, NRF_SDH_BLE_GATT_MAX_MTU_SIZE));
break;
}
#endif
case BLE_GAP_EVT_PASSKEY_DISPLAY:
securityManager.processPasskeyDisplayEvent(p_ble_evt->evt.gap_evt.conn_handle, p_ble_evt->evt.gap_evt.params.passkey_display.passkey);
break;
case BLE_GAP_EVT_TIMEOUT:
gap.processTimeoutEvent(static_cast<Gap::TimeoutSource_t>(p_ble_evt->evt.gap_evt.params.timeout.src));
break;
case BLE_GATTC_EVT_TIMEOUT:
case BLE_GATTS_EVT_TIMEOUT:
// Disconnect on GATT Server and Client timeout events.
// ASSERT_STATUS_RET_VOID (sd_ble_gap_disconnect(m_conn_handle,
// BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION));
break;
case BLE_GAP_EVT_ADV_REPORT: {
const ble_gap_evt_adv_report_t *advReport = &p_ble_evt->evt.gap_evt.params.adv_report;
gap.processAdvertisementReport(advReport->peer_addr.addr,
advReport->rssi,
advReport->scan_rsp,
static_cast<GapAdvertisingParams::AdvertisingType_t>(advReport->type),
advReport->dlen,
advReport->data);
break;
}
default:
break;
}
gattServer.hwCallback(p_ble_evt);
}
/*! @brief Callback when an error occurs inside the SoftDevice */
void assert_nrf_callback(uint16_t line_num, const uint8_t *p_file_name)
{
ASSERT_TRUE(false, (void) 0);
}
#if NRF_SD_BLE_API_VERSION >= 5
/*!
@brief Handler for general errors above the SoftDevice layer.
Typically we can' recover from this so we do a reset.
This implementation will override the default weak symbol generated by the Nordic SDK
*/
void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
{
ASSERT_STATUS_RET_VOID( id );
NVIC_SystemReset();
}
#else
/*!
@brief Handler for general errors above the SoftDevice layer.
Typically we can' recover from this so we do a reset.
*/
void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t *p_file_name)
{
ASSERT_STATUS_RET_VOID( error_code );
NVIC_SystemReset();
}
#endif
#if NRF_SDK14PLUS_EVENT_HANDLERS
/*!
@brief Handler of Softdevice events.
This signals that the softdevie has events that need to be processed.
*/
extern "C" void SD_EVT_IRQHandler(void)
{
ASSERT_STATUS_RET_VOID(signalEvent());
}
#endif

View File

@ -0,0 +1,75 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 _BTLE_H_
#define _BTLE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "common/common.h"
#include "ble_srv_common.h"
#include "headers/nrf_ble.h"
#if NRF_SD_BLE_API_VERSION >= 5
#include "sdk_config.h"
#endif
#define NRF_CONNECTION_TAG 1 /**<Connection tag to use with softdevice */
#define CENTRAL_LINK_COUNT NRF_SDH_BLE_CENTRAL_LINK_COUNT /**<number of central links used by the application. When changing this number remember to adjust the RAM settings */
/** If value for YOTTA_CFG_NORDIC_BLE_PERIPHERAL_LINKS was used, ram settings are adjusted by the yotta target module. */
#define PERIPHERAL_LINK_COUNT NRF_SDH_BLE_PERIPHERAL_LINK_COUNT /**<number of peripheral links used by the application. When changing this number remember to adjust the RAM settings*/
/** If value for YOTTA_CFG_NORDIC_BLE_CENTRAL_LINKS was used, ram settings are adjusted by the yotta target module. */
#define TOTAL_LINK_COUNT NRF_SDH_BLE_TOTAL_LINK_COUNT /**<number of total links used by the application. When changing this number remember to adjust the RAM settings */
#define GATTS_ATTR_TAB_SIZE NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE /**< GATTS attribite table size. */
/** If value for YOTTA_CFG_NORDIC_BLE_GATTS_ATTR_TAB_SIZE was used, ram settings are adjusted by the yotta target module. */
/**
* Using this call, the application can select whether to include the
* Service Changed characteristic in the GATT Server. The default in all
* previous releases has been to include the Service Changed characteristic,
* but this affects how GATT clients behave. Specifically, it requires
* clients to subscribe to this attribute and not to cache attribute handles
* between connections unless the devices are bonded. If the application
* does not need to change the structure of the GATT server attributes at
* runtime this adds unnecessary complexity to the interaction with peer
* clients. If the SoftDevice is enabled with the Service Changed
* Characteristics turned off, then clients are allowed to cache attribute
* handles making applications simpler on both sides.
*/
#define IS_SRVC_CHANGED_CHARACT_PRESENT NRF_SDH_BLE_SERVICE_CHANGED
#define UUID_TABLE_MAX_ENTRIES NRF_SDH_BLE_VS_UUID_COUNT /* This is the maximum number of 128-bit UUIDs with distinct bases that
* we expect to be in use; increase this limit if needed. */
#define NRF_SDK14PLUS_EVENT_HANDLERS (NRF_SD_BLE_API_VERSION >= 5) // Softdevice event dispatching has changed in SDK14
error_t btle_init(void);
// flag indicating if events have been signaled or not
// It is used by processEvents and signalEventsToProcess
// signalEventsToProcess raise the flag and processEvents
// clears it.
extern volatile bool isEventsSignaled;
#ifdef __cplusplus
}
#endif
#endif // ifndef _BTLE_H_

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic Semiconductor ASA
* integrated circuit in a product or a software update for such product, must reproduce
* the above copyright notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific prior
* written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary or object form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _BTLE_CLOCK_H_
#define _BTLE_CLOCK_H_
#include "nrf5x_lf_clk_helper.h"
/**
* @brief Module that generates settings for the low-frequency (LF) clock configuration.
*
* This module provides macros that are generated from the mbed config system macros.
*
*
*
* As a result, this module provides the following: @n
* - literal value LFCLK_CONF_SOURCE @n
* - literal value LFCLK_CONF_ACCURACY @n
* - literal value LFCLK_CONF_RC_CTIV @n
* - literal value LFCLK_CONF_RC_TEMP_CTIV
*/
#include "nrf_sdm.h"
#define DEFAULT_LFCLK_CONF_ACCURACY NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM
#ifdef NRF52
#define MAX_LFCLK_CONF_RC_CTIV 32
#else
#define MAX_LFCLK_CONF_RC_CTIV 64
#endif
#define MAX_LFCLK_CONF_RC_TEMP_CTIV 33
#define DEFAULT_LFCLK_CONF_RC_CTIV 16 // Check temperature every 16 * 250ms.
#define DEFAULT_LFCLK_CONF_RC_TEMP_CTIV 1 // Only calibrate if temperature has changed.
#define NRF_LF_SRC_XTAL 2
#define NRF_LF_SRC_SYNTH 3
#define NRF_LF_SRC_RC 4
#if MBED_CONF_NORDIC_NRF_LF_CLOCK_SRC == NRF_LF_SRC_RC
#define LFCLK_CONF_SOURCE NRF_CLOCK_LF_SRC_RC
#ifdef MBED_CONF_NORDIC_NRF_LF_CLOCK_CALIB_TIMER_INTERVAL
#define LFCLK_CONF_RC_CTIV MBED_CONF_NORDIC_NRF_LF_CLOCK_CALIB_TIMER_INTERVAL
#else
#define LFCLK_CONF_RC_CTIV DEFAULT_LFCLK_CONF_RC_CTIV
#endif
#ifdef MBED_CONF_NORDIC_NRF_LF_CLOCK_CALIB_MODE_CONFIG
#define LFCLK_CONF_RC_TEMP_CTIV MBED_CONF_NORDIC_NRF_LF_CLOCK_CALIB_MODE_CONFIG
#else
#define LFCLK_CONF_RC_TEMP_CTIV DEFAULT_LFCLK_CONF_RC_TEMP_CTIV
#endif
#if (LFCLK_CONF_RC_CTIV < 1) || (LFCLK_CONF_RC_CTIV > MAX_LFCLK_CONF_RC_CTIV)
#error Calibration timer interval out of range!
#endif
#if (LFCLK_CONF_RC_TEMP_CTIV < 0 ) || (LFCLK_CONF_RC_TEMP_CTIV > 33)
#error Number/mode of LF RC calibration intervals out of range!
#endif
#elif MBED_CONF_NORDIC_NRF_LF_CLOCK_SRC == NRF_LF_SRC_SYNTH
#define LFCLK_CONF_SOURCE NRF_CLOCK_LF_SRC_SYNTH
#define LFCLK_CONF_RC_CTIV 0 // Must be 0 if source is not NRF_CLOCK_LF_SRC_RC.
#define LFCLK_CONF_RC_TEMP_CTIV 0 // Must be 0 if source is not NRF_CLOCK_LF_SRC_RC.
#ifdef MBED_CONF_NORDIC_LF_CLOCK_HF_SYNTH_ACCURACY
#define LFCLK_CONF_ACCURACY MBED_CONF_NORDIC_LF_CLOCK_HF_SYNTH_ACCURACY
#endif
#else // default is NRF_LF_SRC_SYNTH
#define LFCLK_CONF_SOURCE NRF_CLOCK_LF_SRC_XTAL
#define LFCLK_CONF_RC_CTIV 0 // Must be 0 if source is not NRF_CLOCK_LF_SRC_RC.
#define LFCLK_CONF_RC_TEMP_CTIV 0 // Must be 0 if source is not NRF_CLOCK_LF_SRC_RC.
#ifdef MBED_CONF_NORDIC_LF_CLOCK_XTAL_ACCURACY
#define LFCLK_CONF_ACCURACY MBED_CONF_NORDIC_LF_CLOCK_XTAL_ACCURACY
#endif
#endif
#ifndef LFCLK_CONF_ACCURACY
#define LFCLK_CONF_ACCURACY DEFAULT_LFCLK_CONF_ACCURACY
#endif
#if (LFCLK_CONF_ACCURACY > NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM) || (LFCLK_CONF_ACCURACY < NRF_CLOCK_LF_XTAL_ACCURACY_250_PPM)
#error Low frequency clock accuracy out of range!
#endif
#endif //_BTLE_CLOCK_H_

View File

@ -0,0 +1,99 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 "common/common.h"
#include "headers/ble_gap.h"
#include "ble_conn_params.h"
static inline uint32_t msec_to_1_25msec(uint32_t interval_ms) ATTR_ALWAYS_INLINE ATTR_CONST;
#if SDK_CONN_PARAMS_MODULE_ENABLE
static void error_callback(uint32_t nrf_error);
#endif // SDK_CONN_PARAMS_MODULE_ENABLE
/**************************************************************************/
/*!
@brief Initialise GAP in the underlying SoftDevice
@returns
*/
/**************************************************************************/
error_t btle_gap_init(void)
{
ble_gap_conn_params_t gap_conn_params = {0};
gap_conn_params.min_conn_interval = msec_to_1_25msec(CFG_GAP_CONNECTION_MIN_INTERVAL_MS); // in 1.25ms units
gap_conn_params.max_conn_interval = msec_to_1_25msec(CFG_GAP_CONNECTION_MAX_INTERVAL_MS); // in 1.25ms unit
gap_conn_params.slave_latency = CFG_GAP_CONNECTION_SLAVE_LATENCY;
gap_conn_params.conn_sup_timeout = CFG_GAP_CONNECTION_SUPERVISION_TIMEOUT_MS / 10; // in 10ms unit
ble_gap_conn_sec_mode_t sec_mode;
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); // no security is needed
ASSERT_STATUS( sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *) CFG_GAP_LOCAL_NAME, strlen(CFG_GAP_LOCAL_NAME)));
ASSERT_STATUS( sd_ble_gap_appearance_set(CFG_GAP_APPEARANCE));
ASSERT_STATUS( sd_ble_gap_ppcp_set(&gap_conn_params));
ASSERT_STATUS( sd_ble_gap_tx_power_set(CFG_BLE_TX_POWER_LEVEL));
/**
* Call to conn_params_init() is not necessary; and so is disabled by default.
* This API should be exposed to the user to be invoked when necessary.
*/
#if SDK_CONN_PARAMS_MODULE_ENABLE
/* Connection Parameters */
enum {
FIRST_UPDATE_DELAY = APP_TIMER_TICKS(5000, CFG_TIMER_PRESCALER),
NEXT_UPDATE_DELAY = APP_TIMER_TICKS(5000, CFG_TIMER_PRESCALER),
MAX_UPDATE_COUNT = 3
};
ble_conn_params_init_t cp_init = {0};
cp_init.p_conn_params = NULL;
cp_init.first_conn_params_update_delay = FIRST_UPDATE_DELAY;
cp_init.next_conn_params_update_delay = NEXT_UPDATE_DELAY;
cp_init.max_conn_params_update_count = MAX_UPDATE_COUNT;
cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
cp_init.disconnect_on_fail = true;
cp_init.evt_handler = NULL;
cp_init.error_handler = error_callback;
ASSERT_STATUS ( ble_conn_params_init(&cp_init));
#endif // SDK_CONN_PARAMS_MODULE_ENABLE
return ERROR_NONE;
}
/**************************************************************************/
/*!
@brief Converts msecs to an integer representing 1.25ms units
@param[in] ms
The number of milliseconds to conver to 1.25ms units
@returns The number of 1.25ms units in the supplied number of ms
*/
/**************************************************************************/
static inline uint32_t msec_to_1_25msec(uint32_t interval_ms)
{
return (interval_ms * 4) / 5;
}
#if SDK_CONN_PARAMS_MODULE_ENABLE
static void error_callback(uint32_t nrf_error)
{
ASSERT_STATUS_RET_VOID( nrf_error );
}
#endif // SDK_CONN_PARAMS_MODULE_ENABLE

View File

@ -0,0 +1,24 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 _BTLE_GAP_H_
#define _BTLE_GAP_H_
#include "common/common.h"
error_t btle_gap_init(void);
#endif // ifndef _BTLE_GAP_H_

View File

@ -0,0 +1,326 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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(S110)
#include "btle.h"
#include "nRF5xn.h"
extern "C" {
#include "pstorage.h"
#include "device_manager.h"
#include "id_manager.h"
}
#include "btle_security.h"
static dm_application_instance_t applicationInstance;
static bool initialized = false;
static ret_code_t dm_handler(dm_handle_t const *p_handle, dm_event_t const *p_event, ret_code_t event_result);
// default security parameters. Avoid "holes" between member assigments in order to compile by gcc++11.
static ble_gap_sec_params_t securityParameters = {
.bond = true, /**< Perform bonding. */
.mitm = true, /**< Man In The Middle protection required. */
.lesc = false, /**< Enable LE Secure Connection pairing. */
.keypress = false, /**< Enable generation of keypress notifications. */
.io_caps = SecurityManager::IO_CAPS_NONE, /**< IO capabilities, see @ref BLE_GAP_IO_CAPS. */
.oob = 0, /**< Out Of Band data available. */
.min_key_size = 16, /**< Minimum encryption key size in octets between 7 and 16. If 0 then not applicable in this instance. */
.max_key_size = 16, /**< Maximum encryption key size in octets between min_key_size and 16. */
.kdist_own = {
.enc = 0, /**< Long Term Key and Master Identification. */
.id = 0, /**< Identity Resolving Key and Identity Address Information. */
.sign = 0, /**< Connection Signature Resolving Key. */
.link = 0 /**< Derive the Link Key from the LTK. */
}, /**< Key distribution bitmap: keys that the local device will distribute. */
.kdist_peer = {
.enc = 1, /**< Long Term Key and Master Identification. */
.id = 1, /**< Identity Resolving Key and Identity Address Information. */
.sign = 1, /**< Connection Signature Resolving Key. */
.link = 0 /**< Derive the Link Key from the LTK. */
} /**< Key distribution bitmap: keys that the peripheral device will distribute. */
};
bool
btle_hasInitializedSecurity(void)
{
return initialized;
}
ble_error_t
btle_initializeSecurity(bool enableBonding,
bool requireMITM,
SecurityManager::SecurityIOCapabilities_t iocaps,
const SecurityManager::Passkey_t passkey)
{
/* guard against multiple initializations */
if (initialized) {
return BLE_ERROR_NONE;
}
if (pstorage_init() != NRF_SUCCESS) {
return BLE_ERROR_UNSPECIFIED;
}
ret_code_t rc;
if (passkey) {
ble_opt_t opts;
opts.gap_opt.passkey.p_passkey = const_cast<uint8_t *>(passkey);
if ((rc = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &opts)) != NRF_SUCCESS) {
switch (rc) {
case BLE_ERROR_INVALID_CONN_HANDLE:
case NRF_ERROR_INVALID_ADDR:
case NRF_ERROR_INVALID_PARAM:
default:
return BLE_ERROR_INVALID_PARAM;
case NRF_ERROR_INVALID_STATE:
return BLE_ERROR_INVALID_STATE;
case NRF_ERROR_BUSY:
return BLE_STACK_BUSY;
}
}
}
dm_init_param_t dm_init_param = {
.clear_persistent_data = false /* Set to true in case the module should clear all persistent data. */
};
if (dm_init(&dm_init_param) != NRF_SUCCESS) {
return BLE_ERROR_UNSPECIFIED;
}
// update default security parameters with function call parameters
securityParameters.bond = enableBonding;
securityParameters.mitm = requireMITM;
securityParameters.io_caps = iocaps;
const dm_application_param_t dm_param = {
.evt_handler = dm_handler,
.service_type = DM_PROTOCOL_CNTXT_GATT_CLI_ID,
.sec_param = securityParameters
};
if ((rc = dm_register(&applicationInstance, &dm_param)) != NRF_SUCCESS) {
switch (rc) {
case NRF_ERROR_INVALID_STATE:
return BLE_ERROR_INVALID_STATE;
case NRF_ERROR_NO_MEM:
return BLE_ERROR_NO_MEM;
default:
return BLE_ERROR_UNSPECIFIED;
}
}
initialized = true;
return BLE_ERROR_NONE;
}
ble_error_t
btle_purgeAllBondingState(void)
{
ret_code_t rc;
if ((rc = dm_device_delete_all(&applicationInstance)) == NRF_SUCCESS) {
return BLE_ERROR_NONE;
}
switch (rc) {
case NRF_ERROR_INVALID_STATE:
return BLE_ERROR_INVALID_STATE;
case NRF_ERROR_NO_MEM:
return BLE_ERROR_NO_MEM;
default:
return BLE_ERROR_UNSPECIFIED;
}
}
ble_error_t
btle_getLinkSecurity(Gap::Handle_t connectionHandle, SecurityManager::LinkSecurityStatus_t *securityStatusP)
{
ret_code_t rc;
dm_handle_t dmHandle = {
.appl_id = applicationInstance,
};
if ((rc = dm_handle_get(connectionHandle, &dmHandle)) != NRF_SUCCESS) {
if (rc == NRF_ERROR_NOT_FOUND) {
return BLE_ERROR_INVALID_PARAM;
} else {
return BLE_ERROR_UNSPECIFIED;
}
}
if ((rc = dm_security_status_req(&dmHandle, reinterpret_cast<dm_security_status_t *>(securityStatusP))) != NRF_SUCCESS) {
switch (rc) {
case NRF_ERROR_INVALID_STATE:
return BLE_ERROR_INVALID_STATE;
case NRF_ERROR_NO_MEM:
return BLE_ERROR_NO_MEM;
default:
return BLE_ERROR_UNSPECIFIED;
}
}
return BLE_ERROR_NONE;
}
ble_error_t
btle_setLinkSecurity(Gap::Handle_t connectionHandle, SecurityManager::SecurityMode_t securityMode)
{
// use default and updated parameters as starting point
// and modify structure based on security mode.
ble_gap_sec_params_t params = securityParameters;
switch (securityMode) {
case SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK:
/**< Require no protection, open link. */
securityParameters.bond = false;
securityParameters.mitm = false;
break;
case SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM:
/**< Require encryption, but no MITM protection. */
securityParameters.bond = true;
securityParameters.mitm = false;
break;
// not yet implemented security modes
case SecurityManager::SECURITY_MODE_NO_ACCESS:
case SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM:
/**< Require encryption and MITM protection. */
case SecurityManager::SECURITY_MODE_SIGNED_NO_MITM:
/**< Require signing or encryption, but no MITM protection. */
case SecurityManager::SECURITY_MODE_SIGNED_WITH_MITM:
/**< Require signing or encryption, and MITM protection. */
default:
return BLE_ERROR_NOT_IMPLEMENTED;
}
// update security settings for given connection
uint32_t result = sd_ble_gap_authenticate(connectionHandle, &params);
if (result == NRF_SUCCESS) {
return BLE_ERROR_NONE;
} else {
return BLE_ERROR_UNSPECIFIED;
}
}
ret_code_t
dm_handler(dm_handle_t const *p_handle, dm_event_t const *p_event, ret_code_t event_result)
{
nRF5xn &ble = nRF5xn::Instance(BLE::DEFAULT_INSTANCE);
nRF5xSecurityManager &securityManager = (nRF5xSecurityManager &) ble.getSecurityManager();
switch (p_event->event_id) {
case DM_EVT_SECURITY_SETUP: /* started */ {
const ble_gap_sec_params_t *peerParams = &p_event->event_param.p_gap_param->params.sec_params_request.peer_params;
securityManager.processSecuritySetupInitiatedEvent(p_event->event_param.p_gap_param->conn_handle,
peerParams->bond,
peerParams->mitm,
(SecurityManager::SecurityIOCapabilities_t)peerParams->io_caps);
break;
}
case DM_EVT_SECURITY_SETUP_COMPLETE:
securityManager.
processSecuritySetupCompletedEvent(p_event->event_param.p_gap_param->conn_handle,
(SecurityManager::SecurityCompletionStatus_t)(p_event->event_param.p_gap_param->params.auth_status.auth_status));
break;
case DM_EVT_LINK_SECURED: {
unsigned securityMode = p_event->event_param.p_gap_param->params.conn_sec_update.conn_sec.sec_mode.sm;
unsigned level = p_event->event_param.p_gap_param->params.conn_sec_update.conn_sec.sec_mode.lv;
SecurityManager::SecurityMode_t resolvedSecurityMode = SecurityManager::SECURITY_MODE_NO_ACCESS;
switch (securityMode) {
case 1:
switch (level) {
case 1:
resolvedSecurityMode = SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK;
break;
case 2:
resolvedSecurityMode = SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM;
break;
case 3:
resolvedSecurityMode = SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM;
break;
}
break;
case 2:
switch (level) {
case 1:
resolvedSecurityMode = SecurityManager::SECURITY_MODE_SIGNED_NO_MITM;
break;
case 2:
resolvedSecurityMode = SecurityManager::SECURITY_MODE_SIGNED_WITH_MITM;
break;
}
break;
}
securityManager.processLinkSecuredEvent(p_event->event_param.p_gap_param->conn_handle, resolvedSecurityMode);
break;
}
case DM_EVT_DEVICE_CONTEXT_STORED:
securityManager.processSecurityContextStoredEvent(p_event->event_param.p_gap_param->conn_handle);
break;
default:
break;
}
return NRF_SUCCESS;
}
ble_error_t
btle_createWhitelistFromBondTable(ble_gap_whitelist_t *p_whitelist)
{
if (!btle_hasInitializedSecurity()) {
return BLE_ERROR_INITIALIZATION_INCOMPLETE;
}
ret_code_t err = dm_whitelist_create(&applicationInstance, p_whitelist);
if (err == NRF_SUCCESS) {
return BLE_ERROR_NONE;
} else if (err == NRF_ERROR_NULL) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
} else {
return BLE_ERROR_INVALID_STATE;
}
}
bool
btle_matchAddressAndIrk(ble_gap_addr_t const * p_addr, ble_gap_irk_t const * p_irk)
{
/*
* Use a helper function from the Nordic SDK to test whether the BLE
* address can be generated using the IRK.
*/
return im_address_resolve(p_addr, p_irk);
}
void
btle_generateResolvableAddress(const ble_gap_irk_t &irk, ble_gap_addr_t &address)
{
/* Set type to resolvable */
address.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE;
/*
* Assign a random number to the most significant 3 bytes
* of the address.
*/
address.addr[BLE_GAP_ADDR_LEN - 3] = 0x8E;
address.addr[BLE_GAP_ADDR_LEN - 2] = 0x4F;
address.addr[BLE_GAP_ADDR_LEN - 1] = 0x7C;
/* Calculate the hash and store it in the top half of the address */
ah(irk.irk, &address.addr[BLE_GAP_ADDR_LEN - 3], address.addr);
}
#endif

View File

@ -0,0 +1,146 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 _BTLE_SECURITY_H_
#define _BTLE_SECURITY_H_
#include "ble/Gap.h"
#include "ble/SecurityManager.h"
/**
* Function to test whether the SecurityManager has been initialized.
* Possible by a call to @ref btle_initializeSecurity().
*
* @return True if the SecurityManager was previously initialized, false
* otherwise.
*/
bool btle_hasInitializedSecurity(void);
/**
* Enable Nordic's Device Manager, which brings in functionality from the
* stack's Security Manager. The Security Manager implements the actual
* cryptographic algorithms and protocol exchanges that allow two devices to
* securely exchange data and privately detect each other.
*
* @param[in] enableBonding Allow for bonding.
* @param[in] requireMITM Require protection for man-in-the-middle attacks.
* @param[in] iocaps To specify IO capabilities of this peripheral,
* such as availability of a display or keyboard to
* support out-of-band exchanges of security data.
* @param[in] passkey To specify a static passkey.
*
* @return BLE_ERROR_NONE on success.
*/
ble_error_t btle_initializeSecurity(bool enableBonding = true,
bool requireMITM = true,
SecurityManager::SecurityIOCapabilities_t iocaps = SecurityManager::IO_CAPS_NONE,
const SecurityManager::Passkey_t passkey = NULL);
/**
* Get the security status of a link.
*
* @param[in] connectionHandle
* Handle to identify the connection.
* @param[out] securityStatusP
* security status.
*
* @return BLE_ERROR_NONE Or appropriate error code indicating reason for failure.
*/
ble_error_t btle_getLinkSecurity(Gap::Handle_t connectionHandle, SecurityManager::LinkSecurityStatus_t *securityStatusP);
/**
* Set the security mode on a connection. Useful for elevating the security mode
* once certain conditions are met, e.g., a particular service is found.
*
* @param[in] connectionHandle
* Handle to identify the connection.
* @param[in] securityMode
* security mode.
*
* @return BLE_ERROR_NONE Or appropriate error code indicating reason for failure.
*/
ble_error_t btle_setLinkSecurity(Gap::Handle_t connectionHandle, SecurityManager::SecurityMode_t securityMode);
/**
* Function for deleting all peer device context and all related bonding
* information from the database.
*
* @retval BLE_ERROR_NONE On success, else an error code indicating reason for failure.
* @retval BLE_ERROR_INVALID_STATE If the API is called without module initialization and/or
* application registration.
*/
ble_error_t btle_purgeAllBondingState(void);
#if (NRF_SD_BLE_API_VERSION <= 2)
/**
* Query the SoftDevice bond table to extract a whitelist containing the BLE
* addresses and IRKs of bonded devices.
*
* @param[in/out] p_whitelist
* (on input) p_whitelist->addr_count and
* p_whitelist->irk_count specify the maximum number of
* addresses and IRKs added to the whitelist structure.
* (on output) *p_whitelist is a whitelist containing the
* addresses and IRKs of the bonded devices.
*
* @return BLE_ERROR_NONE Or appropriate error code indicating reason for failure.
*/
ble_error_t btle_createWhitelistFromBondTable(ble_gap_whitelist_t *p_whitelist);
#endif
/**
* Function to test whether a BLE address is generated using an IRK.
*
* @param[in] p_addr
* Pointer to a BLE address.
* @param[in] p_irk
* Pointer to an IRK.
*
* @return True if p_addr can be generated using p_irk, false otherwise.
*/
bool btle_matchAddressAndIrk(ble_gap_addr_t const * p_addr, ble_gap_irk_t const * p_irk);
/**
* Function to generate a private resolvable BLE address.
*
* @param[out] p_addr
* The output address.
* @param[in] p_irk
* A reference to a IRK.
*
* @note This function does not generate a secure address since the prand number in the
* resolvable address is not truly random. Therefore, the output of this function
* is only meant to be used by the application internally but never exported.
*/
void btle_generateResolvableAddress(const ble_gap_irk_t &irk, ble_gap_addr_t &address);
#if (NRF_SD_BLE_API_VERSION >= 3)
/**
* @brief Returns a list of addresses from peers in the stacks bond table.
*
* @param[in/out] addresses
* (on input) @ref Gap::Whitelist_t structure where at
* most addresses.capacity addresses from bonded peers will
* be stored.
* (on output) A copy of the addresses from bonded peers.
*
* @retval BLE_ERROR_NONE if successful.
* @retval BLE_ERROR_UNSPECIFIED Bond data could not be found in flash or is inconsistent.
*/
ble_error_t btle_getAddressesFromBondTable(Gap::Whitelist_t &addrList);
#endif
#endif /* _BTLE_SECURITY_H_ */

View File

@ -0,0 +1,488 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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(S130) || defined(S132) || defined(S140)
#include "btle.h"
#include "nRF5xn.h"
extern "C" {
#include "peer_manager.h"
#include "id_manager.h"
#include "fds.h"
}
#include "btle_security.h"
extern "C" void ah(uint8_t const * p_k, uint8_t const * p_r, uint8_t * p_local_hash);
static bool initialized = false;
static void pm_handler(pm_evt_t const *p_event);
static bool _enc_in_progress = false; // helper flag for distinguish between state of link connected and link connected in progres of encryption establishing.
volatile static uint32_t async_ret_code; // busy loop support variable for asyncronous API.
// default security parameters. Avoid "holes" between member assigments in order to compile by gcc++11.
static ble_gap_sec_params_t securityParameters = {
.bond = true, /**< Perform bonding. */
.mitm = true, /**< Man In The Middle protection required. */
.lesc = false, /**< Enable LE Secure Connection pairing. */
.keypress = false, /**< Enable generation of keypress notifications. */
.io_caps = SecurityManager::IO_CAPS_NONE, /**< IO capabilities, see @ref BLE_GAP_IO_CAPS. */
.oob = 0, /**< Out Of Band data available. */
.min_key_size = 16, /**< Minimum encryption key size in octets between 7 and 16. If 0 then not applicable in this instance. */
.max_key_size = 16, /**< Maximum encryption key size in octets between min_key_size and 16. */
.kdist_own = {
.enc = 0, /**< Long Term Key and Master Identification. */
.id = 0, /**< Identity Resolving Key and Identity Address Information. */
.sign = 0, /**< Connection Signature Resolving Key. */
.link = 0 /**< Derive the Link Key from the LTK. */
}, /**< Key distribution bitmap: keys that the local device will distribute. */
.kdist_peer = {
.enc = 1, /**< Long Term Key and Master Identification. */
.id = 1, /**< Identity Resolving Key and Identity Address Information. */
.sign = 0, /**< Connection Signature Resolving Key. */
.link = 0 /**< Derive the Link Key from the LTK. */
} /**< Key distribution bitmap: keys that the peripheral device will distribute. */
};
bool
btle_hasInitializedSecurity(void)
{
return initialized;
}
ble_error_t
btle_initializeSecurity(bool enableBonding,
bool requireMITM,
SecurityManager::SecurityIOCapabilities_t iocaps,
const SecurityManager::Passkey_t passkey)
{
/* guard against multiple initializations */
if (initialized) {
return BLE_ERROR_NONE;
}
ret_code_t rc;
if (passkey) {
ble_opt_t opts;
opts.gap_opt.passkey.p_passkey = const_cast<uint8_t *>(passkey);
if ((rc = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &opts)) != NRF_SUCCESS) {
switch (rc) {
case BLE_ERROR_INVALID_CONN_HANDLE:
case NRF_ERROR_INVALID_ADDR:
case NRF_ERROR_INVALID_PARAM:
default:
return BLE_ERROR_INVALID_PARAM;
case NRF_ERROR_INVALID_STATE:
return BLE_ERROR_INVALID_STATE;
case NRF_ERROR_BUSY:
return BLE_STACK_BUSY;
}
}
}
// update default security parameters with function call parameters
securityParameters.bond = enableBonding;
securityParameters.mitm = requireMITM;
securityParameters.io_caps = iocaps;
if (enableBonding) {
securityParameters.kdist_own.enc = 1;
securityParameters.kdist_own.id = 1;
} else {
securityParameters.kdist_own.enc = 0;
securityParameters.kdist_own.id = 0;
}
rc = pm_sec_params_set(&securityParameters);
if (rc == NRF_SUCCESS) {
rc = pm_register(pm_handler);
}
switch (rc) {
case NRF_SUCCESS:
initialized = true;
return BLE_ERROR_NONE;
case NRF_ERROR_INVALID_STATE:
return BLE_ERROR_INVALID_STATE;
case NRF_ERROR_INVALID_PARAM:
return BLE_ERROR_INVALID_PARAM;
default:
return BLE_ERROR_UNSPECIFIED;
}
initialized = true;
return BLE_ERROR_NONE;
}
ble_error_t
btle_purgeAllBondingState(void)
{
ret_code_t rc;
async_ret_code = NRF_ERROR_BUSY;
rc = pm_peers_delete(); // it is asynhronous API
if (rc == NRF_SUCCESS)
{
// waiting for respond from pm_handler
while (async_ret_code == NRF_ERROR_BUSY) {
// busy-loop
}
rc = async_ret_code;
}
switch (rc) {
case NRF_SUCCESS:
return BLE_ERROR_NONE;
case NRF_ERROR_INVALID_STATE:
return BLE_ERROR_INVALID_STATE;
default:
return BLE_ERROR_UNSPECIFIED;
}
}
ble_error_t
btle_getLinkSecurity(Gap::Handle_t connectionHandle, SecurityManager::LinkSecurityStatus_t *securityStatusP)
{
ret_code_t rc;
pm_conn_sec_status_t conn_sec_status;
rc = pm_conn_sec_status_get(connectionHandle, &conn_sec_status);
if (rc == NRF_SUCCESS)
{
if (conn_sec_status.encrypted) {
*securityStatusP = SecurityManager::ENCRYPTED;
}
else if (conn_sec_status.connected) {
if (_enc_in_progress) {
*securityStatusP = SecurityManager::ENCRYPTION_IN_PROGRESS;
}
else {
*securityStatusP = SecurityManager::NOT_ENCRYPTED;
}
}
return BLE_ERROR_NONE;
}
switch (rc) {
case BLE_ERROR_INVALID_CONN_HANDLE:
return BLE_ERROR_INVALID_PARAM;
case NRF_ERROR_INVALID_STATE:
return BLE_ERROR_INVALID_STATE;
default:
return BLE_ERROR_UNSPECIFIED;
}
}
ble_error_t
btle_setLinkSecurity(Gap::Handle_t connectionHandle, SecurityManager::SecurityMode_t securityMode)
{
// use default and updated parameters as starting point
// and modify structure based on security mode.
ret_code_t rc;
switch (securityMode) {
case SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK:
/**< Require no protection, open link. */
securityParameters.bond = false;
securityParameters.mitm = false;
securityParameters.kdist_own.enc = 0;
break;
case SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM:
/**< Require encryption, but no MITM protection. */
securityParameters.bond = true;
securityParameters.mitm = false;
securityParameters.kdist_own.enc = 1;
break;
// not yet implemented security modes
case SecurityManager::SECURITY_MODE_NO_ACCESS:
case SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM:
/**< Require encryption and MITM protection. */
case SecurityManager::SECURITY_MODE_SIGNED_NO_MITM:
/**< Require signing or encryption, but no MITM protection. */
case SecurityManager::SECURITY_MODE_SIGNED_WITH_MITM:
/**< Require signing or encryption, and MITM protection. */
default:
return BLE_ERROR_NOT_IMPLEMENTED;
}
// update security settings for given connection
rc = pm_sec_params_set(&securityParameters);
if (rc == NRF_SUCCESS) {
rc = pm_conn_secure(connectionHandle, false);
}
switch (rc) {
case NRF_SUCCESS:
initialized = true;
return BLE_ERROR_NONE;
case NRF_ERROR_INVALID_STATE:
return BLE_ERROR_INVALID_STATE;
case NRF_ERROR_INVALID_PARAM:
return BLE_ERROR_INVALID_PARAM;
default:
return BLE_ERROR_UNSPECIFIED;
}
}
void pm_handler(pm_evt_t const *p_event)
{
nRF5xn &ble = nRF5xn::Instance(BLE::DEFAULT_INSTANCE);
nRF5xSecurityManager &securityManager = (nRF5xSecurityManager &) ble.getSecurityManager();
ret_code_t err_code;
SecurityManager::SecurityMode_t resolvedSecurityMode;
switch (p_event->evt_id) {
case PM_EVT_CONN_SEC_START: /* started */ {
const ble_gap_sec_params_t *peerParams = &securityParameters;
securityManager.processSecuritySetupInitiatedEvent(p_event->conn_handle,
peerParams->bond,
peerParams->mitm,
(SecurityManager::SecurityIOCapabilities_t)peerParams->io_caps);
_enc_in_progress = true;
break;
}
case PM_EVT_CONN_SEC_SUCCEEDED:
// Update the rank of the peer.
if (p_event->params.conn_sec_succeeded.procedure == PM_LINK_SECURED_PROCEDURE_BONDING)
{
err_code = pm_peer_rank_highest(p_event->peer_id);
}
securityManager.
processSecuritySetupCompletedEvent(p_event->conn_handle,
SecurityManager::SEC_STATUS_SUCCESS);// SEC_STATUS_SUCCESS of SecurityCompletionStatus_t
ble_gap_conn_sec_t conn_sec;
sd_ble_gap_conn_sec_get(p_event->conn_handle, &conn_sec);
resolvedSecurityMode = SecurityManager::SECURITY_MODE_NO_ACCESS;
switch (conn_sec.sec_mode.sm) {
case 1:
switch (conn_sec.sec_mode.lv) {
case 1:
resolvedSecurityMode = SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK;
break;
case 2:
resolvedSecurityMode = SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM;
break;
case 3:
resolvedSecurityMode = SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM;
break;
}
break;
case 2:
switch (conn_sec.sec_mode.lv) {
case 1:
resolvedSecurityMode = SecurityManager::SECURITY_MODE_SIGNED_NO_MITM;
break;
case 2:
resolvedSecurityMode = SecurityManager::SECURITY_MODE_SIGNED_WITH_MITM;
break;
}
break;
}
securityManager.processLinkSecuredEvent(p_event->conn_handle, resolvedSecurityMode);
_enc_in_progress = false;
break;
case PM_EVT_CONN_SEC_FAILED:
SecurityManager::SecurityCompletionStatus_t securityCompletionStatus;
if ((uint32_t)p_event->params.conn_sec_failed.error >= PM_CONN_SEC_ERROR_BASE ) {
securityCompletionStatus = SecurityManager::SEC_STATUS_UNSPECIFIED;
} else {
securityCompletionStatus =
(SecurityManager::SecurityCompletionStatus_t)p_event->params.conn_sec_failed.error;
}
securityManager.
processSecuritySetupCompletedEvent(p_event->conn_handle, securityCompletionStatus);
_enc_in_progress = false;
break;
case PM_EVT_BONDED_PEER_CONNECTED:
pm_peer_rank_highest(p_event->peer_id);
break;
case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
if (p_event->params.peer_data_update_succeeded.action == PM_PEER_DATA_OP_UPDATE)
{
securityManager.processSecurityContextStoredEvent(p_event->conn_handle);
}
break;
case PM_EVT_PEER_DATA_UPDATE_FAILED:
break;
case PM_EVT_PEERS_DELETE_SUCCEEDED:
async_ret_code = NRF_SUCCESS; // respond SUCCESS to the busy-loop in f. btle_purgeAllBondingState
break;
case PM_EVT_PEERS_DELETE_FAILED:
async_ret_code = NRF_ERROR_INTERNAL; // respond FAILURE to the busy-loop in f. btle_purgeAllBondingState
break;
case PM_EVT_STORAGE_FULL:
// Run garbage collection on the flash.
err_code = fds_gc();
if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES)
{
// Retry.
}
else
{
APP_ERROR_CHECK(err_code);
}
break;//PM_EVT_STORAGE_FULL
case PM_EVT_CONN_SEC_CONFIG_REQ:{
// A connected peer (central) is trying to pair, but the Peer Manager already has a bond
// for that peer. Setting allow_repairing to false rejects the pairing request.
// If this event is ignored (pm_conn_sec_config_reply is not called in the event
// handler), the Peer Manager assumes allow_repairing to be false.
pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
pm_conn_sec_config_reply(p_event->conn_handle, &conn_sec_config);
}
break;//PM_EVT_CONN_SEC_CONFIG_REQ
default:
break;
}
}
#if (NRF_SD_BLE_API_VERSION <= 2)
ble_error_t
btle_createWhitelistFromBondTable(ble_gap_whitelist_t *p_whitelist)
{
if (!btle_hasInitializedSecurity()) {
return BLE_ERROR_INITIALIZATION_INCOMPLETE;
}
ret_code_t err = pm_whitelist_create( NULL, BLE_GAP_WHITELIST_ADDR_MAX_COUNT, p_whitelist);
if (err == NRF_SUCCESS) {
return BLE_ERROR_NONE;
} else if (err == NRF_ERROR_NULL) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
} else {
return BLE_ERROR_INVALID_STATE;
}
}
#endif
bool
btle_matchAddressAndIrk(ble_gap_addr_t const * p_addr, ble_gap_irk_t const * p_irk)
{
/*
* Use a helper function from the Nordic SDK to test whether the BLE
* address can be generated using the IRK.
*/
return im_address_resolve(p_addr, p_irk);
}
void
btle_generateResolvableAddress(const ble_gap_irk_t &irk, ble_gap_addr_t &address)
{
/* Set type to resolvable */
address.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE;
/*
* Assign a random number to the most significant 3 bytes
* of the address.
*/
address.addr[BLE_GAP_ADDR_LEN - 3] = 0x8E;
address.addr[BLE_GAP_ADDR_LEN - 2] = 0x4F;
address.addr[BLE_GAP_ADDR_LEN - 1] = 0x7C;
/* Calculate the hash and store it in the top half of the address */
ah(irk.irk, &address.addr[BLE_GAP_ADDR_LEN - 3], address.addr);
}
#if (NRF_SD_BLE_API_VERSION >= 3)
ble_error_t btle_getAddressesFromBondTable(Gap::Whitelist_t &addrList)
{
pm_peer_id_t peer_id;
ret_code_t ret;
pm_peer_data_bonding_t bond_data;
addrList.size = 0;
peer_id = pm_next_peer_id_get(PM_PEER_ID_INVALID);
/**
* Fill addresses list:
* Copy addresses from bond table, or
* for every private resolvable address in the bond table generate the resolvable address.
*/
while ((peer_id != PM_PEER_ID_INVALID) && (addrList.capacity > addrList.size)) {
memset(&bond_data, 0x00, sizeof(bond_data));
// Read peer data from flash.
ret = pm_peer_data_bonding_load(peer_id, &bond_data);
if ((ret == NRF_ERROR_NOT_FOUND) || (ret == NRF_ERROR_INVALID_PARAM)) {
// Peer data could not be found in flash or peer ID is not valid.
return BLE_ERROR_UNSPECIFIED;
}
if (bond_data.peer_ble_id.id_addr_info.addr_type == BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE) {
btle_generateResolvableAddress(bond_data.peer_ble_id.id_info,
(ble_gap_addr_t &) addrList.addresses[addrList.size].address);
} else {
memcpy(&addrList.addresses[addrList.size].address,
&bond_data.peer_ble_id.id_addr_info.addr,
sizeof(addrList.addresses[0].address));
}
addrList.addresses[addrList.size].type = static_cast<BLEProtocol::AddressType_t> (bond_data.peer_ble_id.id_addr_info.addr_type);
addrList.size++;
// get next peer id
peer_id = pm_next_peer_id_get(peer_id);
}
return BLE_ERROR_NONE;
}
#endif
#endif // defined(S130) || defined(S132) || defined(S140)

View File

@ -0,0 +1,372 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 "custom_helper.h"
#include "../btle.h"
/*
* The current version of the soft-device doesn't handle duplicate 128-bit UUIDs
* very well. It is therefore necessary to filter away duplicates before
* passing long UUIDs to sd_ble_uuid_vs_add(). The following types and data
* structures involved in maintaining a local cache of 128-bit UUIDs.
*/
typedef struct {
UUID::LongUUIDBytes_t uuid;
uint8_t type;
} converted_uuid_table_entry_t;
static unsigned uuidTableEntries = 0; /* current usage of the table */
converted_uuid_table_entry_t convertedUUIDTable[UUID_TABLE_MAX_ENTRIES];
void custom_reset_128bits_uuid_table() {
uuidTableEntries = 0;
}
/**
* lookup the cache of previously converted 128-bit UUIDs to find a type value.
* @param uuid base 128-bit UUID
* @param recoveredType the type field of the 3-byte nRF's uuid.
* @return true if a match is found.
*/
static bool
lookupConvertedUUIDTable(const UUID::LongUUIDBytes_t uuid, uint8_t *recoveredType)
{
unsigned i;
for (i = 0; i < uuidTableEntries; i++) {
unsigned byteIndex;
for (byteIndex = 0; byteIndex < UUID::LENGTH_OF_LONG_UUID; byteIndex++) {
/* Skip bytes 2 and 3, because they contain the shortUUID (16-bit) version of the
* long UUID; and we're comparing against the remainder. */
if ((byteIndex == 2) || (byteIndex == 3)) {
continue;
}
if (convertedUUIDTable[i].uuid[byteIndex] != uuid[byteIndex]) {
break;
}
}
if (byteIndex == UUID::LENGTH_OF_LONG_UUID) {
*recoveredType = convertedUUIDTable[i].type;
return true;
}
}
return false;
}
static void
addToConvertedUUIDTable(const UUID::LongUUIDBytes_t uuid, uint8_t type)
{
if (uuidTableEntries == UUID_TABLE_MAX_ENTRIES) {
return; /* recovery needed; or at least the user should be warned about this fact.*/
}
memcpy(convertedUUIDTable[uuidTableEntries].uuid, uuid, UUID::LENGTH_OF_LONG_UUID);
convertedUUIDTable[uuidTableEntries].uuid[2] = 0;
convertedUUIDTable[uuidTableEntries].uuid[3] = 0;
convertedUUIDTable[uuidTableEntries].type = type;
uuidTableEntries++;
}
/**
* The nRF transport has its own 3-byte representation of a UUID. If the user-
* specified UUID is 128-bits wide, then the UUID base needs to be added to the
* soft-device and converted to a 3-byte handle before being used further. This
* function is responsible for this translation of user-specified UUIDs into
* nRF's representation.
*
* @param[in] uuid
* user-specified UUID
* @return nRF
* 3-byte UUID (containing a type and 16-bit UUID) representation
* to be used with SVC calls.
*/
ble_uuid_t custom_convert_to_nordic_uuid(const UUID &uuid)
{
ble_uuid_t nordicUUID;
nordicUUID.uuid = uuid.getShortUUID();
nordicUUID.type = BLE_UUID_TYPE_UNKNOWN; /* to be set below */
if (uuid.shortOrLong() == UUID::UUID_TYPE_SHORT) {
nordicUUID.type = BLE_UUID_TYPE_BLE;
} else {
if (!lookupConvertedUUIDTable(uuid.getBaseUUID(), &nordicUUID.type)) {
nordicUUID.type = custom_add_uuid_base(uuid.getBaseUUID());
addToConvertedUUIDTable(uuid.getBaseUUID(), nordicUUID.type);
}
}
return nordicUUID;
}
/**************************************************************************/
/*!
@brief Adds the base UUID to the custom service. All UUIDs used
by this service are based on this 128-bit UUID.
@note This UUID needs to be added to the SoftDevice stack before
adding the service's primary service via
'sd_ble_gatts_service_add'
@param[in] p_uuid_base A pointer to the 128-bit UUID array (8*16)
@returns The UUID type.
A return value of 0 should be considered an error.
@retval 0x00 BLE_UUID_TYPE_UNKNOWN
@retval 0x01 BLE_UUID_TYPE_BLE
@retval 0x02 BLE_UUID_TYPE_VENDOR_BEGIN
@section EXAMPLE
@code
// Take note that bytes 2/3 are blank since these are used to identify
// the primary service and individual characteristics
#define CFG_CUSTOM_UUID_BASE "\x6E\x40\x00\x00\xB5\xA3\xF3\x93\xE0\xA9\xE5\x0E\x24\xDC\xCA\x9E"
uint8_t uuid_type = custom_add_uuid_base(CFG_CUSTOM_UUID_BASE);
ASSERT(uuid_type > 0, ERROR_NOT_FOUND);
// We can now safely add the primary service and any characteristics
// for our custom service ...
@endcode
*/
/**************************************************************************/
uint8_t custom_add_uuid_base(uint8_t const *const p_uuid_base)
{
ble_uuid128_t base_uuid;
uint8_t uuid_type = 0;
for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) {
base_uuid.uuid128[i] = p_uuid_base[i];
}
ASSERT_INT( ERROR_NONE, sd_ble_uuid_vs_add( &base_uuid, &uuid_type ), 0);
return uuid_type;
}
/**************************************************************************/
/*!
*/
/**************************************************************************/
error_t custom_decode_uuid_base(uint8_t const *const p_uuid_base,
ble_uuid_t *p_uuid)
{
UUID::LongUUIDBytes_t uuid_base_le;
for (uint8_t i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) {
uuid_base_le[i] = p_uuid_base[i];
}
ASSERT_STATUS( sd_ble_uuid_decode(UUID::LENGTH_OF_LONG_UUID, uuid_base_le, p_uuid));
return ERROR_NONE;
}
/**************************************************************************/
/*!
@brief Adds a new characteristic to the custom service, assigning
properties, a UUID add-on value, etc.
@param[in] service_handle
@param[in] p_uuid The 16-bit value to add to the base UUID
for this characteristic (normally >1
since 1 is typically used by the primary
service).
@param[in] char_props The characteristic properties, as
defined by ble_gatt_char_props_t
@param[in] max_length The maximum length of this characeristic
@param[in] has_variable_len Whether the characteristic data has
variable length.
@param[out] p_char_handle
@returns
@retval ERROR_NONE Everything executed normally
*/
/**************************************************************************/
error_t custom_add_in_characteristic(uint16_t service_handle,
ble_uuid_t *p_uuid,
uint8_t properties,
SecurityManager::SecurityMode_t requiredSecurity,
uint8_t *p_data,
uint16_t length,
uint16_t max_length,
bool has_variable_len,
const uint8_t *userDescriptionDescriptorValuePtr,
uint16_t userDescriptionDescriptorValueLen,
const uint8_t *presentationFormatDescriptorValuePtr,
uint16_t presentationFormatDescriptorValueLen,
bool readAuthorization,
bool writeAuthorization,
ble_gatts_char_handles_t *p_char_handle)
{
/* Characteristic metadata */
ble_gatts_attr_md_t cccd_md;
ble_gatt_char_props_t char_props;
memcpy(&char_props, &properties, 1);
if (char_props.notify || char_props.indicate) {
/* Notification requires cccd */
memclr_( &cccd_md, sizeof(ble_gatts_attr_md_t));
cccd_md.vloc = BLE_GATTS_VLOC_STACK;
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
}
ble_gatts_char_md_t char_md = {0};
char_md.char_props = char_props;
char_md.p_cccd_md =
(char_props.notify || char_props.indicate) ? &cccd_md : NULL;
if ((userDescriptionDescriptorValueLen > 0) && (userDescriptionDescriptorValuePtr != NULL)) {
char_md.p_char_user_desc = const_cast<uint8_t *>(userDescriptionDescriptorValuePtr);
char_md.char_user_desc_max_size = userDescriptionDescriptorValueLen;
char_md.char_user_desc_size = userDescriptionDescriptorValueLen;
}
if ((presentationFormatDescriptorValueLen > 0) && (presentationFormatDescriptorValuePtr != NULL)) {
ASSERT_TRUE( sizeof(ble_gatts_char_pf_t) == sizeof(GattCharacteristic::PresentationFormat_t), ERROR_INVALID_PARAM );
ASSERT_TRUE( presentationFormatDescriptorValueLen == sizeof(GattCharacteristic::PresentationFormat_t), ERROR_INVALID_PARAM );
char_md.p_char_pf = const_cast<ble_gatts_char_pf_t *>(reinterpret_cast<const ble_gatts_char_pf_t *>(presentationFormatDescriptorValuePtr));
}
/* Attribute declaration */
ble_gatts_attr_md_t attr_md = {0};
attr_md.rd_auth = readAuthorization;
attr_md.wr_auth = writeAuthorization;
attr_md.vloc = BLE_GATTS_VLOC_STACK;
/* Always set variable size */
attr_md.vlen = has_variable_len;
if (char_props.read || char_props.notify || char_props.indicate) {
switch (requiredSecurity) {
case SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK :
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
break;
case SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM :
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&attr_md.read_perm);
break;
case SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM :
BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(&attr_md.read_perm);
break;
case SecurityManager::SECURITY_MODE_SIGNED_NO_MITM :
BLE_GAP_CONN_SEC_MODE_SET_SIGNED_NO_MITM(&attr_md.read_perm);
break;
case SecurityManager::SECURITY_MODE_SIGNED_WITH_MITM :
BLE_GAP_CONN_SEC_MODE_SET_SIGNED_WITH_MITM(&attr_md.read_perm);
break;
default:
break;
};
}
if (char_props.write || char_props.write_wo_resp) {
switch (requiredSecurity) {
case SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK :
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
break;
case SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM :
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&attr_md.write_perm);
break;
case SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM :
BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(&attr_md.write_perm);
break;
case SecurityManager::SECURITY_MODE_SIGNED_NO_MITM :
BLE_GAP_CONN_SEC_MODE_SET_SIGNED_NO_MITM(&attr_md.write_perm);
break;
case SecurityManager::SECURITY_MODE_SIGNED_WITH_MITM :
BLE_GAP_CONN_SEC_MODE_SET_SIGNED_WITH_MITM(&attr_md.write_perm);
break;
default:
break;
};
}
ble_gatts_attr_t attr_char_value = {0};
attr_char_value.p_uuid = p_uuid;
attr_char_value.p_attr_md = &attr_md;
attr_char_value.init_len = length;
attr_char_value.max_len = max_length;
attr_char_value.p_value = p_data;
ASSERT_STATUS ( sd_ble_gatts_characteristic_add(service_handle,
&char_md,
&attr_char_value,
p_char_handle));
return ERROR_NONE;
}
/**************************************************************************/
/*!
@brief Adds a new descriptor to the custom service, assigning
value, a UUID add-on value, etc.
@param[in] char_handle
@param[in] p_uuid The 16-bit value to add to the base UUID
for this descriptor (normally >1
since 1 is typically used by the primary
service).
@param[in] max_length The maximum length of this descriptor
@param[in] has_variable_len Whether the characteristic data has
variable length.
@returns
@retval ERROR_NONE Everything executed normally
*/
/**************************************************************************/
error_t custom_add_in_descriptor(uint16_t char_handle,
ble_uuid_t *p_uuid,
uint8_t *p_data,
uint16_t length,
uint16_t max_length,
bool has_variable_len,
uint16_t *p_desc_handle)
{
/* Descriptor metadata */
ble_gatts_attr_md_t desc_md = {0};
desc_md.vloc = BLE_GATTS_VLOC_STACK;
/* Always set variable size */
desc_md.vlen = has_variable_len;
/* Make it readable and writable */
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&desc_md.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&desc_md.write_perm);
ble_gatts_attr_t attr_desc = {0};
attr_desc.p_uuid = p_uuid;
attr_desc.p_attr_md = &desc_md;
attr_desc.init_len = length;
attr_desc.max_len = max_length;
attr_desc.p_value = p_data;
ASSERT_STATUS ( sd_ble_gatts_descriptor_add(char_handle,
&attr_desc,
p_desc_handle));
return ERROR_NONE;
}

View File

@ -0,0 +1,70 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 _CUSTOM_HELPER_H_
#define _CUSTOM_HELPER_H_
#include "common/common.h"
#include "headers/nrf_ble.h"
#include "ble/UUID.h"
#include "ble/GattCharacteristic.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Reset the table of 128bits uuids.
* This table is used to keep track of vendors uuids added to the softdevice.
* It is important to reset it before disabling the softdevice otherwise the
* next time the softdevice will be enabled, this table will not be synchronmized
* with the softdevice table.
*/
void custom_reset_128bits_uuid_table();
uint8_t custom_add_uuid_base(uint8_t const *const p_uuid_base);
error_t custom_decode_uuid(uint8_t const *const p_uuid_base,
ble_uuid_t *p_uuid);
ble_uuid_t custom_convert_to_nordic_uuid(const UUID &uuid);
error_t custom_add_in_characteristic(uint16_t service_handle,
ble_uuid_t *p_uuid,
uint8_t properties,
SecurityManager::SecurityMode_t requiredSecurity,
uint8_t *p_data,
uint16_t length,
uint16_t max_length,
bool has_variable_len,
const uint8_t *userDescriptionDescriptorValuePtr,
uint16_t userDescriptionDescriptorValueLen,
const uint8_t *presentationFormatDescriptorValuePtr,
uint16_t presentationFormatDescriptorValueLen,
bool readAuthorization,
bool writeAuthorization,
ble_gatts_char_handles_t *p_char_handle);
error_t custom_add_in_descriptor(uint16_t char_handle,
ble_uuid_t *p_uuid,
uint8_t *p_data,
uint16_t length,
uint16_t max_length,
bool has_variable_len,
uint16_t *p_desc_handle);
#ifdef __cplusplus
}
#endif
#endif // ifndef _CUSTOM_HELPER_H_

View File

@ -0,0 +1,103 @@
/**************************************************************************/
/*!
@file ansi_esc_code.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
/** \file
* \brief TBD
*
* \note TBD
*/
/** \ingroup TBD
* \defgroup TBD
* \brief TBD
*
* @{
*/
#ifndef _ANSI_ESC_CODE_H_
#define _ANSI_ESC_CODE_H_
#ifdef __cplusplus
extern "C" {
#endif
#define CSI_CODE(seq) "\33[" seq
#define CSI_SGR(x) CSI_CODE(#x) "m"
//------------- Cursor movement -------------//
#define ANSI_CURSOR_UP(n) CSI_CODE(#n "A")
#define ANSI_CURSOR_DOWN(n) CSI_CODE(#n "B")
#define ANSI_CURSOR_FORWARD(n) CSI_CODE(#n "C")
#define ANSI_CURSOR_BACKWARD(n) CSI_CODE(#n "D")
#define ANSI_CURSOR_LINE_DOWN(n) CSI_CODE(#n "E")
#define ANSI_CURSOR_LINE_UP(n) CSI_CODE(#n "F")
#define ANSI_CURSOR_POSITION(n, m) CSI_CODE(#n ";" #m "H")
#define ANSI_ERASE_SCREEN(n) CSI_CODE(#n "J")
#define ANSI_ERASE_LINE(n) CSI_CODE(#n "K")
/** text color */
#define ANSI_TEXT_BLACK CSI_SGR(30)
#define ANSI_TEXT_RED CSI_SGR(31)
#define ANSI_TEXT_GREEN CSI_SGR(32)
#define ANSI_TEXT_YELLOW CSI_SGR(33)
#define ANSI_TEXT_BLUE CSI_SGR(34)
#define ANSI_TEXT_MAGENTA CSI_SGR(35)
#define ANSI_TEXT_CYAN CSI_SGR(36)
#define ANSI_TEXT_WHITE CSI_SGR(37)
#define ANSI_TEXT_DEFAULT CSI_SGR(39)
/** background color */
#define ANSI_BG_BLACK CSI_SGR(40)
#define ANSI_BG_RED CSI_SGR(41)
#define ANSI_BG_GREEN CSI_SGR(42)
#define ANSI_BG_YELLOW CSI_SGR(43)
#define ANSI_BG_BLUE CSI_SGR(44)
#define ANSI_BG_MAGENTA CSI_SGR(45)
#define ANSI_BG_CYAN CSI_SGR(46)
#define ANSI_BG_WHITE CSI_SGR(47)
#define ANSI_BG_DEFAULT CSI_SGR(49)
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_ANSI_ESC_CODE_H_ */
/** @} */

View File

@ -0,0 +1,197 @@
/**************************************************************************/
/*!
@file assertion.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, K. Townsend (microBuilder.eu)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/
/** \file
* \brief TBD
*
* \note TBD
*/
/** \ingroup TBD
* \defgroup TBD
* \brief TBD
*
* @{
*/
#ifndef _ASSERTION_H_
#define _ASSERTION_H_
#include "projectconfig.h"
#ifdef __cplusplus
extern "C"
{
#endif
static inline void debugger_breakpoint(void) ATTR_ALWAYS_INLINE;
static inline void debugger_breakpoint(void)
{
#ifndef _TEST_
__asm("BKPT #0\n");
#endif
}
//--------------------------------------------------------------------+
// Compile-time Assert
//--------------------------------------------------------------------+
#if defined __COUNTER__ && __COUNTER__ != __COUNTER__
#define _ASSERT_COUNTER __COUNTER__
#else
#define _ASSERT_COUNTER __LINE__
#endif
#define ASSERT_STATIC(const_expr, message) enum { XSTRING_CONCAT_(static_assert_, _ASSERT_COUNTER) = 1/(!!(const_expr)) }
//--------------------------------------------------------------------+
// Assert Helper
//--------------------------------------------------------------------+
//#ifndef _TEST_
// #define ASSERT_MESSAGE(format, ...) _PRINTF("Assert at %s: %s: %d: " format "\n", __BASE_FILE__, __PRETTY_FUNCTION__, __LINE__, __VA_ARGS__)
//#else
// #define ASSERT_MESSAGE(format, ...) _PRINTF("%d:note: Assert " format "\n", __LINE__, __VA_ARGS__)
//#endif
#if CFG_DEBUG == 3
#define ASSERT_MESSAGE(format, ...) debugger_breakpoint()
#elif CFG_DEBUG == 2
#define ASSERT_MESSAGE(format, ...) printf("Assert at %s: %s: %d: " format "\n", __BASE_FILE__, __PRETTY_FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define ASSERT_MESSAGE(format, ...)
#endif
#define ASSERT_ERROR_HANDLER(x, para) \
return (x)
#define ASSERT_DEFINE_WITH_HANDLER(error_handler, handler_para, setup_statement, condition, error, format, ...) \
do{\
setup_statement;\
if (!(condition)) {\
ASSERT_MESSAGE(format, __VA_ARGS__);\
error_handler(error, handler_para);\
}\
}while(0)
#define ASSERT_DEFINE(...) ASSERT_DEFINE_WITH_HANDLER(ASSERT_ERROR_HANDLER, NULL, __VA_ARGS__)
//--------------------------------------------------------------------+
// error_t Status Assert TODO use ASSERT_DEFINE
//--------------------------------------------------------------------+
#define ASSERT_STATUS_MESSAGE(sts, message) \
ASSERT_DEFINE(error_t status = (error_t)(sts),\
ERROR_NONE == status, status, "%s: %s", ErrorStr[status], message)
#define ASSERT_STATUS(sts) \
ASSERT_DEFINE(error_t status = (error_t)(sts),\
ERROR_NONE == status, status, "error = %d", status)
#define ASSERT_STATUS_RET_VOID(sts) \
ASSERT_DEFINE(error_t status = (error_t)(sts),\
ERROR_NONE == status, (void) 0, "error = %d", status)
//--------------------------------------------------------------------+
// Logical Assert
//--------------------------------------------------------------------+
#define ASSERT_TRUE(condition , error) ASSERT_DEFINE( , (condition), error, "%s", "evaluated to false")
#define ASSERT_FALSE(condition , error) ASSERT_DEFINE( ,!(condition), error, "%s", "evaluated to true")
//--------------------------------------------------------------------+
// Pointer Assert
//--------------------------------------------------------------------+
#define ASSERT_PTR(...) ASSERT_PTR_NOT_NULL(__VA_ARGS__)
#define ASSERT_PTR_NOT_NULL(pointer, error) ASSERT_DEFINE( , NULL != (pointer), error, "%s", "pointer is NULL")
#define ASSERT_PTR_NULL(pointer, error) ASSERT_DEFINE( , NULL == (pointer), error, "%s", "pointer is not NULL")
//--------------------------------------------------------------------+
// Integral Assert
//--------------------------------------------------------------------+
#define ASSERT_XXX_EQUAL(type_format, expected, actual, error) \
ASSERT_DEFINE(\
uint32_t exp = (expected); uint32_t act = (actual),\
exp==act,\
error,\
"expected " type_format ", actual " type_format, exp, act)
#define ASSERT_XXX_WITHIN(type_format, lower, upper, actual, error) \
ASSERT_DEFINE(\
uint32_t low = (lower); uint32_t up = (upper); uint32_t act = (actual),\
(low <= act) && (act <= up),\
error,\
"expected within " type_format " - " type_format ", actual " type_format, low, up, act)
//--------------------------------------------------------------------+
// Integer Assert
//--------------------------------------------------------------------+
#define ASSERT_INT(...) ASSERT_INT_EQUAL(__VA_ARGS__)
#define ASSERT_INT_EQUAL(...) ASSERT_XXX_EQUAL("%d", __VA_ARGS__)
#define ASSERT_INT_WITHIN(...) ASSERT_XXX_WITHIN("%d", __VA_ARGS__)
//--------------------------------------------------------------------+
// Hex Assert
//--------------------------------------------------------------------+
#define ASSERT_HEX(...) ASSERT_HEX_EQUAL(__VA_ARGS__)
#define ASSERT_HEX_EQUAL(...) ASSERT_XXX_EQUAL("0x%x", __VA_ARGS__)
#define ASSERT_HEX_WITHIN(...) ASSERT_XXX_WITHIN("0x%x", __VA_ARGS__)
//--------------------------------------------------------------------+
// Bin Assert
//--------------------------------------------------------------------+
#define BIN8_PRINTF_PATTERN "%d%d%d%d%d%d%d%d"
#define BIN8_PRINTF_CONVERT(byte) \
((byte) & 0x80 ? 1 : 0), \
((byte) & 0x40 ? 1 : 0), \
((byte) & 0x20 ? 1 : 0), \
((byte) & 0x10 ? 1 : 0), \
((byte) & 0x08 ? 1 : 0), \
((byte) & 0x04 ? 1 : 0), \
((byte) & 0x02 ? 1 : 0), \
((byte) & 0x01 ? 1 : 0)
#define ASSERT_BIN8(...) ASSERT_BIN8_EQUAL(__VA_ARGS__)
#define ASSERT_BIN8_EQUAL(expected, actual, error)\
ASSERT_DEFINE(\
uint8_t exp = (expected); uint8_t act = (actual),\
exp==act,\
error,\
"expected " BIN8_PRINTF_PATTERN ", actual " BIN8_PRINTF_PATTERN, BIN8_PRINTF_CONVERT(exp), BIN8_PRINTF_CONVERT(act) )
#ifdef __cplusplus
}
#endif
#endif /* _ASSERTION_H_ */
/** @} */

View File

@ -0,0 +1,96 @@
/**************************************************************************/
/*!
@file binary.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, K. Townsend (microBuilder.eu)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/
/** \ingroup TBD
* \defgroup TBD
* \brief TBD
*
* @{
*/
#ifndef _BINARY_H_
#define _BINARY_H_
#ifdef __cplusplus
extern "C" {
#endif
/// n-th Bit
#define BIT(n) (1 << (n))
/// set n-th bit of x to 1
#define BIT_SET(x, n) ( (x) | BIT(n) )
/// clear n-th bit of x
#define BIT_CLR(x, n) ( (x) & (~BIT(n)) )
/// test n-th bit of x
#define BIT_TEST(x, n) ( (x) & BIT(n) )
#if defined(__GNUC__) && !defined(__CC_ARM) // keil does not support binary format
#define BIN8(x) ((uint8_t) (0b##x))
#define BIN16(b1, b2) ((uint16_t) (0b##b1##b2))
#define BIN32(b1, b2, b3, b4) ((uint32_t) (0b##b1##b2##b3##b4))
#else
// internal macro of B8, B16, B32
#define _B8__(x) (((x&0x0000000FUL)?1:0) \
+((x&0x000000F0UL)?2:0) \
+((x&0x00000F00UL)?4:0) \
+((x&0x0000F000UL)?8:0) \
+((x&0x000F0000UL)?16:0) \
+((x&0x00F00000UL)?32:0) \
+((x&0x0F000000UL)?64:0) \
+((x&0xF0000000UL)?128:0))
#define BIN8(d) ((uint8_t) _B8__(0x##d##UL))
#define BIN16(dmsb,dlsb) (((uint16_t)BIN8(dmsb)<<8) + BIN8(dlsb))
#define BIN32(dmsb,db2,db3,dlsb) \
(((uint32_t)BIN8(dmsb)<<24) \
+ ((uint32_t)BIN8(db2)<<16) \
+ ((uint32_t)BIN8(db3)<<8) \
+ BIN8(dlsb))
#endif
#ifdef __cplusplus
}
#endif
#endif /* _BINARY_H_ */
/** @} */

View File

@ -0,0 +1,151 @@
/**************************************************************************/
/*!
@file ble_error.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, K. Townsend (microBuilder.eu)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/
/** \file
* \brief Error Header
*
* \note TBD
*/
/** \ingroup Group_Common
* \defgroup Group_Error Error Codes
* @{
*/
#ifndef _BLE_ERROR_H_
#define _BLE_ERROR_H_
#include "projectconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
/*=======================================================================
NORDIC GLOBAL ERRORS 0x0000 .. 0x00FF
-----------------------------------------------------------------------
Errors mapped from nrf_error.h
-----------------------------------------------------------------------*/
ERROR_NONE = 0x0000 , ///< Successful command
ERROR_SVC_HANDLER_MISSING = 0x0001 , ///< SVC handler is missing
ERROR_SOFTDEVICE_NOT_ENABLED = 0x0002 , ///< SoftDevice has not been enabled
ERROR_INTERNAL = 0x0003 , ///< Internal Error
ERROR_NO_MEM = 0x0004 , ///< No Memory for operation
ERROR_NOT_FOUND = 0x0005 , ///< Not found
ERROR_NOT_SUPPORTED = 0x0006 , ///< Not supported
ERROR_INVALID_PARAM = 0x0007 , ///< Invalid Parameter
ERROR_INVALID_STATE = 0x0008 , ///< Invalid state, operation disallowed in this state
ERROR_INVALID_LENGTH = 0x0009 , ///< Invalid Length
ERROR_INVALID_FLAGS = 0x000A , ///< Invalid Flags
ERROR_INVALID_DATA = 0x000B , ///< Invalid Data
ERROR_DATA_SIZE = 0x000C , ///< Data size exceeds limit
ERROR_TIMEOUT = 0x000D , ///< Operation timed out
ERROR_NULL = 0x000E , ///< Null Pointer
ERROR_FORBIDDEN = 0x000F , ///< Forbidden Operation
ERROR_INVALID_ADDR = 0x0010 , ///< Bad Memory Address
ERROR_BUSY = 0x0011 , ///< Busy
/*=======================================================================*/
ERROR_INVALIDPARAMETER = 0x0100 , /**< An invalid parameter value was provided */
ERROR_I2C_XFER_FAILED = 0x0101 , /**< an failed attempt to make I2C transfer */
/*=======================================================================
SIMPLE BINARY PROTOCOL ERRORS 0x0120 .. 0x013F
-----------------------------------------------------------------------
Errors relating to the simple binary protocol (/src//protocol)
-----------------------------------------------------------------------*/
ERROR_PROT_INVALIDMSGTYPE = 0x121, /**< Unexpected msg type encountered */
ERROR_PROT_INVALIDCOMMANDID = 0x122, /**< Unknown or out of range command ID */
ERROR_PROT_INVALIDPAYLOAD = 0x123, /**< Message payload has a problem (invalid len, etc.) */
/*=======================================================================*/
//------------- based on Nordic SDM nrf_error_sdm.h -------------//
ERROR_SDM_LFCLK_SOURCE_UNKNOWN = 0x1000 , ///< Unknown lfclk source
ERROR_SDM_INCORRECT_INTERRUPT_CONFIGURATION = 0x1001 , ///< Incorrect interrupt configuration (can be caused by using illegal priority levels, or having enabled SoftDevice interrupts)
ERROR_SDM_INCORRECT_CLENR0 = 0x1002 , ///< Incorrect CLENR0 (can be caused by erronous SoftDevice flashing)
//------------- based on Nordic SOC nrf_error_soc.h -------------//
/* Mutex Errors */
ERROR_SOC_MUTEX_ALREADY_TAKEN = 0x2000 , ///< Mutex already taken
/* NVIC errors */
ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE = 0x2001 , ///< NVIC interrupt not available
ERROR_SOC_NVIC_INTERRUPT_PRIORITY_NOT_ALLOWED = 0x2002 , ///< NVIC interrupt priority not allowed
ERROR_SOC_NVIC_SHOULD_NOT_RETURN = 0x2003 , ///< NVIC should not return
/* Power errors */
ERROR_SOC_POWER_MODE_UNKNOWN = 0x2004 , ///< Power mode unknown
ERROR_SOC_POWER_POF_THRESHOLD_UNKNOWN = 0x2005 , ///< Power POF threshold unknown
ERROR_SOC_POWER_OFF_SHOULD_NOT_RETURN = 0x2006 , ///< Power off should not return
/* Rand errors */
ERROR_SOC_RAND_NOT_ENOUGH_VALUES = 0x2007 , ///< RAND not enough values
/* PPI errors */
ERROR_SOC_PPI_INVALID_CHANNEL = 0x2008 , ///< Invalid PPI Channel
ERROR_SOC_PPI_INVALID_GROUP = 0x2009 , ///< Invalid PPI Group
//------------- based on Nordic STK (ble) ble_err.h -------------//
ERROR_BLE_INVALID_CONN_HANDLE = 0x3001 , /**< Invalid connection handle. */
ERROR_BLE_INVALID_ATTR_HANDLE = 0x3002 , /**< Invalid attribute handle. */
ERROR_BLE_NO_TX_BUFFERS = 0x3003 , /**< Buffer capacity exceeded. */
// L2CAP
ERROR_BLE_L2CAP_CID_IN_USE = 0x3100 , /**< CID already in use. */
// GAP
ERROR_BLE_GAP_UUID_LIST_MISMATCH = 0x3200 , /**< UUID list does not contain an integral number of UUIDs. */
ERROR_BLE_GAP_DISCOVERABLE_WITH_WHITELIST = 0x3201 , /**< Use of Whitelist not permitted with discoverable advertising. */
ERROR_BLE_GAP_INVALID_BLE_ADDR = 0x3202 , /**< The upper two bits of the address do not correspond to the specified address type. */
// GATTC
ERROR_BLE_GATTC_PROC_NOT_PERMITTED = 0x3300 ,
// GATTS
ERROR_BLEGATTS_INVALID_ATTR_TYPE = 0x3400 , /**< Invalid attribute type. */
ERROR_BLEGATTS_SYS_ATTR_MISSING = 0x3401 , /**< System Attributes missing. */
}error_t;
#ifdef __cplusplus
}
#endif
#endif /* _BLE_ERROR_H_ */
/** @} */

View File

@ -0,0 +1,236 @@
/**************************************************************************/
/*!
@file common.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, K. Townsend (microBuilder.eu)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/
/** \defgroup Group_Common Common Files
* @{
*
* \defgroup Group_CommonH common.h
*
* @{
*/
#ifndef _COMMON_H_
#define _COMMON_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// INCLUDES
//--------------------------------------------------------------------+
//------------- Standard Header -------------//
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
//------------- General Header -------------//
#include "projectconfig.h"
#include "compiler.h"
#include "assertion.h"
#include "binary.h"
#include "ble_error.h"
//------------- MCU header -------------//
//#include "nrf.h"
//--------------------------------------------------------------------+
// TYPEDEFS
//--------------------------------------------------------------------+
typedef unsigned char byte_t;
typedef float float32_t;
typedef double float64_t;
//--------------------------------------------------------------------+
// MACROS
//--------------------------------------------------------------------+
#define STRING_(x) #x // stringify without expand
#define XSTRING_(x) STRING_(x) // expand then stringify
#define STRING_CONCAT_(a, b) a##b // concat without expand
#define XSTRING_CONCAT_(a, b) STRING_CONCAT_(a, b) // expand then concat
#define U16_HIGH_U8(u16) ((uint8_t) (((u16) >> 8) & 0x00ff))
#define U16_LOW_U8(u16) ((uint8_t) ((u16) & 0x00ff))
#define U16_TO_U8S_BE(u16) U16_HIGH_U8(u16), U16_LOW_U8(u16)
#define U16_TO_U8S_LE(u16) U16_LOW_U8(u16), U16_HIGH_U8(u16)
#define U32_B1_U8(u32) ((uint8_t) (((u32) >> 24) & 0x000000ff)) // MSB
#define U32_B2_U8(u32) ((uint8_t) (((u32) >> 16) & 0x000000ff))
#define U32_B3_U8(u32) ((uint8_t) (((u32) >> 8) & 0x000000ff))
#define U32_B4_U8(u32) ((uint8_t) ((u32) & 0x000000ff)) // LSB
#define U32_TO_U8S_BE(u32) U32_B1_U8(u32), U32_B2_U8(u32), U32_B3_U8(u32), U32_B4_U8(u32)
#define U32_TO_U8S_LE(u32) U32_B4_U8(u32), U32_B3_U8(u32), U32_B2_U8(u32), U32_B1_U8(u32)
//--------------------------------------------------------------------+
// INLINE FUNCTION
//--------------------------------------------------------------------+
#define memclr_(buffer, size) memset(buffer, 0, size)
//------------- Conversion -------------//
/// form an uint32_t from 4 x uint8_t
static inline uint32_t u32_from_u8(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint32_t u32_from_u8(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4)
{
return (b1 << 24) + (b2 << 16) + (b3 << 8) + b4;
}
static inline uint8_t u16_high_u8(uint16_t u16) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t u16_high_u8(uint16_t u16)
{
return (uint8_t) ((u16 >> 8) & 0x00ff);
}
static inline uint8_t u16_low_u8(uint16_t u16) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t u16_low_u8(uint16_t u16)
{
return (uint8_t) (u16 & 0x00ff);
}
//------------- Min -------------//
static inline uint8_t min8_of(uint8_t x, uint8_t y) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint8_t min8_of(uint8_t x, uint8_t y)
{
return (x < y) ? x : y;
}
static inline uint16_t min16_of(uint16_t x, uint16_t y) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint16_t min16_of(uint16_t x, uint16_t y)
{
return (x < y) ? x : y;
}
static inline uint32_t min32_of(uint32_t x, uint32_t y) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint32_t min32_of(uint32_t x, uint32_t y)
{
return (x < y) ? x : y;
}
//------------- Max -------------//
static inline uint32_t max32_of(uint32_t x, uint32_t y) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint32_t max32_of(uint32_t x, uint32_t y)
{
return (x > y) ? x : y;
}
//------------- Align -------------//
static inline uint32_t align32 (uint32_t value) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint32_t align32 (uint32_t value)
{
return (value & 0xFFFFFFE0UL);
}
static inline uint32_t align16 (uint32_t value) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint32_t align16 (uint32_t value)
{
return (value & 0xFFFFFFF0UL);
}
static inline uint32_t align_n (uint32_t alignment, uint32_t value) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint32_t align_n (uint32_t alignment, uint32_t value)
{
return value & (~(alignment-1));
}
static inline uint32_t align4k (uint32_t value) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint32_t align4k (uint32_t value)
{
return (value & 0xFFFFF000UL);
}
static inline uint32_t offset4k(uint32_t value) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint32_t offset4k(uint32_t value)
{
return (value & 0xFFFUL);
}
//------------- Mathematics -------------//
/// inclusive range checking
static inline bool is_in_range(uint32_t lower, uint32_t value, uint32_t upper) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline bool is_in_range(uint32_t lower, uint32_t value, uint32_t upper)
{
return (lower <= value) && (value <= upper);
}
/// exclusive range checking
static inline bool is_in_range_exclusive(uint32_t lower, uint32_t value, uint32_t upper) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline bool is_in_range_exclusive(uint32_t lower, uint32_t value, uint32_t upper)
{
return (lower < value) && (value < upper);
}
static inline uint8_t log2_of(uint32_t value) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint8_t log2_of(uint32_t value)
{
uint8_t result = 0; // log2 of a value is its MSB's position
while (value >>= 1)
{
result++;
}
return result;
}
// return the number of set bits in value
static inline uint8_t cardinality_of(uint32_t value) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint8_t cardinality_of(uint32_t value)
{
// Brian Kernighan's method goes through as many iterations as there are set bits. So if we have a 32-bit word with only
// the high bit set, then it will only go once through the loop
// Published in 1988, the C Programming Language 2nd Ed. (by Brian W. Kernighan and Dennis M. Ritchie)
// mentions this in exercise 2-9. On April 19, 2006 Don Knuth pointed out to me that this method
// "was first published by Peter Wegner in CACM 3 (1960), 322. (Also discovered independently by Derrick Lehmer and
// published in 1964 in a book edited by Beckenbach.)"
uint8_t count;
for (count = 0; value; count++)
{
value &= value - 1; // clear the least significant bit set
}
return count;
}
#ifdef __cplusplus
}
#endif
#endif /* _COMMON_H_ */
/** @} */
/** @} */

View File

@ -0,0 +1,160 @@
/**************************************************************************/
/*!
@file compiler.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, K. Townsend (microBuilder.eu)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/
/** \file
* \brief GCC Header
*/
/** \ingroup Group_Compiler
* \defgroup Group_GCC GNU GCC
* @{
*/
#ifndef _COMPILER_GCC_H_
#define _COMPILER_GCC_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "projectconfig.h"
//#ifndef __GNUC__
// #define ATTR_ALWAYS_INLINE
// #define ATTR_CONST
//#else
#ifdef _TEST_
#define ATTR_ALWAYS_INLINE
#define STATIC_
#define INLINE_
#else
#define STATIC_ static
#define INLINE_ inline
#if CFG_DEBUG == 3
#define ATTR_ALWAYS_INLINE // no inline for debug = 3
#endif
#endif
#ifdef __GNUC__
#define ALIGN_OF(x) __alignof__(x)
/// Normally, the compiler places the objects it generates in sections like data or bss & function in text. Sometimes, however, you need additional sections, or you need certain particular variables to appear in special sections, for example to map to special hardware. The section attribute specifies that a variable (or function) lives in a particular section
#define ATTR_SECTION(section) __attribute__ ((#section))
/// If this attribute is used on a function declaration and a call to such a function is not eliminated through dead code elimination or other optimizations, an error that includes message is diagnosed. This is useful for compile-time checking
#define ATTR_ERROR(Message) __attribute__ ((error(Message)))
/// If this attribute is used on a function declaration and a call to such a function is not eliminated through dead code elimination or other optimizations, a warning that includes message is diagnosed. This is useful for compile-time checking
#define ATTR_WARNING(Message) __attribute__ ((warning(Message)))
/**
* \defgroup Group_VariableAttr Variable Attributes
* @{
*/
/// This attribute specifies a minimum alignment for the variable or structure field, measured in bytes
#define ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
/// The packed attribute specifies that a variable or structure field should have the smallest possible alignment—one byte for a variable, and one bit for a field, unless you specify a larger value with the aligned attribute
#define ATTR_PACKED __attribute__ ((packed))
#define ATTR_PREPACKED
#define ATTR_PACKED_STRUCT(x) x __attribute__ ((packed))
/** @} */
/**
* \defgroup Group_FuncAttr Function Attributes
* @{
*/
#ifndef ATTR_ALWAYS_INLINE
/// Generally, functions are not inlined unless optimization is specified. For functions declared inline, this attribute inlines the function even if no optimization level is specified
#define ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
#endif
/// The nonnull attribute specifies that some function parameters should be non-null pointers. f the compiler determines that a null pointer is passed in an argument slot marked as non-null, and the -Wnonnull option is enabled, a warning is issued. All pointer arguments are marked as non-null
#define ATTR_NON_NULL __attribute__ ((nonull))
/// Many functions have no effects except the return value and their return value depends only on the parameters and/or global variables. Such a function can be subject to common subexpression elimination and loop optimization just as an arithmetic operator would be. These functions should be declared with the attribute pure
#define ATTR_PURE __attribute__ ((pure))
/// Many functions do not examine any values except their arguments, and have no effects except the return value. Basically this is just slightly more strict class than the pure attribute below, since function is not allowed to read global memory.
/// Note that a function that has pointer arguments and examines the data pointed to must not be declared const. Likewise, a function that calls a non-const function usually must not be const. It does not make sense for a const function to return void
#define ATTR_CONST __attribute__ ((const))
/// The deprecated attribute results in a warning if the function is used anywhere in the source file. This is useful when identifying functions that are expected to be removed in a future version of a program. The warning also includes the location of the declaration of the deprecated function, to enable users to easily find further information about why the function is deprecated, or what they should do instead. Note that the warnings only occurs for uses
#define ATTR_DEPRECATED __attribute__ ((deprecated))
/// Same as the deprecated attribute with optional message in the warning
#define ATTR_DEPRECATED_MESS(mess) __attribute__ ((deprecated(mess)))
/// The weak attribute causes the declaration to be emitted as a weak symbol rather than a global. This is primarily useful in defining library functions that can be overridden in user code
#define ATTR_WEAK __attribute__ ((weak))
/// The alias attribute causes the declaration to be emitted as an alias for another symbol, which must be specified
#define ATTR_ALIAS(func) __attribute__ ((alias(#func)))
/// The weakref attribute marks a declaration as a weak reference. It is equivalent with weak + alias attribute, but require function is static
#define ATTR_WEAKREF(func) __attribute__ ((weakref(#func)))
/// The warn_unused_result attribute causes a warning to be emitted if a caller of the function with this attribute does not use its return value. This is useful for functions where not checking the result is either a security problem or always a bug
#define ATTR_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
/// This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced. This is useful, for example, when the function is referenced only in inline assembly.
#define ATTR_USED __attribute__ ((used))
/// This attribute, attached to a function, means that the function is meant to be possibly unused. GCC does not produce a warning for this function.
#define ATTR_UNUSED __attribute__ ((unused))
#elif defined (__ICCARM__) //IAR
#define ATTR_ALWAYS_INLINE // IAR dosn't provide such a syntax extension in function's prototypes.
#define ATTR_CONST // IAR dosn't provide such a syntax extension in function's prototypes.
#endif
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* _COMPILER_GCC_H_ */
/// @}

View File

@ -0,0 +1,257 @@
/* mbed Microcontroller Library
* Copyright (c) 2017-2017 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_NORDIC_PAL_GATT_CLIENT_H_
#define BLE_NORDIC_PAL_GATT_CLIENT_H_
#include "ble/pal/PalGattClient.h"
#include "ble/blecommon.h"
#include "ble/UUID.h"
#include "headers/nrf_ble.h"
#include "headers/ble_gatt.h"
#include "headers/ble_types.h"
#include "btle.h"
namespace ble {
namespace pal {
namespace vendor {
namespace nordic {
/**
* Implementation of pal::GattClient for the Nordic stack.
*/
class nRF5XGattClient : public ble::pal::GattClient {
public:
nRF5XGattClient();
virtual ~nRF5XGattClient();
/**
* see pal::GattClient::initialize .
*/
virtual ble_error_t initialize();
/**
* see pal::GattClient::terminate .
*/
virtual ble_error_t terminate();
/**
* see pal::GattClient::exchange_mtu .
*/
virtual ble_error_t exchange_mtu(connection_handle_t connection);
/**
* see pal::GattClient::get_mtu_size .
*/
virtual ble_error_t get_mtu_size(
connection_handle_t connection_handle, uint16_t& mtu_size
);
/**
* see pal::GattClient::discover_primary_service .
*/
virtual ble_error_t discover_primary_service(
connection_handle_t connection,
attribute_handle_t discovery_range_begining
);
/**
* see pal::GattClient::discover_primary_service_by_service_uuid .
*/
virtual ble_error_t discover_primary_service_by_service_uuid(
connection_handle_t connection_handle,
attribute_handle_t discovery_range_beginning,
const UUID& uuid
);
/**
* see pal::GattClient::find_included_service .
*/
virtual ble_error_t find_included_service(
connection_handle_t connection_handle,
attribute_handle_range_t service_range
);
/**
* see pal::GattClient::discover_characteristics_of_a_service .
*/
virtual ble_error_t discover_characteristics_of_a_service(
connection_handle_t connection_handle,
attribute_handle_range_t discovery_range
);
/**
* see pal::GattClient::discover_characteristics_descriptors .
*/
virtual ble_error_t discover_characteristics_descriptors(
connection_handle_t connection_handle,
attribute_handle_range_t descriptors_discovery_range
);
/**
* see pal::GattClient::read_attribute_value .
*/
virtual ble_error_t read_attribute_value(
connection_handle_t connection_handle,
attribute_handle_t attribute_handle
);
/**
* see pal::GattClient::read_using_characteristic_uuid .
*/
virtual ble_error_t read_using_characteristic_uuid(
connection_handle_t connection_handle,
attribute_handle_range_t read_range,
const UUID& uuid
);
/**
* see pal::GattClient::read_attribute_blob .
*/
virtual ble_error_t read_attribute_blob(
connection_handle_t connection,
attribute_handle_t attribute_handle,
uint16_t offset
);
/**
* see pal::GattClient::read_multiple_characteristic_values .
*/
virtual ble_error_t read_multiple_characteristic_values(
connection_handle_t connection,
const ArrayView<const attribute_handle_t>& characteristic_handles
);
/**
* see pal::GattClient::write_without_response .
*/
virtual ble_error_t write_without_response(
connection_handle_t connection_handle,
attribute_handle_t characteristic_value_handle,
const ArrayView<const uint8_t>& value
);
/**
* see pal::GattClient::signed_write_without_response .
*/
virtual ble_error_t signed_write_without_response(
connection_handle_t connection_handle,
attribute_handle_t characteristic_value_handle,
const ArrayView<const uint8_t>& value
);
/**
* see pal::GattClient::write_attribute .
*/
virtual ble_error_t write_attribute(
connection_handle_t connection_handle,
attribute_handle_t attribute_handle,
const ArrayView<const uint8_t>& value
);
/**
* see pal::GattClient::queue_prepare_write .
*/
virtual ble_error_t queue_prepare_write(
connection_handle_t connection_handle,
attribute_handle_t characteristic_value_handle,
const ArrayView<const uint8_t>& value,
uint16_t offset
);
/**
* see pal::GattClient::execute_write_queue .
*/
virtual ble_error_t execute_write_queue(
connection_handle_t connection_handle,
bool execute
);
// singleton of the ARM Cordio client
static nRF5XGattClient& get_client();
/**
* Function call from btle.cpp
*
* Do not call directly.
*/
static void handle_events(const ble_evt_t *p_ble_evt);
/**
* Called by btle.cpp when a disconnection happens.
*/
static void handle_connection_termination(connection_handle_t connection);
private:
struct GattProcedure;
struct RegularGattProcedure;
struct DiscoverPrimaryServiceProcedure;
struct DiscoverPrimaryServiceByUUIDProcedure;
struct FindIncludedServicesProcedure;
struct DiscoverCharacteristicsProcedure;
struct DiscoverDescriptorsProcedure;
struct ReadAttributeProcedure;
struct ReadUsingCharacteristicUUIDProcedure;
struct ReadAttributeBlobProcedure;
struct ReadMultipleCharacteristicsProcedure;
struct WriteAttributeProcedure;
struct QueuePrepareWriteProcedure;
struct ExecuteWriteQueueProcedure;
template<typename ProcType, typename A0>
ble_error_t launch_procedure(connection_handle_t connection, const A0& a0);
template<typename ProcType, typename A0, typename A1>
ble_error_t launch_procedure(
connection_handle_t connection, const A0& a0, const A1& a1
);
template<typename ProcType, typename A0, typename A1, typename A2>
ble_error_t launch_procedure(
connection_handle_t connection,
const A0& a0, const A1& a1, const A2& a2
);
template<typename ProcType, typename A0, typename A1, typename A2, typename A3>
ble_error_t launch_procedure(
connection_handle_t connection,
const A0& a0, const A1& a1, const A2& a2, const A3& a3
);
GattProcedure* get_procedure(connection_handle_t) const;
bool register_procedure(GattProcedure*);
bool remove_procedure(GattProcedure*);
void handle_procedure_event(const ble_evt_t &evt);
void handle_hvx_event(const ble_evt_t &evt);
void handle_timeout_event(const ble_evt_t &evt);
static const size_t max_procedures_count =
CENTRAL_LINK_COUNT + PERIPHERAL_LINK_COUNT;
// Note: Ideally we would have used an array of variant here
GattProcedure* _procedures[max_procedures_count];
};
} // nordic
} // vendor
} // pal
} // ble
#endif /* BLE_NORDIC_PAL_GATT_CLIENT_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,285 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 __NRF5x_GAP_H__
#define __NRF5x_GAP_H__
#ifdef YOTTA_CFG_MBED_OS
#include "mbed-drivers/mbed.h"
#else
#include "mbed.h"
#endif
#ifndef YOTTA_CFG_WHITELIST_MAX_SIZE
#define YOTTA_CFG_WHITELIST_MAX_SIZE BLE_GAP_WHITELIST_ADDR_MAX_COUNT
#elif YOTTA_CFG_WHITELIST_MAX_SIZE > BLE_GAP_WHITELIST_ADDR_MAX_COUNT
#undef YOTTA_CFG_WHITELIST_MAX_SIZE
#define YOTTA_CFG_WHITELIST_MAX_SIZE BLE_GAP_WHITELIST_ADDR_MAX_COUNT
#endif
#ifndef YOTTA_CFG_IRK_TABLE_MAX_SIZE
#if (NRF_SD_BLE_API_VERSION >= 3)
#define YOTTA_CFG_IRK_TABLE_MAX_SIZE BLE_GAP_DEVICE_IDENTITIES_MAX_COUNT
#else
#define YOTTA_CFG_IRK_TABLE_MAX_SIZE BLE_GAP_WHITELIST_IRK_MAX_COUNT
#endif
#elif YOTTA_CFG_IRK_TABLE_MAX_SIZE > BLE_GAP_WHITELIST_IRK_MAX_COUNT
#undef YOTTA_CFG_IRK_TABLE_MAX_SIZE
#define YOTTA_CFG_IRK_TABLE_MAX_SIZE BLE_GAP_WHITELIST_IRK_MAX_COUNT
#endif
#include "ble/blecommon.h"
#include "headers/nrf_ble.h"
#include "ble/GapAdvertisingParams.h"
#include "ble/GapAdvertisingData.h"
#include "ble/Gap.h"
#include "ble/GapScanningParams.h"
#include "nrf_soc.h"
extern "C" {
#include "ble_radio_notification.h"
#include "app_util_platform.h"
}
#include "btle_security.h"
void radioNotificationStaticCallback(bool param);
/**************************************************************************/
/*!
\brief
*/
/**************************************************************************/
class nRF5xGap : public Gap
{
public:
/* Functions that must be implemented from Gap */
virtual ble_error_t setAddress(AddressType_t type, const Address_t address);
virtual ble_error_t getAddress(AddressType_t *typeP, Address_t address);
virtual ble_error_t setAdvertisingData(const GapAdvertisingData &, const GapAdvertisingData &);
virtual uint16_t getMinAdvertisingInterval(void) const {return GapAdvertisingParams::ADVERTISEMENT_DURATION_UNITS_TO_MS(BLE_GAP_ADV_INTERVAL_MIN);}
virtual uint16_t getMinNonConnectableAdvertisingInterval(void) const {
#if (NRF_SD_BLE_API_VERSION >= 5) // In SD v5+, non connectable advertising interval is the same as connectable advertising interval
// The Mbed infrastructure expects 100ms+ - so for now return that
// return getMinAdvertisingInterval();
// FIXME infrastructure
return GapAdvertisingParams::GAP_ADV_PARAMS_INTERVAL_MIN_NONCON;
#else
return GapAdvertisingParams::ADVERTISEMENT_DURATION_UNITS_TO_MS(BLE_GAP_ADV_NONCON_INTERVAL_MIN);
#endif
}
virtual uint16_t getMaxAdvertisingInterval(void) const {return GapAdvertisingParams::ADVERTISEMENT_DURATION_UNITS_TO_MS(BLE_GAP_ADV_INTERVAL_MAX);}
virtual ble_error_t startAdvertising(const GapAdvertisingParams &);
virtual ble_error_t stopAdvertising(void);
virtual ble_error_t connect(const Address_t, BLEProtocol::AddressType_t peerAddrType, const ConnectionParams_t *connectionParams, const GapScanningParams *scanParams);
virtual ble_error_t disconnect(Handle_t connectionHandle, DisconnectionReason_t reason);
virtual ble_error_t disconnect(DisconnectionReason_t reason);
virtual ble_error_t setDeviceName(const uint8_t *deviceName);
virtual ble_error_t getDeviceName(uint8_t *deviceName, unsigned *lengthP);
virtual ble_error_t setAppearance(GapAdvertisingData::Appearance appearance);
virtual ble_error_t getAppearance(GapAdvertisingData::Appearance *appearanceP);
virtual ble_error_t setTxPower(int8_t txPower);
virtual void getPermittedTxPowerValues(const int8_t **valueArrayPP, size_t *countP);
void setConnectionHandle(uint16_t con_handle);
uint16_t getConnectionHandle(void);
virtual ble_error_t getPreferredConnectionParams(ConnectionParams_t *params);
virtual ble_error_t setPreferredConnectionParams(const ConnectionParams_t *params);
virtual ble_error_t updateConnectionParams(Handle_t handle, const ConnectionParams_t *params);
virtual ble_error_t reset(void);
/*
* The following functions are part of the whitelisting experimental API.
* Therefore, this functionality can change in the near future.
*/
virtual uint8_t getMaxWhitelistSize(void) const;
virtual ble_error_t getWhitelist(Gap::Whitelist_t &whitelistOut) const;
virtual ble_error_t setWhitelist(const Gap::Whitelist_t &whitelistIn);
virtual ble_error_t setAdvertisingPolicyMode(AdvertisingPolicyMode_t mode);
virtual ble_error_t setScanningPolicyMode(ScanningPolicyMode_t mode);
virtual ble_error_t setInitiatorPolicyMode(InitiatorPolicyMode_t mode);
virtual Gap::AdvertisingPolicyMode_t getAdvertisingPolicyMode(void) const;
virtual Gap::ScanningPolicyMode_t getScanningPolicyMode(void) const;
virtual Gap::InitiatorPolicyMode_t getInitiatorPolicyMode(void) const;
virtual ble_error_t initRadioNotification(void) {
if (ble_radio_notification_init(APP_IRQ_PRIORITY_HIGH /*MID*/, NRF_RADIO_NOTIFICATION_DISTANCE_800US, radioNotificationStaticCallback) == NRF_SUCCESS) {
return BLE_ERROR_NONE;
}
return BLE_ERROR_UNSPECIFIED;
}
/* Observer role is not supported by S110, return BLE_ERROR_NOT_IMPLEMENTED */
#if !defined(TARGET_MCU_NRF51_16K_S110) && !defined(TARGET_MCU_NRF51_32K_S110)
virtual ble_error_t startRadioScan(const GapScanningParams &scanningParams);
virtual ble_error_t stopScan(void);
#endif
private:
/*
* Whitelisting API related structures and helper functions.
*/
/* Policy modes set by the user. By default these are set to ignore the whitelist */
Gap::AdvertisingPolicyMode_t advertisingPolicyMode;
Gap::ScanningPolicyMode_t scanningPolicyMode;
/* Internal representation of a whitelist */
uint8_t whitelistAddressesSize;
ble_gap_addr_t whitelistAddresses[YOTTA_CFG_WHITELIST_MAX_SIZE];
#if (NRF_SD_BLE_API_VERSION <= 2)
/*
* An internal function used to populate the ble_gap_whitelist_t that will be used by
* the SoftDevice for filtering requests. This function is needed because for the BLE
* API the whitelist is just a collection of keys, but for the stack it also includes
* the IRK table.
*/
ble_error_t generateStackWhitelist(ble_gap_whitelist_t &whitelist);
#endif
#if (NRF_SD_BLE_API_VERSION >= 3)
/* internal type for passing a whitelist and a identities list. */
typedef struct
{
ble_gap_addr_t addrs[YOTTA_CFG_WHITELIST_MAX_SIZE];
uint32_t addrs_cnt;
ble_gap_id_key_t identities[YOTTA_CFG_IRK_TABLE_MAX_SIZE];
uint32_t identities_cnt;
} GapWhiteAndIdentityList_t;
/* Function for preparing setting of the whitelist feature and the identity-resolving feature (privacy).*/
ble_error_t getStackWhiteIdentityList(GapWhiteAndIdentityList_t &whiteAndIdentityList);
/* Function for applying setting of the whitelist feature and identity-resolving feature (privacy).*/
ble_error_t applyWhiteIdentityList(GapWhiteAndIdentityList_t &whiteAndIdentityList);
/* Function for introducing whitelist feature and the identity-resolving feature setting into SoftDevice.
*
* This function incorporates getStackWhiteIdentityList and applyWhiteIdentityList together. */
ble_error_t updateWhiteAndIdentityListInStack(void);
#endif
private:
bool radioNotificationCallbackParam; /* parameter to be passed into the Timeout-generated radio notification callback. */
Timeout radioNotificationTimeout;
/*
* A helper function to post radio notification callbacks with low interrupt priority.
*/
void postRadioNotificationCallback(void) {
#ifdef YOTTA_CFG_MBED_OS
/*
* In mbed OS, all user-facing BLE events (interrupts) are posted to the
* MINAR scheduler to be executed as callbacks in thread mode. MINAR guards
* its critical sections from interrupts by acquiring CriticalSectionLock,
* which results in a call to sd_nvic_critical_region_enter(). Thus, it is
* safe to invoke MINAR APIs from interrupt context as long as those
* interrupts are blocked by sd_nvic_critical_region_enter().
*
* Radio notifications are a special case for the above. The Radio
* Notification IRQ is handled at a very high priority--higher than the
* level blocked by sd_nvic_critical_region_enter(). Thus Radio Notification
* events can preempt MINAR's critical sections. Using MINAR APIs (such as
* posting an event) directly in processRadioNotification() may result in a
* race condition ending in a hard-fault.
*
* The solution is to *not* call MINAR APIs directly from the Radio
* Notification handling; i.e. to do the bulk of RadioNotification
* processing at a reduced priority which respects MINAR's critical
* sections. Unfortunately, on a cortex-M0, there is no clean way to demote
* priority for the currently executing interrupt--we wouldn't want to
* demote the radio notification handling anyway because it is sensitive to
* timing, and the system expects to finish this handling very quickly. The
* workaround is to employ a Timeout to trigger
* postRadioNotificationCallback() after a very short delay (~0 us) and post
* the MINAR callback that context.
*
* !!!WARNING!!! Radio notifications are very time critical events. The
* current solution is expected to work under the assumption that
* postRadioNotificationCalback() will be executed BEFORE the next radio
* notification event is generated.
*/
minar::Scheduler::postCallback(
mbed::util::FunctionPointer1<void, bool>(&radioNotificationCallback, &FunctionPointerWithContext<bool>::call).bind(radioNotificationCallbackParam)
);
#else
/*
* In mbed classic, all user-facing BLE events execute callbacks in interrupt
* mode. Radio Notifications are a special case because its IRQ is handled at
* a very high priority. Thus Radio Notification events can preempt other
* operations that require interaction with the SoftDevice such as advertising
* payload updates and changing the Gap state. Therefore, executing a Radio
* Notification callback directly from processRadioNotification() may result
* in a race condition ending in a hard-fault.
*
* The solution is to *not* execute the Radio Notification callback directly
* from the Radio Notification handling; i.e. to do the bulk of the
* Radio Notification processing at a reduced priority. Unfortunately, on a
* cortex-M0, there is no clean way to demote priority for the currently
* executing interrupt--we wouldn't want to demote the radio notification
* handling anyway because it is sensitive to timing, and the system expects
* to finish this handling very quickly. The workaround is to employ a Timeout
* to trigger postRadioNotificationCallback() after a very short delay (~0 us)
* and execute the callback in that context.
*
* !!!WARNING!!! Radio notifications are very time critical events. The
* current solution is expected to work under the assumption that
* postRadioNotificationCalback() will be executed BEFORE the next radio
* notification event is generated.
*/
radioNotificationCallback.call(radioNotificationCallbackParam);
#endif /* #ifdef YOTTA_CFG_MBED_OS */
}
/**
* A helper function to process radio-notification events; to be called internally.
* @param param [description]
*/
void processRadioNotificationEvent(bool param) {
radioNotificationCallbackParam = param;
radioNotificationTimeout.attach_us(mbed::callback(this, &nRF5xGap::postRadioNotificationCallback), 0);
}
friend void radioNotificationStaticCallback(bool param); /* allow invocations of processRadioNotificationEvent() */
private:
uint16_t m_connectionHandle;
/*
* Allow instantiation from nRF5xn when required.
*/
friend class nRF5xn;
nRF5xGap() :
advertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST),
scanningPolicyMode(Gap::SCAN_POLICY_IGNORE_WHITELIST),
whitelistAddressesSize(0) {
m_connectionHandle = BLE_CONN_HANDLE_INVALID;
}
nRF5xGap(nRF5xGap const &);
void operator=(nRF5xGap const &);
};
#endif // ifndef __NRF5x_GAP_H__

View File

@ -0,0 +1,847 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 "nRF5xGattServer.h"
#ifdef YOTTA_CFG_MBED_OS
#include "mbed-drivers/mbed.h"
#else
#include "mbed.h"
#endif
#include "common/common.h"
#include "btle/custom/custom_helper.h"
#include "nRF5xn.h"
namespace {
static const ble_gatts_rw_authorize_reply_params_t write_auth_queue_full_reply = {
.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
.params = {
.write = {
.gatt_status = BLE_GATT_STATUS_ATTERR_PREPARE_QUEUE_FULL
}
}
};
static const ble_gatts_rw_authorize_reply_params_t write_auth_invalid_offset_reply = {
.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
.params = {
.write = {
.gatt_status = BLE_GATT_STATUS_ATTERR_INVALID_OFFSET
}
}
};
static const ble_gatts_rw_authorize_reply_params_t write_auth_succes_reply = {
.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
.params = {
.write = {
.gatt_status = BLE_GATT_STATUS_SUCCESS,
.update = 0
}
}
};
static const ble_gatts_rw_authorize_reply_params_t write_auth_invalid_reply = {
.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
.params = {
.write = {
.gatt_status = BLE_GATT_STATUS_ATTERR_INVALID
}
}
};
static ble_error_t set_attribute_value(
Gap::Handle_t connectionHandle,
GattAttribute::Handle_t attributeHandle,
ble_gatts_value_t *value
) {
uint32_t err = sd_ble_gatts_value_set(connectionHandle, attributeHandle, value);
switch(err) {
case NRF_SUCCESS:
return BLE_ERROR_NONE;
case NRF_ERROR_INVALID_ADDR:
case NRF_ERROR_INVALID_PARAM:
return BLE_ERROR_INVALID_PARAM;
case NRF_ERROR_NOT_FOUND:
case NRF_ERROR_DATA_SIZE:
case BLE_ERROR_INVALID_CONN_HANDLE:
case BLE_ERROR_GATTS_INVALID_ATTR_TYPE:
return BLE_ERROR_PARAM_OUT_OF_RANGE;
case NRF_ERROR_FORBIDDEN:
return BLE_ERROR_OPERATION_NOT_PERMITTED;
default:
return BLE_ERROR_UNSPECIFIED;
}
}
} // end of anonymous namespace
/**************************************************************************/
/*!
@brief Adds a new service to the GATT table on the peripheral
@returns ble_error_t
@retval BLE_ERROR_NONE
Everything executed properly
@section EXAMPLE
@code
@endcode
*/
/**************************************************************************/
ble_error_t nRF5xGattServer::addService(GattService &service)
{
/* ToDo: Make sure this service UUID doesn't already exist (?) */
/* ToDo: Basic validation */
/* Add the service to the nRF51 */
ble_uuid_t nordicUUID;
nordicUUID = custom_convert_to_nordic_uuid(service.getUUID());
uint16_t serviceHandle;
ASSERT_TRUE( ERROR_NONE ==
sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&nordicUUID,
&serviceHandle),
BLE_ERROR_PARAM_OUT_OF_RANGE );
service.setHandle(serviceHandle);
/* Add characteristics to the service */
for (uint8_t i = 0; i < service.getCharacteristicCount(); i++) {
if (characteristicCount >= BLE_TOTAL_CHARACTERISTICS) {
return BLE_ERROR_NO_MEM;
}
GattCharacteristic *p_char = service.getCharacteristic(i);
GattAttribute *p_description_descriptor = NULL;
GattAttribute *p_presentation_format_descriptor = NULL;
/* Skip any incompletely defined, read-only characteristics. */
if ((p_char->getValueAttribute().getValuePtr() == NULL) &&
(p_char->getValueAttribute().getLength() == 0) &&
(p_char->getProperties() == GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ)) {
continue;
}
nordicUUID = custom_convert_to_nordic_uuid(p_char->getValueAttribute().getUUID());
/* The user-description and presentation-format descriptors are special cases
* that need to be handled at the time of adding each characteristic. The
* following block is meant to discover their presence. */
const uint8_t *userDescriptionDescriptorValuePtr = NULL;
uint16_t userDescriptionDescriptorValueLen = 0;
const uint8_t *presentationFormatDescriptorValuePtr = NULL;
uint16_t presentationFormatDescriptorValueLen = 0;
for (uint8_t j = 0; j < p_char->getDescriptorCount(); j++) {
GattAttribute *p_desc = p_char->getDescriptor(j);
if (p_desc->getUUID() == BLE_UUID_DESCRIPTOR_CHAR_USER_DESC) {
p_description_descriptor = p_desc;
userDescriptionDescriptorValuePtr = p_desc->getValuePtr();
userDescriptionDescriptorValueLen = p_desc->getLength();
}
if (p_desc->getUUID() == BLE_UUID_DESCRIPTOR_CHAR_PRESENTATION_FORMAT) {
p_presentation_format_descriptor = p_desc;
presentationFormatDescriptorValuePtr = p_desc->getValuePtr();
presentationFormatDescriptorValueLen = p_desc->getLength();
}
}
ASSERT_TRUE ( ERROR_NONE ==
custom_add_in_characteristic(BLE_GATT_HANDLE_INVALID,
&nordicUUID,
p_char->getProperties(),
p_char->getRequiredSecurity(),
p_char->getValueAttribute().getValuePtr(),
p_char->getValueAttribute().getLength(),
p_char->getValueAttribute().getMaxLength(),
p_char->getValueAttribute().hasVariableLength(),
userDescriptionDescriptorValuePtr,
userDescriptionDescriptorValueLen,
presentationFormatDescriptorValuePtr,
presentationFormatDescriptorValueLen,
p_char->isReadAuthorizationEnabled(),
p_char->isWriteAuthorizationEnabled(),
&nrfCharacteristicHandles[characteristicCount]),
BLE_ERROR_PARAM_OUT_OF_RANGE );
/* Update the characteristic handle */
p_characteristics[characteristicCount] = p_char;
p_char->getValueAttribute().setHandle(nrfCharacteristicHandles[characteristicCount].value_handle);
if (p_description_descriptor) {
p_description_descriptor->setHandle(
nrfCharacteristicHandles[characteristicCount].user_desc_handle
);
}
if (p_presentation_format_descriptor) {
// The handle is not available from the SoftDevice
p_presentation_format_descriptor->setHandle(GattAttribute::INVALID_HANDLE);
}
characteristicCount++;
/* Add optional descriptors if any */
for (uint8_t j = 0; j < p_char->getDescriptorCount(); j++) {
if (descriptorCount >= BLE_TOTAL_DESCRIPTORS) {
return BLE_ERROR_NO_MEM;
}
GattAttribute *p_desc = p_char->getDescriptor(j);
/* skip the user-description or presentation-format descriptor here;
* they have already been handled when adding the characteristic (above). */
if (p_desc->getUUID() == BLE_UUID_DESCRIPTOR_CHAR_USER_DESC
|| p_desc->getUUID() == BLE_UUID_DESCRIPTOR_CHAR_PRESENTATION_FORMAT) {
continue;
}
nordicUUID = custom_convert_to_nordic_uuid(p_desc->getUUID());
ASSERT_TRUE(ERROR_NONE ==
custom_add_in_descriptor(BLE_GATT_HANDLE_INVALID,
&nordicUUID,
p_desc->getValuePtr(),
p_desc->getLength(),
p_desc->getMaxLength(),
p_desc->hasVariableLength(),
&nrfDescriptorHandles[descriptorCount]),
BLE_ERROR_PARAM_OUT_OF_RANGE);
p_descriptors[descriptorCount] = p_desc;
p_desc->setHandle(nrfDescriptorHandles[descriptorCount]);
descriptorCount++;
}
}
serviceCount++;
return BLE_ERROR_NONE;
}
/**************************************************************************/
/*!
@brief Reads the value of a characteristic, based on the service
and characteristic index fields
@param[in] attributeHandle
The handle of the GattCharacteristic to read from
@param[in] buffer
Buffer to hold the the characteristic's value
(raw byte array in LSB format)
@param[in/out] len
input: Length in bytes to be read.
output: Total length of attribute value upon successful return.
@returns ble_error_t
@retval BLE_ERROR_NONE
Everything executed properly
*/
/**************************************************************************/
ble_error_t nRF5xGattServer::read(GattAttribute::Handle_t attributeHandle, uint8_t buffer[], uint16_t *lengthP)
{
return read(BLE_CONN_HANDLE_INVALID, attributeHandle, buffer, lengthP);
}
ble_error_t nRF5xGattServer::read(Gap::Handle_t connectionHandle, GattAttribute::Handle_t attributeHandle, uint8_t buffer[], uint16_t *lengthP)
{
ble_gatts_value_t value = {
.len = *lengthP,
.offset = 0,
.p_value = buffer,
};
ASSERT_TRUE( ERROR_NONE ==
sd_ble_gatts_value_get(connectionHandle, attributeHandle, &value),
BLE_ERROR_PARAM_OUT_OF_RANGE);
*lengthP = value.len;
return BLE_ERROR_NONE;
}
/**************************************************************************/
/*!
@brief Updates the value of a characteristic, based on the service
and characteristic index fields
@param[in] charHandle
The handle of the GattCharacteristic to write to
@param[in] buffer
Data to use when updating the characteristic's value
(raw byte array in LSB format)
@param[in] len
The number of bytes in buffer
@returns ble_error_t
@retval BLE_ERROR_NONE
Everything executed properly
*/
/**************************************************************************/
ble_error_t nRF5xGattServer::write(GattAttribute::Handle_t attributeHandle, const uint8_t buffer[], uint16_t len, bool localOnly)
{
return write(BLE_CONN_HANDLE_INVALID, attributeHandle, buffer, len, localOnly);
}
ble_error_t nRF5xGattServer::write(Gap::Handle_t connectionHandle, GattAttribute::Handle_t attributeHandle, const uint8_t buffer[], uint16_t len, bool localOnly)
{
ble_error_t returnValue = BLE_ERROR_NONE;
ble_gatts_value_t value = {
.len = len,
.offset = 0,
.p_value = const_cast<uint8_t *>(buffer),
};
if (localOnly) {
/* Only update locally regardless of notify/indicate */
ASSERT_INT( ERROR_NONE,
sd_ble_gatts_value_set(connectionHandle, attributeHandle, &value),
BLE_ERROR_PARAM_OUT_OF_RANGE );
return BLE_ERROR_NONE;
}
int characteristicIndex = resolveValueHandleToCharIndex(attributeHandle);
if ((characteristicIndex != -1) &&
(p_characteristics[characteristicIndex]->getProperties() & (GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY))) {
/* HVX update for the characteristic value */
ble_gatts_hvx_params_t hvx_params;
hvx_params.handle = attributeHandle;
hvx_params.type =
(p_characteristics[characteristicIndex]->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) ? BLE_GATT_HVX_NOTIFICATION : BLE_GATT_HVX_INDICATION;
hvx_params.offset = 0;
hvx_params.p_data = const_cast<uint8_t *>(buffer);
hvx_params.p_len = &len;
if (connectionHandle == BLE_CONN_HANDLE_INVALID) { /* use the default connection handle if the caller hasn't specified a valid connectionHandle. */
nRF5xGap &gap = (nRF5xGap &) nRF5xn::Instance(BLE::DEFAULT_INSTANCE).getGap();
connectionHandle = gap.getConnectionHandle();
}
bool updatesEnabled = false;
if (connectionHandle != BLE_CONN_HANDLE_INVALID) {
ble_error_t err = areUpdatesEnabled(connectionHandle, attributeHandle, &updatesEnabled);
// FIXME: The softdevice allocates and populates CCCD when the client
// interract with them. Checking for updates may return an out of
// range error in such case.
if(err && err != BLE_ERROR_PARAM_OUT_OF_RANGE) {
return err;
}
}
if (updatesEnabled) {
error_t error = (error_t) sd_ble_gatts_hvx(connectionHandle, &hvx_params);
if (error != ERROR_NONE) {
switch (error) {
case ERROR_BLE_NO_TX_BUFFERS: /* Notifications consume application buffers. The return value can be used for resending notifications. */
case ERROR_BUSY:
returnValue = BLE_STACK_BUSY;
break;
case ERROR_INVALID_STATE:
case ERROR_BLEGATTS_SYS_ATTR_MISSING:
returnValue = BLE_ERROR_INVALID_STATE;
break;
default :
ASSERT_INT( ERROR_NONE,
sd_ble_gatts_value_set(connectionHandle, attributeHandle, &value),
BLE_ERROR_PARAM_OUT_OF_RANGE );
/* Notifications consume application buffers. The return value can
* be used for resending notifications. */
returnValue = BLE_STACK_BUSY;
break;
}
}
} else {
returnValue = set_attribute_value(connectionHandle, attributeHandle, &value);
}
} else {
returnValue = set_attribute_value(connectionHandle, attributeHandle, &value);
}
return returnValue;
}
ble_error_t nRF5xGattServer::areUpdatesEnabled(const GattCharacteristic &characteristic, bool *enabledP)
{
/* Forward the call with the default connection handle. */
nRF5xGap &gap = (nRF5xGap &) nRF5xn::Instance(BLE::DEFAULT_INSTANCE).getGap();
return areUpdatesEnabled(gap.getConnectionHandle(), characteristic, enabledP);
}
ble_error_t nRF5xGattServer::areUpdatesEnabled(Gap::Handle_t connectionHandle, const GattCharacteristic &characteristic, bool *enabledP)
{
return areUpdatesEnabled(connectionHandle, characteristic.getValueHandle(), enabledP);
}
ble_error_t nRF5xGattServer::areUpdatesEnabled(Gap::Handle_t connectionHandle, GattAttribute::Handle_t attributeHandle, bool *enabledP)
{
int characteristicIndex = resolveValueHandleToCharIndex(attributeHandle);
if (characteristicIndex == -1) {
return BLE_ERROR_INVALID_PARAM;
}
/* Read the cccd value from the GATT server. */
GattAttribute::Handle_t cccdHandle = nrfCharacteristicHandles[characteristicIndex].cccd_handle;
uint16_t cccdValue;
uint16_t length = sizeof(cccdValue);
ble_error_t rc = read(connectionHandle, cccdHandle, reinterpret_cast<uint8_t *>(&cccdValue), &length);
if (rc != BLE_ERROR_NONE) {
return rc;
}
if (length != sizeof(cccdValue)) {
return BLE_ERROR_INVALID_STATE;
}
/* Check for NOTFICATION or INDICATION in CCCD. */
if ((cccdValue & BLE_GATT_HVX_NOTIFICATION) || (cccdValue & BLE_GATT_HVX_INDICATION)) {
*enabledP = true;
}
return BLE_ERROR_NONE;
}
/**************************************************************************/
/*!
@brief Clear nRF5xGattServer's state.
@returns ble_error_t
@retval BLE_ERROR_NONE
Everything executed properly
*/
/**************************************************************************/
ble_error_t nRF5xGattServer::reset(void)
{
/* Clear all state that is from the parent, including private members */
if (GattServer::reset() != BLE_ERROR_NONE) {
return BLE_ERROR_INVALID_STATE;
}
/* Clear derived class members */
memset(p_characteristics, 0, sizeof(p_characteristics));
memset(p_descriptors, 0, sizeof(p_descriptors));
memset(nrfCharacteristicHandles, 0, sizeof(ble_gatts_char_handles_t));
memset(nrfDescriptorHandles, 0, sizeof(nrfDescriptorHandles));
descriptorCount = 0;
releaseAllWriteRequests();
return BLE_ERROR_NONE;
}
/**************************************************************************/
/*!
@brief Callback handler for events getting pushed up from the SD
*/
/**************************************************************************/
void nRF5xGattServer::hwCallback(const ble_evt_t *p_ble_evt)
{
GattAttribute::Handle_t handle_value;
GattServerEvents::gattEvent_t eventType;
const ble_gatts_evt_t *gattsEventP = &p_ble_evt->evt.gatts_evt;
switch (p_ble_evt->header.evt_id) {
case BLE_GATTS_EVT_WRITE: {
/* There are 2 use case here: Values being updated & CCCD (indicate/notify) enabled */
/* 1.) Handle CCCD changes */
handle_value = gattsEventP->params.write.handle;
int characteristicIndex = resolveCCCDHandleToCharIndex(handle_value);
if ((characteristicIndex != -1) &&
(p_characteristics[characteristicIndex]->getProperties() &
(GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY))) {
uint16_t cccd_value = (gattsEventP->params.write.data[1] << 8) | gattsEventP->params.write.data[0]; /* Little Endian but M0 may be mis-aligned */
if (((p_characteristics[characteristicIndex]->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE) && (cccd_value & BLE_GATT_HVX_INDICATION)) ||
((p_characteristics[characteristicIndex]->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) && (cccd_value & BLE_GATT_HVX_NOTIFICATION))) {
eventType = GattServerEvents::GATT_EVENT_UPDATES_ENABLED;
} else {
eventType = GattServerEvents::GATT_EVENT_UPDATES_DISABLED;
}
handleEvent(eventType, p_characteristics[characteristicIndex]->getValueHandle());
return;
}
/* 2.) Changes to the characteristic value will be handled with other events below */
eventType = GattServerEvents::GATT_EVENT_DATA_WRITTEN;
}
break;
case BLE_GATTS_EVT_HVC:
/* Indication confirmation received */
eventType = GattServerEvents::GATT_EVENT_CONFIRMATION_RECEIVED;
handle_value = gattsEventP->params.hvc.handle;
break;
#if NRF_SD_BLE_API_VERSION >= 4 // This event has been renamed in API V4+
case BLE_GATTS_EVT_HVN_TX_COMPLETE: {
handleDataSentEvent(p_ble_evt->evt.gatts_evt.params.hvn_tx_complete.count);
return;
}
#else
case BLE_EVT_TX_COMPLETE: {
handleDataSentEvent(p_ble_evt->evt.common_evt.params.tx_complete.count);
return;
}
#endif
case BLE_GATTS_EVT_SYS_ATTR_MISSING:
sd_ble_gatts_sys_attr_set(gattsEventP->conn_handle, NULL, 0, 0);
return;
case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
switch (gattsEventP->params.authorize_request.type) {
case BLE_GATTS_AUTHORIZE_TYPE_READ:
eventType = GattServerEvents::GATT_EVENT_READ_AUTHORIZATION_REQ;
handle_value = gattsEventP->params.authorize_request.request.read.handle;
break;
case BLE_GATTS_AUTHORIZE_TYPE_WRITE:
eventType = GattServerEvents::GATT_EVENT_WRITE_AUTHORIZATION_REQ;
handle_value = gattsEventP->params.authorize_request.request.write.handle;
break;
default:
return;
}
break;
case BLE_EVT_USER_MEM_REQUEST: {
uint16_t conn_handle = p_ble_evt->evt.common_evt.conn_handle;
// allocate a new long request for this connection
// NOTE: we don't care about the result at this stage,
// it is not possible to cancel the operation anyway.
// If the request was not allocated then it will gracefully failled
// at subsequent stages.
allocateLongWriteRequest(conn_handle);
sd_ble_user_mem_reply(conn_handle, NULL);
return;
}
default:
return;
}
int characteristicIndex = resolveValueHandleToCharIndex(handle_value);
if (characteristicIndex == -1) {
// filter out the case were the request is a long one,
// and there is no attribute handle provided
uint8_t write_op = gattsEventP->params.authorize_request.request.write.op;
if (eventType != GattServerEvents::GATT_EVENT_WRITE_AUTHORIZATION_REQ ||
(write_op != BLE_GATTS_OP_EXEC_WRITE_REQ_NOW &&
write_op != BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)) {
return;
}
}
/* Find index (charHandle) in the pool */
switch (eventType) {
case GattServerEvents::GATT_EVENT_DATA_WRITTEN: {
GattWriteCallbackParams cbParams = {
/* .connHandle = */ gattsEventP->conn_handle,
/* .handle = */ handle_value,
/* .writeOp = */ static_cast<GattWriteCallbackParams::WriteOp_t>(gattsEventP->params.write.op),
/* .offset = */ gattsEventP->params.write.offset,
/* .len = */ gattsEventP->params.write.len,
/* .data = */ gattsEventP->params.write.data
};
handleDataWrittenEvent(&cbParams);
break;
}
case GattServerEvents::GATT_EVENT_WRITE_AUTHORIZATION_REQ: {
uint16_t conn_handle = gattsEventP->conn_handle;
const ble_gatts_evt_write_t& input_req = gattsEventP->params.authorize_request.request.write;
const uint16_t max_size = getBiggestCharacteristicSize();
// this is a long write request, handle it here.
switch (input_req.op) {
case BLE_GATTS_OP_PREP_WRITE_REQ: {
// verify that the request is not outside of the possible range
if ((input_req.offset + input_req.len) > max_size) {
sd_ble_gatts_rw_authorize_reply(conn_handle, &write_auth_invalid_offset_reply);
releaseLongWriteRequest(conn_handle);
return;
}
// find the write request
long_write_request_t* req = findLongWriteRequest(conn_handle);
if (!req) {
sd_ble_gatts_rw_authorize_reply(conn_handle, &write_auth_invalid_reply);
return;
}
// initialize the first request by setting the offset
if (req->length == 0) {
req->attr_handle = input_req.handle;
req->offset = input_req.offset;
} else {
// it should be the subsequent write
if ((req->offset + req->length) != input_req.offset) {
sd_ble_gatts_rw_authorize_reply(conn_handle, &write_auth_invalid_offset_reply);
releaseLongWriteRequest(conn_handle);
return;
}
// it is not allowed to write multiple characteristic with the same request
if (input_req.handle != req->attr_handle) {
sd_ble_gatts_rw_authorize_reply(conn_handle, &write_auth_invalid_reply);
releaseLongWriteRequest(conn_handle);
return;
}
}
// start the copy of what is in input
memcpy(req->data + req->length, input_req.data, input_req.len);
// update the lenght of the data written
req->length = req->length + input_req.len;
// success, signal it to the softdevice
ble_gatts_rw_authorize_reply_params_t reply = {
.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
.params = {
.write = {
.gatt_status = BLE_GATT_STATUS_SUCCESS,
.update = 1,
.offset = input_req.offset,
.len = input_req.len,
.p_data = input_req.data
}
}
};
sd_ble_gatts_rw_authorize_reply(conn_handle, &reply);
} return;
case BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL: {
releaseLongWriteRequest(conn_handle);
sd_ble_gatts_rw_authorize_reply(conn_handle, &write_auth_succes_reply);
} return;
case BLE_GATTS_OP_EXEC_WRITE_REQ_NOW: {
long_write_request_t* req = findLongWriteRequest(conn_handle);
if (!req) {
sd_ble_gatts_rw_authorize_reply(conn_handle, &write_auth_invalid_reply);
return;
}
GattWriteAuthCallbackParams cbParams = {
.connHandle = conn_handle,
.handle = req->attr_handle,
.offset = req->offset,
.len = req->length,
.data = req->data,
.authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS /* the callback handler must leave this member
* set to AUTH_CALLBACK_REPLY_SUCCESS if the client
* request is to proceed. */
};
uint16_t write_authorization = p_characteristics[characteristicIndex]->authorizeWrite(&cbParams);
// the user code didn't provide the write authorization,
// just leave here.
if (write_authorization != AUTH_CALLBACK_REPLY_SUCCESS) {
// report the status of the operation in any cases
sd_ble_gatts_rw_authorize_reply(conn_handle, &write_auth_invalid_reply);
releaseLongWriteRequest(conn_handle);
return;
}
// FIXME can't use ::write here, this function doesn't take the offset into account ...
ble_gatts_value_t value = {
.len = req->length,
.offset = req->offset,
.p_value = req->data
};
uint32_t update_err = sd_ble_gatts_value_set(conn_handle, req->attr_handle, &value);
if (update_err) {
sd_ble_gatts_rw_authorize_reply(conn_handle, &write_auth_invalid_reply);
releaseLongWriteRequest(conn_handle);
return;
}
sd_ble_gatts_rw_authorize_reply(conn_handle, &write_auth_succes_reply);
GattWriteCallbackParams writeParams = {
/* .connHandle = */ conn_handle,
/* .handle = */ req->attr_handle,
/* .writeOp = */ static_cast<GattWriteCallbackParams::WriteOp_t>(input_req.op),
/* .offset = */ req->offset,
/* .len = */ req->length,
/* .data = */ req->data,
};
handleDataWrittenEvent(&writeParams);
releaseLongWriteRequest(conn_handle);
} return;
}
GattWriteAuthCallbackParams cbParams = {
.connHandle = gattsEventP->conn_handle,
.handle = handle_value,
.offset = gattsEventP->params.authorize_request.request.write.offset,
.len = gattsEventP->params.authorize_request.request.write.len,
.data = gattsEventP->params.authorize_request.request.write.data,
.authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS /* the callback handler must leave this member
* set to AUTH_CALLBACK_REPLY_SUCCESS if the client
* request is to proceed. */
};
ble_gatts_rw_authorize_reply_params_t reply = {
.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
.params = {
.write = {
.gatt_status = p_characteristics[characteristicIndex]->authorizeWrite(&cbParams),
.update = 1,
.offset = cbParams.offset,
.len = cbParams.len,
.p_data = cbParams.data
}
}
};
if (reply.params.write.gatt_status != BLE_GATT_STATUS_SUCCESS)
{
reply.params.write.update = 0;
}
sd_ble_gatts_rw_authorize_reply(gattsEventP->conn_handle, &reply);
/*
* If write-authorization is enabled for a characteristic,
* AUTHORIZATION_REQ event (if replied with true) is *not*
* followed by another DATA_WRITTEN event; so we still need
* to invoke handleDataWritten(), much the same as we would
* have done if write-authorization had not been enabled.
*/
if (reply.params.write.gatt_status == BLE_GATT_STATUS_SUCCESS) {
GattWriteCallbackParams cbParams = {
/* .connHandle = */ gattsEventP->conn_handle,
/* .handle = */ handle_value,
/* .writeOp = */ static_cast<GattWriteCallbackParams::WriteOp_t>(gattsEventP->params.authorize_request.request.write.op),
/* .offset = */ gattsEventP->params.authorize_request.request.write.offset,
/* .len = */ gattsEventP->params.authorize_request.request.write.len,
/* .data = */ gattsEventP->params.authorize_request.request.write.data,
};
handleDataWrittenEvent(&cbParams);
}
break;
}
case GattServerEvents::GATT_EVENT_READ_AUTHORIZATION_REQ: {
GattReadAuthCallbackParams cbParams = {
.connHandle = gattsEventP->conn_handle,
.handle = handle_value,
.offset = gattsEventP->params.authorize_request.request.read.offset,
.len = 0,
.data = NULL,
.authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS /* the callback handler must leave this member
* set to AUTH_CALLBACK_REPLY_SUCCESS if the client
* request is to proceed. */
};
ble_gatts_rw_authorize_reply_params_t reply = {
.type = BLE_GATTS_AUTHORIZE_TYPE_READ,
.params = {
.read = {
.gatt_status = p_characteristics[characteristicIndex]->authorizeRead(&cbParams)
}
}
};
if (cbParams.authorizationReply == BLE_GATT_STATUS_SUCCESS) {
if (cbParams.data != NULL) {
reply.params.read.update = 1;
reply.params.read.offset = cbParams.offset;
reply.params.read.len = cbParams.len;
reply.params.read.p_data = cbParams.data;
}
}
sd_ble_gatts_rw_authorize_reply(gattsEventP->conn_handle, &reply);
break;
}
default:
handleEvent(eventType, handle_value);
break;
}
}
uint16_t nRF5xGattServer::getBiggestCharacteristicSize() const {
uint16_t result = 0;
for (size_t i = 0; i < characteristicCount; ++i) {
uint16_t current_size = p_characteristics[i]->getValueAttribute().getMaxLength();
if (current_size > result) {
result = current_size;
}
}
return result;
}
nRF5xGattServer::long_write_request_t* nRF5xGattServer::allocateLongWriteRequest(uint16_t connection_handle) {
for (size_t i = 0; i < TOTAL_CONCURRENT_LONG_WRITE_REQUESTS; ++i) {
long_write_request_t& req = long_write_requests[i];
if (req.data == NULL) {
uint16_t block_size = getBiggestCharacteristicSize();
req.data = static_cast<uint8_t*>(malloc(block_size));
req.offset = 0;
req.length = 0;
req.conn_handle = connection_handle;
return &req;
}
}
// if nothing has been found then return null
return NULL;
}
bool nRF5xGattServer::releaseLongWriteRequest(uint16_t connection_handle) {
long_write_request_t* req = findLongWriteRequest(connection_handle);
if (!req) {
return false;
}
free(req->data);
req->data = NULL;
// the other fields are not relevant, return now
return true;
}
nRF5xGattServer::long_write_request_t* nRF5xGattServer::findLongWriteRequest(uint16_t connection_handle) {
for (size_t i = 0; i < TOTAL_CONCURRENT_LONG_WRITE_REQUESTS; ++i) {
long_write_request_t& req = long_write_requests[i];
if (req.data != NULL && req.conn_handle == connection_handle) {
return &req;
}
}
// if nothing has been found then return null
return NULL;
}
void nRF5xGattServer::releaseAllWriteRequests() {
for (size_t i = 0; i < TOTAL_CONCURRENT_LONG_WRITE_REQUESTS; ++i) {
long_write_request_t& req = long_write_requests[i];
if (req.data != NULL) {
free(req.data);
req.data = NULL;
}
}
}

View File

@ -0,0 +1,167 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 __NRF51822_GATT_SERVER_H__
#define __NRF51822_GATT_SERVER_H__
#include <stddef.h>
#include "ble/blecommon.h"
#include "headers/nrf_ble.h" /* nordic ble */
#include "ble/Gap.h"
#include "ble/GattServer.h"
class nRF5xGattServer : public GattServer
{
public:
/* Functions that must be implemented from GattServer */
virtual ble_error_t addService(GattService &);
virtual ble_error_t read(GattAttribute::Handle_t attributeHandle, uint8_t buffer[], uint16_t *lengthP);
virtual ble_error_t read(Gap::Handle_t connectionHandle, GattAttribute::Handle_t attributeHandle, uint8_t buffer[], uint16_t *lengthP);
virtual ble_error_t write(GattAttribute::Handle_t, const uint8_t[], uint16_t, bool localOnly = false);
virtual ble_error_t write(Gap::Handle_t connectionHandle, GattAttribute::Handle_t, const uint8_t[], uint16_t, bool localOnly = false);
virtual ble_error_t areUpdatesEnabled(const GattCharacteristic &characteristic, bool *enabledP);
virtual ble_error_t areUpdatesEnabled(Gap::Handle_t connectionHandle, const GattCharacteristic &characteristic, bool *enabledP);
virtual ble_error_t reset(void);
/* nRF51 Functions */
void eventCallback(void);
void hwCallback(const ble_evt_t *p_ble_evt);
private:
const static unsigned BLE_TOTAL_CHARACTERISTICS = 20;
const static unsigned BLE_TOTAL_DESCRIPTORS = 8;
const static unsigned TOTAL_CONCURRENT_LONG_WRITE_REQUESTS = 3;
private:
struct long_write_request_t {
// the connection handle for a long write request
uint16_t conn_handle;
// the attribute handle for the long write request
// This implementation folow the bluetooth route
// where a write request target a single characteristic
// (see BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] - 4.9.4)
uint16_t attr_handle;
// offset of the transaction
uint16_t offset;
// length of the data
uint16_t length;
// current data
uint8_t* data;
};
private:
/**
* resolve a value attribute to its owning characteristic.
* @param valueHandle the value handle to be resolved.
* @return characteristic index if a resolution is found, else -1.
*/
int resolveValueHandleToCharIndex(GattAttribute::Handle_t valueHandle) const {
unsigned charIndex;
for (charIndex = 0; charIndex < characteristicCount; charIndex++) {
if (nrfCharacteristicHandles[charIndex].value_handle == valueHandle) {
return charIndex;
}
}
return -1;
}
/**
* resolve a CCCD attribute handle to its owning characteristic.
* @param cccdHandle the CCCD handle to be resolved.
* @return characteristic index if a resolution is found, else -1.
*/
int resolveCCCDHandleToCharIndex(GattAttribute::Handle_t cccdHandle) const {
unsigned charIndex;
for (charIndex = 0; charIndex < characteristicCount; charIndex++) {
if (nrfCharacteristicHandles[charIndex].cccd_handle == cccdHandle) {
return charIndex;
}
}
return -1;
}
/**
* Return the biggest size used by a characteristic in the server
*/
uint16_t getBiggestCharacteristicSize() const;
/**
* Allocate a new write long request. return null if no requests are available.
* @param connection_handle The connection handle to be associated with the request.
* @return the allocated request or NULL if no requests are available.
*/
long_write_request_t* allocateLongWriteRequest(uint16_t connection_handle);
/**
* Release a long write request and free a slot for subsequent write long requests.
* @param connection_handle The connection handle associated with the request
* @return true if the request where allocated and was release, false otherwise.
*/
bool releaseLongWriteRequest(uint16_t connection_handle);
/**
* Find a long write request from a characteristic handle
* @param connection_handle The connection handle associated with the request.
* @return a pointer to the request if found otherwise NULL.
*/
long_write_request_t* findLongWriteRequest(uint16_t connection_handle);
/**
* Release all pending write requests.
*/
void releaseAllWriteRequests();
/**
* Query if updates of a characteristics are enabled for a given connection.
*/
ble_error_t areUpdatesEnabled(
Gap::Handle_t connectionHandle,
GattAttribute::Handle_t valueHandle,
bool *enabledP
);
private:
GattCharacteristic *p_characteristics[BLE_TOTAL_CHARACTERISTICS];
ble_gatts_char_handles_t nrfCharacteristicHandles[BLE_TOTAL_CHARACTERISTICS];
GattAttribute *p_descriptors[BLE_TOTAL_DESCRIPTORS];
uint8_t descriptorCount;
uint16_t nrfDescriptorHandles[BLE_TOTAL_DESCRIPTORS];
long_write_request_t long_write_requests[TOTAL_CONCURRENT_LONG_WRITE_REQUESTS];
/*
* Allow instantiation from nRF5xn when required.
*/
friend class nRF5xn;
nRF5xGattServer() : GattServer(), p_characteristics(), nrfCharacteristicHandles(), p_descriptors(), descriptorCount(0), nrfDescriptorHandles(), long_write_requests() {
/* empty */
}
private:
nRF5xGattServer(const nRF5xGattServer &);
const nRF5xGattServer& operator=(const nRF5xGattServer &);
};
#endif // ifndef __NRF51822_GATT_SERVER_H__

View File

@ -0,0 +1,194 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 __NRF51822_SECURITY_MANAGER_H__
#define __NRF51822_SECURITY_MANAGER_H__
#include <stddef.h>
#include "nRF5xGap.h"
#include "ble/SecurityManager.h"
#include "btle_security.h"
class nRF5xSecurityManager : public SecurityManager
{
public:
/* Functions that must be implemented from SecurityManager */
virtual ble_error_t init(bool enableBonding,
bool requireMITM,
SecurityIOCapabilities_t iocaps,
const Passkey_t passkey) {
return btle_initializeSecurity(enableBonding, requireMITM, iocaps, passkey);
}
virtual ble_error_t getLinkSecurity(Gap::Handle_t connectionHandle, LinkSecurityStatus_t *securityStatusP) {
return btle_getLinkSecurity(connectionHandle, securityStatusP);
}
virtual ble_error_t setLinkSecurity(Gap::Handle_t connectionHandle, SecurityMode_t securityMode) {
return btle_setLinkSecurity(connectionHandle, securityMode);
}
virtual ble_error_t purgeAllBondingState(void) {
return btle_purgeAllBondingState();
}
#if (NRF_SD_BLE_API_VERSION <= 2)
/**
* @brief Returns a list of addresses from peers in the stacks bond table.
*
* @param[in/out] addresses
* (on input) @ref Gap::Whitelist_t structure where at
* most addresses.capacity addresses from bonded peers will
* be stored.
* (on output) A copy of the addresses from bonded peers.
*
* @return
* BLE_ERROR_NONE if successful.
*/
virtual ble_error_t getAddressesFromBondTable(Gap::Whitelist_t &addresses) const {
uint8_t i;
ble_gap_whitelist_t whitelistFromBondTable;
ble_gap_addr_t *addressPtr[YOTTA_CFG_WHITELIST_MAX_SIZE];
ble_gap_irk_t *irkPtr[YOTTA_CFG_IRK_TABLE_MAX_SIZE];
/* Initialize the structure so that we get as many addreses as the whitelist can hold */
whitelistFromBondTable.addr_count = YOTTA_CFG_IRK_TABLE_MAX_SIZE;
whitelistFromBondTable.pp_addrs = addressPtr;
whitelistFromBondTable.irk_count = YOTTA_CFG_IRK_TABLE_MAX_SIZE;
whitelistFromBondTable.pp_irks = irkPtr;
ble_error_t error = createWhitelistFromBondTable(whitelistFromBondTable);
if (error != BLE_ERROR_NONE) {
addresses.size = 0;
return error;
}
/* Put all the addresses in the structure */
for (i = 0; i < whitelistFromBondTable.addr_count; ++i) {
if (i >= addresses.capacity) {
/* Ran out of space in the output Gap::Whitelist_t */
addresses.size = i;
return BLE_ERROR_NONE;
}
memcpy(&addresses.addresses[i], whitelistFromBondTable.pp_addrs[i], sizeof(BLEProtocol::Address_t));
}
/* Update the current address count */
addresses.size = i;
/* The assumption here is that the underlying implementation of
* createWhitelistFromBondTable() will not return the private resolvable
* addresses (which is the case in the SoftDevice). Rather it returns the
* IRKs, so we need to generate the private resolvable address by ourselves.
*/
for (i = 0; i < whitelistFromBondTable.irk_count; ++i) {
if (i + addresses.size >= addresses.capacity) {
/* Ran out of space in the output Gap::Whitelist_t */
addresses.size += i;
return BLE_ERROR_NONE;
}
btle_generateResolvableAddress(
*whitelistFromBondTable.pp_irks[i],
(ble_gap_addr_t &) addresses.addresses[i + addresses.size]
);
}
/* Update the current address count */
addresses.size += i;
return BLE_ERROR_NONE;
}
#else // -> NRF_SD_BLE_API_VERSION >= 3
/**
* @brief Returns a list of addresses from peers in the stacks bond table.
*
* @param[in/out] addresses
* (on input) @ref Gap::Whitelist_t structure where at
* most addresses.capacity addresses from bonded peers will
* be stored.
* (on output) A copy of the addresses from bonded peers.
*
* @retval BLE_ERROR_NONE if successful.
* @retval BLE_ERROR_UNSPECIFIED Bond data could not be found in flash or is inconsistent.
*/
virtual ble_error_t getAddressesFromBondTable(Gap::Whitelist_t &addresses) const {
return btle_getAddressesFromBondTable(addresses);
}
#endif // #if (NRF_SD_BLE_API_VERSION <= 2)
/**
* @brief Clear nRF5xSecurityManager's state.
*
* @return
* BLE_ERROR_NONE if successful.
*/
virtual ble_error_t reset(void)
{
if (SecurityManager::reset() != BLE_ERROR_NONE) {
return BLE_ERROR_INVALID_STATE;
}
return BLE_ERROR_NONE;
}
bool hasInitialized(void) const {
return btle_hasInitializedSecurity();
}
public:
/*
* Allow instantiation from nRF5xn when required.
*/
friend class nRF5xn;
nRF5xSecurityManager() {
/* empty */
}
private:
nRF5xSecurityManager(const nRF5xSecurityManager &);
const nRF5xSecurityManager& operator=(const nRF5xSecurityManager &);
#if (NRF_SD_BLE_API_VERSION <= 2)
/*
* Expose an interface that allows us to query the SoftDevice bond table
* and extract a whitelist.
*/
ble_error_t createWhitelistFromBondTable(ble_gap_whitelist_t &whitelistFromBondTable) const {
return btle_createWhitelistFromBondTable(&whitelistFromBondTable);
}
#endif
/*
* Given a BLE address and a IRK this function check whether the address
* can be generated from the IRK. To do so, this function uses the hash
* function and algorithm described in the Bluetooth low Energy
* Specification. Internally, Nordic SDK functions are used.
*/
bool matchAddressAndIrk(ble_gap_addr_t *address, ble_gap_irk_t *irk) const {
return btle_matchAddressAndIrk(address, irk);
}
/*
* Give nRF5xGap access to createWhitelistFromBondTable() and
* matchAddressAndIrk()
*/
friend class nRF5xGap;
};
#endif // ifndef __NRF51822_SECURITY_MANAGER_H__

View File

@ -0,0 +1,241 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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.
*/
#ifdef YOTTA_CFG_MBED_OS
#include "mbed-drivers/mbed.h"
#else
#include "mbed.h"
#endif
#include "nRF5xn.h"
#include "ble/blecommon.h"
#include "nrf_soc.h"
#include "btle/btle.h"
#include "btle/custom/custom_helper.h"
#include "nrf_delay.h"
extern "C" {
#include "nrf_sdh.h"
}
#include "nRF5XPalGattClient.h"
/**
* The singleton which represents the nRF51822 transport for the BLE.
*/
static nRF5xn& getDeviceInstance() {
static nRF5xn deviceInstance;
return deviceInstance;
}
/**
* BLE-API requires an implementation of the following function in order to
* obtain its transport handle.
*/
BLEInstanceBase *
createBLEInstance(void)
{
return &nRF5xn::Instance(BLE::DEFAULT_INSTANCE);
}
nRF5xn& nRF5xn::Instance(BLE::InstanceID_t instanceId)
{
return getDeviceInstance();
}
nRF5xn::nRF5xn(void) :
initialized(false),
instanceID(BLE::DEFAULT_INSTANCE),
gapInstance(),
gattServerInstance(NULL),
gattClient(&(ble::pal::vendor::nordic::nRF5XGattClient::get_client())),
securityManagerInstance(NULL)
{
}
nRF5xn::~nRF5xn(void)
{
}
const char *nRF5xn::getVersion(void)
{
if (!initialized) {
return "INITIALIZATION_INCOMPLETE";
}
static char versionString[32];
static bool versionFetched = false;
if (!versionFetched) {
ble_version_t version;
if ((sd_ble_version_get(&version) == NRF_SUCCESS) && (version.company_id == 0x0059)) {
switch (version.version_number) {
case 0x07:
case 0x08:
snprintf(versionString, sizeof(versionString), "Nordic BLE4.1 ver:%u fw:%04x", version.version_number, version.subversion_number);
break;
default:
snprintf(versionString, sizeof(versionString), "Nordic (spec unknown) ver:%u fw:%04x", version.version_number, version.subversion_number);
break;
}
versionFetched = true;
} else {
strncpy(versionString, "unknown", sizeof(versionString));
}
}
return versionString;
}
/**************************************************************************/
/*!
@brief Initialize the BLE stack.
@returns ble_error_t
@retval BLE_ERROR_NONE if everything executed properly and
BLE_ERROR_ALREADY_INITIALIZED if the stack has already
been initialized (possibly through a call to nRF5xn::init()).
BLE_ERROR_INTERNAL_STACK_FAILURE is returned if initialization
of the internal stack (SoftDevice) failed.
*/
/**************************************************************************/
ble_error_t nRF5xn::init(BLE::InstanceID_t instanceID, FunctionPointerWithContext<BLE::InitializationCompleteCallbackContext *> callback)
{
if (initialized) {
BLE::InitializationCompleteCallbackContext context = {
BLE::Instance(instanceID),
BLE_ERROR_ALREADY_INITIALIZED
};
callback.call(&context);
return BLE_ERROR_ALREADY_INITIALIZED;
}
instanceID = instanceID;
/* ToDo: Clear memory contents, reset the SD, etc. */
if (btle_init() != ERROR_NONE) {
return BLE_ERROR_INTERNAL_STACK_FAILURE;
}
initialized = true;
BLE::InitializationCompleteCallbackContext context = {
BLE::Instance(instanceID),
BLE_ERROR_NONE
};
callback.call(&context);
return BLE_ERROR_NONE;
}
/**************************************************************************/
/*!
@brief Purge the BLE stack of GATT and GAP state.
@returns ble_error_t
@retval BLE_ERROR_NONE
Everything executed properly
@note When using S110, GattClient::shutdown() will not be called
since Gatt client features are not supported.
*/
/**************************************************************************/
ble_error_t nRF5xn::shutdown(void)
{
if (!initialized) {
return BLE_ERROR_INITIALIZATION_INCOMPLETE;
}
/*
* Shutdown the SoftDevice first. This is because we need to disable all
* interrupts. Otherwise if we clear the BLE API and glue code first there
* will be many NULL references and no config information which could lead
* to errors if the shutdown process is interrupted.
*/
#if NRF_SD_BLE_API_VERSION >= 5
if (nrf_sdh_disable_request() != NRF_SUCCESS) {
return BLE_STACK_BUSY;
}
#else
if (softdevice_handler_sd_disable() != NRF_SUCCESS) {
return BLE_STACK_BUSY;
}
#endif
/* Shutdown the BLE API and nRF51 glue code */
ble_error_t error;
if (gattServerInstance != NULL) {
error = gattServerInstance->reset();
if (error != BLE_ERROR_NONE) {
return error;
}
}
if (securityManagerInstance != NULL) {
error = securityManagerInstance->reset();
if (error != BLE_ERROR_NONE) {
return error;
}
}
/* S110 does not support BLE client features, nothing to reset. */
#if !defined(TARGET_MCU_NRF51_16K_S110) && !defined(TARGET_MCU_NRF51_32K_S110)
error = getGattClient().reset();
if (error != BLE_ERROR_NONE) {
return error;
}
#endif
/* Gap instance is always present */
error = gapInstance.reset();
if (error != BLE_ERROR_NONE) {
return error;
}
custom_reset_128bits_uuid_table();
initialized = false;
return BLE_ERROR_NONE;
}
void
nRF5xn::waitForEvent(void)
{
processEvents();
sd_app_evt_wait();
}
void nRF5xn::processEvents() {
core_util_critical_section_enter();
while (isEventsSignaled) {
isEventsSignaled = false;
core_util_critical_section_exit();
#if NRF_SD_BLE_API_VERSION >= 5
// We use the "polling" dispatch model
// http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v14.2.0/group__nrf__sdh.html?cp=4_0_0_6_11_60_20#gab4d7be69304d4f5feefd1d440cc3e6c7
// This will process any pending events from the Softdevice
nrf_sdh_evts_poll();
#else
intern_softdevice_events_execute();
#endif
core_util_critical_section_enter();
}
core_util_critical_section_exit();
}

View File

@ -0,0 +1,179 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 __NRF51822_H__
#define __NRF51822_H__
#include "ble/BLE.h"
#include "ble/blecommon.h"
#include "ble/BLEInstanceBase.h"
#include "ble/generic/GenericGattClient.h"
#include "nRF5xGap.h"
#include "nRF5xGattServer.h"
#include "nRF5xSecurityManager.h"
#include "btle.h"
class nRF5xn : public BLEInstanceBase
{
public:
nRF5xn(void);
virtual ~nRF5xn(void);
virtual ble_error_t init(BLE::InstanceID_t instanceID, FunctionPointerWithContext<BLE::InitializationCompleteCallbackContext *> callback);
virtual bool hasInitialized(void) const {
return initialized;
}
virtual ble_error_t shutdown(void);
virtual const char *getVersion(void);
/**
* Accessors to GAP. This function checks whether gapInstance points to an
* object. If if does not, then the gapInstance is updated to
* &_getInstance before returning.
*
* @return A reference to GattServer.
*
* @note Unlike the GattClient, GattServer and SecurityManager, Gap is
* always needed in a BLE application. Therefore it is allocated
* statically.
*/
virtual Gap &getGap() {
return gapInstance;
};
/**
* Accessors to GATT Server. This function checks whether a GattServer
* object was previously instantiated. If such object does not exist, then
* it is created before returning.
*
* @return A reference to GattServer.
*/
virtual GattServer &getGattServer() {
if (gattServerInstance == NULL) {
gattServerInstance = new nRF5xGattServer();
}
return *gattServerInstance;
};
/**
* Accessors to GATT Client. This function checks whether a GattClient
* object was previously instantiated. If such object does not exist, then
* it is created before returning.
*
* @return A reference to GattClient.
*/
virtual GattClient &getGattClient() {
return gattClient;
}
/**
* Accessors to Security Manager. This function checks whether a SecurityManager
* object was previously instantiated. If such object does not exist, then
* it is created before returning.
*
* @return A reference to GattServer.
*/
virtual nRF5xSecurityManager &getSecurityManager() {
if (securityManagerInstance == NULL) {
securityManagerInstance = new nRF5xSecurityManager();
}
return *securityManagerInstance;
}
/**
* Accessors to GAP. This function checks whether gapInstance points to an
* object. If if does not, then the gapInstance is updated to
* &_getInstance before returning.
*
* @return A const reference to GattServer.
*
* @note Unlike the GattClient, GattServer and SecurityManager, Gap is
* always needed in a BLE application. Therefore it is allocated
* statically.
*
* @note The accessor is able to modify the object's state because the
* internal pointer has been declared mutable.
*/
virtual const nRF5xGap &getGap() const {
return gapInstance;
};
/**
* Accessors to GATT Server. This function checks whether a GattServer
* object was previously instantiated. If such object does not exist, then
* it is created before returning.
*
* @return A const reference to GattServer.
*
* @note The accessor is able to modify the object's state because the
* internal pointer has been declared mutable.
*/
virtual const nRF5xGattServer &getGattServer() const {
if (gattServerInstance == NULL) {
gattServerInstance = new nRF5xGattServer();
}
return *gattServerInstance;
};
/**
* Accessors to Security Manager. This function checks whether a SecurityManager
* object was previously instantiated. If such object does not exist, then
* it is created before returning.
*
* @return A const reference to GattServer.
*
* @note The accessor is able to modify the object's state because the
* internal pointer has been declared mutable.
*/
virtual const nRF5xSecurityManager &getSecurityManager() const {
if (securityManagerInstance == NULL) {
securityManagerInstance = new nRF5xSecurityManager();
}
return *securityManagerInstance;
}
virtual void waitForEvent(void);
virtual void processEvents();
public:
static nRF5xn& Instance(BLE::InstanceID_t instanceId);
private:
bool initialized;
BLE::InstanceID_t instanceID;
private:
mutable nRF5xGap gapInstance; /**< Gap instance whose reference is returned from a call to
* getGap(). Unlike the GattClient, GattServer and
* SecurityManager, Gap is always needed in a BLE application. */
private:
mutable nRF5xGattServer *gattServerInstance; /**< Pointer to the GattServer object instance.
* If NULL, then GattServer has not been initialized.
* The pointer has been declared as 'mutable' so that
* it can be assigned inside a 'const' function. */
ble::generic::GenericGattClient gattClient;
mutable nRF5xSecurityManager *securityManagerInstance; /**< Pointer to the SecurityManager object instance.
* If NULL, then SecurityManager has not been initialized.
* The pointer has been declared as 'mutable' so that
* it can be assigned inside a 'const' function. */
};
#endif

View File

@ -0,0 +1,136 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 _PROJECTCONFIG_H_
#define _PROJECTCONFIG_H_
#include "ble/GapAdvertisingData.h"
/*=========================================================================
MCU & BOARD SELCTION
CFG_BOARD is one of the value defined in board.h
-----------------------------------------------------------------------*/
#define CFG_BOARD BOARD_PCA10001
#define CFG_MCU_STRING "nRF51822"
/*=========================================================================*/
/*=========================================================================
CODE BASE VERSION SETTINGS
Please do not modify this version number. To set a version number
for your project or firmware, change the values in your 'boards/'
config file.
-----------------------------------------------------------------------*/
#define CFG_CODEBASE_VERSION_MAJOR 0
#define CFG_CODEBASE_VERSION_MINOR 1
#define CFG_CODEBASE_VERSION_REVISION 0
/*=========================================================================*/
/*=========================================================================
FIRMWARE VERSION SETTINGS
-----------------------------------------------------------------------*/
#define CFG_FIRMWARE_VERSION_MAJOR 0
#define CFG_FIRMWARE_VERSION_MINOR 0
#define CFG_FIRMWARE_VERSION_REVISION 0
/*=========================================================================*/
/*=========================================================================
DEBUG LEVEL
-----------------------------------------------------------------------
CFG_DEBUG Level 3: Full debug output, any failed assert
will produce a breakpoint for the
debugger
Level 2: ATTR_ALWAYS_INLINE is null, ASSERT
has text
Level 1: ATTR_ALWAYS_INLINE is an attribute,
ASSERT has no text
Level 0: No debug information generated
-----------------------------------------------------------------------*/
#define CFG_DEBUG (1)
#if (CFG_DEBUG > 3) || (CFG_DEBUG < 0)
#error "CFG_DEBUG must be a value between 0 (no debug) and 3"
#endif
/*=========================================================================*/
/*=========================================================================
GENERAL NRF51 PERIPHERAL SETTINGS
-----------------------------------------------------------------------
CFG_SCHEDULER_ENABLE Set this to 'true' or 'false' depending on
if you use the event scheduler or not
-----------------------------------------------------------------------*/
#define CFG_SCHEDULER_ENABLE false
/*------------------------------- GPIOTE ------------------------------*/
#define CFG_GPIOTE_MAX_USERS 1 /**< Maximum number of users of the GPIOTE handler. */
/*-------------------------------- TIMER ------------------------------*/
#define CFG_TIMER_PRESCALER 0 /**< Value of the RTC1 PRESCALER register. freq = (32768/(PRESCALER+1)) */
#define CFG_TIMER_MAX_INSTANCE 1 /**< Maximum number of simultaneously created timers. */
#define CFG_TIMER_OPERATION_QUEUE_SIZE 2 /**< Size of timer operation queues. */
/*=========================================================================*/
/*=========================================================================
BTLE SETTINGS
-----------------------------------------------------------------------*/
#define CFG_BLE_TX_POWER_LEVEL 0 /**< in dBm (Valid values are -40, -20, -16, -12, -8, -4, 0, 4) */
/*---------------------------- BOND MANAGER ---------------------------*/
#define CFG_BLE_BOND_FLASH_PAGE_BOND (BLE_FLASH_PAGE_END-1) /**< Flash page used for bond manager bonding information.*/
#define CFG_BLE_BOND_FLASH_PAGE_SYS_ATTR (BLE_FLASH_PAGE_END-3) /**< Flash page used for bond manager system attribute information. TODO check if we can use BLE_FLASH_PAGE_END-2*/
#define CFG_BLE_BOND_DELETE_BUTTON_NUM 0 /**< Button to press to delete bond details during init */
/*------------------------------ SECURITY -----------------------------*/
#define CFG_BLE_SEC_PARAM_MITM 0 /**< Man In The Middle protection not required. */
#define CFG_BLE_SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE /**< No I/O capabilities. */
#define CFG_BLE_SEC_PARAM_OOB 0 /**< Out Of Band data not available. */
#define CFG_BLE_SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */
#define CFG_BLE_SEC_PARAM_MAX_KEY_SIZE 16
/*--------------------------------- GAP -------------------------------*/
#define CFG_GAP_APPEARANCE GapAdvertisingData::GENERIC_TAG
#define CFG_GAP_LOCAL_NAME "nRF5x"
#define CFG_GAP_CONNECTION_MIN_INTERVAL_MS 50 /**< Minimum acceptable connection interval */
#define CFG_GAP_CONNECTION_MAX_INTERVAL_MS 500 /**< Maximum acceptable connection interval */
#define CFG_GAP_CONNECTION_SUPERVISION_TIMEOUT_MS 4000 /**< Connection supervisory timeout */
#define CFG_GAP_CONNECTION_SLAVE_LATENCY 0 /**< Slave Latency in number of connection events. */
#define CFG_GAP_ADV_INTERVAL_MS 25 /**< The advertising interval in miliseconds, should be multiply of 0.625 */
#define CFG_GAP_ADV_TIMEOUT_S 180 /**< The advertising timeout in units of seconds. */
/*=========================================================================*/
/*=========================================================================
VALIDATION
-----------------------------------------------------------------------*/
#if CFG_BLE_TX_POWER_LEVEL != -40 && CFG_BLE_TX_POWER_LEVEL != -20 && CFG_BLE_TX_POWER_LEVEL != -16 && CFG_BLE_TX_POWER_LEVEL != -12 && CFG_BLE_TX_POWER_LEVEL != -8 && CFG_BLE_TX_POWER_LEVEL != -4 && CFG_BLE_TX_POWER_LEVEL != 0 && CFG_BLE_TX_POWER_LEVEL != 4
#error "CFG_BLE_TX_POWER_LEVEL must be -40, -20, -16, -12, -8, -4, 0 or 4"
#endif
/*=========================================================================*/
#endif /* _PROJECTCONFIG_H_ */

View File

@ -0,0 +1,21 @@
# Copyright 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.
message("suppressing warnings from ble-nrf51822")
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
set_target_properties(ble-nrf51822
PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-variable -Wno-unused-parameter -Wno-unused-function -Wno-missing-field-initializers"
)
endif()

View File

@ -611,7 +611,7 @@ static err_t lpc_low_level_output(struct netif *netif, struct pbuf *p)
/* Wait until enough descriptors are available for the transfer. */
/* THIS WILL BLOCK UNTIL THERE ARE ENOUGH DESCRIPTORS AVAILABLE */
#if NO_SYS == 0
for (idx = 0; idx < dn; idx++) {
for (s32_t count = 0; count < dn; count++) {
osSemaphoreAcquire(lpc_enetif->xTXDCountSem.id, osWaitForever);
}
MBED_ASSERT(dn <= lpc_tx_ready(netif));

View File

@ -84,4 +84,4 @@ void HAL_ETH_MspDeInit(ETH_HandleTypeDef* heth)
/* Disable the Ethernet global Interrupt */
NVIC_DisableIRQ(ETH_IRQn);
}
}
}

View File

@ -78,4 +78,4 @@ void HAL_ETH_MspDeInit(ETH_HandleTypeDef* heth)
/* Disable the Ethernet global Interrupt */
NVIC_DisableIRQ(ETH_IRQn);
}
}
}

View File

@ -156,4 +156,4 @@ uint8_t mbed_otp_mac_address(char *mac)
memcpy(mac, _macAddr, 6);
return 1;
}
}

View File

@ -84,4 +84,4 @@ void HAL_ETH_MspDeInit(ETH_HandleTypeDef* heth)
/* Disable the Ethernet global Interrupt */
NVIC_DisableIRQ(ETH_IRQn);
}
}
}

View File

@ -84,4 +84,4 @@ void HAL_ETH_MspDeInit(ETH_HandleTypeDef* heth)
/* Disable the Ethernet global Interrupt */
NVIC_DisableIRQ(ETH_IRQn);
}
}
}

View File

@ -86,4 +86,4 @@ void HAL_ETH_MspDeInit(ETH_HandleTypeDef* heth)
/* Disable the Ethernet global Interrupt */
NVIC_DisableIRQ(ETH_IRQn);
}
}
}

View File

@ -86,4 +86,4 @@ void HAL_ETH_MspDeInit(ETH_HandleTypeDef* heth)
/* Disable the Ethernet global Interrupt */
NVIC_DisableIRQ(ETH_IRQn);
}
}
}

View File

@ -20,7 +20,6 @@
/* mbed includes */
#include "mbed_error.h"
#include "mbed_interface.h"
#include "us_ticker_api.h"
#include "mbed_rtos_storage.h"
/* lwIP includes. */
@ -237,7 +236,7 @@ err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) {
* of milliseconds until received.
*---------------------------------------------------------------------------*/
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) {
uint32_t start = us_ticker_read();
uint32_t start = osKernelGetTickCount();
uint32_t flags = osEventFlagsWait(mbox->id, SYS_MBOX_FETCH_EVENT,
osFlagsWaitAny | osFlagsNoClear, (timeout ? timeout : osWaitForever));
if ((flags & osFlagsError) || !(flags & SYS_MBOX_FETCH_EVENT))
@ -254,7 +253,7 @@ u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) {
osEventFlagsClear(mbox->id, SYS_MBOX_FETCH_EVENT);
osKernelRestoreLock(state);
return (us_ticker_read() - start) / 1000;
return osKernelGetTickCount() - start;
}
/*---------------------------------------------------------------------------*
@ -339,12 +338,12 @@ err_t sys_sem_new(sys_sem_t *sem, u8_t count) {
* u32_t -- Time elapsed or SYS_ARCH_TIMEOUT.
*---------------------------------------------------------------------------*/
u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) {
u32_t start = us_ticker_read();
u32_t start = osKernelGetTickCount();
if (osSemaphoreAcquire(sem->id, (timeout != 0)?(timeout):(osWaitForever)) != osOK)
return SYS_ARCH_TIMEOUT;
return (us_ticker_read() - start) / 1000;
return osKernelGetTickCount() - start;
}
/*---------------------------------------------------------------------------*
@ -414,7 +413,6 @@ osMutexAttr_t lwip_sys_mutex_attr;
mbed_rtos_storage_mutex_t lwip_sys_mutex_data;
void sys_init(void) {
us_ticker_read(); // Init sys tick
lwip_sys_mutex_attr.name = "lwip_sys_mutex";
lwip_sys_mutex_attr.cb_mem = &lwip_sys_mutex_data;
lwip_sys_mutex_attr.cb_size = sizeof(lwip_sys_mutex_data);
@ -430,9 +428,7 @@ void sys_init(void) {
* Used by PPP as a timestamp-ish value
*---------------------------------------------------------------------------*/
u32_t sys_jiffies(void) {
static u32_t jiffies = 0;
jiffies += 1 + (us_ticker_read()/10000);
return jiffies;
return osKernelGetTickCount();
}
/*---------------------------------------------------------------------------*
@ -477,7 +473,7 @@ void sys_arch_unprotect(sys_prot_t p) {
}
u32_t sys_now(void) {
return us_ticker_read() / 1000;
return osKernelGetTickCount();
}
void sys_msleep(u32_t ms) {

View File

@ -18,9 +18,6 @@
#include "lwip/debug.h"
#include "lwip/def.h"
#include "lwip_random.h"
#if FEATURE_COMMON_PAL
#include "randLIB.h"
void lwip_seed_random(void)
@ -37,20 +34,3 @@ inline uint32_t lwip_get_random(void)
{
return randLIB_get_32bit();
}
#else
void lwip_seed_random(void)
{
}
void lwip_add_random_seed(uint64_t seed)
{
}
uint32_t lwip_get_random(void)
{
return rand();
}
#endif

View File

@ -855,7 +855,7 @@ nsapi_error_t mbed_lwip_bringup_2(bool dhcp, bool ppp, const char *ip, const cha
if (lwip_blocking) {
if (sys_arch_sem_wait(&lwip_netif_linked, 15000) == SYS_ARCH_TIMEOUT) {
if (ppp) {
ppp_lwip_disconnect();
(void)ppp_lwip_disconnect();
}
return NSAPI_ERROR_NO_CONNECTION;
}
@ -872,7 +872,7 @@ nsapi_error_t mbed_lwip_bringup_2(bool dhcp, bool ppp, const char *ip, const cha
if (!mbed_lwip_get_ip_addr(true, &lwip_netif)) {
if (sys_arch_sem_wait(&lwip_netif_has_any_addr, DHCP_TIMEOUT * 1000) == SYS_ARCH_TIMEOUT) {
if (ppp) {
ppp_lwip_disconnect();
(void)ppp_lwip_disconnect();
}
return NSAPI_ERROR_DHCP_FAILURE;
}
@ -1135,7 +1135,7 @@ static nsapi_error_t mbed_lwip_socket_bind(nsapi_stack_t *stack, nsapi_socket_t
return NSAPI_ERROR_PARAMETER;
}
if (!ip_addr_isany(&ip_addr) && !mbed_lwip_is_local_addr(&ip_addr)) {
if (!ip_addr_isany_val(ip_addr) && !mbed_lwip_is_local_addr(&ip_addr)) {
return NSAPI_ERROR_PARAMETER;
}

View File

@ -18,14 +18,8 @@
#include "platform/FileHandle.h"
#include "platform/mbed_poll.h"
#include "events/EventQueue.h"
#if defined(FEATURE_COMMON_PAL)
#include "mbed_trace.h"
#define TRACE_GROUP "LPPP"
#else
#define tr_debug(...) (void(0)) //dummies if feature common pal is not added
#define tr_info(...) (void(0)) //dummies if feature common pal is not added
#define tr_error(...) (void(0)) //dummies if feature common pal is not added
#endif //defined(FEATURE_COMMON_PAL)
#include "rtos/Thread.h"
#include "lwip/tcpip.h"
#include "lwip/tcp.h"

View File

@ -12,6 +12,10 @@ This is the Github repo for Mbed cellular connectivity:
common Common and utility sources
targets Vendor specific cellular module adaptations
TESTS Cellular Greentea test
UNITTESTS Cellular unit test
## Known limitations
**Please note that this is a first release of Cellular framework and is subject to further development in future.**
@ -24,7 +28,9 @@ You can find currently supported cellular modules in the `framework/targets/` fo
## Cellular configuration
You can change cellular defaults in the `mbed_app.json` configuration file:
You can change cellular defaults in the `mbed_lib.json` configuration file.
You can also override cellular defaults in the `mbed_app.json` configuration file:
"config": {
"cellular_plmn": {
@ -62,6 +68,14 @@ You can define the debug tracing level in the `mbed_app.json` configuration file
}
}
## Greentea tests
The `TESTS` folder contains Greentea tests for cellular specific classes. You need to give relevant configuration file with `--app-config` parameter, e.g.:
mbed test -n features-cellular-tests-* --app-config features\cellular\TESTS\socket\udp\template_mbed_app.json -vv
Note that Greentea tests use SIM PIN so you need to change that or your SIM card may get locked.
## Unit tests
The `UNITTESTS` folder contains unit tests for cellular specific classes. Unit tests are based on the stubbing method.

View File

@ -1,82 +0,0 @@
/*
* Copyright (c) 2017, Arm Limited and affiliates.
* 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 CELLULAR_TESTS_H
#define CELLULAR_TESTS_H
#include "CellularUtil.h" // for CELLULAR_ helper macros
#include "CellularTargets.h"
#ifdef CELLULAR_DEVICE
#include "mbed_events.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "CellularLog.h"
#include CELLULAR_STRINGIFY(CELLULAR_DEVICE.h)
extern EventQueue queue;
extern CELLULAR_DEVICE cellularDevice;
extern UARTSerial serial;
extern CellularNetwork *network;
extern CellularSMS *sms;
extern CellularPower *pwr;
extern CellularSIM *sim;
/**
* TEST CASES DEFINED HERE AND in main.cpp
*/
// power
void test_create_power(void);
// SIM
void test_get_sim_state(void);
void test_set_pin(void);
void test_change_pin(void);
// sms
void test_sms_init(void);
// network
void test_attach(void);
void test_connect(void);
void test_get_ip_address(void);
void test_disconnect(void);
// stack
void test_socket_open(void);
void test_socket_bind(void);
/*
void test_socket_set_blocking();
void test_socket_send_receive_blocking();
*/
void test_socket_set_non_blocking();
void test_socket_send_receive_non_blocking();
void test_socket_close(void);
// Test closing all interface via device
void test_close_interfaces(void);
#endif // CELLULAR_DEVICE
#endif // CELLULAR_TESTS_H

View File

@ -1,107 +0,0 @@
/*
* Copyright (c) 2017, Arm Limited and affiliates.
* 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 "CellularTests.h"
#if !defined(MBED_CONF_NSAPI_PRESENT)
#error [NOT_SUPPORTED] A json configuration file is needed. Skipping this build.
#endif
#ifndef CELLULAR_DEVICE
#error [NOT_SUPPORTED] CELLULAR_DEVICE must be defined for this test
#endif
EventQueue queue(32 * EVENTS_EVENT_SIZE);
Thread t;
CELLULAR_DEVICE cellularDevice(queue);
UARTSerial serial(MDMTXD, MDMRXD, MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE);
CellularNetwork *network = NULL;
CellularPower *pwr = NULL;
CellularSIM *sim = NULL;
CellularSMS *sms = NULL;
using namespace utest::v1;
// using namespace mbed;
utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason)
{
greentea_case_failure_abort_handler(source, reason);
return STATUS_CONTINUE;
}
Case cases[] = {
// power test
Case("Create power", test_create_power, greentea_failure_handler),
#ifdef MBED_CONF_APP_CELLULAR_SIM_PIN
// sim test
Case("test get SIM state", test_get_sim_state, greentea_failure_handler),
Case("SIM set pin", test_set_pin, greentea_failure_handler),
Case("SIM change pin", test_change_pin, greentea_failure_handler),
#endif
// network tests
Case("attach", test_attach, greentea_failure_handler),
// SMS tests
Case("SMS init", test_sms_init, greentea_failure_handler),
// network tests
Case("connect", test_connect, greentea_failure_handler),
Case("get_ip_address", test_get_ip_address, greentea_failure_handler),
// stack tests
Case("open", test_socket_open, greentea_failure_handler),
Case("bind", test_socket_bind, greentea_failure_handler),
// Case("set socket blocking", test_socket_set_blocking, greentea_failure_handler),
// Case("socket send receive in blocking mode", test_socket_send_receive_blocking, greentea_failure_handler),
Case("set socket non blocking", test_socket_set_non_blocking, greentea_failure_handler),
Case("socket send receive in non blocking mode", test_socket_send_receive_non_blocking, greentea_failure_handler),
Case("close", test_socket_close, greentea_failure_handler),
// network tests
Case("disconnect", test_disconnect, greentea_failure_handler),
// test closing of all interface, must be the last test case
Case("Close all Interfaces", test_close_interfaces, greentea_failure_handler)
};
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(300, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Specification specification(test_setup, cases);
void test_close_interfaces()
{
// SMS is already closed in it's test
cellularDevice.close_network();
cellularDevice.close_sim();
cellularDevice.close_power();
}
int main()
{
#if defined (MDMRTS) && defined (MDMCTS)
serial.set_flow_control(SerialBase::RTSCTS, MDMRTS, MDMCTS);
#endif
pwr = cellularDevice.open_power(&serial);
sim = cellularDevice.open_sim(&serial);
sms = cellularDevice.open_sms(&serial);
network = cellularDevice.open_network(&serial);
t.start(callback(&queue, &EventQueue::dispatch_forever));
return Harness::run(specification);
}

View File

@ -1,104 +0,0 @@
/*
* Copyright (c) 2017, Arm Limited and affiliates.
* 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 "CellularTests.h"
#ifdef CELLULAR_DEVICE
using namespace mbed;
static bool wait_register()
{
tr_info("Try registering to network...");
if (network->set_registration() != NSAPI_ERROR_OK) {
tr_error("Network registration request failed.");
return false;
}
CellularNetwork::RegistrationStatus status;
for (int i=0; i<180; i++) {
tr_info("Register to network %d...", i);
for (int type = 0; type < CellularNetwork::C_MAX; type++) {
if (network->get_registration_status((CellularNetwork::RegistrationType)type, status) == NSAPI_ERROR_OK) {
tr_info("status %d...", status);
switch (status) {
case CellularNetwork::RegisteredRoaming:
// fall-through
case CellularNetwork::RegisteredHomeNetwork:
tr_info("Registered to network.");
return true;
case CellularNetwork::RegisteredSMSOnlyRoaming:
// fall-through
case CellularNetwork::RegisteredSMSOnlyHome:
tr_warn("SMS only network registration!");
return true;
case CellularNetwork::RegisteredCSFBNotPreferredRoaming:
// fall-through
case CellularNetwork::RegisteredCSFBNotPreferredHome:
tr_warn("Not preferred network registration!");
return true;
case CellularNetwork::AttachedEmergencyOnly:
tr_warn("Emergency only network registration!");
return true;
case CellularNetwork::RegistrationDenied:
tr_warn("Network registration denied!");
wait(i);
break;
case CellularNetwork::NotRegistered:
case CellularNetwork::Unknown:
case CellularNetwork::SearchingNetwork:
default:
break;
}
}
}
wait(1);
}
return false;
}
void test_attach()
{
cellularDevice.set_timeout(120*1000); // 120 second timeout for at commands after power is up. It might take time to register, attach and connect
tr_info("Register to network.");
TEST_ASSERT(wait_register());
tr_info("Attach to network.");
nsapi_error_t err = network->set_attach();
TEST_ASSERT(!err);
CellularNetwork::AttachStatus status;
err = network->get_attach(status);
TEST_ASSERT(!err);
}
void test_connect()
{
nsapi_error_t err = network->connect();
TEST_ASSERT(!err);
}
void test_get_ip_address()
{
const char *ip = network->get_ip_address();
TEST_ASSERT(ip && ip[0]);
tr_info("IP: %s\r\n", ip);
}
void test_disconnect()
{
nsapi_error_t err = network->disconnect();
TEST_ASSERT(!err);
}
#endif // CELLULAR_DEVICE

View File

@ -1,52 +0,0 @@
/*
* Copyright (c) 2017, Arm Limited and affiliates.
* 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 "CellularTests.h"
#ifdef CELLULAR_DEVICE
using namespace mbed;
static bool start_cellular_at(CellularPower *pwr)
{
nsapi_error_t err = pwr->off();
if (err != NSAPI_ERROR_OK && err != NSAPI_ERROR_UNSUPPORTED) {
return false;
}
err = pwr->on();
if (err != NSAPI_ERROR_OK && err != NSAPI_ERROR_UNSUPPORTED) {
return false;
}
tr_info("Wait for cellular device 180 seconds...");
for (int i = 0; i < 180; i++) {
if (pwr->set_at_mode() == NSAPI_ERROR_OK) {
return true;
}
wait(1);
}
return false;
}
void test_create_power()
{
TEST_ASSERT(start_cellular_at(pwr));
tr_info("Cellular device is ready!");
}
// TODO: tests still missing for off, sleep, opt_power_save_mode, opt_receive_period
#endif // CELLULAR_DEVICE

View File

@ -1,90 +0,0 @@
/*
* Copyright (c) 2017, Arm Limited and affiliates.
* 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 "CellularTests.h"
#ifdef CELLULAR_DEVICE
#ifdef MBED_CONF_APP_CELLULAR_SIM_PIN
using namespace mbed;
void test_get_sim_state()
{
wait(1);
CellularSIM::SimState state = CellularSIM::SimStateUnknown;
tr_info("Wait SIM for 180 seconds...");
for (int i = 0; i < 180; i++) {
CellularSIM::SimState tmp_state;
if ((sim->get_sim_state(tmp_state) == NSAPI_ERROR_OK) && tmp_state != CellularSIM::SimStateUnknown) {
state = tmp_state;
break;
}
}
TEST_ASSERT_MESSAGE(state == CellularSIM::SimStateReady || state == CellularSIM::SimStatePinNeeded ||
state == CellularSIM::SimStatePukNeeded, "Invalid SIM state");
}
// creates PIN which is different than one defined in MBED_CONF_APP_CELLULAR_SIM_PIN
static void create_random_pin(char* random_pin)
{
char s[11];
do {
sprintf(s,"%d", rand());
} while (strncmp(s, MBED_CONF_APP_CELLULAR_SIM_PIN, 4) == 0);
strncpy(random_pin, s, 4);
random_pin[4] = '\0';
}
void test_set_pin()
{
// run test only if sim is not in ready state as then sim interface will return NSAPI_ERROR_OK
nsapi_error_t err;
CellularSIM::SimState state = CellularSIM::SimStateUnknown;
if ((sim->get_sim_state(state) == NSAPI_ERROR_OK) && (state != CellularSIM::SimStateReady)) {
char random_pin[5];
create_random_pin(random_pin);
err = sim->set_pin(random_pin);
TEST_ASSERT_MESSAGE(err != 0, "Setting random pin should fail");
}
err = sim->set_pin(MBED_CONF_APP_CELLULAR_SIM_PIN);
char err_msg[60];
sprintf(err_msg, "Setting correct pin: %s failed with: %d", MBED_CONF_APP_CELLULAR_SIM_PIN, err);
TEST_ASSERT_MESSAGE(err == 0, err_msg);
}
void test_change_pin()
{
char random_pin[5];
create_random_pin(random_pin);
nsapi_error_t err = sim->change_pin(MBED_CONF_APP_CELLULAR_SIM_PIN, random_pin);
char err_msg[60];
sprintf(err_msg, "Change from original pin failed with: %d", err);
TEST_ASSERT_MESSAGE(err == NSAPI_ERROR_OK, err_msg);
err = sim->change_pin(random_pin, MBED_CONF_APP_CELLULAR_SIM_PIN);
sprintf(err_msg, "Change back original pin failed with: %d", err);
TEST_ASSERT_MESSAGE(err == NSAPI_ERROR_OK, err_msg);
}
#endif
#endif // CELLULAR_DEVICE

View File

@ -1,46 +0,0 @@
/*
* Copyright (c) 2017, Arm Limited and affiliates.
* 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 "CellularTests.h"
#ifdef CELLULAR_DEVICE
using namespace mbed;
void test_sms_init()
{
// for some weird reason if we don't wait for few seconds we get SIM Busy error in initialize even is SIM is ready...
wait(3);
// check SIM state as we have tested sim and it might not be ready
for (int i = 0; i < MAX_SIM_READY_WAITING_TIME; i++) {
CellularSIM::SimState state;
if (sim->get_sim_state(state) == NSAPI_ERROR_OK && state == CellularSIM::SimStateReady) {
break;
}
wait(1);
}
nsapi_error_t err = sms->initialize(CellularSMS::CellularSMSMmodeText);
char err_msg[60];
sprintf(err_msg, "SMS initialize failed with: %d", err);
cellularDevice.close_sms();
sms = NULL;
TEST_ASSERT_MESSAGE(!err, err_msg);
}
#endif // CELLULAR_DEVICE

View File

@ -1,120 +0,0 @@
/*
* Copyright (c) 2017, Arm Limited and affiliates.
* 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 "CellularTests.h"
#ifdef CELLULAR_DEVICE
using namespace mbed;
static UDPSocket socket;
#define SERVER_IP_ADDR "52.215.34.155"
#define SERVER_UDP_PORT 7
static rtos::Semaphore sock_event;
void test_socket_open()
{
nsapi_error_t err = socket.open(network);
TEST_ASSERT(err == NSAPI_ERROR_OK);
}
void test_socket_bind()
{
nsapi_error_t err = socket.bind(3030);
TEST_ASSERT(!err);
}
/*
void test_socket_set_blocking()
{
//socket.set_blocking(true);
socket.set_timeout(5000);
}
void test_socket_send_receive_blocking()
{
char receive_buffer[CELLULAR_MTU] = {0};
char send_buffer[] = { 'H', 'e', 'l', 'l', 'u', 'l', 'a', 'r', '!' };
int send_count = 0;
int send_tries = 1;
int max_send_tries = 3;
// Send to echo server
while (send_tries <= max_send_tries) {
tr_info("ONE!!!");
send_count = socket.sendto(SERVER_IP_ADDR, SERVER_UDP_PORT, send_buffer, sizeof(send_buffer));
TEST_ASSERT_MESSAGE(send_count == sizeof(send_buffer), "Sent count doesnt match sent buffer!");
send_tries++;
// Read response
SocketAddress address;
int receive_count = 0;
// 2 tries. First recv attempt should be blocked and wait for a max 5 seconds for socket read flag
int recv_tries = 2;
while (recv_tries >= 0) {
tr_info("RECV!!!");
receive_count = socket.recvfrom(&address, receive_buffer, sizeof(receive_buffer));
if (receive_count > 0) {
break;
}
recv_tries--;
wait(1);
}
TEST_ASSERT_MESSAGE(receive_count == send_count, "Receive and Sent count dont match!");
TEST_ASSERT_MESSAGE(strncmp(send_buffer, receive_buffer, send_count) == 0, "Sent data doesn't match received data while in ECHO");
}
}
*/
static void socket_sigio_cb()
{
sock_event.release();
}
void test_socket_set_non_blocking()
{
socket.set_blocking(false);
socket.sigio(socket_sigio_cb);
}
void test_socket_send_receive_non_blocking()
{
char receive_buffer[1500] = {0};
char send_buffer[] = { 'H', 'e', 'l', 'l', 'u', 'l', 'a', 'r', '!' };
// Send to echo server
int send_count = socket.sendto(SERVER_IP_ADDR, SERVER_UDP_PORT, send_buffer, sizeof(send_buffer));
TEST_ASSERT(send_count == sizeof(send_buffer));
int32_t event;
event = sock_event.wait(10000);
TEST_ASSERT_MESSAGE( event>=1, "No Socket event within 10 seconds");
// Read response
SocketAddress address;
int receive_count = socket.recvfrom(&address, receive_buffer, sizeof(receive_buffer));
TEST_ASSERT_MESSAGE(receive_count == send_count, "Receive and Sent count dont match!");
TEST_ASSERT_MESSAGE(strncmp(send_buffer, receive_buffer, send_count) == 0, "Sent data doesn't match received data while in ECHO");
}
void test_socket_close()
{
nsapi_error_t err = socket.close();
TEST_ASSERT(!err);
}
#endif // CELLULAR_DEVICE

View File

@ -0,0 +1,203 @@
/*
* Copyright (c) 2018, Arm Limited and affiliates.
* 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.
*/
#if !defined(MBED_CONF_NSAPI_PRESENT)
#error [NOT_SUPPORTED] A json configuration file is needed. Skipping this build.
#endif
#include "CellularUtil.h" // for CELLULAR_ helper macros
#include "CellularTargets.h"
#ifndef CELLULAR_DEVICE
#error [NOT_SUPPORTED] CELLULAR_DEVICE must be defined
#endif
#ifndef MBED_CONF_APP_CELLULAR_SIM_PIN
#error [NOT_SUPPORTED] SIM pin code is needed. Skipping this build.
#endif
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "mbed.h"
#include "CellularConnectionFSM.h"
#if MBED_CONF_CELLULAR_USE_APN_LOOKUP || MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP
#include "APN_db.h"
#endif //MBED_CONF_CELLULAR_USE_APN_LOOKUP || MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP
#include "CellularLog.h"
#define NETWORK_TIMEOUT (180*1000)
#define SOCKET_TIMEOUT (30*1000)
#define ECHO_SERVER_NAME "echo.mbedcloudtesting.com"
#define ECHO_SERVER_UDP_PORT 7
static CellularConnectionFSM::CellularState cellular_target_state;
static UARTSerial cellular_serial(MDMTXD, MDMRXD, MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE);
static rtos::Semaphore network_semaphore(0);
static CellularConnectionFSM cellular;
static SocketAddress echo_server_addr;
class EchoSocket : public UDPSocket {
public:
EchoSocket(int size) : UDPSocket(), _async_flag(0), _data(0), _size(size) {
}
virtual ~EchoSocket() {
delete _data;
}
void set_async(int async) {
_async_flag = async;
if (_async_flag) {
set_blocking(false);
sigio(callback(this, &EchoSocket::async_callback));
} else {
set_blocking(true);
set_timeout(SOCKET_TIMEOUT);
sigio(NULL);
}
}
void test_sendto(const char *const hostname = NULL) {
_data = new uint8_t[_size];
for (int i=0; i<_size; i++) {
_data[i] = (uint8_t)rand();
}
// clear pending events
TEST_ASSERT(!(EchoSocket::eventFlags.clear(_async_flag) & osFlagsError));
if (hostname) {
TEST_ASSERT(sendto(hostname, ECHO_SERVER_UDP_PORT, _data, _size) == _size);
} else {
TEST_ASSERT(sendto(echo_server_addr, _data, _size) == _size);
}
}
void test_recvfrom() {
if (_async_flag) {
TEST_ASSERT((EchoSocket::eventFlags.wait_any(_async_flag, SOCKET_TIMEOUT) & (osFlagsError | _async_flag)) == _async_flag);
}
uint8_t *buf = new uint8_t[_size];
memset(buf, 0, _size);
SocketAddress recv_address;
TEST_ASSERT(recvfrom(&recv_address, buf, _size) == _size);
TEST_ASSERT(recv_address == echo_server_addr);
TEST_ASSERT(memcmp(_data, buf, _size) == 0);
delete buf;
delete _data;
_data = 0;
}
private:
void async_callback() {
EchoSocket::eventFlags.set(_async_flag);
}
uint8_t *_data;
int _size;
uint32_t _async_flag; // 0 for blocking socket, signal bit for async
static rtos::EventFlags eventFlags;
};
rtos::EventFlags EchoSocket::eventFlags;
static void network_callback(nsapi_event_t ev, intptr_t ptr)
{
if (ev == NSAPI_EVENT_CONNECTION_STATUS_CHANGE) {
if (ptr == NSAPI_STATUS_GLOBAL_UP) {
MBED_ASSERT(network_semaphore.release() == osOK);
}
}
}
static void udp_network_stack()
{
cellular.set_serial(&cellular_serial);
TEST_ASSERT(cellular.init() == NSAPI_ERROR_OK);
#if defined (MDMRTS) && defined (MDMCTS)
cellular_serial.set_flow_control(SerialBase::RTSCTS, MDMRTS, MDMCTS);
#endif
cellular.attach(&network_callback);
TEST_ASSERT(cellular.start_dispatch() == NSAPI_ERROR_OK);
cellular.set_sim_pin(MBED_CONF_APP_CELLULAR_SIM_PIN);
cellular_target_state = CellularConnectionFSM::STATE_CONNECTED;
TEST_ASSERT(cellular.continue_to_state(cellular_target_state) == NSAPI_ERROR_OK);
TEST_ASSERT(network_semaphore.wait(NETWORK_TIMEOUT) == 1);
}
static void udp_gethostbyname()
{
TEST_ASSERT(cellular.get_network()->gethostbyname(ECHO_SERVER_NAME, &echo_server_addr) == 0);
tr_info("HOST: %s", echo_server_addr.get_ip_address());
echo_server_addr.set_port(7);
wait(1);
}
static void udp_socket_send_receive()
{
EchoSocket echo_socket(4);
TEST_ASSERT(echo_socket.open(cellular.get_network()) == NSAPI_ERROR_OK);
echo_socket.set_async(0);
echo_socket.test_sendto();
echo_socket.test_recvfrom();
TEST_ASSERT(echo_socket.close() == NSAPI_ERROR_OK);
wait(1);
}
static void udp_socket_send_receive_async()
{
EchoSocket echo_socket(4);
TEST_ASSERT(echo_socket.open(cellular.get_network()) == NSAPI_ERROR_OK);
echo_socket.set_async(1);
echo_socket.test_sendto();
echo_socket.test_recvfrom();
TEST_ASSERT(echo_socket.close() == NSAPI_ERROR_OK);
wait(1);
}
using namespace utest::v1;
static utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason)
{
greentea_case_failure_abort_handler(source, reason);
return STATUS_ABORT;
}
static Case cases[] = {
Case("UDP network stack", udp_network_stack, greentea_failure_handler),
Case("UDP gethostbyname", udp_gethostbyname, greentea_failure_handler),
Case("UDP socket send/receive", udp_socket_send_receive, greentea_failure_handler),
Case("UDP socket send/receive async", udp_socket_send_receive_async, greentea_failure_handler),
//Case("UDP socket multiple simultaneous", udp_socket_multiple_simultaneous, greentea_failure_handler),
};
static utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(10*60, "default_auto"); // network registration may take up to 180 seconds, DNS query a couple of minutes, etc.
return verbose_test_setup_handler(number_of_cases);
}
static Specification specification(test_setup, cases);
int main()
{
mbed_trace_init();
return Harness::run(specification);
}

View File

@ -1,30 +1,39 @@
{
"config": {
"sock-type": "UDP",
"network-interface":{
"help": "Options are ETHERNET,CELLULAR",
"value": "CELLULAR"
},
"cellular_sim_pin": {
"help": "PIN code",
"value": "\"1234\""
},
"apn": {
"help": "The APN string to use for this SIM/network, set to 0 if none",
"value": 0
},
"username": {
"help": "The user name string to use for this APN, set to zero if none",
"value": 0
},
"password": {
"help": "The password string to use for this APN, set to 0 if none",
"value": 0
},
"trace-level": {
"help": "Options are TRACE_LEVEL_ERROR,TRACE_LEVEL_WARN,TRACE_LEVEL_INFO,TRACE_LEVEL_DEBUG",
"macro_name": "MBED_TRACE_MAX_LEVEL",
"value": "TRACE_LEVEL_INFO"
}
},
},
"target_overrides": {
"*": {
"ppp-cell-iface.apn-lookup": false,
"cellular.use-apn-lookup": false,
"target.features_add": ["LWIP", "COMMON_PAL"],
"mbed-trace.enable": true,
"mbed-trace.enable": false,
"lwip.ipv4-enabled": true,
"lwip.ethernet-enabled": false,
"lwip.ipv6-enabled": true,
"lwip.tcp-enabled": false,
"lwip.ppp-enabled": true,
"lwip.tcp-enabled": true,
"lwip.ethernet-enabled": false,
"platform.stdio-convert-newlines": true,
"platform.stdio-baud-rate": 115200,
"platform.default-serial-baud-rate": 115200
}
}

View File

@ -82,6 +82,11 @@ TEST(AT_CellularNetwork, test_AT_CellularNetwork_get_attach)
unit->test_AT_CellularNetwork_get_attach();
}
TEST(AT_CellularNetwork, test_AT_CellularNetwork_detach)
{
unit->test_AT_CellularNetwork_detach();
}
TEST(AT_CellularNetwork, test_AT_CellularNetwork_get_rate_control)
{
unit->test_AT_CellularNetwork_get_rate_control();
@ -157,3 +162,7 @@ TEST(AT_CellularNetwork, test_AT_CellularNetwork_get_operator_params)
unit->test_AT_CellularNetwork_get_operator_params();
}
TEST(AT_CellularNetwork, test_AT_CellularNetwork_get_operator_names)
{
unit->test_AT_CellularNetwork_get_operator_names();
}

View File

@ -81,7 +81,7 @@ void Test_AT_CellularNetwork::test_AT_CellularNetwork_connect()
AT_CellularNetwork cn(at);
cn.set_stack_type(IPV4V6_STACK);
CHECK(NSAPI_ERROR_NO_CONNECTION == cn.connect("APN", "a", "b"));
CHECK(NSAPI_ERROR_UNSUPPORTED == cn.connect("APN", "a", "b"));
ATHandler_stub::nsapi_error_value = NSAPI_ERROR_CONNECTION_LOST;
CHECK(NSAPI_ERROR_NO_CONNECTION == cn.connect("APN"));
@ -152,6 +152,17 @@ void Test_AT_CellularNetwork::test_AT_CellularNetwork_get_attach()
CHECK(NSAPI_ERROR_CONNECTION_LOST == cn.get_attach(stat));
}
void Test_AT_CellularNetwork::test_AT_CellularNetwork_detach()
{
EventQueue que;
FileHandle_stub fh1;
ATHandler at(&fh1, que, 0, ",");
AT_CellularNetwork cn(at);
ATHandler_stub::nsapi_error_value = NSAPI_ERROR_CONNECTION_LOST;
CHECK(NSAPI_ERROR_CONNECTION_LOST == cn.detach());
}
void Test_AT_CellularNetwork::test_AT_CellularNetwork_get_rate_control()
{
EventQueue que;
@ -174,10 +185,12 @@ void Test_AT_CellularNetwork::test_AT_CellularNetwork_get_apn_backoff_timer()
AT_CellularNetwork cn(at);
int time;
ATHandler_stub::nsapi_error_value = NSAPI_ERROR_CONNECTION_LOST;
CHECK(NSAPI_ERROR_PARAMETER == cn.get_apn_backoff_timer(time));
cn.set_credentials("internet", NULL, NULL);
CHECK(NSAPI_ERROR_CONNECTION_LOST == cn.get_apn_backoff_timer(time));
}
void Test_AT_CellularNetwork::test_AT_CellularNetwork_get_ip_address()
{
EventQueue que;
@ -195,8 +208,8 @@ void Test_AT_CellularNetwork::test_AT_CellularNetwork_set_access_technology()
ATHandler at(&fh1, que, 0, ",");
AT_CellularNetwork cn(at);
CHECK(NSAPI_ERROR_UNSUPPORTED == cn.set_access_technology(CellularNetwork::operator_t::RAT_UNKNOWN));
CHECK(NSAPI_ERROR_UNSUPPORTED == cn.set_access_technology(CellularNetwork::operator_t::RAT_GSM_COMPACT));
CHECK(NSAPI_ERROR_UNSUPPORTED == cn.set_access_technology(CellularNetwork::RAT_UNKNOWN));
CHECK(NSAPI_ERROR_UNSUPPORTED == cn.set_access_technology(CellularNetwork::RAT_GSM_COMPACT));
}
void Test_AT_CellularNetwork::test_AT_CellularNetwork_scan_plmn()
@ -306,8 +319,8 @@ void Test_AT_CellularNetwork::test_AT_CellularNetwork_get_cell_id()
AT_CellularNetwork cn(at);
int id;
ATHandler_stub::nsapi_error_value = NSAPI_ERROR_CONNECTION_LOST;
CHECK(NSAPI_ERROR_CONNECTION_LOST == cn.get_cell_id(id));
CHECK(NSAPI_ERROR_OK == cn.get_cell_id(id));
CHECK(id == -1);
}
void Test_AT_CellularNetwork::test_AT_CellularNetwork_get_3gpp_error()
@ -334,3 +347,18 @@ void Test_AT_CellularNetwork::test_AT_CellularNetwork_get_operator_params()
CHECK(NSAPI_ERROR_CONNECTION_LOST == cn.get_operator_params(format, ops));
}
void Test_AT_CellularNetwork::test_AT_CellularNetwork_get_operator_names()
{
EventQueue que;
FileHandle_stub fh1;
ATHandler at(&fh1, que, 0, ",");
AT_CellularNetwork cn(at);
CellularNetwork::operator_names_list name_list;
CHECK(NSAPI_ERROR_OK == cn.get_operator_names(name_list));
ATHandler_stub::nsapi_error_value = NSAPI_ERROR_CONNECTION_LOST;
CHECK(NSAPI_ERROR_CONNECTION_LOST == cn.get_operator_names(name_list));
}

View File

@ -42,6 +42,8 @@ public:
void test_AT_CellularNetwork_get_attach();
void test_AT_CellularNetwork_detach();
void test_AT_CellularNetwork_get_rate_control();
void test_AT_CellularNetwork_get_apn_backoff_timer();
@ -71,6 +73,8 @@ public:
void test_AT_CellularNetwork_get_3gpp_error();
void test_AT_CellularNetwork_get_operator_params();
void test_AT_CellularNetwork_get_operator_names();
};
#endif // TEST_AT_CELLULARNETWORK_H

View File

@ -20,6 +20,7 @@ TEST_SRC_FILES = \
../../stubs/mbed_poll_stub.cpp \
../../stubs/Timer_stub.cpp \
../../stubs/equeue_stub.cpp \
../../stubs/Kernel.cpp \
include ../../MakefileWorker.mk

View File

@ -287,7 +287,7 @@ void Test_ATHandler::test_ATHandler_cmd_start()
ATHandler at(&fh1, que, 0, ",");
mbed_poll_stub::revents_value = POLLOUT;
mbed_poll_stub::int_value = 1;
fh1.size_value = 1;
fh1.size_value = 3;
at.cmd_start("s");
mbed_poll_stub::revents_value = POLLIN;
mbed_poll_stub::int_value = 0;
@ -303,12 +303,13 @@ void Test_ATHandler::test_ATHandler_write_int()
FileHandle_stub fh1;
ATHandler at(&fh1, que, 0, ",");
fh1.size_value = -1;
at.write_int(4);
at.clear_error();
mbed_poll_stub::revents_value = POLLOUT;
mbed_poll_stub::int_value = 1;
fh1.size_value = 1;
fh1.size_value = 6;
at.write_int(4);
at.write_int(2147483647);
@ -331,7 +332,7 @@ void Test_ATHandler::test_ATHandler_write_string()
at.clear_error();
mbed_poll_stub::revents_value = POLLOUT;
mbed_poll_stub::int_value = 1;
fh1.size_value = 1;
fh1.size_value = -1;
at.cmd_start("s");
at.write_string("help", true);
CHECK(NSAPI_ERROR_DEVICE_ERROR == at.get_last_error());
@ -339,7 +340,7 @@ void Test_ATHandler::test_ATHandler_write_string()
at.clear_error();
mbed_poll_stub::revents_value = POLLOUT;
mbed_poll_stub::int_value = 1;
fh1.size_value = 3;
fh1.size_value = -1;
at.write_string("help", true);
CHECK(NSAPI_ERROR_DEVICE_ERROR == at.get_last_error());
@ -357,6 +358,7 @@ void Test_ATHandler::test_ATHandler_cmd_stop()
FileHandle_stub fh1;
ATHandler at(&fh1, que, 0, ",");
fh1.size_value = -1;
at.cmd_stop();
at.write_string("help", true);
@ -371,6 +373,7 @@ void Test_ATHandler::test_ATHandler_write_bytes()
FileHandle_stub fh1;
ATHandler at(&fh1, que, 0, ",");
fh1.size_value = -1;
uint8_t data[] = "data";
at.write_bytes(data, 4);

View File

@ -42,7 +42,7 @@ device_err_t ATHandler_stub::device_err_value;
Callback<void()> ATHandler_stub::callback = NULL;
uint8_t ATHandler_stub::resp_info_true_counter = false;
ATHandler::ATHandler(FileHandle *fh, EventQueue &queue, int timeout, const char *output_delimiter) :
ATHandler::ATHandler(FileHandle *fh, EventQueue &queue, int timeout, const char *output_delimiter, uint16_t send_delay) :
_nextATHandler(0),
_fileHandle(fh),
_queue(queue)
@ -79,9 +79,10 @@ void ATHandler::set_file_handle(FileHandle *fh)
{
}
void ATHandler::set_urc_handler(const char *urc, mbed::Callback<void()> cb)
nsapi_error_t ATHandler::set_urc_handler(const char *urc, mbed::Callback<void()> cb)
{
ATHandler_stub::callback = cb;
return NSAPI_ERROR_OK;
}
nsapi_error_t ATHandler::get_last_error() const

View File

@ -33,6 +33,11 @@ AT_CellularNetwork::~AT_CellularNetwork()
{
}
nsapi_error_t AT_CellularNetwork::init()
{
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularNetwork::set_credentials(const char *apn,
const char *username, const char *password)
{
@ -56,6 +61,11 @@ nsapi_error_t AT_CellularNetwork::connect()
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularNetwork::activate_context()
{
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularNetwork::open_data_channel()
{
return NSAPI_ERROR_OK;
@ -100,11 +110,17 @@ nsapi_ip_stack_t AT_CellularNetwork::string_to_stack_type(const char* pdp_type)
return IPV4_STACK;
}
nsapi_error_t AT_CellularNetwork::set_registration_urc(bool urc_on)
nsapi_error_t AT_CellularNetwork::set_registration_urc(RegistrationType type, bool urc_on)
{
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularNetwork::get_network_registering_mode(NWRegisteringMode& mode)
{
mode = NWModeAutomatic;
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularNetwork::set_registration(const char *plmn)
{
return NSAPI_ERROR_OK;
@ -135,6 +151,10 @@ nsapi_error_t AT_CellularNetwork::get_attach(AttachStatus &status)
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularNetwork::detach()
{
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularNetwork::get_apn_backoff_timer(int &backoffTime)
{
@ -171,16 +191,22 @@ void AT_CellularNetwork::urc_no_carrier()
}
nsapi_error_t AT_CellularNetwork::set_access_technology_impl(operator_t::RadioAccessTechnology opsAct)
nsapi_error_t AT_CellularNetwork::set_access_technology_impl(RadioAccessTechnology opsAct)
{
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularNetwork::set_access_technology(operator_t::RadioAccessTechnology opAct)
nsapi_error_t AT_CellularNetwork::set_access_technology(RadioAccessTechnology opAct)
{
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularNetwork::get_access_technology(RadioAccessTechnology& rat)
{
rat = RAT_CATM1;
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularNetwork::scan_plmn(operList_t &operators, int &opsCount)
{
return NSAPI_ERROR_OK;
@ -231,3 +257,7 @@ int AT_CellularNetwork::get_3gpp_error()
return 0;
}
nsapi_error_t AT_CellularNetwork::get_operator_names(operator_names_list &op_names)
{
return NSAPI_ERROR_OK;
}

View File

@ -64,3 +64,13 @@ nsapi_error_t AT_CellularPower::opt_receive_period(int mode, EDRXAccessTechnolog
{
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularPower::set_device_ready_urc_cb(mbed::Callback<void()> callback)
{
return NSAPI_ERROR_OK;
}
void AT_CellularPower::remove_device_ready_urc_cb(mbed::Callback<void()> callback){
}

View File

@ -29,7 +29,7 @@ static uint8_t filehandle_stub_table_pos = 0;
class FileHandle_stub : public FileHandle
{
public:
size_t size_value;
ssize_t size_value;
FileHandle_stub() {size_value = 0;}
@ -47,9 +47,11 @@ public:
}
virtual ssize_t write(const void *buffer, size_t size){
if( size_value ) {
if (size_value > 0) {
size_value--;
return size;
} else if (size_value < 0) {
return -1;
}
return 0;
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) , Arm Limited and affiliates.
* 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 "Kernel.h"
namespace rtos {
uint64_t Kernel::get_ms_count()
{
return 20;
}
}

View File

@ -23,11 +23,13 @@
#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_INFO
#endif
#include "CellularLog.h"
#include "CellularCommon.h"
// timeout to wait for AT responses
#define TIMEOUT_POWER_ON (1*1000)
#define TIMEOUT_SIM_PIN (1*1000)
#define TIMEOUT_NETWORK (10*1000)
#define TIMEOUT_CONNECT (60*1000)
#define TIMEOUT_REGISTRATION (180*1000)
// maximum time when retrying network register, attach and connect in seconds ( 20minutes )
@ -35,33 +37,34 @@
#define RETRY_COUNT_DEFAULT 3
namespace mbed {
namespace mbed
{
CellularConnectionFSM::CellularConnectionFSM() :
_serial(0), _state(STATE_INIT), _next_state(_state), _status_callback(0), _network(0), _power(0), _sim(0),
_queue(8 * EVENTS_EVENT_SIZE), _queue_thread(0), _retry_count(0), _state_retry_count(0), _at_queue(8 * EVENTS_EVENT_SIZE)
_serial(0), _state(STATE_INIT), _next_state(_state), _status_callback(0), _event_status_cb(0), _network(0), _power(0), _sim(0),
_queue(8 * EVENTS_EVENT_SIZE), _queue_thread(0), _cellularDevice(0), _retry_count(0), _event_timeout(-1),
_at_queue(8 * EVENTS_EVENT_SIZE), _event_id(0), _plmn(0), _command_success(false), _plmn_network_found(false)
{
memset(_sim_pin, 0, sizeof(_sim_pin));
#if MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY == 0
_start_time = 0;
#else
// so that not every device don't start at the exact same time (for example after power outage)
_start_time = rand() % (MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY);
#endif // MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY
// set initial retry values in seconds
_retry_timeout_array[0] = 1;
_retry_timeout_array[0] = 1; // double time on each retry in order to keep network happy
_retry_timeout_array[1] = 2;
_retry_timeout_array[2] = 4;
_retry_timeout_array[3] = 16;
_retry_timeout_array[4] = 32;
_retry_timeout_array[5] = 60;
_retry_timeout_array[6] = 120;
_retry_timeout_array[7] = 360;
_retry_timeout_array[3] = 8;
_retry_timeout_array[4] = 16;
_retry_timeout_array[5] = 32;
_retry_timeout_array[6] = 64;
_retry_timeout_array[7] = 128; // if around two minutes was not enough then let's wait much longer
_retry_timeout_array[8] = 600;
_retry_timeout_array[9] = TIMEOUT_NETWORK_MAX;
_retry_array_length = MAX_RETRY_ARRAY_SIZE;
_cellularDevice = new CELLULAR_DEVICE(_at_queue);
}
CellularConnectionFSM::~CellularConnectionFSM()
@ -69,13 +72,43 @@ CellularConnectionFSM::~CellularConnectionFSM()
stop();
}
void CellularConnectionFSM::stop()
{
_queue.cancel(_event_id);
_queue.break_dispatch();
if (_queue_thread) {
_queue_thread->terminate();
delete _queue_thread;
_queue_thread = NULL;
}
delete _cellularDevice;
_cellularDevice = NULL;
// _cellularDevice closes all interfaces in destructor
_power = NULL;
_network = NULL;
_sim = NULL;
_state = STATE_INIT;
_next_state = _state;
}
nsapi_error_t CellularConnectionFSM::init()
{
tr_info("CELLULAR_DEVICE: %s", CELLULAR_STRINGIFY(CELLULAR_DEVICE));
_cellularDevice = new CELLULAR_DEVICE(_at_queue);
if (!_cellularDevice) {
stop();
return NSAPI_ERROR_NO_MEMORY;
}
_power = _cellularDevice->open_power(_serial);
if (!_power) {
stop();
return NSAPI_ERROR_NO_MEMORY;
}
_network = _cellularDevice->open_network(_serial);
if (!_network) {
stop();
@ -90,24 +123,21 @@ nsapi_error_t CellularConnectionFSM::init()
_at_queue.chain(&_queue);
tr_info("init done...");
return NSAPI_ERROR_OK;
_retry_count = 0;
_state = STATE_INIT;
_next_state = STATE_INIT;
return _network->init();
}
bool CellularConnectionFSM::open_power(FileHandle *fh)
bool CellularConnectionFSM::power_on()
{
if (!_power) {
_power = _cellularDevice->open_power(fh);
if (!_power) {
return false;
}
}
nsapi_error_t err = _power->on();
if (err != NSAPI_ERROR_OK && err != NSAPI_ERROR_UNSUPPORTED) {
tr_warn("Cellular start failed. Power off/on.");
err = _power->off();
if (err != NSAPI_ERROR_OK && err != NSAPI_ERROR_UNSUPPORTED) {
tr_error("Cellular power down failed!");
tr_error("Cellular power down failing after failed power up attempt!");
}
return false;
}
@ -117,6 +147,12 @@ bool CellularConnectionFSM::open_power(FileHandle *fh)
void CellularConnectionFSM::set_sim_pin(const char * sim_pin)
{
strncpy(_sim_pin, sim_pin, sizeof(_sim_pin));
_sim_pin[sizeof(_sim_pin)-1] = '\0';
}
void CellularConnectionFSM::set_plmn(const char* plmn)
{
_plmn = plmn;
}
bool CellularConnectionFSM::open_sim()
@ -124,62 +160,56 @@ bool CellularConnectionFSM::open_sim()
CellularSIM::SimState state = CellularSIM::SimStateUnknown;
// wait until SIM is readable
// here you could add wait(secs) if you know start delay of your SIM
while (_sim->get_sim_state(state) != NSAPI_ERROR_OK || state == CellularSIM::SimStateUnknown) {
tr_info("Waiting for SIM (state %d)...", state);
if (_sim->get_sim_state(state) != NSAPI_ERROR_OK) {
tr_info("Waiting for SIM (err while reading)...");
return false;
}
tr_info("Initial SIM state: %d", state);
if (strlen(_sim_pin)) {
nsapi_error_t err;
if (state == CellularSIM::SimStatePinNeeded) {
if (state == CellularSIM::SimStatePinNeeded) {
if (strlen(_sim_pin)) {
tr_info("SIM pin required, entering pin: %s", _sim_pin);
err = _sim->set_pin(_sim_pin);
nsapi_error_t err = _sim->set_pin(_sim_pin);
if (err) {
tr_error("SIM pin set failed with: %d, bailing out...", err);
return false;
}
// here you could add wait(secs) if you know delay of changing PIN on your SIM
for (int i = 0; i < MAX_SIM_READY_WAITING_TIME; i++) {
if (_sim->get_sim_state(state) == NSAPI_ERROR_OK && state == CellularSIM::SimStateReady) {
break;
}
tr_info("SIM state: %d", state);
return false;
}
} else {
tr_warn("PIN required but No SIM pin provided.");
}
} else {
tr_info("No SIM pin provided.");
}
if (_event_status_cb) {
_event_status_cb((nsapi_event_t)CellularSIMStatusChanged, state);
}
return state == CellularSIM::SimStateReady;
}
void CellularConnectionFSM::device_ready()
bool CellularConnectionFSM::set_network_registration()
{
CellularInformation *info = _cellularDevice->open_information(_serial);
char device_info_buf[2048]; // may be up to 2048 according to 3GPP
if (info->get_manufacturer(device_info_buf, sizeof(device_info_buf)) == NSAPI_ERROR_OK) {
tr_info("Cellular device manufacturer: %s", device_info_buf);
}
if (info->get_model(device_info_buf, sizeof(device_info_buf)) == NSAPI_ERROR_OK) {
tr_info("Cellular device model: %s", device_info_buf);
}
if (info->get_revision(device_info_buf, sizeof(device_info_buf)) == NSAPI_ERROR_OK) {
tr_info("Cellular device revision: %s", device_info_buf);
}
}
bool CellularConnectionFSM::set_network_registration(char *plmn)
{
if (_network->set_registration(plmn) != NSAPI_ERROR_OK) {
if (_network->set_registration(_plmn) != NSAPI_ERROR_OK) {
tr_error("Failed to set network registration.");
return false;
}
return true;
}
bool CellularConnectionFSM::is_registered()
{
CellularNetwork::RegistrationStatus status;
bool is_registered = false;
for (int type = 0; type < CellularNetwork::C_MAX; type++) {
if (get_network_registration((CellularNetwork::RegistrationType) type, status, is_registered)) {
tr_debug("get_network_registration: type=%d, status=%d", type, status);
if (is_registered) {
break;
}
}
}
return is_registered;
}
bool CellularConnectionFSM::get_network_registration(CellularNetwork::RegistrationType type,
CellularNetwork::RegistrationStatus &status, bool &is_registered)
{
@ -195,19 +225,19 @@ bool CellularConnectionFSM::get_network_registration(CellularNetwork::Registrati
switch (status) {
case CellularNetwork::RegisteredRoaming:
is_roaming = true;
// fall-through
// fall-through
case CellularNetwork::RegisteredHomeNetwork:
is_registered = true;
break;
case CellularNetwork::RegisteredSMSOnlyRoaming:
is_roaming = true;
// fall-through
// fall-through
case CellularNetwork::RegisteredSMSOnlyHome:
tr_warn("SMS only network registration!");
break;
case CellularNetwork::RegisteredCSFBNotPreferredRoaming:
is_roaming = true;
// fall-through
// fall-through
case CellularNetwork::RegisteredCSFBNotPreferredHome:
tr_warn("Not preferred network registration!");
break;
@ -255,10 +285,99 @@ void CellularConnectionFSM::report_failure(const char* msg)
}
}
const char* CellularConnectionFSM::get_state_string(CellularState state)
{
#if MBED_CONF_MBED_TRACE_ENABLE
static const char *strings[] = { "Init", "Power", "Device ready", "SIM pin", "Registering network", "Manual registering", "Attaching network", "Activating PDP Context", "Connecting network", "Connected"};
return strings[state];
#else
return NULL;
#endif // #if MBED_CONF_MBED_TRACE_ENABLE
}
nsapi_error_t CellularConnectionFSM::is_automatic_registering(bool& auto_reg)
{
CellularNetwork::NWRegisteringMode mode;
nsapi_error_t err = _network->get_network_registering_mode(mode);
if (err == NSAPI_ERROR_OK) {
tr_debug("automatic registering mode: %d", mode);
auto_reg = (mode == CellularNetwork::NWModeAutomatic);
}
return err;
}
bool CellularConnectionFSM::is_registered_to_plmn()
{
int format;
CellularNetwork::operator_t op;
nsapi_error_t err = _network->get_operator_params(format, op);
if (err == NSAPI_ERROR_OK) {
if (format == 2) {
// great, numeric format we can do comparison for that
if (strcmp(op.op_num, _plmn) == 0) {
return true;
}
return false;
}
// format was alpha, get operator names to do the comparing
CellularNetwork::operator_names_list names_list;
nsapi_error_t err = _network->get_operator_names(names_list);
if (err == NSAPI_ERROR_OK) {
CellularNetwork::operator_names_t* op_names = names_list.get_head();
bool found_match = false;
while (op_names) {
if (format == 0) {
if (strcmp(op.op_long, op_names->alpha) == 0) {
found_match = true;
}
} else if (format == 1) {
if (strcmp(op.op_short, op_names->alpha) == 0) {
found_match = true;
}
}
if (found_match) {
if (strcmp(_plmn, op_names->numeric)) {
names_list.delete_all();
return true;
}
names_list.delete_all();
return false;
}
}
}
names_list.delete_all();
}
return false;
}
nsapi_error_t CellularConnectionFSM::continue_from_state(CellularState state)
{
tr_info("Continue state from %s to %s", get_state_string((CellularConnectionFSM::CellularState)_state),
get_state_string((CellularConnectionFSM::CellularState)state));
_state = state;
_next_state = state;
_retry_count = 0;
if (!_queue.call_in(0, callback(this, &CellularConnectionFSM::event))) {
stop();
return NSAPI_ERROR_NO_MEMORY;
}
return NSAPI_ERROR_OK;
}
nsapi_error_t CellularConnectionFSM::continue_to_state(CellularState state)
{
MBED_ASSERT(_cellularDevice);
_retry_count = 0;
if (state < _state) {
_state = state;
} else {
// update next state so that we don't continue from previous state
_state = _next_state;
}
if (!_queue.call_in(0, callback(this, &CellularConnectionFSM::event))) {
stop();
@ -268,223 +387,243 @@ nsapi_error_t CellularConnectionFSM::continue_to_state(CellularState state)
return NSAPI_ERROR_OK;
}
void CellularConnectionFSM::enter_to_state(CellularState state)
{
_next_state = state;
_retry_count = 0;
_command_success = false;
}
void CellularConnectionFSM::retry_state_or_fail()
{
if (++_retry_count < MAX_RETRY_ARRAY_SIZE) {
tr_debug("Retry State %s, retry %d/%d", get_state_string(_state), _retry_count, MAX_RETRY_ARRAY_SIZE);
_event_timeout = _retry_timeout_array[_retry_count];
} else {
report_failure(get_state_string(_state));
return;
}
}
void CellularConnectionFSM::state_init()
{
_event_timeout = _start_time;
tr_info("Init state, waiting %d ms before POWER state)", _start_time);
enter_to_state(STATE_POWER_ON);
}
void CellularConnectionFSM::state_power_on()
{
_cellularDevice->set_timeout(TIMEOUT_POWER_ON);
tr_info("Cellular power ON (timeout %d ms)", TIMEOUT_POWER_ON);
if (power_on()) {
enter_to_state(STATE_DEVICE_READY);
} else {
// retry to power on device
retry_state_or_fail();
}
}
bool CellularConnectionFSM::device_ready()
{
tr_info("Cellular device ready");
if (_event_status_cb) {
_event_status_cb((nsapi_event_t)CellularDeviceReady, 0);
}
_power->remove_device_ready_urc_cb(mbed::callback(this, &CellularConnectionFSM::ready_urc_cb));
bool success = false;
for (int type = 0; type < CellularNetwork::C_MAX; type++) {
if (!_network->set_registration_urc((CellularNetwork::RegistrationType)type, true)) {
success = true;
}
}
if (!success) {
tr_error("Failed to set any URC's for registration");
report_failure(get_state_string(_state));
return false;
}
return true;
}
void CellularConnectionFSM::state_device_ready()
{
_cellularDevice->set_timeout(TIMEOUT_POWER_ON);
if (_power->set_at_mode() == NSAPI_ERROR_OK) {
if (device_ready()) {
enter_to_state(STATE_SIM_PIN);
}
} else {
if (_retry_count == 0) {
(void)_power->set_device_ready_urc_cb(mbed::callback(this, &CellularConnectionFSM::ready_urc_cb));
}
retry_state_or_fail();
}
}
void CellularConnectionFSM::state_sim_pin()
{
_cellularDevice->set_timeout(TIMEOUT_SIM_PIN);
tr_info("Sim state (timeout %d ms)", TIMEOUT_SIM_PIN);
if (open_sim()) {
if (_plmn) {
enter_to_state(STATE_MANUAL_REGISTERING_NETWORK);
} else {
enter_to_state(STATE_REGISTERING_NETWORK);
}
} else {
retry_state_or_fail();
}
}
void CellularConnectionFSM::state_registering()
{
_cellularDevice->set_timeout(TIMEOUT_NETWORK);
if (is_registered()) {
// we are already registered, go to attach
enter_to_state(STATE_ATTACHING_NETWORK);
} else {
bool auto_reg = false;
nsapi_error_t err = is_automatic_registering(auto_reg);
if (err == NSAPI_ERROR_OK && !auto_reg) {
// automatic registering is not on, set registration and retry
_cellularDevice->set_timeout(TIMEOUT_REGISTRATION);
set_network_registration();
}
retry_state_or_fail();
}
}
// only used when _plmn is set
void CellularConnectionFSM::state_manual_registering_network()
{
_cellularDevice->set_timeout(TIMEOUT_REGISTRATION);
tr_info("state_manual_registering_network");
if (!_plmn_network_found) {
if (is_registered() && is_registered_to_plmn()) {
_plmn_network_found = true;
enter_to_state(STATE_ATTACHING_NETWORK);
} else {
if (!_command_success) {
_command_success = set_network_registration();
}
retry_state_or_fail();
}
}
}
void CellularConnectionFSM::state_attaching()
{
_cellularDevice->set_timeout(TIMEOUT_CONNECT);
CellularNetwork::AttachStatus attach_status;
if (get_attach_network(attach_status)) {
if (attach_status == CellularNetwork::Attached) {
enter_to_state(STATE_ACTIVATING_PDP_CONTEXT);
} else {
if (!_command_success) {
_command_success = set_attach_network();
}
retry_state_or_fail();
}
} else {
retry_state_or_fail();
}
}
void CellularConnectionFSM::state_activating_pdp_context()
{
_cellularDevice->set_timeout(TIMEOUT_CONNECT);
tr_info("Activate PDP Context (timeout %d ms)", TIMEOUT_CONNECT);
if (_network->activate_context() == NSAPI_ERROR_OK) {
// when using modems stack connect is synchronous
_next_state = STATE_CONNECTING_NETWORK;
} else {
retry_state_or_fail();
}
}
void CellularConnectionFSM::state_connect_to_network()
{
_cellularDevice->set_timeout(TIMEOUT_CONNECT);
tr_info("Connect to cellular network (timeout %d ms)", TIMEOUT_CONNECT);
if (_network->connect() == NSAPI_ERROR_OK) {
_cellularDevice->set_timeout(TIMEOUT_NETWORK);
tr_debug("Connected to cellular network, set at timeout (timeout %d ms)", TIMEOUT_NETWORK);
// when using modems stack connect is synchronous
_next_state = STATE_CONNECTED;
} else {
retry_state_or_fail();
}
}
void CellularConnectionFSM::state_connected()
{
_cellularDevice->set_timeout(TIMEOUT_NETWORK);
tr_debug("Cellular ready! (timeout %d ms)", TIMEOUT_NETWORK);
if (_status_callback) {
_status_callback(_state, _next_state);
}
}
void CellularConnectionFSM::event()
{
nsapi_error_t err;
int event_timeout = -1;
_event_timeout = -1;
switch (_state) {
case STATE_INIT:
event_timeout = _start_time;
tr_info("INIT state, waiting %d ms before POWER state)", _start_time);
_next_state = STATE_POWER_ON;
state_init();
break;
case STATE_POWER_ON:
_cellularDevice->set_timeout(TIMEOUT_POWER_ON);
tr_info("Cellular power ON (timeout %d ms)", TIMEOUT_POWER_ON);
if (open_power(_serial)) {
_next_state = STATE_DEVICE_READY;
_retry_count = 0;
} else {
if (++_retry_count <= RETRY_COUNT_DEFAULT) {
tr_warn("Power ON retry %d", _retry_count);
event_timeout = 3 * 1000;
} else {
report_failure("Power");
return;
}
}
state_power_on();
break;
case STATE_DEVICE_READY:
_cellularDevice->set_timeout(TIMEOUT_POWER_ON);
if (_power->set_at_mode() == NSAPI_ERROR_OK) {
tr_info("Cellular device ready");
_next_state = STATE_SIM_PIN;
_retry_count = 0;
device_ready();
} else {
tr_info("Waiting for cellular device (retry %d/%d, timeout %d ms)", _retry_count, RETRY_COUNT_DEFAULT,
TIMEOUT_POWER_ON);
if (_retry_count++ <= RETRY_COUNT_DEFAULT) {
event_timeout = 3 * 1000;
} else {
report_failure("Power");
return;
}
}
state_device_ready();
break;
case STATE_SIM_PIN:
_cellularDevice->set_timeout(TIMEOUT_SIM_PIN);
tr_info("Start cellular (timeout %d ms)", TIMEOUT_SIM_PIN);
if (open_sim()) {
_next_state = STATE_REGISTERING_NETWORK;
_retry_count = 0;
_state_retry_count = 0;
tr_info("Check for network registration");
} else {
if (_retry_count++ <= RETRY_COUNT_DEFAULT) {
tr_warn("Waiting for SIM %d/%d", _retry_count, RETRY_COUNT_DEFAULT);
event_timeout = 3 * 1000;
} else {
report_failure("Entering SIM PIN");
return;
}
}
state_sim_pin();
break;
case STATE_REGISTERING_NETWORK:
_cellularDevice->set_timeout(TIMEOUT_NETWORK);
CellularNetwork::RegistrationStatus status;
bool is_registered;
_next_state = STATE_REGISTER_NETWORK;
for (int type = 0; type < CellularNetwork::C_MAX; type++) {
if (get_network_registration((CellularNetwork::RegistrationType) type, status, is_registered)) {
tr_debug("get_network_registration: type=%d, status=%d", type, status);
if (is_registered) {
tr_info("Registered to cellular network (type %d, status %d)", type, status);
_next_state = STATE_ATTACHING_NETWORK;
_retry_count = 0;
_state_retry_count = 0;
event_timeout = 0;
tr_info("Check cellular network attach state");
break;
} else {
if (_retry_count < 180) {
event_timeout = 1000;
_next_state = STATE_REGISTERING_NETWORK;
tr_info("Waiting for registration %d/180 (type %d, status %d)", _retry_count, type, status);
} else {
tr_info("Start cellular registration");
_next_state = STATE_REGISTER_NETWORK;
_retry_count = 0;
break;
}
}
}
}
if (_next_state == STATE_REGISTERING_NETWORK) {
_retry_count++;
}
state_registering();
break;
case STATE_REGISTER_NETWORK:
_cellularDevice->set_timeout(TIMEOUT_REGISTRATION);
tr_info("Register to cellular network (timeout %d ms)", TIMEOUT_REGISTRATION);
if (set_network_registration()) {
_next_state = STATE_REGISTERING_NETWORK;
_retry_count = 0;
if (_state_retry_count > RETRY_COUNT_DEFAULT) {
report_failure("Registration retry");
return;
}
_state_retry_count++;
} else {
if (_retry_count < _retry_array_length) {
event_timeout = _retry_timeout_array[_retry_count] * 1000;
_retry_count++;
} else {
report_failure("Registration");
return;
}
}
case STATE_MANUAL_REGISTERING_NETWORK:
state_manual_registering_network();
break;
case STATE_ATTACHING_NETWORK:
_cellularDevice->set_timeout(TIMEOUT_NETWORK);
CellularNetwork::AttachStatus attach_status;
if (get_attach_network(attach_status)) {
if (attach_status == CellularNetwork::Attached) {
_next_state = STATE_CONNECT_NETWORK;
_retry_count = 0;
} else {
_next_state = STATE_ATTACH_NETWORK;
_retry_count = 0;
}
} else {
if (_retry_count++ <= RETRY_COUNT_DEFAULT) {
event_timeout = 1 * 1000;
} else {
report_failure("Attaching");
return;
}
}
state_attaching();
break;
case STATE_ATTACH_NETWORK:
_cellularDevice->set_timeout(TIMEOUT_NETWORK);
tr_info("Attach to cellular network (timeout %d ms)", TIMEOUT_NETWORK);
if (set_attach_network()) {
_next_state = STATE_ATTACHING_NETWORK;
_retry_count = 0;
if (_state_retry_count >= RETRY_COUNT_DEFAULT) {
report_failure("Attach retry");
return;
}
_state_retry_count++;
tr_info("Cellular network attaching");
} else {
if (_retry_count < _retry_array_length) {
event_timeout = _retry_timeout_array[_retry_count] * 1000;
_retry_count++;
} else {
report_failure("Attach");
return;
}
}
case STATE_ACTIVATING_PDP_CONTEXT:
state_activating_pdp_context();
break;
case STATE_CONNECT_NETWORK:
_cellularDevice->set_timeout(TIMEOUT_NETWORK);
tr_info("Connect to cellular network (timeout %d ms)", TIMEOUT_NETWORK);
err = _network->connect();
if (!err) {
_next_state = STATE_CONNECTED;
} else {
if (_retry_count < _retry_array_length) {
event_timeout = _retry_timeout_array[_retry_count] * 1000;
_retry_count++;
} else {
report_failure("Network Connect");
return;
}
}
case STATE_CONNECTING_NETWORK:
state_connect_to_network();
break;
case STATE_CONNECTED:
_cellularDevice->set_timeout(TIMEOUT_NETWORK);
tr_debug("Cellular ready! (timeout %d ms)", TIMEOUT_NETWORK);
if (_status_callback) {
if (!_status_callback(_state, _next_state)) {
return;
}
}
state_connected();
break;
default:
MBED_ASSERT(0);
break;
}
if (_next_state != _state || event_timeout >= 0) {
if (_next_state != _state || _event_timeout >= 0) {
if (_next_state != _state) { // state exit condition
tr_info("Cellular state from %d to %d", _state, _next_state);
tr_info("Cellular state from %s to %s", get_state_string((CellularConnectionFSM::CellularState)_state),
get_state_string((CellularConnectionFSM::CellularState)_next_state));
if (_status_callback) {
if (!_status_callback(_state, _next_state)) {
return;
}
}
} else {
if (event_timeout == 0) {
static int retry_count = 0;
if (++retry_count <= 3) {
tr_info("Cellular event retry %d", retry_count);
} else {
report_failure("Cellular connection failed!");
return;
}
} else {
tr_info("Cellular event in %d milliseconds", event_timeout);
}
tr_info("Cellular event in %d seconds", _event_timeout);
}
_state = _next_state;
if (event_timeout == -1) {
event_timeout = 0;
if (_event_timeout == -1) {
_event_timeout = 0;
}
if (!_queue.call_in(event_timeout, callback(this, &CellularConnectionFSM::event))) {
_event_id = _queue.call_in(_event_timeout*1000, callback(this, &CellularConnectionFSM::event));
if (!_event_id) {
report_failure("Cellular event failure!");
return;
}
@ -493,12 +632,9 @@ void CellularConnectionFSM::event()
nsapi_error_t CellularConnectionFSM::start_dispatch()
{
tr_info("CellularConnectionUtil::start");
tr_info("Create cellular thread");
MBED_ASSERT(!_queue_thread);
_queue_thread = new rtos::Thread;
_queue_thread = new rtos::Thread(osPriorityNormal, 2048);
if (!_queue_thread) {
stop();
return NSAPI_ERROR_NO_MEMORY;
@ -508,21 +644,9 @@ nsapi_error_t CellularConnectionFSM::start_dispatch()
return NSAPI_ERROR_NO_MEMORY;
}
tr_info("CellularConnectionUtil::started");
return NSAPI_ERROR_OK;
}
void CellularConnectionFSM::stop()
{
tr_info("CellularConnectionUtil::stop");
_cellularDevice->close_power();
_cellularDevice->close_network();
if (_queue_thread) {
_queue_thread->terminate();
_queue_thread = NULL;
}
}
void CellularConnectionFSM::set_serial(UARTSerial *serial)
{
_serial = serial;
@ -533,6 +657,56 @@ void CellularConnectionFSM::set_callback(mbed::Callback<bool(int, int)> status_c
_status_callback = status_callback;
}
void CellularConnectionFSM::attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
{
MBED_ASSERT(_network);
_event_status_cb = status_cb;
if (status_cb) {
_network->attach(callback(this, &CellularConnectionFSM::network_callback));
} else {
_network->attach(NULL);
}
}
void CellularConnectionFSM::network_callback(nsapi_event_t ev, intptr_t ptr)
{
tr_info("FSM: network_callback called with event: %d, intptr: %d, _state: %s", ev, ptr, get_state_string(_state));
if ((cellular_connection_status_t)ev == CellularRegistrationStatusChanged &&
(_state == STATE_REGISTERING_NETWORK || _state == STATE_MANUAL_REGISTERING_NETWORK)) {
// expect packet data so only these states are valid
if (ptr == CellularNetwork::RegisteredHomeNetwork || ptr == CellularNetwork::RegisteredRoaming) {
if (_plmn) {
if (is_registered_to_plmn()) {
if (!_plmn_network_found) {
_plmn_network_found = true;
_queue.cancel(_event_id);
continue_from_state(STATE_ATTACHING_NETWORK);
}
}
} else {
_queue.cancel(_event_id);
continue_from_state(STATE_ATTACHING_NETWORK);
}
}
}
if (_event_status_cb) {
_event_status_cb(ev, ptr);
}
}
void CellularConnectionFSM::ready_urc_cb()
{
tr_debug("Device ready URC func called");
if (_state == STATE_DEVICE_READY && _power->set_at_mode() == NSAPI_ERROR_OK) {
tr_debug("State was STATE_DEVICE_READY and at mode ready, cancel state and move to next");
_queue.cancel(_event_id);
if (device_ready()) {
continue_from_state(STATE_SIM_PIN);
}
}
}
events::EventQueue *CellularConnectionFSM::get_queue()
{
return &_queue;

View File

@ -29,10 +29,9 @@
#include "CellularNetwork.h"
#include "CellularPower.h"
#include "CellularSIM.h"
#include "CellularUtil.h"
// modem type is defined as CELLULAR_DEVICE macro
#define _CELLULAR_STRINGIFY(a) #a
#define CELLULAR_STRINGIFY(a) _CELLULAR_STRINGIFY(a)
#include CELLULAR_STRINGIFY(CELLULAR_DEVICE.h)
namespace mbed {
@ -58,12 +57,12 @@ public:
STATE_POWER_ON,
STATE_DEVICE_READY,
STATE_SIM_PIN,
STATE_REGISTER_NETWORK,
STATE_REGISTERING_NETWORK,
STATE_ATTACH_NETWORK,
STATE_MANUAL_REGISTERING_NETWORK,
STATE_ATTACHING_NETWORK,
STATE_CONNECT_NETWORK,
STATE_CONNECTED,
STATE_ACTIVATING_PDP_CONTEXT,
STATE_CONNECTING_NETWORK,
STATE_CONNECTED
};
public:
@ -83,6 +82,16 @@ public:
*/
void set_callback(mbed::Callback<bool(int, int)> status_callback);
/** Register callback for status reporting
*
* The specified status callback function will be called on status changes
* on the network. The parameters on the callback are the event type and
* event-type dependent reason parameter.
*
* @param status_cb The callback for status changes
*/
virtual void attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb);
/** Get event queue that can be chained to main event queue (or use start_dispatch)
* @return event queue
*/
@ -93,7 +102,8 @@ public:
*/
nsapi_error_t start_dispatch();
/** Stop event queue dispatching and close cellular interfaces
/** Stop event queue dispatching and close cellular interfaces. After calling stop(), init() must be called
* before any other methods.
*/
void stop();
@ -113,10 +123,10 @@ public:
CellularSIM* get_sim();
/** Change cellular connection to the target state
* @param state to continue
* @param state to continue. Default is to connect.
* @return see nsapi_error_t, 0 on success
*/
nsapi_error_t continue_to_state(CellularState state);
nsapi_error_t continue_to_state(CellularState state = STATE_CONNECTED);
/** Set cellular device SIM PIN code
* @param sim_pin PIN code
@ -131,28 +141,63 @@ public:
*/
void set_retry_timeout_array(uint16_t timeout[], int array_len);
/** Sets the operator plmn which is used when registering to a network specified by plmn. If plmn is not set then automatic
* registering is used when registering to a cellular network. Does not start any operations.
*
* @param plmn operator in numeric format. See more from 3GPP TS 27.007 chapter 7.3.
*/
void set_plmn(const char* plmn);
/** returns readable format of the given state. Used for printing states while debugging.
*
* @param state state which is returned in string format
* @return string format of the given state
*/
const char* get_state_string(CellularState state);
private:
bool open_power(FileHandle *fh);
bool power_on();
bool open_sim();
bool get_network_registration(CellularNetwork::RegistrationType type, CellularNetwork::RegistrationStatus &status, bool &is_registered);
bool set_network_registration(char *plmn = 0);
bool set_network_registration();
bool get_attach_network(CellularNetwork::AttachStatus &status);
bool set_attach_network();
bool is_registered();
bool device_ready();
nsapi_error_t is_automatic_registering(bool& auto_reg);
// state functions to keep state machine simple
void state_init();
void state_power_on();
void state_device_ready();
void state_sim_pin();
void state_registering();
void state_manual_registering_network();
void state_attaching();
void state_activating_pdp_context();
void state_connect_to_network();
void state_connected();
void enter_to_state(CellularState state);
void retry_state_or_fail();
void network_callback(nsapi_event_t ev, intptr_t ptr);
nsapi_error_t continue_from_state(CellularState state);
bool is_registered_to_plmn();
private:
friend class EasyCellularConnection;
NetworkStack *get_stack();
private:
void device_ready();
void report_failure(const char* msg);
void event();
void ready_urc_cb();
UARTSerial *_serial;
CellularState _state;
CellularState _next_state;
Callback<bool(int, int)> _status_callback;
Callback<void(nsapi_event_t, intptr_t)> _event_status_cb;
CellularNetwork *_network;
CellularPower *_power;
@ -162,11 +207,17 @@ private:
CellularDevice *_cellularDevice;
char _sim_pin[PIN_SIZE+1];
int _retry_count;
int _state_retry_count;
int _start_time;
int _event_timeout;
uint16_t _retry_timeout_array[MAX_RETRY_ARRAY_SIZE];
int _retry_array_length;
events::EventQueue _at_queue;
char _st_string[20];
int _event_id;
const char* _plmn;
bool _command_success;
bool _plmn_network_found;
};
} // namespace

View File

@ -37,26 +37,35 @@ namespace mbed {
bool EasyCellularConnection::cellular_status(int state, int next_state)
{
tr_info("cellular_status %d=>%d", state, next_state);
tr_info("cellular_status: %s ==> %s", _cellularConnectionFSM.get_state_string((CellularConnectionFSM::CellularState)state),
_cellularConnectionFSM.get_state_string((CellularConnectionFSM::CellularState)next_state));
if (_target_state == state) {
if (state == CellularConnectionFSM::STATE_CONNECTED) {
tr_info("Target state reached: %s", _cellularConnectionFSM.get_state_string(_target_state));
MBED_ASSERT(_cellularSemaphore.release() == osOK);
return false; // return false -> state machine is halted
}
return true;
}
void EasyCellularConnection::network_callback(nsapi_event_t ev, intptr_t ptr)
{
if (ev == NSAPI_EVENT_CONNECTION_STATUS_CHANGE) {
if (ptr == NSAPI_STATUS_GLOBAL_UP) {
_is_connected = true;
} else {
_is_connected = false;
}
tr_info("Target state reached: %d", _target_state);
MBED_ASSERT(_cellularSemaphore.release() == osOK);
return false;
} else {
_is_connected = false;
}
return true;
if (_status_cb) {
_status_cb(ev, ptr);
}
}
EasyCellularConnection::EasyCellularConnection(bool debug) :
_is_connected(false), _is_initialized(false), _target_state(CellularConnectionFSM::STATE_POWER_ON), _cellularSerial(
MDMTXD, MDMRXD, MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE), _cellularSemaphore(0), _cellularConnectionFSM(), _credentials_err(
NSAPI_ERROR_OK)
NSAPI_ERROR_OK), _status_cb(0)
{
tr_info("EasyCellularConnection()");
#if USE_APN_LOOKUP
@ -67,7 +76,8 @@ EasyCellularConnection::EasyCellularConnection(bool debug) :
EasyCellularConnection::~EasyCellularConnection()
{
_cellularConnectionFSM.stop();
_cellularConnectionFSM.set_callback(NULL);
_cellularConnectionFSM.attach(NULL);
}
nsapi_error_t EasyCellularConnection::init()
@ -84,6 +94,7 @@ nsapi_error_t EasyCellularConnection::init()
if (err == NSAPI_ERROR_OK) {
err = _cellularConnectionFSM.start_dispatch();
_cellularConnectionFSM.attach(callback(this, &EasyCellularConnection::network_callback));
}
_is_initialized = true;
}
@ -108,7 +119,9 @@ void EasyCellularConnection::set_credentials(const char *apn, const char *uname,
}
#endif // #if USE_APN_LOOKUP
} else {
tr_error("NO Network...");
//if get_network() returns NULL it means there was not enough memory for
//an AT_CellularNetwork element during CellularConnectionFSM initialization
tr_error("There was not enough memory during CellularConnectionFSM initialization");
}
}
}
@ -189,7 +202,7 @@ nsapi_error_t EasyCellularConnection::connect()
}
}
if (err) {
tr_info("APN lookup failed");
tr_error("APN lookup failed");
return err;
}
}
@ -212,13 +225,19 @@ nsapi_error_t EasyCellularConnection::disconnect()
{
_credentials_err = NSAPI_ERROR_OK;
_is_connected = false;
_is_initialized = false;
#if USE_APN_LOOKUP
_credentials_set = false;
#endif // #if USE_APN_LOOKUP
if (!_cellularConnectionFSM.get_network()) {
return NSAPI_ERROR_NO_CONNECTION;
nsapi_error_t err = NSAPI_ERROR_OK;
if (_cellularConnectionFSM.get_network()) {
err = _cellularConnectionFSM.get_network()->disconnect();
}
return _cellularConnectionFSM.get_network()->disconnect();
_cellularConnectionFSM.stop();
return err;
}
bool EasyCellularConnection::is_connected()
@ -257,10 +276,7 @@ const char *EasyCellularConnection::get_gateway()
void EasyCellularConnection::attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
{
CellularNetwork *network = _cellularConnectionFSM.get_network();
if (network) {
network->attach(status_cb);
}
_status_cb = status_cb;
}
void EasyCellularConnection::modem_debug_on(bool on)
@ -271,6 +287,11 @@ void EasyCellularConnection::modem_debug_on(bool on)
}
}
void EasyCellularConnection::set_plmn(const char* plmn)
{
_cellularConnectionFSM.set_plmn(plmn);
}
NetworkStack *EasyCellularConnection::get_stack()
{
return _cellularConnectionFSM.get_stack();

View File

@ -16,7 +16,6 @@
*/
#ifndef EASY_CELLULAR_CONNECTION_H
#define EASY_CELLULAR_CONNECTION_H
#include "CellularConnectionFSM.h"
@ -117,6 +116,10 @@ public:
virtual const char *get_gateway();
/** Register callback for status reporting
*
* The specified status callback function will be called on status changes
* on the network. The parameters on the callback are the event type and
* event-type dependent reason parameter.
*
* @param status_cb The callback for status changes
*/
@ -128,6 +131,12 @@ public:
*/
void modem_debug_on(bool on);
/** Sets the operator plmn which is used when registering to a network specified by plmn. If plmn is not set then automatic
* registering is used when registering to a cellular network.
*
* @param plmn operator in numeric format. See more from 3GPP TS 27.007 chapter 7.3.
*/
void set_plmn(const char* plmn);
protected:
/** Provide access to the NetworkStack object
@ -142,6 +151,7 @@ private:
* @return true to continue state machine
*/
bool cellular_status(int state, int next_state);
void network_callback(nsapi_event_t ev, intptr_t ptr);
nsapi_error_t init();
nsapi_error_t check_connect();
@ -156,6 +166,7 @@ private:
rtos::Semaphore _cellularSemaphore;
CellularConnectionFSM _cellularConnectionFSM;
nsapi_error_t _credentials_err;
Callback<void(nsapi_event_t, intptr_t)> _status_cb;
};
} // namespace

View File

@ -83,19 +83,19 @@ public:
*/
virtual void close_network() = 0;
/** Closes the opened CellularNetwork by deleting the CellularSMS instance.
/** Closes the opened CellularSMS by deleting the CellularSMS instance.
*/
virtual void close_sms() = 0;
/** Closes the opened CellularNetwork by deleting the CellularPower instance.
/** Closes the opened CellularPower by deleting the CellularPower instance.
*/
virtual void close_power() = 0;
/** Closes the opened CellularNetwork by deleting the CellularSIM instance.
/** Closes the opened CellularSIM by deleting the CellularSIM instance.
*/
virtual void close_sim() = 0;
/** Closes the opened CellularNetwork by deleting the CellularInformation instance.
/** Closes the opened CellularInformation by deleting the CellularInformation instance.
*/
virtual void close_information() = 0;

View File

@ -116,16 +116,7 @@ public:
CHAP
};
// 3GPP TS 27.007 - 7.3 PLMN selection +COPS
struct operator_t {
enum Status {
Unknown,
Available,
Current,
Forbiden
};
enum RadioAccessTechnology {
enum RadioAccessTechnology {
RAT_GSM,
RAT_GSM_COMPACT,
RAT_UTRAN,
@ -139,6 +130,14 @@ public:
RAT_UNKNOWN
};
// 3GPP TS 27.007 - 7.3 PLMN selection +COPS
struct operator_t {
enum Status {
Unknown,
Available,
Current,
Forbiden
};
Status op_status;
char op_long[MAX_OPERATOR_NAME_LONG+1];
@ -200,6 +199,35 @@ public:
};
typedef CellularList<pdpcontext_params_t> pdpContextList_t;
struct operator_names_t {
char numeric[MAX_OPERATOR_NAME_SHORT+1];
char alpha[MAX_OPERATOR_NAME_LONG+1];
operator_names_t* next;
operator_names_t() {
numeric[0] = '\0';
alpha[0] = '\0';
next = NULL;
}
};
typedef CellularList<operator_names_t> operator_names_list;
/* Network registering mode */
enum NWRegisteringMode {
NWModeAutomatic = 0, // automatic registering
NWModeManual, // manual registering with plmn
NWModeDeRegister, // deregister from network
NWModeSetOnly, // set only <format> (for read command +COPS?), do not attempt registration/deregistration
NWModeManualAutomatic // if manual fails, fallback to automatic
};
/** Does all the needed initializations that can fail
*
* @remark must be called immediately after constructor.
* @return zero on success
*/
virtual nsapi_error_t init() = 0;
/** Request registering to network.
*
* @param plmn format is in numeric format or 0 for automatic network registration
@ -207,6 +235,24 @@ public:
*/
virtual nsapi_error_t set_registration(const char *plmn = 0) = 0;
/** Get the current network registering mode
*
* @param mode on successful return contains the current network registering mode
* @return zero on success
*/
virtual nsapi_error_t get_network_registering_mode(NWRegisteringMode& mode) = 0;
/** Activate/deactivate listening of network events for the given RegistrationType.
* This should be called after network class is created and ready to receive AT commands.
* After successful call network class starts to get information about network changes like
* registration statue, access technology, cell id...
*
* @param type RegistrationType to set urc on/off
* @param on Controls are urc' active or not
* @return zero on success
*/
virtual nsapi_error_t set_registration_urc(RegistrationType type, bool on) = 0;
/** Gets the network registration status.
*
* @param type see RegistrationType values
@ -238,9 +284,11 @@ public:
/** Request attach to network.
*
* @deprecated Parameter timeout will be deprecated. Use mbed-os/features/cellular/framework/API/CellularDevice.h set_timeout instead.
* @param timeout milliseconds to wait for attach response
* @return zero on success
*/
MBED_DEPRECATED_SINCE("mbed-os-5.9", "Parameter timeout will be deprecated. Use mbed-os/features/cellular/framework/API/CellularDevice.h set_timeout instead.")
virtual nsapi_error_t set_attach(int timeout = 10*1000) = 0;
/** Request attach status from network.
@ -250,6 +298,12 @@ public:
*/
virtual nsapi_error_t get_attach(AttachStatus &status) = 0;
/** Request detach from a network.
*
* @return zero on success
*/
virtual nsapi_error_t detach() = 0;
/** Get APN rate control.
*
* @remark optional params are not updated if not received from network, so use good defaults
@ -270,10 +324,17 @@ public:
/** Sets radio access technology.
*
* @param op_rat Radio access technology
* @return zero on success
* @param rat Radio access technology
* @return zero on success
*/
virtual nsapi_error_t set_access_technology(operator_t::RadioAccessTechnology op_rat) = 0;
virtual nsapi_error_t set_access_technology(RadioAccessTechnology rat) = 0;
/** Get current radio access technology.
*
* @param rat Radio access technology
* @return zero on success
*/
virtual nsapi_error_t get_access_technology(RadioAccessTechnology& rat) = 0;
/** Scans for operators module can reach.
*
@ -317,6 +378,13 @@ public:
virtual nsapi_error_t connect(const char *apn,
const char *username = 0, const char *password = 0) = 0;
/** Finds the correct PDP context and activates it. If correct PDP context is not found, one is created.
* Given APN (or not given) and stack type (IPv4/IPv6/dual) are influencing when finding the PDP context.
*
* @return zero on success
*/
virtual nsapi_error_t activate_context() = 0;
/**
* Set the pdn type to be used
*
@ -404,6 +472,12 @@ public:
*/
virtual nsapi_error_t set_blocking(bool blocking) = 0;
/** Read operator names
*
* @param op_names on successful return will contain linked list of operator names.
* @return zero on success
*/
virtual nsapi_error_t get_operator_names(operator_names_list &op_names) = 0;
};
} // namespace mbed

View File

@ -18,8 +18,10 @@
#define CELLULAR_API_CELLULARPOWER_H_
#include "nsapi_types.h"
#include "Callback.h"
namespace mbed {
namespace mbed
{
/**
* Class CellularPower
@ -118,6 +120,21 @@ public:
* @return zero on success
*/
virtual nsapi_error_t opt_receive_period(int mode, EDRXAccessTechnology act_type, uint8_t edrx_value) = 0;
/** Set URC callback function for device specific ready urc. URC is defined in device specific
* power API. Used in startup sequence to listen when device is ready
* for using at commands and possible sim.
*
* @param callback Callback function called when urc received
* @return zero on success
*/
virtual nsapi_error_t set_device_ready_urc_cb(mbed::Callback<void()> callback) = 0;
/** Removes the device ready urc from the list of urc's.
*
* @param callback callback to remove from the list of urc's
*/
virtual void remove_device_ready_urc_cb(mbed::Callback<void()> callback) = 0;
};
} // namespace mbed

View File

@ -24,9 +24,12 @@
#ifdef MBED_CONF_RTOS_PRESENT
#include "rtos/Thread.h"
#endif
#include "Kernel.h"
#include "CellularUtil.h"
using namespace mbed;
using namespace events;
using namespace mbed_cellular_util;
//#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_DEBUG
#include "CellularLog.h"
@ -58,9 +61,8 @@ static const uint8_t map_3gpp_errors[][2] = {
{ 146, 46 }, { 178, 65 }, { 179, 66 }, { 180, 48 }, { 181, 83 }, { 171, 49 },
};
ATHandler::ATHandler(FileHandle *fh, EventQueue &queue, int timeout, const char *output_delimiter) :
ATHandler::ATHandler(FileHandle *fh, EventQueue &queue, int timeout, const char *output_delimiter, uint16_t send_delay) :
_nextATHandler(0),
_fileHandle(fh),
_queue(queue),
_last_err(NSAPI_ERROR_OK),
_last_3gpp_error(0),
@ -68,6 +70,8 @@ ATHandler::ATHandler(FileHandle *fh, EventQueue &queue, int timeout, const char
_oobs(NULL),
_at_timeout(timeout),
_previous_at_timeout(timeout),
_at_send_delay(send_delay),
_last_response_stop(0),
_fh_sigio_set(false),
_processing(false),
_ref_count(1),
@ -85,17 +89,18 @@ ATHandler::ATHandler(FileHandle *fh, EventQueue &queue, int timeout, const char
clear_error();
if (output_delimiter) {
_output_delimiter_length = strlen(output_delimiter);
_output_delimiter = new char[_output_delimiter_length];
for (unsigned i=0; i<_output_delimiter_length; i++) {
_output_delimiter[i] = output_delimiter[i];
_output_delimiter = new char[strlen(output_delimiter) + 1];
if (!_output_delimiter) {
MBED_ASSERT(0);
} else {
memcpy(_output_delimiter, output_delimiter, strlen(output_delimiter) + 1);
}
} else {
_output_delimiter = NULL;
_output_delimiter_length = 0;
_output_delimiter = NULL;
}
reset_buffer();
memset(_recv_buff, 0, sizeof(_recv_buff));
memset(_info_resp_prefix, 0, sizeof(_info_resp_prefix));
_current_scope = NotSet;
@ -103,9 +108,7 @@ ATHandler::ATHandler(FileHandle *fh, EventQueue &queue, int timeout, const char
set_tag(&_info_stop, CRLF);
set_tag(&_elem_stop, ")");
_fileHandle->set_blocking(false);
set_filehandle_sigio();
set_file_handle(fh);
}
void ATHandler::enable_debug(bool enable)
@ -147,24 +150,81 @@ FileHandle *ATHandler::get_file_handle()
void ATHandler::set_file_handle(FileHandle *fh)
{
_fh_sigio_set = false;
_fileHandle = fh;
_fileHandle->set_blocking(false);
set_filehandle_sigio();
}
void ATHandler::set_urc_handler(const char *prefix, mbed::Callback<void()> callback)
void ATHandler::set_filehandle_sigio()
{
struct oob_t *oob = new struct oob_t;
oob->matching_to_received = true;
size_t prefix_len = strlen(prefix);
if (prefix_len > _oob_string_max_length) {
_oob_string_max_length = prefix_len;
if (_oob_string_max_length > _max_resp_length) {
_max_resp_length = _oob_string_max_length;
}
if (_fh_sigio_set) {
return;
}
oob->prefix = prefix;
oob->cb = callback;
oob->next = _oobs;
_oobs = oob;
_fileHandle->sigio(mbed::Callback<void()>(this, &ATHandler::event));
_fh_sigio_set = true;
}
nsapi_error_t ATHandler::set_urc_handler(const char *prefix, mbed::Callback<void()> callback)
{
if (find_urc_handler(prefix, callback)) {
tr_warn("URC already added with prefix: %s", prefix);
return NSAPI_ERROR_OK;
}
struct oob_t *oob = new struct oob_t;
if (!oob) {
return NSAPI_ERROR_NO_MEMORY;
} else {
size_t prefix_len = strlen(prefix);
if (prefix_len > _oob_string_max_length) {
_oob_string_max_length = prefix_len;
if (_oob_string_max_length > _max_resp_length) {
_max_resp_length = _oob_string_max_length;
}
}
oob->prefix = prefix;
oob->prefix_len = prefix_len;
oob->cb = callback;
oob->next = _oobs;
_oobs = oob;
}
return NSAPI_ERROR_OK;
}
void ATHandler::remove_urc_handler(const char *prefix, mbed::Callback<void()> callback)
{
struct oob_t *current = _oobs;
struct oob_t *prev = NULL;
while (current) {
if (strcmp(prefix, current->prefix) == 0 && current->cb == callback) {
if (prev) {
prev->next = current->next;
} else {
_oobs = current->next;
}
delete current;
break;
}
prev = current;
current = prev->next;
}
}
bool ATHandler::find_urc_handler(const char *prefix, mbed::Callback<void()> callback)
{
struct oob_t *oob = _oobs;
while (oob) {
if (strcmp(prefix, oob->prefix) == 0 && oob->cb == callback) {
return true;
}
oob = oob->next;
}
return false;
}
void ATHandler::event()
@ -231,6 +291,7 @@ void ATHandler::process_oob()
timer.start();
do {
if (match_urc()) {
timer.reset();
if (_fileHandle->readable() || (_recv_pos < _recv_len)) {
continue;
}
@ -239,8 +300,10 @@ void ATHandler::process_oob()
// If no match found, look for CRLF and consume everything up to CRLF
if (mem_str(_recv_buff, _recv_len, CRLF, CRLF_LENGTH)) {
consume_to_tag(CRLF, true);
timer.reset();
} else {
if (_fileHandle->readable()) {
timer.reset();
fill_buffer();
} else {
#ifdef MBED_CONF_RTOS_PRESENT
@ -248,7 +311,7 @@ void ATHandler::process_oob()
#endif
}
}
} while (timer.read_ms() < 20); // URC's are very short so 20ms should be enough
} while (timer.read_ms() < 100); // URC's are very short
}
tr_debug("process_oob exit");
@ -257,19 +320,11 @@ void ATHandler::process_oob()
unlock();
}
void ATHandler::set_filehandle_sigio()
{
if (_fh_sigio_set) {
return;
}
_fileHandle->sigio(mbed::Callback<void()>(this, &ATHandler::event));
_fh_sigio_set = true;
}
void ATHandler::reset_buffer()
{
tr_debug("%s", __func__);
_recv_pos = 0; _recv_len = 0;
_recv_pos = 0;
_recv_len = 0;
}
void ATHandler::rewind_buffer()
@ -404,57 +459,78 @@ ssize_t ATHandler::read_bytes(uint8_t *buf, size_t len)
return read_len;
}
ssize_t ATHandler::read_string(char *buf, size_t size, bool read_even_stop_tag)
ssize_t ATHandler::read(char *buf, size_t size, bool read_even_stop_tag, bool hex)
{
tr_debug("%s", __func__);
at_debug("\n----------read_string buff:----------\n");
at_debug("\n----------read buff:----------\n");
for (size_t i = _recv_pos; i < _recv_len; i++) {
at_debug("%c", _recv_buff[i]);
}
at_debug("\n----------buff----------\n");
at_debug("\n----------read end----------\n");
if (_last_err || !_stop_tag || (_stop_tag->found && read_even_stop_tag == false)) {
return -1;
}
uint8_t *pbuf = (uint8_t*)buf;
size_t len = 0;
size_t match_pos = 0;
size_t read_size = hex ? size*2 : size;
consume_char('\"');
for (; len < (size + match_pos); len++) {
size_t read_idx = 0;
size_t buf_idx = 0;
char hexbuf[2];
for (; read_idx < (read_size + match_pos); read_idx++) {
int c = get_char();
buf_idx = hex ? read_idx/2 : read_idx;
if (c == -1) {
pbuf[len] = '\0';
buf[buf_idx] = '\0';
set_error(NSAPI_ERROR_DEVICE_ERROR);
return -1;
} else if (c == _delimiter) {
pbuf[len] = '\0';
buf[buf_idx] = '\0';
break;
} else if (c == '\"') {
match_pos = 0;
len--;
if (read_idx > 0) {
read_idx--;
}
continue;
} else if (_stop_tag->len && c == _stop_tag->tag[match_pos]) {
match_pos++;
if (match_pos == _stop_tag->len) {
_stop_tag->found = true;
// remove tag from string if it was matched
len -= (_stop_tag->len - 1);
pbuf[len] = '\0';
buf_idx -= (_stop_tag->len - 1);
buf[buf_idx] = '\0';
break;
}
} else if (match_pos) {
match_pos = 0;
}
pbuf[len] = c;
if (!hex) {
buf[buf_idx] = c;
} else {
hexbuf[read_idx % 2] = c;
if (read_idx % 2 == 1) {
hex_str_to_char_str(hexbuf, 2, buf+buf_idx);
}
}
}
// Do we need _stop_found set after reading by size -> is _stop_tag_by_len needed or not?
return len;
return buf_idx;
}
ssize_t ATHandler::read_string(char *buf, size_t size, bool read_even_stop_tag)
{
return read(buf, size, read_even_stop_tag, false);
}
ssize_t ATHandler::read_hex_string(char *buf, size_t size)
{
return read(buf, size, false, true);
}
int32_t ATHandler::read_int()
@ -558,10 +634,10 @@ bool ATHandler::match_urc()
rewind_buffer();
size_t prefix_len = 0;
for (struct oob_t *oob = _oobs; oob; oob = oob->next) {
prefix_len = strlen(oob->prefix);
prefix_len = oob->prefix_len;
if (_recv_len >= prefix_len) {
if (match(oob->prefix, prefix_len)) {
tr_debug("URC! %s", oob->prefix);
tr_debug("URC! %s\n", oob->prefix);
set_scope(InfoType);
if (oob->cb) {
oob->cb();
@ -648,11 +724,9 @@ void ATHandler::set_3gpp_error(int err, DeviceErrorType error_type)
void ATHandler::at_error(bool error_code_expected, DeviceErrorType error_type)
{
int32_t err = -1;
if (error_code_expected && (error_type == DeviceErrorTypeErrorCMS || error_type == DeviceErrorTypeErrorCME)) {
set_scope(InfoType);
err = read_int();
int32_t err = read_int();
if (err != -1) {
set_3gpp_error(err, error_type);
@ -893,6 +967,8 @@ void ATHandler::resp_stop()
set_tag(&_resp_stop, OK);
// Reset info resp prefix
memset(_info_resp_prefix, 0, sizeof(_info_resp_prefix));
_last_response_stop = rtos::Kernel::get_ms_count();
}
void ATHandler::information_response_stop()
@ -936,7 +1012,18 @@ const char* ATHandler::mem_str(const char* dest, size_t dest_len, const char* sr
void ATHandler::cmd_start(const char* cmd)
{
tr_debug("AT> %s", cmd);
if (_at_send_delay) {
uint64_t current_time = rtos::Kernel::get_ms_count();
uint64_t time_difference = current_time - _last_response_stop;
if (time_difference < (uint64_t)_at_send_delay) {
wait_ms((uint64_t)_at_send_delay - time_difference);
tr_debug("AT wait %llu %llu", current_time, _last_response_stop);
}
}
at_debug("AT cmd %s (err %d)\n", cmd, _last_err);
if (_last_err != NSAPI_ERROR_OK) {
return;
@ -949,7 +1036,7 @@ void ATHandler::cmd_start(const char* cmd)
void ATHandler::write_int(int32_t param)
{
tr_debug("write_int: %d", param);
at_debug("AT int %d\n", param);
// do common checks before sending subparameter
if (check_cmd_send() == false) {
return;
@ -966,7 +1053,7 @@ void ATHandler::write_int(int32_t param)
void ATHandler::write_string(const char* param, bool useQuotations)
{
tr_debug("write_string: %s, %d", param, useQuotations);
at_debug("AT str %s (with quotes %d)\n", param, useQuotations);
// do common checks before sending subparameter
if (check_cmd_send() == false) {
return;
@ -987,37 +1074,43 @@ void ATHandler::write_string(const char* param, bool useQuotations)
void ATHandler::cmd_stop()
{
at_debug("AT stop %s (err %d)\n", _output_delimiter, _last_err);
if (_last_err != NSAPI_ERROR_OK) {
return;
}
// Finish with CR
(void)write(_output_delimiter, _output_delimiter_length);
(void)write(_output_delimiter, strlen(_output_delimiter));
}
size_t ATHandler::write_bytes(const uint8_t *data, size_t len)
{
at_debug("AT write bytes %d (err %d)\n", len, _last_err);
if (_last_err != NSAPI_ERROR_OK) {
return 0;
}
ssize_t write_len = write(data, len);
return write_len < 0 ? 0 : (size_t)write_len;
return write(data, len);
}
ssize_t ATHandler::write(const void *data, size_t len)
size_t ATHandler::write(const void *data, size_t len)
{
pollfh fhs;
fhs.fh = _fileHandle;
fhs.events = POLLOUT;
ssize_t write_len = -1;
int count = poll(&fhs, 1, _at_timeout);
if (count > 0 && (fhs.revents & POLLOUT)) {
write_len = _fileHandle->write(data, len);
}
if (write_len < 0 || (size_t)write_len != len) {
set_error(NSAPI_ERROR_DEVICE_ERROR);
size_t write_len = 0;
for (; write_len < len; ) {
int count = poll(&fhs, 1, _at_timeout);
if (count <= 0 || !(fhs.revents & POLLOUT)) {
set_error(NSAPI_ERROR_DEVICE_ERROR);
return 0;
}
ssize_t ret = _fileHandle->write((uint8_t*)data + write_len, len - write_len);
if (ret < 0) {
set_error(NSAPI_ERROR_DEVICE_ERROR);
return 0;
}
write_len += (size_t)ret;
}
return write_len;

View File

@ -29,7 +29,8 @@
#include "Callback.h"
#include "EventQueue.h"
namespace mbed {
namespace mbed
{
class FileHandle;
@ -74,8 +75,9 @@ public:
* @param queue Event queue used to transfer sigio events to this thread
* @param timeout Timeout when reading for AT response
* @param output_delimiter delimiter used when parsing at responses, "\r" should be used as output_delimiter
* @param send_delay the minimum delay in ms between the end of last response and the beginning of a new command
*/
ATHandler(FileHandle *fh, events::EventQueue &queue, int timeout, const char *output_delimiter);
ATHandler(FileHandle *fh, events::EventQueue &queue, int timeout, const char *output_delimiter, uint16_t send_delay = 0);
~ATHandler();
/** Return used file handle.
@ -84,12 +86,6 @@ public:
*/
FileHandle *get_file_handle();
/** Set file handle, which is used for reading AT responses and writing AT commands
*
* @param fh file handle used for reading AT responses and writing AT commands
*/
void set_file_handle(FileHandle *fh);
/** Locks the mutex for file handle if AT_HANDLER_MUTEX is defined.
*/
void lock();
@ -105,11 +101,20 @@ public:
nsapi_error_t unlock_return_error();
/** Set the urc callback for urc. If urc is found when parsing AT responses, then call if called.
* If urc is already set then it's not set twice.
*
* @param prefix Register urc prefix for callback. Urc could be for example "+CMTI: "
* @param callback Callback, which is called if urc is found in AT response
* @return NSAPI_ERROR_OK or NSAPI_ERROR_NO_MEMORY if no memory
*/
nsapi_error_t set_urc_handler(const char *prefix, mbed::Callback<void()> callback);
/** Remove urc handler from linked list of urc's
*
* @param prefix Register urc prefix for callback. Urc could be for example "+CMTI: "
* @param callback Callback, which is called if urc is found in AT response
*/
void set_urc_handler(const char *prefix, mbed::Callback<void()> callback);
void remove_urc_handler(const char *prefix, mbed::Callback<void()> callback);
ATHandler *_nextATHandler; // linked list
@ -154,6 +159,11 @@ public:
*/
void clear_error();
/**
* Flushes the underlying stream
*/
void flush();
/** Tries to find oob's from the AT response. Call the urc callback if one is found.
*/
void process_oob();
@ -162,10 +172,11 @@ public:
*/
void set_filehandle_sigio();
/**
* Flushes the underlying stream
/** Set file handle, which is used for reading AT responses and writing AT commands
*
* @param fh file handle used for reading AT responses and writing AT commands
*/
void flush();
void set_file_handle(FileHandle *fh);
protected:
void event();
@ -183,25 +194,26 @@ private:
device_err_t _last_at_err;
uint16_t _oob_string_max_length;
char *_output_delimiter;
uint8_t _output_delimiter_length;
struct oob_t {
bool matching_to_received;
const char *prefix;
int prefix_len;
mbed::Callback<void()> cb;
oob_t *next;
};
oob_t *_oobs;
bool _response_terminated;
uint32_t _at_timeout;
uint32_t _previous_at_timeout;
uint16_t _at_send_delay;
uint64_t _last_response_stop;
bool _fh_sigio_set;
bool _processing;
int32_t _ref_count;
//*************************************
//*************************************
public:
/** Starts the command writing by clearing the last error and writing the given command.
@ -291,6 +303,17 @@ public:
*/
ssize_t read_string(char *str, size_t size, bool read_even_stop_tag = false);
/** Reads chars representing hex ascii values and converts them to the corresponding chars.
* For example: "4156" to "AV".
* Terminates with null. Skips the quotation marks.
* Stops on delimiter or stop tag.
*
* @param str output buffer for the read
* @param size maximum number of chars to output
* @return length of output string or -1 in case of read timeout before delimiter or stop tag is found
*/
ssize_t read_hex_string(char *str, size_t size);
/** Reads as string and converts result to integer. Supports only positive integers.
*
* @return the positive integer or -1 in case of error.
@ -446,7 +469,7 @@ private:
void set_3gpp_error(int err, DeviceErrorType error_type);
bool check_cmd_send();
ssize_t write(const void *data, size_t len);
size_t write(const void *data, size_t len);
/** Copy content of one char buffer to another buffer and sets NULL terminator
*
@ -467,6 +490,11 @@ private:
* @return pointer to first occurrence of src in dest
*/
const char* mem_str(const char* dest, size_t dest_len, const char* src, size_t src_len);
// check is urc is already added
bool find_urc_handler(const char *prefix, mbed::Callback<void()> callback);
ssize_t read(char *buf, size_t size, bool read_even_stop_tag, bool hex);
};
} // namespace mbed

View File

@ -41,7 +41,6 @@ AT_CellularDevice::~AT_CellularDevice()
ATHandler *old = atHandler;
atHandler = atHandler->_nextATHandler;
delete old;
old = NULL;
}
}
@ -60,7 +59,7 @@ ATHandler* AT_CellularDevice::get_at_handler(FileHandle *fileHandle)
atHandler = atHandler->_nextATHandler;
}
atHandler = new ATHandler(fileHandle, _queue, _default_timeout, "\r");
atHandler = new ATHandler(fileHandle, _queue, _default_timeout, "\r", get_send_delay());
if (atHandler) {
if (_modem_debug_on) {
atHandler->enable_debug(_modem_debug_on);
@ -90,7 +89,6 @@ void AT_CellularDevice::release_at_handler(ATHandler* at_handler)
prev->_nextATHandler = atHandler->_nextATHandler;
}
delete atHandler;
atHandler = NULL;
break;
} else {
prev = atHandler;
@ -225,6 +223,11 @@ void AT_CellularDevice::set_timeout(int timeout)
}
}
uint16_t AT_CellularDevice::get_send_delay()
{
return 0;
}
void AT_CellularDevice::modem_debug_on(bool on)
{
_modem_debug_on = on;

View File

@ -77,6 +77,8 @@ public: // CellularDevice
virtual void set_timeout(int timeout);
virtual uint16_t get_send_delay();
virtual void modem_debug_on(bool on);
virtual NetworkStack *get_stack();

View File

@ -20,6 +20,7 @@
#include "nsapi_ppp.h"
#include "CellularUtil.h"
#include "CellularLog.h"
#include "CellularCommon.h"
using namespace std;
using namespace mbed_cellular_util;
@ -28,28 +29,58 @@ using namespace mbed;
struct at_reg_t {
const CellularNetwork::RegistrationType type;
const char *const cmd;
const char *const urc_prefix;
};
static const at_reg_t at_reg[] = {
{ CellularNetwork::C_EREG, "AT+CEREG" },
{ CellularNetwork::C_GREG, "AT+CGREG" },
{ CellularNetwork::C_REG, "AT+CREG" },
{ CellularNetwork::C_EREG, "AT+CEREG", "+CEREG:"},
{ CellularNetwork::C_GREG, "AT+CGREG", "+CGREG:"},
{ CellularNetwork::C_REG, "AT+CREG", "+CREG:"}
};
AT_CellularNetwork::AT_CellularNetwork(ATHandler &atHandler) : AT_CellularBase(atHandler),
_stack(NULL), _apn(NULL), _uname(NULL), _pwd(NULL), _ip_stack_type_requested(DEFAULT_STACK), _ip_stack_type(DEFAULT_STACK), _cid(-1),
_connection_status_cb(NULL), _op_act(operator_t::RAT_UNKNOWN), _authentication_type(CHAP), _last_reg_type(C_REG),
_connect_status(NSAPI_STATUS_DISCONNECTED), _new_context_set(false)
_stack(NULL), _apn(NULL), _uname(NULL), _pwd(NULL), _ip_stack_type_requested(DEFAULT_STACK),
_ip_stack_type(DEFAULT_STACK), _cid(-1), _connection_status_cb(NULL), _op_act(RAT_UNKNOWN),
_authentication_type(CHAP), _cell_id(-1), _connect_status(NSAPI_STATUS_DISCONNECTED), _new_context_set(false),
_is_context_active(false), _reg_status(NotRegistered), _current_act(RAT_UNKNOWN)
{
_at.set_urc_handler("NO CARRIER", callback(this, &AT_CellularNetwork::urc_no_carrier));
}
AT_CellularNetwork::~AT_CellularNetwork()
{
#if NSAPI_PPP_AVAILABLE
(void)disconnect();
#else
delete _stack;
#endif // NSAPI_PPP_AVAILABLE
for (int type = 0; type < CellularNetwork::C_MAX; type++) {
if (has_registration((RegistrationType)type)) {
_at.remove_urc_handler(at_reg[type].urc_prefix, _urc_funcs[type]);
}
}
_at.remove_urc_handler("NO CARRIER", callback(this, &AT_CellularNetwork::urc_no_carrier));
free_credentials();
}
nsapi_error_t AT_CellularNetwork::init()
{
_urc_funcs[C_EREG] = callback(this, &AT_CellularNetwork::urc_cereg);
_urc_funcs[C_GREG] = callback(this, &AT_CellularNetwork::urc_cgreg);
_urc_funcs[C_REG] = callback(this, &AT_CellularNetwork::urc_creg);
for (int type = 0; type < CellularNetwork::C_MAX; type++) {
if (has_registration((RegistrationType)type)) {
if (_at.set_urc_handler(at_reg[type].urc_prefix, _urc_funcs[type]) != NSAPI_ERROR_OK) {
return NSAPI_ERROR_NO_MEMORY;
}
}
}
return _at.set_urc_handler("NO CARRIER", callback(this, &AT_CellularNetwork::urc_no_carrier));
}
void AT_CellularNetwork::free_credentials()
{
if (_uname) {
@ -67,12 +98,71 @@ void AT_CellularNetwork::free_credentials()
void AT_CellularNetwork::urc_no_carrier()
{
tr_error("Data call failed: no carrier");
_connect_status = NSAPI_STATUS_DISCONNECTED;
if (_connection_status_cb) {
_connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED);
}
}
void AT_CellularNetwork::read_reg_params_and_compare(RegistrationType type)
{
RegistrationStatus reg_status = NotRegistered;
int lac = -1, cell_id = -1, act = -1;
read_reg_params(type, reg_status, lac, cell_id, act);
#if MBED_CONF_MBED_TRACE_ENABLE
switch (reg_status) {
case NotRegistered:
tr_error("not registered");
break;
case RegistrationDenied:
tr_error("registration denied");
break;
case Unknown:
tr_error("registration status unknown");
break;
default:
break;
}
#endif
if (_at.get_last_error() == NSAPI_ERROR_OK && _connection_status_cb) {
tr_debug("stat: %d, lac: %d, cellID: %d, act: %d", reg_status, lac, cell_id, act);
if (act != -1 && (RadioAccessTechnology)act != _current_act) {
_current_act = (RadioAccessTechnology)act;
_connection_status_cb((nsapi_event_t)CellularRadioAccessTechnologyChanged, _current_act);
}
if (reg_status != _reg_status) {
_reg_status = reg_status;
_connection_status_cb((nsapi_event_t)CellularRegistrationStatusChanged, _reg_status);
}
if (cell_id != -1 && cell_id != _cell_id) {
_cell_id = cell_id;
_connection_status_cb((nsapi_event_t)CellularCellIDChanged, _cell_id);
}
}
}
void AT_CellularNetwork::urc_creg()
{
tr_debug("urc_creg");
read_reg_params_and_compare(C_REG);
}
void AT_CellularNetwork::urc_cereg()
{
tr_debug("urc_cereg");
read_reg_params_and_compare(C_EREG);
}
void AT_CellularNetwork::urc_cgreg()
{
tr_debug("urc_cgreg");
read_reg_params_and_compare(C_GREG);
}
nsapi_error_t AT_CellularNetwork::set_credentials(const char *apn,
const char *username, const char *password)
{
@ -149,20 +239,78 @@ nsapi_error_t AT_CellularNetwork::delete_current_context()
return _at.get_last_error();
}
nsapi_error_t AT_CellularNetwork::connect()
nsapi_error_t AT_CellularNetwork::activate_context()
{
_at.lock();
nsapi_error_t err = set_context_to_be_activated();
if (err != NSAPI_ERROR_OK) {
_at.unlock();
tr_error("Failed to activate network context! (%d)", err);
_connect_status = NSAPI_STATUS_DISCONNECTED;
if (_connection_status_cb) {
_connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED);
}
return err;
}
// do check for stack to validate that we have support for stack
_stack = get_stack();
if (!_stack) {
tr_error("No cellular stack!");
return NSAPI_ERROR_UNSUPPORTED;
}
_is_context_active = false;
_at.cmd_start("AT+CGACT?");
_at.cmd_stop();
_at.resp_start("+CGACT:");
while (_at.info_resp()) {
int context_id = _at.read_int();
int context_activation_state = _at.read_int();
if (context_id == _cid && context_activation_state == 1) {
_is_context_active = true;
}
}
_at.resp_stop();
if (!_is_context_active) {
tr_info("Activate PDP context %d",_cid);
_at.cmd_start("AT+CGACT=1,");
_at.write_int(_cid);
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
}
err = (_at.get_last_error() == NSAPI_ERROR_OK) ? NSAPI_ERROR_OK : NSAPI_ERROR_NO_CONNECTION;
// If new PDP context was created and failed to activate, delete it
if (err != NSAPI_ERROR_OK && _new_context_set) {
delete_current_context();
} else if (err == NSAPI_ERROR_OK) {
_is_context_active = true;
}
_at.unlock();
return err;
}
nsapi_error_t AT_CellularNetwork::connect()
{
_connect_status = NSAPI_STATUS_CONNECTING;
if (_connection_status_cb) {
_connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_CONNECTING);
}
nsapi_error_t err = set_context_to_be_activated();
if (err != NSAPI_ERROR_OK) {
_at.unlock();
tr_error("Failed to activate network context!");
nsapi_error_t err = NSAPI_ERROR_OK;
if (!_is_context_active) {
err = activate_context();
}
if (err) {
_connect_status = NSAPI_STATUS_DISCONNECTED;
if (_connection_status_cb) {
_connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED);
@ -171,29 +319,19 @@ nsapi_error_t AT_CellularNetwork::connect()
return err;
}
#if NSAPI_PPP_AVAILABLE
_at.lock();
err = open_data_channel();
_at.unlock();
if (err != NSAPI_ERROR_OK) {
// If new PDP context was created and failed to activate, delete it
if (_new_context_set) {
delete_current_context();
}
_at.unlock();
tr_error("Failed to open data channel!");
_connect_status = NSAPI_STATUS_DISCONNECTED;
if (_connection_status_cb) {
_connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED);
}
return err;
}
_at.unlock();
#if !NSAPI_PPP_AVAILABLE
#else
_connect_status = NSAPI_STATUS_GLOBAL_UP;
if (_connection_status_cb) {
_connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_GLOBAL_UP);
@ -205,8 +343,6 @@ nsapi_error_t AT_CellularNetwork::connect()
nsapi_error_t AT_CellularNetwork::open_data_channel()
{
//old way: _at.send("ATD*99***%d#", _cid) && _at.recv("CONNECT");
nsapi_error_t err = NSAPI_ERROR_NO_CONNECTION;
#if NSAPI_PPP_AVAILABLE
tr_info("Open data channel in PPP mode");
_at.cmd_start("AT+CGDATA=\"PPP\",");
@ -218,43 +354,12 @@ nsapi_error_t AT_CellularNetwork::open_data_channel()
tr_warn("Failed to CONNECT");
}
/* Initialize PPP
* mbed_ppp_init() is a blocking call, it will block until
* connected, or timeout after 30 seconds*/
err = nsapi_ppp_connect(_at.get_file_handle(), callback(this, &AT_CellularNetwork::ppp_status_cb), _uname, _pwd, _ip_stack_type);
* If blocking: mbed_ppp_init() is a blocking call, it will block until
connected, or timeout after 30 seconds*/
return nsapi_ppp_connect(_at.get_file_handle(), callback(this, &AT_CellularNetwork::ppp_status_cb), NULL, NULL, _ip_stack_type);
#else
// do check for stack to validate that we have support for stack
_stack = get_stack();
if (!_stack) {
return err;
}
bool is_context_active = false;
_at.cmd_start("AT+CGACT?");
_at.cmd_stop();
_at.resp_start("+CGACT:");
while (_at.info_resp()) {
int context_id = _at.read_int();
int context_activation_state = _at.read_int();
if (context_id == _cid && context_activation_state == 1) {
is_context_active = true;
tr_debug("PDP context %d is active.", _cid);
break;
}
}
_at.resp_stop();
if (!is_context_active) {
tr_info("Activate PDP context %d", _cid);
_at.cmd_start("AT+CGACT=1,");
_at.write_int(_cid);
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
}
err = (_at.get_last_error() == NSAPI_ERROR_OK) ? NSAPI_ERROR_OK : NSAPI_ERROR_NO_CONNECTION;
#endif
return err;
return NSAPI_ERROR_OK;
#endif // #if NSAPI_PPP_AVAILABLE
}
/**
@ -300,11 +405,10 @@ nsapi_error_t AT_CellularNetwork::set_blocking(bool blocking)
#if NSAPI_PPP_AVAILABLE
return nsapi_ppp_set_blocking(blocking);
#else
return NSAPI_ERROR_UNSUPPORTED;
return NSAPI_ERROR_OK;
#endif
}
#if NSAPI_PPP_AVAILABLE
void AT_CellularNetwork::ppp_status_cb(nsapi_event_t event, intptr_t parameter)
{
@ -316,8 +420,6 @@ void AT_CellularNetwork::ppp_status_cb(nsapi_event_t event, intptr_t parameter)
}
#endif
nsapi_error_t AT_CellularNetwork::set_context_to_be_activated()
{
// try to find or create context with suitable stack
@ -370,7 +472,7 @@ bool AT_CellularNetwork::set_new_context(int cid)
strncpy(pdp_type, "IPV6", sizeof(pdp_type));
break;
case IPV4V6_STACK:
strncpy(pdp_type, "IPV4V6", sizeof(pdp_type));
strncpy(pdp_type, "IPV6", sizeof(pdp_type)); // try first IPV6 and then fall-back to IPv4
break;
default:
break;
@ -522,43 +624,52 @@ nsapi_ip_stack_t AT_CellularNetwork::string_to_stack_type(const char* pdp_type)
return stack;
}
nsapi_error_t AT_CellularNetwork::set_registration_urc(bool urc_on)
nsapi_error_t AT_CellularNetwork::set_registration_urc(RegistrationType type, bool urc_on)
{
for (unsigned int i = 0; i < sizeof(at_reg)/sizeof(at_reg[0]); i++) {
if (has_registration(at_reg[i].type)) {
_last_reg_type = at_reg[i].type;
if (urc_on) {
_at.cmd_start(at_reg[i].cmd);
_at.write_string("=2", false);
_at.cmd_stop();
} else {
_at.cmd_start(at_reg[i].cmd);
_at.write_string("=0", false);
_at.cmd_stop();
}
int index = (int)type;
MBED_ASSERT(index >= 0 && index < C_MAX);
_at.resp_start();
_at.resp_stop();
if (!has_registration(type)) {
return NSAPI_ERROR_UNSUPPORTED;
} else {
_at.lock();
if (urc_on) {
_at.cmd_start(at_reg[index].cmd);
_at.write_string("=2", false);
_at.cmd_stop();
} else {
_at.cmd_start(at_reg[index].cmd);
_at.write_string("=0", false);
_at.cmd_stop();
}
_at.resp_start();
_at.resp_stop();
return _at.unlock_return_error();
}
return _at.get_last_error();
}
nsapi_error_t AT_CellularNetwork::get_network_registering_mode(NWRegisteringMode& mode)
{
_at.lock();
_at.cmd_start("AT+COPS?");
_at.cmd_stop();
_at.resp_start("+COPS:");
mode = (NWRegisteringMode)_at.read_int();
_at.resp_stop();
return _at.unlock_return_error();
}
nsapi_error_t AT_CellularNetwork::set_registration(const char *plmn)
{
_at.lock();
nsapi_error_t ret = set_registration_urc(false);
if (ret) {
tr_error("Setting registration URC failed!");
_at.clear_error(); // allow temporary failures here
}
if (!plmn) {
tr_debug("Automatic network registration");
_at.cmd_start("AT+COPS?");
_at.cmd_stop();
_at.resp_start("AT+COPS:");
_at.resp_start("+COPS:");
int mode = _at.read_int();
_at.resp_stop();
if (mode != 0) {
@ -580,40 +691,13 @@ nsapi_error_t AT_CellularNetwork::set_registration(const char *plmn)
return _at.unlock_return_error();
}
nsapi_error_t AT_CellularNetwork::get_registration_status(RegistrationType type, RegistrationStatus &status)
void AT_CellularNetwork::read_reg_params(RegistrationType type, RegistrationStatus &reg_status, int &lac, int &cell_id, int &act)
{
int i = (int)type;
MBED_ASSERT(i >= 0 && i < C_MAX);
const char *rsp[] = { "+CEREG:", "+CGREG:", "+CREG:"};
const int LAC_LENGTH = 5, CELL_ID_LENGTH = 9;
char lac_string[LAC_LENGTH] = {0}, cell_id_string[CELL_ID_LENGTH] = {0};
bool lac_read = false, cell_id_read = false;
_cell_id = -1;
_lac = -1;
_at.lock();
if (!has_registration(at_reg[i].type)) {
_at.unlock();
return NSAPI_ERROR_UNSUPPORTED;
}
_at.cmd_start(at_reg[i].cmd);
_at.write_string("=2", false);
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
_at.cmd_start(at_reg[i].cmd);
_at.write_string("?", false);
_at.cmd_stop();
_at.resp_start(rsp[i]);
_at.read_int(); // ignore urc mode subparam
status = (RegistrationStatus)_at.read_int();
reg_status = (RegistrationStatus)_at.read_int();
int len = _at.read_string(lac_string, LAC_LENGTH);
if (memcmp(lac_string, "ffff", LAC_LENGTH-1) && len >= 0) {
@ -625,38 +709,56 @@ nsapi_error_t AT_CellularNetwork::get_registration_status(RegistrationType type,
cell_id_read = true;
}
_at.resp_stop();
_at.cmd_start(at_reg[i].cmd);
_at.write_string("=0", false);
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
nsapi_error_t ret = _at.get_last_error();
_at.unlock();
act = _at.read_int();
if (lac_read) {
_lac = hex_str_to_int(lac_string, LAC_LENGTH);
tr_debug("lac %s %d", lac_string, _lac );
lac = hex_str_to_int(lac_string, LAC_LENGTH);
tr_debug("lac %s %d", lac_string, lac );
}
if (cell_id_read) {
_cell_id = hex_str_to_int(cell_id_string, CELL_ID_LENGTH);
tr_debug("cell_id %s %d", cell_id_string, _cell_id );
cell_id = hex_str_to_int(cell_id_string, CELL_ID_LENGTH);
tr_debug("cell_id %s %d", cell_id_string, cell_id );
}
}
nsapi_error_t AT_CellularNetwork::get_registration_status(RegistrationType type, RegistrationStatus &status)
{
int i = (int)type;
MBED_ASSERT(i >= 0 && i < C_MAX);
if (!has_registration(at_reg[i].type)) {
return NSAPI_ERROR_UNSUPPORTED;
}
return ret;
_at.lock();
const char *rsp[] = { "+CEREG:", "+CGREG:", "+CREG:"};
_at.cmd_start(at_reg[i].cmd);
_at.write_string("?", false);
_at.cmd_stop();
_at.resp_start(rsp[i]);
(void)_at.read_int(); // ignore urc mode subparam
int lac = -1, cell_id = -1, act = -1;
read_reg_params(type, status, lac, cell_id, act);
_at.resp_stop();
_reg_status = status;
if (cell_id != -1) {
_cell_id = cell_id;
}
if (act != -1) {
_current_act = (RadioAccessTechnology)act;
}
return _at.unlock_return_error();
}
nsapi_error_t AT_CellularNetwork::get_cell_id(int &cell_id)
{
RegistrationStatus tmp;
nsapi_error_t error = get_registration_status(_last_reg_type, tmp);
cell_id = _cell_id;
return error;
return NSAPI_ERROR_OK;
}
bool AT_CellularNetwork::has_registration(RegistrationType reg_type)
@ -665,7 +767,7 @@ bool AT_CellularNetwork::has_registration(RegistrationType reg_type)
return true;
}
nsapi_error_t AT_CellularNetwork::set_attach(int timeout)
nsapi_error_t AT_CellularNetwork::set_attach(int /*timeout*/)
{
_at.lock();
@ -702,13 +804,24 @@ nsapi_error_t AT_CellularNetwork::get_attach(AttachStatus &status)
return _at.unlock_return_error();
}
nsapi_error_t AT_CellularNetwork::get_apn_backoff_timer(int &backoff_timer)
nsapi_error_t AT_CellularNetwork::detach()
{
_at.lock();
tr_debug("Network detach");
_at.cmd_start("AT+CGATT=0");
_at.cmd_stop();
_at.resp_start();
_at.resp_stop();
return _at.unlock_return_error();
}
nsapi_error_t AT_CellularNetwork::get_apn_backoff_timer(int &backoff_timer)
{
// If apn is set
if (_apn) {
_at.lock();
_at.cmd_start("AT+CABTRDP=");
_at.write_string(_apn);
_at.cmd_stop();
@ -718,18 +831,19 @@ nsapi_error_t AT_CellularNetwork::get_apn_backoff_timer(int &backoff_timer)
backoff_timer = _at.read_int();
}
_at.resp_stop();
return _at.unlock_return_error();
}
return _at.unlock_return_error();
return NSAPI_ERROR_PARAMETER;
}
NetworkStack *AT_CellularNetwork::get_stack()
{
// use lwIP/PPP if modem does not have IP stack
#if NSAPI_PPP_AVAILABLE
_stack = nsapi_ppp_get_stack();
#else
_stack = NULL;
// use lwIP/PPP if modem does not have IP stack
if (!_stack) {
_stack = nsapi_ppp_get_stack();
}
#endif
return _stack;
}
@ -751,14 +865,12 @@ const char *AT_CellularNetwork::get_ip_address()
nsapi_error_t AT_CellularNetwork::set_stack_type(nsapi_ip_stack_t stack_type)
{
if (get_modem_stack_type(stack_type)) {
_ip_stack_type_requested = stack_type;
return NSAPI_ERROR_OK;
} else {
return NSAPI_ERROR_PARAMETER;
}
}
nsapi_ip_stack_t AT_CellularNetwork::get_stack_type()
@ -775,14 +887,20 @@ bool AT_CellularNetwork::get_modem_stack_type(nsapi_ip_stack_t requested_stack)
}
}
nsapi_error_t AT_CellularNetwork::set_access_technology_impl(operator_t::RadioAccessTechnology opsAct)
nsapi_error_t AT_CellularNetwork::set_access_technology_impl(RadioAccessTechnology opsAct)
{
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_error_t AT_CellularNetwork::set_access_technology(operator_t::RadioAccessTechnology opAct)
nsapi_error_t AT_CellularNetwork::get_access_technology(RadioAccessTechnology& rat)
{
if (opAct == operator_t::RAT_UNKNOWN) {
rat = _current_act;
return NSAPI_ERROR_OK;
}
nsapi_error_t AT_CellularNetwork::set_access_technology(RadioAccessTechnology opAct)
{
if (opAct == RAT_UNKNOWN) {
return NSAPI_ERROR_UNSUPPORTED;
}
@ -808,6 +926,14 @@ nsapi_error_t AT_CellularNetwork::scan_plmn(operList_t &operators, int &opsCount
while (_at.info_elem('(')) {
op = operators.add_new();
if (!op) {
tr_warn("Could not allocate new operator");
_at.resp_stop();
_at.unlock();
operators.delete_all();
opsCount = 0;
return NSAPI_ERROR_NO_MEMORY;
}
op->op_status = (operator_t::Status)_at.read_int();
_at.read_string(op->op_long, sizeof(op->op_long));
@ -816,10 +942,10 @@ nsapi_error_t AT_CellularNetwork::scan_plmn(operList_t &operators, int &opsCount
// Optional - try read an int
ret = _at.read_int();
op->op_rat = (ret == error_code) ? operator_t::RAT_UNKNOWN:(operator_t::RadioAccessTechnology)ret;
op->op_rat = (ret == error_code) ? RAT_UNKNOWN:(RadioAccessTechnology)ret;
if ((_op_act == operator_t::RAT_UNKNOWN) ||
((op->op_rat != operator_t::RAT_UNKNOWN) && (op->op_rat == _op_act))) {
if ((_op_act == RAT_UNKNOWN) ||
((op->op_rat != RAT_UNKNOWN) && (op->op_rat == _op_act))) {
idx++;
} else {
operators.delete_last();
@ -829,7 +955,6 @@ nsapi_error_t AT_CellularNetwork::scan_plmn(operList_t &operators, int &opsCount
_at.resp_stop();
opsCount = idx;
return _at.unlock_return_error();
}
@ -941,8 +1066,9 @@ nsapi_error_t AT_CellularNetwork::get_pdpcontext_params(pdpContextList_t& params
params = params_list.add_new();
if (!params) {
tr_warn("Could not allocate new pdpcontext_params_t");
params_list.delete_all();
_at.resp_stop();
_at.unlock();
params_list.delete_all();
free(temp);
free(ipv6_and_subnetmask);
return NSAPI_ERROR_NO_MEMORY;
@ -1053,7 +1179,6 @@ int AT_CellularNetwork::get_3gpp_error()
return _at.get_3gpp_error();
}
nsapi_error_t AT_CellularNetwork::get_operator_params(int &format, operator_t &operator_params)
{
_at.lock();
@ -1066,25 +1191,47 @@ nsapi_error_t AT_CellularNetwork::get_operator_params(int &format, operator_t &o
format = _at.read_int();
if (_at.get_last_error() == NSAPI_ERROR_OK) {
switch (format) {
case 0:
_at.read_string(operator_params.op_long, sizeof(operator_params.op_long));
break;
case 1:
_at.read_string(operator_params.op_short, sizeof(operator_params.op_short));
break;
default:
_at.read_string(operator_params.op_num, sizeof(operator_params.op_num));
break;
}
operator_params.op_rat = (operator_t::RadioAccessTechnology)_at.read_int();
operator_params.op_rat = (RadioAccessTechnology)_at.read_int();
}
_at.resp_stop();
return _at.unlock_return_error();
}
nsapi_error_t AT_CellularNetwork::get_operator_names(operator_names_list &op_names)
{
_at.lock();
_at.cmd_start("AT+COPN?");
_at.cmd_stop();
_at.resp_start("+COPN:");
operator_names_t *names = NULL;
while (_at.info_resp()) {
names = op_names.add_new();
if (!names) {
tr_warn("Could not allocate new operator_names_t");
_at.resp_stop();
_at.unlock();
op_names.delete_all();
return NSAPI_ERROR_NO_MEMORY;
}
_at.read_string(names->numeric, sizeof(names->numeric));
_at.read_string(names->alpha, sizeof(names->alpha));
}
_at.resp_stop();
return _at.unlock_return_error();
}

View File

@ -60,14 +60,22 @@ protected:
virtual NetworkStack *get_stack();
public: // CellularNetwork
virtual nsapi_error_t init();
virtual nsapi_error_t activate_context();
virtual nsapi_error_t set_registration(const char *plmn = 0);
virtual nsapi_error_t get_network_registering_mode(NWRegisteringMode& mode);
virtual nsapi_error_t get_registration_status(RegistrationType type, RegistrationStatus &status);
virtual nsapi_error_t set_attach(int timeout = 10*1000);
virtual nsapi_error_t get_attach(AttachStatus &status);
virtual nsapi_error_t detach();
virtual nsapi_error_t get_rate_control(CellularNetwork::RateControlExceptionReports &reports,
CellularNetwork::RateControlUplinkTimeUnit &time_unit, int &uplink_rate);
@ -81,7 +89,8 @@ public: // CellularNetwork
virtual const char *get_ip_address();
virtual nsapi_error_t set_access_technology(operator_t::RadioAccessTechnology op_rat);
virtual nsapi_error_t set_access_technology(RadioAccessTechnology rat);
virtual nsapi_error_t get_access_technology(RadioAccessTechnology& rat);
virtual nsapi_error_t scan_plmn(operList_t &operators, int &ops_count);
@ -107,6 +116,9 @@ public: // CellularNetwork
virtual nsapi_error_t get_operator_params(int &format, operator_t &operator_params);
virtual nsapi_error_t set_registration_urc(RegistrationType type, bool on);
virtual nsapi_error_t get_operator_names(operator_names_list &op_names);
protected:
/** Check if modem supports the given stack type.
@ -128,11 +140,15 @@ protected:
*
* @return zero on success
*/
virtual nsapi_error_t set_access_technology_impl(operator_t::RadioAccessTechnology op_rat);
virtual nsapi_error_t set_access_technology_impl(RadioAccessTechnology op_rat);
private:
// "NO CARRIER" urc
void urc_no_carrier();
void urc_creg();
void urc_cereg();
void urc_cgreg();
nsapi_error_t set_context_to_be_activated();
nsapi_ip_stack_t string_to_stack_type(const char* pdp_type);
@ -141,9 +157,12 @@ private:
nsapi_error_t open_data_channel();
bool get_context();
bool set_new_context(int cid);
nsapi_error_t set_registration_urc(bool on);
nsapi_error_t delete_current_context();
void read_reg_params_and_compare(RegistrationType type);
void read_reg_params(RegistrationType type, RegistrationStatus &reg_status, int &lac, int &cell_id, int &act);
#if NSAPI_PPP_AVAILABLE
void ppp_status_cb(nsapi_event_t, intptr_t);
#endif
@ -157,13 +176,15 @@ protected:
nsapi_ip_stack_t _ip_stack_type;
int _cid;
Callback<void(nsapi_event_t, intptr_t)> _connection_status_cb;
operator_t::RadioAccessTechnology _op_act;
RadioAccessTechnology _op_act;
AuthenticationType _authentication_type;
int _lac;
int _cell_id;
RegistrationType _last_reg_type;
nsapi_connection_status_t _connect_status;
bool _new_context_set;
bool _is_context_active;
RegistrationStatus _reg_status;
RadioAccessTechnology _current_act;
mbed::Callback<void()> _urc_funcs[C_MAX];
};
} // namespace mbed

View File

@ -18,6 +18,7 @@
#include "AT_CellularPower.h"
#include "CellularUtil.h"
#include "CellularLog.h"
#include "CellularTargets.h"
#include "nsapi_types.h"
static const int PSMTimerBits = 5;
@ -231,3 +232,12 @@ nsapi_error_t AT_CellularPower::opt_receive_period(int mode, EDRXAccessTechnolog
return _at.unlock_return_error();
}
nsapi_error_t AT_CellularPower::set_device_ready_urc_cb(mbed::Callback<void()> callback)
{
return NSAPI_ERROR_UNSUPPORTED;
}
void AT_CellularPower::remove_device_ready_urc_cb(mbed::Callback<void()> callback)
{
}

Some files were not shown because too many files have changed in this diff Show More