diff options
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/ble/ble_services/ble_ipsp/ble_ipsp.c')
-rw-r--r-- | thirdparty/nRF5_SDK_15.0.0_a53641a/components/ble/ble_services/ble_ipsp/ble_ipsp.c | 981 |
1 files changed, 981 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/ble/ble_services/ble_ipsp/ble_ipsp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/ble/ble_services/ble_ipsp/ble_ipsp.c new file mode 100644 index 0000000..291b536 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/ble/ble_services/ble_ipsp/ble_ipsp.c @@ -0,0 +1,981 @@ +/** + * Copyright (c) 2014 - 2018, 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 form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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. + * + */ +/* Attention! + * To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile + * qualification listings, this section of source code must not be modified. + */ +#include <stdbool.h> +#include <string.h> +#include "nordic_common.h" +#include "sdk_errors.h" +#include "nrf.h" +#include "sdk_config.h" +#include "ble_ipsp.h" +#include "ble_srv_common.h" +#include "sdk_os.h" + + +/** + * @defgroup ble_ipsp_log Module's Log Macros + * @details Macros used for creating module logs which can be useful in understanding handling + * of events or actions on API requests. These are intended for debugging purposes and + * can be enabled by defining the IOT_BLE_IPSP_CONFIG_LOG_ENABLED to 1. + * @note If NRF_LOG_ENABLED is disabled, having IOT_BLE_IPSP_CONFIG_LOG_ENABLED + * has no effect. + * @{ + */ + +#if IOT_BLE_IPSP_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME ipsp + +#define NRF_LOG_LEVEL IOT_BLE_IPSP_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR IOT_BLE_IPSP_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR IOT_BLE_IPSP_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define BLE_IPSP_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define BLE_IPSP_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define BLE_IPSP_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define BLE_IPSP_ENTRY() BLE_IPSP_TRC(">> %s", __func__) +#define BLE_IPSP_EXIT() BLE_IPSP_TRC("<< %s", __func__) +#define BLE_IPSP_EXIT_WITH_RESULT(result) BLE_IPSP_TRC("<< %s, result 0x%08lX", __func__, result) + +#else // IOT_BLE_IPSP_CONFIG_LOG_ENABLED + +#define BLE_IPSP_TRC(...) /**< Disables traces. */ +#define BLE_IPSP_DUMP(...) /**< Disables dumping of octet streams. */ +#define BLE_IPSP_ERR(...) /**< Disables error logs. */ + +#define BLE_IPSP_ENTRY(...) +#define BLE_IPSP_EXIT(...) +#define BLE_IPSP_EXIT_WITH_RESULT(...) + +#endif // IOT_BLE_IPSP_CONFIG_LOG_ENABLED + +#define IPSP_ANY_CID 0xFFFE /**< Identifier for any channel. Usage: Search for existing channel on a connection handle. */ + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * BLE_HPS_DISABLE_API_PARAM_CHECK should be defined to disable these checks. + * + * @{ + */ +#if (BLE_IPSP_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Macro to check is module is initialized before requesting one of the module procedures. */ +#define VERIFY_MODULE_IS_INITIALIZED() \ + if (m_evt_handler == NULL) \ + { \ + return (NRF_ERROR_MODULE_NOT_INITIALIZED + NRF_ERROR_BLE_IPSP_ERR_BASE); \ + } + +/**@brief Macro to check is module is initialized before requesting one of the module + procedures but does not use any return code. */ +#define VERIFY_MODULE_IS_INITIALIZED_VOID() \ + if (m_evt_handler == NULL) \ + { \ + return; \ + } + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL + NRF_ERROR_BLE_IPSP_ERR_BASE); \ + } + +/**@brief Verify the connection handle passed to the API. */ +#define VERIFY_CON_HANDLE(CON_HANDLE) \ + if ((CON_HANDLE) == BLE_CONN_HANDLE_INVALID) \ + { \ + return (NRF_ERROR_INVALID_PARAM + NRF_ERROR_BLE_IPSP_ERR_BASE); \ + } + +#else // BLE_IPSP_DISABLE_API_PARAM_CHECK + +#define VERIFY_MODULE_IS_INITIALIZED() +#define VERIFY_MODULE_IS_INITIALIZED_VOID() +#define NULL_PARAM_CHECK(PARAM) +#define VERIFY_CON_HANDLE(CON_HANDLE) + +#endif //BLE_IPSP_DISABLE_API_PARAM_CHECK + +/** + * @defgroup ble_ipsp_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case the need to use an alternative architecture arises. + * @{ + */ +#define BLE_IPSP_MUTEX_LOCK() SDK_MUTEX_LOCK(m_ipsp_mutex) /**< Lock module using mutex */ +#define BLE_IPSP_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_ipsp_mutex) /**< Unlock module using mutex */ +/** @} */ + +#define IPSP_MAX_CONNECTED_DEVICES BLE_IPSP_MAX_CHANNELS /**< Table for maximum number of connected devices the module will keep track of. */ +#define RX_BUFFER_TOTAL_SIZE (BLE_IPSP_RX_BUFFER_SIZE * BLE_IPSP_RX_BUFFER_COUNT) /**< Total receive buffer size reserved for each IPSP channel. */ +#define MAX_L2CAP_RX_BUFFER (RX_BUFFER_TOTAL_SIZE * BLE_IPSP_MAX_CHANNELS) /**< Total receive buffer received for all channels. */ +#define INVALID_CHANNEL_INSTANCE 0xFF /**< Indicates channel instance is invalid. */ + + +/**@brief IPSP Channel States. */ +typedef enum +{ + CHANNEL_IDLE, /**< Indicates the channel is free and not in use. */ + CHANNEL_CONNECTING, /**< Indicates the channel creation is requested and is awaiting a response. */ + CHANNEL_CONNECTED, /**< Indicates the channel is connected and ready for data exchange. */ + CHANNEL_DISCONNECTING /**< Indicates the channel is in the process of being disconnected. */ +} channel_state_t; + + +/**@brief Possible response actions for an incoming channel. Default is to accept. */ +typedef enum +{ + INCOMING_CHANNEL_ACCEPT, /**< Indicates that the incoming channel should be accepted if all other criteria are met. */ + INCOMING_CHANNEL_REJECT /**< Indicates that the incoming channel for IPSP PSM should be rejected regardless of the other criteria. */ +} incoming_channel_action_t; + +/**@brief Data type for book keeping connected devices. + * + * @note Not all connected devices establish an L2CAP connection. + */ +typedef struct +{ + volatile incoming_channel_action_t response; /**< Indicator if the incoming channel should be accepted or rejected. */ + ble_gap_addr_t ble_addr; /**< Bluetooth device address of the peer. */ + uint16_t conn_handle; /**< Connection handle identifying the link with the peer. */ +} peer_connection_t; + + +/**@brief IPSP Channel Information. */ +typedef struct +{ + uint16_t conn_handle; /**< Identifies the BLE link on which channel is established. BLE_CONN_HANDLE_INVALID if channel is unassigned. */ + uint16_t cid; /**< L2CAP channel identifier needed to manage the channel once established. BLE_L2CAP_CID_INVALID if channel is unassigned. */ + uint16_t rx_buffer_status; /**< Usage status of RX buffers. */ + uint8_t state; /**< State information for the channel. See @ref channel_state_t for details. */ + uint8_t * p_rx_buffer; /**< Receive buffer for the channel. */ +} channel_t; + + +static ble_ipsp_evt_handler_t m_evt_handler = NULL; /**< Asynchronous event notification callback registered with the module. */ +static channel_t m_channel[BLE_IPSP_MAX_CHANNELS]; /**< Table of channels managed by the module. */ +static uint8_t m_rx_buffer[MAX_L2CAP_RX_BUFFER]; /**< Receive buffer reserved for all channels to receive data on the L2CAP IPSP channel. */ +static peer_connection_t m_connected_device[IPSP_MAX_CONNECTED_DEVICES]; /**< Table maintaining list of peer devices and the connection handle. + \n This information is needed for the 6lowpan compression and decompression. + \n And no interface exists to query the softdevice. */ +SDK_MUTEX_DEFINE(m_ipsp_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ + + +/**@brief Initialize the peer connected device in the list. + * + * @param[in] index Identifies the list element to be initialized. + */ +static __INLINE void connected_device_init(uint32_t index) +{ + memset (&m_connected_device[index].ble_addr, 0, sizeof(ble_gap_addr_t)); + m_connected_device[index].conn_handle = BLE_CONN_HANDLE_INVALID; + m_connected_device[index].response = INCOMING_CHANNEL_ACCEPT; +} + + +/**@brief Allocate an entry for the peer connected device in the list. + * + * @param[in] p_peer_addr Pointer to peer's device address. + * @param[in] conn_handle Connection handle identifying the link with the peer. + */ +static __INLINE void connected_device_allocate(ble_gap_addr_t const * p_peer_addr, + uint16_t conn_handle) +{ + for (uint32_t index = 0; index < IPSP_MAX_CONNECTED_DEVICES; index++) + { + if (m_connected_device[index].conn_handle == BLE_CONN_HANDLE_INVALID) + { + m_connected_device[index].conn_handle = conn_handle; + memcpy(m_connected_device[index].ble_addr.addr, p_peer_addr->addr, BLE_GAP_ADDR_LEN); + break; + } + } +} + + +/**@brief Search for an entry for the peer connected device in the list. + * + * @param[in] conn_handle Connection handle identifying the link with the peer. + * + * @retval A valid device index in the list if found, else, + * IPSP_MAX_CONNECTED_DEVICES indicating the search failed. + */ +static __INLINE uint32_t connected_device_search(uint16_t conn_handle) +{ + for (uint32_t index = 0; index < IPSP_MAX_CONNECTED_DEVICES; index++) + { + if (m_connected_device[index].conn_handle == conn_handle) + { + return index; + } + } + return IPSP_MAX_CONNECTED_DEVICES; +} + + +/**@brief Initialize channel. + * + * @param[in] ch_id Identifies the IPSP channel on which the procedure is requested. + */ +static __INLINE void channel_init(uint8_t ch_id) +{ + m_channel[ch_id].conn_handle = BLE_CONN_HANDLE_INVALID; + m_channel[ch_id].cid = BLE_L2CAP_CID_INVALID; + m_channel[ch_id].rx_buffer_status = 0; + m_channel[ch_id].state = CHANNEL_IDLE; + m_channel[ch_id].p_rx_buffer = &m_rx_buffer[ch_id*RX_BUFFER_TOTAL_SIZE]; +} + + +/**@brief Free channel. + * + * @param[in] ch_id Identifies the IPSP channel on which the procedure is requested. + */ +static __INLINE void channel_free(uint8_t ch_id) +{ + BLE_IPSP_TRC("[Index 0x%02X]:[Conn Handle 0x%04X]:[CID 0x%04X]: Freeing channel", + ch_id, m_channel[ch_id].conn_handle, m_channel[ch_id].cid); + + channel_init(ch_id); +} + + +/**@brief Searches the IPSP channel based on connection handle and local L2CAP channel identifier. + * + * @param[in] conn_handle The connection handle, identifying the peer device. + * @param[in] l2cap_cid The local L2CAP channel identifier, identifying the L2CAP channel. + * @param[out] p_ch_id The IPSP channel identifier, if the search succeeded, else, + * BLE_IPSP_MAX_CHANNELS indicating no IPSP channel was found. + */ +static __INLINE uint32_t channel_search(uint16_t conn_handle, uint16_t l2cap_cid, uint8_t * p_ch_id) +{ + BLE_IPSP_TRC("[Conn Handle 0x%04X]:[CID 0x%04X]: channel_search", + conn_handle, l2cap_cid); + + for (int i = 0; i < BLE_IPSP_MAX_CHANNELS; i++) + { + BLE_IPSP_TRC("[@ Index 0x%02X] ==> Conn Handle: 0x%04X" + " CID : 0x%04X", + i, m_channel[i].conn_handle, m_channel[i].cid); + + if (m_channel[i].conn_handle == conn_handle) + { + if ((l2cap_cid == IPSP_ANY_CID) || (m_channel[i].cid == l2cap_cid)) + { + BLE_IPSP_TRC("channel_search succeeded, index 0x%04X", i); + + *p_ch_id = (uint8_t)i; + return NRF_SUCCESS; + } + } + } + + BLE_IPSP_TRC("No matching channel found!"); + return (NRF_ERROR_BLE_IPSP_ERR_BASE + NRF_ERROR_NOT_FOUND); +} + + +/**@brief Notify application of an event. + * + * @param[in] Identifies the IPSP instance for which the event is notified. + * @param[in] Describes the notified event and its parameters, if any. + */ +static __INLINE void app_notify(ble_ipsp_handle_t * p_handle, ble_ipsp_evt_t * p_event) +{ + BLE_IPSP_MUTEX_UNLOCK(); + + BLE_IPSP_TRC("[Conn Handle 0x%04X]:[CID 0x%04X]: Notifying application of event 0x%04X", + p_handle->conn_handle, p_handle->cid, p_event->evt_id); + + UNUSED_VARIABLE(m_evt_handler(p_handle, p_event)); + + BLE_IPSP_MUTEX_LOCK(); +} + + +/**@brief Verifies if the buffer is TX buffer on the channel or not. + * + * @param[in] ch_id Identifies the IPSP channel for which the procedure is requested. + * @param[in] p_buffer Address of the buffer being verified to be TX or not. + */ +static __INLINE bool is_tx_buffer(uint32_t ch_id, const uint8_t * p_buffer) +{ + // If the buffer is in the RX buffer list, then it is not TX! + if ((p_buffer >= (uint8_t *)&m_channel[ch_id].p_rx_buffer) && + (p_buffer < (uint8_t *)&m_channel[ch_id].p_rx_buffer[RX_BUFFER_TOTAL_SIZE])) + { + return false; + } + + return true; +} + + +/**@brief Submit receive buffers to the softdevice for a channel. + * + * @param[in] ch_id Identifies the IPSP channel for which the procedure is requested. + */ +static __INLINE void rx_buffers_submit(uint32_t ch_id) +{ + uint32_t retval; + + for (uint32_t buffer_index = 0; buffer_index < BLE_IPSP_RX_BUFFER_COUNT; buffer_index++) + { + const ble_data_t sdu_buf = + { + .p_data = (uint8_t *)&m_channel[ch_id].p_rx_buffer[buffer_index * BLE_IPSP_MTU], + .len = BLE_IPSP_MTU + }; + + if (IS_SET(m_channel[ch_id].rx_buffer_status, buffer_index) == 0) + { + retval = sd_ble_l2cap_ch_rx(m_channel[ch_id].conn_handle, + m_channel[ch_id].cid, + &sdu_buf); + if (retval == NRF_SUCCESS) + { + SET_BIT(m_channel[ch_id].rx_buffer_status, buffer_index); + } + } + } +} + + +/**@brief Mark a receive buffer as not in use for a particular channel. + * + * @param[in] ch_id Identifies the IPSP channel for which the procedure is requested. + * @param[in] p_buffer The buffer to be marked as unused. + * + * @note This is a temporary state for the receive buffer before it is resubmitted to the SoftDevice. + */ +static __INLINE void rx_buffer_mark_unused(uint32_t ch_id, uint8_t * p_buffer) +{ + for (uint32_t buffer_index = 0; buffer_index < BLE_IPSP_RX_BUFFER_COUNT; buffer_index++) + { + if (&m_channel[ch_id].p_rx_buffer[buffer_index * BLE_IPSP_MTU] == p_buffer) + { + CLR_BIT(m_channel[ch_id].rx_buffer_status, buffer_index); + } + } +} + + +void ble_ipsp_evt_handler(ble_evt_t const * p_evt) +{ + VERIFY_MODULE_IS_INITIALIZED_VOID(); + + ble_ipsp_handle_t handle; + ble_ipsp_evt_t ipsp_event; + uint32_t retval; + uint8_t ch_id; + bool notify_event; + bool submit_rx_buffer; + + ch_id = INVALID_CHANNEL_INSTANCE; + notify_event = false; + submit_rx_buffer = false; + retval = NRF_SUCCESS; + ipsp_event.evt_result = NRF_SUCCESS; + handle.conn_handle = BLE_CONN_HANDLE_INVALID; + handle.cid = BLE_L2CAP_CID_INVALID; + + BLE_IPSP_TRC("Received BLE Event 0x%04X",p_evt->header.evt_id); + + BLE_IPSP_MUTEX_LOCK(); + + switch (p_evt->header.evt_id) + { + case BLE_GAP_EVT_CONNECTED: + { + // Create an entry in the connected devices table. + // This is needed to be able to fetch the peer address on IPSP channel establishment. + connected_device_allocate(&p_evt->evt.gap_evt.params.connected.peer_addr, + p_evt->evt.gap_evt.conn_handle); + break; + } + case BLE_L2CAP_EVT_CH_SETUP_REQUEST: + { + // This event is generated for the acceptor role and indicates an channel establishment + // request from the peer. + ble_l2cap_ch_setup_params_t reply_param; + uint16_t local_cid; + + memset(&reply_param, 0, sizeof(ble_l2cap_ch_setup_params_t)); + + reply_param.le_psm = p_evt->evt.l2cap_evt.params.ch_setup_request.le_psm; + reply_param.rx_params.rx_mtu = BLE_IPSP_MTU; + reply_param.rx_params.rx_mps = BLE_IPSP_RX_MPS; + + // Check if a channel already exists with the peer. + retval = channel_search(p_evt->evt.l2cap_evt.conn_handle, + IPSP_ANY_CID, + &ch_id); + + BLE_IPSP_TRC("Exiting channel_search result 0x%08X", ch_id); + + if (retval == NRF_SUCCESS) + { + BLE_IPSP_TRC("Rejecting channel, as IPSP channel already exists " + "0x%08X in state 0x%08X", ch_id, m_channel[ch_id].state); + + reply_param.status = BLE_L2CAP_CH_STATUS_CODE_NO_RESOURCES; + + // Reinitialize ch_id to invalid so that existing channel is not impacted. + ch_id = INVALID_CHANNEL_INSTANCE; + } + else + { + uint32_t peer_device_index = connected_device_search(p_evt->evt.l2cap_evt.conn_handle); + local_cid = p_evt->evt.l2cap_evt.local_cid; + + if (p_evt->evt.l2cap_evt.params.ch_setup_request.le_psm != BLE_IPSP_PSM) + { + reply_param.status = BLE_L2CAP_CH_STATUS_CODE_LE_PSM_NOT_SUPPORTED; + BLE_IPSP_TRC("Rejecting L2CAP Channel, unknown PSM %04X!", + p_evt->evt.l2cap_evt.params.ch_setup_request.le_psm); + } + else if ((peer_device_index != IPSP_MAX_CONNECTED_DEVICES) && + (m_connected_device[peer_device_index].response == INCOMING_CHANNEL_REJECT)) + { + reply_param.status = BLE_L2CAP_CH_STATUS_CODE_NO_RESOURCES; + BLE_IPSP_ERR("Barred incoming requests by the application. " + "Rejecting L2CAP Channel %04X!", + p_evt->evt.l2cap_evt.params.ch_setup_request.le_psm); + } + else if (p_evt->evt.l2cap_evt.params.ch_setup_request.tx_params.tx_mtu < BLE_IPSP_MTU) + { + reply_param.status = BLE_L2CAP_CH_STATUS_CODE_UNACCEPTABLE_PARAMS; + BLE_IPSP_TRC("Rejecting L2CAP Channel, unacceptable TX parameters!"); + } + else + { + // Peer request acceptable, look for a free channel. + retval = channel_search(BLE_CONN_HANDLE_INVALID, BLE_L2CAP_CID_INVALID, &ch_id); + BLE_IPSP_TRC("Free channel search result 0x%08X", ch_id); + + if (retval != NRF_SUCCESS) + { + BLE_IPSP_TRC("Rejecting L2CAP Channel, no resources!"); + reply_param.status = BLE_L2CAP_CH_STATUS_CODE_NO_RESOURCES; + } + else + { + BLE_IPSP_TRC("Accepting L2CAP Channel"); + reply_param.rx_params.sdu_buf.p_data = NULL; + reply_param.rx_params.sdu_buf.len = 0; + reply_param.status = BLE_L2CAP_CH_STATUS_CODE_SUCCESS; + } + } + } + + retval = sd_ble_l2cap_ch_setup(p_evt->evt.l2cap_evt.conn_handle, + &local_cid, + &reply_param); + + BLE_IPSP_TRC("sd_ble_l2cap_ch_setup result = 0x%08lX", retval); + + if ((retval == NRF_SUCCESS) && + (reply_param.status == BLE_L2CAP_CH_STATUS_CODE_SUCCESS) && + (ch_id != INVALID_CHANNEL_INSTANCE)) + { + BLE_IPSP_TRC("[0x%04X][0x%04X]: Channel Connected. Rx MPS = 0x%04X", + p_evt->evt.l2cap_evt.conn_handle, + p_evt->evt.l2cap_evt.local_cid, + reply_param.rx_params.rx_mps); + + ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_CONNECTED; + ipsp_event.evt_result = NRF_SUCCESS; + + // Channel is assigned to this link. + m_channel[ch_id].state = CHANNEL_CONNECTING; + m_channel[ch_id].conn_handle = p_evt->evt.l2cap_evt.conn_handle; + m_channel[ch_id].cid = local_cid; + } + else if (ch_id != INVALID_CHANNEL_INSTANCE) + { + // Free the allocated channel. + channel_init(ch_id); + } + break; + } + case BLE_L2CAP_EVT_CH_SETUP: + { + // This event is generated for both initiator and acceptor roles. + // This event indicates that the IPSP channel is successfully established. + retval = channel_search(p_evt->evt.l2cap_evt.conn_handle, + p_evt->evt.l2cap_evt.local_cid, + &ch_id); + + if (retval != NRF_SUCCESS) + { + BLE_IPSP_TRC("Reply on unknown channel, dropping the event."); + } + else + { + if (m_channel[ch_id].state == CHANNEL_CONNECTING) + { + // Channel created successfully. + + // Initialize IPSP handle. + handle.conn_handle = p_evt->evt.l2cap_evt.conn_handle; + handle.cid = p_evt->evt.l2cap_evt.local_cid; + + // Initialize the event. + ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_CONNECTED; + ipsp_event.evt_result = NRF_SUCCESS; + + // Set the channel state appropriately. + m_channel[ch_id].state = CHANNEL_CONNECTED; + + // Set the flag to trigger submission of the receive buffers to the softdevice. + submit_rx_buffer = true; + + // Notify the event to the application. + notify_event = true; + } + } + break; + } + case BLE_L2CAP_EVT_CH_SETUP_REFUSED: + { + // This event is generated for both initiator and acceptor roles. + // This event indicates that the IPSP channel establishment failed. + retval = channel_search(p_evt->evt.l2cap_evt.conn_handle, + p_evt->evt.l2cap_evt.local_cid, + &ch_id); + + if (retval != NRF_SUCCESS) + { + BLE_IPSP_TRC("Reply on unknown channel, dropping the event."); + } + else + { + if (m_channel[ch_id].state == CHANNEL_CONNECTING) + { + // Channel creation failed as peer rejected the connection. + + // Initialize the event. + ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_CONNECTED; + ipsp_event.evt_result = NRF_ERROR_BLE_IPSP_PEER_REJECTED; + + BLE_IPSP_ERR("Peer rejected channel creation request, reason %d", + p_evt->evt.l2cap_evt.params.ch_setup_refused.status); + + // Free the channel. + channel_free(ch_id); + + // Notify the event to the application. + notify_event = true; + } + } + break; + } + case BLE_L2CAP_EVT_CH_RELEASED: + { + BLE_IPSP_TRC("L2CAP Channel disconnected."); + + ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_DISCONNECTED; + + retval = channel_search(p_evt->evt.l2cap_evt.conn_handle, + p_evt->evt.l2cap_evt.local_cid, + &ch_id); + + // Notify application of disconnection. + if (retval == NRF_SUCCESS) + { + handle.conn_handle = p_evt->evt.l2cap_evt.conn_handle; + handle.cid = p_evt->evt.l2cap_evt.local_cid; + + channel_free(ch_id); + + // Notify the event to the application. + notify_event = true; + } + break; + } + case BLE_L2CAP_EVT_CH_RX: + { + ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_DATA_RX; + + retval = channel_search(p_evt->evt.l2cap_evt.conn_handle, + p_evt->evt.l2cap_evt.local_cid, + &ch_id); + + if (retval == NRF_SUCCESS) + { + handle.conn_handle = p_evt->evt.l2cap_evt.conn_handle; + handle.cid = p_evt->evt.l2cap_evt.local_cid; + + rx_buffer_mark_unused(ch_id, p_evt->evt.l2cap_evt.params.rx.sdu_buf.p_data); + + // Set the flag to trigger submission of the receive buffers to the softdevice. + submit_rx_buffer = true; + + // Notify the event to the application. + notify_event = true; + } + break; + } + case BLE_L2CAP_EVT_CH_TX: + { + BLE_IPSP_TRC("BLE_L2CAP_EVT_CH_TX --> p_sdu_buf = %p, p_sdu_buf.p_data = %p", + &p_evt->evt.l2cap_evt.params.tx.sdu_buf, p_evt->evt.l2cap_evt.params.tx.sdu_buf.p_data); + + retval = channel_search(p_evt->evt.l2cap_evt.conn_handle, + p_evt->evt.l2cap_evt.local_cid, + &ch_id); + + if ((ch_id != INVALID_CHANNEL_INSTANCE) && + p_evt->evt.l2cap_evt.local_cid == m_channel[ch_id].cid) + { + // Initialize the event. + ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_DATA_TX_COMPLETE; + + // Initialize the handle. + handle.conn_handle = m_channel[ch_id].conn_handle; + handle.cid = m_channel[ch_id].cid; + + // Notify the event to the application. + notify_event = true; + } + break; + } + case BLE_L2CAP_EVT_CH_SDU_BUF_RELEASED: + { + BLE_IPSP_TRC("BLE_L2CAP_EVT_CH_SDU_BUF_RELEASED --> p_sdu_buf = %p, p_sdu_buf.p_data = %p", + &p_evt->evt.l2cap_evt.params.ch_sdu_buf_released.sdu_buf, + p_evt->evt.l2cap_evt.params.ch_sdu_buf_released.sdu_buf.p_data); + + retval = channel_search(p_evt->evt.l2cap_evt.conn_handle, + p_evt->evt.l2cap_evt.local_cid, + &ch_id); + + if ((ch_id != INVALID_CHANNEL_INSTANCE) && + (p_evt->evt.l2cap_evt.local_cid == m_channel[ch_id].cid) && + (is_tx_buffer(ch_id, p_evt->evt.l2cap_evt.params.ch_sdu_buf_released.sdu_buf.p_data))) + { + // Initialize the event. + ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_DATA_TX_COMPLETE; + ipsp_event.evt_result = NRF_ERROR_BLE_IPSP_LINK_DISCONNECTED; + + // Initialize the handle. + handle.conn_handle = m_channel[ch_id].conn_handle; + handle.cid = m_channel[ch_id].cid; + + // Notify the event to the application. + notify_event = true; + } + break; + } + case BLE_GAP_EVT_DISCONNECTED: + { + uint32_t peer_device_index = connected_device_search(handle.conn_handle); + if (peer_device_index < IPSP_MAX_CONNECTED_DEVICES) + { + connected_device_init(peer_device_index); + } + break; + } + default: + break; + } + + if (notify_event) + { + ble_ipsp_event_param_t event_param; + uint32_t peer_device_index; + + peer_device_index = connected_device_search(handle.conn_handle); + + if (peer_device_index < IPSP_MAX_CONNECTED_DEVICES) + { + event_param.p_peer = &m_connected_device[peer_device_index].ble_addr; + BLE_IPSP_TRC("Found peer device. Address type = 0x%02x", + event_param.p_peer->addr_type); + BLE_IPSP_DUMP((uint8_t *)event_param.p_peer->addr, 6); + } + else + { + event_param.p_peer = NULL; + } + + event_param.p_l2cap_evt = &p_evt->evt.l2cap_evt; + ipsp_event.p_evt_param = &event_param; + + app_notify(&handle, &ipsp_event); + } + + // Trigger submission of the receive buffers to the softdevice. + if (submit_rx_buffer) + { + rx_buffers_submit(ch_id); + } + + BLE_IPSP_MUTEX_UNLOCK(); + UNUSED_VARIABLE(retval); +} + + +uint32_t ble_ipsp_init(const ble_ipsp_init_t * p_init) +{ + BLE_IPSP_ENTRY(); + + ble_uuid_t ble_uuid; + uint32_t err_code; + uint16_t handle; + + NULL_PARAM_CHECK(p_init); + NULL_PARAM_CHECK(p_init->evt_handler); + + SDK_MUTEX_INIT(m_ipsp_mutex); + + BLE_IPSP_MUTEX_LOCK(); + + // Add service to indicate IPSP support. + BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_IPSP_SERVICE); + + err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &handle); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + m_evt_handler = p_init->evt_handler; + + // Initialize the channel. + for (int i = 0; i < BLE_IPSP_MAX_CHANNELS; i++) + { + channel_init(i); + } + + // Initialize the connected peer device table. + for (int i = 0; i < IPSP_MAX_CONNECTED_DEVICES; i++) + { + connected_device_init(i); + } + BLE_IPSP_MUTEX_UNLOCK(); + + BLE_IPSP_EXIT(); + + return NRF_SUCCESS; +} + + +uint32_t ble_ipsp_connect(const ble_ipsp_handle_t * p_handle) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + VERIFY_CON_HANDLE(p_handle->conn_handle); + + uint32_t err_code; + uint8_t ch_id = INVALID_CHANNEL_INSTANCE; + + BLE_IPSP_TRC("[Conn Handle 0x%04X]: >> ble_ipsp_connect", + p_handle->conn_handle); + + BLE_IPSP_MUTEX_LOCK(); + + // Check if channel already exists with the peer. + err_code = channel_search(p_handle->conn_handle, IPSP_ANY_CID, &ch_id); + + if (err_code == NRF_SUCCESS) + { + // IPSP channel already exists. + err_code = NRF_ERROR_BLE_IPSP_CHANNEL_ALREADY_EXISTS; + } + else + { + // Search for a free channel. + err_code = channel_search(BLE_CONN_HANDLE_INVALID, BLE_L2CAP_CID_INVALID, &ch_id); + BLE_IPSP_TRC("2 channel_search result %08X", err_code); + + if (err_code == NRF_SUCCESS) + { + m_channel[ch_id].state = CHANNEL_CONNECTING; + + ble_l2cap_ch_setup_params_t param = + { + .le_psm = BLE_IPSP_PSM, + .rx_params = { + .rx_mtu = BLE_IPSP_MTU, + .rx_mps = BLE_IPSP_RX_MPS, + .sdu_buf = + { + .p_data = NULL, + .len = 0 + } + } + }; + BLE_IPSP_TRC("Requesting sd_ble_l2cap_ch_setup"); + + err_code = sd_ble_l2cap_ch_setup(p_handle->conn_handle, + &m_channel[ch_id].cid, + ¶m); + + if (err_code != NRF_SUCCESS) + { + BLE_IPSP_ERR("sd_ble_l2cap_ch_conn_request failed, reason %08lX", err_code); + channel_free(ch_id); + } + else + { + BLE_IPSP_TRC("Local channel id from SD %04X.", m_channel[ch_id].cid); + m_channel[ch_id].conn_handle = p_handle->conn_handle; + } + } + else + { + err_code = (NRF_ERROR_BLE_IPSP_ERR_BASE + NRF_ERROR_NO_MEM); + } + } + + BLE_IPSP_MUTEX_UNLOCK(); + + BLE_IPSP_EXIT_WITH_RESULT(err_code); + + return err_code; +} + + +uint32_t ble_ipsp_send(ble_ipsp_handle_t const * p_handle, + uint8_t const * p_data, + uint16_t data_len) +{ + BLE_IPSP_ENTRY(); + + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_data); + VERIFY_CON_HANDLE(p_handle->conn_handle); + + uint32_t err_code; + uint8_t ch_id; + + BLE_IPSP_MUTEX_LOCK(); + + err_code = channel_search(p_handle->conn_handle, p_handle->cid, &ch_id); + + if (err_code == NRF_SUCCESS) + { + const ble_data_t p_sdu_buf = + { + .p_data = (uint8_t *)p_data, + .len = data_len + }; + + BLE_IPSP_TRC("p_sdu_buf = %p, p_sdu_buf.p_data = %p", + &p_sdu_buf, p_data); + + err_code = sd_ble_l2cap_ch_tx(p_handle->conn_handle, + p_handle->cid, + &p_sdu_buf); + } + + BLE_IPSP_MUTEX_UNLOCK(); + + BLE_IPSP_EXIT_WITH_RESULT(err_code); + + return err_code; +} + + +uint32_t ble_ipsp_disconnect(ble_ipsp_handle_t const * p_handle) +{ + BLE_IPSP_ENTRY(); + + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + VERIFY_CON_HANDLE(p_handle->conn_handle); + + uint32_t err_code; + uint8_t ch_id; + + BLE_IPSP_MUTEX_LOCK(); + + err_code = channel_search(p_handle->conn_handle, p_handle->cid, &ch_id); + + if (err_code == NRF_SUCCESS) + { + m_channel[ch_id].state = CHANNEL_DISCONNECTING; + + err_code = sd_ble_l2cap_ch_release(p_handle->conn_handle, + p_handle->cid); + } + + BLE_IPSP_MUTEX_UNLOCK(); + + BLE_IPSP_EXIT_WITH_RESULT(err_code); + + return err_code; +} + + +void ble_ipsp_incoming_channel_reject(uint16_t conn_handle) +{ + uint32_t peer_device_index = connected_device_search(conn_handle); + + if (peer_device_index != IPSP_MAX_CONNECTED_DEVICES) + { + m_connected_device[peer_device_index].response = INCOMING_CHANNEL_REJECT; + } +} + + +void ble_ipsp_incoming_channel_accept(uint16_t conn_handle) +{ + uint32_t peer_device_index = connected_device_search(conn_handle); + + if (peer_device_index != IPSP_MAX_CONNECTED_DEVICES) + { + m_connected_device[peer_device_index].response = INCOMING_CHANNEL_ACCEPT; + } +} |