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