/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. * * The information contained herein is property of Nordic Semiconductor ASA. * Terms and conditions of usage are described in detail in NORDIC * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. * * Licensees are granted free, non-transferable use of the information. NO * WARRANTY of ANY KIND is provided. This heading must NOT be removed from * the file. * */ #include "hci_transport_config.h" #include "hci_transport.h" #include "hci_slip.h" #include "crc16.h" #include "hci_mem_pool.h" #include "hci_mem_pool_internal.h" #include "app_timer.h" #include "app_error.h" #include #include "sdk_common.h" #define PKT_HDR_SIZE 4u /**< Packet header size in number of bytes. */ #define PKT_CRC_SIZE 2u /**< Packet CRC size in number of bytes. */ #define PKT_TYPE_VENDOR_SPECIFIC 14u /**< Packet type vendor specific. */ #define PKT_TYPE_ACK 0 /**< Packet type acknowledgement. */ #define DATA_INTEGRITY_MASK (1u << 6u) /**< Mask for data integrity bit in the packet header. */ #define RELIABLE_PKT_MASK (1u << 7u) /**< Mask for reliable packet bit in the packet header. */ #define INITIAL_ACK_NUMBER_EXPECTED 1u /**< Initial acknowledge number expected. */ #define INITIAL_ACK_NUMBER_TX INITIAL_ACK_NUMBER_EXPECTED /**< Initial acknowledge number transmitted. */ #define INVALID_PKT_TYPE 0xFFFFFFFFu /**< Internal invalid packet type value. */ #define MAX_TRANSMISSION_TIME (ROUNDED_DIV((MAX_PACKET_SIZE_IN_BITS * 1000u), USED_BAUD_RATE)) /**< Max transmission time of a single application packet over UART in units of mseconds. */ #define RETRANSMISSION_TIMEOUT_IN_MS (3u * MAX_TRANSMISSION_TIME) /**< Retransmission timeout for application packet in units of mseconds. */ #define APP_TIMER_PRESCALER 0 /**< Value of the RTC1 PRESCALER register. */ #define RETRANSMISSION_TIMEOUT_IN_TICKS APP_TIMER_TICKS(RETRANSMISSION_TIMEOUT_IN_MS, APP_TIMER_PRESCALER) /**< Retransmission timeout for application packet in units of timer ticks. */ #define MAX_RETRY_COUNT 5u /**< Max retransmission retry count for application packets. */ #define ACK_BUF_SIZE 5u /**< Length of module internal RX buffer which is big enough to hold an acknowledgement packet. */ /**@brief States of the TX state machine. */ typedef enum { TX_STATE_IDLE, /**< State for: no application transmission packet processing in progress. */ TX_STATE_PENDING, /**< State for: TX in progress in slip layer and TX-done event is waited for to signal the end of transmission. */ TX_STATE_ACTIVE /**< State for: application packet has been delivered to slip for transmission and peer transport entity acknowledgement packet is waited for. */ } tx_state_t; /**@brief TX state machine events. */ typedef enum { TX_EVENT_STATE_ENTRY, /**< Event for: state entry use case. */ TX_EVENT_SLIP_TX_DONE, /**< Event for: HCI_SLIP_TX_DONE event use case. */ TX_EVENT_TIMEOUT, /**< Event for: retransmission timeout use case. */ TX_EVENT_VALID_RX_ACK /**< Event for: valid acknowledgement received for TX packet use case. */ } tx_event_t; static void tx_sm_state_change(tx_state_t new_state); static tx_state_t m_tx_state; /**< Current TX state. */ static hci_transport_tx_done_handler_t m_transport_tx_done_handle; /**< TX done event callback function. */ static hci_transport_event_handler_t m_transport_event_handle; /**< Event handler callback function. */ static uint8_t * mp_slip_used_rx_buffer; /**< Reference to RX buffer used by the slip layer. */ static uint32_t m_packet_expected_seq_number; /**< Sequence number counter of the packet expected to be received . */ static uint32_t m_packet_transmit_seq_number; /**< Sequence number counter of the transmitted packet for which acknowledgement packet is waited for. */ static uint8_t * mp_tx_buffer; /**< Pointer to TX application buffer to be transmitted. */ static uint32_t m_tx_buffer_length; /**< Length of application TX packet data to be transmitted in bytes. */ static bool m_is_slip_decode_ready; /**< Boolean to determine has slip decode been completed or not. */ APP_TIMER_DEF(m_app_timer_id); /**< Application timer id. */ static uint32_t m_tx_retry_counter; /**< Application packet retransmission counter. */ static hci_transport_tx_done_result_t m_tx_done_result_code; /**< TX done event callback function result code. */ static uint8_t m_rx_ack_buffer[ACK_BUF_SIZE];/**< RX buffer big enough to hold an acknowledgement packet and which is taken in use upon receiving HCI_SLIP_RX_OVERFLOW event. */ /**@brief Function for validating a received packet. * * @param[in] p_buffer Pointer to the packet data. * @param[in] length Length of packet data in bytes. * * @return true if received packet is valid, false in other case. */ static bool is_rx_pkt_valid(const uint8_t * p_buffer, uint32_t length) { // Executed packet filtering algorithm order: // - verify packet overall length // - verify data integrity bit set // - verify reliable packet bit set // - verify supported packet type // - verify header checksum // - verify payload length field // - verify CRC if (length <= PKT_HDR_SIZE) { return false; } if (!(p_buffer[0] & DATA_INTEGRITY_MASK)) { return false; } if (!(p_buffer[0] & RELIABLE_PKT_MASK)) { return false; } if ((p_buffer[1] & 0x0Fu) != PKT_TYPE_VENDOR_SPECIFIC) { return false; } const uint32_t expected_checksum = ((p_buffer[0] + p_buffer[1] + p_buffer[2] + p_buffer[3])) & 0xFFu; if (expected_checksum != 0) { return false; } const uint16_t crc_calculated = crc16_compute(p_buffer, (length - PKT_CRC_SIZE), NULL); const uint16_t crc_received = uint16_decode(&p_buffer[length - PKT_CRC_SIZE]); if (crc_calculated != crc_received) { return false; } return true; } /**@brief Function for getting the sequence number of the next reliable packet expected. * * @return sequence number of the next reliable packet expected. */ static __INLINE uint8_t packet_number_expected_get(void) { return (uint8_t) m_packet_expected_seq_number; } /**@brief Function for calculating a packet header checksum. * * @param[in] p_hdr Pointer to the packet header. * * @return Calculated checksum. */ static uint8_t header_checksum_calculate(const uint8_t * p_hdr) { // @note: no pointer validation check needed as already checked by calling function. uint32_t checksum; checksum = p_hdr[0]; checksum += p_hdr[1]; checksum += p_hdr[2]; checksum &= 0xFFu; checksum = (~checksum + 1u); return (uint8_t)checksum; } /**@brief Function for writing an acknowledgment packet for transmission. */ static void ack_transmit(void) { static uint8_t ack_packet[PKT_HDR_SIZE]; // TX ACK packet format: // - Unreliable Packet type // - Payload Length set to 0 // - Sequence Number set to 0 // - Header checksum calculated // - Acknowledge Number set correctly ack_packet[0] = (packet_number_expected_get() << 3u); ack_packet[1] = 0; ack_packet[2] = 0; ack_packet[3] = header_checksum_calculate(ack_packet); // @note: no return value check needed for hci_slip_write(...) call as acknowledgement packets // are considered to be from system design point of view unreliable packets.Use case where // underlying slip layer does not accept a packet for transmission is managed either by: // - acknowledged by possible future application packet as acknowledgement number header field // is included // - protocol peer entity will retransmit the packet UNUSED_VARIABLE(hci_slip_write(ack_packet, sizeof(ack_packet))); } /**@brief Function for validating a received packet. * * @param[in] p_buffer Pointer to the packet data. * * @return sequence number field of the packet header with unrelated data masked out. */ static __INLINE uint8_t packet_seq_nmbr_extract(const uint8_t * p_buffer) { return (p_buffer[0] & 0x07u); } /**@brief Function for incrementing the sequence number counter for next reliable packet expected. */ static __INLINE void packet_number_expected_inc(void) { ++m_packet_expected_seq_number; m_packet_expected_seq_number &= 0x07u; } /**@brief Function for decoding a packet type field. * * @param[in] p_buffer Pointer to the packet data. * @param[in] length Length of packet data in bytes. * * @return Packet type field or INVALID_PKT_TYPE in case of decode error. */ static __INLINE uint32_t packet_type_decode(const uint8_t * p_buffer, uint32_t length) { // @note: no pointer validation check needed as allready checked by calling function. uint32_t return_value; if (length >= PKT_HDR_SIZE) { return_value = (p_buffer[1] & 0x0Fu); } else { return_value = INVALID_PKT_TYPE; } return return_value; } /**@brief Function for processing a received vendor specific packet. * * @param[in] p_buffer Pointer to the packet data. * @param[in] length Length of packet data in bytes. */ static void rx_vendor_specific_pkt_type_handle(const uint8_t * p_buffer, uint32_t length) { // @note: no pointer validation check needed as allready checked by calling function. uint32_t err_code; if (is_rx_pkt_valid(p_buffer, length)) { // RX packet is valid: validate sequence number. const uint8_t rx_seq_number = packet_seq_nmbr_extract(p_buffer); if (packet_number_expected_get() == rx_seq_number) { // Sequence number is valid: transmit acknowledgement. packet_number_expected_inc(); ack_transmit(); m_is_slip_decode_ready = true; err_code = hci_mem_pool_rx_data_size_set(length); APP_ERROR_CHECK(err_code); err_code = hci_mem_pool_rx_produce(RX_BUF_SIZE, (void **)&mp_slip_used_rx_buffer); APP_ERROR_CHECK_BOOL((err_code == NRF_SUCCESS) || (err_code == NRF_ERROR_NO_MEM)); // If memory pool RX buffer produce succeeded we register that buffer to slip layer // otherwise we register the internal acknowledgement buffer. err_code = hci_slip_rx_buffer_register( (err_code == NRF_SUCCESS) ? mp_slip_used_rx_buffer : m_rx_ack_buffer, (err_code == NRF_SUCCESS) ? RX_BUF_SIZE : ACK_BUF_SIZE); APP_ERROR_CHECK(err_code); if (m_transport_event_handle != NULL) { // Send application event of RX packet reception. const hci_transport_evt_t evt = {HCI_TRANSPORT_RX_RDY}; m_transport_event_handle(evt); } } else { // RX packet discarded: sequence number not valid, set the same buffer to slip layer in // order to avoid buffer overrun. err_code = hci_slip_rx_buffer_register(mp_slip_used_rx_buffer, RX_BUF_SIZE); APP_ERROR_CHECK(err_code); // As packet did not have expected sequence number: send acknowledgement with the // current expected sequence number. ack_transmit(); } } else { // RX packet discarded: reset the same buffer to slip layer in order to avoid buffer // overrun. err_code = hci_slip_rx_buffer_register(mp_slip_used_rx_buffer, RX_BUF_SIZE); APP_ERROR_CHECK(err_code); } } /**@brief Function for getting the sequence number of a reliable TX packet for which peer protocol * entity acknowledgment is pending. * * @return sequence number of a reliable TX packet for which peer protocol entity acknowledgement * is pending. */ static __INLINE uint8_t packet_number_to_transmit_get(void) { return m_packet_transmit_seq_number; } /**@brief Function for getting the expected acknowledgement number. * * @return expected acknowledgement number. */ static __INLINE uint8_t expected_ack_number_get(void) { uint8_t seq_nmbr = packet_number_to_transmit_get(); ++seq_nmbr; seq_nmbr &= 0x07u; return seq_nmbr; } /**@brief Function for processing a received acknowledgement packet. * * Verifies does the received acknowledgement packet has the expected acknowledgement number and * that the header checksum is correct. * * @param[in] p_buffer Pointer to the packet data. * * @return true if valid acknowledgement packet received. */ static __INLINE bool rx_ack_pkt_type_handle(const uint8_t * p_buffer) { // @note: no pointer validation check needed as allready checked by calling function. // Verify header checksum. const uint32_t expected_checksum = ((p_buffer[0] + p_buffer[1] + p_buffer[2] + p_buffer[3])) & 0xFFu; if (expected_checksum != 0) { return false; } const uint8_t ack_number = (p_buffer[0] >> 3u) & 0x07u; // Verify expected acknowledgment number. return (ack_number == expected_ack_number_get()); } /**@brief Function for incrementing the sequence number counter of the TX packet. */ static __INLINE void packet_number_tx_inc(void) { ++m_packet_transmit_seq_number; m_packet_transmit_seq_number &= 0x07u; } /**@brief Function for TX state machine event processing in a state centric manner. * * @param[in] event Type of event occurred. */ static void tx_sm_event_handle(tx_event_t event) { uint32_t err_code; switch (m_tx_state) { case TX_STATE_IDLE: if (event == TX_EVENT_STATE_ENTRY) { err_code = app_timer_stop(m_app_timer_id); APP_ERROR_CHECK(err_code); // Send TX-done event if registered handler exists. if (m_transport_tx_done_handle != NULL) { m_transport_tx_done_handle(m_tx_done_result_code); } } break; case TX_STATE_PENDING: if (event == TX_EVENT_SLIP_TX_DONE) { // @note: this call should always succeed as called from HCI_SLIP_TX_DONE context // and error cases are managed by dedicated error event from the slip layer. err_code = hci_slip_write(mp_tx_buffer, (m_tx_buffer_length + PKT_HDR_SIZE + PKT_CRC_SIZE)); APP_ERROR_CHECK(err_code); tx_sm_state_change(TX_STATE_ACTIVE); } break; case TX_STATE_ACTIVE: switch (event) { case TX_EVENT_VALID_RX_ACK: // Tx sequence number counter incremented as packet transmission // acknowledged by peer transport entity. packet_number_tx_inc(); tx_sm_state_change(TX_STATE_IDLE); break; case TX_EVENT_STATE_ENTRY: m_tx_retry_counter = 0; err_code = app_timer_start(m_app_timer_id, RETRANSMISSION_TIMEOUT_IN_TICKS, NULL); APP_ERROR_CHECK(err_code); break; case TX_EVENT_TIMEOUT: if (m_tx_retry_counter != MAX_RETRY_COUNT) { ++m_tx_retry_counter; // @note: no return value check done for hci_slip_write(...) call as current // system design allows use case where retransmission is not accepted by the // slip layer due to existing acknowledgement packet transmission in the // slip layer. UNUSED_VARIABLE(hci_slip_write(mp_tx_buffer, (m_tx_buffer_length + PKT_HDR_SIZE + PKT_CRC_SIZE))); } else { // Application packet retransmission count reached: // - set correct TX done event callback function result code // - execute state change // @note: m_tx_retry_counter is reset in TX_STATE_ACTIVE state entry. m_tx_done_result_code = HCI_TRANSPORT_TX_DONE_FAILURE; tx_sm_state_change(TX_STATE_IDLE); } break; default: // No implementation needed. break; } break; default: // No implementation needed. break; } } /**@brief Function for changing the state of the TX state machine. * * @param[in] new_state State TX state machine transits to. */ static void tx_sm_state_change(tx_state_t new_state) { m_tx_state = new_state; tx_sm_event_handle(TX_EVENT_STATE_ENTRY); } /**@brief Function for handling slip events. * * @param[in] event The event structure. */ void slip_event_handle(hci_slip_evt_t event) { uint32_t return_code; uint32_t err_code; switch (event.evt_type) { case HCI_SLIP_TX_DONE: tx_sm_event_handle(TX_EVENT_SLIP_TX_DONE); break; case HCI_SLIP_RX_RDY: return_code = packet_type_decode(event.packet, event.packet_length); switch (return_code) { case PKT_TYPE_VENDOR_SPECIFIC: rx_vendor_specific_pkt_type_handle(event.packet, event.packet_length); break; case PKT_TYPE_ACK: if (rx_ack_pkt_type_handle(event.packet)) { // Valid expected acknowledgement packet received: set correct TX done event // callback function result code and execute state change. m_tx_done_result_code = HCI_TRANSPORT_TX_DONE_SUCCESS; tx_sm_event_handle(TX_EVENT_VALID_RX_ACK); } /* fall-through */ default: // RX packet dropped: reset memory buffer to slip in order to avoid RX buffer // overflow. // If existing mem pool produced RX buffer exists reuse that one. If existing // mem pool produced RX buffer does not exist try to produce new one. If // producing fails use the internal acknowledgement buffer. if (mp_slip_used_rx_buffer != NULL) { err_code = hci_slip_rx_buffer_register(mp_slip_used_rx_buffer, RX_BUF_SIZE); APP_ERROR_CHECK(err_code); } else { err_code = hci_mem_pool_rx_produce(RX_BUF_SIZE, (void **)&mp_slip_used_rx_buffer); APP_ERROR_CHECK_BOOL((err_code == NRF_SUCCESS) || (err_code == NRF_ERROR_NO_MEM)); err_code = hci_slip_rx_buffer_register( (err_code == NRF_SUCCESS) ? mp_slip_used_rx_buffer : m_rx_ack_buffer, (err_code == NRF_SUCCESS) ? RX_BUF_SIZE : ACK_BUF_SIZE); APP_ERROR_CHECK(err_code); } break; } break; case HCI_SLIP_RX_OVERFLOW: err_code = hci_slip_rx_buffer_register(m_rx_ack_buffer, ACK_BUF_SIZE); APP_ERROR_CHECK(err_code); break; case HCI_SLIP_ERROR: APP_ERROR_HANDLER(event.evt_type); break; default: APP_ERROR_HANDLER(event.evt_type); break; } } uint32_t hci_transport_evt_handler_reg(hci_transport_event_handler_t event_handler) { uint32_t err_code; m_transport_event_handle = event_handler; err_code = hci_slip_evt_handler_register(slip_event_handle); APP_ERROR_CHECK(err_code); return (event_handler != NULL) ? NRF_SUCCESS : NRF_ERROR_NULL; } uint32_t hci_transport_tx_done_register(hci_transport_tx_done_handler_t event_handler) { uint32_t err_code; m_transport_tx_done_handle = event_handler; err_code = hci_slip_evt_handler_register(slip_event_handle); APP_ERROR_CHECK(err_code); return (event_handler != NULL) ? NRF_SUCCESS : NRF_ERROR_NULL; } /**@brief Function for handling the application packet retransmission timeout. * * This function is registered in the @ref app_timer module when a timer is created on * @ref hci_transport_open. * * @note This function must be executed in APP-LO context otherwise retransmission behaviour is * undefined, see @ref nrf51_system_integration_serialization. * * @param[in] p_context The timeout context. */ void hci_transport_timeout_handle(void * p_context) { tx_sm_event_handle(TX_EVENT_TIMEOUT); } uint32_t hci_transport_open(void) { mp_tx_buffer = NULL; m_tx_buffer_length = 0; m_tx_retry_counter = 0; m_is_slip_decode_ready = false; m_tx_state = TX_STATE_IDLE; m_packet_expected_seq_number = INITIAL_ACK_NUMBER_EXPECTED; m_packet_transmit_seq_number = INITIAL_ACK_NUMBER_TX; m_tx_done_result_code = HCI_TRANSPORT_TX_DONE_FAILURE; uint32_t err_code = app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, hci_transport_timeout_handle); if (err_code != NRF_SUCCESS) { // @note: conduct required interface adjustment. return NRF_ERROR_INTERNAL; } err_code = hci_mem_pool_open(); VERIFY_SUCCESS(err_code); err_code = hci_slip_open(); VERIFY_SUCCESS(err_code); err_code = hci_mem_pool_rx_produce(RX_BUF_SIZE, (void **)&mp_slip_used_rx_buffer); if (err_code != NRF_SUCCESS) { // @note: conduct required interface adjustment. return NRF_ERROR_INTERNAL; } err_code = hci_slip_rx_buffer_register(mp_slip_used_rx_buffer, RX_BUF_SIZE); return err_code; } uint32_t hci_transport_close(void) { uint32_t err_code; m_transport_tx_done_handle = NULL; m_transport_event_handle = NULL; err_code = hci_mem_pool_close(); APP_ERROR_CHECK(err_code); err_code = hci_slip_close(); APP_ERROR_CHECK(err_code); // @note: NRF_ERROR_NO_MEM is the only return value which should never be returned. err_code = app_timer_stop(m_app_timer_id); APP_ERROR_CHECK_BOOL(err_code != NRF_ERROR_NO_MEM); return NRF_SUCCESS; } uint32_t hci_transport_tx_alloc(uint8_t ** pp_memory) { const uint32_t err_code = hci_mem_pool_tx_alloc((void **)pp_memory); if (err_code == NRF_SUCCESS) { // @note: no need to validate pp_memory against null as validation has already been done // by hci_mem_pool_tx_alloc(...) and visible to us from the method return code. //lint -e(413) "Likely use of null pointer" *pp_memory += PKT_HDR_SIZE; } return err_code; } uint32_t hci_transport_tx_free(void) { return hci_mem_pool_tx_free(); } /**@brief Function for constructing 1st byte of the packet header of the packet to be transmitted. * * @return 1st byte of the packet header of the packet to be transmitted */ static __INLINE uint8_t tx_packet_byte_zero_construct(void) { const uint32_t value = DATA_INTEGRITY_MASK | RELIABLE_PKT_MASK | (packet_number_expected_get() << 3u) | packet_number_to_transmit_get(); return (uint8_t) value; } /**@brief Function for handling the application packet write request in tx-idle state. */ static uint32_t pkt_write_handle(void) { uint32_t err_code; // Set packet header fields. mp_tx_buffer -= PKT_HDR_SIZE; mp_tx_buffer[0] = tx_packet_byte_zero_construct(); const uint16_t type_and_length_fields = ((m_tx_buffer_length << 4u) | PKT_TYPE_VENDOR_SPECIFIC); // @note: no use case for uint16_encode(...) return value. UNUSED_VARIABLE(uint16_encode(type_and_length_fields, &(mp_tx_buffer[1]))); mp_tx_buffer[3] = header_checksum_calculate(mp_tx_buffer); // Calculate, append CRC to the packet and write it. const uint16_t crc = crc16_compute(mp_tx_buffer, (PKT_HDR_SIZE + m_tx_buffer_length), NULL); // @note: no use case for uint16_encode(...) return value. UNUSED_VARIABLE(uint16_encode(crc, &(mp_tx_buffer[PKT_HDR_SIZE + m_tx_buffer_length]))); err_code = hci_slip_write(mp_tx_buffer, (m_tx_buffer_length + PKT_HDR_SIZE + PKT_CRC_SIZE)); switch (err_code) { case NRF_SUCCESS: tx_sm_state_change(TX_STATE_ACTIVE); break; case NRF_ERROR_NO_MEM: tx_sm_state_change(TX_STATE_PENDING); err_code = NRF_SUCCESS; break; default: // No implementation needed. break; } return err_code; } uint32_t hci_transport_pkt_write(const uint8_t * p_buffer, uint16_t length) { uint32_t err_code; if (p_buffer) { switch (m_tx_state) { case TX_STATE_IDLE: mp_tx_buffer = (uint8_t *)p_buffer; m_tx_buffer_length = length; err_code = pkt_write_handle(); break; default: err_code = NRF_ERROR_NO_MEM; break; } } else { err_code = NRF_ERROR_NULL; } return err_code; } uint32_t hci_transport_rx_pkt_extract(uint8_t ** pp_buffer, uint16_t * p_length) { uint32_t err_code; if (pp_buffer != NULL && p_length != NULL) { uint32_t length = 0; if (m_is_slip_decode_ready) { m_is_slip_decode_ready = false; err_code = hci_mem_pool_rx_extract(pp_buffer, &length); length -= (PKT_HDR_SIZE + PKT_CRC_SIZE); *p_length = (uint16_t)length; *pp_buffer += PKT_HDR_SIZE; } else { err_code = NRF_ERROR_NO_MEM; } } else { err_code = NRF_ERROR_NULL; } return err_code; } uint32_t hci_transport_rx_pkt_consume(uint8_t * p_buffer) { return (hci_mem_pool_rx_consume(p_buffer - PKT_HDR_SIZE)); }