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