Move xmodem protocol in appropriate dir.
[bertos.git] / bertos / net / xmodem.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2004, 2005, 2006, 2007 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 1999, 2001 Bernardo Innocenti <bernie@develer.com>
31  *
32  * -->
33  * \brief X-Modem serial transmission protocol (implementation)
34  *
35  * Supports the CRC-16 and 1K-blocks variants of the standard.
36  * \see ymodem.txt for the protocol description.
37  *
38  * \todo Break xmodem_send() and xmodem_recv() in smaller functions.
39  *
40  * \version $Id$
41  * \author Bernardo Innocenti <bernie@develer.com>
42  * \author Francesco Sacchi <batt@develer.com>
43  */
44
45
46 #include "xmodem.h"
47
48 #include <appconfig.h>
49 #include <string.h> /* for memset() */
50 #include <drv/ser.h>
51 #include <algo/crc.h>
52 #include <cfg/debug.h>
53
54
55
56
57 /**
58  * \name Protocol control codes
59  * \{
60  */
61 #define XM_SOH  0x01  /**< Start Of Header (128-byte block) */
62 #define XM_STX  0x02  /**< Start Of Header (1024-byte block) */
63 #define XM_EOT  0x04  /**< End Of Transmission */
64 #define XM_ACK  0x06  /**< Acknowledge block */
65 #define XM_NAK  0x15  /**< Negative Acknowledge */
66 #define XM_C    0x43  /**< Request CRC-16 transmission */
67 #define XM_CAN  0x18  /**< CANcel transmission */
68 /*\}*/
69
70 #define XM_MAXRETRIES     15  /**< Max retries before giving up */
71 #define XM_MAXCRCRETRIES   7  /**< Max retries before switching to BCC */
72
73 #if CONFIG_XMODEM_1KCRC == 1
74         #define XM_BUFSIZE       1024  /**< 1024 bytes of block buffer */
75 #else
76         #define XM_BUFSIZE       128   /**< 128 bytes of block buffer */
77 #endif
78
79
80 #if CONFIG_XMODEM_RECV
81 /**
82  * \brief Receive a file using the XModem protocol.
83  *
84  * \param port Serial port to use for transfer
85  * \param fd Destination file
86  *
87  * \note This function allocates a large amount of stack (\see XM_BUFSIZE).
88  */
89 bool xmodem_recv(struct KFileSerial *port, KFile *fd)
90 {
91         char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
92         int c, i, blocksize;
93         int blocknr = 0, last_block_done = 0, retries = 0;
94         char *buf;
95         uint8_t checksum;
96         uint16_t crc;
97         bool purge = false;
98         bool usecrc = true;
99
100
101         XMODEM_PROGRESS("Starting Transfer...\n");
102         purge = true;
103         ser_clearerr(port);
104
105         /* Send initial NAK to start transmission */
106         for(;;)
107         {
108                 if (XMODEM_CHECK_ABORT)
109                 {
110                         kfile_putc(XM_CAN, &port->fd);
111                         kfile_putc(XM_CAN, &port->fd);
112                         XMODEM_PROGRESS("Transfer aborted\n");
113                         return false;
114                 }
115
116                 /*
117                  * Discard incoming input until a timeout occurs, then send
118                  * a NAK to the transmitter.
119                  */
120                 if (purge)
121                 {
122                         purge = false;
123
124                         if (kfile_error(&port->fd))
125                                 XMODEM_PROGRESS("Retries %d\n", retries);
126
127                         ser_resync(port, 200);
128                         retries++;
129
130                         if (retries >= XM_MAXRETRIES)
131                         {
132                                 kfile_putc(XM_CAN, &port->fd);
133                                 kfile_putc(XM_CAN, &port->fd);
134                                 XMODEM_PROGRESS("Transfer aborted\n");
135                                 return false;
136                         }
137
138                         /* Transmission start? */
139                         if (blocknr == 0)
140                         {
141                                 if (retries < XM_MAXCRCRETRIES)
142                                 {
143                                         XMODEM_PROGRESS("Request Tx (CRC)\n");
144                                         kfile_putc(XM_C, &port->fd);
145                                 }
146                                 else
147                                 {
148                                         /* Give up with CRC and fall back to checksum */
149                                         usecrc = false;
150                                         XMODEM_PROGRESS("Request Tx (BCC)\n");
151                                         kfile_putc(XM_NAK, &port->fd);
152                                 }
153                         }
154                         else
155                                 kfile_putc(XM_NAK, &port->fd);
156                 }
157
158                 switch (kfile_getc(&port->fd))
159                 {
160                 #if XM_BUFSIZE >= 1024
161                 case XM_STX:  /* Start of header (1024-byte block) */
162                         blocksize = 1024;
163                         goto getblock;
164                 #endif
165
166                 case XM_SOH:  /* Start of header (128-byte block) */
167                         blocksize = 128;
168                         /* Needed to avoid warning if XM_BUFSIZE < 1024 */
169
170                 getblock:
171                         /* Get block number */
172                         c = kfile_getc(&port->fd);
173
174                         /* Check complemented block number */
175                         if ((~c & 0xff) != kfile_getc(&port->fd))
176                         {
177                                 XMODEM_PROGRESS("Bad blk (%d)\n", c);
178                                 purge = true;
179                                 break;
180                         }
181
182                         /* Determine which block is being sent */
183                         if (c == (blocknr & 0xff))
184                                 /* Last block repeated */
185                                 XMODEM_PROGRESS("Repeat blk %d\n", blocknr);
186                         else if (c == ((blocknr + 1) & 0xff))
187                                 /* Next block */
188                                 XMODEM_PROGRESS("Recv blk %d\n", ++blocknr);
189                         else
190                         {
191                                 /* Sync lost */
192                                 XMODEM_PROGRESS("Sync lost (%d/%d)\n", c, blocknr);
193                                 purge = true;
194                                 break;
195                         }
196
197                         buf = block_buffer;     /* Reset pointer to start of buffer */
198                         checksum = 0;
199                         crc = 0;
200                         for (i = 0; i < blocksize; i++)
201                         {
202                                 if ((c = kfile_getc(&port->fd)) == EOF)
203                                 {
204                                         purge = true;
205                                         break;
206                                 }
207
208                                 /* Store in buffer */
209                                 *buf++ = (char)c;
210
211                                 /* Calculate block checksum or CRC */
212                                 if (usecrc)
213                                         crc = UPDCRC16(c, crc);
214                                 else
215                                         checksum += (char)c;
216                         }
217
218                         if (purge)
219                                 break;
220
221                         /* Get the checksum byte or the CRC-16 MSB */
222                         if ((c = kfile_getc(&port->fd)) == EOF)
223                         {
224                                 purge = true;
225                                 break;
226                         }
227
228                         if (usecrc)
229                         {
230                                 crc = UPDCRC16(c, crc);
231
232                                 /* Get CRC-16 LSB */
233                                 if ((c = kfile_getc(&port->fd)) == EOF)
234                                 {
235                                         purge = true;
236                                         break;
237                                 }
238
239                                 crc = UPDCRC16(c, crc);
240
241                                 if (crc)
242                                 {
243                                         XMODEM_PROGRESS("Bad CRC: %04x\n", crc);
244                                         purge = true;
245                                         break;
246                                 }
247                         }
248                         /* Compare the checksum */
249                         else if (c != checksum)
250                         {
251                                 XMODEM_PROGRESS("Bad sum: %04x/%04x\n", checksum, c);
252                                 purge = true;
253                                 break;
254                         }
255
256                         /*
257                          * Avoid flushing the same block twice.
258                          * This could happen when the sender does not receive our
259                          * acknowledge and resends the same block.
260                          */
261                         if (last_block_done < blocknr)
262                         {
263                                 /* Call user function to flush the buffer */
264                                 if (kfile_write(fd, block_buffer, blocksize))
265                                 {
266                                         /* Acknowledge block and clear error counter */
267                                         kfile_putc(XM_ACK, &port->fd);
268                                         retries = 0;
269                                         last_block_done = blocknr;
270                                 }
271                                 else
272                                 {
273                                         /* User callback failed: abort transfer immediately */
274                                         retries = XM_MAXRETRIES;
275                                         purge = true;
276                                 }
277                         }
278                         break;
279
280                 case XM_EOT:    /* End of transmission */
281                         kfile_putchar(XM_ACK, &port->fd);
282                         XMODEM_PROGRESS("Transfer completed\n");
283                         return true;
284
285                 case EOF: /* Timeout or serial error */
286                         purge = true;
287                         break;
288
289                 default:
290                         XMODEM_PROGRESS("Skipping garbage\n");
291                         purge = true;
292                         break;
293                 }
294         } /* End forever */
295 }
296 #endif
297
298
299 #if CONFIG_XMODEM_SEND
300 /**
301  * \brief Transmit some data using the XModem protocol.
302  *
303  * \param port Serial port to use for transfer
304  * \param fd Source file
305  *
306  * \note This function allocates a large amount of stack for
307  *       the XModem transfer buffer (\see XM_BUFSIZE).
308  */
309 bool xmodem_send(struct KFileSerial *port, KFile *fd)
310 {
311         char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
312         size_t size = -1;
313         int blocknr = 1, retries = 0, c, i;
314         bool proceed, usecrc = false;
315         uint16_t crc;
316         uint8_t sum;
317
318         /*
319          * Reading a block can be very slow, so we read the first block early
320          * to avoid receiving double XM_C char.
321          * This could happen if we check for XM_C and then read the block, giving
322          * the receiving device time to send another XM_C char misinterpretating
323          * the blocks sent.
324          */
325         size = kfile_read(fd, block_buffer, XM_BUFSIZE);
326
327         kfile_clearerr(&port->fd);
328         ser_purge(port);
329         XMODEM_PROGRESS("Wait remote host\n");
330
331         for(;;)
332         {
333                 proceed = false;
334                 do
335                 {
336                         if (XMODEM_CHECK_ABORT)
337                                 return false;
338
339                         switch (c = kfile_getc(&port->fd))
340                         {
341                         case XM_NAK:
342                                 XMODEM_PROGRESS("Resend blk %d\n", blocknr);
343                                 proceed = true;
344                                 break;
345
346                         case XM_C:
347                                 if (c == XM_C)
348                                 {
349                                         XMODEM_PROGRESS("Tx start (CRC)\n");
350                                         usecrc = true;
351                                 }
352                                 else
353                                         XMODEM_PROGRESS("Tx start (BCC)\n");
354
355                                 proceed = true;
356                                 break;
357
358                         case XM_ACK:
359                                 /* End of transfer? */
360                                 if (!size)
361                                         return true;
362
363                                 /* Call user function to read in one block */
364                                 size = kfile_read(fd, block_buffer, XM_BUFSIZE);
365                                 XMODEM_PROGRESS("Send blk %d\n", blocknr);
366                                 blocknr++;
367                                 retries = 0;
368                                 proceed = true;
369                                 break;
370
371                         case EOF:
372                                 kfile_clearerr(&port->fd);
373                                 retries++;
374                                 XMODEM_PROGRESS("Retries %d\n", retries);
375                                 if (retries <= XM_MAXRETRIES)
376                                         break;
377                                 /* falling through! */
378
379                         case XM_CAN:
380                                 XMODEM_PROGRESS("Transfer aborted\n");
381                                 return false;
382
383                         default:
384                                 XMODEM_PROGRESS("Skipping garbage\n");
385                                 break;
386                         }
387                 }
388                 while (!proceed);
389
390                 if (!size)
391                 {
392                         kfile_putc(XM_EOT, &port->fd);
393                         continue;
394                 }
395
396                 /* Pad block with 0xFF if it's partially full */
397                 memset(block_buffer + size, 0xFF, XM_BUFSIZE - size);
398
399                 /* Send block header (STX, blocknr, ~blocknr) */
400                 #if XM_BUFSIZE == 128
401                         kfile_putc(XM_SOH, &port->fd);
402                 #else
403                         kfile_putc(XM_STX, &port->fd);
404                 #endif
405                 kfile_putc(blocknr & 0xFF, &port->fd);
406                 kfile_putc(~blocknr & 0xFF, &port->fd);
407
408                 /* Send block and compute its CRC/checksum */
409                 sum = 0;
410                 crc = 0;
411                 for (i = 0; i < XM_BUFSIZE; i++)
412                 {
413                         kfile_putc(block_buffer[i], &port->fd);
414                         crc = UPDCRC16(block_buffer[i], crc);
415                         sum += block_buffer[i];
416                 }
417
418                 /* Send CRC/Checksum */
419                 if (usecrc)
420                 {
421                         crc = UPDCRC16(0, crc);
422                         crc = UPDCRC16(0, crc);
423                         ser_putchar(crc >> 8, port);
424                         ser_putchar(crc & 0xFF, port);
425                 }
426                 else
427                         kfile_putc(sum, &port->fd);
428         }
429 }
430 #endif