773 lines
22 KiB
C
773 lines
22 KiB
C
/**
|
|
* This software is subject to the ANT+ Shared Source License
|
|
* www.thisisant.com/swlicenses
|
|
* Copyright (c) Garmin Canada Inc. 2018
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or
|
|
* without modification, are permitted provided that the following
|
|
* conditions are met:
|
|
*
|
|
* 1) Redistributions of source code must retain the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer.
|
|
*
|
|
* 2) Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials
|
|
* provided with the distribution.
|
|
*
|
|
* 3) Neither the name of Garmin nor the names of its
|
|
* contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* The following actions are prohibited:
|
|
*
|
|
* 1) Redistribution of source code containing the ANT+ Network
|
|
* Key. The ANT+ Network Key is available to ANT+ Adopters.
|
|
* Please refer to http://thisisant.com to become an ANT+
|
|
* Adopter and access the key.
|
|
*
|
|
* 2) Reverse engineering, decompilation, and/or disassembly of
|
|
* software provided in binary form under this license.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE HEREBY
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; DAMAGE TO ANY DEVICE, 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. SOME STATES DO NOT ALLOW
|
|
* THE EXCLUSION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THE
|
|
* ABOVE LIMITATIONS MAY NOT APPLY TO YOU.
|
|
*
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include "sdk_common.h"
|
|
|
|
#include "ant_channel_config.h"
|
|
#include "ant_interface.h"
|
|
#include "ant_parameters.h"
|
|
#include "nrf_assert.h"
|
|
#include "nrf_balloc.h"
|
|
#include "nrf_bootloader_info.h"
|
|
#include "nrf_dfu_handling_error.h"
|
|
#include "nrf_dfu_req_handler.h"
|
|
#include "nrf_dfu_transport.h"
|
|
#include "nrf_dfu_mbr.h"
|
|
#include "nrf_sdh.h"
|
|
#include "nrf_sdh_ant.h"
|
|
#include "nrf_soc.h"
|
|
|
|
#define NRF_LOG_MODULE_NAME nrf_dfu_ant
|
|
#include "nrf_log.h"
|
|
NRF_LOG_MODULE_REGISTER();
|
|
|
|
/**@file
|
|
*
|
|
* @defgroup nrf_dfu_ant ANT transport for reference DFU.
|
|
* @ingroup nrf_dfu
|
|
* @brief Device Firmware Update (DFU) transport layer using ANT.
|
|
*
|
|
* Transport documentation:
|
|
*
|
|
* The ANT transport uses all of the same opcodes and payload formats as the
|
|
* UART serial transport. The only differences are the packet header format and
|
|
* some extra details to deal with retransmissions.
|
|
*
|
|
* The device receiving the update is the ANT master. The format of the
|
|
* broadcast buffer is as follows;
|
|
* Byte 0: Current Slave -> Master sequence number.
|
|
* Byte 1: Current Master -> Slave sequence number.
|
|
* Bytes 2-7: Reserved, set to 0.
|
|
*
|
|
* The sequence numbers are used to detect retransmissions, any messages sent
|
|
* with a sequence number equivalent to the current sequence will be ignored.
|
|
*
|
|
* When the slave first connects to the master it should inspect the broadcast
|
|
* data in order to synchronize its sequence counters.
|
|
*
|
|
* All commands/responses are padded out to the nearest 8-byte boundary after
|
|
* framing, and then sent using either a burst or acknowledged data depending on
|
|
* length (ack data is used for 8-byte messages). The message transmission is
|
|
* retried until an EVENT_TRANSFER_TX_COMPLETE event is received.
|
|
* All messages are framed using the following format:
|
|
* Bytes 0-1: Message length before padding, little endian, includes header.
|
|
* Byte 2: Sequence number. Increment for every new message.
|
|
* Byte 3: Op code. Always 0x60 for responses.
|
|
* Bytes 4-N: Command/Response payload. This follows the same format as the
|
|
* UART serial transport, without any SLIP encoding.
|
|
*
|
|
* As a final note, the MTU for this protocol is the maximum size of a burst
|
|
* that can be received.
|
|
*/
|
|
|
|
/** Packet header is always 2 byte length + seq num + op code */
|
|
#define PKT_HEADER_SIZE 4
|
|
|
|
/** Maximum size of the payload in a write command. */
|
|
#define MAX_WRITE_PAYLOAD (NRF_DFU_ANT_MTU - PKT_HEADER_SIZE)
|
|
|
|
/** Bursts are always a multiple of the standard data size. */
|
|
STATIC_ASSERT_MSG(
|
|
ALIGN_NUM(ANT_STANDARD_DATA_PAYLOAD_SIZE, NRF_DFU_ANT_MTU) == NRF_DFU_ANT_MTU,
|
|
"ANT MTU must be a multiple of " STRINGIFY(ANT_STANDARD_DATA_PAYLOAD_SIZE));
|
|
|
|
/** Number of buffers to reserve space for with balloc. */
|
|
#if (NRF_DFU_ANT_BUFFERS_OVERRIDE)
|
|
#define NUM_BUFFERS NRF_DFU_ANT_BUFFERS
|
|
#else
|
|
#define NUM_BUFFERS CEIL_DIV(CODE_PAGE_SIZE, MAX_WRITE_PAYLOAD)
|
|
#endif
|
|
|
|
static uint32_t ant_dfu_init(nrf_dfu_observer_t observer);
|
|
static uint32_t ant_dfu_close(nrf_dfu_transport_t const * p_exception);
|
|
|
|
static ant_channel_config_t m_channel_config = {
|
|
.channel_number = 0,
|
|
.channel_type = CHANNEL_TYPE_MASTER,
|
|
.rf_freq = NRF_DFU_ANT_RF_FREQ,
|
|
.transmission_type = 1, // Non-shared, no global pages.
|
|
.device_type = NRF_DFU_ANT_DEV_TYPE,
|
|
.channel_period = NRF_DFU_ANT_CHANNEL_PERIOD,
|
|
};
|
|
|
|
static nrf_dfu_observer_t m_observer;
|
|
/** Has transport been initialized by DFU core */
|
|
static bool m_initialized = false;
|
|
/** Has the channel started broadcasting */
|
|
static bool m_started = false;
|
|
/** Has some data been received on the transport. */
|
|
static bool m_active = false;
|
|
|
|
/** State tracking for rx transfers. */
|
|
static struct
|
|
{
|
|
/** Buffer for holding the command. */
|
|
uint8_t * buff;
|
|
/** Amount of data written */
|
|
size_t offset;
|
|
/** Sequence of last processed command. */
|
|
uint8_t seq;
|
|
} m_rx;
|
|
|
|
/** State tracking for tx transfers. */
|
|
static struct
|
|
{
|
|
/** Raw data to send. */
|
|
uint8_t resp[ALIGN_NUM(ANT_STANDARD_DATA_PAYLOAD_SIZE,
|
|
PKT_HEADER_SIZE + sizeof(nrf_dfu_response_t))];
|
|
/** Length of data to send. 0 Indicates no response queued. */
|
|
size_t len;
|
|
/** Sequence number of last queued response. */
|
|
uint8_t seq;
|
|
/**
|
|
* Used as burst flag for softdevice, allows to busy loop until all data is
|
|
* accepted by softdevice.
|
|
*/
|
|
volatile bool buffering;
|
|
/**
|
|
* Indicate that a new response was generated before the last one was
|
|
* confirmed.
|
|
*/
|
|
bool response_overwritten;
|
|
/** Data buffer used for broadcast messages. */
|
|
uint8_t bcast_data[ANT_STANDARD_DATA_PAYLOAD_SIZE];
|
|
} m_tx;
|
|
|
|
/** State tracking for progress notifications. */
|
|
static struct
|
|
{
|
|
/** Requested PRN */
|
|
uint16_t limit;
|
|
/** How many more write commands until a CRC should be sent back. */
|
|
uint16_t remaining;
|
|
} m_pkt_notify;
|
|
|
|
DFU_TRANSPORT_REGISTER(nrf_dfu_transport_t const ant_dfu_transport) = {
|
|
.init_func = ant_dfu_init,
|
|
.close_func = ant_dfu_close,
|
|
};
|
|
|
|
NRF_BALLOC_DEF(m_buffer_pool, NRF_DFU_ANT_MTU, NUM_BUFFERS);
|
|
|
|
static void release_rx_buff(void)
|
|
{
|
|
if (m_rx.buff != NULL)
|
|
{
|
|
nrf_balloc_free(&m_buffer_pool, m_rx.buff);
|
|
m_rx.buff = NULL;
|
|
}
|
|
}
|
|
|
|
static void transmit_response(void)
|
|
{
|
|
uint32_t err_code = NRF_SUCCESS;
|
|
|
|
size_t full_len = ALIGN_NUM(ANT_STANDARD_DATA_PAYLOAD_SIZE, m_tx.len);
|
|
|
|
// Pad out with 0's.
|
|
memset(&m_tx.resp[m_tx.len], 0, full_len - m_tx.len);
|
|
|
|
if (full_len > ANT_STANDARD_DATA_PAYLOAD_SIZE)
|
|
{
|
|
err_code = sd_ant_burst_handler_request(
|
|
m_channel_config.channel_number,
|
|
full_len, m_tx.resp,
|
|
BURST_SEGMENT_START | BURST_SEGMENT_END);
|
|
} else
|
|
{
|
|
err_code = sd_ant_acknowledge_message_tx(
|
|
m_channel_config.channel_number,
|
|
full_len, m_tx.resp);
|
|
}
|
|
|
|
// Wait for buffer to be consumed.
|
|
// TODO: wait flag management needs to be improved if this will coexist with
|
|
// other channels.
|
|
while (err_code == NRF_SUCCESS && m_tx.buffering)
|
|
{
|
|
err_code = sd_app_evt_wait();
|
|
}
|
|
|
|
if (err_code != NRF_SUCCESS)
|
|
{
|
|
NRF_LOG_ERROR("Sending response failed with error %d", err_code);
|
|
}
|
|
}
|
|
|
|
static void update_bcast_data(void)
|
|
{
|
|
memset(m_tx.bcast_data, 0, sizeof(m_tx.bcast_data));
|
|
m_tx.bcast_data[0] = m_rx.seq;
|
|
m_tx.bcast_data[1] = m_tx.seq;
|
|
|
|
if (NRF_SUCCESS != sd_ant_broadcast_message_tx(
|
|
m_channel_config.channel_number,
|
|
sizeof(m_tx.bcast_data), m_tx.bcast_data))
|
|
{
|
|
NRF_LOG_WARNING("Unable to update broadcast data.");
|
|
}
|
|
}
|
|
|
|
static void handle_write_complete(void * p_buf)
|
|
{
|
|
nrf_balloc_free(&m_buffer_pool, p_buf);
|
|
}
|
|
|
|
static void prepare_response(nrf_dfu_response_t * p_res)
|
|
{
|
|
if (m_tx.len)
|
|
{
|
|
NRF_LOG_WARNING("Overwriting previous response.");
|
|
m_tx.response_overwritten = true;
|
|
}
|
|
|
|
// reserve first 2 bytes for length.
|
|
m_tx.len = 2;
|
|
|
|
m_tx.resp[m_tx.len++] = ++(m_tx.seq);
|
|
m_tx.resp[m_tx.len++] = NRF_DFU_OP_RESPONSE;
|
|
m_tx.resp[m_tx.len++] = p_res->request;
|
|
m_tx.resp[m_tx.len++] = p_res->result;
|
|
|
|
if (p_res->result == NRF_DFU_RES_CODE_SUCCESS)
|
|
{
|
|
switch(p_res->request)
|
|
{
|
|
case NRF_DFU_OP_PROTOCOL_VERSION:
|
|
{
|
|
m_tx.resp[m_tx.len++] = p_res->protocol.version;
|
|
} break;
|
|
|
|
case NRF_DFU_OP_CRC_GET:
|
|
{
|
|
m_tx.len += uint32_encode(
|
|
p_res->crc.offset, &m_tx.resp[m_tx.len]);
|
|
m_tx.len += uint32_encode(
|
|
p_res->crc.crc, &m_tx.resp[m_tx.len]);
|
|
} break;
|
|
|
|
case NRF_DFU_OP_OBJECT_SELECT:
|
|
{
|
|
m_tx.len += uint32_encode(
|
|
p_res->select.max_size, &m_tx.resp[m_tx.len]);
|
|
m_tx.len += uint32_encode(
|
|
p_res->select.offset, &m_tx.resp[m_tx.len]);
|
|
m_tx.len += uint32_encode(
|
|
p_res->select.crc, &m_tx.resp[m_tx.len]);
|
|
} break;
|
|
|
|
case NRF_DFU_OP_MTU_GET:
|
|
{
|
|
m_tx.len += uint16_encode(
|
|
p_res->mtu.size, &m_tx.resp[m_tx.len]);
|
|
} break;
|
|
|
|
case NRF_DFU_OP_PING:
|
|
{
|
|
m_tx.resp[m_tx.len++] = p_res->ping.id;
|
|
} break;
|
|
|
|
case NRF_DFU_OP_HARDWARE_VERSION:
|
|
{
|
|
m_tx.len += uint32_encode(
|
|
p_res->hardware.part, &m_tx.resp[m_tx.len]);
|
|
m_tx.len += uint32_encode(
|
|
p_res->hardware.variant, &m_tx.resp[m_tx.len]);
|
|
m_tx.len += uint32_encode(
|
|
p_res->hardware.memory.rom_size, &m_tx.resp[m_tx.len]);
|
|
m_tx.len += uint32_encode(
|
|
p_res->hardware.memory.rom_page_size, &m_tx.resp[m_tx.len]);
|
|
m_tx.len += uint32_encode(
|
|
p_res->hardware.memory.ram_size, &m_tx.resp[m_tx.len]);
|
|
} break;
|
|
|
|
case NRF_DFU_OP_FIRMWARE_VERSION:
|
|
{
|
|
m_tx.resp[m_tx.len++] = p_res->firmware.type;
|
|
m_tx.len += uint32_encode(
|
|
p_res->firmware.version, &m_tx.resp[m_tx.len]);
|
|
m_tx.len += uint32_encode(
|
|
p_res->firmware.addr, &m_tx.resp[m_tx.len]);
|
|
m_tx.len += uint32_encode(
|
|
p_res->firmware.len, &m_tx.resp[m_tx.len]);
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (p_res->result == NRF_DFU_RES_CODE_EXT_ERROR)
|
|
{
|
|
m_tx.resp[m_tx.len++] = ext_error_get();
|
|
UNUSED_RETURN_VALUE(ext_error_set(NRF_DFU_EXT_ERROR_NO_ERROR));
|
|
}
|
|
|
|
// Finally fill in the length.
|
|
UNUSED_RETURN_VALUE(uint16_encode(m_tx.len, m_tx.resp));
|
|
|
|
// Safety check buffer overflow.
|
|
ASSERT(m_tx.len <= sizeof(m_tx.resp));
|
|
|
|
if (!m_tx.response_overwritten)
|
|
{
|
|
// Can send out the response immediately if there wasn't a previous one
|
|
// queued.
|
|
transmit_response();
|
|
}
|
|
}
|
|
|
|
static void handle_response(nrf_dfu_response_t * p_res, void * p_context)
|
|
{
|
|
UNUSED_PARAMETER(p_context);
|
|
|
|
if (p_res->result != NRF_DFU_RES_CODE_SUCCESS)
|
|
{
|
|
NRF_LOG_WARNING("Operation %d had result %d",
|
|
p_res->request, p_res->result);
|
|
}
|
|
|
|
if (p_res->request == NRF_DFU_OP_OBJECT_WRITE)
|
|
{
|
|
if (m_pkt_notify.limit == 0 ||
|
|
--m_pkt_notify.remaining != 0)
|
|
{
|
|
// No packet notification needed, filter out response.
|
|
return;
|
|
}
|
|
|
|
// Packet Notification time, send a CRC response.
|
|
m_pkt_notify.remaining = m_pkt_notify.limit;
|
|
p_res->request = NRF_DFU_OP_CRC_GET;
|
|
uint32_t offset = p_res->write.offset;
|
|
uint32_t crc = p_res->write.crc;
|
|
p_res->crc.offset = offset;
|
|
p_res->crc.crc = crc;
|
|
}
|
|
|
|
prepare_response(p_res);
|
|
}
|
|
|
|
static uint32_t handle_request(void)
|
|
{
|
|
uint16_t len = uint16_decode(m_rx.buff);
|
|
uint16_t offset = sizeof(uint16_t);
|
|
|
|
if (len < PKT_HEADER_SIZE || len > m_rx.offset)
|
|
{
|
|
NRF_LOG_WARNING("Ignoring command with invalid length.");
|
|
return NRF_ERROR_DATA_SIZE;
|
|
}
|
|
|
|
uint8_t seq = m_rx.buff[offset++];
|
|
if (!m_active)
|
|
{
|
|
m_active = true;
|
|
// Close all other transports.
|
|
UNUSED_RETURN_VALUE(nrf_dfu_transports_close(&ant_dfu_transport));
|
|
}
|
|
else if (seq == m_rx.seq)
|
|
{
|
|
NRF_LOG_DEBUG("Ignoring repeated command");
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
m_rx.seq = seq;
|
|
|
|
nrf_dfu_request_t request = {
|
|
.request = (nrf_dfu_op_t)m_rx.buff[offset++],
|
|
.callback.response = handle_response,
|
|
};
|
|
|
|
switch(request.request)
|
|
{
|
|
case NRF_DFU_OP_OBJECT_CREATE:
|
|
{
|
|
request.create.object_type = m_rx.buff[offset++];
|
|
request.create.object_size = uint32_decode(&m_rx.buff[offset]);
|
|
offset += sizeof(uint32_t);
|
|
} break;
|
|
|
|
case NRF_DFU_OP_RECEIPT_NOTIF_SET:
|
|
{
|
|
request.prn.target = uint16_decode(&m_rx.buff[offset]);
|
|
offset += sizeof(uint16_t);
|
|
} break;
|
|
|
|
case NRF_DFU_OP_OBJECT_SELECT:
|
|
{
|
|
request.select.object_type = m_rx.buff[offset++];
|
|
} break;
|
|
|
|
case NRF_DFU_OP_OBJECT_WRITE:
|
|
{
|
|
request.write.p_data = &m_rx.buff[offset];
|
|
request.write.len = len - offset;
|
|
offset = len;
|
|
} break;
|
|
|
|
case NRF_DFU_OP_PING:
|
|
{
|
|
request.ping.id = m_rx.buff[offset++];
|
|
} break;
|
|
|
|
case NRF_DFU_OP_FIRMWARE_VERSION:
|
|
{
|
|
request.firmware.image_number = m_rx.buff[offset++];
|
|
} break;
|
|
|
|
case NRF_DFU_OP_MTU_GET:
|
|
{
|
|
NRF_LOG_DEBUG("ANT DFU: Responding to MTU request with %d",
|
|
NRF_DFU_ANT_MTU);
|
|
request.mtu.size = NRF_DFU_ANT_MTU;
|
|
} break;
|
|
|
|
default:
|
|
// Do nothing.
|
|
break;
|
|
}
|
|
|
|
if (offset > len)
|
|
{
|
|
NRF_LOG_WARNING("Ignoring command with invalid length");
|
|
return NRF_ERROR_DATA_SIZE;
|
|
}
|
|
|
|
// Some processing that is only safe to do if accepting the command.
|
|
|
|
switch (request.request)
|
|
{
|
|
case NRF_DFU_OP_RECEIPT_NOTIF_SET:
|
|
{
|
|
m_pkt_notify.limit = request.prn.target;
|
|
m_pkt_notify.remaining = m_pkt_notify.limit;
|
|
} break;
|
|
|
|
case NRF_DFU_OP_OBJECT_CREATE:
|
|
case NRF_DFU_OP_OBJECT_SELECT:
|
|
{
|
|
m_pkt_notify.remaining = m_pkt_notify.limit;
|
|
} break;
|
|
|
|
case NRF_DFU_OP_OBJECT_WRITE:
|
|
{
|
|
// Ownership of buffer is transferred to the write command.
|
|
request.callback.write = handle_write_complete;
|
|
m_rx.buff = NULL;
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return nrf_dfu_req_handler_on_req(&request);
|
|
}
|
|
|
|
static void handle_tx_transfer_complete(bool success)
|
|
{
|
|
if (m_tx.response_overwritten)
|
|
{
|
|
// By treating the result as a failure the retransmission will send out
|
|
// the new response.
|
|
success = false;
|
|
m_tx.response_overwritten = false;
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
m_tx.len = 0;
|
|
update_bcast_data();
|
|
}
|
|
else
|
|
{
|
|
transmit_response();
|
|
}
|
|
}
|
|
|
|
static void handle_rx_transfer_start()
|
|
{
|
|
if (m_rx.buff == NULL)
|
|
{
|
|
m_rx.buff = nrf_balloc_alloc(&m_buffer_pool);
|
|
if (m_rx.buff != NULL)
|
|
{
|
|
NRF_LOG_INFO("Allocated buffer %x", m_rx.buff);
|
|
}
|
|
else
|
|
{
|
|
NRF_LOG_ERROR("Unable to allocate buffer for incoming packet.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
NRF_LOG_DEBUG("Resetting rx pointer.");
|
|
m_rx.offset = 0;
|
|
}
|
|
|
|
static void handle_rx_transfer_complete(bool success)
|
|
{
|
|
if (success)
|
|
{
|
|
uint32_t err_code = handle_request();
|
|
if (err_code != NRF_SUCCESS)
|
|
{
|
|
NRF_LOG_ERROR("Error %d handling request.", err_code);
|
|
}
|
|
}
|
|
|
|
release_rx_buff();
|
|
}
|
|
|
|
static void handle_rx_transfer_data(uint8_t * data, size_t len)
|
|
{
|
|
if (m_rx.buff == NULL)
|
|
{
|
|
NRF_LOG_DEBUG("Ignoring transfer data.");
|
|
return;
|
|
}
|
|
|
|
if (m_rx.offset + len > NRF_DFU_ANT_MTU)
|
|
{
|
|
NRF_LOG_ERROR("Received packet overflows MTU.");
|
|
handle_rx_transfer_complete(false);
|
|
return;
|
|
}
|
|
|
|
memcpy(&m_rx.buff[m_rx.offset], data, len);
|
|
m_rx.offset += len;
|
|
}
|
|
|
|
static void handle_data_mesg(ANT_MESSAGE * p_msg)
|
|
{
|
|
bool is_first = false;
|
|
bool is_last = false;
|
|
uint8_t len = ANT_STANDARD_DATA_PAYLOAD_SIZE;
|
|
|
|
switch(p_msg->ANT_MESSAGE_ucMesgID)
|
|
{
|
|
case MESG_BROADCAST_DATA_ID:
|
|
{
|
|
// Broadcast data is ignored.
|
|
len = 0;
|
|
} break;
|
|
|
|
case MESG_ACKNOWLEDGED_DATA_ID:
|
|
{
|
|
is_first = true;
|
|
is_last = true;
|
|
} break;
|
|
|
|
case MESG_ADV_BURST_DATA_ID:
|
|
{
|
|
len = p_msg->ANT_MESSAGE_ucSize - MESG_CHANNEL_NUM_SIZE;
|
|
} // FALL-THROUGH : both burst types act the same other than len.
|
|
case MESG_BURST_DATA_ID:
|
|
{
|
|
uint8_t seq = p_msg->ANT_MESSAGE_ucChannel & SEQUENCE_NUMBER_MASK;
|
|
is_first = seq == SEQUENCE_FIRST_MESSAGE;
|
|
is_last = !!(seq & SEQUENCE_LAST_MESSAGE);
|
|
} break;
|
|
}
|
|
|
|
if (len != 0)
|
|
{
|
|
if (is_first)
|
|
{
|
|
handle_rx_transfer_start();
|
|
}
|
|
|
|
handle_rx_transfer_data(p_msg->ANT_MESSAGE_aucPayload, len);
|
|
|
|
if (is_last)
|
|
{
|
|
handle_rx_transfer_complete(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ant_dfu_evt_handler(ant_evt_t * p_ant_evt, void * p_context)
|
|
{
|
|
// Ignore messages meant for other channels.
|
|
if (p_ant_evt->channel != m_channel_config.channel_number)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch(p_ant_evt->event)
|
|
{
|
|
case EVENT_TX:
|
|
{
|
|
if (!m_started)
|
|
{
|
|
m_started = true;
|
|
m_observer(NRF_DFU_EVT_TRANSPORT_ACTIVATED);
|
|
}
|
|
} break;
|
|
|
|
case EVENT_TRANSFER_TX_COMPLETED:
|
|
handle_tx_transfer_complete(true);
|
|
break;
|
|
case EVENT_TRANSFER_TX_FAILED:
|
|
handle_tx_transfer_complete(false);
|
|
break;
|
|
|
|
case EVENT_RX:
|
|
handle_data_mesg(&p_ant_evt->message);
|
|
break;
|
|
|
|
case EVENT_TRANSFER_RX_FAILED:
|
|
handle_rx_transfer_complete(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uint32_t ant_dfu_init(nrf_dfu_observer_t observer)
|
|
{
|
|
uint32_t err_code = NRF_SUCCESS;
|
|
if (m_initialized)
|
|
{
|
|
return err_code;
|
|
}
|
|
|
|
NRF_SDH_ANT_OBSERVER(ant_dfu_observer, NRF_DFU_ANT_EVT_HANDLER_PRIO,
|
|
ant_dfu_evt_handler, NULL);
|
|
|
|
m_observer = observer;
|
|
m_tx.seq = m_rx.seq = 0;
|
|
m_active = false;
|
|
m_started = false;
|
|
|
|
NRF_LOG_DEBUG("Initializing ANT DFU transport");
|
|
|
|
err_code = nrf_balloc_init(&m_buffer_pool);
|
|
VERIFY_SUCCESS(err_code);
|
|
|
|
err_code = nrf_dfu_mbr_init_sd();
|
|
VERIFY_SUCCESS(err_code);
|
|
|
|
NRF_LOG_DEBUG("Setting up vector table: 0x%08x", BOOTLOADER_START_ADDR);
|
|
err_code = sd_softdevice_vector_table_base_set(BOOTLOADER_START_ADDR);
|
|
VERIFY_SUCCESS(err_code);
|
|
|
|
NRF_LOG_DEBUG("Enabling softdevice");
|
|
err_code = nrf_sdh_enable_request();
|
|
VERIFY_SUCCESS(err_code);
|
|
|
|
err_code = nrf_sdh_ant_enable();
|
|
VERIFY_SUCCESS(err_code);
|
|
|
|
static uint8_t adv_burst_conf[] = {
|
|
ADV_BURST_MODE_ENABLE,
|
|
ADV_BURST_MODES_SIZE_24_BYTES,
|
|
0, // No required modes.
|
|
0, 0, // Reserved
|
|
ADV_BURST_MODES_FREQ_HOP, // Optional Modes
|
|
0, 0, // Reserved
|
|
// No optional configs.
|
|
};
|
|
err_code = sd_ant_adv_burst_config_set(adv_burst_conf, sizeof(adv_burst_conf));
|
|
VERIFY_SUCCESS(err_code);
|
|
|
|
m_channel_config.device_number = NRF_FICR->DEVICEID[0];
|
|
m_channel_config.transmission_type |= (NRF_FICR->DEVICEID[1] & 0xF) << 4;
|
|
|
|
err_code = ant_channel_init(&m_channel_config);
|
|
VERIFY_SUCCESS(err_code);
|
|
|
|
update_bcast_data();
|
|
|
|
err_code = sd_ant_channel_open(m_channel_config.channel_number);
|
|
VERIFY_SUCCESS(err_code);
|
|
|
|
NRF_LOG_DEBUG("ANT transport intialized");
|
|
|
|
m_initialized = true;
|
|
return err_code;
|
|
}
|
|
|
|
static uint32_t ant_dfu_close(nrf_dfu_transport_t const * p_exception)
|
|
{
|
|
uint32_t err_code = NRF_SUCCESS;
|
|
|
|
if (p_exception != &ant_dfu_transport && m_initialized)
|
|
{
|
|
NRF_LOG_DEBUG("Shutting down ANT DFU transport");
|
|
|
|
m_initialized = false;
|
|
|
|
err_code = sd_ant_channel_close(m_channel_config.channel_number);
|
|
VERIFY_SUCCESS(err_code);
|
|
|
|
uint8_t status;
|
|
do
|
|
{
|
|
// The initial wait is safe because the close command above would
|
|
// have generated at least 1 app event.
|
|
err_code = sd_app_evt_wait();
|
|
VERIFY_SUCCESS(err_code);
|
|
err_code = sd_ant_channel_status_get(
|
|
m_channel_config.channel_number, &status);
|
|
VERIFY_SUCCESS(err_code);
|
|
} while ((status & STATUS_CHANNEL_STATE_MASK) != STATUS_ASSIGNED_CHANNEL);
|
|
|
|
err_code = nrf_sdh_disable_request();
|
|
VERIFY_SUCCESS(err_code);
|
|
|
|
NRF_LOG_DEBUG("ANT transport disabled.");
|
|
}
|
|
|
|
return err_code;
|
|
}
|