Sebastián Pastor / EtheriosCloudConnector
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers connector_firmware.h Source File

connector_firmware.h

00001 /*
00002  * Copyright (c) 2013 Digi International Inc.,
00003  * All rights not expressly granted are reserved.
00004  *
00005  * This Source Code Form is subject to the terms of the Mozilla Public
00006  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
00007  * You can obtain one at http://mozilla.org/MPL/2.0/.
00008  *
00009  * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
00010  * =======================================================================
00011  */
00012 
00013 #define FW_ID_STRING_LENGTH     128  /* bytes */
00014 
00015 /* time to send target list to keep download alive */
00016 #define FW_TARGET_LIST_MSG_INTERVAL_IN_SECONDS     30
00017 
00018 /**
00019  * Firmware Upgrade Facility Opcodes
00020  * @doc These are the valid opcodes for the Firmware Upgrade Facility
00021  */
00022 typedef enum {
00023     fw_target_list_opcode,
00024     fw_info_request_opcode,
00025     fw_info_response_opcode,
00026     fw_download_request_opcode,
00027     fw_download_response_opcode,
00028     fw_binary_block_opcode,
00029     fw_binary_block_ack_opcode,
00030     fw_download_abort_opcode,
00031     fw_download_complete_opcode,
00032     fw_download_complete_response_opcode,
00033     fw_target_reset_opcode,
00034     fw_download_status_opcode,
00035     fw_error_opcode
00036 } fw_opcodes_t;
00037 
00038 typedef union {
00039     enum {
00040         fw_invalid_target,
00041         fw_invalid_opcode,
00042         fw_invalid_msg
00043     } error_status;
00044 
00045     enum {
00046         fw_user_abort,
00047         fw_device_error,
00048         fw_invalid_offset,
00049         fw_invalid_data,
00050         fw_hardware_error
00051     } abort_status;
00052 
00053     connector_firmware_status_t user_status;
00054 
00055 } fw_abort_status_t;
00056 
00057 /* Firmware message header format:
00058  *  ------------------------
00059  * |   0    |   1    | 3... |
00060  *  ------------------------
00061  * | opcode | target | data |
00062  *  ------------------------
00063  *
00064  */
00065 enum fw_message {
00066     field_define(fw_message, opcode, uint8_t),
00067     field_define(fw_message, target, uint8_t),
00068     record_end(fw_message)
00069 };
00070 #define FW_MESSAGE_HEADER_SIZE record_bytes(fw_message)
00071 #define FW_MESSAGE_RESPONSE_MAX_SIZE    16
00072 
00073 enum fw_target_list_hdr {
00074     field_define(fw_target_list, opcode, uint8_t)
00075 };
00076 
00077 /* target + version pairs format: */
00078 enum fw_target_list{
00079     field_define(fw_target_list, target, uint8_t),
00080     field_define(fw_target_list, version, uint32_t),
00081     record_end(fw_target_list)
00082 };
00083 
00084 #define FW_VERSION_NUMBER(version)  (MAKE32_4(version.major, version.minor, version.revision, version.build))
00085 
00086 static size_t const target_list_header_size = field_named_data(fw_target_list, opcode, size);
00087 static size_t const target_list_size = record_bytes(fw_target_list);
00088 
00089 typedef struct {
00090     connector_data_t * connector_ptr;
00091     unsigned long last_fw_keepalive_sent_time;
00092 
00093     size_t desc_length;
00094     size_t spec_length;
00095     size_t  response_size;
00096     connector_bool_t send_busy;
00097     connector_bool_t update_started;
00098     connector_bool_t fw_keepalive_start;
00099     connector_firmware_info_t target_info;
00100 
00101     uint8_t response_buffer[FW_MESSAGE_RESPONSE_MAX_SIZE + PACKET_EDP_FACILITY_SIZE];
00102     uint8_t target_count;
00103 } connector_firmware_data_t;
00104 
00105 #if defined CONNECTOR_RCI_SERVICE
00106 static connector_status_t confirm_fw_version(connector_firmware_data_t * const fw_ptr, uint8_t target_number, connector_firmware_version_t const version)
00107 {
00108     connector_status_t result = connector_working;
00109     uint32_t const version_number = FW_VERSION_NUMBER(version);
00110 
00111     if (target_number == 0 && version_number != rci_get_firmware_target_zero_version())
00112     {
00113         connector_data_t * const connector_ptr = fw_ptr->connector_ptr;
00114         connector_request_id_t request_id;
00115 
00116         connector_debug_printf("confirm_fw_version: 0x%X != FIRMWARE_TARGET_ZERO_VERSION (0x%X)\n", version_number, rci_get_firmware_target_zero_version());
00117         request_id.firmware_request = connector_request_id_firmware_info;
00118         if (notify_error_status(connector_ptr->callback, connector_class_id_firmware, request_id, connector_bad_version) != connector_working)
00119         {
00120             result = connector_abort;
00121         }
00122     }
00123     return result;
00124 }
00125 #endif
00126 
00127 static connector_status_t get_fw_config(connector_firmware_data_t * const fw_ptr,
00128                                         connector_request_id_firmware_t const fw_request_id,
00129                                         void * const data)
00130 {
00131 
00132     connector_data_t * const connector_ptr = fw_ptr->connector_ptr;
00133     connector_status_t result = connector_working;
00134 
00135     unsigned long end_time_stamp = 0;
00136 
00137     {
00138         connector_request_id_t request_id;
00139         connector_callback_status_t status;
00140 
00141         request_id.firmware_request = fw_request_id;
00142         status = connector_callback(connector_ptr->callback, connector_class_id_firmware, request_id, data);
00143 
00144         if (get_system_time(connector_ptr, &end_time_stamp) != connector_working)
00145         {
00146             result = connector_abort;
00147             goto done;
00148         }
00149 
00150         switch (status)
00151         {
00152         case connector_callback_continue:
00153             break;
00154         case connector_callback_busy:
00155             result = connector_pending;
00156             break;
00157         case connector_callback_abort:
00158         case connector_callback_unrecognized:
00159         case connector_callback_error:
00160             result = connector_abort;
00161             goto done;
00162         }
00163     }
00164 
00165     if (result == connector_pending && fw_ptr->last_fw_keepalive_sent_time > 0)
00166     {
00167         /*
00168          * Check whether we need to send target list message
00169          * to keep connection alive.
00170          */
00171         fw_ptr->fw_keepalive_start = ((end_time_stamp - fw_ptr->last_fw_keepalive_sent_time) >= FW_TARGET_LIST_MSG_INTERVAL_IN_SECONDS) ? connector_true : connector_false;
00172     }
00173     else
00174     {
00175         fw_ptr->fw_keepalive_start = connector_false;
00176     }
00177 
00178 done:
00179     return result;
00180 }
00181 
00182 static fw_abort_status_t get_abort_status_code(connector_firmware_status_t const status)
00183 {
00184     fw_abort_status_t code;
00185 
00186     code.abort_status = fw_user_abort;
00187 
00188     /* convert status to abort status code for abort message */
00189     switch (status)
00190     {
00191     case connector_firmware_status_user_abort:
00192         code.abort_status = fw_user_abort;
00193         break;
00194     case connector_firmware_status_invalid_offset:
00195         code.abort_status = fw_invalid_offset;
00196         break;
00197     case connector_firmware_status_invalid_data:
00198         code.abort_status = fw_invalid_data;
00199         break;
00200     case connector_firmware_status_hardware_error:
00201         code.abort_status = fw_hardware_error;
00202         break;
00203     case connector_firmware_status_device_error:
00204     case connector_firmware_status_download_denied:
00205     case connector_firmware_status_download_invalid_size:
00206     case connector_firmware_status_download_invalid_version:
00207     case connector_firmware_status_download_unauthenticated:
00208     case connector_firmware_status_download_not_allowed:
00209     case connector_firmware_status_download_configured_to_reject:
00210     case connector_firmware_status_encountered_error:
00211         /* not abort status so default to device error */
00212         code.abort_status = fw_device_error;
00213         break;
00214     case connector_firmware_status_success:
00215         ASSERT(connector_false);
00216         break;
00217 
00218     }
00219     return code;
00220 }
00221 
00222 /* abort and error message format:
00223  *  --------------------------
00224  * |   0    |    1   |    2   |
00225  *  --------------------------
00226  * | opcode | target | status |
00227  *  --------------------------
00228  *
00229  */
00230 enum fw_abort {
00231     field_define(fw_abort, opcode, uint8_t),
00232     field_define(fw_abort, target, uint8_t),
00233     field_define(fw_abort, status, uint8_t),
00234     record_end(fw_abort)
00235 };
00236 
00237 #define FW_ABORT_HEADER_SIZE    record_bytes(fw_abort)
00238 
00239 static connector_status_t send_fw_message(connector_firmware_data_t * const fw_ptr)
00240 {
00241 
00242     connector_status_t result;
00243 
00244     result = tcp_initiate_send_facility_packet(fw_ptr->connector_ptr, fw_ptr->response_buffer, fw_ptr->response_size, E_MSG_FAC_FW_NUM, NULL, NULL);
00245     fw_ptr->send_busy = (result == connector_pending) ? connector_true : connector_false;
00246     return result;
00247 
00248 }
00249 
00250 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)
00251 {
00252 
00253     connector_status_t result;
00254 
00255     uint8_t * fw_abort = GET_PACKET_DATA_POINTER(fw_ptr->response_buffer, PACKET_EDP_FACILITY_SIZE);
00256     uint8_t abort_code = (uint8_t)abort_status.error_status;
00257 
00258     ASSERT(abort_status.error_status <= UCHAR_MAX);
00259 
00260     /* need to adjust abort status code in the fw_status_t */
00261     if (msg_opcode != fw_error_opcode)
00262     {
00263         fw_abort_status_t status;
00264         status = get_abort_status_code(abort_status.user_status);
00265 
00266         ASSERT(status.abort_status <= UCHAR_MAX);
00267         abort_code = (uint8_t)status.abort_status;
00268 
00269     }
00270 
00271     ASSERT((sizeof fw_ptr->response_buffer - PACKET_EDP_FACILITY_SIZE) > FW_ABORT_HEADER_SIZE);
00272 
00273     /* build abort message */
00274     message_store_u8(fw_abort, opcode, msg_opcode);
00275     message_store_u8(fw_abort, target, target);
00276     message_store_u8(fw_abort, status, abort_code);
00277 
00278     fw_ptr->response_size = FW_ABORT_HEADER_SIZE;
00279     result = send_fw_message(fw_ptr);
00280 
00281     if (fw_ptr->target_info.target_number == target)
00282     {
00283         fw_ptr->update_started = connector_false;
00284     }
00285     return result;
00286 
00287 }
00288 
00289 static connector_status_t  process_fw_info_request(connector_firmware_data_t * const fw_ptr, uint8_t * const fw_message, uint16_t const length)
00290 {
00291 /* firmware info response message format:
00292  *  ---------------------------------------------------
00293  * |   0    |    1   |  2 - 5  |  6 - 9    |  10 ...   |
00294  *  ---------------------------------------------------
00295  * | opcode | target | version | Available | Firmware  |
00296  * |        |        |         | code size | ID string |
00297  *  ---------------------------------------------------
00298  *
00299  *  Firmware ID string = [descr]0xa[file name spec]
00300 */
00301 enum fw_info {
00302     field_define(fw_info, opcode, uint8_t),
00303     field_define(fw_info, target, uint8_t),
00304     field_define(fw_info, version, uint32_t),
00305     field_define(fw_info, code_size, uint32_t),
00306     record_end(fw_info)
00307 };
00308 
00309 #define MAX_FW_INFO_REQUEST_LENGTH  2
00310 
00311     connector_data_t * const connector_ptr = fw_ptr->connector_ptr;
00312     connector_status_t result = connector_idle;
00313     uint8_t const target = message_load_u8(fw_message, target);
00314 
00315     connector_debug_printf("Firmware Facility: process info request\n");
00316     /* parse firmware info request
00317      *  -----------------
00318      * |   0    |    1   |
00319      *  -----------------
00320      * | opcode | target |
00321      *  -----------------
00322      */
00323     fw_ptr->last_fw_keepalive_sent_time = 0;
00324     fw_ptr->fw_keepalive_start = connector_false;
00325 
00326     if (length != MAX_FW_INFO_REQUEST_LENGTH)
00327     {
00328         fw_abort_status_t fw_status;
00329         connector_debug_printf("process_fw_info_request: invalid message length\n");
00330 
00331         fw_status.error_status = fw_invalid_msg;
00332         result = send_fw_abort(fw_ptr, target, fw_error_opcode, fw_status);
00333         goto done;
00334 
00335     }
00336 
00337     /* let's get and build target info response */
00338     {
00339         connector_firmware_info_t * firmware_info = &fw_ptr->target_info;
00340 
00341         firmware_info->target_number = target;
00342 
00343         result = get_fw_config(fw_ptr, connector_request_id_firmware_info, firmware_info);
00344         if (result != connector_working)
00345         {
00346             goto done;
00347         }
00348 #if defined CONNECTOR_RCI_SERVICE
00349         else
00350         {
00351            result = confirm_fw_version(fw_ptr, firmware_info->target_number, firmware_info->version);
00352         }
00353 #endif
00354         fw_ptr->desc_length = strlen(firmware_info->description);
00355         fw_ptr->spec_length = strlen(firmware_info->filespec);
00356 
00357         if ((fw_ptr->desc_length + fw_ptr->spec_length) > (FW_ID_STRING_LENGTH -1))
00358         {
00359             connector_request_id_t request_id;
00360 
00361             request_id.firmware_request = connector_request_id_firmware_info;
00362             connector_debug_printf("process_fw_info_request: description length = %lu + name spec length = %lu\n",
00363                                     (unsigned long int)fw_ptr->desc_length, (unsigned long int)fw_ptr->spec_length);
00364             notify_error_status(connector_ptr->callback, connector_class_id_firmware, request_id, connector_invalid_data_size);
00365             fw_ptr->desc_length = 0;
00366             fw_ptr->spec_length = 0;
00367             result = connector_abort;
00368         }
00369     }
00370 
00371     /* let's build a response.
00372      * build and send firmware info response
00373     */
00374     {
00375         connector_firmware_info_t * firmware_info = &fw_ptr->target_info;
00376 
00377         uint8_t * edp_header;
00378         uint8_t * fw_info;
00379         uint8_t * start_ptr;
00380         size_t avail_length;
00381 
00382         edp_header = tcp_get_packet_buffer(connector_ptr, E_MSG_FAC_FW_NUM, &fw_info, &avail_length);
00383         if (edp_header == NULL)
00384         {
00385             result = connector_pending;
00386             goto done;
00387         }
00388         start_ptr = fw_info;
00389 
00390         ASSERT(avail_length > (record_bytes(fw_info) + fw_ptr->desc_length + fw_ptr->spec_length + 1));
00391 
00392         message_store_u8(fw_info, opcode, fw_info_response_opcode);
00393         message_store_u8(fw_info, target, target);
00394         message_store_be32(fw_info, version, FW_VERSION_NUMBER(firmware_info->version));
00395         message_store_be32(fw_info, code_size, INT32_C(0));
00396         fw_info += record_bytes(fw_info);
00397 
00398         connector_debug_printf("firmware description = %d %s %s\n", fw_ptr->desc_length, firmware_info->description, firmware_info->filespec);
00399         if (firmware_info->description != NULL)
00400         {
00401             memcpy(fw_info, firmware_info->description, fw_ptr->desc_length);
00402             fw_info += fw_ptr->desc_length;
00403         }
00404         *fw_info++ = '\n';
00405 
00406         if (firmware_info->filespec != NULL)
00407         {
00408             memcpy(fw_info, firmware_info->filespec, fw_ptr->spec_length);
00409             fw_info += fw_ptr->spec_length;
00410         }
00411 
00412         /* reset back to initial values */
00413         firmware_info->version.major = 0;
00414         firmware_info->version.minor = 0;
00415         firmware_info->version.revision = 0;
00416         firmware_info->version.build = 0;
00417         fw_ptr->desc_length = 0;
00418         fw_ptr->spec_length = 0;
00419 
00420         {
00421             size_t const send_packet_length = (size_t)(fw_info-start_ptr);
00422 
00423             result = tcp_initiate_send_facility_packet(connector_ptr, edp_header, send_packet_length, E_MSG_FAC_FW_NUM, tcp_release_packet_buffer, NULL);
00424 
00425             if (result != connector_working)
00426             {
00427                 tcp_release_packet_buffer(connector_ptr, edp_header, connector_working, NULL);
00428             }
00429         }
00430     }
00431 
00432 done:
00433     return result;
00434 }
00435 
00436 static connector_status_t process_fw_download_request(connector_firmware_data_t * const fw_ptr, uint8_t * fw_download_request, uint16_t const length)
00437 {
00438 #define FW_STRING_ID_ITEMS  2
00439 
00440 /* Firmware download request message format:
00441  *  -----------------------------------------------------------
00442  * |   0    |   1    |  2 - 5  |  6 - 9    |  10...             |
00443  *  -----------------------------------------------------------
00444  * | opcode | target | version | code size | firmware ID string |
00445  *  ------------------------------------------------------------
00446  *
00447  *  Firmware ID string: [label]0x0a[file name spec]0xa[file name]
00448  *
00449  *  Call the callback with these values and send download request response.
00450  */
00451 enum fw_download_request {
00452     field_define(fw_download_request, opcode, uint8_t),
00453     field_define(fw_download_request, target, uint8_t),
00454     field_define(fw_download_request, version, uint32_t),
00455     field_define(fw_download_request, code_size, uint32_t),
00456     record_end(fw_download_request)
00457 };
00458 
00459 
00460 /* Firmware download response message format:
00461  *  ---------------------------------
00462  * |  0     |   1    |     2         |
00463  *  ---------------------------------
00464  * | opcode | target | response type |
00465  *  ---------------------------------
00466  *
00467  */
00468 enum fw_download_response {
00469     field_define(fw_download_response, opcode, uint8_t),
00470     field_define(fw_download_response, target, uint8_t),
00471     field_define(fw_download_response, response_type, uint8_t),
00472     record_end(fw_download_response)
00473 };
00474 
00475     connector_status_t result = connector_working;
00476     connector_firmware_download_start_t download_request;
00477     fw_abort_status_t response_status;
00478     size_t string_id_length;
00479 
00480     uint8_t abort_opcode = fw_download_abort_opcode;
00481 
00482     download_request.target_number = message_load_u8(fw_download_request, target);
00483 
00484     response_status.user_status = connector_firmware_status_device_error;
00485     if (length < record_bytes(fw_download_request))
00486     {
00487         connector_debug_printf("process_fw_download_request: invalid message length\n");
00488         abort_opcode = fw_error_opcode;
00489         response_status.error_status = fw_invalid_msg;
00490         goto error;
00491     }
00492 
00493     if (fw_ptr->update_started == connector_true)
00494     {
00495         connector_debug_printf("process_fw_download_request: cannot start another firmware update target %d\n", download_request.target_number);
00496         goto error;
00497     }
00498 
00499     /* Parse firmware download request. Then, call the callback
00500      * with these values and send download request response.
00501      */
00502 #if (CONNECTOR_VERSION >= 0x02010000)
00503     download_request.code_size = message_load_be32(fw_download_request, code_size);
00504 #endif
00505 
00506     string_id_length = (size_t)(length - record_bytes(fw_download_request));
00507 
00508     {
00509         char * string_id_ptr = (char *)fw_download_request;
00510         unsigned int i;
00511 
00512         string_id_ptr += record_bytes(fw_download_request);
00513 
00514         /* parse firmware ID String for label and filename spec
00515          * separated by 0x0a.
00516          */
00517         for (i=0; i < FW_STRING_ID_ITEMS; i++)
00518         {
00519             char * end_ptr;
00520 
00521             end_ptr = strchr(string_id_ptr, '\n');
00522             if (end_ptr != NULL) *end_ptr = '\0';
00523             {
00524                 size_t const label_length = (size_t)(end_ptr - string_id_ptr);
00525                 string_id_length -= (UINT32_C(1) + label_length);
00526             }
00527             string_id_ptr++;
00528         }
00529         /* get filename */
00530         download_request.filename = string_id_ptr;
00531         *(download_request.filename + string_id_length) = '\0';
00532     }
00533 
00534     /* call callback */
00535     download_request.status = connector_firmware_status_success;
00536     result = get_fw_config(fw_ptr, connector_request_id_firmware_download_start, &download_request);
00537     if (result == connector_working) response_status.user_status = download_request.status;
00538 
00539 error:
00540     if (result != connector_pending)
00541     {
00542         uint8_t * fw_download_response = GET_PACKET_DATA_POINTER(fw_ptr->response_buffer, PACKET_EDP_FACILITY_SIZE);
00543 
00544         if (response_status.user_status >= connector_firmware_status_user_abort)
00545         {
00546             send_fw_abort(fw_ptr, download_request.target_number, abort_opcode, response_status);
00547             goto done;
00548         }
00549 
00550         /* get a buffer for sending a response */
00551         ASSERT((sizeof fw_ptr->response_buffer - PACKET_EDP_FACILITY_SIZE) > record_bytes(fw_download_response));
00552 
00553         /* send firmware download response */
00554         message_store_u8(fw_download_response, opcode, fw_download_response_opcode);
00555         message_store_u8(fw_download_response, target, download_request.target_number);
00556         message_store_u8(fw_download_response, response_type, download_request.status);
00557 
00558         fw_ptr->response_size = record_bytes(fw_download_response);
00559 
00560         send_fw_message(fw_ptr);
00561         if (download_request.status == connector_firmware_status_success)
00562         {
00563             fw_ptr->update_started = connector_true;
00564             fw_ptr->target_info.target_number = download_request.target_number;
00565         }
00566 
00567     }
00568 
00569 done:
00570     return result;
00571 }
00572 
00573 static connector_status_t process_fw_binary_block(connector_firmware_data_t * const fw_ptr, uint8_t * const fw_binary_block, uint16_t const length)
00574 {
00575 /* Firmware binary block message format:
00576  *  --------------------------------------------------------
00577  * |   0    |   1    |     2        |  3 - 6  |   7..       |
00578  *  --------------------------------------------------------
00579  * | opcode | target | Ack required |  offset | binary data |
00580  *  --------------------------------------------------------
00581  *
00582  */
00583 enum fw_binary_block {
00584     field_define(fw_binary_block, opcode, uint8_t),
00585     field_define(fw_binary_block, target, uint8_t),
00586     field_define(fw_binary_block, ack_required, uint8_t),
00587     field_define(fw_binary_block, offset, uint32_t),
00588     record_end(fw_binary_block)
00589 };
00590 
00591 /* Firmware binary block acknowledge message format:
00592  *  -----------------------------------
00593  * |   0    |    1   | 2 - 5  |    6   |
00594  *  -----------------------------------
00595  * | opcode | target | offset | status |
00596  *  -----------------------------------
00597  *
00598  */
00599 enum fw_binary_ack {
00600     field_define(fw_binary_ack, opcode, uint8_t),
00601     field_define(fw_binary_ack, target, uint8_t),
00602     field_define(fw_binary_ack, offset, uint32_t),
00603     field_define(fw_binary_ack, status, uint8_t),
00604     record_end(fw_binary_ack)
00605 };
00606     connector_status_t result = connector_idle;
00607 
00608     uint8_t ack_required;
00609     connector_firmware_download_data_t download_data;
00610 
00611     /* Parse firmware binary block */
00612     download_data.target_number = message_load_u8(fw_binary_block, target);
00613 
00614     if (fw_ptr->update_started == connector_false)
00615     {
00616         /* Ignore this packet since we have not started downloading.
00617          * We may already abort download request.
00618          */
00619         goto done;
00620     }
00621 
00622     if ((fw_ptr->target_info.target_number != download_data.target_number) || (length < record_bytes(fw_binary_block)))
00623     {
00624         fw_abort_status_t fw_status;
00625 
00626         connector_debug_printf("process_fw_binary_block: invalid target or message length\n");
00627         fw_status.error_status = fw_invalid_msg;
00628         result = send_fw_abort(fw_ptr, download_data.target_number, fw_error_opcode, fw_status);
00629         goto done;
00630     }
00631 
00632     /* Parse firmware binary block */
00633     ack_required = message_load_u8(fw_binary_block, ack_required);
00634     download_data.image.offset = message_load_be32(fw_binary_block, offset);
00635     download_data.image.bytes_used = (size_t)(length - record_bytes(fw_binary_block));
00636 
00637     download_data.image.data = (fw_binary_block + record_bytes(fw_binary_block));
00638     download_data.status = connector_firmware_status_success;
00639 
00640     result = get_fw_config(fw_ptr, connector_request_id_firmware_download_data, &download_data);
00641 
00642     if (result == connector_working && download_data.status == connector_firmware_status_success)
00643     {
00644 
00645         if(ack_required)
00646         {
00647             uint8_t * fw_binary_ack = GET_PACKET_DATA_POINTER(fw_ptr->response_buffer, PACKET_EDP_FACILITY_SIZE);
00648 
00649             ASSERT((sizeof fw_ptr->response_buffer - PACKET_EDP_FACILITY_SIZE) > record_bytes(fw_binary_ack));
00650             /* send firmware binary block acknowledge */
00651             message_store_u8(fw_binary_ack, opcode, fw_binary_block_ack_opcode);
00652             message_store_u8(fw_binary_ack, target, download_data.target_number);
00653             message_store_be32(fw_binary_ack, offset, download_data.image.offset);
00654             message_store_u8(fw_binary_ack, status, connector_firmware_status_success);
00655 
00656             fw_ptr->response_size = record_bytes(fw_binary_ack);
00657             result = send_fw_message(fw_ptr);
00658         }
00659     }
00660     else if (result != connector_pending)
00661     {
00662         fw_abort_status_t fw_status;
00663         fw_status.user_status = download_data.status;
00664         result = send_fw_abort(fw_ptr, download_data.target_number, fw_download_abort_opcode, fw_status);
00665     }
00666 done:
00667     return result;
00668 }
00669 
00670 static connector_status_t process_fw_abort(connector_firmware_data_t * const fw_ptr, uint8_t * const fw_abort, uint16_t const length)
00671 {
00672 
00673     connector_status_t result = connector_working;
00674 
00675     /* parse firmware download abort */
00676     if (length != FW_ABORT_HEADER_SIZE)
00677     {
00678         connector_debug_printf("process_fw_abort: invalid message length\n");
00679     }
00680     else if (fw_ptr->update_started)
00681     {
00682         connector_firmware_download_abort_t request_data;
00683         uint8_t abort_status = message_load_u8(fw_abort, status);
00684 
00685         request_data.target_number = message_load_u8(fw_abort, target);
00686         switch (abort_status)
00687         {
00688         case fw_user_abort:
00689             request_data.status = connector_firmware_status_user_abort;
00690             break;
00691         case fw_device_error:
00692             request_data.status = connector_firmware_status_device_error;
00693             break;
00694         case fw_invalid_offset:
00695             request_data.status = connector_firmware_status_invalid_offset;
00696             break;
00697         case fw_invalid_data:
00698             request_data.status = connector_firmware_status_invalid_data;
00699             break;
00700         case fw_hardware_error:
00701             request_data.status = connector_firmware_status_hardware_error;
00702             break;
00703         default:
00704             ASSERT(connector_false);
00705             break;
00706         }
00707 
00708         /* call callback */
00709         if (fw_ptr->target_info.target_number == request_data.target_number)
00710         {
00711             result = get_fw_config(fw_ptr, connector_request_id_firmware_download_abort, &request_data);
00712             if (result != connector_pending)
00713             {
00714                 fw_ptr->update_started = connector_false;
00715             }
00716             fw_ptr->last_fw_keepalive_sent_time = 0;
00717         }
00718     }
00719     return result;
00720 
00721 }
00722 
00723 static connector_status_t process_fw_complete(connector_firmware_data_t * const fw_ptr, uint8_t * const fw_complete_request, uint16_t const length)
00724 {
00725 
00726 /* Firmware download complete message format:
00727  *  ----------------------------------------
00728  * |   0    |   1    |   2 - 5   |  6 - 9   |
00729  *  ----------------------------------------
00730  * | opcode | target | code size | checksum |
00731  *  ----------------------------------------
00732  *
00733  */
00734 enum fw_complete_request {
00735     field_define(fw_complete_request, opcode, uint8_t),
00736     field_define(fw_complete_request, target, uint8_t),
00737     field_define(fw_complete_request, code_size, uint32_t),
00738     field_define(fw_complete_request, checksum, uint32_t),
00739     record_end(fw_complete_request)
00740 };
00741 
00742 /* Firmware download complete response message format:
00743  *  -------------------------------------------------
00744  * |   0    |   1    |  2 - 5  |  6 - 9     |  10    |
00745  *  --------------------------------------------------
00746  * | opcode | target | version | calculated | status |
00747  * |        |        |         |  checksum  |        |
00748  *  --------------------------------------------------
00749  */
00750 enum fw_complete_response {
00751     field_define(fw_complete_response, opcode, uint8_t),
00752     field_define(fw_complete_response, target, uint8_t),
00753     field_define(fw_complete_response, version, uint32_t),
00754     field_define(fw_complete_response, checksum, uint32_t),
00755     field_define(fw_complete_response, status, uint8_t),
00756     record_end(fw_complete_response)
00757 };
00758 
00759     connector_status_t result = connector_working;
00760     connector_firmware_download_complete_t download_complete;
00761 
00762     download_complete.target_number = message_load_u8(fw_complete_request, target);
00763     download_complete.status = connector_firmware_download_success;
00764 
00765     if ((length != record_bytes(fw_complete_request)) ||
00766         (fw_ptr->update_started == connector_false) ||
00767         (fw_ptr->target_info.target_number != download_complete.target_number))
00768     {
00769         fw_abort_status_t  fw_status;
00770 
00771         connector_debug_printf("process_fw_complete: invalid message length, invalid target or no firmware update started\n");
00772         fw_status.error_status = fw_invalid_msg;
00773         result = send_fw_abort(fw_ptr, download_complete.target_number, fw_error_opcode, fw_status);
00774         goto done;
00775     }
00776 
00777 
00778     /* call callback */
00779     result = get_fw_config(fw_ptr, connector_request_id_firmware_download_complete, &download_complete);
00780     if (result == connector_working)
00781     {
00782         uint8_t * fw_complete_response = GET_PACKET_DATA_POINTER(fw_ptr->response_buffer, PACKET_EDP_FACILITY_SIZE);
00783 
00784         ASSERT((sizeof fw_ptr->response_buffer - PACKET_EDP_FACILITY_SIZE) > record_bytes(fw_complete_response));
00785 
00786         /* send firmware download complete response */
00787         message_store_u8(fw_complete_response, opcode, fw_download_complete_response_opcode);
00788         message_store_u8(fw_complete_response, target, download_complete.target_number);
00789         message_store_be32(fw_complete_response, version, INT32_C(0));
00790         message_store_be32(fw_complete_response, checksum, INT32_C(0));
00791         message_store_u8(fw_complete_response, status, download_complete.status);
00792 
00793         fw_ptr->last_fw_keepalive_sent_time = 0;
00794         fw_ptr->fw_keepalive_start = connector_false;
00795 
00796         fw_ptr->response_size = record_bytes(fw_complete_response);
00797         result = send_fw_message(fw_ptr);
00798         fw_ptr->update_started = connector_false;
00799     }
00800     else if (result != connector_pending)
00801     {
00802         fw_abort_status_t fw_status;
00803 
00804         fw_status.user_status = connector_firmware_status_user_abort;
00805         send_fw_abort(fw_ptr, download_complete.target_number, fw_download_abort_opcode, fw_status);
00806     }
00807 
00808 
00809 done:
00810     return result;
00811 
00812 }
00813 
00814 static connector_status_t process_target_reset(connector_firmware_data_t * const fw_ptr, uint8_t * const fw_message, uint16_t const length)
00815 {
00816     connector_status_t result;
00817     connector_firmware_reset_t firmware_reset;
00818 
00819     UNUSED_PARAMETER(length);
00820     connector_debug_printf("Firmware Facility: process target reset\n");
00821 
00822     firmware_reset.target_number = message_load_u8(fw_message, target);
00823 
00824     result = get_fw_config(fw_ptr, connector_request_id_firmware_target_reset, &firmware_reset);
00825 
00826     return result;
00827 }
00828 
00829 static connector_status_t send_discovery_packet_callback(connector_data_t * const connector_ptr, uint8_t const * const packet,
00830                                            connector_status_t const send_status, void * const user_data)
00831 {
00832     connector_status_t result;
00833     connector_firmware_data_t * const fw_ptr = user_data;
00834     /* update fw download keepalive timing */
00835     result = get_system_time(connector_ptr, &fw_ptr->last_fw_keepalive_sent_time);
00836 
00837     tcp_release_packet_buffer(connector_ptr, packet, send_status, user_data);
00838 
00839     return result;
00840 }
00841 
00842 static connector_status_t fw_discovery(connector_data_t * const connector_ptr, void * const facility_data,
00843                                             uint8_t * const packet, unsigned int * receive_timeout)
00844 {
00845 /* Firmware target list message format:
00846  *
00847  * --------------------------------------------------------
00848  * |   0    |    1   |  2 - 5  |  6 ...                    |
00849  *  -------------------------------------------------------
00850  * | opcode | target | version | Additional target-version |
00851  * |        |        |         |       pairs               |
00852  *  -------------------------------------------------------
00853  *
00854  */
00855 
00856     connector_status_t result = connector_idle;
00857     connector_firmware_data_t * const fw_ptr = facility_data;
00858 
00859     UNUSED_PARAMETER(packet);
00860     UNUSED_PARAMETER(receive_timeout);
00861 
00862     /* Construct a target list message.
00863      * Get target count and then get version for each target to build target list message
00864      *
00865      */
00866 
00867     if (fw_ptr->target_count > 0)
00868     {
00869         uint8_t * edp_header;
00870         uint8_t * fw_target_list;
00871         size_t avail_length;
00872         size_t discovery_length = 0;
00873         uint8_t target_number;
00874 
00875         /* get packet pointer for constructing target list info */
00876         edp_header = tcp_get_packet_buffer(connector_ptr, E_MSG_FAC_FW_NUM, &fw_target_list, &avail_length);
00877         if (edp_header == NULL)
00878         {
00879             result = connector_pending;
00880             goto done;
00881         }
00882 
00883         message_store_u8(fw_target_list, opcode, fw_target_list_opcode);
00884 
00885         discovery_length = target_list_header_size;
00886         fw_target_list += discovery_length;
00887 
00888         for (target_number=0; target_number < fw_ptr->target_count; target_number++)
00889         {
00890             connector_firmware_info_t firmware_info;
00891 
00892             /* get the current firmware version for this target */
00893             firmware_info.target_number = target_number;
00894             memset(&firmware_info.version, 0x00, sizeof firmware_info.version);
00895             /* call callback */
00896             result = get_fw_config(fw_ptr, connector_request_id_firmware_info, &firmware_info);
00897             if (result == connector_working)
00898             {
00899 
00900 #if defined CONNECTOR_RCI_SERVICE
00901                 /* coverity[uninit_use] */
00902                 result = confirm_fw_version(fw_ptr, firmware_info.target_number, firmware_info.version);
00903                 if (result != connector_working) goto error;
00904 #endif
00905                 message_store_u8(fw_target_list, target, firmware_info.target_number);
00906                 message_store_be32(fw_target_list, version, FW_VERSION_NUMBER(firmware_info.version));
00907 
00908                 /* set up for next target pair info*/
00909                 fw_target_list += target_list_size;
00910                 discovery_length += target_list_size;
00911             }
00912             else
00913             {
00914                 goto error;
00915             }
00916         }
00917 
00918         result = tcp_initiate_send_facility_packet(connector_ptr, edp_header, discovery_length,
00919                                                E_MSG_FAC_FW_NUM, send_discovery_packet_callback, fw_ptr);
00920 error:
00921         if (result != connector_working)
00922         {
00923             tcp_release_packet_buffer(connector_ptr, edp_header, connector_working, NULL);
00924         }
00925     }
00926 
00927 done:
00928     return result;
00929 }
00930 
00931 static connector_status_t fw_process(connector_data_t * const connector_ptr, void * const facility_data,
00932                                           uint8_t * const edp_header, unsigned int * const receive_timeout)
00933 {
00934     connector_status_t result = connector_idle;
00935     connector_firmware_data_t * const fw_ptr = facility_data;
00936     uint8_t opcode;
00937     uint8_t target;
00938     uint8_t * fw_message;
00939     uint16_t length;
00940 
00941     if (edp_header == NULL)
00942     {
00943         if (fw_ptr->update_started)
00944         {
00945             result = connector_pending;
00946         }
00947         goto done;
00948     }
00949 
00950     if (fw_ptr->fw_keepalive_start)
00951     {
00952         result = fw_discovery(connector_ptr, facility_data, edp_header, receive_timeout);
00953         if (result == connector_working)
00954         {
00955             if (get_system_time(connector_ptr, &fw_ptr->last_fw_keepalive_sent_time) != connector_working)
00956             {
00957                 result = connector_abort;
00958                 goto done;
00959             }
00960             fw_ptr->fw_keepalive_start = connector_false;
00961             result = connector_pending;
00962         }
00963         goto done;
00964     }
00965 
00966     if (fw_ptr->send_busy == connector_true)
00967     {
00968         /* callback is already called for this message.
00969          * We're here because we were unable to send a response
00970          * message which already message is constructed in
00971          * fw_ptr->response_buffer.
00972          */
00973         result = send_fw_message(fw_ptr);
00974         goto done;
00975     }
00976 
00977     length = message_load_be16(edp_header, length);
00978     if (length < FW_MESSAGE_HEADER_SIZE)
00979     {
00980         connector_debug_printf("fw_process: invalid packet size %d\n", length);
00981         goto done;
00982     }
00983 
00984     fw_message = GET_PACKET_DATA_POINTER(edp_header, PACKET_EDP_FACILITY_SIZE);
00985     opcode = message_load_u8(fw_message, opcode);
00986     target = message_load_u8(fw_message, target);
00987 
00988     if (target >= fw_ptr->target_count)
00989     {
00990         fw_abort_status_t  fw_status;
00991 
00992         connector_debug_printf("fw_process: invalid target\n");
00993 
00994         fw_status.error_status = fw_invalid_target;
00995         result = send_fw_abort(fw_ptr, target, fw_error_opcode, fw_status);
00996         goto done;
00997     }
00998 
00999     switch(opcode)
01000     {
01001     case fw_info_request_opcode:
01002         result = process_fw_info_request(fw_ptr, fw_message, length);
01003         break;
01004     case fw_download_request_opcode:
01005         result = process_fw_download_request(fw_ptr, fw_message, length);
01006         break;
01007     case fw_binary_block_opcode:
01008         result = process_fw_binary_block(fw_ptr, fw_message, length);
01009         break;
01010     case fw_download_abort_opcode:
01011         result = process_fw_abort(fw_ptr, fw_message, length);
01012         fw_ptr->last_fw_keepalive_sent_time = 0;
01013         fw_ptr->fw_keepalive_start = connector_false;
01014         break;
01015     case fw_download_complete_opcode:
01016         if (fw_ptr->last_fw_keepalive_sent_time == 0)
01017         {
01018             /* start firmware keepalive which allow firmware download complete
01019              * callback to start flash (which requires device to
01020              * send target list message).
01021              *
01022              * Note. We only start firmware keepalive when we receive this complete
01023              * code. Can we start when we receive block opcode?
01024              */
01025             fw_ptr->last_fw_keepalive_sent_time = connector_ptr->edp_data.keepalive.last_tx_received_time;
01026         }
01027         result = process_fw_complete(fw_ptr, fw_message, length);
01028         break;
01029     case fw_target_reset_opcode:
01030         result = process_target_reset(fw_ptr, fw_message, length);
01031         fw_ptr->last_fw_keepalive_sent_time = 0;
01032         fw_ptr->fw_keepalive_start = connector_false;
01033         break;
01034     default:
01035     {
01036         fw_abort_status_t  fw_status;
01037         fw_status.error_status = fw_invalid_opcode;
01038         result = send_fw_abort(fw_ptr, target, fw_error_opcode, fw_status);
01039         break;
01040     }
01041     }
01042 
01043 done:
01044     return result;
01045 }
01046 
01047 static connector_status_t connector_facility_firmware_delete(connector_data_t * const connector_ptr)
01048 {
01049     return del_facility_data(connector_ptr, E_MSG_FAC_FW_NUM);
01050 }
01051 
01052 static connector_status_t connector_facility_firmware_init(connector_data_t * const connector_ptr, unsigned int const facility_index)
01053 {
01054     connector_status_t result = connector_working;
01055     connector_firmware_data_t * fw_ptr;
01056 
01057     /* Add firmware access facility to Connector
01058      *
01059      * Make sure firmware access facility is not already created. If firmware
01060      * access facility is already created, we probably reconnect to Device Cloud
01061      * so just need to reset to initial state.
01062      *
01063      */
01064     fw_ptr = get_facility_data(connector_ptr, E_MSG_FAC_FW_NUM);
01065     if (fw_ptr == NULL)
01066     {
01067         void * ptr;
01068         result = add_facility_data(connector_ptr, facility_index, E_MSG_FAC_FW_NUM, &ptr, sizeof *fw_ptr);
01069 
01070         if (result != connector_working || ptr == NULL)
01071         {
01072             goto done;
01073         }
01074         fw_ptr = ptr;
01075    }
01076     fw_ptr->target_count = 0;
01077     fw_ptr->target_info.target_number = 0;
01078     fw_ptr->desc_length = 0;
01079     fw_ptr->spec_length = 0;
01080     fw_ptr->last_fw_keepalive_sent_time = 0;
01081     fw_ptr->fw_keepalive_start = connector_false;
01082     fw_ptr->send_busy = connector_false;
01083     fw_ptr->update_started = connector_false;
01084     fw_ptr->connector_ptr = connector_ptr;
01085 
01086     {
01087         connector_firmware_count_t firmware_data;
01088 
01089         result = get_fw_config(fw_ptr, connector_request_id_firmware_target_count, &firmware_data);
01090         if (result == connector_working)
01091         {
01092             /* coverity[uninit_use] */
01093             fw_ptr->target_count = firmware_data.count;
01094             if (fw_ptr->target_count > 0)
01095             {
01096                 size_t const buffer_size = sizeof connector_ptr->edp_data.send_packet.packet_buffer.buffer;
01097                 size_t const overhead = (PACKET_EDP_FACILITY_SIZE + target_list_header_size);
01098                 size_t const max_targets = (buffer_size - overhead) / target_list_size;
01099 
01100                 /* get max count of targets that fit into the response buffer */
01101                 if (fw_ptr->target_count > max_targets)
01102                 {
01103                     connector_request_id_t request_id;
01104 
01105                     request_id.firmware_request = connector_request_id_firmware_target_count;
01106                     notify_error_status(connector_ptr->callback, connector_class_id_firmware, request_id, connector_invalid_data_range);
01107                     result = connector_abort;
01108                     goto done;
01109                 }
01110             }
01111             else
01112             {
01113                 connector_debug_printf("fw_discovery: No target supported\n");
01114             }
01115         }
01116    }
01117 
01118 done:
01119     return result;
01120 }
01121 
01122