File: isp_utils.c

package info (click to toggle)
lpctools 1.07-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 240 kB
  • sloc: ansic: 2,075; makefile: 41
file content (427 lines) | stat: -rw-r--r-- 10,185 bytes parent folder | download | duplicates (2)
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
/*********************************************************************
 *
 *   LPC ISP - Utility functions
 *
 *
 *  Copyright (C) 2012 Nathael Pajani <nathael.pajani@nathael.net>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *********************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

#include <unistd.h> /* for open, close */
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

#include <string.h> /* memcpy */

#include <termios.h> /* serial */
#include <ctype.h>


#define FILE_CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP)

extern int trace_on;

/* display data as in hexdump -C :
   00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
 */
void isp_dump(const unsigned char* buf, unsigned int buf_size)
{
	unsigned int count = 0;
	char pass = 0;
	if (buf_size == 0) {
		return;
	}
	while ((count < buf_size) || (pass == 0)) {
		if (count == buf_size) {
			while (count & 0x0F) {
				printf("   ");
				count++;
			}
			pass = 1;
			count -= 0x10;
		}
		if ((count % 0x10) == 0) {
			if (pass == 0) {
				printf(" %08x  ", count);
			} else {
				printf(" |");
			}
		}
		if (pass == 0) {
			printf("%02x ", buf[count]);
		} else {
			if (isgraph((int)buf[count])) {
				printf("%c", buf[count]);
			} else {
				printf(".");
			}
		}
		count++;
		if ((count % 0x10) == 0) {
			if (pass == 0) {
				count -= 0x10;
			} else {
				if (count == buf_size) {
					break; /* prevent infinite loop */
				}
				printf("|\n");
			}
			pass = !pass;
		}
	}
	printf("|\n");
}


/* ---- Serial utility functions ---------------------------------------------------*/

static int serial_fd = -1;

/* Open the serial device and set it up.
 * Returns 0 on success, negativ value on error.
 * Actal setup is done according to LPC11xx user's manual.
 * Only baudrate can be changed using command line option.
 */
int isp_serial_open(int baudrate, char* serial_device)
{
	struct termios tio;

	if (serial_device == NULL) {
		printf("No serial device given on command line\n");
		return -2;
	}

	/* Open serial port */
	serial_fd = open(serial_device, O_RDWR | O_NONBLOCK);
	if (serial_fd < 0) {
		perror("Unable to open serial_device");
		printf("Tried to open \"%s\".\n", serial_device);
		return -1;
	}
	/* Setup serial port */
	memset(&tio, 0, sizeof(tio));
	tio.c_iflag = IXON | IXOFF;  /* See section 21.4.4 of LPC11xx user's manual (UM10398) */
	tio.c_cflag = CS8 | CREAD | CLOCAL;  /* 8n1, see termios.h for more information */
	tio.c_cc[VMIN] = 1;
	tio.c_cc[VTIME] = 5;
	cfsetospeed(&tio, baudrate);
	cfsetispeed(&tio, baudrate);
	tcsetattr(serial_fd, TCSANOW, &tio);

	return 0;
}

void isp_serial_close(void)
{
	close(serial_fd);
}

/* Simple write() wrapper, with trace if enabled */
int isp_serial_write(const char* buf, unsigned int buf_size)
{
	int nb = 0;

	if (trace_on) {
		printf("Sending %d octet(s) :\n", buf_size);
		isp_dump((unsigned char*)buf, buf_size);
	}
	nb = write(serial_fd, buf, buf_size);
	if (nb <= 0) {
		perror("Serial write error");
		return -1;
	}
	return nb;
}

static char next_read_char = 0;
void isp_serial_empty_buffer()
{
	int nb = 0;
	char unused = 0;
	unsigned int loops = 0; /* Used to create a timeout */

	do {
		nb = read(serial_fd, &unused, 1);
		if (nb < 0) {
			if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
				if (loops++ > 100) {
					break; /* timeout at 500ms */
				}
				usleep( 5000 );
				continue;
			}
			perror("Serial read error");
			return;
		} else if (nb == 0) {
			printf("serial_read: end of file !!!!\n");
			return;
		}
	} while ((unused != '\r') && (unused != '\n'));

	/* This should be improved by reading ALL \r and \n */
	if (unused == '\r') {
		nb = read(serial_fd, &unused, 1);
	}
	if (unused == '\n') {
		return;
	}
	next_read_char = unused;
}

/* Try to read at least "min_read" characters from the serial line.
 * Returns -1 on error, 0 on end of file, or read count otherwise.
 */
int isp_serial_read(char* buf, unsigned int buf_size, unsigned int min_read)
{
	int nb = 0;
	unsigned int count = 0;
	unsigned int loops = 0; /* Used to create a timeout */

	if (min_read > buf_size) {
		printf("serial_read: buffer too small for min read value.\n");
		return -3;
	}
	if (next_read_char != 0) {
		buf[count++] = next_read_char;
		next_read_char = 0;
	}

	do {
		nb = read(serial_fd, &buf[count], (buf_size - count));
		if (nb < 0) {
			if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
				if (loops++ > 100) {
					break; /* timeout at 500ms */
				}
				usleep( 5000 );
				continue;
			}
			perror("Serial read error");
			return -1;
		} else if (nb == 0) {
			printf("serial_read: end of file !!!!\n");
			return 0;
		}
		if (trace_on == 2) {
			isp_dump((unsigned char*)(&buf[count]), nb);
		}
		count += nb;
	} while (count < min_read);

	if (trace_on) {
		printf("Received %d octet(s) :\n", count);
		isp_dump((unsigned char*)buf, count);
	}
	return count;
}


/* ---- UU_Encoding utility functions ----------------------------------------------*/

/* This might have been taken from a lib, but I hate lib dependencies, and installing
 *  a full window manager (or MTA or MUA for instance) for two functions is not an
 *  option */
#define UUENCODE_ADDED_VAL 32
#define LINE_DATA_LENGTH_MAX 45
#define LINE_LENGTH_NULL 96

int isp_uu_encode(char* dest, char* src, unsigned int orig_size)
{
	unsigned int new_size = 0;
	unsigned int pos = 0;

	while (pos < orig_size) {
		unsigned int line_length = orig_size - pos;
		unsigned int i = 0;

		/* Start with line length */
		if (line_length > LINE_DATA_LENGTH_MAX) {
			line_length = LINE_DATA_LENGTH_MAX;
		}
		dest[new_size] = line_length + UUENCODE_ADDED_VAL;
		new_size++;

		/* Encode line */
		while (i < line_length) {
			uint32_t int_triplet = 0;
			int j = 0;

			/* Get original triplet (three bytes) */
			for (j=0; j<3; j++) {
				int_triplet |= ((src[pos + i + j] & 0xFF) << (8 * (2 - j)));
				/* if not enougth data, leave it as nul */
				if ((i + j) > line_length) {
					break;
				}
			}
			for (j=0; j<4; j++) {
				/* Store triplet in four bytes */
				dest[new_size + j] = ((int_triplet >> (6 * (3 - j))) & 0x3F);
				/* Add offset */
				dest[new_size + j] += UUENCODE_ADDED_VAL;
			}
			i += 3;
			new_size += 4;
		}
		pos += line_length;

		/* Add \r\n */
		dest[new_size++] = '\r';
		dest[new_size++] = '\n';
	}
	return new_size;
}

int isp_uu_decode(char* dest, char* src, unsigned int orig_size)
{
	unsigned int new_size = 0;
	unsigned int pos = 0;

	while (pos < orig_size) {
		unsigned int line_length = 0;
		unsigned int i = 0;
		int j = 0;

		/* Read line length */
		line_length = src[pos] - UUENCODE_ADDED_VAL;
		if (src[pos] == LINE_LENGTH_NULL) {
			/* Empty line ? then we are done converting
			 * (should not happen in communication with ISP) */
			return new_size;
		}
		pos++;

		/* Decode line */
		while (i < line_length) {
			char quartet[4];
			uint32_t int_triplet = 0;
			/* copy data */
			memcpy(quartet, &src[pos], 4);
			pos += 4;
			/* Get the original bits */
			for (j=0; j<4; j++) {
				/* Remove the offset added by uuencoding */
				quartet[j] -= UUENCODE_ADDED_VAL;
				int_triplet |= ((quartet[j] & 0x3F) << ((3 - j) * 6));
			}
			/* And store them */
			for (j=2; j>=0; j--) {
				dest[new_size++] = ((int_triplet >> (j * 8)) & 0xFF );
				i++;
				if (i >= line_length) {
					break;
				}
			}
		}

		/* Find next line */
		while ((src[pos] < UUENCODE_ADDED_VAL) && (pos < orig_size))   {
			pos++;
		}
	}
	return new_size;
}



/* ---- File utility functions ----------------------------------------------*/

int isp_buff_to_file(char* data, unsigned int len, char* filename)
{
	int ret = 0;
	int out_fd = -1;

	/* Open file for writing if output is not stdout */
	if (strncmp(filename, "-", strlen(filename)) == 0) {
		out_fd = STDOUT_FILENO;
	} else {
		out_fd = open(filename, (O_WRONLY | O_CREAT | O_TRUNC), FILE_CREATE_MODE);
		if (out_fd <= 0) {
			perror("Unable to open or create file for writing");
			printf("Tried to open \"%s\".\n", filename);
			return -1;
		}
	}

	/* Write data to file */
	ret = write(out_fd, data, len);
	if (ret != (int)len) {
		perror("File write error");
		printf("Unable to write %u bytes of data to file \"%s\", returned %d !",
				len, filename, ret);
		ret = -2;
	}

	/* Close file */
	if (out_fd != STDOUT_FILENO) {
		close(out_fd);
	}

	return ret;
}

int isp_file_to_buff(char* data, unsigned int len, char* filename)
{
	int in_fd = -1;
	unsigned int bytes_read = 0;

	/* Open file for reading if input is not stdin */
	if (strncmp(filename, "-", strlen(filename)) == 0) {
		/* FIXME : set as non blocking ? */
		in_fd = STDIN_FILENO;
	} else {
		in_fd = open(filename, O_RDONLY | O_NONBLOCK);
		if (in_fd <= 0) {
			perror("Unable to open file for reading");
            printf("Tried to open \"%s\".\n", filename);
            return -13;
        }
    }

    /* Read file content */
    do {
        int nb = read(in_fd, &data[bytes_read], (len - bytes_read));
        if (nb < 0) {
            if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                usleep( 50 );
                continue;
            }
            perror("Input file read error");
            if (in_fd != STDIN_FILENO) {
                close(in_fd);
            }
            return -12;
        } else if (nb == 0) {
            /* End of file */
            break;
        }
        bytes_read += nb;
    } while (bytes_read < len);

    if (in_fd != STDIN_FILENO) {
        close(in_fd);
    }

	return bytes_read;
}