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.11 2007/06/07 09:10:44 batt
28 *#* Revision 1.10 2006/07/19 12:56:28 bernie
29 *#* Convert to new Doxygen style.
31 *#* Revision 1.9 2005/11/04 16:20:02 bernie
32 *#* Fix reference to README.devlib in header.
34 *#* Revision 1.8 2004/08/25 14:12:09 rasky
35 *#* Aggiornato il comment block dei log RCS
37 *#* Revision 1.7 2004/08/15 06:30:06 bernie
38 *#* Make the buffer a local variable, as documented.
40 *#* Revision 1.6 2004/08/15 05:31:46 bernie
41 *#* Add an #error to spread some FUD about the quality of this module;
42 *#* Add a few TODOs from Rasky's review;
43 *#* Update to the new drv/ser.c API;
44 *#* Move FlushSerial() to drv/ser.c and generalize.
46 *#* Revision 1.5 2004/08/12 23:46:21 bernie
47 *#* Remove extra indentation level in switch statements.
49 *#* Revision 1.4 2004/08/12 23:35:50 bernie
50 *#* Replace a handmade loop with memset().
52 *#* Revision 1.3 2004/08/12 23:34:36 bernie
53 *#* Replace if/else with continue to reduce indentation level.
55 *#* Revision 1.2 2004/08/12 23:24:07 bernie
56 *#* Rename UPDCRC() to UPDCRC16().
58 *#* Revision 1.1 2004/08/11 19:54:22 bernie
59 *#* Import XModem protocol into DevLib.
66 #include <string.h> /* for memset() */
67 #include <algos/crc.h>
71 * \name Protocol control codes
74 #define XM_SOH 0x01 /**< Start Of Header (128-byte block) */
75 #define XM_STX 0x02 /**< Start Of Header (1024-byte block) */
76 #define XM_EOT 0x04 /**< End Of Transmission */
77 #define XM_ACK 0x06 /**< Acknowledge block */
78 #define XM_NAK 0x15 /**< Negative Acknowledge */
79 #define XM_C 0x43 /**< Request CRC-16 transmission */
80 #define XM_CAN 0x18 /**< CANcel transmission */
83 #define XM_MAXRETRIES 15 /**< Max retries before giving up */
84 #define XM_MAXCRCRETRIES 7 /**< Max retries before switching to BCC */
86 #if CONFIG_XMODEM_1KCRC == 1
87 #define XM_BUFSIZE 1024 /**< 1024 bytes of block buffer */
89 #define XM_BUFSIZE 128 /**< 128 bytes of block buffer */
93 #if CONFIG_XMODEM_RECV
95 * \brief Receive a file using the XModem protocol.
97 * \param port Serial port to use for transfer
98 * \param fd Destination file
100 * \note This function allocates a large amount of stack (\see XM_BUFSIZE).
102 bool xmodem_recv(struct Serial *port, KFile *fd)
104 char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
106 int blocknr = 0, last_block_done = 0, retries = 0;
114 XMODEM_PROGRESS("Starting Transfer...\n");
116 ser_setstatus(port, 0);
118 /* Send initial NAK to start transmission */
121 if (XMODEM_CHECK_ABORT)
123 ser_putchar(XM_CAN, port);
124 ser_putchar(XM_CAN, port);
125 XMODEM_PROGRESS("Transfer aborted\n");
130 * Discard incoming input until a timeout occurs, then send
131 * a NAK to the transmitter.
137 if (ser_getstatus(port))
138 XMODEM_PROGRESS("Retries %d\n", retries);
140 ser_resync(port, 200);
143 if (retries >= XM_MAXRETRIES)
145 ser_putchar(XM_CAN, port);
146 ser_putchar(XM_CAN, port);
147 XMODEM_PROGRESS("Transfer aborted\n");
151 /* Transmission start? */
154 if (retries < XM_MAXCRCRETRIES)
156 XMODEM_PROGRESS("Request Tx (CRC)\n");
157 ser_putchar(XM_C, port);
161 /* Give up with CRC and fall back to checksum */
163 XMODEM_PROGRESS("Request Tx (BCC)");
164 ser_putchar(XM_NAK, port);
168 ser_putchar(XM_NAK, port);
171 switch (ser_getchar(port))
173 #if XM_BUFSIZE >= 1024
174 case XM_STX: /* Start of header (1024-byte block) */
179 case XM_SOH: /* Start of header (128-byte block) */
183 /* Get block number */
184 c = ser_getchar(port);
186 /* Check complemented block number */
187 if ((~c & 0xff) != ser_getchar(port))
189 XMODEM_PROGRESS("Bad blk (%d)\n", c);
194 /* Determine which block is being sent */
195 if (c == (blocknr & 0xff))
196 /* Last block repeated */
197 XMODEM_PROGRESS("Repeat blk %d\n", blocknr);
198 else if (c == ((blocknr + 1) & 0xff))
200 XMODEM_PROGRESS("Recv blk %d\n", ++blocknr);
204 XMODEM_PROGRESS("Sync lost (%d/%d)\n", c, blocknr);
209 buf = block_buffer; /* Reset pointer to start of buffer */
212 for (i = 0; i < blocksize; i++)
214 if ((c = ser_getchar(port)) == EOF)
220 /* Store in buffer */
223 /* Calculate block checksum or CRC */
225 crc = UPDCRC16(c, crc);
233 /* Get the checksum byte or the CRC-16 MSB */
234 if ((c = ser_getchar(port)) == EOF)
242 crc = UPDCRC16(c, crc);
245 if ((c = ser_getchar(port)) == EOF)
251 crc = UPDCRC16(c, crc);
255 XMODEM_PROGRESS("Bad CRC: %04x\n", crc);
260 /* Compare the checksum */
261 else if (c != checksum)
263 XMODEM_PROGRESS("Bad sum: %04x/%04x\n", checksum, c);
269 * Avoid flushing the same block twice.
270 * This could happen when the sender does not receive our
271 * acknowledge and resends the same block.
273 if (last_block_done < blocknr)
275 /* Call user function to flush the buffer */
276 if (fd->write(fd, block_buffer, blocksize))
278 /* Acknowledge block and clear error counter */
279 ser_putchar(XM_ACK, port);
281 last_block_done = blocknr;
285 /* User callback failed: abort transfer immediately */
286 retries = XM_MAXRETRIES;
292 case XM_EOT: /* End of transmission */
293 ser_putchar(XM_ACK, port);
294 XMODEM_PROGRESS("Transfer completed");
297 case EOF: /* Timeout or serial error */
302 XMODEM_PROGRESS("Skipping garbage");
311 #if CONFIG_XMODEM_SEND
313 * \brief Transmit some data using the XModem protocol.
315 * \param port Serial port to use for transfer
316 * \param fd Source file
318 * \note This function allocates a large amount of stack for
319 * the XModem transfer buffer (\see XM_BUFSIZE).
321 bool xmodem_send(struct Serial *port, KFile *fd)
323 char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
325 int blocknr = 1, retries = 0, c, i;
326 bool proceed, usecrc = false;
331 * Reading a block can be very slow, so we read the first block early
332 * to avoid receiving double XM_C char.
333 * This could happen if we check for XM_C and then read the block, giving
334 * the receiving device time to send another XM_C char misinterpretating
337 size = fd->read(fd, block_buffer, XM_BUFSIZE);
339 ser_setstatus(port, 0);
341 XMODEM_PROGRESS("Wait remote host\n");
348 if (XMODEM_CHECK_ABORT)
351 switch (c = ser_getchar(port))
354 XMODEM_PROGRESS("Resend blk %d\n", blocknr);
361 XMODEM_PROGRESS("Tx start (CRC)\n");
365 XMODEM_PROGRESS("Tx start (BCC)\n");
371 /* End of transfer? */
375 /* Call user function to read in one block */
376 size = fd->read(fd, block_buffer, XM_BUFSIZE);
377 XMODEM_PROGRESS("Send blk %d\n", blocknr);
384 ser_setstatus(port, 0);
386 XMODEM_PROGRESS("Retries %d\n", retries);
387 if (retries <= XM_MAXRETRIES)
389 /* falling through! */
392 XMODEM_PROGRESS("Transfer aborted\n");
396 XMODEM_PROGRESS("Skipping garbage\n");
404 ser_putchar(XM_EOT, port);
408 /* Pad block with 0xFF if it's partially full */
409 memset(block_buffer + size, 0xFF, XM_BUFSIZE - size);
411 /* Send block header (STX, blocknr, ~blocknr) */
412 #if XM_BUFSIZE == 128
413 ser_putchar(XM_SOH, port);
415 ser_putchar(XM_STX, port);
417 ser_putchar(blocknr & 0xFF, port);
418 ser_putchar(~blocknr & 0xFF, port);
420 /* Send block and compute its CRC/checksum */
423 for (i = 0; i < XM_BUFSIZE; i++)
425 ser_putchar(block_buffer[i], port);
426 crc = UPDCRC16(block_buffer[i], crc);
427 sum += block_buffer[i];
430 /* Send CRC/Checksum */
433 crc = UPDCRC16(0, crc);
434 crc = UPDCRC16(0, crc);
435 ser_putchar(crc >> 8, port);
436 ser_putchar(crc & 0xFF, port);
439 ser_putchar(sum, port);