1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2000
4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 */
6
7#include <common.h>
8#include <s_record.h>
9
10static int hex1_bin (char  c);
11static int hex2_bin (char *s);
12
13int srec_decode (char *input, int *count, ulong *addr, char *data)
14{
15	int	i;
16	int	v;				/* conversion buffer	*/
17	int	srec_type;			/* S-Record type	*/
18	unsigned char chksum;			/* buffer for checksum	*/
19
20	chksum = 0;
21
22	/* skip anything before 'S', and the 'S' itself.
23	 * Return error if not found
24	 */
25
26	for (; *input; ++input) {
27		if (*input == 'S') {		/* skip 'S' */
28			++input;
29			break;
30		}
31	}
32	if (*input == '\0') {			/* no more data?	*/
33		return (SREC_EMPTY);
34	}
35
36	v = *input++;				/* record type		*/
37
38	if ((*count = hex2_bin(input)) < 0) {
39		return (SREC_E_NOSREC);
40	}
41
42	chksum += *count;
43	input  += 2;
44
45	switch (v) {				/* record type		*/
46
47	case '0':				/* start record		*/
48		srec_type = SREC_START;		/* 2 byte addr field	*/
49		*count   -= 3;			/* - checksum and addr	*/
50		break;
51	case '1':
52		srec_type = SREC_DATA2;		/* 2 byte addr field	*/
53		*count   -= 3;			/* - checksum and addr	*/
54		break;
55	case '2':
56		srec_type = SREC_DATA3;		/* 3 byte addr field	*/
57		*count   -= 4;			/* - checksum and addr	*/
58		break;
59	case '3':				/* data record with a	*/
60		srec_type = SREC_DATA4;		/* 4 byte addr field	*/
61		*count   -= 5;			/* - checksum and addr	*/
62		break;
63/***	case '4'  ***/
64	case '5':			/* count record, addr field contains */
65		srec_type = SREC_COUNT;	/* a 2 byte record counter	*/
66		*count    = 0;			/* no data		*/
67		break;
68/***	case '6' -- not used  ***/
69	case '7':				/* end record with a	*/
70		srec_type = SREC_END4;		/* 4 byte addr field	*/
71		*count   -= 5;			/* - checksum and addr	*/
72		break;
73	case '8':				/* end record with a	*/
74		srec_type = SREC_END3;		/* 3 byte addr field	*/
75		*count   -= 4;			/* - checksum and addr	*/
76		break;
77	case '9':				/* end record with a	*/
78		srec_type = SREC_END2;		/* 2 byte addr field	*/
79		*count   -= 3;			/* - checksum and addr	*/
80		break;
81	default:
82		return (SREC_E_BADTYPE);
83	}
84
85	/* read address field */
86	*addr = 0;
87
88	switch (v) {
89	case '3':				/* 4 byte addr field	*/
90	case '7':
91		if ((v = hex2_bin(input)) < 0) {
92			return (SREC_E_NOSREC);
93		}
94		*addr  += v;
95		chksum += v;
96		input  += 2;
97		/* FALL THRU */
98	case '2':				/* 3 byte addr field	*/
99	case '8':
100		if ((v = hex2_bin(input)) < 0) {
101			return (SREC_E_NOSREC);
102		}
103		*addr <<= 8;
104		*addr  += v;
105		chksum += v;
106		input  += 2;
107		/* FALL THRU */
108	case '0':				/* 2 byte addr field	*/
109	case '1':
110	case '5':
111	case '9':
112		if ((v = hex2_bin(input)) < 0) {
113			return (SREC_E_NOSREC);
114		}
115		*addr <<= 8;
116		*addr  += v;
117		chksum += v;
118		input  += 2;
119
120		if ((v = hex2_bin(input)) < 0) {
121			return (SREC_E_NOSREC);
122		}
123		*addr <<= 8;
124		*addr  += v;
125		chksum += v;
126		input  += 2;
127
128		break;
129	default:
130		return (SREC_E_BADTYPE);
131	}
132
133	/* convert data and calculate checksum */
134	for (i=0; i < *count; ++i) {
135		if ((v = hex2_bin(input)) < 0) {
136			return (SREC_E_NOSREC);
137		}
138		data[i] = v;
139		chksum += v;
140		input  += 2;
141	}
142
143	/* read anc check checksum */
144	if ((v = hex2_bin(input)) < 0) {
145		return (SREC_E_NOSREC);
146	}
147
148	if ((unsigned char)v != (unsigned char)~chksum) {
149		return (SREC_E_BADCHKS);
150	}
151
152	return (srec_type);
153}
154
155static int hex1_bin (char c)
156{
157	if (c >= '0' && c <= '9')
158		return (c - '0');
159	if (c >= 'a' && c <= 'f')
160		return (c + 10 - 'a');
161	if (c >= 'A' && c <= 'F')
162		return (c + 10 - 'A');
163	return (-1);
164}
165
166static int hex2_bin (char *s)
167{
168	int i, j;
169
170	if ((i = hex1_bin(*s++)) < 0) {
171		return (-1);
172	}
173	if ((j = hex1_bin(*s)) < 0) {
174		return (-1);
175	}
176
177	return ((i<<4) + j);
178}
179