This repository has been archived by the owner on Mar 11, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathsrecord.c
227 lines (186 loc) · 8.65 KB
/
srecord.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*
* srecord.h
* Utility functions to create, read, write, and print Motorola S-Record binary records.
*
* Written by Vanya A. Sergeev <[email protected]>
* Version 1.0.5 - February 2011
*
*/
#include "srecord.h"
/* Lengths of the ASCII hex encoded address fields of different SRecord types */
static int SRecord_Address_Lengths[] = {
4, // S0
4, // S1
6, // S2
8, // S3
8, // S4
4, // S5
6, // S6
8, // S7
6, // S8
4, // S9
};
/* Initializes a new SRecord structure that the paramater srec points to with the passed
* S-Record type, up to 32-bit integer address, 8-bit data array, and size of 8-bit data array. */
int New_SRecord(int type, uint32_t address, const uint8_t *data, int dataLen, SRecord *srec) {
/* Data length size check, assertion of srec pointer */
if (dataLen < 0 || dataLen > SRECORD_MAX_DATA_LEN/2 || srec == NULL)
return SRECORD_ERROR_INVALID_ARGUMENTS;
srec->type = type;
srec->address = address;
memcpy(srec->data, data, (unsigned int)dataLen);
srec->dataLen = dataLen;
srec->checksum = Checksum_SRecord(srec);
return SRECORD_OK;
}
/* Utility function to read an S-Record from a file */
int Read_SRecord(SRecord *srec, FILE *in) {
char recordBuff[SRECORD_RECORD_BUFF_SIZE];
/* A temporary buffer to hold ASCII hex encoded data, set to the maximum length we would ever need */
char hexBuff[SRECORD_MAX_ADDRESS_LEN+1];
int asciiAddressLen, asciiDataLen, dataOffset, fieldDataCount, i;
/* Check our record pointer and file pointer */
if (srec == NULL || in == NULL)
return SRECORD_ERROR_INVALID_ARGUMENTS;
if (fgets(recordBuff, SRECORD_RECORD_BUFF_SIZE, in) == NULL) {
/* In case we hit EOF, don't report a file error */
if (feof(in) != 0)
return SRECORD_ERROR_EOF;
else
return SRECORD_ERROR_FILE;
}
/* Null-terminate the string at the first sign of a \r or \n */
for (i = 0; i < (int)strlen(recordBuff); i++) {
if (recordBuff[i] == '\r' || recordBuff[i] == '\n') {
recordBuff[i] = 0;
break;
}
}
/* Check if we hit a newline */
if (strlen(recordBuff) == 0)
return SRECORD_ERROR_NEWLINE;
/* Size check for type and count fields */
if (strlen(recordBuff) < SRECORD_TYPE_LEN + SRECORD_COUNT_LEN)
return SRECORD_ERROR_INVALID_RECORD;
/* Check for the S-Record start code at the beginning of every record */
if (recordBuff[SRECORD_START_CODE_OFFSET] != SRECORD_START_CODE)
return SRECORD_ERROR_INVALID_RECORD;
/* Copy the ASCII hex encoding of the type field into hexBuff, convert it into a usable integer */
strncpy(hexBuff, recordBuff+SRECORD_TYPE_OFFSET, SRECORD_TYPE_LEN);
hexBuff[SRECORD_TYPE_LEN] = 0;
srec->type = (int)strtoul(hexBuff, (char **)NULL, 16);
/* Copy the ASCII hex encoding of the count field into hexBuff, convert it to a usable integer */
strncpy(hexBuff, recordBuff+SRECORD_COUNT_OFFSET, SRECORD_COUNT_LEN);
hexBuff[SRECORD_COUNT_LEN] = 0;
fieldDataCount = (int)strtoul(hexBuff, (char **)NULL, 16);
/* Check that our S-Record type is valid */
if (srec->type < SRECORD_TYPE_S0 || srec->type > SRECORD_TYPE_S9)
return SRECORD_ERROR_INVALID_RECORD;
/* Get the ASCII hex address length of this particular S-Record type */
asciiAddressLen = SRecord_Address_Lengths[srec->type];
/* Size check for address field */
if (strlen(recordBuff) < (unsigned int)(SRECORD_ADDRESS_OFFSET+asciiAddressLen))
return SRECORD_ERROR_INVALID_RECORD;
/* Copy the ASCII hex encoding of the count field into hexBuff, convert it to a usable integer */
strncpy(hexBuff, recordBuff+SRECORD_ADDRESS_OFFSET, (unsigned int)asciiAddressLen);
hexBuff[asciiAddressLen] = 0;
srec->address = (uint32_t)strtoul(hexBuff, (char **)NULL, 16);
/* Compute the ASCII hex data length by subtracting the remaining field lengths from the S-Record
* count field (times 2 to account for the number of characters used in ASCII hex encoding) */
asciiDataLen = (fieldDataCount*2) - asciiAddressLen - SRECORD_CHECKSUM_LEN;
/* Bailout if we get an invalid data length */
if (asciiDataLen < 0 || asciiDataLen > SRECORD_MAX_DATA_LEN)
return SRECORD_ERROR_INVALID_RECORD;
/* Size check for final data field and checksum field */
if (strlen(recordBuff) < (unsigned int)(SRECORD_ADDRESS_OFFSET+asciiAddressLen+asciiDataLen+SRECORD_CHECKSUM_LEN))
return SRECORD_ERROR_INVALID_RECORD;
dataOffset = SRECORD_ADDRESS_OFFSET+asciiAddressLen;
/* Loop through each ASCII hex byte of the data field, pull it out into hexBuff,
* convert it and store the result in the data buffer of the S-Record */
for (i = 0; i < asciiDataLen/2; i++) {
/* Times two i because every byte is represented by two ASCII hex characters */
strncpy(hexBuff, recordBuff+dataOffset+2*i, SRECORD_ASCII_HEX_BYTE_LEN);
hexBuff[SRECORD_ASCII_HEX_BYTE_LEN] = 0;
srec->data[i] = (uint8_t)strtoul(hexBuff, (char **)NULL, 16);
}
/* Real data len is divided by two because every byte is represented by two ASCII hex characters */
srec->dataLen = asciiDataLen/2;
/* Copy out the checksum ASCII hex encoded byte, and convert it back to a usable integer */
strncpy(hexBuff, recordBuff+dataOffset+asciiDataLen, SRECORD_CHECKSUM_LEN);
hexBuff[SRECORD_CHECKSUM_LEN] = 0;
srec->checksum = (uint8_t)strtoul(hexBuff, (char **)NULL, 16);
if (srec->checksum != Checksum_SRecord(srec))
return SRECORD_ERROR_INVALID_RECORD;
return SRECORD_OK;
}
/* Utility function to write an S-Record to a file */
int Write_SRecord(const SRecord *srec, FILE *out) {
char strAddress[SRECORD_MAX_ADDRESS_LEN+1];
int asciiAddressLen, asciiAddressOffset, fieldDataCount, i;
/* Check our record pointer and file pointer */
if (srec == NULL || out == NULL)
return SRECORD_ERROR_INVALID_ARGUMENTS;
/* Check that the type and data length is within range */
if (srec->type < SRECORD_TYPE_S0 || srec->type > SRECORD_TYPE_S9 || srec->dataLen > SRECORD_MAX_DATA_LEN/2)
return SRECORD_ERROR_INVALID_RECORD;
/* Compute the record count, address and checksum lengths are halved because record count
* is the number of bytes left in the record, not the length of the ASCII hex representation */
fieldDataCount = SRecord_Address_Lengths[srec->type]/2 + srec->dataLen + SRECORD_CHECKSUM_LEN/2;
asciiAddressLen = SRecord_Address_Lengths[srec->type];
/* The offset of the ASCII hex encoded address from zero, this is used so we only write as
* many bytes of the address as permitted by the S-Record type. */
asciiAddressOffset = SRECORD_MAX_ADDRESS_LEN-asciiAddressLen;
if (fprintf(out, "%c%1.1X%2.2X", SRECORD_START_CODE, srec->type, fieldDataCount) < 0)
return SRECORD_ERROR_FILE;
/* Write the ASCII hex representation of the address, starting from the offset to only
* write as much of the address as permitted by the S-Record type (calculated above). */
/* Fix the hex to be 8 hex digits of precision, to fit all 32-bits, including zeros */
snprintf(strAddress, sizeof(strAddress), "%2.8X", srec->address);
if (fprintf(out, "%s", strAddress+asciiAddressOffset) < 0)
return SRECORD_ERROR_FILE;
/* Write each byte of the data, guaranteed to be two hex ASCII characters each since
* srec->data[i] has the type of uint8_t */
for (i = 0; i < srec->dataLen; i++) {
if (fprintf(out, "%2.2X", srec->data[i]) < 0)
return SRECORD_ERROR_FILE;
}
/* Last but not least, the checksum */
if (fprintf(out, "%2.2X\r\n", Checksum_SRecord(srec)) < 0)
return SRECORD_ERROR_FILE;
return SRECORD_OK;
}
/* Utility function to print the information stored in an S-Record */
void Print_SRecord(const SRecord *srec) {
int i;
printf("S-Record Type: \t\tS%d\n", srec->type);
printf("S-Record Address: \t0x%2.8X\n", srec->address);
printf("S-Record Data: \t\t{");
for (i = 0; i < srec->dataLen; i++) {
if (i+1 < srec->dataLen)
printf("0x%02X, ", srec->data[i]);
else
printf("0x%02X", srec->data[i]);
}
printf("}\n");
printf("S-Record Checksum: \t0x%2.2X\n", srec->checksum);
}
/* Utility function to calculate the checksum of an S-Record */
uint8_t Checksum_SRecord(const SRecord *srec) {
uint8_t checksum;
int fieldDataCount, i;
/* Compute the record count, address and checksum lengths are halved because record count
* is the number of bytes left in the record, not the length of the ASCII hex representation */
fieldDataCount = SRecord_Address_Lengths[srec->type]/2 + srec->dataLen + SRECORD_CHECKSUM_LEN/2;
/* Add the count, address, and data fields together */
checksum = (uint8_t)fieldDataCount;
/* Add each byte of the address individually */
checksum += (uint8_t)(srec->address & 0x000000FF);
checksum += (uint8_t)((srec->address & 0x0000FF00) >> 8);
checksum += (uint8_t)((srec->address & 0x00FF0000) >> 16);
checksum += (uint8_t)((srec->address & 0xFF000000) >> 24);
for (i = 0; i < srec->dataLen; i++)
checksum += (uint8_t)srec->data[i];
/* One's complement the checksum */
checksum = ~checksum;
return checksum;
}