315 lines
11 KiB
C
315 lines
11 KiB
C
|
/* 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 "dfu_transport.h"
|
||
|
#include <stddef.h>
|
||
|
#include "dfu.h"
|
||
|
#include <dfu_types.h>
|
||
|
#include "app_error.h"
|
||
|
#include "app_util.h"
|
||
|
#include "hci_transport.h"
|
||
|
#include "app_timer.h"
|
||
|
#include "app_scheduler.h"
|
||
|
|
||
|
#define MAX_BUFFERS 4u /**< Maximum number of buffers that can be received queued without being consumed. */
|
||
|
|
||
|
/**
|
||
|
* defgroup Data Packet Queue Access Operation Macros
|
||
|
* @{
|
||
|
*/
|
||
|
|
||
|
/** Provides status showing if the queue is full or not. */
|
||
|
#define DATA_QUEUE_FULL() \
|
||
|
(((MAX_BUFFERS -1) == m_data_queue.count) ? true : false)
|
||
|
|
||
|
/** Provides status showing if the queue is empty or not */
|
||
|
#define DATA_QUEUE_EMPTY() \
|
||
|
((0 == m_data_queue.count) ? true : false)
|
||
|
|
||
|
/** Initializes an element of the data queue. */
|
||
|
#define DATA_QUEUE_ELEMENT_INIT(i) \
|
||
|
m_data_queue.data_packet[(i)].packet_type = INVALID_PACKET
|
||
|
|
||
|
/** Sets the packet type of an element of the data queue. */
|
||
|
#define DATA_QUEUE_ELEMENT_SET_PTYPE(i, t) \
|
||
|
m_data_queue.data_packet[(i)].packet_type = (t)
|
||
|
|
||
|
/** Copies a data packet pointer of an element of the data queue. */
|
||
|
#define DATA_QUEUE_ELEMENT_COPY_PDATA(i, dp) \
|
||
|
m_data_queue.data_packet[(i)].params.data_packet.p_data_packet = (uint32_t *)(dp)
|
||
|
|
||
|
/** Sets the packet length of an element in the data queue. */
|
||
|
#define DATA_QUEUE_ELEMENT_SET_PLEN(i, l) \
|
||
|
m_data_queue.data_packet[(i)].params.data_packet.packet_length = (l)
|
||
|
|
||
|
/** Gets a data packet pointer of an element in the data queue. */
|
||
|
#define DATA_QUEUE_ELEMENT_GET_PDATA(i) \
|
||
|
(m_data_queue.data_packet[(i)].params.data_packet.p_data_packet)
|
||
|
|
||
|
/** Gets the packet type of an element in the data queue. */
|
||
|
#define DATA_QUEUE_ELEMENT_GET_PTYPE(i) \
|
||
|
m_data_queue.data_packet[(i)].packet_type
|
||
|
|
||
|
/* @} */
|
||
|
|
||
|
/** Abstracts data packet queue */
|
||
|
typedef struct
|
||
|
{
|
||
|
dfu_update_packet_t data_packet[MAX_BUFFERS]; /**< Bootloader data packets used when processing data from the UART. */
|
||
|
volatile uint8_t count; /**< Counter to maintain number of elements in the queue. */
|
||
|
} dfu_data_queue_t;
|
||
|
|
||
|
static dfu_data_queue_t m_data_queue; /**< Received-data packet queue. */
|
||
|
|
||
|
/**@brief Initializes an element of the data buffer queue.
|
||
|
*
|
||
|
* @param[in] element_index index of the element.
|
||
|
*/
|
||
|
static void data_queue_element_init (uint8_t element_index)
|
||
|
{
|
||
|
DATA_QUEUE_ELEMENT_INIT(element_index);
|
||
|
DATA_QUEUE_ELEMENT_SET_PTYPE(element_index, INVALID_PACKET);
|
||
|
DATA_QUEUE_ELEMENT_COPY_PDATA(element_index, NULL);
|
||
|
DATA_QUEUE_ELEMENT_SET_PLEN(element_index, 0);
|
||
|
}
|
||
|
|
||
|
/** Initializes data buffer queue */
|
||
|
static void data_queue_init(void)
|
||
|
{
|
||
|
uint32_t index;
|
||
|
|
||
|
m_data_queue.count = 0;
|
||
|
|
||
|
for (index = 0; index < MAX_BUFFERS; index++)
|
||
|
{
|
||
|
data_queue_element_init(index);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**@brief Function for freeing an element.
|
||
|
*
|
||
|
* @param[in] element_index index of the element.
|
||
|
*/
|
||
|
static uint32_t data_queue_element_free(uint8_t element_index)
|
||
|
{
|
||
|
uint8_t * p_data;
|
||
|
uint32_t retval;
|
||
|
|
||
|
retval = NRF_ERROR_INVALID_PARAM;
|
||
|
|
||
|
if (MAX_BUFFERS > element_index)
|
||
|
{
|
||
|
p_data = (uint8_t *)DATA_QUEUE_ELEMENT_GET_PDATA(element_index);
|
||
|
if (INVALID_PACKET != DATA_QUEUE_ELEMENT_GET_PTYPE(element_index))
|
||
|
{
|
||
|
m_data_queue.count--;
|
||
|
data_queue_element_init (element_index);
|
||
|
retval = hci_transport_rx_pkt_consume((p_data - 4));
|
||
|
APP_ERROR_CHECK(retval);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return NRF_ERROR_INVALID_PARAM;
|
||
|
}
|
||
|
|
||
|
return NRF_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**@brief Function for Allocating element.
|
||
|
*
|
||
|
* @param[in] packet_type packet type.
|
||
|
* @param[out] p_element_index index of the element.
|
||
|
*/
|
||
|
static uint32_t data_queue_element_alloc(uint8_t * p_element_index, uint8_t packet_type)
|
||
|
{
|
||
|
uint32_t retval;
|
||
|
uint32_t index;
|
||
|
|
||
|
retval = NRF_ERROR_NO_MEM;
|
||
|
|
||
|
if (INVALID_PACKET == packet_type)
|
||
|
{
|
||
|
retval = NRF_ERROR_INVALID_PARAM;
|
||
|
}
|
||
|
else if (true == DATA_QUEUE_FULL())
|
||
|
{
|
||
|
retval = NRF_ERROR_NO_MEM;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (index = 0; index < MAX_BUFFERS; index++)
|
||
|
{
|
||
|
if (INVALID_PACKET == DATA_QUEUE_ELEMENT_GET_PTYPE(index))
|
||
|
{
|
||
|
// Found a free element: allocate, and end search.
|
||
|
*p_element_index = index;
|
||
|
DATA_QUEUE_ELEMENT_SET_PTYPE(index, packet_type);
|
||
|
retval = NRF_SUCCESS;
|
||
|
m_data_queue.count++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
// Flush everything on disconnect or stop.
|
||
|
static void data_queue_flush(void)
|
||
|
{
|
||
|
uint32_t index;
|
||
|
|
||
|
for (index = 0; index < MAX_BUFFERS; index++)
|
||
|
{
|
||
|
// In this case it does not matter if free succeeded or not as data packets are being flushed because DFU Trnsport was closed
|
||
|
(void)data_queue_element_free(index);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**@brief Function for handling the callback events from the dfu module.
|
||
|
* Callbacks are expected when \ref dfu_data_pkt_handle has been executed.
|
||
|
*
|
||
|
* @param[in] packet Packet type for which this callback is related. START_PACKET, DATA_PACKET.
|
||
|
* @param[in] result Operation result code. NRF_SUCCESS when a queued operation was successful.
|
||
|
* @param[in] p_data Pointer to the data to which the operation is related.
|
||
|
*/
|
||
|
static void dfu_cb_handler(uint32_t packet, uint32_t result, uint8_t * p_data)
|
||
|
{
|
||
|
APP_ERROR_CHECK(result);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void process_dfu_packet(void * p_event_data, uint16_t event_size)
|
||
|
{
|
||
|
uint32_t retval;
|
||
|
uint32_t index;
|
||
|
dfu_update_packet_t * packet;
|
||
|
|
||
|
// Adafruit modification for startup dfu
|
||
|
extern bool forced_startup_dfu_packet_received;
|
||
|
forced_startup_dfu_packet_received = true;
|
||
|
|
||
|
while (false == DATA_QUEUE_EMPTY())
|
||
|
{
|
||
|
// Fetch the element to be processed.
|
||
|
for (index = 0; index < MAX_BUFFERS ; index++)
|
||
|
{
|
||
|
packet = &m_data_queue.data_packet[index];
|
||
|
if (INVALID_PACKET != packet->packet_type)
|
||
|
{
|
||
|
extern void blinky_fast_set(bool isFast);
|
||
|
switch (DATA_QUEUE_ELEMENT_GET_PTYPE(index))
|
||
|
{
|
||
|
case DATA_PACKET:
|
||
|
(void)dfu_data_pkt_handle(packet);
|
||
|
break;
|
||
|
|
||
|
case START_PACKET:
|
||
|
packet->params.start_packet =
|
||
|
(dfu_start_packet_t*)packet->params.data_packet.p_data_packet;
|
||
|
retval = dfu_start_pkt_handle(packet);
|
||
|
APP_ERROR_CHECK(retval);
|
||
|
break;
|
||
|
|
||
|
case INIT_PACKET:
|
||
|
(void)dfu_init_pkt_handle(packet);
|
||
|
retval = dfu_init_pkt_complete();
|
||
|
APP_ERROR_CHECK(retval);
|
||
|
|
||
|
blinky_fast_set(true);
|
||
|
break;
|
||
|
|
||
|
case STOP_DATA_PACKET:
|
||
|
(void)dfu_image_validate();
|
||
|
(void)dfu_image_activate();
|
||
|
|
||
|
blinky_fast_set(false);
|
||
|
|
||
|
// Break the loop by returning.
|
||
|
return;
|
||
|
|
||
|
default:
|
||
|
// No implementation needed.
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Free the processed element.
|
||
|
retval = data_queue_element_free(index);
|
||
|
APP_ERROR_CHECK(retval);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void rpc_transport_event_handler(hci_transport_evt_t event)
|
||
|
{
|
||
|
uint32_t retval;
|
||
|
uint16_t rpc_cmd_length_read = 0;
|
||
|
uint8_t * p_rpc_cmd_buffer = NULL;
|
||
|
uint8_t element_index;
|
||
|
|
||
|
retval = hci_transport_rx_pkt_extract(&p_rpc_cmd_buffer, &rpc_cmd_length_read);
|
||
|
if (NRF_SUCCESS == retval)
|
||
|
{
|
||
|
// Verify if the data queue can buffer the packet.
|
||
|
retval = data_queue_element_alloc(&element_index, p_rpc_cmd_buffer[0]);
|
||
|
if (NRF_SUCCESS == retval)
|
||
|
{
|
||
|
//subtract 1 since we are interested in payload length and not the type field.
|
||
|
DATA_QUEUE_ELEMENT_SET_PLEN(element_index,(rpc_cmd_length_read / sizeof(uint32_t)) - 1);
|
||
|
DATA_QUEUE_ELEMENT_COPY_PDATA(element_index, &p_rpc_cmd_buffer[4]);
|
||
|
retval = app_sched_event_put(NULL, 0, process_dfu_packet);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (p_rpc_cmd_buffer != NULL && NRF_SUCCESS != retval)
|
||
|
{
|
||
|
// Free the packet that could not be processed.
|
||
|
retval = hci_transport_rx_pkt_consume(p_rpc_cmd_buffer);
|
||
|
APP_ERROR_CHECK(retval);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
uint32_t dfu_transport_serial_update_start(void)
|
||
|
{
|
||
|
uint32_t err_code;
|
||
|
|
||
|
// Initialize data buffer queue.
|
||
|
data_queue_init();
|
||
|
|
||
|
dfu_register_callback(dfu_cb_handler);
|
||
|
|
||
|
// Open transport layer.
|
||
|
err_code = hci_transport_open();
|
||
|
APP_ERROR_CHECK(err_code);
|
||
|
|
||
|
// Register callback to be run when commands have been received by the transport layer.
|
||
|
err_code = hci_transport_evt_handler_reg(rpc_transport_event_handler);
|
||
|
APP_ERROR_CHECK(err_code);
|
||
|
|
||
|
return NRF_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
uint32_t dfu_transport_serial_close(void)
|
||
|
{
|
||
|
// Remove all buffered packets.
|
||
|
data_queue_flush();
|
||
|
|
||
|
return hci_transport_close();
|
||
|
}
|
||
|
|