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.4 2004/08/12 23:35:50 bernie
20 * Replace a handmade loop with memset().
22 * Revision 1.3 2004/08/12 23:34:36 bernie
23 * Replace if/else with continue to reduce indentation level.
25 * Revision 1.2 2004/08/12 23:24:07 bernie
26 * Rename UPDCRC() to UPDCRC16().
28 * Revision 1.1 2004/08/11 19:54:22 bernie
29 * Import XModem protocol into DevLib.
37 #include <drv/buzzer.h>
38 #include <mware/crc.h>
39 #include <mware/kfile.h>
41 #include <string.h> /* for memset() */
45 * \name Protocol control codes
48 #define XM_SOH 0x01 /*!< Start Of Header (128-byte block) */
49 #define XM_STX 0x02 /*!< Start Of Header (1024-byte block) */
50 #define XM_EOT 0x04 /*!< End Of Transmission */
51 #define XM_ACK 0x06 /*!< Acknowledge block */
52 #define XM_NAK 0x15 /*!< Negative Acknowledge */
53 #define XM_C 0x43 /*!< Request CRC-16 transmission */
54 #define XM_CAN 0x18 /*!< CANcel transmission */
57 #define XM_MAXRETRIES 15 /*!< Max retries before giving up */
58 #define XM_MAXCRCRETRIES 7 /*!< Max retries before switching to BCC */
59 #define XM_BUFSIZE 1024 /*!< Size of block buffer */
62 #if (ARCH & ARCH_BOOT)
64 #if (ARCH & ARCH_SLIM)
65 #define CHECK_ABORT KEYPRESSED_STOP
66 #elif (ARCH & ARCH_SARF)
67 #define CHECK_ABORT KEYPRESSED_ESC
71 #if (ARCH & ARCH_SLIM)
72 #define CHECK_ABORT (kbd_getchar() == K_STOP)
73 #elif (ARCH & ARCH_SARF)
74 #define CHECK_ABORT (kbd_getchar() == K_ESC)
76 #endif /* ARCH_BOOT */
79 /*! Buffer to hold a block of data */
80 static char block_buffer[XM_BUFSIZE];
84 * Decode serial driver errors and print
85 * them on the display.
87 static void SerialError(int retries)
89 serstatus_t err, status;
91 /* Get serial error code and reset it */
92 status = ser_getstatus();
95 /* Mostra tutti gli errori in sequenza */
96 for (err = 0; status != 0; status >>= 1, err++)
98 /* Se il bit dell'errore e' settato */
101 lcd_printf(0, 3, LCD_FILL, "%s %d", serial_errors[err], retries);
110 * Reset previous serial errors and flush the receive buffer
111 * (set a short timeout to speed up purge)
113 static void FlushSerial(void)
116 ser_settimeouts(200, SER_DEFTXTIMEOUT);
117 while (ser_getchar() != EOF) {}
118 ser_settimeouts(SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT);
123 bool xmodem_recv(KFile *fd)
126 int blocknr = 0, last_block_done = 0, retries = 0;
134 lcd_printf(0, 2, LCD_FILL, "Starting Transfer...");
137 ser_settimeouts(SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT);
140 /* Send initial NAK to start transmission */
147 lcd_printf(0, 2, LCD_FILL, "Transfer aborted");
152 * Discard incoming input until a timeout occurs, then send
153 * a NAK to the transmitter.
160 SerialError(retries);
165 if (retries >= XM_MAXRETRIES)
169 lcd_printf(0, 2, LCD_FILL, "Transfer aborted");
173 /* Transmission start? */
176 if (retries < XM_MAXCRCRETRIES)
178 lcd_printf(0, 2, LCD_FILL, "Request Tx (CRC)");
183 /* Give up with CRC and fall back to checksum */
185 lcd_printf(0, 2, LCD_FILL, "Request Tx (BCC)");
193 switch (ser_getchar())
195 case XM_STX: /* Start of header (1024-byte block) */
199 case XM_SOH: /* Start of header (128-byte block) */
203 /* Get block number */
206 /* Check complemented block number */
207 if ((~c & 0xff) != ser_getchar())
209 lcd_printf(0, 3, LCD_FILL, "Bad blk (%d)", c);
214 /* Determine which block is being sent */
215 if (c == (blocknr & 0xff))
216 /* Last block repeated */
217 lcd_printf(0, 2, LCD_FILL, "Repeat blk %d", blocknr);
218 else if (c == ((blocknr + 1) & 0xff))
220 lcd_printf(0, 2, LCD_FILL, "Recv blk %d", ++blocknr);
224 lcd_printf(0, 3, LCD_FILL, "Sync lost (%d/%d)", c, blocknr);
229 buf = block_buffer; /* Reset pointer to start of buffer */
232 for (i = 0; i < blocksize; i++)
234 if ((c = ser_getchar()) == EOF)
240 /* Store in buffer */
243 /* Calculate block checksum or CRC */
245 crc = UPDCRC16(c, crc);
253 /* Get the checksum byte or the CRC-16 MSB */
254 if ((c = ser_getchar()) == EOF)
262 crc = UPDCRC16(c, crc);
265 if ((c = ser_getchar()) == EOF)
271 crc = UPDCRC16(c, crc);
275 lcd_printf(0, 3, LCD_FILL, "Bad CRC: %04x", crc);
280 /* Compare the checksum */
281 else if (c != checksum)
283 lcd_printf(0, 3, LCD_FILL, "Bad sum: %04x/%04x", checksum, c);
289 * Avoid flushing the same block twice.
290 * This could happen when the sender does not receive our
291 * acknowledge and resends the same block.
293 if (last_block_done < blocknr)
295 /* Call user function to flush the buffer */
296 if (fd->write(fd, block_buffer, blocksize))
298 /* Acknowledge block and clear error counter */
301 last_block_done = blocknr;
305 /* User callback failed: abort transfer immediately */
306 retries = XM_MAXRETRIES;
312 case XM_EOT: /* End of transmission */
314 lcd_printf(0, 2, LCD_FILL, "Transfer completed");
317 case EOF: /* Timeout or serial error */
322 lcd_printf(0, 3, LCD_FILL, "Skipping garbage");
330 bool xmodem_send(KFile *fd)
333 int blocknr = 1, retries = 0, c, i;
334 bool proceed, usecrc = false;
339 ser_settimeouts(SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT);
342 lcd_printf(0, 2, LCD_FILL, "Wait remote host");
352 switch (c = ser_getchar())
360 lcd_printf(0, 2, LCD_FILL, "Tx start (CRC)");
364 lcd_printf(0, 2, LCD_FILL, "Tx start (BCC)");
366 /* Call user function to read in one block */
367 size = fd->read(fd, block_buffer, XM_BUFSIZE);
370 lcd_printf(0, 2, LCD_FILL, "Resend blk %d", blocknr);
375 /* End of transfer? */
379 /* Call user function to read in one block */
380 size = fd->read(fd, block_buffer, XM_BUFSIZE);
384 lcd_printf(0, 2, LCD_FILL, "Send blk %d", blocknr);
389 SerialError(retries);
390 if (retries <= XM_MAXRETRIES)
392 /* falling through! */
395 lcd_printf(0, 2, LCD_FILL, "Transfer aborted");
399 lcd_printf(0, 3, LCD_FILL, "Skipping garbage");
411 /* Pad block with 0xFF if it's partially full */
412 memset(block_buffer + size, 0xFF, XM_BUFSIZE - size);
414 /* Send block header (STX, blocknr, ~blocknr) */
416 ser_putchar(blocknr & 0xFF);
417 ser_putchar(~blocknr & 0xFF);
419 /* Send block and compute its CRC/checksum */
422 for (i = 0; i < XM_BUFSIZE; i++)
424 ser_putchar(block_buffer[i]);
425 crc = UPDCRC16(block_buffer[i], crc);
426 sum += block_buffer[i];
429 /* Send CRC/Checksum */
432 crc = UPDCRC16(0, crc);
433 crc = UPDCRC16(0, crc);
434 ser_putchar(crc >> 8);
435 ser_putchar(crc & 0xFF);