Merge pull request #5313 from pan-/ble-cordio-pal-gap

Cordio: Pal Gap implementation
pull/5744/merge
Cruz Monrreal 2018-01-11 10:35:42 -06:00 committed by GitHub
commit dc87f0b1e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 709 additions and 920 deletions

View File

@ -282,7 +282,6 @@ private:
pal::Gap &_pal_gap;
pal::GenericAccessService &_gap_service;
BLEProtocol::AddressType_t _address_type;
pal::address_t _address;
pal::initiator_policy_t _initiator_policy_mode;
pal::scanning_filter_policy_t _scanning_filter_policy;
pal::advertising_filter_policy_t _advertising_filter_policy;

View File

@ -376,7 +376,6 @@ GenericGap::GenericGap(
_pal_gap(pal_gap),
_gap_service(generic_access_service),
_address_type(BLEProtocol::AddressType::PUBLIC),
_address(),
_initiator_policy_mode(pal::initiator_policy_t::NO_FILTER),
_scanning_filter_policy(pal::scanning_filter_policy_t::NO_FILTER),
_advertising_filter_policy(pal::advertising_filter_policy_t::NO_FILTER),
@ -416,7 +415,6 @@ ble_error_t GenericGap::setAddress(
}
_address_type = type;
_address = pal::address_t(address, true);
return BLE_ERROR_NONE;
}
@ -439,7 +437,14 @@ ble_error_t GenericGap::getAddress(
BLEProtocol::AddressBytes_t address
) {
*type = _address_type;
memcpy(address, _address.data(), _address.size());
pal::address_t address_value;
if (_address_type == BLEProtocol::AddressType::PUBLIC) {
address_value = _pal_gap.get_device_address();
} else {
address_value = _pal_gap.get_random_address();
}
memcpy(address, address_value.data(), address_value.size());
return BLE_ERROR_NONE;
}
@ -990,6 +995,12 @@ void GenericGap::on_connection_complete(const pal::GapConnectionCompleteEvent& e
e.connection_latency,
e.supervision_timeout
};
pal::address_t address;
if (_address_type == BLEProtocol::AddressType::PUBLIC) {
address = _pal_gap.get_device_address();
} else {
address = _pal_gap.get_random_address();
}
processConnectionEvent(
e.connection_handle,
@ -997,7 +1008,7 @@ void GenericGap::on_connection_complete(const pal::GapConnectionCompleteEvent& e
(BLEProtocol::AddressType_t) e.peer_address_type.value(),
e.peer_address.data(),
_address_type,
_address.data(),
address.data(),
&connection_params
);
} else {

View File

@ -22,12 +22,15 @@
#include "ble/BLEInstanceBase.h"
#include "CordioHCIDriver.h"
#include "CordioGap.h"
#include "CordioGattServer.h"
#include "CordioSecurityManager.h"
#include "CordioPalAttClient.h"
#include "ble/pal/AttClientToGattClientAdapter.h"
#include "ble/generic/GenericGattClient.h"
#include "CordioPalGap.h"
#include "CordioPalGenericAccessService.h"
#include "ble/generic/GenericGap.h"
#include "ble/pal/SimpleEventQueue.h"
namespace ble {
namespace vendor {
@ -81,12 +84,12 @@ public:
/**
* @see BLEInstanceBase::getGap
*/
virtual Gap& getGap();
virtual ::Gap& getGap();
/**
* @see BLEInstanceBase::getGap
*/
virtual const Gap& getGap() const;
virtual const ::Gap& getGap() const;
/**
* @see BLEInstanceBase::getGattServer
@ -143,6 +146,7 @@ private:
} initialization_status;
::BLE::InstanceID_t instanceID;
mutable pal::SimpleEventQueue _event_queue;
};
} // namespace cordio

View File

@ -1,262 +0,0 @@
/* 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 CORDIO_GAP_H_
#define CORDIO_GAP_H_
#include <stdint.h>
#include "ble/blecommon.h"
#include "ble/GapAdvertisingParams.h"
#include "ble/GapAdvertisingData.h"
#include "ble/Gap.h"
#include "ble/GapScanningParams.h"
#include "dm_api.h"
#include "att_api.h"
namespace ble {
namespace vendor {
namespace cordio {
/**
* @see ::Gap
*/
class Gap : public ::Gap
{
public:
/**
* Return the Gap singleton implementing ::Gap for the Cordio stac.
*/
static Gap &getInstance();
/**
* This function shall be called once the stack has been initialized
*/
void initialize();
/**
* @see ::Gap::setAddress
*/
virtual ble_error_t setAddress(AddressType_t type, const Address_t address);
/**
* @see ::Gap::getAddress
*/
virtual ble_error_t getAddress(AddressType_t *typeP, Address_t address);
/**
* @see ::Gap::setAdvertisingData
*/
virtual ble_error_t setAdvertisingData(
const GapAdvertisingData&, const GapAdvertisingData&
);
/**
* @see ::Gap::connect
*/
virtual ble_error_t connect(
const BLEProtocol::AddressBytes_t peerAddr,
BLEProtocol::AddressType_t peerAddrType,
const ConnectionParams_t* connectionParams,
const GapScanningParams* scanParams
);
/**
* @see ::Gap::getMinAdvertisingInterval
*/
virtual uint16_t getMinAdvertisingInterval() const;
/**
* @see ::Gap::getMinNonConnectableAdvertisingInterval
*/
virtual uint16_t getMinNonConnectableAdvertisingInterval() const;
/**
* @see ::Gap::getMaxAdvertisingInterval
*/
virtual uint16_t getMaxAdvertisingInterval() const;
/**
* @see ::Gap::startAdvertising
*/
virtual ble_error_t startAdvertising(const GapAdvertisingParams &);
/**
* @see ::Gap::stopAdvertising
*/
virtual ble_error_t stopAdvertising();
/**
* @see ::Gap::disconnect
*/
virtual ble_error_t disconnect(
Handle_t connectionHandle,
DisconnectionReason_t reason
);
/**
* @see ::Gap::disconnect
*/
virtual ble_error_t disconnect(DisconnectionReason_t reason);
/**
* @see ::Gap::setDeviceName
*/
virtual ble_error_t setDeviceName(const uint8_t *deviceName);
/**
* @see ::Gap::getDeviceName
*/
virtual ble_error_t getDeviceName(uint8_t *deviceName, unsigned *lengthP);
/**
* @see ::Gap::setAppearance
*/
virtual ble_error_t setAppearance(GapAdvertisingData::Appearance appearance);
/**
* @see ::Gap::getAppearance
*/
virtual ble_error_t getAppearance(GapAdvertisingData::Appearance *appearanceP);
/**
* @see ::Gap::setTxPower
*/
virtual ble_error_t setTxPower(int8_t txPower);
/**
* @see ::Gap::getPermittedTxPowerValues
*/
virtual void getPermittedTxPowerValues(
const int8_t **valueArrayPP, size_t *countP
);
/**
* Set the internal connection handle
*/
void setConnectionHandle(uint16_t m_connectionHandle);
/**
* Get the current connection handle
*/
uint16_t getConnectionHandle();
/**
* @see ::Gap::getPreferredConnectionParams
*/
virtual ble_error_t getPreferredConnectionParams(ConnectionParams_t *params);
/**
* @see ::Gap::setPreferredConnectionParams
*/
virtual ble_error_t setPreferredConnectionParams(
const ConnectionParams_t *params
);
/**
* @see ::Gap::updateConnectionParams
*/
virtual ble_error_t updateConnectionParams(
Handle_t handle, const ConnectionParams_t *params
);
/**
* @see ::Gap::startRadioScan
*/
virtual ble_error_t startRadioScan(const GapScanningParams &scanningParams);
/**
* @see ::Gap::stopScan
*/
virtual ble_error_t stopScan();
/**
* Called when advertising is stopped.
*/
void advertisingStopped();
// Whitelist management
/**
* @see ::Gap::getMaxWhitelistSize
*/
virtual uint8_t getMaxWhitelistSize(void) const;
/**
* @see ::Gap::getWhitelist
*/
virtual ble_error_t getWhitelist(Whitelist_t &whitelist) const;
/**
* @see ::Gap::setWhitelist
*/
virtual ble_error_t setWhitelist(const Whitelist_t &whitelist);
/**
* @see ::Gap::setAdvertisingPolicyMode
*/
virtual ble_error_t setAdvertisingPolicyMode(AdvertisingPolicyMode_t mode);
/**
* @see ::Gap::getAdvertisingPolicyMode
*/
virtual AdvertisingPolicyMode_t getAdvertisingPolicyMode(void) const;
/**
* @see ::Gap::setScanningPolicyMode
*/
virtual ble_error_t setScanningPolicyMode(ScanningPolicyMode_t mode);
/**
* @see ::Gap::getScanningPolicyMode
*/
virtual ScanningPolicyMode_t getScanningPolicyMode(void) const;
/**
* @see ::Gap::setInitiatorPolicyMode
*/
virtual ble_error_t setInitiatorPolicyMode(InitiatorPolicyMode_t mode);
/**
* @see ::Gap::getInitiatorPolicyMode
*/
virtual InitiatorPolicyMode_t getInitiatorPolicyMode(void) const;
/**
* @see ::Gap::reset
*/
virtual ble_error_t reset(void);
private:
Gap();
Gap(Gap const &);
void operator=(Gap const &);
uint16_t m_connectionHandle;
addr_type_t m_type;
Address_t m_addr;
AdvertisingPolicyMode_t advertising_policy_mode;
ScanningPolicyMode_t scanning_policy_mode;
InitiatorPolicyMode_t initiator_policy_mode;
Whitelist_t whitelist;
};
} // namespace cordio
} // namespace vendor
} // namespace ble
#endif /* CORDIO_GAP_H_ */

View File

@ -0,0 +1,477 @@
#ifndef CORDIO_PAL_GAP_
#define CORDIO_PAL_GAP_
#include "ble/pal/PalGap.h"
#include "dm_api.h"
namespace ble {
namespace pal {
namespace vendor {
namespace cordio {
/**
* Implementation of ble::pal::Gap for the Cordio stack.
*/
class Gap : public ::ble::pal::Gap {
public:
virtual ble_error_t initialize() {
return BLE_ERROR_NONE;
}
virtual ble_error_t terminate() {
return BLE_ERROR_NONE;
}
virtual address_t get_device_address() {
return address_t(HciGetBdAddr(), true);
}
virtual address_t get_random_address() {
return device_random_address;
}
virtual ble_error_t set_random_address(const address_t& address) {
device_random_address = address;
DmDevSetRandAddr(const_cast<uint8_t*>(address.data()));
return BLE_ERROR_NONE;
}
virtual ble_error_t set_advertising_parameters(
uint16_t advertising_interval_min,
uint16_t advertising_interval_max,
advertising_type_t advertising_type,
own_address_type_t own_address_type,
advertising_peer_address_type_t peer_address_type,
const address_t& peer_address,
advertising_channel_map_t advertising_channel_map,
advertising_filter_policy_t advertising_filter_policy
) {
DmAdvSetInterval(
DM_ADV_HANDLE_DEFAULT,
advertising_interval_min,
advertising_interval_max
);
DmAdvSetAddrType(own_address_type.value());
DmAdvSetChannelMap(
DM_ADV_HANDLE_DEFAULT,
advertising_channel_map.value()
);
DmDevSetFilterPolicy(
DM_FILT_POLICY_MODE_ADV,
advertising_filter_policy.value()
);
DmAdvConfig(
DM_ADV_HANDLE_DEFAULT,
advertising_type.value(),
peer_address_type.value(),
const_cast<uint8_t*>(peer_address.data())
);
return BLE_ERROR_NONE;
}
virtual ble_error_t set_advertising_data(
uint8_t advertising_data_length,
const advertising_data_t& advertising_data
) {
DmAdvSetData(
DM_ADV_HANDLE_DEFAULT,
HCI_ADV_DATA_OP_COMP_FRAG,
DM_DATA_LOC_ADV,
advertising_data_length,
const_cast<uint8_t*>(advertising_data.data())
);
return BLE_ERROR_NONE;
}
virtual ble_error_t set_scan_response_data(
uint8_t scan_response_data_length,
const advertising_data_t& scan_response_data
) {
DmAdvSetData(
DM_ADV_HANDLE_DEFAULT,
HCI_ADV_DATA_OP_COMP_FRAG,
DM_DATA_LOC_SCAN,
scan_response_data_length,
const_cast<uint8_t*>(scan_response_data.data())
);
return BLE_ERROR_NONE;
}
virtual ble_error_t advertising_enable(bool enable) {
if (enable) {
uint8_t adv_handles[] = { DM_ADV_HANDLE_DEFAULT };
uint16_t adv_durations[] = { /* infinite */ 0 };
uint8_t max_ea_events[] = { 0 };
DmAdvStart(1, adv_handles, adv_durations, max_ea_events);
} else {
uint8_t adv_handles[] = { DM_ADV_HANDLE_DEFAULT };
DmAdvStop(1, adv_handles);
}
return BLE_ERROR_NONE;
}
virtual ble_error_t set_scan_parameters(
bool active_scanning,
uint16_t scan_interval,
uint16_t scan_window,
own_address_type_t own_address_type,
scanning_filter_policy_t filter_policy
) {
use_active_scanning = active_scanning;
DmScanSetInterval(HCI_INIT_PHY_LE_1M_BIT, &scan_interval, &scan_window);
DmScanSetAddrType(own_address_type.value());
DmDevSetFilterPolicy(
DM_FILT_POLICY_MODE_SCAN,
filter_policy.value()
);
return BLE_ERROR_NONE;
}
virtual ble_error_t scan_enable(
bool enable,
bool filter_duplicates
) {
if (enable) {
uint8_t scanType = use_active_scanning ? DM_SCAN_TYPE_ACTIVE : DM_SCAN_TYPE_PASSIVE;
DmScanStart(
HCI_SCAN_PHY_LE_1M_BIT,
DM_DISC_MODE_NONE,
&scanType,
filter_duplicates,
0,
0
);
} else {
DmScanStop();
}
return BLE_ERROR_NONE;
}
virtual ble_error_t create_connection(
uint16_t scan_interval,
uint16_t scan_window,
initiator_policy_t initiator_policy,
connection_peer_address_type_t peer_address_type,
const address_t& peer_address,
own_address_type_t own_address_type,
uint16_t connection_interval_min,
uint16_t connection_interval_max,
uint16_t connection_latency,
uint16_t supervision_timeout,
uint16_t minimum_connection_event_length,
uint16_t maximum_connection_event_length
) {
DmConnSetScanInterval(scan_interval, scan_window);
DmDevSetFilterPolicy(DM_FILT_POLICY_MODE_INIT, initiator_policy.value());
DmConnSetAddrType(own_address_type.value());
hciConnSpec_t conn_spec = {
connection_interval_min,
connection_interval_max,
connection_latency,
supervision_timeout,
minimum_connection_event_length,
maximum_connection_event_length
};
DmConnSetConnSpec(&conn_spec);
dmConnId_t connection_id = DmConnOpen(
DM_CLIENT_ID_APP,
HCI_INIT_PHY_LE_1M_BIT,
peer_address_type.value(),
const_cast<uint8_t*>(peer_address.data())
);
if (connection_id == DM_CONN_ID_NONE) {
return BLE_ERROR_INTERNAL_STACK_FAILURE;
}
return BLE_ERROR_NONE;
}
virtual ble_error_t cancel_connection_creation() {
DmConnClose(
DM_CLIENT_ID_APP,
/* connection handle - invalid */ DM_CONN_ID_NONE,
/* reason - invalid (use success) */ 0x00
);
return BLE_ERROR_NONE;
}
virtual uint8_t read_white_list_capacity() {
return HciGetWhiteListSize();
}
virtual ble_error_t clear_whitelist() {
DmDevWhiteListClear();
return BLE_ERROR_NONE;
}
virtual ble_error_t add_device_to_whitelist(
whitelist_address_type_t address_type,
address_t address
) {
DmDevWhiteListAdd(
address_type.value(),
const_cast<uint8_t*>(address.data())
);
return BLE_ERROR_NONE;
}
virtual ble_error_t remove_device_from_whitelist(
whitelist_address_type_t address_type,
address_t address
) {
DmDevWhiteListRemove(
address_type.value(),
const_cast<uint8_t*>(address.data())
);
return BLE_ERROR_NONE;
}
virtual ble_error_t connection_parameters_update(
connection_handle_t connection,
uint16_t connection_interval_min,
uint16_t connection_interval_max,
uint16_t connection_latency,
uint16_t supervision_timeout,
uint16_t minimum_connection_event_length,
uint16_t maximum_connection_event_length
) {
if (DmConnCheckIdle(connection) != 0) {
return BLE_ERROR_INVALID_STATE;
}
hciConnSpec_t connection_spec = {
connection_interval_min,
connection_interval_max,
connection_latency,
supervision_timeout,
minimum_connection_event_length,
maximum_connection_event_length
};
DmConnUpdate(
connection,
&connection_spec
);
return BLE_ERROR_NONE;
}
virtual ble_error_t accept_connection_parameter_request(
connection_handle_t connection_handle,
uint16_t interval_min,
uint16_t interval_max,
uint16_t latency,
uint16_t supervision_timeout,
uint16_t minimum_connection_event_length,
uint16_t maximum_connection_event_length
) {
hciConnSpec_t connection_spec = {
interval_min,
interval_max,
latency,
supervision_timeout,
minimum_connection_event_length,
maximum_connection_event_length
};
DmRemoteConnParamReqReply(connection_handle, &connection_spec);
return BLE_ERROR_NONE;
}
virtual ble_error_t reject_connection_parameter_request(
connection_handle_t connection_handle,
hci_error_code_t rejection_reason
) {
DmRemoteConnParamReqNegReply(
connection_handle,
rejection_reason.value()
);
return BLE_ERROR_NONE;
}
virtual ble_error_t disconnect(
connection_handle_t connection,
disconnection_reason_t disconnection_reason
) {
DmConnClose(
DM_CLIENT_ID_APP,
connection,
disconnection_reason.value()
);
return BLE_ERROR_NONE;
}
// singleton of the ARM Cordio client
static Gap& get_gap() {
static Gap _gap;
return _gap;
}
private:
typedef bool (*event_handler_t)(const wsfMsgHdr_t* msg);
public:
/**
* Callback which handle wsfMsgHdr_t and forward them to emit_gap_event.
*/
static void gap_handler(const wsfMsgHdr_t* msg) {
if (msg == NULL) {
return;
}
// all handlers are stored in a static array
static const event_handler_t handlers[] = {
&event_handler<ConnectionCompleteMessageConverter>,
&event_handler<GapAdvertisingReportMessageConverter>,
&event_handler<DisconnectionMessageConverter>,
&event_handler<ConnectionUpdateMessageConverter>,
&event_handler<RemoteConnectionParameterRequestMessageConverter>
};
// event->hdr.param: connection handle
// traverse all handlers and execute them with the event in input.
// exit if an handler has handled the event.
for(size_t i = 0; i < (sizeof(handlers)/sizeof(handlers[0])); ++i) {
if (handlers[i](msg)) {
return;
}
}
}
private:
/**
* T shall define a can_convert and convert function and a type
*/
template<typename T>
static bool event_handler(const wsfMsgHdr_t* msg) {
if (T::can_convert(msg)) {
get_gap().emit_gap_event(T::convert((const typename T::type*)msg));
return true;
}
return false;
}
/**
* Traits defining can_convert for events.
*/
template<uint8_t EventID>
struct MessageConverter {
static bool can_convert(const wsfMsgHdr_t* msg) {
if (msg->event == EventID) {
return true;
}
return false;
}
};
struct ConnectionCompleteMessageConverter : public MessageConverter<DM_CONN_OPEN_IND> {
typedef hciLeConnCmplEvt_t type;
static GapConnectionCompleteEvent convert(const hciLeConnCmplEvt_t* conn_evt) {
return GapConnectionCompleteEvent(
conn_evt->status,
// note the usage of the stack handle, not the HCI handle
conn_evt->hdr.param,
(connection_role_t::type) conn_evt->role,
(advertising_peer_address_type_t::type) conn_evt->addrType,
conn_evt->peerAddr,
conn_evt->connInterval,
conn_evt->connLatency,
conn_evt->supTimeout
);
}
};
struct GapAdvertisingReportMessageConverter : public MessageConverter<DM_SCAN_REPORT_IND> {
typedef hciLeAdvReportEvt_t type;
struct CordioGapAdvertisingReportEvent : public GapAdvertisingReportEvent {
CordioGapAdvertisingReportEvent(const advertising_t& advertising) :
GapAdvertisingReportEvent(), advertising(advertising) {
}
virtual ~CordioGapAdvertisingReportEvent() { }
virtual uint8_t size() const {
return 1;
}
virtual advertising_t operator[](uint8_t i) const {
return advertising;
}
advertising_t advertising;
};
static CordioGapAdvertisingReportEvent convert(const hciLeAdvReportEvt_t *scan_report) {
GapAdvertisingReportEvent::advertising_t advertising = {
(received_advertising_type_t::type) scan_report->eventType,
(connection_peer_address_type_t::type) scan_report->addrType,
scan_report->addr,
make_const_ArrayView(scan_report->pData, scan_report->len),
scan_report->rssi
};
return CordioGapAdvertisingReportEvent(advertising);
}
};
struct DisconnectionMessageConverter : public MessageConverter<DM_CONN_CLOSE_IND> {
typedef hciDisconnectCmplEvt_t type;
static GapDisconnectionCompleteEvent convert(const hciDisconnectCmplEvt_t* disc_evt) {
return GapDisconnectionCompleteEvent(
disc_evt->status,
// note the usage of the stack handle, not the HCI handle
disc_evt->hdr.param,
disc_evt->reason
);
}
};
struct ConnectionUpdateMessageConverter : public MessageConverter<DM_CONN_UPDATE_IND> {
typedef hciLeConnUpdateCmplEvt_t type;
static GapConnectionUpdateEvent convert(const hciLeConnUpdateCmplEvt_t* evt) {
return GapConnectionUpdateEvent(
evt->status,
evt->hdr.param,
evt->connInterval,
evt->connLatency,
evt->supTimeout
);
}
};
struct RemoteConnectionParameterRequestMessageConverter : public MessageConverter<DM_REM_CONN_PARAM_REQ_IND> {
typedef hciLeRemConnParamReqEvt_t type;
static GapRemoteConnectionParameterRequestEvent convert(const hciLeRemConnParamReqEvt_t* evt) {
return GapRemoteConnectionParameterRequestEvent(
evt->hdr.param,
evt->intervalMin,
evt->intervalMax,
evt->latency,
evt->timeout
);
}
};
private:
address_t device_random_address;
bool use_active_scanning;
};
} // cordio
} // vendor
} // pal
} // ble
#endif /* CORDIO_PAL_GAP_ */

View File

@ -0,0 +1,88 @@
#ifndef CORDIO_PAL_GENERIC_ACCESS_SERVICE_
#define CORDIO_PAL_GENERIC_ACCESS_SERVICE_
#include "ble/pal/GenericAccessService.h"
#include "CordioGattServer.h"
namespace ble {
namespace pal {
namespace vendor {
namespace cordio {
/**
* Implementation of ble::pal::GenericAccessService for the Cordio stack.
*/
class GenericAccessService : public ::ble::pal::GenericAccessService {
public:
GenericAccessService() { }
virtual ~GenericAccessService() { }
virtual ble_error_t get_device_name_length(uint8_t& length) {
const uint8_t* name = NULL;
uint16_t actual_length = 0;
gatt_server().getDeviceName(name, actual_length);
length = actual_length;
return BLE_ERROR_NONE;
}
virtual ble_error_t get_device_name(ArrayView<uint8_t>& array) {
const uint8_t* name = NULL;
uint16_t length = 0;
gatt_server().getDeviceName(name, length);
if (length > array.size()) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
memcpy(array.data(), name, length);
return BLE_ERROR_NONE;
}
virtual ble_error_t set_device_name(const uint8_t* device_name) {
return gatt_server().setDeviceName(device_name);
}
virtual ble_error_t get_appearance(
GapAdvertisingData::Appearance& appearance
) {
appearance = gatt_server().getAppearance();
return BLE_ERROR_NONE;
}
virtual ble_error_t set_appearance(
GapAdvertisingData::Appearance appearance
) {
gatt_server().setAppearance(appearance);
return BLE_ERROR_NONE;
}
virtual ble_error_t get_peripheral_prefered_connection_parameters(
::Gap::ConnectionParams_t& parameters
) {
parameters = gatt_server().getPreferredConnectionParams();
return BLE_ERROR_NONE;
}
virtual ble_error_t set_peripheral_prefered_connection_parameters(
const ::Gap::ConnectionParams_t& parameters
) {
gatt_server().setPreferredConnectionParams(parameters);
return BLE_ERROR_NONE;
}
private:
ble::vendor::cordio::GattServer& gatt_server() {
return ble::vendor::cordio::GattServer::getInstance();
}
};
} // cordio
} // vendor
} // pal
} // ble
#endif /* CORDIO_PAL_GENERIC_ACCESS_SERVICE_ */

View File

@ -39,9 +39,6 @@
/*! WSF handler ID */
wsfHandlerId_t stack_handler_id;
/* Store the Event signaling state */
bool isEventsSignaled = false;
/**
* Weak definition of ble_cordio_get_hci_driver.
* A runtime error is generated if the user does not define any
@ -78,10 +75,7 @@ extern "C" void hci_mbed_os_handle_reset_sequence(uint8_t* msg)
*/
extern "C" void wsf_mbed_ble_signal_event(void)
{
if(isEventsSignaled == false) {
isEventsSignaled = true;
ble::vendor::cordio::BLE::deviceInstance().signalEventsToProcess(::BLE::DEFAULT_INSTANCE);
}
ble::vendor::cordio::BLE::deviceInstance().signalEventsToProcess(::BLE::DEFAULT_INSTANCE);
}
/**
@ -99,7 +93,8 @@ namespace cordio {
BLE::BLE(CordioHCIDriver& hci_driver) :
initialization_status(NOT_INITIALIZED),
instanceID(::BLE::DEFAULT_INSTANCE)
instanceID(::BLE::DEFAULT_INSTANCE),
_event_queue()
{
_hci_driver = &hci_driver;
stack_setup();
@ -122,9 +117,9 @@ ble_error_t BLE::init(
::BLE::InstanceID_t instanceID,
FunctionPointerWithContext< ::BLE::InitializationCompleteCallbackContext *> initCallback)
{
switch (initialization_status) {
case NOT_INITIALIZED:
_event_queue.initialize(this, instanceID);
_init_callback = initCallback;
start_stack_reset();
return BLE_ERROR_NONE;
@ -157,6 +152,7 @@ ble_error_t BLE::shutdown()
getGattServer().reset();
getGattClient().reset();
getGap().reset();
_event_queue.clear();
return BLE_ERROR_NONE;
}
@ -167,15 +163,25 @@ const char* BLE::getVersion()
return version;
}
Gap& BLE::getGap()
::Gap& BLE::getGap()
{
return cordio::Gap::getInstance();
typedef ::Gap& return_type;
const BLE* self = this;
return const_cast<return_type>(self->getGap());
}
const Gap& BLE::getGap() const
const ::Gap& BLE::getGap() const
{
return cordio::Gap::getInstance();
}
static pal::vendor::cordio::Gap& cordio_pal_gap =
pal::vendor::cordio::Gap::get_gap();
static pal::vendor::cordio::GenericAccessService cordio_gap_service;
static ble::generic::GenericGap gap(
_event_queue,
cordio_pal_gap,
cordio_gap_service
);
return gap;
};
GattServer& BLE::getGattServer()
{
@ -226,11 +232,8 @@ void BLE::waitForEvent()
void BLE::processEvents()
{
if (isEventsSignaled) {
isEventsSignaled = false;
callDispatcher();
}
}
callDispatcher();
}
void BLE::stack_handler(wsfEventMask_t event, wsfMsgHdr_t* msg)
{
@ -245,66 +248,12 @@ void BLE::processEvents()
BLE_ERROR_NONE
};
deviceInstance().getGattServer().initialize();
deviceInstance().getGap().initialize();
deviceInstance().initialization_status = INITIALIZED;
_init_callback.call(&context);
} break;
case DM_ADV_START_IND:
break;
case DM_ADV_STOP_IND:
Gap::getInstance().advertisingStopped();
break;
case DM_SCAN_REPORT_IND: {
hciLeAdvReportEvt_t *scan_report = (hciLeAdvReportEvt_t*) msg;
Gap::getInstance().processAdvertisementReport(
scan_report->addr,
scan_report->rssi,
(scan_report->eventType == DM_RPT_SCAN_RESPONSE) ? true : false,
(GapAdvertisingParams::AdvertisingType_t) scan_report->eventType,
scan_report->len,
scan_report->pData
);
} break;
case DM_CONN_OPEN_IND: {
hciLeConnCmplEvt_t* conn_evt = (hciLeConnCmplEvt_t*) msg;
dmConnId_t connection_id = conn_evt->hdr.param;
Gap::getInstance().setConnectionHandle(connection_id);
Gap::AddressType_t own_addr_type;
Gap::Address_t own_addr;
Gap::getInstance().getAddress(&own_addr_type, own_addr);
Gap::ConnectionParams_t params = {
conn_evt->connInterval,
conn_evt->connInterval,
conn_evt->connLatency,
conn_evt->supTimeout
};
Gap::getInstance().processConnectionEvent(
connection_id,
(conn_evt->role == DM_ROLE_MASTER) ? Gap::CENTRAL : Gap::PERIPHERAL,
(Gap::AddressType_t) conn_evt->addrType,
conn_evt->peerAddr,
own_addr_type,
own_addr,
&params
);
} break;
case DM_CONN_CLOSE_IND: {
dmEvt_t *disconnect_evt = (dmEvt_t*) msg;
Gap::getInstance().setConnectionHandle(DM_CONN_ID_NONE);
Gap::getInstance().processDisconnectionEvent(
disconnect_evt->hdr.param,
(Gap::DisconnectionReason_t) disconnect_evt->connClose.reason
);
} break;
default:
ble::pal::vendor::cordio::Gap::gap_handler(msg);
break;
}
}
@ -419,6 +368,10 @@ void BLE::start_stack_reset()
void BLE::callDispatcher()
{
// process the external event queue
_event_queue.process();
// follow by stack events
static uint32_t lastTimeUs = us_ticker_read();
uint32_t currTimeUs, deltaTimeMs;

View File

@ -1,543 +0,0 @@
/* 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.
*/
#include <algorithm>
#include "CordioGap.h"
#include "mbed.h"
#include "dm_api.h"
#include "CordioGattServer.h"
#include "hci_core.h"
/**< Minimum Advertising interval in 625 us units, i.e. 20 ms. */
#define BLE_GAP_ADV_INTERVAL_MIN 0x0020
/**< Minimum Advertising interval in 625 us units for non connectable mode, i.e. 100 ms. */
#define BLE_GAP_ADV_NONCON_INTERVAL_MIN 0x00A0
/**< Maximum Advertising interval in 625 us units, i.e. 10.24 s. */
#define BLE_GAP_ADV_INTERVAL_MAX 0x4000
namespace ble {
namespace vendor {
namespace cordio {
Gap &Gap::getInstance()
{
static Gap m_instance;
return m_instance;
}
void Gap::initialize()
{
uint8_t whitelist_size = HciGetWhiteListSize();
if (whitelist_size == 0) {
return;
}
whitelist.addresses = new(std::nothrow) BLEProtocol::Address_t[whitelist_size];
if (whitelist.addresses == NULL) {
return;
}
whitelist.size = 0;
whitelist.capacity = hciCoreCb.whiteListSize;
}
ble_error_t Gap::setAddress(AddressType_t type, const Address_t address)
{
switch (type) {
case BLEProtocol::AddressType::PUBLIC:
// TODO: use vendor specific commands from the driver
return BLE_ERROR_OPERATION_NOT_PERMITTED;
// See bluetooth 5, Vol 6 part, part B, 1.3.2
case BLEProtocol::AddressType::RANDOM_STATIC:
if ((address[5] >> 6) != 3) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
m_type = type;
BdaCpy(m_addr, address);
DmDevSetRandAddr(m_addr);
break;
// should not be here, generation is supposed to be handled by the controller.
case BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE:
case BLEProtocol::AddressType::RANDOM_PRIVATE_NON_RESOLVABLE:
m_type = type;
return BLE_ERROR_NONE;
default:
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
DmAdvSetAddrType(m_type);
DmConnSetAddrType(m_type);
DmScanSetAddrType(m_type);
return BLE_ERROR_NONE;
}
ble_error_t Gap::getAddress(AddressType_t *typeP, Address_t address)
{
*typeP = m_type;
if(m_type == BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE ||
m_type == BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE) {
return BLE_ERROR_NONE;
}
BdaCpy(address, m_addr);
return BLE_ERROR_NONE;
}
ble_error_t Gap::setAdvertisingData(const GapAdvertisingData &advData, const GapAdvertisingData &scanResponse)
{
/* Make sure we don't exceed the advertising payload length */
if (advData.getPayloadLen() > GAP_ADVERTISING_DATA_MAX_PAYLOAD) {
return BLE_ERROR_BUFFER_OVERFLOW;
}
/* Make sure we have a payload! */
if (advData.getPayloadLen() == 0) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
/* set advertising and scan response data for discoverable mode */
DmAdvSetData(DM_ADV_HANDLE_DEFAULT, HCI_ADV_DATA_OP_COMP_FRAG, DM_DATA_LOC_ADV, advData.getPayloadLen(), (uint8_t*)advData.getPayload());
DmAdvSetData(DM_ADV_HANDLE_DEFAULT, HCI_ADV_DATA_OP_COMP_FRAG, DM_DATA_LOC_SCAN, scanResponse.getPayloadLen(), (uint8_t*)scanResponse.getPayload());
return BLE_ERROR_NONE;
}
ble_error_t Gap::connect(
const BLEProtocol::AddressBytes_t peerAddr,
BLEProtocol::AddressType_t peerAddrType,
const ConnectionParams_t* connectionParams,
const GapScanningParams* scanParams
) {
// prepare the scan interval
if (scanParams != NULL) {
DmConnSetScanInterval(scanParams->getInterval(), scanParams->getWindow());
}
if (connectionParams != NULL) {
hciConnSpec_t conn_spec = {
/* connIntervalMin */ connectionParams->minConnectionInterval,
/* connIntervalMax */ connectionParams->maxConnectionInterval,
/* connLatency */ connectionParams->slaveLatency,
/* supTimeout */ connectionParams->connectionSupervisionTimeout,
/* minCeLen */ DM_GAP_CONN_EST_MIN_CE_LEN,
/* maxCeLen */ DM_GAP_CONN_EST_MAX_CE_LEN
};
DmConnSetConnSpec(&conn_spec);
}
DmScanStop();
dmConnId_t connection_id = DmConnOpen(
DM_CLIENT_ID_APP,
HCI_INIT_PHY_LE_1M_BIT,
peerAddrType,
(uint8_t*) peerAddr
);
if (connection_id == DM_CONN_ID_NONE) {
return BLE_ERROR_INTERNAL_STACK_FAILURE;
}
return BLE_ERROR_NONE;
}
uint16_t Gap::getMinAdvertisingInterval() const
{
return BLE_GAP_ADV_INTERVAL_MIN;
}
uint16_t Gap::getMinNonConnectableAdvertisingInterval() const
{
return BLE_GAP_ADV_NONCON_INTERVAL_MIN;
}
uint16_t Gap::getMaxAdvertisingInterval() const
{
return BLE_GAP_ADV_INTERVAL_MAX;
}
ble_error_t Gap::startAdvertising(const GapAdvertisingParams &params)
{
/* Make sure we support the advertising type */
if (params.getAdvertisingType() == GapAdvertisingParams::ADV_CONNECTABLE_DIRECTED) {
/* ToDo: This requires a proper security implementation, etc. */
return BLE_ERROR_NOT_IMPLEMENTED;
}
/* Check interval range */
if (params.getAdvertisingType() == GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED) {
/* Min delay is slightly longer for unconnectable devices */
if ((params.getIntervalInADVUnits() < GapAdvertisingParams::GAP_ADV_PARAMS_INTERVAL_MIN_NONCON) ||
(params.getIntervalInADVUnits() > GapAdvertisingParams::GAP_ADV_PARAMS_INTERVAL_MAX)) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
} else {
if ((params.getIntervalInADVUnits() < GapAdvertisingParams::GAP_ADV_PARAMS_INTERVAL_MIN) ||
(params.getIntervalInADVUnits() > GapAdvertisingParams::GAP_ADV_PARAMS_INTERVAL_MAX)) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
}
/* Check timeout is zero for Connectable Directed */
if ((params.getAdvertisingType() == GapAdvertisingParams::ADV_CONNECTABLE_DIRECTED) && (params.getTimeout() != 0)) {
/* Timeout must be 0 with this type, although we'll never get here */
/* since this isn't implemented yet anyway */
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
/* Check timeout for other advertising types */
if ((params.getAdvertisingType() != GapAdvertisingParams::ADV_CONNECTABLE_DIRECTED) &&
(params.getTimeout() > GapAdvertisingParams::GAP_ADV_PARAMS_TIMEOUT_MAX)) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
uint16_t adv_interval_min = params.getIntervalInADVUnits();
if (adv_interval_min == GapAdvertisingParams::GAP_ADV_PARAMS_INTERVAL_MAX) {
--adv_interval_min;
}
uint16_t adv_interval_max = adv_interval_min + 1;
DmAdvSetInterval(DM_ADV_HANDLE_DEFAULT, adv_interval_min, adv_interval_max);
/* Peer Addr Type 0 = Public */
uint8_t peerAddrType = 0;
uint8_t peerAddr[6] = { 0 };
DmAdvConfig(DM_ADV_HANDLE_DEFAULT, params.getAdvertisingType(), peerAddrType, peerAddr);
uint8_t adv_handles[] = { DM_ADV_HANDLE_DEFAULT };
uint16_t adv_durations[] = { (uint16_t) (params.getTimeout() * 1000) };
uint8_t max_ea_events[] = { 0 };
DmAdvStart(1, adv_handles, adv_durations, max_ea_events);
state.advertising = 1;
return BLE_ERROR_NONE;
}
ble_error_t Gap::stopAdvertising(void)
{
uint8_t adv_handles[] = { DM_ADV_HANDLE_DEFAULT };
DmAdvStop(1, adv_handles);
state.advertising = 0;
return BLE_ERROR_NONE;
}
ble_error_t Gap::disconnect(Handle_t connectionHandle, DisconnectionReason_t reason)
{
DmConnClose(DM_CLIENT_ID_APP, connectionHandle, reason);
state.advertising = 0;
state.connected = 0;
return BLE_ERROR_NONE;
}
ble_error_t Gap::disconnect(DisconnectionReason_t reason)
{
DmConnClose(DM_CLIENT_ID_APP, m_connectionHandle, reason);
state.advertising = 0;
state.connected = 0;
return BLE_ERROR_NONE;
}
ble_error_t Gap::setDeviceName(const uint8_t *deviceName)
{
return GattServer::getInstance().setDeviceName(deviceName);
}
ble_error_t Gap::getDeviceName(uint8_t *deviceName, unsigned *lengthP)
{
const uint8_t* name = NULL;
uint16_t length = 0;
GattServer::getInstance().getDeviceName(name, length);
if (deviceName != NULL) {
memcpy(deviceName, name, std::min((uint16_t) *lengthP, length));
}
*lengthP = length;
return BLE_ERROR_NONE;
}
ble_error_t Gap::setAppearance(GapAdvertisingData::Appearance appearance)
{
GattServer::getInstance().setAppearance(appearance);
return BLE_ERROR_NONE;
}
ble_error_t Gap::getAppearance(GapAdvertisingData::Appearance *appearanceP)
{
*appearanceP = GattServer::getInstance().getAppearance();
return BLE_ERROR_NONE;
}
ble_error_t Gap::setTxPower(int8_t txPower)
{
#if 0
HciVsSetTxPower(txPower);
return BLE_ERROR_NONE;
#else
return BLE_ERROR_NOT_IMPLEMENTED;
#endif
}
void Gap::getPermittedTxPowerValues(const int8_t **valueArrayPP, size_t *countP)
{
*valueArrayPP = NULL;
*countP = 0;
}
void Gap::setConnectionHandle(uint16_t connectionHandle)
{
m_connectionHandle = connectionHandle;
}
uint16_t Gap::getConnectionHandle(void)
{
return m_connectionHandle;
}
ble_error_t Gap::getPreferredConnectionParams(ConnectionParams_t *params)
{
*params = GattServer::getInstance().getPreferredConnectionParams();
return BLE_ERROR_NONE;
}
ble_error_t Gap::setPreferredConnectionParams(const ConnectionParams_t *params)
{
// ensure that parameters are correct
// see BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part C]
// section 12.3 PERIPHERAL PREFERRED CONNECTION PARAMETERS CHARACTERISTIC
if (((0x0006 > params->minConnectionInterval) || (params->minConnectionInterval > 0x0C80)) &&
params->minConnectionInterval != 0xFFFF) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
if (((params->minConnectionInterval > params->maxConnectionInterval) || (params->maxConnectionInterval > 0x0C80)) &&
params->maxConnectionInterval != 0xFFFF) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
if (params->slaveLatency > 0x01F3) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
if (((0x000A > params->connectionSupervisionTimeout) || (params->connectionSupervisionTimeout > 0x0C80)) &&
params->connectionSupervisionTimeout != 0xFFFF) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
GattServer::getInstance().setPreferredConnectionParams(*params);
return BLE_ERROR_NONE;
}
ble_error_t Gap::updateConnectionParams(Handle_t handle, const ConnectionParams_t *newParams)
{
if (DmConnCheckIdle(handle) != 0) {
return BLE_STACK_BUSY;
}
hciConnSpec_t connSpec;
connSpec.connIntervalMin = newParams->minConnectionInterval;
connSpec.connIntervalMax = newParams->maxConnectionInterval;
connSpec.connLatency = newParams->slaveLatency;
connSpec.supTimeout = newParams->connectionSupervisionTimeout;
DmConnUpdate(handle, &connSpec);
return BLE_ERROR_NONE;
}
ble_error_t Gap::startRadioScan(const GapScanningParams &scanningParams)
{
// not needed to start scanning if the whitelist is empty and the scanning
// policy filter all the advertising packets
if ((whitelist.size == 0) && (scanning_policy_mode == Gap::SCAN_POLICY_FILTER_ALL_ADV)) {
return BLE_ERROR_INVALID_STATE;
}
uint16_t scan_intervals[] = { scanningParams.getInterval() };
uint16_t scan_windows[] = { scanningParams.getWindow() };
DmScanSetInterval(HCI_SCAN_PHY_LE_1M_BIT, scan_intervals, scan_windows);
uint8_t scanType = scanningParams.getActiveScanning() ? DM_SCAN_TYPE_ACTIVE : DM_SCAN_TYPE_PASSIVE;
uint32_t duration = (uint32_t)scanningParams.getTimeout() * 1000;
if (duration > 0xFFFF) {
// saturate to 16-bits
duration = 0xFFFF;
}
DmScanStart(HCI_SCAN_PHY_LE_1M_BIT, DM_DISC_MODE_NONE, &scanType, TRUE, duration, 0);
return BLE_ERROR_NONE;
}
ble_error_t Gap::stopScan(void)
{
DmScanStop();
return BLE_ERROR_NONE;
}
void Gap::advertisingStopped(void)
{
/* If advertising stopped due to a call to stopAdvertising(), state.advertising will
* be '0.' Otherwise, advertising must have stopped due to a timeout
*/
if (state.advertising) {
processTimeoutEvent(Gap::TIMEOUT_SRC_ADVERTISING);
}
}
uint8_t Gap::getMaxWhitelistSize(void) const
{
return whitelist.capacity;
}
ble_error_t Gap::getWhitelist(Whitelist_t &other) const
{
// i is a shorthand for other.size
uint8_t& i = other.size;
for (i = 0; (i < whitelist.capacity) && (i < other.capacity); ++i) {
other.addresses[i] = whitelist.addresses[i];
}
return BLE_ERROR_NONE;
}
ble_error_t Gap::setWhitelist(const Whitelist_t& other)
{
if (other.capacity > whitelist.capacity) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
// note : can be improved by sending the diff instead of the full list
DmDevWhiteListClear();
// alias i to whitelist.size
uint8_t& i = whitelist.size;
for (i = 0; (i < other.capacity) && (i < whitelist.capacity); ++i) {
whitelist.addresses[i] = other.addresses[i];
DmDevWhiteListAdd(
(whitelist.addresses[i].type > 1) ? 0xFF : whitelist.addresses[i].type,
whitelist.addresses[i].address
);
}
return BLE_ERROR_NONE;
}
ble_error_t Gap::setAdvertisingPolicyMode(AdvertisingPolicyMode_t mode)
{
bool_t result = DmDevSetFilterPolicy(
DM_FILT_POLICY_MODE_ADV,
mode
);
if (result == false) {
return BLE_ERROR_INVALID_STATE;
}
advertising_policy_mode = mode;
return BLE_ERROR_NONE;
}
Gap::AdvertisingPolicyMode_t Gap::getAdvertisingPolicyMode(void) const
{
return advertising_policy_mode;
}
ble_error_t Gap::setScanningPolicyMode(ScanningPolicyMode_t mode)
{
bool_t result = DmDevSetFilterPolicy(
DM_FILT_POLICY_MODE_SCAN,
mode
);
if (result == false) {
return BLE_ERROR_INVALID_STATE;
}
scanning_policy_mode = mode;
return BLE_ERROR_NONE;
}
Gap::ScanningPolicyMode_t Gap::getScanningPolicyMode(void) const
{
return scanning_policy_mode;
}
ble_error_t Gap::setInitiatorPolicyMode(InitiatorPolicyMode_t mode)
{
bool_t result = DmDevSetFilterPolicy(
DM_FILT_POLICY_MODE_INIT,
mode
);
if (result == false) {
return BLE_ERROR_INVALID_STATE;
}
initiator_policy_mode = mode;
return BLE_ERROR_NONE;
}
Gap::InitiatorPolicyMode_t Gap::getInitiatorPolicyMode(void) const
{
return initiator_policy_mode;
}
ble_error_t Gap::reset(void)
{
this->::Gap::reset();
delete[] whitelist.addresses;
whitelist.addresses = NULL;
whitelist.size = 0;
whitelist.capacity = 0;
return BLE_ERROR_NONE;
}
Gap::Gap() :
::Gap(),
m_connectionHandle(DM_CONN_ID_NONE),
m_type(BLEProtocol::AddressType::RANDOM_STATIC),
m_addr(),
advertising_policy_mode(ADV_POLICY_IGNORE_WHITELIST),
scanning_policy_mode(SCAN_POLICY_IGNORE_WHITELIST),
initiator_policy_mode(INIT_POLICY_IGNORE_WHITELIST),
whitelist()
{
}
} // namespace cordio
} // namespace vendor
} // namespace ble

View File

@ -16,7 +16,6 @@
#include <algorithm>
#include "CordioGattServer.h"
#include "CordioGap.h"
#include "mbed.h"
#include "wsf_types.h"
#include "att_api.h"
@ -286,33 +285,63 @@ ble_error_t GattServer::read(Gap::Handle_t connectionHandle, GattAttribute::Hand
ble_error_t GattServer::write(GattAttribute::Handle_t attributeHandle, const uint8_t buffer[], uint16_t len, bool localOnly)
{
uint16_t connectionHandle = Gap::getInstance().getConnectionHandle();
// Check to see if this is a CCCD, if it is the case update the value for all
// connections
uint8_t idx;
for (idx = 0; idx < cccCnt; idx++) {
if (attributeHandle == cccSet[idx].handle) {
for (uint16_t conn_id = 0, conn_found = 0; (conn_found < DM_CONN_MAX) && (conn_id < 0x100); ++conn_id) {
if (DmConnInUse(conn_id) == true) {
++conn_found;
} else {
continue;
}
AttsCccSet(conn_id, idx, *((uint16_t*)buffer));
}
return BLE_ERROR_NONE;
}
}
// write the value to the attribute handle
if (AttsSetAttr(attributeHandle, len, (uint8_t*)buffer) != ATT_SUCCESS) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
if (!localOnly) {
if (connectionHandle != DM_CONN_ID_NONE) {
// return if the update does not have to be propagated to peers
if (localOnly) {
return BLE_ERROR_NONE;
}
// Check to see if this characteristic has a CCCD attribute
uint8_t idx;
for (idx = 0; idx < cccCnt; idx++) {
if (attributeHandle == cccHandles[idx]) {
break;
}
// Check to see if this characteristic has a CCCD attribute
for (idx = 0; idx < cccCnt; idx++) {
if (attributeHandle == cccHandles[idx]) {
break;
}
}
// exit if the characteristic has no CCCD attribute
if (idx >= cccCnt) {
return BLE_ERROR_NONE;
}
// This characteristic has a CCCD attribute. Handle notifications and indications.
// for all connections
for (uint16_t conn_id = 0, conn_found = 0; (conn_found < DM_CONN_MAX) && (conn_id < 0x100); ++conn_id) {
if (DmConnInUse(conn_id) == true) {
++conn_found;
} else {
uint16_t cccEnabled = AttsCccEnabled(conn_id, idx);
if (cccEnabled & ATT_CLIENT_CFG_NOTIFY) {
AttsHandleValueNtf(conn_id, attributeHandle, len, (uint8_t*)buffer);
}
if (idx < cccCnt) {
// This characteristic has a CCCD attribute. Handle notifications and indications.
uint16_t cccEnabled = AttsCccEnabled(connectionHandle, idx);
if (cccEnabled & ATT_CLIENT_CFG_NOTIFY) {
AttsHandleValueNtf(connectionHandle, attributeHandle, len, (uint8_t*)buffer);
}
if (cccEnabled & ATT_CLIENT_CFG_INDICATE) {
AttsHandleValueInd(connectionHandle, attributeHandle, len, (uint8_t*)buffer);
}
if (cccEnabled & ATT_CLIENT_CFG_INDICATE) {
AttsHandleValueInd(conn_id, attributeHandle, len, (uint8_t*)buffer);
}
}
AttsCccSet(conn_id, idx, *((uint16_t*)buffer));
}
return BLE_ERROR_NONE;
@ -332,26 +361,59 @@ ble_error_t GattServer::write(Gap::Handle_t connectionHandle, GattAttribute::Han
}
}
// This is not a CCCD. Use the non-connection specific update method.
return write(attributeHandle, buffer, len, localOnly);
// write the value to the attribute handle
if (AttsSetAttr(attributeHandle, len, (uint8_t*)buffer) != ATT_SUCCESS) {
return BLE_ERROR_PARAM_OUT_OF_RANGE;
}
// return if the update does not have to be propagated to peers
if (localOnly) {
return BLE_ERROR_NONE;
}
// Check to see if this characteristic has a CCCD attribute
for (idx = 0; idx < cccCnt; idx++) {
if (attributeHandle == cccHandles[idx]) {
break;
}
}
// exit if the characteristic has no CCCD attribute
if (idx >= cccCnt) {
return BLE_ERROR_NONE;
}
// This characteristic has a CCCD attribute. Handle notifications and indications.
uint16_t cccEnabled = AttsCccEnabled(connectionHandle, idx);
if (cccEnabled & ATT_CLIENT_CFG_NOTIFY) {
AttsHandleValueNtf(connectionHandle, attributeHandle, len, (uint8_t*)buffer);
}
if (cccEnabled & ATT_CLIENT_CFG_INDICATE) {
AttsHandleValueInd(connectionHandle, attributeHandle, len, (uint8_t*)buffer);
}
return BLE_ERROR_NONE;
}
ble_error_t GattServer::areUpdatesEnabled(const GattCharacteristic &characteristic, bool *enabledP)
{
uint16_t connectionHandle = Gap::getInstance().getConnectionHandle();
if (connectionHandle != DM_CONN_ID_NONE) {
uint8_t idx;
for (idx = 0; idx < cccCnt; idx++) {
if (characteristic.getValueHandle() == cccHandles[idx]) {
uint16_t cccValue = AttsCccGet(connectionHandle, idx);
if (cccValue & ATT_CLIENT_CFG_NOTIFY) {
*enabledP = true;
for (size_t idx = 0; idx < cccCnt; idx++) {
if (characteristic.getValueHandle() == cccHandles[idx]) {
for (uint16_t conn_id = 0, conn_found = 0; (conn_found < DM_CONN_MAX) && (conn_id < 0x100); ++conn_id) {
if (DmConnInUse(conn_id) == true) {
++conn_found;
} else {
*enabledP = false;
continue;
}
uint16_t cccValue = AttsCccGet(conn_id, idx);
if ((cccValue & ATT_CLIENT_CFG_NOTIFY) || (cccValue & ATT_CLIENT_CFG_INDICATE)) {
*enabledP = true;
return BLE_ERROR_NONE;
}
return BLE_ERROR_NONE;
}
*enabledP = false;
return BLE_ERROR_NONE;
}
}
@ -365,7 +427,7 @@ ble_error_t GattServer::areUpdatesEnabled(Gap::Handle_t connectionHandle, const
for (idx = 0; idx < cccCnt; idx++) {
if (characteristic.getValueHandle() == cccHandles[idx]) {
uint16_t cccValue = AttsCccGet(connectionHandle, idx);
if (cccValue & ATT_CLIENT_CFG_NOTIFY) {
if (cccValue & ATT_CLIENT_CFG_NOTIFY || (cccValue & ATT_CLIENT_CFG_INDICATE)) {
*enabledP = true;
} else {
*enabledP = false;