1 /*********************************************************************
2  *
3  *   LPC ISP - Utility functions
4  *
5  *
6  *  Copyright (C) 2012 Nathael Pajani <nathael.pajani@nathael.net>
7  *
8  *  This program is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  *********************************************************************/
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <stdint.h>
28 #include <unistd.h> /* for open, close */
29 #include <fcntl.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <errno.h>
34 #include <string.h> /* memcpy */
36 #include <termios.h> /* serial */
37 #include <ctype.h>
40 #define FILE_CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP)
42 extern int trace_on;
44 /* display data as in hexdump -C :
45    00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
46  */
47 void isp_dump(const unsigned char* buf, unsigned int buf_size)
48 {
49         unsigned int count = 0;
50         char pass = 0;
51         if (buf_size == 0) {
52                 return;
53         }
54         while ((count < buf_size) || (pass == 0)) {
55                 if (count == buf_size) {
56                         while (count & 0x0F) {
57                                 printf("   ");
58                                 count++;
59                         }
60                         pass = 1;
61                         count -= 0x10;
62                 }
63                 if ((count % 0x10) == 0) {
64                         if (pass == 0) {
65                                 printf(" %08x  ", count);
66                         } else {
67                                 printf(" |");
68                         }
69                 }
70                 if (pass == 0) {
71                         printf("%02x ", buf[count]);
72                 } else {
73                         if (isgraph((int)buf[count])) {
74                                 printf("%c", buf[count]);
75                         } else {
76                                 printf(".");
77                         }
78                 }
79                 count++;
80                 if ((count % 0x10) == 0) {
81                         if (pass == 0) {
82                                 count -= 0x10;
83                         } else {
84                                 if (count == buf_size) {
85                                         break; /* prevent infinite loop */
86                                 }
87                                 printf("|\n");
88                         }
89                         pass = !pass;
90                 }
91         }
92         printf("|\n");
93 }
96 /* ---- Serial utility functions ---------------------------------------------------*/
98 static int serial_fd = -1;
100 /* Open the serial device and set it up.
101  * Returns 0 on success, negativ value on error.
102  * Actal setup is done according to LPC11xx user's manual.
103  * Only baudrate can be changed using command line option.
104  */
105 int isp_serial_open(int baudrate, char* serial_device)
107         struct termios tio;
109         if (serial_device == NULL) {
110                 printf("No serial device given on command line\n");
111                 return -2;
112         }
114         /* Open serial port */
115         serial_fd = open(serial_device, O_RDWR | O_NONBLOCK);
116         if (serial_fd < 0) {
117                 perror("Unable to open serial_device");
118                 printf("Tried to open \"%s\".\n", serial_device);
119                 return -1;
120         }
121         /* Setup serial port */
122         memset(&tio, 0, sizeof(tio));
123         tio.c_iflag = IXON | IXOFF;  /* See section 21.4.4 of LPC11xx user's manual (UM10398) */
124         tio.c_cflag = CS8 | CREAD | CLOCAL;  /* 8n1, see termios.h for more information */
125         tio.c_cc[VMIN] = 1;
126         tio.c_cc[VTIME] = 5;
127         cfsetospeed(&tio, baudrate);
128         cfsetispeed(&tio, baudrate);
129         tcsetattr(serial_fd, TCSANOW, &tio);
131         return 0;
134 void isp_serial_close(void)
136         close(serial_fd);
139 /* Simple write() wrapper, with trace if enabled */
140 int isp_serial_write(const char* buf, unsigned int buf_size)
142         int nb;
143         unsigned int count = 0;
144         unsigned int loops = 0; /* Used to create a timeout */
146         if (trace_on) {
147                 printf("Sending %d byte(s) :\n", buf_size);
148                 isp_dump((unsigned char*)buf, buf_size);
149         }
150         do {
151                 nb = write(serial_fd, buf + count, buf_size - count);
152                 if (nb < 0) {
153                         if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
154                                 if (loops++ > 100) {
155                                         break; /* timeout at 500ms */
156                                 }
157                                 usleep( 5000 );
158                                 continue;
159                         }
160                         perror("Serial write error");
161                         return -1;
162                 }
163                 count += nb;
164         } while (count < buf_size);
165         return count;
168 /* Empty internal buffer of USB-serial bridge
169  * Some USB-Serial bridge include an internal buffer which must be emptied
170  * before sending the first command in order to be able to read the replies
171  * or the bridge would drop the reply if it's internal buffer is full.
172  * This one has no timeout and should end with EWOULDBLOCK or EAGAIN once the
173  * buffer is empty.
174  */
175 void isp_serial_flush_input()
177         int nb;
178         char unused;
179         do {
180                 nb = read(serial_fd, &unused, 1);
181         } while (nb > 0);
182         if (nb == 0) {
183                 printf("serial_read: end of file !!!!\n");
184         } else if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) {
185                 perror("Serial read error");
186         }
189 /* Empty read buffer between commands
190  * The micor-controller has echo on, so we must read the echoed command
191  * before reading the reply.
192  */
193 static char next_read_char = 0;
194 void isp_serial_empty_buffer()
196         int nb = 0;
197         char unused = 0;
198         unsigned int loops = 0; /* Used to create a timeout */
200         do {
201                 nb = read(serial_fd, &unused, 1);
202                 if (nb < 0) {
203                         if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
204                                 if (loops++ > 100) {
205                                         break; /* timeout at 500ms */
206                                 }
207                                 usleep( 5000 );
208                                 continue;
209                         }
210                         perror("Serial read error");
211                         return;
212                 } else if (nb == 0) {
213                         printf("serial_read: end of file !!!!\n");
214                         return;
215                 }
216         } while ((unused != '\r') && (unused != '\n'));
218         /* This should be improved by reading ALL \r and \n */
219         if (unused == '\r') {
220                 nb = read(serial_fd, &unused, 1);
221         }
222         if (unused == '\n') {
223                 return;
224         }
225         next_read_char = unused;
228 /* Try to read at least "min_read" characters from the serial line.
229  * Returns -1 on error, 0 on end of file, or read count otherwise.
230  */
231 int isp_serial_read(char* buf, unsigned int buf_size, unsigned int min_read)
233         int nb = 0;
234         unsigned int count = 0;
235         unsigned int loops = 0; /* Used to create a timeout */
237         if (min_read > buf_size) {
238                 printf("serial_read: buffer too small for min read value.\n");
239                 return -3;
240         }
241         if (next_read_char != 0) {
242                 buf[count++] = next_read_char;
243                 next_read_char = 0;
244         }
246         do {
247                 nb = read(serial_fd, &buf[count], (buf_size - count));
248                 if (nb < 0) {
249                         if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
250                                 if (loops++ > 100) {
251                                         break; /* timeout at 500ms */
252                                 }
253                                 usleep( 5000 );
254                                 continue;
255                         }
256                         perror("Serial read error");
257                         return -1;
258                 } else if (nb == 0) {
259                         printf("serial_read: end of file !!!!\n");
260                         return 0;
261                 }
262                 if (trace_on == 2) {
263                         isp_dump((unsigned char*)(&buf[count]), nb);
264                 }
265                 count += nb;
266         } while (count < min_read);
268         if (trace_on) {
269                 printf("Received %d byte(s) :\n", count);
270                 isp_dump((unsigned char*)buf, count);
271         }
272         return count;
276 /* ---- UU_Encoding utility functions ----------------------------------------------*/
278 /* This might have been taken from a lib, but I hate lib dependencies, and installing
279  *  a full window manager (or MTA or MUA for instance) for two functions is not an
280  *  option */
281 #define UUENCODE_ADDED_VAL 32
282 #define UUENCODE_ZERO 96
283 #define LINE_DATA_LENGTH_MAX 45
285 int isp_uu_encode(char* dest, char* src, unsigned int orig_size)
287         unsigned int new_size = 0;
288         unsigned int pos = 0;
290         while (pos < orig_size) {
291                 unsigned int line_length = orig_size - pos;
292                 unsigned int i = 0;
294                 /* Start with line length */
295                 if (line_length > LINE_DATA_LENGTH_MAX) {
296                         line_length = LINE_DATA_LENGTH_MAX;
297                 }
298                 dest[new_size] = line_length + UUENCODE_ADDED_VAL;
299                 new_size++;
301                 /* Encode line */
302                 while (i < line_length) {
303                         uint32_t int_triplet = 0;
304                         int j = 0;
306                         /* Get original triplet (three bytes) */
307                         for (j=0; j<3; j++) {
308                                 int_triplet |= ((src[pos + i + j] & 0xFF) << (8 * (2 - j)));
309                                 /* if not enougth data, leave it as nul */
310                                 if ((i + j) > line_length) {
311                                         break;
312                                 }
313                         }
314                         for (j=0; j<4; j++) {
315                                 /* Store triplet in four bytes */
316                                 dest[new_size + j] = ((int_triplet >> (6 * (3 - j))) & 0x3F);
317                                 /* Add offset */
318                                 if (dest[new_size + j]) {
319                                         dest[new_size + j] += UUENCODE_ADDED_VAL;
320                                 } else {
321                                         dest[new_size + j] = UUENCODE_ZERO;
322                                 }
323                         }
324                         i += 3;
325                         new_size += 4;
326                 }
327                 pos += line_length;
329                 /* Add \r\n */
330                 dest[new_size++] = '\r';
331                 dest[new_size++] = '\n';
332         }
333         return new_size;
336 int isp_uu_decode(char* dest, char* src, unsigned int orig_size)
338         unsigned int new_size = 0;
339         unsigned int pos = 0;
341         while (pos < orig_size) {
342                 unsigned int line_length = 0;
343                 unsigned int i = 0;
344                 int j = 0;
346                 /* Read line length */
347                 line_length = src[pos] - UUENCODE_ADDED_VAL;
348                 if (src[pos] == UUENCODE_ZERO) {
349                         /* Empty line ? then we are done converting
350                          * (should not happen in communication with ISP) */
351                         return new_size;
352                 }
353                 pos++;
355                 /* Decode line */
356                 while (i < line_length) {
357                         char quartet[4];
358                         uint32_t int_triplet = 0;
359                         /* copy data */
360                         memcpy(quartet, &src[pos], 4);
361                         pos += 4;
362                         /* Get the original bits */
363                         for (j=0; j<4; j++) {
364                                 /* Remove the offset added by uuencoding */
365                                 quartet[j] -= UUENCODE_ADDED_VAL;
366                                 int_triplet |= ((quartet[j] & 0x3F) << ((3 - j) * 6));
367                         }
368                         /* And store them */
369                         for (j=2; j>=0; j--) {
370                                 dest[new_size++] = ((int_triplet >> (j * 8)) & 0xFF );
371                                 i++;
372                                 if (i >= line_length) {
373                                         break;
374                                 }
375                         }
376                 }
378                 /* Find next line */
379                 while ((src[pos] < UUENCODE_ADDED_VAL) && (pos < orig_size))   {
380                         pos++;
381                 }
382         }
383         return new_size;
388 /* ---- File utility functions ----------------------------------------------*/
390 int isp_buff_to_file(char* data, unsigned int len, char* filename)
392         int ret = 0;
393         int out_fd = -1;
395         /* Open file for writing if output is not stdout */
396         if (strncmp(filename, "-", strlen(filename)) == 0) {
397                 out_fd = STDOUT_FILENO;
398         } else {
399                 out_fd = open(filename, (O_WRONLY | O_CREAT | O_TRUNC), FILE_CREATE_MODE);
400                 if (out_fd <= 0) {
401                         perror("Unable to open or create file for writing");
402                         printf("Tried to open \"%s\".\n", filename);
403                         return -1;
404                 }
405         }
407         /* Write data to file */
408         ret = write(out_fd, data, len);
409         if (ret != (int)len) {
410                 perror("File write error");
411                 printf("Unable to write %u bytes of data to file \"%s\", returned %d !",
412                                 len, filename, ret);
413                 ret = -2;
414         }
416         /* Close file */
417         if (out_fd != STDOUT_FILENO) {
418                 close(out_fd);
419         }
421         return ret;
424 int isp_file_to_buff(char* data, unsigned int len, char* filename)
426         int in_fd = -1;
427         unsigned int bytes_read = 0;
429         /* Open file for reading if input is not stdin */
430         if (strncmp(filename, "-", strlen(filename)) == 0) {
431                 /* FIXME : set as non blocking ? */
432                 in_fd = STDIN_FILENO;
433         } else {
434                 in_fd = open(filename, O_RDONLY | O_NONBLOCK);
435                 if (in_fd <= 0) {
436                         perror("Unable to open file for reading");
437             printf("Tried to open \"%s\".\n", filename);
438             return -13;
439         }
440     }
442     /* Read file content */
443     do {
444         int nb = read(in_fd, &data[bytes_read], (len - bytes_read));
445         if (nb < 0) {
446             if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
447                 usleep( 50 );
448                 continue;
449             }
450             perror("Input file read error");
451             if (in_fd != STDIN_FILENO) {
452                 close(in_fd);
453             }
454             return -12;
455         } else if (nb == 0) {
456             /* End of file */
457             break;
458         }
459         bytes_read += nb;
460     } while (bytes_read < len);
462     if (in_fd != STDIN_FILENO) {
463         close(in_fd);
464     }
466         return bytes_read;