4 * Copyright 2004, 2005, 2006, 2007 Develer S.r.l. (http://www.develer.com/)
5 * Copyright 1999, 2001 Bernardo Innocenti <bernie@develer.com>
6 * This file is part of DevLib - See README.devlib for information.
8 * \brief X-Modem serial transmission protocol (implementation)
10 * Supports the CRC-16 and 1K-blocks variants of the standard.
11 * \see ymodem.txt for the protocol description.
13 * \todo Break xmodem_send() and xmodem_recv() in smaller functions.
15 * \todo Maybe convert drv/ser.c to the KFile interface for symmetry and
19 * \author Bernardo Innocenti <bernie@develer.com>
20 * \author Francesco Sacchi <batt@develer.com>
25 *#* Revision 1.12 2007/10/05 09:14:15 batt
26 *#* Fix some debug strings; suppress warning if XM_BUFSIZE < 1024.
28 *#* Revision 1.11 2007/06/07 09:10:44 batt
31 *#* Revision 1.10 2006/07/19 12:56:28 bernie
32 *#* Convert to new Doxygen style.
34 *#* Revision 1.9 2005/11/04 16:20:02 bernie
35 *#* Fix reference to README.devlib in header.
37 *#* Revision 1.8 2004/08/25 14:12:09 rasky
38 *#* Aggiornato il comment block dei log RCS
40 *#* Revision 1.7 2004/08/15 06:30:06 bernie
41 *#* Make the buffer a local variable, as documented.
43 *#* Revision 1.6 2004/08/15 05:31:46 bernie
44 *#* Add an #error to spread some FUD about the quality of this module;
45 *#* Add a few TODOs from Rasky's review;
46 *#* Update to the new drv/ser.c API;
47 *#* Move FlushSerial() to drv/ser.c and generalize.
49 *#* Revision 1.5 2004/08/12 23:46:21 bernie
50 *#* Remove extra indentation level in switch statements.
52 *#* Revision 1.4 2004/08/12 23:35:50 bernie
53 *#* Replace a handmade loop with memset().
55 *#* Revision 1.3 2004/08/12 23:34:36 bernie
56 *#* Replace if/else with continue to reduce indentation level.
58 *#* Revision 1.2 2004/08/12 23:24:07 bernie
59 *#* Rename UPDCRC() to UPDCRC16().
61 *#* Revision 1.1 2004/08/11 19:54:22 bernie
62 *#* Import XModem protocol into DevLib.
68 #include <appconfig.h>
69 #include <string.h> /* for memset() */
71 #include <algos/crc.h>
72 #include <cfg/debug.h>
78 * \name Protocol control codes
81 #define XM_SOH 0x01 /**< Start Of Header (128-byte block) */
82 #define XM_STX 0x02 /**< Start Of Header (1024-byte block) */
83 #define XM_EOT 0x04 /**< End Of Transmission */
84 #define XM_ACK 0x06 /**< Acknowledge block */
85 #define XM_NAK 0x15 /**< Negative Acknowledge */
86 #define XM_C 0x43 /**< Request CRC-16 transmission */
87 #define XM_CAN 0x18 /**< CANcel transmission */
90 #define XM_MAXRETRIES 15 /**< Max retries before giving up */
91 #define XM_MAXCRCRETRIES 7 /**< Max retries before switching to BCC */
93 #if CONFIG_XMODEM_1KCRC == 1
94 #define XM_BUFSIZE 1024 /**< 1024 bytes of block buffer */
96 #define XM_BUFSIZE 128 /**< 128 bytes of block buffer */
100 #if CONFIG_XMODEM_RECV
102 * \brief Receive a file using the XModem protocol.
104 * \param port Serial port to use for transfer
105 * \param fd Destination file
107 * \note This function allocates a large amount of stack (\see XM_BUFSIZE).
109 bool xmodem_recv(struct Serial *port, KFile *fd)
111 char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
113 int blocknr = 0, last_block_done = 0, retries = 0;
121 XMODEM_PROGRESS("Starting Transfer...\n");
123 ser_setstatus(port, 0);
125 /* Send initial NAK to start transmission */
128 if (XMODEM_CHECK_ABORT)
130 ser_putchar(XM_CAN, port);
131 ser_putchar(XM_CAN, port);
132 XMODEM_PROGRESS("Transfer aborted\n");
137 * Discard incoming input until a timeout occurs, then send
138 * a NAK to the transmitter.
144 if (ser_getstatus(port))
145 XMODEM_PROGRESS("Retries %d\n", retries);
147 ser_resync(port, 200);
150 if (retries >= XM_MAXRETRIES)
152 ser_putchar(XM_CAN, port);
153 ser_putchar(XM_CAN, port);
154 XMODEM_PROGRESS("Transfer aborted\n");
158 /* Transmission start? */
161 if (retries < XM_MAXCRCRETRIES)
163 XMODEM_PROGRESS("Request Tx (CRC)\n");
164 ser_putchar(XM_C, port);
168 /* Give up with CRC and fall back to checksum */
170 XMODEM_PROGRESS("Request Tx (BCC)\n");
171 ser_putchar(XM_NAK, port);
175 ser_putchar(XM_NAK, port);
178 switch (ser_getchar(port))
180 #if XM_BUFSIZE >= 1024
181 case XM_STX: /* Start of header (1024-byte block) */
186 case XM_SOH: /* Start of header (128-byte block) */
188 /* Needed to avoid warning if XM_BUFSIZE < 1024 */
191 /* Get block number */
192 c = ser_getchar(port);
194 /* Check complemented block number */
195 if ((~c & 0xff) != ser_getchar(port))
197 XMODEM_PROGRESS("Bad blk (%d)\n", c);
202 /* Determine which block is being sent */
203 if (c == (blocknr & 0xff))
204 /* Last block repeated */
205 XMODEM_PROGRESS("Repeat blk %d\n", blocknr);
206 else if (c == ((blocknr + 1) & 0xff))
208 XMODEM_PROGRESS("Recv blk %d\n", ++blocknr);
212 XMODEM_PROGRESS("Sync lost (%d/%d)\n", c, blocknr);
217 buf = block_buffer; /* Reset pointer to start of buffer */
220 for (i = 0; i < blocksize; i++)
222 if ((c = ser_getchar(port)) == EOF)
228 /* Store in buffer */
231 /* Calculate block checksum or CRC */
233 crc = UPDCRC16(c, crc);
241 /* Get the checksum byte or the CRC-16 MSB */
242 if ((c = ser_getchar(port)) == EOF)
250 crc = UPDCRC16(c, crc);
253 if ((c = ser_getchar(port)) == EOF)
259 crc = UPDCRC16(c, crc);
263 XMODEM_PROGRESS("Bad CRC: %04x\n", crc);
268 /* Compare the checksum */
269 else if (c != checksum)
271 XMODEM_PROGRESS("Bad sum: %04x/%04x\n", checksum, c);
277 * Avoid flushing the same block twice.
278 * This could happen when the sender does not receive our
279 * acknowledge and resends the same block.
281 if (last_block_done < blocknr)
283 /* Call user function to flush the buffer */
284 if (fd->write(fd, block_buffer, blocksize))
286 /* Acknowledge block and clear error counter */
287 ser_putchar(XM_ACK, port);
289 last_block_done = blocknr;
293 /* User callback failed: abort transfer immediately */
294 retries = XM_MAXRETRIES;
300 case XM_EOT: /* End of transmission */
301 ser_putchar(XM_ACK, port);
302 XMODEM_PROGRESS("Transfer completed\n");
305 case EOF: /* Timeout or serial error */
310 XMODEM_PROGRESS("Skipping garbage\n");
319 #if CONFIG_XMODEM_SEND
321 * \brief Transmit some data using the XModem protocol.
323 * \param port Serial port to use for transfer
324 * \param fd Source file
326 * \note This function allocates a large amount of stack for
327 * the XModem transfer buffer (\see XM_BUFSIZE).
329 bool xmodem_send(struct Serial *port, KFile *fd)
331 char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
333 int blocknr = 1, retries = 0, c, i;
334 bool proceed, usecrc = false;
339 * Reading a block can be very slow, so we read the first block early
340 * to avoid receiving double XM_C char.
341 * This could happen if we check for XM_C and then read the block, giving
342 * the receiving device time to send another XM_C char misinterpretating
345 size = fd->read(fd, block_buffer, XM_BUFSIZE);
347 ser_setstatus(port, 0);
349 XMODEM_PROGRESS("Wait remote host\n");
356 if (XMODEM_CHECK_ABORT)
359 switch (c = ser_getchar(port))
362 XMODEM_PROGRESS("Resend blk %d\n", blocknr);
369 XMODEM_PROGRESS("Tx start (CRC)\n");
373 XMODEM_PROGRESS("Tx start (BCC)\n");
379 /* End of transfer? */
383 /* Call user function to read in one block */
384 size = fd->read(fd, block_buffer, XM_BUFSIZE);
385 XMODEM_PROGRESS("Send blk %d\n", blocknr);
392 ser_setstatus(port, 0);
394 XMODEM_PROGRESS("Retries %d\n", retries);
395 if (retries <= XM_MAXRETRIES)
397 /* falling through! */
400 XMODEM_PROGRESS("Transfer aborted\n");
404 XMODEM_PROGRESS("Skipping garbage\n");
412 ser_putchar(XM_EOT, port);
416 /* Pad block with 0xFF if it's partially full */
417 memset(block_buffer + size, 0xFF, XM_BUFSIZE - size);
419 /* Send block header (STX, blocknr, ~blocknr) */
420 #if XM_BUFSIZE == 128
421 ser_putchar(XM_SOH, port);
423 ser_putchar(XM_STX, port);
425 ser_putchar(blocknr & 0xFF, port);
426 ser_putchar(~blocknr & 0xFF, port);
428 /* Send block and compute its CRC/checksum */
431 for (i = 0; i < XM_BUFSIZE; i++)
433 ser_putchar(block_buffer[i], port);
434 crc = UPDCRC16(block_buffer[i], crc);
435 sum += block_buffer[i];
438 /* Send CRC/Checksum */
441 crc = UPDCRC16(0, crc);
442 crc = UPDCRC16(0, crc);
443 ser_putchar(crc >> 8, port);
444 ser_putchar(crc & 0xFF, port);
447 ser_putchar(sum, port);