Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
private/rci_fwstub.h
- Committer:
- spastor
- Date:
- 2013-12-03
- Revision:
- 1:908afea5a49d
- Parent:
- 0:1c358ea10753
File content as of revision 1:908afea5a49d:
/*
* Copyright (c) 2013 Digi International Inc.,
* All rights not expressly granted are reserved.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
* =======================================================================
*/
#define FW_TARGET_COUNT 1
#define FW_NO_CODE_SIZE -1
/**
* Firmware Upgrade Facility Opcodes
* @doc These are the valid opcodes for the Firmware Upgrade Facility
*/
typedef enum {
fw_target_list_opcode,
fw_info_request_opcode,
fw_info_response_opcode,
fw_download_request_opcode,
fw_download_response_opcode,
fw_binary_block_opcode,
fw_binary_block_ack_opcode,
fw_download_abort_opcode,
fw_download_complete_opcode,
fw_download_complete_response_opcode,
fw_target_reset_opcode,
fw_download_status_opcode,
fw_error_opcode
} fw_opcodes_t;
typedef union {
enum {
fw_invalid_target,
fw_invalid_opcode,
fw_invalid_msg
} error_status;
enum {
fw_user_abort,
fw_device_error,
fw_invalid_offset,
fw_invalid_data,
fw_hardware_error
} abort_status;
} fw_abort_status_t;
#define connector_firmware_status_download_configured_to_reject 0x06
/* Firmware message header format:
* ------------------------
* | 0 | 1 | 3... |
* ------------------------
* | opcode | target | data |
* ------------------------
*
*/
enum fw_message {
field_define(fw_message, opcode, uint8_t),
field_define(fw_message, target, uint8_t),
record_end(fw_message)
};
#define FW_MESSAGE_HEADER_SIZE record_bytes(fw_message)
#define FW_MESSAGE_RESPONSE_MAX_SIZE 16
typedef struct {
connector_data_t * connector_ptr;
connector_bool_t send_busy;
size_t response_size;
uint8_t response_buffer[FW_MESSAGE_RESPONSE_MAX_SIZE + PACKET_EDP_FACILITY_SIZE];
} connector_firmware_data_t;
/* abort and error message format:
* --------------------------
* | 0 | 1 | 2 |
* --------------------------
* | opcode | target | status |
* --------------------------
*
*/
enum fw_abort {
field_define(fw_abort, opcode, uint8_t),
field_define(fw_abort, target, uint8_t),
field_define(fw_abort, status, uint8_t),
record_end(fw_abort)
};
#define FW_ABORT_HEADER_SIZE record_bytes(fw_abort)
static connector_status_t send_fw_message(connector_firmware_data_t * const fw_ptr)
{
connector_status_t status;
status = tcp_initiate_send_facility_packet(fw_ptr->connector_ptr, fw_ptr->response_buffer, fw_ptr->response_size, E_MSG_FAC_FW_NUM, NULL, NULL);
fw_ptr->send_busy = (status == connector_pending) ? connector_true : connector_false;
return status;
}
static connector_status_t send_fw_abort(connector_firmware_data_t * const fw_ptr, uint8_t const target, uint8_t const msg_opcode, fw_abort_status_t const abort_status)
{
connector_status_t status;
uint8_t * fw_abort = GET_PACKET_DATA_POINTER(fw_ptr->response_buffer, PACKET_EDP_FACILITY_SIZE);
uint8_t abort_code = (uint8_t)abort_status.error_status;
ASSERT(abort_status.error_status <= UCHAR_MAX);
ASSERT((sizeof fw_ptr->response_buffer - PACKET_EDP_FACILITY_SIZE) > FW_ABORT_HEADER_SIZE);
/* build abort message */
message_store_u8(fw_abort, opcode, msg_opcode);
message_store_u8(fw_abort, target, target);
message_store_u8(fw_abort, status, abort_code);
fw_ptr->response_size = FW_ABORT_HEADER_SIZE;
status = send_fw_message(fw_ptr);
return status;
}
static connector_status_t process_fw_info_request(connector_firmware_data_t * const fw_ptr, uint8_t * const fw_message, uint16_t const length)
{
/* firmware info response message format:
* ---------------------------------------------------
* | 0 | 1 | 2 - 5 | 6 - 9 | 10 ... |
* ---------------------------------------------------
* | opcode | target | version | Available | Firmware |
* | | | | code size | ID string |
* ---------------------------------------------------
*
* Firmware ID string = [descr]0xa[file name spec]
*/
enum fw_info {
field_define(fw_info, opcode, uint8_t),
field_define(fw_info, target, uint8_t),
field_define(fw_info, version, uint32_t),
field_define(fw_info, code_size, uint32_t),
record_end(fw_info)
};
#define MAX_FW_INFO_REQUEST_LENGTH 2
connector_data_t * const connector_ptr = fw_ptr->connector_ptr;
connector_status_t status = connector_idle;
uint8_t const target = message_load_u8(fw_message, target);
connector_debug_printf("Firmware Facility: process info request\n");
/* parse firmware info request
* -----------------
* | 0 | 1 |
* -----------------
* | opcode | target |
* -----------------
*/
if (length != MAX_FW_INFO_REQUEST_LENGTH)
{
fw_abort_status_t fw_status;
connector_debug_printf("process_fw_info_request: invalid message length\n");
fw_status.error_status = fw_invalid_msg;
status = send_fw_abort(fw_ptr, target, fw_error_opcode, fw_status);
goto done;
}
/* let's build a response.
* build and send firmware info response
*/
{
#define FW_NOT_UPGRADABLE_DESCRIPTION "Non updateable firmware"
static const char fw_target_description[] = FW_NOT_UPGRADABLE_DESCRIPTION;
static const size_t fw_target_description_length = sizeof fw_target_description -1;
uint8_t * edp_header;
uint8_t * fw_info;
uint8_t * start_ptr;
size_t avail_length;
edp_header = tcp_get_packet_buffer(connector_ptr, E_MSG_FAC_FW_NUM, &fw_info, &avail_length);
if (edp_header == NULL)
{
status = connector_pending;
goto done;
}
start_ptr = fw_info;
ASSERT(avail_length > (record_bytes(fw_info) + fw_target_description_length));
message_store_u8(fw_info, opcode, fw_info_response_opcode);
message_store_u8(fw_info, target, 0);
message_store_be32(fw_info, version, rci_get_firmware_target_zero_version());
message_store_be32(fw_info, code_size, FW_NO_CODE_SIZE);
fw_info += record_bytes(fw_info);
memcpy(fw_info, fw_target_description, fw_target_description_length);
fw_info += fw_target_description_length;
*fw_info++ = '\n';
/* reset back to initial values */
status = tcp_initiate_send_facility_packet(connector_ptr, edp_header, fw_info-start_ptr, E_MSG_FAC_FW_NUM, tcp_release_packet_buffer, NULL);
if (status != connector_working)
{
tcp_release_packet_buffer(connector_ptr, edp_header, connector_working, NULL);
}
}
done:
return status;
}
static connector_status_t process_fw_download_request(connector_firmware_data_t * const fw_ptr, uint8_t * fw_download_request, uint16_t const length)
{
/* Firmware download request message format:
* -----------------------------------------------------------
* | 0 | 1 | 2 - 5 | 6 - 9 | 10... |
* -----------------------------------------------------------
* | opcode | target | version | code size | firmware ID string |
* ------------------------------------------------------------
*
* Firmware ID string: [label]0x0a[file name spec]0xa[file name]
*
* Call the callback with these values and send download request response.
*/
enum fw_download_request {
field_define(fw_download_request, opcode, uint8_t),
field_define(fw_download_request, target, uint8_t),
field_define(fw_download_request, version, uint32_t),
field_define(fw_download_request, code_size, uint32_t),
record_end(fw_download_request)
};
/* Firmware download response message format:
* ---------------------------------
* | 0 | 1 | 2 |
* ---------------------------------
* | opcode | target | response type |
* ---------------------------------
*
*/
enum fw_download_response {
field_define(fw_download_response, opcode, uint8_t),
field_define(fw_download_response, target, uint8_t),
field_define(fw_download_response, response_type, uint8_t),
record_end(fw_download_response)
};
connector_status_t status = connector_idle;
fw_abort_status_t response_status;
uint8_t const target_number = message_load_u8(fw_download_request, target);
if (length < record_bytes(fw_download_request))
{
connector_debug_printf("process_fw_download_request: invalid message length\n");
response_status.error_status = fw_invalid_msg;
status = send_fw_abort(fw_ptr, target_number, fw_error_opcode, response_status);
goto done;
}
{
/* get a buffer for sending a response */
uint8_t * fw_download_response = GET_PACKET_DATA_POINTER(fw_ptr->response_buffer, PACKET_EDP_FACILITY_SIZE);
ASSERT((sizeof fw_ptr->response_buffer - PACKET_EDP_FACILITY_SIZE) > record_bytes(fw_download_response));
/* send error firmware download response */
message_store_u8(fw_download_response, opcode, fw_download_response_opcode);
message_store_u8(fw_download_response, target, target_number);
message_store_u8(fw_download_response, response_type, connector_firmware_status_download_configured_to_reject);
fw_ptr->response_size = record_bytes(fw_download_response);
status = send_fw_message(fw_ptr);
}
done:
return status;
}
static connector_status_t fw_discovery(connector_data_t * const connector_ptr, void * const facility_data,
uint8_t * const packet, unsigned int * receive_timeout)
{
/* Firmware target list message format:
*
* --------------------------------------------------------
* | 0 | 1 | 2 - 5 | 6 ... |
* -------------------------------------------------------
* | opcode | target | version | Additional target-version |
* | | | | pairs |
* -------------------------------------------------------
*
*/
enum fw_target_list {
field_define(fw_target_list, opcode, uint8_t),
field_define(fw_target_list, target, uint8_t),
field_define(fw_target_list, version, uint32_t),
record_end(fw_target_list)
};
connector_status_t status = connector_idle;
UNUSED_PARAMETER(packet);
UNUSED_PARAMETER(receive_timeout);
UNUSED_PARAMETER(facility_data);
/* Construct a target list message.
*/
{
uint8_t * edp_header;
uint8_t * fw_target_list;
size_t avail_length;
/* get packet pointer for constructing target list info */
edp_header = tcp_get_packet_buffer(connector_ptr, E_MSG_FAC_FW_NUM, &fw_target_list, &avail_length);
if (edp_header == NULL)
{
status = connector_pending;
goto done;
}
ASSERT(avail_length >= record_bytes(fw_target_list));
message_store_u8(fw_target_list, opcode, fw_target_list_opcode);
{
uint8_t const target_number = 0; /* one target only */
message_store_u8(fw_target_list, target, target_number);
message_store_be32(fw_target_list, version, rci_get_firmware_target_zero_version());
}
status = tcp_initiate_send_facility_packet(connector_ptr, edp_header, record_bytes(fw_target_list),
E_MSG_FAC_FW_NUM, tcp_release_packet_buffer, NULL);
}
done:
return status;
}
static connector_status_t fw_process(connector_data_t * const connector_ptr, void * const facility_data,
uint8_t * const edp_header, unsigned int * const receive_timeout)
{
connector_status_t status = connector_idle;
connector_firmware_data_t * const fw_ptr = facility_data;
uint8_t opcode;
uint8_t target;
uint8_t * fw_message;
uint16_t length;
UNUSED_PARAMETER(receive_timeout);
fw_ptr->connector_ptr = connector_ptr;
if (edp_header == NULL)
{
goto done;
}
if (fw_ptr->send_busy == connector_true)
{
/* callback is already called for this message.
* We're here because we were unable to send a response
* message which already message is constructed in
* fw_ptr->response_buffer.
*/
status = send_fw_message(fw_ptr);
goto done;
}
length = message_load_be16(edp_header, length);
if (length < FW_MESSAGE_HEADER_SIZE)
{
connector_debug_printf("fw_process: invalid packet size %d\n", length);
goto done;
}
fw_message = GET_PACKET_DATA_POINTER(edp_header, PACKET_EDP_FACILITY_SIZE);
opcode = message_load_u8(fw_message, opcode);
target = message_load_u8(fw_message, target);
ASSERT(FW_TARGET_COUNT == 1);
if (target >= FW_TARGET_COUNT)
{
fw_abort_status_t fw_status;
connector_debug_printf("fw_process: invalid target\n");
fw_status.error_status = fw_invalid_target;
status = send_fw_abort(fw_ptr, target, fw_error_opcode, fw_status);
goto done;
}
switch(opcode)
{
case fw_info_request_opcode:
status = process_fw_info_request(fw_ptr, fw_message, length);
break;
case fw_download_request_opcode:
status = process_fw_download_request(fw_ptr, fw_message, length);
break;
case fw_download_complete_opcode:
case fw_binary_block_opcode:
/* We already reject the download request.
* so should not receive this packet.
*/
ASSERT(connector_false);
break;
case fw_download_abort_opcode:
break;
case fw_target_reset_opcode:
break;
default:
{
fw_abort_status_t fw_status;
fw_status.error_status = fw_invalid_opcode;
status = send_fw_abort(fw_ptr, target, fw_error_opcode, fw_status);
break;
}
}
done:
return status;
}
static connector_status_t connector_facility_firmware_delete(connector_data_t * const connector_ptr)
{
return del_facility_data(connector_ptr, E_MSG_FAC_FW_NUM);
}
static connector_status_t connector_facility_firmware_init(connector_data_t * const connector_ptr, unsigned int const facility_index)
{
connector_status_t status = connector_idle;
connector_firmware_data_t * fw_ptr;
/* Add firmware access facility to Device Cloud
*
* Make sure firmware access facility is not already created. If firmware
* access facility is already created, we probably reconnect to Device Cloud
* so just need to reset to initial state.
*
*/
fw_ptr = get_facility_data(connector_ptr, E_MSG_FAC_FW_NUM);
if (fw_ptr == NULL)
{
void * ptr;
status = add_facility_data(connector_ptr, facility_index, E_MSG_FAC_FW_NUM, &ptr, sizeof *fw_ptr);
if (status != connector_working || ptr == NULL)
{
goto done;
}
fw_ptr = ptr;
}
fw_ptr->send_busy = connector_false;
fw_ptr->connector_ptr = connector_ptr;
done:
return status;
}