4 * Copyright 2004 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 devlib/README for information.
8 * \brief X-Modem serial transmission protocol (implementation)
10 * Suppots the CRC-16 and 1K-blocks variants of the standard.
11 * \see ymodem.txt for the protocol description.
14 * \author Bernardo Innocenti <bernie@develer.com>
19 * Revision 1.5 2004/08/12 23:46:21 bernie
20 * Remove extra indentation level in switch statements.
22 * Revision 1.4 2004/08/12 23:35:50 bernie
23 * Replace a handmade loop with memset().
25 * Revision 1.3 2004/08/12 23:34:36 bernie
26 * Replace if/else with continue to reduce indentation level.
28 * Revision 1.2 2004/08/12 23:24:07 bernie
29 * Rename UPDCRC() to UPDCRC16().
31 * Revision 1.1 2004/08/11 19:54:22 bernie
32 * Import XModem protocol into DevLib.
40 #include <drv/buzzer.h>
41 #include <mware/crc.h>
42 #include <mware/kfile.h>
44 #include <string.h> /* for memset() */
48 * \name Protocol control codes
51 #define XM_SOH 0x01 /*!< Start Of Header (128-byte block) */
52 #define XM_STX 0x02 /*!< Start Of Header (1024-byte block) */
53 #define XM_EOT 0x04 /*!< End Of Transmission */
54 #define XM_ACK 0x06 /*!< Acknowledge block */
55 #define XM_NAK 0x15 /*!< Negative Acknowledge */
56 #define XM_C 0x43 /*!< Request CRC-16 transmission */
57 #define XM_CAN 0x18 /*!< CANcel transmission */
60 #define XM_MAXRETRIES 15 /*!< Max retries before giving up */
61 #define XM_MAXCRCRETRIES 7 /*!< Max retries before switching to BCC */
62 #define XM_BUFSIZE 1024 /*!< Size of block buffer */
65 #if (ARCH & ARCH_BOOT)
67 #if (ARCH & ARCH_SLIM)
68 #define CHECK_ABORT KEYPRESSED_STOP
69 #elif (ARCH & ARCH_SARF)
70 #define CHECK_ABORT KEYPRESSED_ESC
74 #if (ARCH & ARCH_SLIM)
75 #define CHECK_ABORT (kbd_getchar() == K_STOP)
76 #elif (ARCH & ARCH_SARF)
77 #define CHECK_ABORT (kbd_getchar() == K_ESC)
79 #endif /* ARCH_BOOT */
82 /*! Buffer to hold a block of data */
83 static char block_buffer[XM_BUFSIZE];
87 * Decode serial driver errors and print
88 * them on the display.
90 static void SerialError(int retries)
92 serstatus_t err, status;
94 /* Get serial error code and reset it */
95 status = ser_getstatus();
98 /* Mostra tutti gli errori in sequenza */
99 for (err = 0; status != 0; status >>= 1, err++)
101 /* Se il bit dell'errore e' settato */
104 lcd_printf(0, 3, LCD_FILL, "%s %d", serial_errors[err], retries);
113 * Reset previous serial errors and flush the receive buffer
114 * (set a short timeout to speed up purge)
116 static void FlushSerial(void)
119 ser_settimeouts(200, SER_DEFTXTIMEOUT);
120 while (ser_getchar() != EOF) {}
121 ser_settimeouts(SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT);
126 bool xmodem_recv(KFile *fd)
129 int blocknr = 0, last_block_done = 0, retries = 0;
137 lcd_printf(0, 2, LCD_FILL, "Starting Transfer...");
140 ser_settimeouts(SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT);
143 /* Send initial NAK to start transmission */
150 lcd_printf(0, 2, LCD_FILL, "Transfer aborted");
155 * Discard incoming input until a timeout occurs, then send
156 * a NAK to the transmitter.
163 SerialError(retries);
168 if (retries >= XM_MAXRETRIES)
172 lcd_printf(0, 2, LCD_FILL, "Transfer aborted");
176 /* Transmission start? */
179 if (retries < XM_MAXCRCRETRIES)
181 lcd_printf(0, 2, LCD_FILL, "Request Tx (CRC)");
186 /* Give up with CRC and fall back to checksum */
188 lcd_printf(0, 2, LCD_FILL, "Request Tx (BCC)");
196 switch (ser_getchar())
198 case XM_STX: /* Start of header (1024-byte block) */
202 case XM_SOH: /* Start of header (128-byte block) */
206 /* Get block number */
209 /* Check complemented block number */
210 if ((~c & 0xff) != ser_getchar())
212 lcd_printf(0, 3, LCD_FILL, "Bad blk (%d)", c);
217 /* Determine which block is being sent */
218 if (c == (blocknr & 0xff))
219 /* Last block repeated */
220 lcd_printf(0, 2, LCD_FILL, "Repeat blk %d", blocknr);
221 else if (c == ((blocknr + 1) & 0xff))
223 lcd_printf(0, 2, LCD_FILL, "Recv blk %d", ++blocknr);
227 lcd_printf(0, 3, LCD_FILL, "Sync lost (%d/%d)", c, blocknr);
232 buf = block_buffer; /* Reset pointer to start of buffer */
235 for (i = 0; i < blocksize; i++)
237 if ((c = ser_getchar()) == EOF)
243 /* Store in buffer */
246 /* Calculate block checksum or CRC */
248 crc = UPDCRC16(c, crc);
256 /* Get the checksum byte or the CRC-16 MSB */
257 if ((c = ser_getchar()) == EOF)
265 crc = UPDCRC16(c, crc);
268 if ((c = ser_getchar()) == EOF)
274 crc = UPDCRC16(c, crc);
278 lcd_printf(0, 3, LCD_FILL, "Bad CRC: %04x", crc);
283 /* Compare the checksum */
284 else if (c != checksum)
286 lcd_printf(0, 3, LCD_FILL, "Bad sum: %04x/%04x", checksum, c);
292 * Avoid flushing the same block twice.
293 * This could happen when the sender does not receive our
294 * acknowledge and resends the same block.
296 if (last_block_done < blocknr)
298 /* Call user function to flush the buffer */
299 if (fd->write(fd, block_buffer, blocksize))
301 /* Acknowledge block and clear error counter */
304 last_block_done = blocknr;
308 /* User callback failed: abort transfer immediately */
309 retries = XM_MAXRETRIES;
315 case XM_EOT: /* End of transmission */
317 lcd_printf(0, 2, LCD_FILL, "Transfer completed");
320 case EOF: /* Timeout or serial error */
325 lcd_printf(0, 3, LCD_FILL, "Skipping garbage");
333 bool xmodem_send(KFile *fd)
336 int blocknr = 1, retries = 0, c, i;
337 bool proceed, usecrc = false;
342 ser_settimeouts(SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT);
345 lcd_printf(0, 2, LCD_FILL, "Wait remote host");
355 switch (c = ser_getchar())
363 lcd_printf(0, 2, LCD_FILL, "Tx start (CRC)");
367 lcd_printf(0, 2, LCD_FILL, "Tx start (BCC)");
369 /* Call user function to read in one block */
370 size = fd->read(fd, block_buffer, XM_BUFSIZE);
373 lcd_printf(0, 2, LCD_FILL, "Resend blk %d", blocknr);
378 /* End of transfer? */
382 /* Call user function to read in one block */
383 size = fd->read(fd, block_buffer, XM_BUFSIZE);
387 lcd_printf(0, 2, LCD_FILL, "Send blk %d", blocknr);
392 SerialError(retries);
393 if (retries <= XM_MAXRETRIES)
395 /* falling through! */
398 lcd_printf(0, 2, LCD_FILL, "Transfer aborted");
402 lcd_printf(0, 3, LCD_FILL, "Skipping garbage");
414 /* Pad block with 0xFF if it's partially full */
415 memset(block_buffer + size, 0xFF, XM_BUFSIZE - size);
417 /* Send block header (STX, blocknr, ~blocknr) */
419 ser_putchar(blocknr & 0xFF);
420 ser_putchar(~blocknr & 0xFF);
422 /* Send block and compute its CRC/checksum */
425 for (i = 0; i < XM_BUFSIZE; i++)
427 ser_putchar(block_buffer[i]);
428 crc = UPDCRC16(block_buffer[i], crc);
429 sum += block_buffer[i];
432 /* Send CRC/Checksum */
435 crc = UPDCRC16(0, crc);
436 crc = UPDCRC16(0, crc);
437 ser_putchar(crc >> 8);
438 ser_putchar(crc & 0xFF);