Arrow / Mbed OS DAPLink Reset
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers intelhex.c Source File

intelhex.c

Go to the documentation of this file.
00001 /**
00002  * @file    intelhex.c
00003  * @brief   Implementation of intelhex.h
00004  *
00005  * DAPLink Interface Firmware
00006  * Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
00007  * SPDX-License-Identifier: Apache-2.0
00008  *
00009  * Licensed under the Apache License, Version 2.0 (the "License"); you may
00010  * not use this file except in compliance with the License.
00011  * You may obtain a copy of the License at
00012  *
00013  * http://www.apache.org/licenses/LICENSE-2.0
00014  *
00015  * Unless required by applicable law or agreed to in writing, software
00016  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00017  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00018  * See the License for the specific language governing permissions and
00019  * limitations under the License.
00020  */
00021 
00022 #include <string.h>
00023 
00024 #include "intelhex.h"
00025 
00026 typedef enum hex_record_t hex_record_t;
00027 enum hex_record_t {
00028     DATA_RECORD = 0,
00029     EOF_RECORD = 1,
00030     EXT_SEG_ADDR_RECORD = 2,
00031     START_SEG_ADDR_RECORD = 3,
00032     EXT_LINEAR_ADDR_RECORD = 4,
00033     START_LINEAR_ADDR_RECORD = 5
00034 };
00035 
00036 typedef union hex_line_t hex_line_t;
00037 union __attribute__((packed)) hex_line_t {
00038     uint8_t buf[0x25];
00039     struct __attribute__((packed)) {
00040         uint8_t  byte_count;
00041         uint16_t address;
00042         uint8_t  record_type;
00043         uint8_t  data[0x25 - 0x5];
00044         uint8_t  checksum;
00045     };
00046 };
00047 
00048 /** Swap 16bit value - let compiler figure out the best way
00049  *  @param val a variable of size uint16_t to be swapped
00050  *  @return the swapped value
00051  */
00052 static uint16_t swap16(uint16_t a)
00053 {
00054     return ((a & 0x00ff) << 8) | ((a & 0xff00) >> 8);
00055 }
00056 
00057 /** Converts a character representation of a hex to real value.
00058  *   @param c is the hex value in char format
00059  *   @return the value of the hex
00060  */
00061 static uint8_t ctoh(char c)
00062 {
00063     return (c & 0x10) ? /*0-9*/ c & 0xf : /*A-F, a-f*/ (c & 0xf) + 9;
00064 }
00065 
00066 /** Calculate checksum on a hex record
00067  *   @param data is the line of hex record
00068  *   @param size is the length of the data array
00069  *   @return 1 if the data provided is a valid hex record otherwise 0
00070  */
00071 static uint8_t validate_checksum(hex_line_t *record)
00072 {
00073     uint8_t result = 0, i = 0;
00074 
00075     for (; i < (record->byte_count + 5); i++) {
00076         result += record->buf[i];
00077     }
00078 
00079     return (result == 0);
00080 }
00081 
00082 static hex_line_t line = {0}, shadow_line = {0};
00083 static uint32_t next_address_to_write = 0;
00084 static uint8_t low_nibble = 0, idx = 0, record_processed = 0, load_unaligned_record = 0;
00085 
00086 void reset_hex_parser(void)
00087 {
00088     memset(line.buf, 0, sizeof(hex_line_t));
00089     memset(shadow_line.buf, 0, sizeof(hex_line_t));
00090     next_address_to_write = 0;
00091     low_nibble = 0;
00092     idx = 0;
00093     record_processed = 0;
00094     load_unaligned_record = 0;
00095 }
00096 
00097 hexfile_parse_status_t parse_hex_blob(const uint8_t *hex_blob, const uint32_t hex_blob_size, uint32_t *hex_parse_cnt, uint8_t *bin_buf, const uint32_t bin_buf_size, uint32_t *bin_buf_address, uint32_t *bin_buf_cnt)
00098 {
00099     uint8_t *end = (uint8_t *)hex_blob + hex_blob_size;
00100     hexfile_parse_status_t status = HEX_PARSE_UNINIT ;
00101     // reset the amount of data that is being return'd
00102     *bin_buf_cnt = (uint32_t)0;
00103 
00104     // we had an exit state where the address was unaligned to the previous record and data count.
00105     //  Need to pop the last record into the buffer before decoding anthing else since it was
00106     //  already decoded.
00107     if (load_unaligned_record) {
00108         // need some help...
00109         load_unaligned_record = 0;
00110         // move from line buffer back to input buffer
00111         memcpy((uint8_t *)bin_buf, (uint8_t *)line.data, line.byte_count);
00112         bin_buf += line.byte_count;
00113         *bin_buf_cnt = (uint32_t)(*bin_buf_cnt) + line.byte_count;
00114         // Store next address to write
00115         next_address_to_write = ((next_address_to_write & 0xffff0000) | line.address) + line.byte_count;
00116     }
00117 
00118     while (hex_blob != end) {
00119         switch ((uint8_t)(*hex_blob)) {
00120             // we've hit the end of an ascii line
00121             // junk we dont care about could also just run the validate_checksum on &line
00122             case '\r':
00123             case '\n':
00124                 //ignore new lines
00125                 break;
00126 
00127             // found start of a new record. reset state variables
00128             case ':':
00129                 memset(line.buf, 0, sizeof(hex_line_t));
00130                 low_nibble = 0;
00131                 idx = 0;
00132                 record_processed = 0;
00133                 break;
00134 
00135             // decoding lines
00136             default:
00137                 if (low_nibble) {
00138                     line.buf[idx] |= ctoh((uint8_t)(*hex_blob)) & 0xf;
00139                     if (++idx >= (line.byte_count + 5)) { //all data in
00140                         if (0 == validate_checksum(&line)) {
00141                             status = HEX_PARSE_CKSUM_FAIL ;
00142                             goto hex_parser_exit;
00143                         } else {
00144                             if (!record_processed) {
00145                                 record_processed = 1;
00146                                 // address byteswap...
00147                                 line.address = swap16(line.address);
00148 
00149                                 switch (line.record_type) {
00150                                     case DATA_RECORD:
00151                                         // keeping a record of the last hex record
00152                                         memcpy(shadow_line.buf, line.buf, sizeof(hex_line_t));
00153 
00154                                         // verify this is a continous block of memory or need to exit and dump
00155                                         if (((next_address_to_write & 0xffff0000) | line.address) != next_address_to_write) {
00156                                             load_unaligned_record = 1;
00157                                             status = HEX_PARSE_UNALIGNED ;
00158                                             goto hex_parser_exit;
00159                                         }
00160 
00161                                         // move from line buffer back to input buffer
00162                                         memcpy(bin_buf, line.data, line.byte_count);
00163                                         bin_buf += line.byte_count;
00164                                         *bin_buf_cnt = (uint32_t)(*bin_buf_cnt) + line.byte_count;
00165                                         // Save next address to write
00166                                         next_address_to_write = ((next_address_to_write & 0xffff0000) | line.address) + line.byte_count;
00167                                         break;
00168 
00169                                     case EOF_RECORD:
00170                                         status = HEX_PARSE_EOF ;
00171                                         goto hex_parser_exit;
00172 
00173                                     case EXT_SEG_ADDR_RECORD:
00174                                         // Could have had data in the buffer so must exit and try to program
00175                                         //  before updating bin_buf_address with next_address_to_write
00176                                         memset(bin_buf, 0xff, (bin_buf_size - (uint32_t)(*bin_buf_cnt)));
00177                                         // figure the start address for the buffer before returning
00178                                         *bin_buf_address = next_address_to_write - (uint32_t)(*bin_buf_cnt);
00179                                         *hex_parse_cnt = (uint32_t)(hex_blob_size - (end - hex_blob));
00180                                         // update the address msb's
00181                                         next_address_to_write = (next_address_to_write & 0x00000000) | ((line.data[0] << 12) | (line.data[1] << 4));
00182                                         // Need to exit and program if buffer has been filled
00183                                         status = HEX_PARSE_UNALIGNED ;
00184                                         return status;
00185 
00186                                     case EXT_LINEAR_ADDR_RECORD:
00187                                         // Could have had data in the buffer so must exit and try to program
00188                                         //  before updating bin_buf_address with next_address_to_write
00189                                         //  Good catch Gaute!!
00190                                         memset(bin_buf, 0xff, (bin_buf_size - (uint32_t)(*bin_buf_cnt)));
00191                                         // figure the start address for the buffer before returning
00192                                         *bin_buf_address = next_address_to_write - (uint32_t)(*bin_buf_cnt);
00193                                         *hex_parse_cnt = (uint32_t)(hex_blob_size - (end - hex_blob));
00194                                         // update the address msb's
00195                                         next_address_to_write = (next_address_to_write & 0x00000000) | ((line.data[0] << 24) | (line.data[1] << 16));
00196                                         // Need to exit and program if buffer has been filled
00197                                         status = HEX_PARSE_UNALIGNED ;
00198                                         return status;
00199 
00200                                     default:
00201                                         break;
00202                                 }
00203                             }
00204                         }
00205                     }
00206                 } else {
00207                     if (idx < sizeof(hex_line_t)) {
00208                         line.buf[idx] = ctoh((uint8_t)(*hex_blob)) << 4;
00209                     }
00210                 }
00211 
00212                 low_nibble = !low_nibble;
00213                 break;
00214         }
00215 
00216         hex_blob++;
00217     }
00218 
00219     // decoded an entire hex block - verify (cant do this hex_parse_cnt is figured below)
00220     //status = (hex_blob_size == (uint32_t)(*hex_parse_cnt)) ? HEX_PARSE_OK : HEX_PARSE_FAILURE;
00221     status = HEX_PARSE_OK ;
00222 hex_parser_exit:
00223     memset(bin_buf, 0xff, (bin_buf_size - (uint32_t)(*bin_buf_cnt)));
00224     // figure the start address for the buffer before returning
00225     *bin_buf_address = next_address_to_write - (uint32_t)(*bin_buf_cnt);
00226     *hex_parse_cnt = (uint32_t)(hex_blob_size - (end - hex_blob));
00227     return status;
00228 }