initial commit
[freebsd-arm:freebsd-arm.git] / boot / arm / at91 / libat91 / xmodem.c
1 /*-
2  * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * This software is derived from software provide by Kwikbyte who specifically
25  * disclaimed copyright on the code.  This version of xmodem has been nearly
26  * completely rewritten, but the CRC is from the original.
27  *
28  * $FreeBSD$
29  */
30
31 #include "lib.h"
32
33 #define PACKET_SIZE     128
34
35 /* Line control codes */
36 #define SOH                     0x01    /* start of header */
37 #define ACK                     0x06    /* Acknowledge */
38 #define NAK                     0x15    /* Negative acknowledge */
39 #define CAN                     0x18    /* Cancel */
40 #define EOT                     0x04    /* end of text */
41
42 #define TO      10
43 /*
44  * int GetRecord(char , char *)
45  *  This private function receives a x-modem record to the pointer and
46  * returns non-zero on success.
47  */
48 static int
49 GetRecord(char blocknum, char *dest)
50 {
51         int             size;
52         int             ch;
53         unsigned        chk, j;
54
55         chk = 0;
56
57         if ((ch = getc(TO)) == -1)
58                 goto err;
59         if (ch != blocknum) 
60                 goto err;
61         if ((ch = getc(TO)) == -1) 
62                 goto err;
63         if (ch != (~blocknum & 0xff))
64                 goto err;
65         
66         for (size = 0; size < PACKET_SIZE; ++size) {
67                 if ((ch = getc(TO)) == -1)
68                         goto err;
69                 chk = chk ^ ch << 8;
70                 for (j = 0; j < 8; ++j) {
71                         if (chk & 0x8000)
72                                 chk = chk << 1 ^ 0x1021;
73                         else
74                                 chk = chk << 1;
75                 }
76                 *dest++ = ch;
77         }
78
79         chk &= 0xFFFF;
80
81         if (((ch = getc(TO)) == -1) || ((ch & 0xff) != ((chk >> 8) & 0xFF)))
82                 goto err;
83         if (((ch = getc(TO)) == -1) || ((ch & 0xff) != (chk & 0xFF)))
84                 goto err;
85         putchar(ACK);
86
87         return (1);
88 err:;
89         putchar(CAN);
90         // We should allow for resend, but we don't.
91         return (0);
92 }
93
94 /*
95  * int xmodem_rx(char *)
96  *  This global function receives a x-modem transmission consisting of
97  * (potentially) several blocks.  Returns the number of bytes received or
98  * -1 on error.
99  */
100 int
101 xmodem_rx(char *dest)
102 {
103         int             starting, ch;
104         char            packetNumber, *startAddress = dest;
105
106         packetNumber = 1;
107         starting = 1;
108
109         while (1) {
110                 if (starting)
111                         putchar('C');
112                 if (((ch = getc(1)) == -1) || (ch != SOH && ch != EOT))
113                         continue;
114                 if (ch == EOT) {
115                         putchar(ACK);
116                         return (dest - startAddress);
117                 }
118                 starting = 0;
119                 // Xmodem packets: SOH PKT# ~PKT# 128-bytes CRC16
120                 if (!GetRecord(packetNumber, dest))
121                         return (-1);
122                 dest += PACKET_SIZE;
123                 packetNumber++;
124         }
125
126         // the loop above should return in all cases
127         return (-1);
128 }