Wi-SUN OAD¶
The application layer is responsible for plugging the OAD service. This section documents WiSUN stack specific implementation of OAD. This covers the high level protocol, the message structures, OAD image distributor and the target side software needed to handle OAD.
OAD Protocol¶
In the context of TI Wi-SUN stack, the distributor of OAD images is the Border Router in combination with the host. The OAD target is node flashed with the CoAP OAD application. The OAD module of this application handles the communication with the OAD image distributor, manages and stores the downloaded firmware images. The distributor initiates the OAD process. Then the target is responsible for requesting OAD image blocks, re-requesting when required and handle error conditions. Once OAD is complete, the OAD client will reset and start running with the new image.
![../_images/oad-architecture.png](../_images/oad-architecture.png)
Figure 60. OAD distributor and target¶
OAD Messages¶
These messages are exchanged between the distributor and target over the air with CoAP protocol.
OAD Firmware Request¶
The distributor sends a firmware request message to the target’s unicast address with the oad/fwv URI. The response from the target is the current software version number. This message is independent of the OAD protocol and is used independently by the distributor. The content of the response is as follows.:
/* MCU Boot image version Structure */
typedef struct __attribute__((__packed__)) {
uint8_t iv_major;
uint8_t iv_minor;
uint16_t iv_revision;
uint32_t iv_build_num;
} mcuboot_image_version_t;
/* OAD Firmware request response content */
typedef struct __attribute__((__packed__)) {
uint8_t img_id;
uint8_t platform_type;
mcuboot_image_version_t image_version;
} oad_fw_version_rsp_msg_t;
![../_images/fw_request.png](../_images/fw_request.png)
Figure 63. OAD Firmware Request(Distributor) <-> Response(Target)¶
OAD Notification Request¶
To initiate the OAD process, the distributor sends the OAD notification request to the target. The request contains information about the new candidate image, such as firmware image type and size, and block size to be used. The target device responds with OAD notification response.
/* OAD Notification Request */
typedef struct __attribute__((packed)){
uint8_t img_id;
uint8_t platform_type;
uint16_t block_len;
uint32_t image_len;
mcuboot_image_version_t image_version;
} oad_notif_req_msg_t;
OAD Notification Response¶
When the OAD target receives OAD notification request, it checks the content of the request and validates the image. It makes a decision whether to accept or reject the image and sends the response back to the distributor.
/* OAD Notification Response */
typedef struct __attribute__((__packed__)) {
uint8_t img_id;
uint8_t status;
} oad_notif_rsp_msg_t;
![../_images/oad_notif_request.png](../_images/oad_notif_request.png)
Figure 64. OAD Notification Request(Distributor) <-> Response(Target)¶
OAD Image Block Request¶
Once the target accepts the image, it will initiate the download process. The target sends image block requests to the distributor’s unicast address with the oad/img URI. The request sepcifies the block number of the image needed. These requests are done in a sequential order. Thus the target keeps track of the block number and requestest the next block, only when the current block is successfully downloaded. The target immediately requests the next block once the current block is downloaded. The OAD server does not keep track of the block number. If there is corrupted block or lost block, the target requests for the block again automatically.
/* OAD Block Request */
typedef struct __attribute__((packed)){
uint8_t imgId;
uint16_t blkNum;
uint16_t totalBlks;
} oad_imgBlockReq_t;
OAD Image Block Response¶
The distributor responds to valid block requests with a block of the OAD image with the block number specified by the request.
/* OAD Block Response */
typedef struct __attribute__((packed)){
uint8_t imgId;
uint16_t blkNum;
uint8_t data[OAD_BLOCK_SIZE];
} oad_imgBlockRsp_t;
![../_images/oad_image_download.png](../_images/oad_image_download.png)
Figure 65. OAD Block Request(Target) <-> OAD Block Repsonse (Distributor)¶
OAD Complete Request¶
When the target determines that it has received the last OAD image block, it sends an OAD complete request to the distributor. This is a block request as defined above with 0xFFFF(OAD_COMPLETE Flag) as the block id number. This tells the distributor to mark the OAD session as complete. The total blocks field in the block request structure is ignored for the OAD complete message.
OAD Complete Response¶
The distributor responds with an Ack called OAD complete response to the OAD complete request. This triggers a reset on the target and allows the target to boot to the newly downloaded image. Thereby, completing the OAD process.
![../_images/oad_complete.png](../_images/oad_complete.png)
Figure 66. OAD Completion Interaction¶
OAD Abort¶
The target can intiate an abort of the OAD process due to timing out of requests. The is done by sending a request to the unicast address of the distributor with the oad/abort URI.
OAD Target Module¶
The software on the target that enables OAD functionality is called the OAD module. This module implements the behavior described by the OAD Target State Machine.
The OAD module on that target has an event driven tasklet and callbacks that handle the requests to the OAD URIs.
OAD Tasklet¶
The OAD tasklet handles block transfer and OAD complete messages. It constructs and sends to OAD block request (OAD_BLOCK_REQ_EVT) and handles the OAD block response (OAD_BLOCK_RECV_EVT), issuing another OAD block request afterwards if needed. On last block received, it sends and handles the response for the OAD complete request (OAD_COMPLETE_EVT). It also handles the abort request from the target(OAD_ABORT_EVT) and retries during block transfers due to timeout(OAD_BLOCK_REQ_TIMEOUT_EVT).
/**
* @brief OAD tasklet event enums
*/
typedef enum oad_evt {
OAD_INIT_EVT = 0,
OAD_BLOCK_REQ_EVT = 1,
OAD_BLOCK_RECV_EVT = 2,
OAD_COMPLETE_EVT = 3,
OAD_ABORT_EVT = 4,
OAD_BLOCK_REQ_TIMEOUT_EVT = 5,
OAD_COMPLETE_TIMEOUT_EVT = 6,
} oad_evt_t;
/**
* @brief Initializes the CoAP OAD tasket which handles CoAP block transfer
*
* @param None.
*
* @return None.
*/
void oad_tasklet_start(void);
/**
* @brief Tasklet for handling OAD block request/response and OAD complete request/response.
* Handles OAD_BLOCK_REQ_EVT, OAD_BLOCK_RECV_EVT, OAD_COMPLETE_EVT, OAD_ABORT_EVT,
* OAD_BLOCK_REQ_TIMEOUT_EVT, and OAD_COMPLETE_TIMEOUT_EVT
*
* @param event - Event object containing event type to trigger
*
* @return None
*/
static void oad_tasklet(arm_event_s *event)
OAD CoAP Callbacks¶
There are multiple callbacks executing on the target reacting to requests and responses from the distributor. These process the incoming data and trigger events so that the tasklet can get unblocked.
/**
* @brief CoAP callback for the CoAP OAD service. Handles CoAP requests to the
* oad/fvw and oad/ntf URIs.
*
* @param service_id - Service ID for the CoAP server
* @param source_address - Source address of the CoAP request message
* @param source_port - Source port of the CoAP request message
* @param request_ptr - Pointer to the contents of the CoAP request message
*
* @return 0 on successfully processing CoAP request. -1 otherwise.
*/
int coap_oad_cb(int8_t service_id, uint8_t source_address[static 16],
uint16_t source_port, sn_coap_hdr_s *request_ptr);
/**
* @brief Callback for OAD image block response. Process the payload of the block
* response and trigger the OAD_BLOCK_RECV_EVT callback.
*
* @param service_id - Service ID for the CoAP block response
* @param source_address - Source address of the CoAP block response
* @param source_port - Source port of the CoAP block response
* @param response_ptr - Pointer to the contents of the CoAP block response
*
* @return 0 on successfully processing CoAP response. -1 otherwise.
*/
static int oad_img_rsp_cb(int8_t service_id, uint8_t source_address[static 16],
uint16_t source_port, sn_coap_hdr_s *response_ptr)
/**
* @brief Callback for OAD complete response. This triggers boot into the newly downloaded image.
*
* @param service_id - Service ID for the CoAP complete response
* @param source_address - Source address of the CoAP complete response
* @param source_port - Source port of the CoAP complete response
* @param request_ptr - Pointer to the contents of the CoAP complete response
*
* @return 0 on successfully processing CoAP response. -1 otherwise. Should reset before ever returning.
*/
static int oad_complete_rsp_cb(int8_t service_id, uint8_t source_address[static 16],
uint16_t source_port, sn_coap_hdr_s *response_ptr)
OAD Storage¶
The OAD module also handles the storage and provides device independent APIs to store the downloaded firmware image to flash. These APIs are the same for both on-chip and off-chip solutions.
/*********************************************************************
* @fn OADStorage_init
*
* @brief Initialize OAD flash storage interface.
*
* @param None.
*
* @return None.
*/
extern void OADStorage_init(void);
/*********************************************************************
* @fn OADStorage_imgBlockWrite
*
* @brief Write Image Block.
*
* @param blockNum - block number to be written
* @param blockLen - length of block (full block size)
* @param pBlockData - pointer to data to be written
* @param dataLen - length of data to be written (may be <= blockLen)
*
*
* @return status
*/
extern OADStorage_Status_t OADStorage_imgBlockWrite(uint32_t blockNum, uint16_t blockLen, uint8_t *pBlockData, uint8_t dataLen);
/*********************************************************************
* @fn OADStorage_eraseImg
*
* @brief Erases the required number of flash pages in preparation
* for a new image.
*
* @param image_len Length of flash to be erased to prepare for the
* new image.
*
* @return OADStorage_Status_t
*/
extern OADStorage_Status_t OADStorage_eraseImg(uint32_t imageLen);
/*********************************************************************
* @fn OADStorage_close
*
* @brief Releases the OAD flash storage interface.
*
* @param none
*
* @return none
*/
extern void OADStorage_close(void);
OAD Distributor¶
The OAD distributor is a system with an application processor(PC/AM64x) connected to a Wi-SUN Border Router as a Network Co-Processor.
TI Wi-SUN FAN pySpinel GitHub repo contains a python based CLI which now has the additional APIs that enable you to get firmware version from the target and initiate the OAD process. It also has additional logic to allow the host behave as a OAD Server that is described in OAD Distributor State Machine. This pyspinel application runs on both a windows or a linux host.
The APIs are mentioned here, but the usage examples can be accessed with the help command in the spinel-cli.
Function |
PySpinel API |
OAD Firmware Request |
getoadfwver |
OAD Notification Request |
startoad |
In addition to the APIs, the Spinel CLI also has the following functionalities implemented.
Token based CoAP Response Tracking¶
CoAP protocol provides the option of including an up to 8 byte token in each CoAP message. This token is echoed back in the response to the CoAP message to properly track which request maps to which response. See the CoAP RFC for details.
PySpinel CoAP implementation includes token support. This support will be utilized in firmware, notification, and block request/response messages to distinguish between responses.
In the case of received CoAP responses (triggered by CoAP requests sent by PySpinel):
Saved tokens are compared to the response token. In PySpinel, 3 tokens are saved: firmware version request, notification request, LED request (updated from regular coap_node implementation).
If there is a match, the response has been identified as a response to a specific request sent by PySpinel. Perform the appropriate action (usually different payload parsing and log output).
In the case of received CoAP requests (currently only OAD block requests):
Check the URI of the request and compare to supported URIs (oad/img for OAD block requests)
If there is a match, take the appropriate action. Send the appropriate CoAP response, echoing the token in the CoAP request.
Block Request handling¶
Handle the block request as follows:
Navigate to the appropriate byte of the OAD upgrade image as indicated by the requested block number and block size.
Assemble the OAD response payload by combining OAD image ID, number of bytes in block, and block contents.
Send the CoAP block response, echoing the token of the request as described above.
Response handling¶
Notification and Firmware Responses are processed by printing out the data of interest on the command line Notification response would have the decision of the target whether the distributor’s OAD request was accepted or rejected. Firmware Response would have the image ID, platform, OAD firmaware version number.