4 * This file is part of BeRTOS.
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.
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.
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
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.
29 * Copyright 2004, 2005, 2006, 2007 Develer S.r.l. (http://www.develer.com/)
30 * Copyright 1999, 2001 Bernardo Innocenti <bernie@develer.com>
34 * \brief X-Modem serial transmission protocol (implementation)
36 * Supports the CRC-16 and 1K-blocks variants of the standard.
37 * \see ymodem.txt for the protocol description.
39 * \todo Break xmodem_send() and xmodem_recv() in smaller functions.
43 * \author Bernardo Innocenti <bernie@develer.com>
44 * \author Francesco Sacchi <batt@develer.com>
50 #include <cfg/debug.h>
51 #include "cfg/cfg_xmodem.h"
57 #include <string.h> /* for memset() */
60 * \name Protocol control codes
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 */
72 #define XM_MAXRETRIES 15 /**< Max retries before giving up */
73 #define XM_MAXCRCRETRIES 7 /**< Max retries before switching to BCC */
75 #if CONFIG_XMODEM_1KCRC == 1
76 #define XM_BUFSIZE 1024 /**< 1024 bytes of block buffer */
78 #define XM_BUFSIZE 128 /**< 128 bytes of block buffer */
82 #if CONFIG_XMODEM_RECV
84 * \brief Receive a file using the XModem protocol.
86 * \param port Serial port to use for transfer
87 * \param fd Destination file
89 * \note This function allocates a large amount of stack (\see XM_BUFSIZE).
91 bool xmodem_recv(struct KFileSerial *port, KFile *fd)
93 char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
95 int blocknr = 0, last_block_done = 0, retries = 0;
103 XMODEM_PROGRESS("Starting Transfer...\n");
105 kfile_clearerr(&port->fd);
107 /* Send initial NAK to start transmission */
110 if (XMODEM_CHECK_ABORT)
112 kfile_putc(XM_CAN, &port->fd);
113 kfile_putc(XM_CAN, &port->fd);
114 XMODEM_PROGRESS("Transfer aborted\n");
119 * Discard incoming input until a timeout occurs, then send
120 * a NAK to the transmitter.
126 if (kfile_error(&port->fd))
127 XMODEM_PROGRESS("Retries %d\n", retries);
129 ser_resync(port, 200);
132 if (retries >= XM_MAXRETRIES)
134 kfile_putc(XM_CAN, &port->fd);
135 kfile_putc(XM_CAN, &port->fd);
136 XMODEM_PROGRESS("Transfer aborted\n");
140 /* Transmission start? */
143 if (retries < XM_MAXCRCRETRIES)
145 XMODEM_PROGRESS("Request Tx (CRC)\n");
146 kfile_putc(XM_C, &port->fd);
150 /* Give up with CRC and fall back to checksum */
152 XMODEM_PROGRESS("Request Tx (BCC)\n");
153 kfile_putc(XM_NAK, &port->fd);
157 kfile_putc(XM_NAK, &port->fd);
160 switch (kfile_getc(&port->fd))
162 #if XM_BUFSIZE >= 1024
163 case XM_STX: /* Start of header (1024-byte block) */
168 case XM_SOH: /* Start of header (128-byte block) */
170 /* Needed to avoid warning if XM_BUFSIZE < 1024 */
173 /* Get block number */
174 c = kfile_getc(&port->fd);
176 /* Check complemented block number */
177 if ((~c & 0xff) != kfile_getc(&port->fd))
179 XMODEM_PROGRESS("Bad blk (%d)\n", c);
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))
190 XMODEM_PROGRESS("Recv blk %d\n", ++blocknr);
194 XMODEM_PROGRESS("Sync lost (%d/%d)\n", c, blocknr);
199 buf = block_buffer; /* Reset pointer to start of buffer */
202 for (i = 0; i < blocksize; i++)
204 if ((c = kfile_getc(&port->fd)) == EOF)
210 /* Store in buffer */
213 /* Calculate block checksum or CRC */
215 crc = UPDCRC16(c, crc);
223 /* Get the checksum byte or the CRC-16 MSB */
224 if ((c = kfile_getc(&port->fd)) == EOF)
232 crc = UPDCRC16(c, crc);
235 if ((c = kfile_getc(&port->fd)) == EOF)
241 crc = UPDCRC16(c, crc);
245 XMODEM_PROGRESS("Bad CRC: %04x\n", crc);
250 /* Compare the checksum */
251 else if (c != checksum)
253 XMODEM_PROGRESS("Bad sum: %04x/%04x\n", checksum, c);
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.
263 if (last_block_done < blocknr)
265 /* Call user function to flush the buffer */
266 if (kfile_write(fd, block_buffer, blocksize))
268 /* Acknowledge block and clear error counter */
269 kfile_putc(XM_ACK, &port->fd);
271 last_block_done = blocknr;
275 /* User callback failed: abort transfer immediately */
276 retries = XM_MAXRETRIES;
282 case XM_EOT: /* End of transmission */
283 kfile_putc(XM_ACK, &port->fd);
284 XMODEM_PROGRESS("Transfer completed\n");
287 case EOF: /* Timeout or serial error */
292 XMODEM_PROGRESS("Skipping garbage\n");
301 #if CONFIG_XMODEM_SEND
303 * \brief Transmit some data using the XModem protocol.
305 * \param port Serial port to use for transfer
306 * \param fd Source file
308 * \note This function allocates a large amount of stack for
309 * the XModem transfer buffer (\see XM_BUFSIZE).
311 bool xmodem_send(struct KFileSerial *port, KFile *fd)
313 char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
315 int blocknr = 1, retries = 0, c, i;
316 bool proceed, usecrc = false;
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
327 size = kfile_read(fd, block_buffer, XM_BUFSIZE);
329 kfile_clearerr(&port->fd);
331 XMODEM_PROGRESS("Wait remote host\n");
338 if (XMODEM_CHECK_ABORT)
341 switch (c = kfile_getc(&port->fd))
344 XMODEM_PROGRESS("Resend blk %d\n", blocknr);
351 XMODEM_PROGRESS("Tx start (CRC)\n");
355 XMODEM_PROGRESS("Tx start (BCC)\n");
361 /* End of transfer? */
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);
374 kfile_clearerr(&port->fd);
376 XMODEM_PROGRESS("Retries %d\n", retries);
377 if (retries <= XM_MAXRETRIES)
379 /* falling through! */
382 XMODEM_PROGRESS("Transfer aborted\n");
386 XMODEM_PROGRESS("Skipping garbage\n");
394 kfile_putc(XM_EOT, &port->fd);
398 /* Pad block with 0xFF if it's partially full */
399 memset(block_buffer + size, 0xFF, XM_BUFSIZE - size);
401 /* Send block header (STX, blocknr, ~blocknr) */
402 #if XM_BUFSIZE == 128
403 kfile_putc(XM_SOH, &port->fd);
405 kfile_putc(XM_STX, &port->fd);
407 kfile_putc(blocknr & 0xFF, &port->fd);
408 kfile_putc(~blocknr & 0xFF, &port->fd);
410 /* Send block and compute its CRC/checksum */
413 for (i = 0; i < XM_BUFSIZE; i++)
415 kfile_putc(block_buffer[i], &port->fd);
416 crc = UPDCRC16(block_buffer[i], crc);
417 sum += block_buffer[i];
420 /* Send CRC/Checksum */
423 crc = UPDCRC16(0, crc);
424 crc = UPDCRC16(0, crc);
425 kfile_putc(crc >> 8, &port->fd);
426 kfile_putc(crc & 0xFF, &port->fd);
429 kfile_putc(sum, &port->fd);