aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.c')
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.c1978
1 files changed, 1978 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.c
new file mode 100644
index 0000000..b7bf6fa
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.c
@@ -0,0 +1,1978 @@
+/**
+ * Copyright (c) 2013 - 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.
+ *
+ */
+/** @file
+ *
+ * @defgroup ble_sdk_6lowpan 6LoWPAN Adaptation Layer
+ * @{
+ * @ingroup ble_sdk_iot
+ * @brief 6LoWPAN Adaptation Layer
+ *
+ * @details This module enables 6LoWPAN over Bluetooth Low Energy.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include "nrf_soc.h"
+#include "nordic_common.h"
+#include "ble_ipsp.h"
+#include "ble_6lowpan.h"
+#include "iot_common.h"
+#include "iot_context_manager.h"
+#include "app_util_platform.h"
+#include "mem_manager.h"
+
+/**
+ * @defgroup ble_sdk_6lowpan 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_6LOWPAN_CONFIG_LOG_ENABLED.
+ * @note If NRF_LOG_ENABLED is disabled, having IOT_BLE_6LOWPAN_CONFIG_LOG_ENABLED
+ * has no effect.
+ * @{
+ */
+
+#if IOT_BLE_6LOWPAN_CONFIG_LOG_ENABLED
+
+#define NRF_LOG_MODULE_NAME 6lowpan
+
+#define NRF_LOG_LEVEL IOT_BLE_6LOWPAN_CONFIG_LOG_LEVEL
+#define NRF_LOG_INFO_COLOR IOT_BLE_6LOWPAN_CONFIG_INFO_COLOR
+#define NRF_LOG_DEBUG_COLOR IOT_BLE_6LOWPAN_CONFIG_DEBUG_COLOR
+
+#include "nrf_log.h"
+NRF_LOG_MODULE_REGISTER();
+
+#define BLE_6LOWPAN_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */
+#define BLE_6LOWPAN_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */
+#define BLE_6LOWPAN_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */
+
+#define BLE_6LOWPAN_ENTRY() BLE_6LOWPAN_TRC(">> %s", __func__)
+#define BLE_6LOWPAN_EXIT() BLE_6LOWPAN_TRC("<< %s", __func__)
+
+#else // IOT_BLE_6LOWPAN_CONFIG_LOG_ENABLED
+
+#define BLE_6LOWPAN_TRC(...) /**< Disables traces. */
+#define BLE_6LOWPAN_DUMP(...) /**< Disables dumping of octet streams. */
+#define BLE_6LOWPAN_ERR(...) /**< Disables error logs. */
+
+#define BLE_6LOWPAN_ENTRY(...)
+#define BLE_6LOWPAN_EXIT(...)
+
+#endif // IOT_BLE_6LOWPAN_CONFIG_LOG_ENABLED
+
+
+
+/** @} */
+
+/**
+ * @defgroup ble_6lowpan_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 need arises to use an alternative architecture.
+ * @{
+ */
+#define BLE_6LOWPAN_MUTEX_LOCK() SDK_MUTEX_LOCK(m_6lowpan_mutex) /**< Lock module using mutex */
+#define BLE_6LOWPAN_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_6lowpan_mutex) /**< Unlock module using mutex */
+/** @} */
+
+/**
+ * @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_6LOWPAN_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks.
+ *
+ * @{
+ */
+
+#if (BLE_6LOWPAN_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_event_handler == NULL) \
+ { \
+ return (SDK_ERR_MODULE_NOT_INITIALIZED | BLE_6LOWPAN_ERR_BASE); \
+ }
+
+/**@brief Verify NULL parameters are not passed to API by application. */
+#define NULL_PARAM_CHECK(PARAM) \
+ if ((PARAM) == NULL) \
+ { \
+ return (NRF_ERROR_NULL | BLE_6LOWPAN_ERR_BASE); \
+ }
+
+/**@brief Check if packet has at least IP Header in it (40 bytes). */
+#define PACKET_LENGTH_CHECK(PARAM) \
+ if ((PARAM) < IPV6_IP_HEADER_SIZE) \
+ { \
+ return (NRF_ERROR_INVALID_PARAM | BLE_6LOWPAN_ERR_BASE); \
+ }
+
+#else // BLE_6LOWPAN_DISABLE_API_PARAM_CHECK
+
+#define VERIFY_MODULE_IS_INITIALIZED()
+#define NULL_PARAM_CHECK(PARAM)
+#define PACKET_LENGTH_CHECK(PARAM)
+
+#endif // BLE_6LOWPAN_DISABLE_API_PARAM_CHECK
+
+/** @} */
+
+/**@brief Maximum different between compressed and uncompressed packet. */
+#define IPHC_MAX_COMPRESSED_DIFF (IPV6_IP_HEADER_SIZE + UDP_HEADER_SIZE - 7)
+
+/**@brief Transmit FIFO mask. */
+#define TX_FIFO_MASK (BLE_6LOWPAN_TX_FIFO_SIZE - 1)
+
+/**@brief Value and position of IPHC dispatch. */
+#define IPHC_START_DISPATCH 0x03
+#define IPHC_START_DISPATCH_POS 5
+
+/**@brief Values and positions of IPHC fields. */
+#define IPHC_TF_MASK 0x18
+#define IPHC_TF_POS 3
+#define IPHC_NH_MASK 0x04
+#define IPHC_NH_POS 2
+#define IPHC_HLIM_MASK 0x03
+#define IPHC_HLIM_POS 0
+#define IPHC_CID_MASK 0x80
+#define IPHC_CID_POS 7
+#define IPHC_SAC_MASK 0x40
+#define IPHC_SAC_POS 6
+#define IPHC_SAM_MASK 0x30
+#define IPHC_SAM_POS 4
+#define IPHC_M_MASK 0x08
+#define IPHC_M_POS 3
+#define IPHC_DAC_MASK 0x04
+#define IPHC_DAC_POS 2
+#define IPHC_DAM_MASK 0x03
+#define IPHC_DAM_POS 0
+
+/**@brief IPHC Traffic Flow compression. */
+#define IPHC_TF_DSCP_MASK 0x3F
+#define IPHC_TF_ECN_MASK 0xC0
+#define IPHC_TF_ECN_POS 6
+
+/**@brief IPHC values of fields. */
+#define IPHC_TF_00 0x00
+#define IPHC_TF_01 0x01
+#define IPHC_TF_10 0x02
+#define IPHC_TF_11 0x03
+#define IPHC_NH_0 0x00
+#define IPHC_NH_1 0x01
+#define IPHC_HLIM_00 0x00
+#define IPHC_HLIM_01 0x01
+#define IPHC_HLIM_10 0x02
+#define IPHC_HLIM_11 0x03
+#define IPHC_CID_0 0x00
+#define IPHC_CID_1 0x01
+#define IPHC_SAC_0 0x00
+#define IPHC_SAC_1 0x01
+#define IPHC_SAM_00 0x00
+#define IPHC_SAM_01 0x01
+#define IPHC_SAM_10 0x02
+#define IPHC_SAM_11 0x03
+#define IPHC_M_0 0x00
+#define IPHC_M_1 0x01
+#define IPHC_DAC_0 0x00
+#define IPHC_DAC_1 0x01
+#define IPHC_DAM_00 0x00
+#define IPHC_DAM_01 0x01
+#define IPHC_DAM_10 0x02
+#define IPHC_DAM_11 0x03
+
+/**@brief IPHC Context Identifier compression. */
+#define IPHC_CID_SOURCE_MASK 0xF0
+#define IPHC_CID_SOURCE_POS 4
+#define IPHC_CID_DESTINATION_MASK 0x0F
+#define IPHC_CID_DESTINATION_POS 0
+
+/**@brief IPHC Next Header Compression dispatches. */
+#define IPHC_NHC_UDP_DISPATCH 0xF0
+#define IPHC_NHC_UDP_MASK 0xF8
+#define IPHC_NHC_EXT_DISPATCH 0xE0
+#define IPHC_NHC_EXT_MASK 0xF0
+
+/**@brief IPHC Next Header Compression UDP fields. */
+#define IPHC_NHC_UDP_CSUM_MASK 0x04
+#define IPHC_NHC_UDP_CSUM_POS 0x02
+#define IPHC_NHC_UDP_PORTS_MASK 0x03
+#define IPHC_NHC_UDP_PORTS_POS 0x00
+#define IPHC_NHC_UDP_PORTS_00 0x00
+#define IPHC_NHC_UDP_PORTS_01 0x01
+#define IPHC_NHC_UDP_PORTS_10 0x02
+#define IPHC_NHC_UDP_PORTS_11 0x03
+
+#define IPHC_NHC_UDP_COMPRESSION_MAX_MASK 0xFFF0
+#define IPHC_NHC_UDP_COMPRESSION_MAX 0xF0B0
+#define IPHC_NHC_UDP_COMPRESSION_MIN_MASK 0xFF00
+#define IPHC_NHC_UDP_COMPRESSION_MIN 0xF000
+
+/**@brief IPHC Next Header Compression Extended Header fields. */
+#define IPHC_NHC_EXT_EID_MASK 0x0E
+#define IPHC_NHC_EXT_EID_POS 0x01
+#define IPHC_NHC_EXT_EID_HOP_BY_HOP 0x00
+#define IPHC_NHC_EXT_EID_ROUTING 0x01
+#define IPHC_NHC_EXT_EID_FRAGMENT 0x02
+#define IPHC_NHC_EXT_EID_DESTINATION 0x03
+#define IPHC_NHC_EXT_EID_MOBILITY 0x04
+#define IPHC_NHC_EXT_EID_IPV6 0x07
+
+/**@brief IPHC default value of IPv6 Header fields. */
+#define IPHC_IPHEADER_VER_TC 0x60
+#define IPHC_IPHEADER_TC_FL 0x00
+#define IPHC_IPHEADER_FL 0x00
+
+/**@brief Check if address can be fully elidable. */
+#define IPV6_ADDRESS_IS_FULLY_ELIDABLE(ll_addr, addr) \
+ (((addr)->u8[8] == (((ll_addr[0]) ^ IPV6_IID_FLIP_VALUE))) && \
+ ((addr)->u8[9] == ll_addr[1]) && \
+ ((addr)->u8[10] == ll_addr[2]) && \
+ ((addr)->u8[11] == ll_addr[3]) && \
+ ((addr)->u8[12] == ll_addr[4]) && \
+ ((addr)->u8[11] == 0xff) && \
+ ((addr)->u8[12] == 0xfe) && \
+ ((addr)->u8[13] == ll_addr[5]) && \
+ ((addr)->u8[14] == ll_addr[6]) && \
+ ((addr)->u8[15] == ll_addr[7]) \
+ )
+
+/**@brief Check if address is 16-bit and can be compressed.
+ * 16-bit COMPRESSABLE format: ::0000:00ff:fe00:XXXX.
+ */
+#define IPV6_ADDRESS_IS_16_BIT_COMPRESSABLE(addr) \
+ (((addr)->u8[8] == 0) && \
+ ((addr)->u8[9] == 0) && \
+ ((addr)->u8[10] == 0) && \
+ ((addr)->u8[11] == 0xff) && \
+ ((addr)->u8[12] == 0xfe) && \
+ ((addr)->u8[13] == 0) \
+ )
+
+/**@brief Check if address is 48-bit multi-cast and can be compressed.
+ * 48-bit COMPRESSABLE format: FFXX::00XX:XXXX:XXXX.
+ */
+#define IPV6_ADDRESS_IS_48_BIT_MCAST_COMPRESSABLE(addr) \
+ (((addr)->u16[1] == 0) && \
+ ((addr)->u16[2] == 0) && \
+ ((addr)->u16[3] == 0) && \
+ ((addr)->u16[4] == 0) && \
+ ((addr)->u8[10] == 0) \
+ )
+
+/**@brief Check if address is 32-bit multi-cast and can be compressed.
+ * 32-bit COMPRESSABLE format: FFXX::00XX:XXXX.
+ */
+#define IPV6_ADDRESS_IS_32_BIT_MCAST_COMPRESSABLE(addr) \
+ (((addr)->u16[1] == 0) && \
+ ((addr)->u32[1] == 0) && \
+ ((addr)->u32[2] == 0) && \
+ ((addr)->u8[12] == 0) \
+ )
+
+/**@brief Check if address is 8-bit multi-cast and can be compressed.
+ * 8-bit COMPRESSABLE format: FF02::XX.
+ */
+#define IPV6_ADDRESS_IS_8_BIT_MCAST_COMPRESSABLE(addr) \
+ (((addr)->u8[1] == 2) && \
+ ((addr)->u16[1] == 0) && \
+ ((addr)->u32[1] == 0) && \
+ ((addr)->u32[2] == 0) && \
+ ((addr)->u16[6] == 0) && \
+ ((addr)->u8[14] == 0) \
+ )
+
+/******************************************************************************
+ * 6LoWPAN Core and Transport structures.
+ ******************************************************************************/
+
+/**@brief Element of TX Queue. */
+typedef struct
+{
+ uint8_t * p_mem_block; /**< Base address of memory block, using for release the buffer. */
+ uint8_t * p_data; /**< Pointer to TX Data. */
+ uint16_t data_len; /**< Size of TX data. */
+} tx_packet_t;
+
+/**@brief A TX Queue (FIFO) structure. */
+typedef struct
+{
+ tx_packet_t packets[BLE_6LOWPAN_TX_FIFO_SIZE]; /**< Array of TX packet in FIFO. */
+ volatile uint32_t read_pos; /**< Next read position in the FIFO buffer. */
+ volatile uint32_t write_pos; /**< Next write position in the FIFO buffer. */
+} tx_fifo_t;
+
+/**@brief Transport instance structure. */
+typedef struct
+{
+ iot_interface_t interface;
+ ble_ipsp_handle_t handle;
+ tx_fifo_t tx_fifo;
+ tx_packet_t * p_tx_cur_packet;
+} transport_instance_t;
+
+/******************************************************************************
+ * @name Global variables
+ *****************************************************************************/
+
+/**@brief Application Event Handler. */
+static ble_6lowpan_evt_handler_t m_event_handler = NULL;
+
+/**@brief Hop Limit options. */
+static const uint8_t m_hop_limit[] = {0, 1, 64, 255};
+
+/**@brief Link-local prefix. */
+static const uint8_t m_link_local_prefix[] = {0xFE, 0x80};
+
+/**@brief Additional extenders for EUI-48. */
+static const uint8_t m_link_local_16_middle[] = {0xFF, 0xFE};
+
+/**@brief nRF EUI-64 link-layer address */
+static eui64_t m_ll_addr = {{0, 0, 0, 0, 0, 0, 0, 0}};
+
+/**@brief Transport interfaces table. */
+static transport_instance_t m_instances[BLE_6LOWPAN_MAX_INTERFACE];
+
+/**@brief Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */
+SDK_MUTEX_DEFINE(m_6lowpan_mutex)
+
+/******************************************************************************
+ * @name 6LoWPAN core functions
+ *****************************************************************************/
+
+/**@brief Function for checking if IID can be completely elided. This situation
+ * may happen when receiver can still reconstruct IPv6 address by using context
+ * prefix and Link Layer address.
+ *
+ * @param[in] p_addr Pointer to IPv6 address.
+ * @param[in] p_context Pointer to context entry that is compressed with.
+ * @param[in] p_ll_addr Pointer to link layer address of BT-LE device.
+ *
+ * @return True if IID can be elided, False otherwise.
+ */
+static bool is_context_cover_iid(const ipv6_addr_t * p_addr,
+ const iot_context_t * p_context,
+ const eui64_t * p_ll_addr)
+{
+ uint32_t start_byte, offset;
+
+ // Context covers IPv6 address by its size.
+ if (p_context->prefix_len == 128)
+ {
+ return true;
+ }
+
+ // Check if IID can be retrieved in case of longer prefix than 64 bits.
+ if (p_context->prefix_len > 64)
+ {
+ // Check only IID fields that are not covered by context prefix.
+ start_byte = p_context->prefix_len >> 3;
+ offset = p_context->prefix_len % 8;
+
+ // Check all bytes from the second one.
+ if (start_byte == 15 ||
+ 0 == memcmp(&p_addr->u8[start_byte+1], &p_ll_addr->identifier[start_byte-7], 15-start_byte))
+ {
+ // Then check first byte.
+ return (p_addr->u8[start_byte] << offset) == (p_ll_addr->identifier[start_byte-8] << offset);
+ }
+ }
+
+ return false;
+}
+
+/**@brief Function for decoding Next Header Compression.
+ * It supports UDP header decompression.
+ *
+ * @param[in] p_iphc Pointer to currently process IPHC field.
+ * @param[in] p_data Pointer to constructing uncompressed IP packet.
+ * @param[in] p_length Place of UDP header in case of Extension Header.
+ * @param[out] p_length Length of the constructed uncompressed header.
+ *
+ * @return Number of processed IPHC field.
+ */
+static uint32_t iphc_nhc_decode(uint8_t * p_iphc, uint8_t * p_data, uint16_t * p_length)
+{
+ uint8_t nhc_dispatch = *p_iphc;
+ uint8_t * p_nhc = p_iphc;
+
+ ipv6_header_t * iphdr = (ipv6_header_t *)&p_data[0];
+
+ // UDP Next Header Compression.
+ if ((nhc_dispatch & IPHC_NHC_UDP_MASK) == IPHC_NHC_UDP_DISPATCH)
+ {
+ udp6_header_t * udphdr = (udp6_header_t * )&p_data[IPV6_IP_HEADER_SIZE + *p_length];
+
+ iphdr->next_header = IPV6_NEXT_HEADER_UDP;
+
+ // Start length from UDP Header Size.
+ *p_length += UDP_HEADER_SIZE;
+ p_nhc += 1;
+
+ switch ((nhc_dispatch & IPHC_NHC_UDP_PORTS_MASK) >> IPHC_NHC_UDP_PORTS_POS)
+ {
+ case IPHC_NHC_UDP_PORTS_00:
+ memcpy(&udphdr->srcport, p_nhc, 2);
+ memcpy(&udphdr->destport, p_nhc + 2, 2);
+ p_nhc += 4;
+ break;
+
+ case IPHC_NHC_UDP_PORTS_01:
+ memcpy(&udphdr->srcport, p_nhc, 2);
+ udphdr->destport = HTONS(IPHC_NHC_UDP_COMPRESSION_MIN | *(p_nhc + 2));
+ p_nhc += 3;
+ break;
+
+ case IPHC_NHC_UDP_PORTS_10:
+ udphdr->srcport = HTONS(IPHC_NHC_UDP_COMPRESSION_MIN | *p_nhc);
+ memcpy(&udphdr->destport, p_nhc + 1, 2);
+ p_nhc += 3;
+ break;
+
+ case IPHC_NHC_UDP_PORTS_11:
+ udphdr->srcport = HTONS((IPHC_NHC_UDP_COMPRESSION_MAX | ((*p_nhc & 0xf0) >> 4)));
+ udphdr->destport = HTONS((IPHC_NHC_UDP_COMPRESSION_MAX | ((*p_nhc & 0x0f))));
+ p_nhc += 1;
+ break;
+ }
+
+ if ((nhc_dispatch & IPHC_NHC_UDP_CSUM_MASK) >> IPHC_NHC_UDP_CSUM_POS)
+ {
+ udphdr->checksum = 0;
+ }
+ else
+ {
+ memcpy(&udphdr->checksum, p_nhc, 2);
+ p_nhc += 2;
+ }
+ }
+
+ return (p_nhc - p_iphc);
+}
+
+/**@brief Function for encoding Next Header Compression.
+ * It supports UDP header compression.
+ *
+ * @param[in] p_iphc Pointer to currently process IPHC field.
+ * @param[in] p_data Pointer to constructing uncompressed IP packet.
+ * @param[in] p_length Place of UDP header in case of Extension Header.
+ * @param[out] p_length Length of the constructed uncompressed header.
+ *
+ * @return Number of processed IPHC field.
+ */
+static uint32_t iphc_nhc_encode(uint8_t * p_iphc, const uint8_t * p_data, uint16_t * p_length)
+{
+ uint8_t * p_nhc = p_iphc;
+ ipv6_header_t * iphdr = (ipv6_header_t *)p_data;
+
+ switch (iphdr->next_header)
+ {
+ case IPV6_NEXT_HEADER_UDP:
+ {
+ udp6_header_t * udphdr = (udp6_header_t * )&p_data[IPV6_IP_HEADER_SIZE + *p_length];
+ *p_iphc = IPHC_NHC_UDP_DISPATCH;
+ p_nhc += 1;
+
+ // Full 4-bit compression for source and destination ports.
+ if ( ((HTONS(udphdr->srcport) & IPHC_NHC_UDP_COMPRESSION_MAX_MASK) ==
+ IPHC_NHC_UDP_COMPRESSION_MAX) &&
+ ((HTONS(udphdr->destport) & IPHC_NHC_UDP_COMPRESSION_MAX_MASK) ==
+ IPHC_NHC_UDP_COMPRESSION_MAX))
+ {
+ *p_iphc |= (IPHC_NHC_UDP_PORTS_11 >> IPHC_NHC_UDP_PORTS_POS);
+
+ *p_nhc =
+ (((HTONS(udphdr->srcport) & 0x0f) << 4) | (HTONS(udphdr->destport) & 0x0f));
+ p_nhc += 1;
+ }
+ // Source port compressed, destination in-line.
+ else if ((HTONS(udphdr->srcport) & IPHC_NHC_UDP_COMPRESSION_MIN_MASK) ==
+ IPHC_NHC_UDP_COMPRESSION_MIN)
+ {
+ *p_iphc |= (IPHC_NHC_UDP_PORTS_10 >> IPHC_NHC_UDP_PORTS_POS);
+
+ *p_nhc = (HTONS(udphdr->srcport) & 0xff);
+ p_nhc += 1;
+
+ memcpy(p_nhc, &udphdr->destport, 2);
+ p_nhc += 2;
+ }
+ // Source port in-line, destination compressed.
+ else if ((HTONS(udphdr->destport) & IPHC_NHC_UDP_COMPRESSION_MIN_MASK) ==
+ IPHC_NHC_UDP_COMPRESSION_MIN)
+ {
+ *p_iphc |= (IPHC_NHC_UDP_PORTS_01 >> IPHC_NHC_UDP_PORTS_POS);
+
+ memcpy(p_nhc, &udphdr->srcport, 2);
+ p_nhc += 2;
+
+ *p_nhc = (HTONS(udphdr->destport) & 0xff);
+ p_nhc += 1;
+ }
+ // Source and destination port in-line.
+ else
+ {
+ *p_iphc |= (IPHC_NHC_UDP_PORTS_00 >> IPHC_NHC_UDP_PORTS_POS);
+
+ memcpy(p_nhc, &udphdr->srcport, 2);
+ memcpy(p_nhc + 2, &udphdr->destport, 2);
+ p_nhc += 4;
+ }
+
+ // Checksum always in-line, [RFC4944] disallows the compression of the
+ // UDP checksum. The compressor MUST NOT set the C bit unless it has received
+ // authorization.
+ memcpy(p_nhc, &udphdr->checksum, 2);
+ p_nhc += 2;
+
+ // Set UDP ext header size.
+ *p_length = UDP_HEADER_SIZE;
+
+ break;
+ }
+ }
+
+ return (p_nhc - p_iphc);
+}
+
+
+ /**@brief Function for checking if it's possible to use NHC.
+ *
+ * @param[in] next_header Value of Next Header field in IPv6 packet.
+ *
+ * @return Returns 1 if header can be compressed, otherwise 0.
+ */
+static uint32_t iphc_nhc_compressable(uint8_t next_header)
+{
+ switch (next_header)
+ {
+ case IPV6_NEXT_HEADER_UDP:
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**@brief Function for decoding IPHC (IP Header Compression) defined in
+ * IETF RFC 6282.
+ *
+ * @param[in] p_instance Transport instance from where packet came.
+ * @param[in] p_input Pointer to received packet from IPSP module.
+ * @param[in] input_len Length of received packet.
+ * @param[in] p_output Pointer to allocated buffer for decompressed packet.
+ * @param[out] p_output Pointer to decompressed IPv6 packet.
+ * @param[out] p_output_len Length of decompressed IPv6 packet.
+ *
+ * @return NRF_SUCCESS on success, otherwise an error code.
+ */
+static uint32_t iphc_decode(iot_interface_t * p_interface,
+ uint8_t * p_output,
+ uint16_t * p_output_len,
+ uint8_t * p_input,
+ uint16_t input_len,
+ iot_context_id_t * p_rx_contexts)
+{
+ uint32_t retval = NRF_SUCCESS;
+ uint32_t err_code = NRF_SUCCESS;
+ uint8_t * p_iphc = p_input;
+ uint8_t sci = IPV6_CONTEXT_IDENTIFIER_NONE;
+ uint8_t dci = IPV6_CONTEXT_IDENTIFIER_NONE;
+ uint16_t nhc_length = 0;
+ iot_context_t * p_ctx = NULL;
+
+ // IPv6 headers used in decompression.
+ ipv6_header_t * p_iphdr = (ipv6_header_t *)p_output;
+ udp6_header_t * p_udphdr = (udp6_header_t *)&p_output[IPV6_IP_HEADER_SIZE];
+
+ // Check if format of packet is correct.
+ if ((p_input[0] >> IPHC_START_DISPATCH_POS) != IPHC_START_DISPATCH)
+ {
+ BLE_6LOWPAN_ERR("[6LoWPAN] Packet has incorrect IPHC structure!");
+
+ return NRF_ERROR_INVALID_DATA;
+ }
+
+ // IPHC basic form has 2 bytes.
+ p_iphc += 2;
+
+ // RFC6282: An additional 8-bit Context Identifier Extension field
+ // immediately follows the Destination Address Mode (DAM) field.
+ if ((p_input[1] & IPHC_CID_MASK))
+ {
+ sci = ((*p_iphc & IPHC_CID_SOURCE_MASK) >> IPHC_CID_SOURCE_POS);
+ dci = ((*p_iphc & IPHC_CID_DESTINATION_MASK) >> IPHC_CID_DESTINATION_POS);
+ p_iphc += 1;
+ }
+
+ // Set proper context identifiers.
+ p_rx_contexts->src_cntxt_id = sci;
+ p_rx_contexts->dest_cntxt_id = dci;
+
+ switch ((p_input[0] & IPHC_TF_MASK) >> IPHC_TF_POS)
+ {
+ case IPHC_TF_11:
+ // Elide Traffic Class and Flow Label.
+ p_iphdr->version_traffic_class = IPHC_IPHEADER_VER_TC;
+ p_iphdr->traffic_class_flowlabel = IPHC_IPHEADER_TC_FL;
+ p_iphdr->flowlabel = IPHC_IPHEADER_FL;
+ break;
+
+ case IPHC_TF_10:
+ // Elide Flow Label.
+ p_iphdr->version_traffic_class = IPHC_IPHEADER_VER_TC | ((*p_iphc & IPHC_TF_DSCP_MASK) >> 2);
+ p_iphdr->traffic_class_flowlabel = ((*p_iphc & 0x03) << 6) |
+ ((*p_iphc & IPHC_TF_ECN_MASK) >> 2);
+ p_iphdr->flowlabel = IPHC_IPHEADER_FL;
+ p_iphc += 1;
+ break;
+
+ case IPHC_TF_01:
+ // Elide DSCP, carry ECN and Flow Label.
+ p_iphdr->version_traffic_class = IPHC_IPHEADER_VER_TC;
+ p_iphdr->traffic_class_flowlabel = ((*p_iphc & IPHC_TF_ECN_MASK) >> 2) |
+ ((*p_iphc & 0x0f));
+ memcpy(&p_iphdr->flowlabel, p_iphc + 1, 2);
+ p_iphc += 3;
+ break;
+
+ case IPHC_TF_00:
+ // Flow Label and Traffic Class in-line.
+ p_iphdr->version_traffic_class = IPHC_IPHEADER_VER_TC | ((*p_iphc & IPHC_TF_DSCP_MASK) >> 2);
+ p_iphdr->traffic_class_flowlabel = ((*p_iphc & 0x03) << 6) |
+ ((*p_iphc & IPHC_TF_ECN_MASK) >> 2) |
+ ((*(p_iphc + 1) & 0x0f));
+ memcpy(&p_iphdr->flowlabel, p_iphc + 2, 2);
+ p_iphc += 4;
+ break;
+ }
+
+ if (!((p_input[0] & IPHC_NH_MASK)))
+ {
+ // Set next header.
+ p_iphdr->next_header = *p_iphc++;
+ }
+
+ if ((p_input[0] & IPHC_HLIM_MASK))
+ {
+ p_iphdr->hoplimit = m_hop_limit[((p_input[0] & IPHC_HLIM_MASK) >> IPHC_HLIM_POS)];
+ }
+ else
+ {
+ p_iphdr->hoplimit = *p_iphc++;
+ }
+
+ // Clear IPv6 addresses.
+ memset(p_iphdr->srcaddr.u8, 0, IPV6_ADDR_SIZE);
+ memset(p_iphdr->destaddr.u8, 0, IPV6_ADDR_SIZE);
+
+ // Source address decompression.
+ if ((p_input[1] & IPHC_SAC_MASK) >> IPHC_SAC_POS)
+ {
+ if ( ((p_input[1] & IPHC_SAM_MASK) >> IPHC_SAM_POS) == IPHC_SAM_00 )
+ {
+ // Unspecified address.
+ memset(p_iphdr->srcaddr.u8, 0, IPV6_ADDR_SIZE);
+ }
+ else
+ {
+ switch ((p_input[1] & IPHC_SAM_MASK) >> IPHC_SAM_POS)
+ {
+ case IPHC_SAM_01:
+ // 64 bits in-line, first IID then prefix in case prefix > 64.
+ memcpy(p_iphdr->srcaddr.u8 + 8, p_iphc, 8);
+ p_iphc += 8;
+ break;
+
+ case IPHC_SAM_10:
+ // 16 bits in-line.
+ memcpy(p_iphdr->srcaddr.u8 + 14, p_iphc, 2);
+ memcpy(p_iphdr->srcaddr.u8 + 11, m_link_local_16_middle, 2);
+ p_iphc += 2;
+ break;
+
+ case IPHC_SAM_11:
+ // Take the address from lower layer.
+ memcpy(p_iphdr->srcaddr.u8 + 8, p_interface->peer_addr.identifier, 8);
+ p_iphdr->srcaddr.u8[8] ^= IPV6_IID_FLIP_VALUE;
+ break;
+ }
+
+ /* Look up for context */
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+ err_code = iot_context_manager_get_by_cid(p_interface, sci, &p_ctx);
+ BLE_6LOWPAN_MUTEX_LOCK();
+
+ if (err_code == NRF_SUCCESS)
+ {
+ /* Add prefix */
+ IPV6_ADDRESS_PREFIX_SET(p_iphdr->srcaddr.u8, p_ctx->prefix.u8, p_ctx->prefix_len);
+ }
+ else
+ {
+ /* Set error and continue decompression. */
+ retval = BLE_6LOWPAN_CID_NOT_FOUND;
+
+ BLE_6LOWPAN_ERR("Cannot find context identifier in the context table.");
+ }
+ }
+ }
+ else
+ {
+ switch ((p_input[1] & IPHC_SAM_MASK) >> IPHC_SAM_POS)
+ {
+ case IPHC_SAM_00:
+ // Full 128-bits in-line.
+ memcpy(p_iphdr->srcaddr.u8, p_iphc, IPV6_ADDR_SIZE);
+ p_iphc += IPV6_ADDR_SIZE;
+ break;
+
+ case IPHC_SAM_01:
+ // 64 bits in-line, first IID then prefix in case prefix > 64.
+ memcpy(p_iphdr->srcaddr.u8, m_link_local_prefix, 2);
+ memcpy(p_iphdr->srcaddr.u8 + 8, p_iphc, 8);
+ p_iphc += 8;
+ break;
+
+ case IPHC_SAM_10:
+ // 16 bits in-line.
+ memcpy(p_iphdr->srcaddr.u8, m_link_local_prefix, 2);
+ memcpy(p_iphdr->srcaddr.u8 + 11, m_link_local_16_middle, 2);
+ memcpy(p_iphdr->srcaddr.u8 + 14, p_iphc, 2);
+ p_iphc += 2;
+ break;
+
+ case IPHC_SAM_11:
+ memcpy(p_iphdr->srcaddr.u8, m_link_local_prefix, 2);
+ memcpy(p_iphdr->srcaddr.u8 + 8, p_interface->peer_addr.identifier, 8);
+ p_iphdr->srcaddr.u8[8] ^= IPV6_IID_FLIP_VALUE;
+ break;
+ }
+ }
+
+ // Destination address decompression.
+ if ((p_input[1] & IPHC_DAC_MASK) >> IPHC_DAC_POS)
+ {
+ if ((p_input[1] & IPHC_M_MASK) >> IPHC_M_POS)
+ {
+ switch ((p_input[1] & IPHC_DAM_MASK) >> IPHC_DAM_POS)
+ {
+ case IPHC_DAM_00:
+ // 48-bits in-line.
+ p_iphdr->destaddr.u8[0] = 0xff;
+ memcpy(p_iphdr->destaddr.u8 + 1, p_iphc, 2);
+ memcpy(p_iphdr->destaddr.u8 + 12, p_iphc + 2, 4);
+ p_iphc += 6;
+ break;
+
+ default:
+ BLE_6LOWPAN_ERR("Reserved value in IPHC header!");
+ return NRF_ERROR_INVALID_DATA;
+ }
+ }
+ else
+ {
+ switch ((p_input[1] & IPHC_DAM_MASK) >> IPHC_DAM_POS)
+ {
+ case IPHC_DAM_01:
+ // 64 bits in-line.
+ memcpy(p_iphdr->destaddr.u8 + 8, p_iphc, 8);
+ p_iphc += 8;
+ break;
+
+ case IPHC_DAM_10:
+ // 16 bits in-line.
+ memcpy(p_iphdr->destaddr.u8 + 11, m_link_local_16_middle, 2);
+ memcpy(p_iphdr->destaddr.u8 + 14, p_iphc, 2);
+ p_iphc += 2;
+ break;
+
+ case IPHC_DAM_11:
+ // Take the address from lower layer.
+ memcpy(p_iphdr->destaddr.u8 + 8, p_interface->local_addr.identifier, 8);
+ p_iphdr->destaddr.u8[8] ^= IPV6_IID_FLIP_VALUE;
+ break;
+
+ default:
+ BLE_6LOWPAN_ERR("Reserved value in IPHC header!");
+ return NRF_ERROR_INVALID_DATA;
+ }
+
+ /* Look up for context */
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+ err_code = iot_context_manager_get_by_cid(p_interface, dci, &p_ctx);
+ BLE_6LOWPAN_MUTEX_LOCK();
+
+ if (err_code == NRF_SUCCESS)
+ {
+ /* Add prefix */
+ IPV6_ADDRESS_PREFIX_SET(p_iphdr->destaddr.u8, p_ctx->prefix.u8, p_ctx->prefix_len);
+ }
+ else
+ {
+ /* Set error and continue decompression. */
+ retval = BLE_6LOWPAN_CID_NOT_FOUND;
+
+ BLE_6LOWPAN_ERR("Cannot find context identifier in the context table.");
+ }
+ }
+ }
+ else
+ {
+ if ((p_input[1] & IPHC_M_MASK) >> IPHC_M_POS)
+ {
+ switch ((p_input[1] & IPHC_DAM_MASK) >> IPHC_DAM_POS)
+ {
+ case IPHC_DAM_00:
+ // 128 bits in-line.
+ memcpy(p_iphdr->destaddr.u8, p_iphc, IPV6_ADDR_SIZE);
+ p_iphc += IPV6_ADDR_SIZE;
+ break;
+
+ case IPHC_DAM_01:
+ // 48 bits in-line.
+ p_iphdr->destaddr.u8[0] = 0xFF;
+ p_iphdr->destaddr.u8[1] = *p_iphc;
+ memcpy(p_iphdr->destaddr.u8 + 11, p_iphc + 1, 5);
+ p_iphc += 6;
+ break;
+
+ case IPHC_DAM_10:
+ // 32 bits in-line.
+ p_iphdr->destaddr.u8[0] = 0xFF;
+ p_iphdr->destaddr.u8[1] = *p_iphc;
+ memcpy(p_iphdr->destaddr.u8 + 13, p_iphc + 1, 3);
+ p_iphc += 4;
+ break;
+
+ case IPHC_DAM_11:
+ // 8 bits in-line.
+ p_iphdr->destaddr.u8[0] = 0xFF;
+ p_iphdr->destaddr.u8[1] = 0x02;
+ p_iphdr->destaddr.u8[15] = *p_iphc;
+ p_iphc += 1;
+ break;
+ }
+ }
+ else
+ {
+ switch ((p_input[1] & IPHC_DAM_MASK) >> IPHC_DAM_POS)
+ {
+ case IPHC_DAM_00:
+ // 128 bits in-line.
+ memcpy(p_iphdr->destaddr.u8, p_iphc, IPV6_ADDR_SIZE);
+ p_iphc += IPV6_ADDR_SIZE;
+ break;
+
+ case IPHC_DAM_01:
+ // 64 bits in-line, first IID then prefix in case prefix > 64.
+ memcpy(p_iphdr->destaddr.u8, m_link_local_prefix, 2);
+ memcpy(p_iphdr->destaddr.u8 + 8, p_iphc, 8);
+ p_iphc += 8;
+ break;
+
+ case IPHC_DAM_10:
+ // 16 bits in-line.
+ memcpy(p_iphdr->destaddr.u8, m_link_local_prefix, 2);
+ memcpy(p_iphdr->destaddr.u8 + 11, m_link_local_16_middle, 2);
+ memcpy(p_iphdr->destaddr.u8 + 14, p_iphc, 2);
+ p_iphc += 2;
+ break;
+
+ case IPHC_DAM_11:
+ // Take the address from lower layer.
+ memcpy(p_iphdr->destaddr.u8, m_link_local_prefix, 2);
+ memcpy(p_iphdr->destaddr.u8 + 8, p_interface->local_addr.identifier, 8);
+ p_iphdr->destaddr.u8[8] ^= IPV6_IID_FLIP_VALUE;
+ break;
+ }
+ }
+ }
+
+ // Perform next header compression.
+ if (((p_input[0] & IPHC_NH_MASK) >> IPHC_NH_POS))
+ {
+ p_iphc += iphc_nhc_decode(p_iphc, p_output, &nhc_length);
+
+ if (nhc_length == 0)
+ {
+ // Unknown NHC field.
+ BLE_6LOWPAN_ERR("IPHC contains unsupported NHC header!");
+
+ return NRF_ERROR_INVALID_DATA;
+ }
+ }
+
+ // Calculate IPv6 Header Length.
+ p_iphdr->length = input_len - (p_iphc - p_input);
+
+ // Check if IPHC contains more bytes than whole packet.
+ if (p_iphdr->length > input_len)
+ {
+ // We cannot decompress it.
+ BLE_6LOWPAN_ERR("IPHC contains more bytes than expected!");
+
+ return NRF_ERROR_INVALID_DATA;
+ }
+
+ // Copy the data.
+ memcpy(p_output + IPV6_IP_HEADER_SIZE + nhc_length, p_iphc, p_iphdr->length);
+
+ // Add uncompressed headers length if any.
+ p_iphdr->length += nhc_length;
+
+ // Return length of whole IPv6 packet.
+ *p_output_len = p_iphdr->length + IPV6_IP_HEADER_SIZE;
+
+ // Keep proper endianness.
+ p_iphdr->length = HTONS(p_iphdr->length);
+
+ // Restore UDP length if compression was used.
+ if ( p_iphdr->next_header == IPV6_NEXT_HEADER_UDP )
+ {
+ memcpy(&p_udphdr->length, &p_iphdr->length, 2);
+ }
+
+ return retval;
+}
+
+
+/**@brief Function for encoding IPHC (IP Header Compression) defined in
+ * IETF RFC 6282. Instead of having separate buffer for compression,
+ * needed compression is performed on the IPv6 packet and buffer holding
+ * the packet is reused to overwrite the headers compressed.
+ *
+ * @param[in] p_interface IoT interface where packet must be sent.
+ * @param[in] p_input Pointer to full IPv6 packet.
+ * @param[in] input_len Length of IPv6 packet.
+ * @param[out] p_output Pointer to place of start IPHC packet.
+ * @param[out] p_output_len Length of compressed packet.
+ *
+ * @return NRF_SUCCESS on success, otherwise an error code.
+ */
+static uint32_t iphc_encode(const iot_interface_t * p_interface,
+ uint8_t ** p_output,
+ uint16_t * p_output_len,
+ const uint8_t * p_input,
+ uint16_t input_len)
+{
+ // Create a buffer with maximum of IPHC value.
+ uint32_t err_code;
+ uint8_t iphc_buff[IPV6_IP_HEADER_SIZE + UDP_HEADER_SIZE];
+ uint8_t traffic_class;
+ uint8_t * p_iphc = &iphc_buff[2];
+ uint16_t iphc_len = 0;
+ uint16_t nhc_length = 0;
+ iot_context_t * p_ctx = NULL;
+ uint8_t sci = p_interface->tx_contexts.src_cntxt_id;
+ uint8_t dci = p_interface->tx_contexts.dest_cntxt_id;
+ bool sci_cover = false;
+ bool dci_cover = false;
+
+ // IPv6 header.
+ ipv6_header_t * p_iphdr = (ipv6_header_t *)p_input;
+
+ *p_iphc = 0;
+
+ // Set IPHC dispatch.
+ iphc_buff[0] = IPHC_START_DISPATCH << IPHC_START_DISPATCH_POS;
+
+ // Check if address can be compressed using context identifier.
+ if (sci == IPV6_CONTEXT_IDENTIFIER_NONE)
+ {
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+ err_code = iot_context_manager_get_by_addr(p_interface, &p_iphdr->srcaddr, &p_ctx);
+ BLE_6LOWPAN_MUTEX_LOCK();
+
+ if (err_code == NRF_SUCCESS)
+ {
+ sci_cover = is_context_cover_iid(&p_iphdr->srcaddr, p_ctx, &p_interface->local_addr);
+ sci = p_ctx->context_id;
+ }
+ }
+
+ if (dci == IPV6_CONTEXT_IDENTIFIER_NONE)
+ {
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+ err_code = iot_context_manager_get_by_addr(p_interface, &p_iphdr->destaddr, &p_ctx);
+ BLE_6LOWPAN_MUTEX_LOCK();
+
+ if (err_code == NRF_SUCCESS)
+ {
+ dci_cover = is_context_cover_iid(&p_iphdr->destaddr, p_ctx, &p_interface->peer_addr);
+ dci = p_ctx->context_id;
+ }
+ }
+
+ if (((sci != IPV6_CONTEXT_IDENTIFIER_NONE) || dci != IPV6_CONTEXT_IDENTIFIER_NONE))
+ {
+ iphc_buff[1] = (IPHC_CID_1 << IPHC_CID_POS);
+
+ // Add Source Context if exists.
+ if (sci != IPV6_CONTEXT_IDENTIFIER_NONE)
+ {
+ *p_iphc |= (sci << 4);
+ }
+
+ // Add Destination Context if exists.
+ if (dci != IPV6_CONTEXT_IDENTIFIER_NONE)
+ {
+ *p_iphc |= dci;
+ }
+
+ p_iphc += 1;
+ }
+ else
+ {
+ // Unset Context Identifier bit.
+ iphc_buff[1] = (IPHC_CID_0 << IPHC_CID_POS);
+ }
+
+ // Change ECN with DSCP in Traffic Class.
+ traffic_class = (p_iphdr->version_traffic_class & 0x0f) << 4;
+ traffic_class |= ((p_iphdr->traffic_class_flowlabel & 0xf0) >> 4);
+ traffic_class = (((traffic_class & 0x03) << 6) | (traffic_class >> 2));
+
+ if ((p_iphdr->flowlabel == 0) && ((p_iphdr->traffic_class_flowlabel & 0x0f) == 0))
+ {
+ if (traffic_class == 0)
+ {
+ // Elide Flow Label and Traffic Class.
+ iphc_buff[0] |= (IPHC_TF_11 << IPHC_TF_POS);
+ }
+ else
+ {
+ // Elide Flow Label and carry Traffic Class in-line.
+ iphc_buff[0] |= (IPHC_TF_10 << IPHC_TF_POS);
+
+ *p_iphc++ = traffic_class;
+ }
+ }
+ else
+ {
+ if (traffic_class & IPHC_TF_DSCP_MASK)
+ {
+ // Carry Flow Label and Traffic Class in-line.
+ iphc_buff[0] |= (IPHC_TF_00 << IPHC_TF_POS);
+
+ *p_iphc++ = traffic_class;
+ *p_iphc++ = (p_iphdr->traffic_class_flowlabel & 0x0f);
+ memcpy(p_iphc, &p_iphdr->flowlabel, 2);
+ p_iphc += 2;
+ }
+ else
+ {
+ // Carry Flow Label and ECN only with 2-bit padding.
+ iphc_buff[0] |= (IPHC_TF_01 << IPHC_TF_POS);
+
+ *p_iphc++ =
+ ((traffic_class & IPHC_TF_ECN_MASK) | (p_iphdr->traffic_class_flowlabel & 0x0f));
+ memcpy(p_iphc, &p_iphdr->flowlabel, 2);
+ p_iphc += 2;
+ }
+ }
+
+ // Checks if next header is compressed.
+ if (iphc_nhc_compressable(p_iphdr->next_header))
+ {
+ iphc_buff[0] |= (IPHC_NH_1 << IPHC_NH_POS);
+ }
+ else
+ {
+ iphc_buff[0] |= (IPHC_NH_0 << IPHC_NH_POS);
+ *p_iphc++ = p_iphdr->next_header;
+ }
+
+ // Hop limit compression.
+ switch (p_iphdr->hoplimit)
+ {
+ case 1:
+ iphc_buff[0] |= (IPHC_HLIM_01 << IPHC_HLIM_POS);
+ break;
+
+ case 64:
+ iphc_buff[0] |= (IPHC_HLIM_10 << IPHC_HLIM_POS);
+ break;
+
+ case 255:
+ iphc_buff[0] |= (IPHC_HLIM_11 << IPHC_HLIM_POS);
+ break;
+
+ default:
+ // Carry Hop Limit in-line.
+ iphc_buff[0] |= (IPHC_HLIM_00 << IPHC_HLIM_POS);
+ *p_iphc++ = p_iphdr->hoplimit;
+ break;
+ }
+
+ // Source address compression.
+ if (IPV6_ADDRESS_IS_UNSPECIFIED(&p_iphdr->srcaddr))
+ {
+ iphc_buff[1] |= (IPHC_SAC_1 << IPHC_SAC_POS);
+ iphc_buff[1] |= (IPHC_SAM_00 << IPHC_SAM_POS);
+ }
+ else if (sci != IPV6_CONTEXT_IDENTIFIER_NONE || IPV6_ADDRESS_IS_LINK_LOCAL(&p_iphdr->srcaddr))
+ {
+ if (sci != IPV6_CONTEXT_IDENTIFIER_NONE)
+ {
+ // Set stateful source address compression.
+ iphc_buff[1] |= (IPHC_SAC_1 << IPHC_SAC_POS);
+ }
+
+ if (IPV6_ADDRESS_IS_FULLY_ELIDABLE(p_interface->local_addr.identifier,
+ &p_iphdr->srcaddr)
+ ||
+ sci_cover == true)
+ {
+ iphc_buff[1] |= (IPHC_SAM_11 << IPHC_SAM_POS);
+ }
+ else if (IPV6_ADDRESS_IS_16_BIT_COMPRESSABLE(&p_iphdr->srcaddr))
+ {
+ iphc_buff[1] |= (IPHC_SAM_10 << IPHC_SAM_POS);
+ memcpy(p_iphc, &p_iphdr->srcaddr.u8[14], 2);
+ p_iphc += 2;
+ }
+ else
+ {
+ iphc_buff[1] |= (IPHC_SAM_01 << IPHC_SAM_POS);
+ memcpy(p_iphc, &p_iphdr->srcaddr.u8[8], 8);
+ p_iphc += 8;
+ }
+ }
+ else
+ {
+ // Carry full source address in-line.
+ iphc_buff[1] |= (IPHC_SAC_0 << IPHC_SAC_POS);
+ iphc_buff[1] |= (IPHC_SAM_00 << IPHC_SAM_POS);
+ memcpy(p_iphc, p_iphdr->srcaddr.u8, IPV6_ADDR_SIZE);
+ p_iphc += IPV6_ADDR_SIZE;
+ }
+
+ // Destination compression.
+ if (IPV6_ADDRESS_IS_MULTICAST(&p_iphdr->destaddr))
+ {
+ iphc_buff[1] |= (IPHC_M_1 << IPHC_M_POS);
+
+ if (dci != IPV6_CONTEXT_IDENTIFIER_NONE)
+ {
+ iphc_buff[1] |= (IPHC_DAC_1 << IPHC_DAC_POS);
+ iphc_buff[1] |= (IPHC_DAM_00 << IPHC_DAM_POS);
+
+ p_iphdr->destaddr.u8[0] = 0xff;
+ memcpy(p_iphc, &p_iphdr->destaddr.u8[1], 2);
+ memcpy(p_iphc + 2, &p_iphdr->destaddr.u8[12], 4);
+ p_iphc += 6;
+ }
+ else if (IPV6_ADDRESS_IS_8_BIT_MCAST_COMPRESSABLE(&p_iphdr->destaddr))
+ {
+ iphc_buff[1] |= (IPHC_DAC_0 << IPHC_DAC_POS);
+ iphc_buff[1] |= (IPHC_DAM_11 << IPHC_DAM_POS);
+ *p_iphc++ = p_iphdr->destaddr.u8[15];
+
+ }
+ else if (IPV6_ADDRESS_IS_32_BIT_MCAST_COMPRESSABLE(&p_iphdr->destaddr))
+ {
+ iphc_buff[1] |= (IPHC_DAC_0 << IPHC_DAC_POS);
+ iphc_buff[1] |= (IPHC_DAM_10 << IPHC_DAM_POS);
+
+ *p_iphc = p_iphdr->destaddr.u8[1];
+ memcpy(p_iphc + 1, &p_iphdr->destaddr.u8[13], 3);
+ p_iphc += 4;
+ }
+ else if (IPV6_ADDRESS_IS_48_BIT_MCAST_COMPRESSABLE(&p_iphdr->destaddr))
+ {
+ iphc_buff[1] |= (IPHC_DAC_0 << IPHC_DAC_POS);
+ iphc_buff[1] |= (IPHC_DAM_01 << IPHC_DAM_POS);
+
+ *p_iphc = p_iphdr->destaddr.u8[1];
+ memcpy(p_iphc + 1, &p_iphdr->destaddr.u8[11], 5);
+ p_iphc += 6;
+ }
+ else
+ {
+ // Carry full destination multi-cast address in-line.
+ iphc_buff[1] |= (IPHC_DAC_0 << IPHC_DAC_POS);
+ iphc_buff[1] |= (IPHC_DAM_00 << IPHC_DAM_POS);
+ memcpy(p_iphc, p_iphdr->destaddr.u8, IPV6_ADDR_SIZE);
+ p_iphc += IPV6_ADDR_SIZE;
+ }
+ }
+ else
+ {
+ iphc_buff[1] |= (IPHC_M_0 << IPHC_M_POS);
+
+ if (dci != IPV6_CONTEXT_IDENTIFIER_NONE || IPV6_ADDRESS_IS_LINK_LOCAL(&p_iphdr->destaddr))
+ {
+ if (dci != IPV6_CONTEXT_IDENTIFIER_NONE)
+ {
+ iphc_buff[1] |= (IPHC_DAC_1 << IPHC_DAC_POS);
+ }
+
+ if (IPV6_ADDRESS_IS_FULLY_ELIDABLE(p_interface->peer_addr.identifier,
+ &p_iphdr->destaddr)
+ ||
+ dci_cover == true)
+ {
+ iphc_buff[1] |= (IPHC_DAM_11 << IPHC_DAM_POS);
+ }
+ else if (IPV6_ADDRESS_IS_16_BIT_COMPRESSABLE(&p_iphdr->destaddr))
+ {
+ iphc_buff[1] |= (IPHC_DAM_10 << IPHC_DAM_POS);
+ memcpy(p_iphc, &p_iphdr->destaddr.u8[14], 2);
+ p_iphc += 2;
+ }
+ else
+ {
+ iphc_buff[1] |= (IPHC_DAM_01 << IPHC_DAM_POS);
+ memcpy(p_iphc, &p_iphdr->destaddr.u8[8], 8);
+ p_iphc += 8;
+ }
+ }
+ else
+ {
+ // Carry full destination address in-line.
+ iphc_buff[1] |= (IPHC_DAC_0 << IPHC_DAC_POS);
+ iphc_buff[1] |= (IPHC_DAM_00 << IPHC_DAM_POS);
+ memcpy(p_iphc, p_iphdr->destaddr.u8, IPV6_ADDR_SIZE);
+ p_iphc += IPV6_ADDR_SIZE;
+ }
+ }
+
+ if ( iphc_buff[0] & IPHC_NH_MASK)
+ {
+ p_iphc += iphc_nhc_encode(p_iphc, p_input, &nhc_length);
+ }
+
+ // Calculate IPHC size.
+ iphc_len = (p_iphc - iphc_buff);
+
+ // Calculate IPv6 packet size.
+ *p_output_len = input_len - (IPV6_IP_HEADER_SIZE - iphc_len + nhc_length);
+
+ // Use p_data as final buffer (since IPHC+NHC <= IPv6 Header + NHC (UDP)).
+ memcpy((uint8_t *)&p_input[IPV6_IP_HEADER_SIZE + nhc_length - iphc_len], iphc_buff, iphc_len);
+
+ // Set correct address of output data.
+ *p_output = (uint8_t *) &p_input[IPV6_IP_HEADER_SIZE + nhc_length - iphc_len];
+
+ return NRF_SUCCESS;
+}
+
+
+/******************************************************************************
+ * @name 6LoWPAN transport functions
+ *****************************************************************************/
+
+/**@brief Function for notifying application of an error in an ongoing procedure.
+ *
+ * @param[in] p_interface Pointer to 6LoWPAN interface.
+ * @param[in] err_code Internally error code.
+ *
+ * @return None.
+ */
+static void app_notify_error(iot_interface_t * p_interface,
+ uint32_t err_code)
+{
+ ble_6lowpan_event_t event;
+
+ event.event_id = BLE_6LO_EVT_ERROR;
+ event.event_result = err_code;
+
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+
+ m_event_handler(p_interface, &event);
+
+ BLE_6LOWPAN_MUTEX_LOCK();
+}
+
+
+/**@brief Function for notifying application of the new packet received.
+ *
+ * @param[in] p_interface Pointer to iot interface.
+ * @param[in] p_packet Pointer to decompressed IPv6 packet.
+ * @param[in] packet_len Length of IPv6 packet.
+ * @param[in] result Processing result of packet. Could be NRF_SUCCESS, or
+ * NRF_ERROR_NOT_FOUND if context identifier is unknown.
+ * @param[in] p_rx_contexts Received contexts if any.
+ *
+ * @return None.
+ */
+static void app_notify_rx_data(iot_interface_t * p_interface,
+ uint8_t * p_packet,
+ uint16_t packet_len,
+ uint32_t result,
+ iot_context_id_t * p_rx_contexts)
+{
+ ble_6lowpan_event_t event;
+
+ event.event_id = BLE_6LO_EVT_INTERFACE_DATA_RX;
+ event.event_result = result;
+ event.event_param.rx_event_param.p_packet = p_packet;
+ event.event_param.rx_event_param.packet_len = packet_len;
+ event.event_param.rx_event_param.rx_contexts = *(p_rx_contexts);
+
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+
+ m_event_handler(p_interface, &event);
+
+ BLE_6LOWPAN_MUTEX_LOCK();
+}
+
+
+/**@brief Function for notifying application of the new interface established.
+ *
+ * @param[in] p_interface Pointer to new iot interface.
+ *
+ * @return None.
+ */
+static void app_notify_interface_add(iot_interface_t * p_interface)
+{
+ ble_6lowpan_event_t event;
+
+ event.event_id = BLE_6LO_EVT_INTERFACE_ADD;
+ event.event_result = NRF_SUCCESS;
+
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+
+ m_event_handler(p_interface, &event);
+
+ BLE_6LOWPAN_MUTEX_LOCK();
+}
+
+
+/**@brief Function for notifying application of the interface disconnection.
+ *
+ * @param[in] p_interface Pointer to removed iot interface.
+ *
+ * @return None.
+ */
+static void app_notify_interface_delete(iot_interface_t * p_interface)
+{
+ ble_6lowpan_event_t event;
+
+ event.event_id = BLE_6LO_EVT_INTERFACE_DELETE;
+ event.event_result = NRF_SUCCESS;
+
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+
+ m_event_handler(p_interface, &event);
+
+ BLE_6LOWPAN_MUTEX_LOCK();
+}
+
+
+/**@brief Function for initialize transmit FIFO.
+ *
+ * @param[in] p_fifo Pointer to transmit FIFO instance.
+ *
+ * @return None.
+ */
+static void tx_fifo_init(tx_fifo_t * p_fifo)
+{
+ memset(p_fifo->packets, 0, BLE_6LOWPAN_TX_FIFO_SIZE * sizeof (tx_packet_t));
+ p_fifo->read_pos = 0;
+ p_fifo->write_pos = 0;
+}
+
+
+/**@brief Function for putting new packet to transmit FIFO.
+ *
+ * @param[in] p_fifo Pointer to transmit FIFO instance.
+ * @param[in] p_packet Pointer to new packet.
+ *
+ * @return NRF_SUCCESS on success, otherwise NRF_ERROR_NO_MEM error.
+ */
+static uint32_t tx_fifo_put(tx_fifo_t * p_fifo, tx_packet_t * p_packet)
+{
+ uint32_t err_code = BLE_6LOWPAN_TX_FIFO_FULL;
+
+ // To prevent "The order of volatile accesses is undefined in this statement"
+ // in subsequent conditional statement.
+ uint32_t write_pos = p_fifo->write_pos;
+ uint32_t read_pos = p_fifo->read_pos;
+
+ if ((write_pos - read_pos) <= TX_FIFO_MASK)
+ {
+ p_fifo->packets[p_fifo->write_pos & TX_FIFO_MASK].p_data = p_packet->p_data;
+ p_fifo->packets[p_fifo->write_pos & TX_FIFO_MASK].data_len = p_packet->data_len;
+ p_fifo->packets[p_fifo->write_pos & TX_FIFO_MASK].p_mem_block = p_packet->p_mem_block;
+
+ p_fifo->write_pos++;
+
+ err_code = NRF_SUCCESS;
+ }
+
+ return err_code;
+}
+
+
+/**@brief Function for popping currently processed packet in transmit FIFO.
+ * It releases element on FIFO only when processing of the element is done.
+ *
+ * @param[in] p_fifo Pointer to transmit FIFO instance.
+ *
+ * @return None.
+ */
+static void tx_fifo_release(tx_fifo_t * p_fifo)
+{
+ p_fifo->read_pos++;
+}
+
+
+/**@brief Function for reading front element of transmit FIFO.
+ * After finish processing element in queue, it must be
+ * released using tx_fifo_release function.
+ *
+ * @param[in] p_fifo Pointer to transmit FIFO instance.
+ * @param[in] pp_packet Pointer to front packet.
+ *
+ * @return NRF_SUCCESS on success, otherwise NRF_ERROR_NO_MEM error.
+ */
+static uint32_t tx_fifo_get(tx_fifo_t * p_fifo, tx_packet_t * * pp_packet)
+{
+ uint32_t err_code = NRF_ERROR_NOT_FOUND;
+
+ // To prevent "The order of volatile accesses is undefined in this statement"
+ // in subsequent conditional statement.
+ uint32_t write_pos = p_fifo->write_pos;
+ uint32_t read_pos = p_fifo->read_pos;
+
+ if ((write_pos - read_pos) != 0)
+ {
+ *pp_packet = &p_fifo->packets[p_fifo->read_pos & TX_FIFO_MASK];
+ err_code = NRF_SUCCESS;
+ }
+
+ return err_code;
+}
+
+
+/**@brief Function for searching transport interface by given IPSP handle.
+ *
+ * @param[in] p_ipsp_handle Pointer to IPSP handle.
+ *
+ * @return Transport interface related with IPSP handle, or NULL if not found.
+ */
+static transport_instance_t * interface_get_by_handle(const ble_ipsp_handle_t * p_ipsp_handle)
+{
+ uint32_t index;
+
+ for (index = 0; index < BLE_6LOWPAN_MAX_INTERFACE; index++)
+ {
+ if (m_instances[index].handle.cid == p_ipsp_handle->cid &&
+ m_instances[index].handle.conn_handle == p_ipsp_handle->conn_handle)
+ {
+ return &m_instances[index];
+ }
+ }
+
+ return NULL;
+}
+
+
+/**@brief Function for initializing transport instance.
+ *
+ * @param[in] index Index of instance.
+ *
+ * @return None.
+ */
+static void instance_init(uint32_t index)
+{
+ memset(&m_instances[index], 0, sizeof (transport_instance_t));
+ m_instances[index].handle.cid = BLE_L2CAP_CID_INVALID;
+ m_instances[index].p_tx_cur_packet = NULL;
+ m_instances[index].interface.p_transport = (void *) index;
+}
+
+
+/**@brief Function for resetting specific 6lowpan interface.
+ *
+ * @param[in] p_interface Pointer to transport interface.
+ *
+ * @return None.
+ */
+static void interface_reset(transport_instance_t * p_instance)
+{
+ uint32_t index;
+ uint32_t instance_id = (uint32_t) p_instance->interface.p_transport;
+
+ // Free all queued packets.
+ for (index = 0; index < BLE_6LOWPAN_TX_FIFO_SIZE; index++)
+ {
+ if (m_instances[instance_id].tx_fifo.packets[index].p_mem_block != NULL)
+ {
+ nrf_free(m_instances[instance_id].tx_fifo.packets[index].p_mem_block);
+ }
+ }
+
+ instance_init (instance_id);
+}
+
+
+/**@brief Function for adding new transport instance.
+ *
+ * @param[in] p_peer_addr Pointer EUI-64 of peer address.
+ * @param[in] p_ipsp_handle Pointer IPSP handle, related with L2CAP CoC channel.
+ * @param[out] pp_instance Pointer to added transport instances, if operation succeeded.
+ *
+ * @return NRF_SUCCESS on success, otherwise NRF_ERROR_NO_MEM error.
+ */
+static uint32_t interface_add(const eui64_t * p_peer_addr,
+ const ble_ipsp_handle_t * p_ipsp_handle,
+ transport_instance_t ** pp_instance)
+{
+ uint32_t index;
+
+ for (index = 0; index < BLE_6LOWPAN_MAX_INTERFACE; index++)
+ {
+ if (m_instances[index].handle.cid == BLE_L2CAP_CID_INVALID)
+ {
+ m_instances[index].handle.cid = p_ipsp_handle->cid;
+ m_instances[index].handle.conn_handle = p_ipsp_handle->conn_handle;
+ m_instances[index].interface.tx_contexts.src_cntxt_id = IPV6_CONTEXT_IDENTIFIER_NONE;
+ m_instances[index].interface.tx_contexts.dest_cntxt_id = IPV6_CONTEXT_IDENTIFIER_NONE;
+
+ memcpy(&m_instances[index].interface.peer_addr, p_peer_addr, sizeof (eui64_t));
+ memcpy(&m_instances[index].interface.local_addr, &m_ll_addr, sizeof (eui64_t));
+
+ // Initialize TX FIFO.
+ tx_fifo_init(&m_instances[index].tx_fifo);
+
+ *pp_instance = &m_instances[index];
+
+ return NRF_SUCCESS;
+ }
+ }
+
+ return NRF_ERROR_NO_MEM;
+}
+
+
+/**@brief Function for checking if any transmission is currently processing on specific interface.
+ * Current version of L2CAP CoC in SoftDevice has limitation to send one packet at same
+ * time.
+ *
+ * @param[in] p_instance Pointer to transport instance.
+ *
+ * @return TRUE if interface not sending any packets, FALSE if busy.
+ */
+static bool tx_is_free(transport_instance_t * p_instance)
+{
+ return (NULL == p_instance->p_tx_cur_packet);
+}
+
+
+/**@brief Function uses for indicating transmission complete on specific interface.
+ *
+ * @param[in] p_instance Pointer to transport instance.
+ *
+ * @return None.
+ */
+static void tx_complete(transport_instance_t * p_instance)
+{
+ BLE_6LOWPAN_TRC("[CID 0x%04X]: Transmission complete.",
+ p_instance->handle.cid);
+
+ // Free the transmit buffer.
+ nrf_free(p_instance->p_tx_cur_packet->p_mem_block);
+ p_instance->p_tx_cur_packet = NULL;
+
+ // Release last processed packet.
+ tx_fifo_release(&p_instance->tx_fifo);
+}
+
+
+/**@brief Function for sending front packet from transmit FIFO on specific interface.
+ *
+ * @param[in] p_instance Pointer to transport instance.
+ *
+ * @return None.
+ */
+static void tx_send(transport_instance_t * p_instance)
+{
+ uint32_t err_code = NRF_SUCCESS;
+
+ if (NRF_SUCCESS == tx_fifo_get(&p_instance->tx_fifo,
+ &p_instance->p_tx_cur_packet))
+ {
+ err_code = ble_ipsp_send(&p_instance->handle,
+ p_instance->p_tx_cur_packet->p_data,
+ p_instance->p_tx_cur_packet->data_len);
+
+ if (NRF_SUCCESS != err_code)
+ {
+ BLE_6LOWPAN_TRC("Cannot send the packet, error = 0x%08lX", err_code);
+
+ app_notify_error(&p_instance->interface, err_code);
+
+ tx_complete(p_instance);
+
+ // Try to send another packet.
+ tx_send(p_instance);
+ }
+ }
+}
+
+/**@brief Callback registered with IPSP to receive asynchronous events from the module.
+ *
+ * @param[in] p_handle Pointer to IPSP handle.
+ * @param[in] p_evt Pointer to specific event, generated by IPSP module.
+ *
+ * @return NRF_SUCCESS on success, otherwise NRF_ERROR_NO_MEM error.
+ */
+static uint32_t ipsp_evt_handler(ble_ipsp_handle_t const * p_handle, ble_ipsp_evt_t const * p_evt)
+{
+ BLE_6LOWPAN_ENTRY();
+
+ VERIFY_MODULE_IS_INITIALIZED();
+
+ uint32_t mem_size;
+ uint16_t buff_len;
+ eui64_t peer_addr;
+ iot_context_id_t rx_contexts;
+ uint32_t retval = NRF_SUCCESS;
+ transport_instance_t * p_instance = NULL;
+ uint8_t * p_buff = NULL;
+
+ BLE_6LOWPAN_MUTEX_LOCK();
+
+ p_instance = interface_get_by_handle(p_handle);
+
+ switch (p_evt->evt_id)
+ {
+ case BLE_IPSP_EVT_CHANNEL_CONNECTED:
+ {
+ BLE_6LOWPAN_TRC("[CID 0x%04X]: >> BLE_IPSP_EVT_CHANNEL_CONNECTED",
+ p_handle->cid);
+ BLE_6LOWPAN_TRC("New channel established.");
+
+ if (p_instance == NULL)
+ {
+ IPV6_EUI64_CREATE_FROM_EUI48(peer_addr.identifier,
+ p_evt->p_evt_param->p_peer->addr,
+ p_evt->p_evt_param->p_peer->addr_type);
+
+ // Add interface to internal table.
+ retval = interface_add(&peer_addr, p_handle, &p_instance);
+
+ if (NRF_SUCCESS == retval)
+ {
+ BLE_6LOWPAN_TRC("Added new IPSP interface.");
+
+ // Notify application.
+ app_notify_interface_add(&p_instance->interface);
+ }
+ else
+ {
+ BLE_6LOWPAN_ERR("Cannot add new interface. Table is full.");
+ UNUSED_VARIABLE(ble_ipsp_disconnect(p_handle));
+ }
+ }
+ else
+ {
+ // Got a connection event, when already connected.
+ UNUSED_VARIABLE(ble_ipsp_disconnect(p_handle));
+ }
+
+ break;
+ }
+
+ case BLE_IPSP_EVT_CHANNEL_DISCONNECTED:
+ {
+ BLE_6LOWPAN_TRC("[CID 0x%04X]: >> BLE_IPSP_EVT_CHANNEL_DISCONNECTED",
+ p_handle->cid);
+ BLE_6LOWPAN_TRC("Channel disconnection.");
+
+ if (NULL != p_instance)
+ {
+ BLE_6LOWPAN_TRC("Removed IPSP interface.");
+
+ // Notify application.
+ app_notify_interface_delete(&p_instance->interface);
+
+ // Remove interface from internal table.
+ interface_reset(p_instance);
+ }
+
+ break;
+ }
+
+ case BLE_IPSP_EVT_CHANNEL_DATA_RX:
+ {
+
+ if (NULL != p_instance)
+ {
+ const uint16_t packet_len = MIN(p_evt->p_evt_param->p_l2cap_evt->params.rx.sdu_buf.len,
+ p_evt->p_evt_param->p_l2cap_evt->params.rx.sdu_len);
+
+ BLE_6LOWPAN_TRC("[CID 0x%04X]: >> BLE_IPSP_EVT_CHANNEL_DATA_RX",
+ p_handle->cid);
+
+ BLE_6LOWPAN_DUMP(p_evt->p_evt_param->p_l2cap_evt->params.rx.sdu_buf.p_data,
+ packet_len);
+
+ BLE_6LOWPAN_TRC("Processing received data.");
+
+ mem_size = packet_len + IPHC_MAX_COMPRESSED_DIFF;
+
+ // Try to allocate memory for incoming data.
+ retval = nrf_mem_reserve(&p_buff, &mem_size);
+
+ if (retval == NRF_SUCCESS)
+ {
+ // Decompress IPHC data into IPv6 packet.
+ retval = iphc_decode(&p_instance->interface,
+ p_buff,
+ &buff_len,
+ p_evt->p_evt_param->p_l2cap_evt->params.rx.sdu_buf.p_data,
+ packet_len,
+ &rx_contexts);
+
+ // Do not discard if packet decompressed successfully,
+ // otherwise error in Context based decompression.
+ if (retval == NRF_SUCCESS || retval == BLE_6LOWPAN_CID_NOT_FOUND)
+ {
+ if ((p_evt->evt_result == NRF_ERROR_BLE_IPSP_RX_PKT_TRUNCATED) &&
+ (retval == NRF_SUCCESS))
+ {
+ // Inform the application that the packet is truncated.
+ retval = NRF_ERROR_BLE_IPSP_RX_PKT_TRUNCATED;
+ }
+
+ BLE_6LOWPAN_TRC("Decompressed packet:");
+ BLE_6LOWPAN_DUMP(p_buff, buff_len);
+
+ // Notify application.
+ app_notify_rx_data(&p_instance->interface,
+ p_buff,
+ buff_len,
+ retval,
+ &rx_contexts);
+ }
+ else
+ {
+ BLE_6LOWPAN_ERR("Decompression failed!");
+
+ nrf_free(p_buff);
+ }
+ }
+ else
+ {
+ BLE_6LOWPAN_ERR(
+ "No buffer in memory pool available, packet dropped!");
+ }
+ }
+ else
+ {
+ BLE_6LOWPAN_ERR("Got data to unknown interface!");
+ }
+
+ break;
+ }
+
+ case BLE_IPSP_EVT_CHANNEL_DATA_TX_COMPLETE:
+ {
+ BLE_6LOWPAN_TRC("[CID 0x%04X]: >> BLE_IPSP_EVT_CHANNEL_DATA_TX_COMPLETE",
+ p_handle->cid);
+
+ // Free TX buffer.
+ tx_complete(p_instance);
+
+ // Try to send another packet.
+ tx_send(p_instance);
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+
+ BLE_6LOWPAN_EXIT();
+
+ return retval;
+}
+
+/******************************************************************************
+ * @name 6LoWPAN API functions
+ *****************************************************************************/
+
+uint32_t ble_6lowpan_init(const ble_6lowpan_init_t * p_init)
+{
+ BLE_6LOWPAN_ENTRY();
+
+ uint32_t index;
+ uint32_t retval = NRF_SUCCESS;
+ ble_ipsp_init_t ipsp_init_params;
+
+ NULL_PARAM_CHECK(p_init);
+ NULL_PARAM_CHECK(p_init->p_eui64);
+ NULL_PARAM_CHECK(p_init->event_handler);
+
+ // Check if the transmit FIFO size is a power of two.
+ if (!IS_POWER_OF_TWO(BLE_6LOWPAN_TX_FIFO_SIZE))
+ {
+ return NRF_ERROR_INVALID_LENGTH | BLE_6LOWPAN_ERR_BASE;
+ }
+
+ SDK_MUTEX_INIT(m_6lowpan_mutex);
+
+ BLE_6LOWPAN_MUTEX_LOCK();
+
+ // Store EUI64 in internal variable.
+ memcpy(m_ll_addr.identifier, p_init->p_eui64->identifier, EUI_64_ADDR_SIZE);
+
+ // Set application event handler.
+ m_event_handler = p_init->event_handler;
+
+ // Clear transport interfaces table.
+ for (index = 0; index < BLE_6LOWPAN_MAX_INTERFACE; index++)
+ {
+ instance_init(index);
+ }
+
+ // IPSP module initialization.
+ ipsp_init_params.evt_handler = ipsp_evt_handler;
+
+ // Initialize the IPSP module.
+ retval = ble_ipsp_init(&ipsp_init_params);
+
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+
+ BLE_6LOWPAN_EXIT();
+
+ return retval;
+}
+
+
+uint32_t ble_6lowpan_interface_disconnect(const iot_interface_t * p_interface)
+{
+ BLE_6LOWPAN_ENTRY();
+
+ VERIFY_MODULE_IS_INITIALIZED();
+
+ NULL_PARAM_CHECK(p_interface);
+
+ uint32_t retval;
+ transport_instance_t * p_instance = &m_instances[(uint32_t)p_interface->p_transport];
+
+ BLE_6LOWPAN_MUTEX_LOCK();
+
+ retval = ble_ipsp_disconnect(&p_instance->handle);
+
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+
+ BLE_6LOWPAN_EXIT();
+
+ return retval;
+}
+
+
+uint32_t ble_6lowpan_interface_send(const iot_interface_t * p_interface,
+ const uint8_t * p_packet,
+ uint16_t packet_len)
+{
+ BLE_6LOWPAN_ENTRY();
+
+ VERIFY_MODULE_IS_INITIALIZED();
+
+ NULL_PARAM_CHECK(p_packet);
+ NULL_PARAM_CHECK(p_interface);
+ PACKET_LENGTH_CHECK(packet_len);
+
+ uint32_t retval = NRF_SUCCESS;
+ uint8_t * p_output_buff = NULL;
+ uint16_t output_len;
+ tx_packet_t tx_packet;
+ transport_instance_t * p_instance = &m_instances[(uint32_t)p_interface->p_transport];
+
+ BLE_6LOWPAN_MUTEX_LOCK();
+
+ BLE_6LOWPAN_TRC("Uncompressed packet:");
+ BLE_6LOWPAN_DUMP((uint8_t *)p_packet, packet_len);
+
+ // Encode IP packet into IPHC.
+ retval = iphc_encode(p_interface,
+ &p_output_buff,
+ &output_len,
+ p_packet,
+ packet_len);
+
+ if (NRF_SUCCESS == retval)
+ {
+ BLE_6LOWPAN_TRC("Successfully compressed packet.");
+
+ tx_packet.p_data = p_output_buff;
+ tx_packet.data_len = output_len;
+ tx_packet.p_mem_block = (uint8_t *)p_packet;
+
+ retval = tx_fifo_put(&p_instance->tx_fifo, &tx_packet);
+
+ if (NRF_SUCCESS == retval)
+ {
+ BLE_6LOWPAN_TRC("Compressed packet:");
+ BLE_6LOWPAN_DUMP(p_output_buff, output_len);
+
+ // Send packet immediately if transport interface is not busy.
+ if (tx_is_free(p_instance))
+ {
+ tx_send(p_instance);
+ }
+ }
+ else
+ {
+ BLE_6LOWPAN_ERR("No place in TX queue!");
+ }
+ }
+ else
+ {
+ BLE_6LOWPAN_ERR("Error while compression!");
+ }
+
+ BLE_6LOWPAN_MUTEX_UNLOCK();
+
+ BLE_6LOWPAN_EXIT();
+
+ return retval;
+}