1 #error This module has not been revised for the API changes in several DevLib modules
5 * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
6 * Copyright 1999, 2001 Bernardo Innocenti <bernie@develer.com>
7 * This file is part of DevLib - See README.devlib for information.
9 * \brief X-Modem serial transmission protocol (implementation)
11 * Suppots the CRC-16 and 1K-blocks variants of the standard.
12 * \see ymodem.txt for the protocol description.
14 * \todo Decouple this code from the LCD, buzzer and timer drivers
15 * introducing user hooks or macros like CHECK_ABORT.
17 * \todo Break xmodem_send() and xmodem_recv() in smaller functions.
19 * \todo Add CONFIG_* vars to exclude either the receiver or the sender,
20 * to reduce the footprint for applications that don't need both.
22 * \todo Maybe convert drv/ser.c to the KFile interface for symmetry and
26 * \author Bernardo Innocenti <bernie@develer.com>
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.
70 #include <drv/buzzer.h>
71 #include <mware/crc.h>
72 #include <mware/kfile.h>
74 #include <string.h> /* for memset() */
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 */
92 #define XM_BUFSIZE 1024 /**< Size of block buffer */
95 #if (ARCH & ARCH_BOOT)
97 #if (ARCH & ARCH_SLIM)
98 #define CHECK_ABORT KEYPRESSED_STOP
99 #elif (ARCH & ARCH_SARF)
100 #define CHECK_ABORT KEYPRESSED_ESC
104 #if (ARCH & ARCH_SLIM)
105 #define CHECK_ABORT (kbd_getchar() == K_STOP)
106 #elif (ARCH & ARCH_SARF)
107 #define CHECK_ABORT (kbd_getchar() == K_ESC)
109 #endif /* ARCH_BOOT */
113 * Decode serial driver errors and print them on the display.
115 static void print_serial_error(struct Serial *port, int retries)
117 serstatus_t err, status;
119 /* Get serial error code and reset it */
120 status = ser_getstatus(port);
121 ser_setstatus(port, 0);
123 /* Mostra tutti gli errori in sequenza */
124 for (err = 0; status != 0; status >>= 1, err++)
126 /* Se il bit dell'errore e' settato */
129 lcd_printf(0, 3, LCD_FILL, "%s %d", serial_errors[err], retries);
138 * \brief Receive a file using the XModem protocol.
140 * \param port Serial port to use for transfer
141 * \param fd Destination file
143 * \note This function allocates a large amount of stack (>1KB).
145 bool xmodem_recv(struct Serial *port, KFile *fd)
147 char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
149 int blocknr = 0, last_block_done = 0, retries = 0;
157 lcd_printf(0, 2, LCD_FILL, "Starting Transfer...");
160 ser_settimeouts(port, SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT);
161 ser_setstatus(port, 0);
163 /* Send initial NAK to start transmission */
168 ser_putchar(XM_CAN, port);
169 ser_putchar(XM_CAN, port);
170 lcd_printf(0, 2, LCD_FILL, "Transfer aborted");
175 * Discard incoming input until a timeout occurs, then send
176 * a NAK to the transmitter.
183 SerialError(retries);
185 ser_resync(port, 200);
188 if (retries >= XM_MAXRETRIES)
190 ser_putchar(XM_CAN, port);
191 ser_putchar(XM_CAN, port);
192 lcd_printf(0, 2, LCD_FILL, "Transfer aborted");
196 /* Transmission start? */
199 if (retries < XM_MAXCRCRETRIES)
201 lcd_printf(0, 2, LCD_FILL, "Request Tx (CRC)");
202 ser_putchar(XM_C, port);
206 /* Give up with CRC and fall back to checksum */
208 lcd_printf(0, 2, LCD_FILL, "Request Tx (BCC)");
209 ser_putchar(XM_NAK, port);
213 ser_putchar(XM_NAK, port);
216 switch (ser_getchar(port))
218 case XM_STX: /* Start of header (1024-byte block) */
222 case XM_SOH: /* Start of header (128-byte block) */
226 /* Get block number */
227 c = ser_getchar(port);
229 /* Check complemented block number */
230 if ((~c & 0xff) != ser_getchar(port))
232 lcd_printf(0, 3, LCD_FILL, "Bad blk (%d)", c);
237 /* Determine which block is being sent */
238 if (c == (blocknr & 0xff))
239 /* Last block repeated */
240 lcd_printf(0, 2, LCD_FILL, "Repeat blk %d", blocknr);
241 else if (c == ((blocknr + 1) & 0xff))
243 lcd_printf(0, 2, LCD_FILL, "Recv blk %d", ++blocknr);
247 lcd_printf(0, 3, LCD_FILL, "Sync lost (%d/%d)", c, blocknr);
252 buf = block_buffer; /* Reset pointer to start of buffer */
255 for (i = 0; i < blocksize; i++)
257 if ((c = ser_getchar(port)) == EOF)
263 /* Store in buffer */
266 /* Calculate block checksum or CRC */
268 crc = UPDCRC16(c, crc);
276 /* Get the checksum byte or the CRC-16 MSB */
277 if ((c = ser_getchar(port)) == EOF)
285 crc = UPDCRC16(c, crc);
288 if ((c = ser_getchar(port)) == EOF)
294 crc = UPDCRC16(c, crc);
298 lcd_printf(0, 3, LCD_FILL, "Bad CRC: %04x", crc);
303 /* Compare the checksum */
304 else if (c != checksum)
306 lcd_printf(0, 3, LCD_FILL, "Bad sum: %04x/%04x", checksum, c);
312 * Avoid flushing the same block twice.
313 * This could happen when the sender does not receive our
314 * acknowledge and resends the same block.
316 if (last_block_done < blocknr)
318 /* Call user function to flush the buffer */
319 if (fd->write(fd, block_buffer, blocksize))
321 /* Acknowledge block and clear error counter */
322 ser_putchar(XM_ACK, port);
324 last_block_done = blocknr;
328 /* User callback failed: abort transfer immediately */
329 retries = XM_MAXRETRIES;
335 case XM_EOT: /* End of transmission */
336 ser_putchar(XM_ACK, port);
337 lcd_printf(0, 2, LCD_FILL, "Transfer completed");
340 case EOF: /* Timeout or serial error */
345 lcd_printf(0, 3, LCD_FILL, "Skipping garbage");
354 * \brief Transmit a file using the XModem protocol.
356 * \param port Serial port to use for transfer
357 * \param fd Source file
359 * \note This function allocates a large amount of stack for
360 * the XModem transfer buffer (1KB).
362 bool xmodem_send(struct Serial *port, KFile *fd)
364 char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
366 int blocknr = 1, retries = 0, c, i;
367 bool proceed, usecrc = false;
372 ser_settimeouts(port, SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT);
373 ser_setstatus(port, 0);
375 lcd_printf(0, 2, LCD_FILL, "Wait remote host");
385 switch (c = ser_getchar(port))
393 lcd_printf(0, 2, LCD_FILL, "Tx start (CRC)");
397 lcd_printf(0, 2, LCD_FILL, "Tx start (BCC)");
399 /* Call user function to read in one block */
400 size = fd->read(fd, block_buffer, XM_BUFSIZE);
403 lcd_printf(0, 2, LCD_FILL, "Resend blk %d", blocknr);
408 /* End of transfer? */
412 /* Call user function to read in one block */
413 size = fd->read(fd, block_buffer, XM_BUFSIZE);
417 lcd_printf(0, 2, LCD_FILL, "Send blk %d", blocknr);
422 SerialError(retries);
423 if (retries <= XM_MAXRETRIES)
425 /* falling through! */
428 lcd_printf(0, 2, LCD_FILL, "Transfer aborted");
432 lcd_printf(0, 3, LCD_FILL, "Skipping garbage");
440 ser_putchar(XM_EOT, port);
444 /* Pad block with 0xFF if it's partially full */
445 memset(block_buffer + size, 0xFF, XM_BUFSIZE - size);
447 /* Send block header (STX, blocknr, ~blocknr) */
448 ser_putchar(XM_STX, port);
449 ser_putchar(blocknr & 0xFF, port);
450 ser_putchar(~blocknr & 0xFF, port);
452 /* Send block and compute its CRC/checksum */
455 for (i = 0; i < XM_BUFSIZE; i++)
457 ser_putchar(block_buffer[i], port);
458 crc = UPDCRC16(block_buffer[i], crc);
459 sum += block_buffer[i];
462 /* Send CRC/Checksum */
465 crc = UPDCRC16(0, crc);
466 crc = UPDCRC16(0, crc);
467 ser_putchar(crc >> 8, port);
468 ser_putchar(crc & 0xFF, port);
471 ser_putchar(sum, port);