1 /*********************************************************************
2  *
3  *   LPC ISP - Utility functions
4  *
5  *
6  * Written by Nathael Pajani <nathael.pajani@nathael.net>
7  *
8  * This programm is released under the terms of the GNU GPLv3 licence
9  * as can be found on the GNU website : <http://www.gnu.org/licenses/>
10  *
11  *********************************************************************/
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <stdint.h>
18 #include <unistd.h> /* for open, close */
19 #include <fcntl.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <errno.h>
24 #include <string.h> /* memcpy */
26 #include <termios.h> /* serial */
27 #include <ctype.h>
30 #define FILE_CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP)
32 extern int trace_on;
34 /* display data as in hexdump -C :
35    00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
36  */
37 void isp_dump(const unsigned char* buf, unsigned int buf_size)
38 {
39         unsigned int count = 0;
40         char pass = 0;
41         if (buf_size == 0) {
42                 return;
43         }
44         while ((count < buf_size) || (pass == 0)) {
45                 if (count == buf_size) {
46                         while (count & 0x0F) {
47                                 printf("   ");
48                                 count++;
49                         }
50                         pass = 1;
51                         count -= 0x10;
52                 }
53                 if ((count % 0x10) == 0) {
54                         if (pass == 0) {
55                                 printf(" %08x  ", count);
56                         } else {
57                                 printf(" |");
58                         }
59                 }
60                 if (pass == 0) {
61                         printf("%02x ", buf[count]);
62                 } else {
63                         if (isgraph((int)buf[count])) {
64                                 printf("%c", buf[count]);
65                         } else {
66                                 printf(".");
67                         }
68                 }
69                 count++;
70                 if ((count % 0x10) == 0) {
71                         if (pass == 0) {
72                                 count -= 0x10;
73                         } else {
74                                 if (count == buf_size) {
75                                         break; /* prevent infinite loop */
76                                 }
77                                 printf("|\n");
78                         }
79                         pass = !pass;
80                 }
81         }
82         printf("|\n");
83 }
86 /* ---- Serial utility functions ---------------------------------------------------*/
88 static int serial_fd = -1;
90 /* Open the serial device and set it up.
91  * Returns 0 on success, negativ value on error.
92  * Actal setup is done according to LPC11xx user's manual.
93  * Only baudrate can be changed using command line option.
94  */
95 int isp_serial_open(int baudrate, char* serial_device)
96 {
97         struct termios tio;
99         if (serial_device == NULL) {
100                 printf("No serial device given on command line\n");
101                 return -2;
102         }
104         /* Open serial port */
105         serial_fd = open(serial_device, O_RDWR | O_NONBLOCK);
106         if (serial_fd < 0) {
107                 perror("Unable to open serial_device");
108                 printf("Tried to open \"%s\".\n", serial_device);
109                 return -1;
110         }
111         /* Setup serial port */
112         memset(&tio, 0, sizeof(tio));
113         tio.c_iflag = IXON | IXOFF;  /* See section 21.4.4 of LPC11xx user's manual (UM10398) */
114         tio.c_cflag = CS8 | CREAD | CLOCAL;  /* 8n1, see termios.h for more information */
115         tio.c_cc[VMIN] = 1;
116         tio.c_cc[VTIME] = 5;
117         cfsetospeed(&tio, baudrate);
118         cfsetispeed(&tio, baudrate);
119         tcsetattr(serial_fd, TCSANOW, &tio);
121         return 0;
124 void isp_serial_close(void)
126         close(serial_fd);
129 /* Simple write() wrapper, with trace if enabled */
130 int isp_serial_write(const char* buf, unsigned int buf_size)
132         int nb = 0;
134         if (trace_on) {
135                 printf("Sending %d octet(s) :\n", buf_size);
136                 isp_dump((unsigned char*)buf, buf_size);
137         }
138         nb = write(serial_fd, buf, buf_size);
139         if (nb <= 0) {
140                 perror("Serial write error");
141                 return -1;
142         }
143         return nb;
146 static char next_read_char = 0;
147 void isp_serial_empty_buffer()
149         int nb = 0;
150         char unused = 0;
151         unsigned int loops = 0; /* Used to create a timeout */
153         do {
154                 nb = read(serial_fd, &unused, 1);
155                 if (nb < 0) {
156                         if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
157                                 if (loops++ > 100) {
158                                         break; /* timeout at 500ms */
159                                 }
160                                 usleep( 5000 );
161                                 continue;
162                         }
163                         perror("Serial read error");
164                         return;
165                 } else if (nb == 0) {
166                         printf("serial_read: end of file !!!!\n");
167                         return;
168                 }
169         } while ((unused != '\r') && (unused != '\n'));
171         /* This should be improved by reading ALL \r and \n */
172         if (unused == '\r') {
173                 nb = read(serial_fd, &unused, 1);
174         }
175         if (unused == '\n') {
176                 return;
177         }
178         next_read_char = unused;
181 /* Try to read at least "min_read" characters from the serial line.
182  * Returns -1 on error, 0 on end of file, or read count otherwise.
183  */
184 int isp_serial_read(char* buf, unsigned int buf_size, unsigned int min_read)
186         int nb = 0;
187         unsigned int count = 0;
188         unsigned int loops = 0; /* Used to create a timeout */
190         if (min_read > buf_size) {
191                 printf("serial_read: buffer too small for min read value.\n");
192                 return -3;
193         }
194         if (next_read_char != 0) {
195                 buf[count++] = next_read_char;
196                 next_read_char = 0;
197         }
199         do {
200                 nb = read(serial_fd, &buf[count], (buf_size - count));
201                 if (nb < 0) {
202                         if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
203                                 if (loops++ > 100) {
204                                         break; /* timeout at 500ms */
205                                 }
206                                 usleep( 5000 );
207                                 continue;
208                         }
209                         perror("Serial read error");
210                         return -1;
211                 } else if (nb == 0) {
212                         printf("serial_read: end of file !!!!\n");
213                         return 0;
214                 }
215                 if (trace_on == 2) {
216                         isp_dump((unsigned char*)(&buf[count]), nb);
217                 }
218                 count += nb;
219         } while (count < min_read);
221         if (trace_on) {
222                 printf("Received %d octet(s) :\n", count);
223                 isp_dump((unsigned char*)buf, count);
224         }
225         return count;
229 /* ---- UU_Encoding utility functions ----------------------------------------------*/
231 /* This might have been taken from a lib, but I hate lib dependencies, and installing
232  *  a full window manager (or MTA or MUA for instance) for two functions is not an
233  *  option */
234 #define UUENCODE_ADDED_VAL 32
235 #define LINE_DATA_LENGTH_MAX 45
236 #define LINE_LENGTH_NULL 96
238 int isp_uu_encode(char* dest, char* src, unsigned int orig_size)
240         unsigned int new_size = 0;
241         unsigned int pos = 0;
243         while (pos < orig_size) {
244                 unsigned int line_length = orig_size - pos;
245                 unsigned int i = 0;
247                 /* Start with line length */
248                 if (line_length > LINE_DATA_LENGTH_MAX) {
249                         line_length = LINE_DATA_LENGTH_MAX;
250                 }
251                 dest[new_size] = line_length + UUENCODE_ADDED_VAL;
252                 new_size++;
254                 /* Encode line */
255                 while (i < line_length) {
256                         uint32_t int_triplet = 0;
257                         int j = 0;
259                         /* Get original triplet (three bytes) */
260                         for (j=0; j<3; j++) {
261                                 int_triplet |= ((src[pos + i + j] & 0xFF) << (8 * (2 - j)));
262                                 /* if not enougth data, leave it as nul */
263                                 if ((i + j) > line_length) {
264                                         break;
265                                 }
266                         }
267                         for (j=0; j<4; j++) {
268                                 /* Store triplet in four bytes */
269                                 dest[new_size + j] = ((int_triplet >> (6 * (3 - j))) & 0x3F);
270                                 /* Add offset */
271                                 dest[new_size + j] += UUENCODE_ADDED_VAL;
272                         }
273                         i += 3;
274                         new_size += 4;
275                 }
276                 pos += line_length;
278                 /* Add \r\n */
279                 dest[new_size++] = '\r';
280                 dest[new_size++] = '\n';
281         }
282         return new_size;
285 int isp_uu_decode(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 = 0;
292                 unsigned int i = 0;
293                 int j = 0;
295                 /* Read line length */
296                 line_length = src[pos] - UUENCODE_ADDED_VAL;
297                 if (src[pos] == LINE_LENGTH_NULL) {
298                         /* Empty line ? then we are done converting
299                          * (should not happen in communication with ISP) */
300                         return new_size;
301                 }
302                 pos++;
304                 /* Decode line */
305                 while (i < line_length) {
306                         char quartet[4];
307                         uint32_t int_triplet = 0;
308                         /* copy data */
309                         memcpy(quartet, &src[pos], 4);
310                         pos += 4;
311                         /* Get the original bits */
312                         for (j=0; j<4; j++) {
313                                 /* Remove the offset added by uuencoding */
314                                 quartet[j] -= UUENCODE_ADDED_VAL;
315                                 int_triplet |= ((quartet[j] & 0x3F) << ((3 - j) * 6));
316                         }
317                         /* And store them */
318                         for (j=2; j>=0; j--) {
319                                 dest[new_size++] = ((int_triplet >> (j * 8)) & 0xFF );
320                                 i++;
321                                 if (i >= line_length) {
322                                         break;
323                                 }
324                         }
325                 }
327                 /* Find next line */
328                 while ((src[pos] < UUENCODE_ADDED_VAL) && (pos < orig_size))   {
329                         pos++;
330                 }
331         }
332         return new_size;
337 /* ---- File utility functions ----------------------------------------------*/
339 int isp_buff_to_file(char* data, unsigned int len, char* filename)
341         int ret = 0;
342         int out_fd = -1;
344         /* Open file for writing if output is not stdout */
345         if (strncmp(filename, "-", strlen(filename)) == 0) {
346                 out_fd = STDOUT_FILENO;
347         } else {
348                 out_fd = open(filename, (O_WRONLY | O_CREAT | O_TRUNC), FILE_CREATE_MODE);
349                 if (out_fd <= 0) {
350                         perror("Unable to open or create file for writing");
351                         printf("Tried to open \"%s\".\n", filename);
352                         return -1;
353                 }
354         }
356         /* Write data to file */
357         ret = write(out_fd, data, len);
358         if (ret != (int)len) {
359                 perror("File write error");
360                 printf("Unable to write %u bytes of data to file \"%s\", returned %d !",
361                                 len, filename, ret);
362                 ret = -2;
363         }
365         /* Close file */
366         if (out_fd != STDOUT_FILENO) {
367                 close(out_fd);
368         }
370         return ret;
373 int isp_file_to_buff(char* data, unsigned int len, char* filename)
375         int in_fd = -1;
376         unsigned int bytes_read = 0;
378         /* Open file for reading if input is not stdin */
379         if (strncmp(filename, "-", strlen(filename)) == 0) {
380                 /* FIXME : set as non blocking ? */
381                 in_fd = STDIN_FILENO;
382         } else {
383                 in_fd = open(filename, O_RDONLY | O_NONBLOCK);
384                 if (in_fd <= 0) {
385                         perror("Unable to open file for reading");
386             printf("Tried to open \"%s\".\n", filename);
387             return -13;
388         }
389     }
391     /* Read file content */
392     do {
393         int nb = read(in_fd, &data[bytes_read], (len - bytes_read));
394         if (nb < 0) {
395             if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
396                 usleep( 50 );
397                 continue;
398             }
399             perror("Input file read error");
400             if (in_fd != STDIN_FILENO) {
401                 close(in_fd);
402             }
403             return -12;
404         } else if (nb == 0) {
405             /* End of file */
406             break;
407         }
408         bytes_read += nb;
409     } while (bytes_read < len);
411     if (in_fd != STDIN_FILENO) {
412         close(in_fd);
413     }
415         return bytes_read;