1 /*********************************************************************
2  *
3  *   LPC1114 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 /* Try to read at least "min_read" characters from the serial line.
147  * Returns -1 on error, 0 on end of file, or read count otherwise.
148  */
149 int isp_serial_read(char* buf, unsigned int buf_size, unsigned int min_read)
151         int nb = 0;
152         unsigned int count = 0;
153         unsigned int loops = 0; /* Used to create a timeout */
155         if (min_read > buf_size) {
156                 printf("serial_read: buffer too small for min read value.\n");
157                 return -3;
158         }
160         do {
161                 nb = read(serial_fd, &buf[count], (buf_size - count));
162                 if (nb < 0) {
163                         if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
164                                 if (loops++ > 100) {
165                                         break; /* timeout at 500ms */
166                                 }
167                                 usleep( 5000 );
168                                 continue;
169                         }
170                         perror("Serial read error");
171                         return -1;
172                 } else if (nb == 0) {
173                         printf("serial_read: end of file !!!!\n");
174                         return 0;
175                 }
176                 if (trace_on == 2) {
177                         isp_dump((unsigned char*)(&buf[count]), nb);
178                 }
179                 count += nb;
180         } while (count < min_read);
182         if (trace_on) {
183                 printf("Received %d octet(s) :\n", count);
184                 isp_dump((unsigned char*)buf, count);
185         }
186         return count;
190 /* ---- UU_Encoding utility functions ----------------------------------------------*/
192 /* This might have been taken from a lib, but I hate lib dependencies, and installing
193  *  a full window manager (or MTA or MUA for instance) for two functions is not an
194  *  option */
195 #define UUENCODE_ADDED_VAL 32
196 #define LINE_DATA_LENGTH_MAX 45
197 #define LINE_LENGTH_NULL 96
199 int isp_uu_encode(char* dest, char* src, unsigned int orig_size)
201         unsigned int new_size = 0;
202         unsigned int pos = 0;
204         while (pos < orig_size) {
205                 unsigned int line_length = orig_size - pos;
206                 unsigned int i = 0;
208                 /* Start with line length */
209                 if (line_length > LINE_DATA_LENGTH_MAX) {
210                         line_length = LINE_DATA_LENGTH_MAX;
211                 }
212                 dest[new_size] = line_length + UUENCODE_ADDED_VAL;
213                 new_size++;
215                 /* Encode line */
216                 while (i < line_length) {
217                         uint32_t int_triplet = 0;
218                         int j = 0;
220                         /* Get original triplet (three bytes) */
221                         for (j=0; j<3; j++) {
222                                 int_triplet |= ((src[pos + i + j] & 0xFF) << (8 * (2 - j)));
223                                 /* if not enougth data, leave it as nul */
224                                 if ((i + j) > line_length) {
225                                         break;
226                                 }
227                         }
228                         for (j=0; j<4; j++) {
229                                 /* Store triplet in four bytes */
230                                 dest[new_size + j] = ((int_triplet >> (6 * (3 - j))) & 0x3F);
231                                 /* Add offset */
232                                 dest[new_size + j] += UUENCODE_ADDED_VAL;
233                         }
234                         i += 3;
235                         new_size += 4;
236                 }
237                 pos += line_length;
239                 /* Add \r\n */
240                 dest[new_size++] = '\r';
241                 dest[new_size++] = '\n';
242         }
243         return new_size;
246 int isp_uu_decode(char* dest, char* src, unsigned int orig_size)
248         unsigned int new_size = 0;
249         unsigned int pos = 0;
251         while (pos < orig_size) {
252                 unsigned int line_length = 0;
253                 unsigned int i = 0;
254                 int j = 0;
256                 /* Read line length */
257                 line_length = src[pos] - UUENCODE_ADDED_VAL;
258                 if (src[pos] == LINE_LENGTH_NULL) {
259                         /* Empty line ? then we are done converting
260                          * (should not happen in communication with ISP) */
261                         return new_size;
262                 }
263                 pos++;
265                 /* Decode line */
266                 while (i < line_length) {
267                         char quartet[4];
268                         uint32_t int_triplet = 0;
269                         /* copy data */
270                         memcpy(quartet, &src[pos], 4);
271                         pos += 4;
272                         /* Get the original bits */
273                         for (j=0; j<4; j++) {
274                                 /* Remove the offset added by uuencoding */
275                                 quartet[j] -= UUENCODE_ADDED_VAL;
276                                 int_triplet |= ((quartet[j] & 0x3F) << ((3 - j) * 6));
277                         }
278                         /* And store them */
279                         for (j=2; j>=0; j--) {
280                                 dest[new_size++] = ((int_triplet >> (j * 8)) & 0xFF );
281                                 i++;
282                                 if (i >= line_length) {
283                                         break;
284                                 }
285                         }
286                 }
288                 /* Find next line */
289                 while ((src[pos] < UUENCODE_ADDED_VAL) && (pos < orig_size))   {
290                         pos++;
291                 }
292         }
293         return new_size;
298 /* ---- File utility functions ----------------------------------------------*/
300 int isp_buff_to_file(char* data, unsigned int len, char* filename)
302         int ret = 0;
303         int out_fd = -1;
305         /* Open file for writing if output is not stdout */
306         if (strncmp(filename, "-", strlen(filename)) == 0) {
307                 out_fd = STDOUT_FILENO;
308         } else {
309                 out_fd = open(filename, (O_WRONLY | O_CREAT | O_TRUNC), FILE_CREATE_MODE);
310                 if (out_fd <= 0) {
311                         perror("Unable to open or create file for writing");
312                         printf("Tried to open \"%s\".\n", filename);
313                         return -1;
314                 }
315         }
317         /* Write data to file */
318         ret = write(out_fd, data, len);
319         if (ret != (int)len) {
320                 perror("File write error");
321                 printf("Unable to write %u bytes of data to file \"%s\", returned %d !",
322                                 len, filename, ret);
323                 ret = -2;
324         }
326         /* Close file */
327         if (out_fd != STDOUT_FILENO) {
328                 close(out_fd);
329         }
331         return ret;