Statistics
| Branch: | Tag: | Revision:

amiro-blt / Host / Source / SerialBoot / srecord.c @ 69661903

History | View | Annotate | Download (17.746 KB)

1
/************************************************************************************//**
2
* \file         srecord.c
3
* \brief        Motorola S-record library header file.
4
* \ingroup      SerialBoot
5
* \internal
6
*----------------------------------------------------------------------------------------
7
*                          C O P Y R I G H T
8
*----------------------------------------------------------------------------------------
9
*   Copyright (c) 2014  by Feaser    http://www.feaser.com    All rights reserved
10
*
11
*----------------------------------------------------------------------------------------
12
*                            L I C E N S E
13
*----------------------------------------------------------------------------------------
14
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
15
* modify it under the terms of the GNU General Public License as published by the Free
16
* Software Foundation, either version 3 of the License, or (at your option) any later
17
* version.
18
*
19
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
20
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
21
* PURPOSE. See the GNU General Public License for more details.
22
*
23
* You should have received a copy of the GNU General Public License along with OpenBLT.
24
* If not, see <http://www.gnu.org/licenses/>.
25
*
26
* A special exception to the GPL is included to allow you to distribute a combined work 
27
* that includes OpenBLT without being obliged to provide the source code for any 
28
* proprietary components. The exception text is included at the bottom of the license
29
* file <license.html>.
30
* 
31
* \endinternal
32
****************************************************************************************/
33

    
34
/****************************************************************************************
35
* Include files
36
****************************************************************************************/
37
#include <assert.h>                                   /* assertion module              */
38
#include <sb_types.h>                                 /* C types                       */
39
#include <string.h>                                   /* for strcpy etc.               */
40
#include <ctype.h>                                    /* for toupper() etc.            */
41
#include "srecord.h"                                  /* S-record library              */
42

    
43

    
44
/****************************************************************************************
45
* Type definitions
46
****************************************************************************************/
47
/** \brief Enumeration for the different S-record line types. */
48
typedef enum
49
{
50
  LINE_TYPE_S1,                                  /**< 16-bit address line              */
51
  LINE_TYPE_S2,                                  /**< 24-bit address line              */
52
  LINE_TYPE_S3,                                  /**< 32-bit address line              */
53
  LINE_TYPE_UNSUPPORTED                          /**< unsupported line                 */
54
} tSrecordLineType;
55

    
56

    
57
/****************************************************************************************
58
* Function prototypes
59
****************************************************************************************/
60
static tSrecordLineType SrecordGetLineType(const sb_char *line);
61
static sb_uint8         SrecordVerifyChecksum(const sb_char *line);
62
static sb_uint8         SrecordHexStringToByte(const sb_char *hexstring);
63
static sb_uint8         SrecordReadLine(sb_file srecordHandle, sb_char *line);
64

    
65

    
66
/************************************************************************************//**
67
** \brief     Checks if the specified srecordFile exists and contains s-records.
68
** \param     srecordFile The S-record file with full path if applicable.
69
** \return    SB_TRUE on the S-record is valid, SB_FALSE otherwise.
70
**
71
****************************************************************************************/
72
sb_uint8 SrecordIsValid(const sb_char *srecordFile)
73
{
74
  sb_file tempHandle;
75
  sb_char line[SRECORD_MAX_CHARS_PER_LINE];
76

    
77
  /* attempt to open the file */
78
  tempHandle = SrecordOpen(srecordFile);
79
  /* is the file available? */
80
  if (tempHandle == SB_NULL)
81
  {
82
    /* cannot open the file */
83
    return SB_FALSE;
84
  }
85
  /* all lines should be formatted as S-records. read the first one to check this */
86
  if (SrecordReadLine(tempHandle, line) == SB_FALSE)
87
  {
88
    /* could not read a line. file must be empty */
89
    SrecordClose(tempHandle);
90
    return SB_FALSE;
91
  }
92
  /* check if the line starts with the 'S' character, followed by a digit */
93
  if ( (toupper(line[0]) != 'S') || (isdigit(line[1]) == 0) )
94
  {
95
    SrecordClose(tempHandle);
96
    return SB_FALSE;
97
  }
98

    
99
  /* still here so it is a valid s-record */
100
  SrecordClose(tempHandle);
101
  return SB_TRUE;
102
} /*** end of SrecordIsValid ***/
103

    
104

    
105
/************************************************************************************//**
106
** \brief     Opens the S-record file for reading.
107
** \param     srecordFile The S-record file with full path if applicable.
108
** \return    The filehandle if successful, SB_NULL otherwise.
109
**
110
****************************************************************************************/
111
sb_file SrecordOpen(const sb_char *srecordFile)
112
{
113
  /* open the file for reading */
114
  return fopen(srecordFile, "r");
115
} /*** end of SrecordOpen ***/
116

    
117

    
118
/************************************************************************************//**
119
** \brief     Parse the S-record file to obtain information about its contents.
120
** \param     srecordHandle The S-record file handle. It is returned by SrecordOpen.
121
** \param     parseResults Pointer to where the parse results should be stored.
122
** \return    none.
123
**
124
****************************************************************************************/
125
void SrecordParse(sb_file srecordHandle, tSrecordParseResults *parseResults)
126
{
127
  tSrecordLineParseResults lineResults;
128

    
129
  /* start at the beginning of the file */
130
  rewind(srecordHandle);
131
  
132
  /* init data structure */
133
  parseResults->address_high = 0;
134
  parseResults->address_low = 0xffffffff;
135
  parseResults->data_bytes_total = 0;
136

    
137
  /* loop through all S-records with program data */
138
  while (SrecordParseNextDataLine(srecordHandle, &lineResults) == SB_TRUE)
139
  {
140
    /* update byte total */
141
    parseResults->data_bytes_total += lineResults.length;
142
    /* is this a new lowest address? */
143
    if (lineResults.address < parseResults->address_low)
144
    {
145
      parseResults->address_low = lineResults.address;
146
    }
147
    /* is this a new highest address? */
148
    if ((lineResults.address + lineResults.length - 1) > parseResults->address_high)
149
    {
150
      parseResults->address_high = (lineResults.address + lineResults.length - 1);
151
    }
152
  }
153
  /* reset to the beginning of the file again */
154
  rewind(srecordHandle);
155
} /*** end of SrecordParse ***/
156

    
157

    
158
/************************************************************************************//**
159
** \brief     Closes the S-record file.
160
** \param     srecordHandle The S-record file handle. It is returned by SrecordOpen.
161
** \return    none.
162
**
163
****************************************************************************************/
164
void SrecordClose(sb_file srecordHandle)
165
{
166
  /* close the file handle if valid */
167
  if (srecordHandle != SB_NULL)
168
  {
169
    fclose(srecordHandle);
170
  }
171
} /*** end of SrecordClose ***/
172

    
173

    
174
/************************************************************************************//**
175
** \brief     Reads the next S-record with program data, parses it and returns the 
176
**            results.
177
** \param     srecordHandle The S-record file handle. It is returned by SrecordOpen.
178
** \param     parseResults Pointer to where the parse results should be stored.
179
** \return    SB_TRUE is valid parse results were stored. SB_FALSE in case of end-of-
180
**            file.
181
**
182
****************************************************************************************/
183
sb_uint8 SrecordParseNextDataLine(sb_file srecordHandle, tSrecordLineParseResults *parseResults)
184
{
185
  sb_char line[SRECORD_MAX_CHARS_PER_LINE];
186
  sb_uint8 data_line_found = SB_FALSE;
187
  tSrecordLineType lineType;
188
  sb_uint16 bytes_on_line;
189
  sb_uint16 i;
190
  sb_char *linePtr;
191

    
192
  /* first set the length paramter to 0 */
193
  parseResults->length = 0;
194

    
195
  while (data_line_found == SB_FALSE)
196
  {
197
    /* read the next line from the file */
198
    if (SrecordReadLine(srecordHandle, line) == SB_FALSE)
199
    {
200
      /* end-of-file encountered */
201
      return SB_FALSE;
202
    }
203
    /* we now have a line. check if it is a S-record data line */
204
    lineType = SrecordGetLineType(line);
205
    if (lineType != LINE_TYPE_UNSUPPORTED)
206
    {
207
      /* check if the checksum on the line is correct */
208
      if (SrecordVerifyChecksum(line) == SB_TRUE)
209
      {
210
        /* found a valid line that can be parsed. loop will stop */
211
        data_line_found = SB_TRUE;
212
        break;
213
      }
214
    }
215
  }
216

    
217
  /* still here so we have a valid S-record data line. start parsing */
218
  linePtr = &line[0];
219
  /* all good so far, now read out the address and databytes for the line */
220
  switch (lineType)
221
  {
222
    /* ---------------------------- S1 line type ------------------------------------- */
223
    case LINE_TYPE_S1:
224
      /* adjust pointer to point to byte count value */
225
      linePtr += 2;
226
      /* read out the number of byte values that follow on the line */
227
      bytes_on_line = SrecordHexStringToByte(linePtr);
228
      /* read out the 16-bit address */
229
      linePtr += 2;
230
      parseResults->address = SrecordHexStringToByte(linePtr) << 8;
231
      linePtr += 2;
232
      parseResults->address += SrecordHexStringToByte(linePtr);
233
      /* adjust pointer to point to the first data byte after the address */
234
      linePtr += 2;
235
      /* determine how many data bytes are on the line */
236
      parseResults->length = bytes_on_line - 3; /* -2 bytes address, -1 byte checksum */
237
      /* read and store data bytes if requested */
238
      for (i=0; i<parseResults->length; i++)
239
      {
240
        parseResults->data[i] = SrecordHexStringToByte(linePtr);
241
        linePtr += 2;
242
      }
243
      break;
244
      
245
    /* ---------------------------- S2 line type ------------------------------------- */
246
    case LINE_TYPE_S2:
247
      /* adjust pointer to point to byte count value */
248
      linePtr += 2;
249
      /* read out the number of byte values that follow on the line */
250
      bytes_on_line = SrecordHexStringToByte(linePtr);
251
      /* read out the 32-bit address */
252
      linePtr += 2;
253
      parseResults->address = SrecordHexStringToByte(linePtr) << 16;
254
      linePtr += 2;
255
      parseResults->address += SrecordHexStringToByte(linePtr) << 8;
256
      linePtr += 2;
257
      parseResults->address += SrecordHexStringToByte(linePtr);
258
      /* adjust pointer to point to the first data byte after the address */
259
      linePtr += 2;
260
      /* determine how many data bytes are on the line */
261
      parseResults->length = bytes_on_line - 4; /* -3 bytes address, -1 byte checksum */
262
      /* read and store data bytes if requested */
263
      for (i=0; i<parseResults->length; i++)
264
      {
265
        parseResults->data[i] = SrecordHexStringToByte(linePtr);
266
        linePtr += 2;
267
      }
268
      break;
269
      
270
    /* ---------------------------- S3 line type ------------------------------------- */
271
    case LINE_TYPE_S3:
272
      /* adjust pointer to point to byte count value */
273
      linePtr += 2;
274
      /* read out the number of byte values that follow on the line */
275
      bytes_on_line = SrecordHexStringToByte(linePtr);
276
      /* read out the 32-bit address */
277
      linePtr += 2;
278
      parseResults->address = SrecordHexStringToByte(linePtr) << 24;
279
      linePtr += 2;
280
      parseResults->address += SrecordHexStringToByte(linePtr) << 16;
281
      linePtr += 2;
282
      parseResults->address += SrecordHexStringToByte(linePtr) << 8;
283
      linePtr += 2;
284
      parseResults->address += SrecordHexStringToByte(linePtr);
285
      /* adjust pointer to point to the first data byte after the address */
286
      linePtr += 2;
287
      /* determine how many data bytes are on the line */
288
      parseResults->length = bytes_on_line - 5; /* -4 bytes address, -1 byte checksum */
289
      /* read and store data bytes if requested */
290
      for (i=0; i<parseResults->length; i++)
291
      {
292
        parseResults->data[i] = SrecordHexStringToByte(linePtr);
293
        linePtr += 2;
294
      }
295
      break;
296

    
297
    default:
298
      /* will not happen */
299
      break;
300
  }
301

    
302
  /* parsing all done */
303
  return SB_TRUE;
304
} /*** end of SrecordParseNextDataLine ***/
305

    
306

    
307
/************************************************************************************//**
308
** \brief     Inspects a line from a Motorola S-Record file to determine its type.
309
** \param     line A line from the S-Record.
310
** \return    the S-Record line type.
311
**
312
****************************************************************************************/
313
static tSrecordLineType SrecordGetLineType(const sb_char *line)
314
{
315
  /* check if the line starts with the 'S' character, followed by a digit */
316
  if ( (toupper(line[0]) != 'S') || (isdigit(line[1]) == 0) )
317
  {
318
    /* not a valid S-Record line type */
319
    return LINE_TYPE_UNSUPPORTED;
320
  }
321
  /* determine the line type */
322
  if (line[1] == '1')
323
  {
324
    return LINE_TYPE_S1;
325
  }
326
  if (line[1] == '2')
327
  {
328
    return LINE_TYPE_S2;
329
  }
330
  if (line[1] == '3')
331
  {
332
    return LINE_TYPE_S3;
333
  }
334
  /* still here so not a supported line type found */
335
  return LINE_TYPE_UNSUPPORTED;
336
} /*** end of SrecordGetLineType ***/
337

    
338

    
339
/************************************************************************************//**
340
** \brief     Inspects an S1, S2 or S3 line from a Motorola S-Record file to
341
**            determine if the checksum at the end is corrrect.
342
** \param     line An S1, S2 or S3 line from the S-Record.
343
** \return    SB_TRUE if the checksum is correct, SB_FALSE otherwise.
344
**
345
****************************************************************************************/
346
static sb_uint8 SrecordVerifyChecksum(const sb_char *line)
347
{
348
  sb_uint16 bytes_on_line;
349
  sb_uint8  checksum = 0;
350
  
351
  /* adjust pointer to point to byte count value */
352
  line += 2;
353
  /* read out the number of byte values that follow on the line */
354
  bytes_on_line = SrecordHexStringToByte(line);
355
  /* byte count is part of checksum */
356
  checksum += bytes_on_line;
357
  /* adjust pointer to the first byte of the address */
358
  line += 2;
359
  /* add byte values of address and data, but not the final checksum */
360
  do 
361
  {
362
    /* add the next byte value to the checksum */
363
    checksum += SrecordHexStringToByte(line);
364
    /* update counter */
365
    bytes_on_line--;
366
    /* point to next hex string in the line */
367
    line += 2;
368
  } 
369
  while (bytes_on_line > 1);
370
  /* the checksum is calculated by summing up the values of the byte count, address and
371
   * databytes and then taking the 1-complement of the sum's least signigicant byte */
372
  checksum = ~checksum;
373
  /* finally verify the calculated checksum with the one at the end of the line */
374
  if (checksum != SrecordHexStringToByte(line))
375
  {
376
    /* checksum incorrect */
377
    return SB_FALSE;
378
  }
379
  /* still here so the checksum was correct */
380
  return SB_TRUE;
381
} /*** end of SrecordVerifyChecksum ***/
382

    
383

    
384
/************************************************************************************//**
385
** \brief     Helper function to convert a sequence of 2 characters that represent
386
**            a hexadecimal value to the actual byte value.
387
**              Example: SrecordHexStringToByte("2f")  --> returns 47.
388
** \param     hexstring String beginning with 2 characters that represent a hexa-
389
**                      decimal value.
390
** \return    The resulting byte value.
391
**
392
****************************************************************************************/
393
static sb_uint8 SrecordHexStringToByte(const sb_char *hexstring)
394
{
395
  sb_uint8 result = 0;
396
  sb_char  c;
397
  sb_uint8 counter;
398
  
399
  /* a hexadecimal character is 2 characters long (i.e 0x4F minus the 0x part) */
400
  for (counter=0; counter < 2; counter++)
401
  {
402
    /* read out the character */
403
    c = toupper(hexstring[counter]);
404
    /* check that the character is 0..9 or A..F */
405
    if ( (c < '0') || (c > 'F') || ( (c > '9') && (c < 'A') ) )
406
    {
407
      /* character not valid */
408
      return 0;
409
    }
410
    /* convert character to 4-bit value (check ASCII table for more info) */
411
    c -= '0';
412
    if (c > 9) 
413
    {
414
      c -= 7;
415
    }
416
    /* add it to the result */
417
    result = (result << 4) + c;
418
  }
419
  /* return the results */
420
  return result;
421
} /*** end of SrecordHexStringToByte ***/
422

    
423

    
424
/************************************************************************************//**
425
** \brief     Reads the next line from the S-record file handle.
426
** \param     srecordHandle The S-record file handle. It is returned by SrecordOpen.
427
** \param     line Destination buffer for the line characters. Should be of size
428
**            SRECORD_MAX_CHARS_PER_LINE.
429
** \return    SB_TRUE if successful, SB_FALSE otherwise.
430
**
431
****************************************************************************************/
432
static sb_uint8 SrecordReadLine(sb_file srecordHandle, sb_char *line)
433
{
434
  /* init the line as an empty line */
435
  line[0] = '\0';
436

    
437
  /* loop as long as we find a non-empty line or end-of-file */
438
  while (line[0] == '\0') 
439
  {
440
    if (fgets(line, SRECORD_MAX_CHARS_PER_LINE, srecordHandle) == SB_NULL)
441
    {
442
      /* no more lines available */
443
      return SB_FALSE;
444
    }
445
    /* replace the line termination with a string termination */
446
    line[strcspn(line, "\n")] = '\0';
447
  }
448
  /* still here so not EOF and not and empty line, so success */
449
  return SB_TRUE;
450
} /*** end of SrecordReadLine ***/
451

    
452

    
453
/*********************************** end of srecord.c **********************************/