Move C labels to start-of-line
[coreboot.git] / src / lib / xmodem.c
1 /*
2         Copyright 2006 Arastra, Inc.
3         Copyright 2001, 2002 Georges Menie (www.menie.org)
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU Lesser General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20 #include <string.h>
21 #include <delay.h>
22 #include <uart8250.h>
23
24 static int _inbyte(int msec)
25 {
26         while (!uart8250_can_rx_byte(CONFIG_TTYS0_BASE)) {
27                 udelay(1000);
28                 if (msec-- <= 0)
29                         return -1;
30         }
31         return uart8250_rx_byte(CONFIG_TTYS0_BASE);
32 }
33
34 static void _outbyte(unsigned char c)
35 {
36         uart8250_tx_byte(CONFIG_TTYS0_BASE, c);
37 }
38
39 static unsigned short crc16_ccitt(const unsigned char *buf, int sz)
40 {
41         unsigned short crc = 0;
42         while (--sz >= 0) {
43                 int i;
44                 crc ^= (unsigned short) *buf++ << 8;
45                 for (i = 0; i < 8; i++)
46                         if (crc & 0x8000)
47                                 crc = crc << 1 ^ 0x1021;
48                         else
49                                 crc <<= 1;
50         }
51         return crc;
52 }
53
54 #define SOH  0x01
55 #define STX  0x02
56 #define EOT  0x04
57 #define ACK  0x06
58 #define NAK  0x15
59 #define CAN  0x18
60 #define CTRLZ 0x1A
61
62 #define DLY_1S 1000
63 #define MAXRETRANS 25
64
65 static int check(int crc, const unsigned char *buf, int sz)
66 {
67         if (crc) {
68                 unsigned short crc = crc16_ccitt(buf, sz);
69                 unsigned short tcrc = (buf[sz]<<8)+buf[sz+1];
70                 if (crc == tcrc)
71                         return 1;
72         }
73         else {
74                 int i;
75                 unsigned char cks = 0;
76                 for (i = 0; i < sz; ++i) {
77                         cks += buf[i];
78                 }
79                 if (cks == buf[sz])
80                 return 1;
81         }
82
83         return 0;
84 }
85
86 static void flushinput(void)
87 {
88         while (_inbyte(((DLY_1S)*3)>>1) >= 0)
89                 ;
90 }
91
92 int xmodemReceive(unsigned char *dest, int destsz)
93 {
94         unsigned char xbuff[1030]; /* 1024 for XModem 1k + 3 head chars + 2 crc + nul */
95         unsigned char *p;
96         int bufsz, crc = 0;
97         unsigned char trychar = 'C';
98         unsigned char packetno = 1;
99         int i, c, len = 0;
100         int retry, retrans = MAXRETRANS;
101
102         for(;;) {
103                 for( retry = 0; retry < 16; ++retry) {
104                         if (trychar) _outbyte(trychar);
105                         if ((c = _inbyte((DLY_1S)<<1)) >= 0) {
106                                 switch (c) {
107                                 case SOH:
108                                         bufsz = 128;
109                                         goto start_recv;
110                                 case STX:
111                                         bufsz = 1024;
112                                         goto start_recv;
113                                 case EOT:
114                                         flushinput();
115                                         _outbyte(ACK);
116                                         return len; /* normal end */
117                                 case CAN:
118                                         if ((c = _inbyte(DLY_1S)) == CAN) {
119                                                 flushinput();
120                                                 _outbyte(ACK);
121                                                 return -1; /* canceled by remote */
122                                         }
123                                         break;
124                                 default:
125                                         break;
126                                 }
127                         }
128                 }
129                 if (trychar == 'C') { trychar = NAK; continue; }
130                 flushinput();
131                 _outbyte(CAN);
132                 _outbyte(CAN);
133                 _outbyte(CAN);
134                 return -2; /* sync error */
135
136         start_recv:
137                 if (trychar == 'C') crc = 1;
138                 trychar = 0;
139                 p = xbuff;
140                 *p++ = c;
141                 for (i = 0;  i < (bufsz+(crc?1:0)+3); ++i) {
142                         if ((c = _inbyte(DLY_1S)) < 0) goto reject;
143                         *p++ = c;
144                 }
145
146                 if (xbuff[1] == (unsigned char)(~xbuff[2]) &&
147                         (xbuff[1] == packetno || xbuff[1] == (unsigned char)packetno-1) &&
148                         check(crc, &xbuff[3], bufsz)) {
149                         if (xbuff[1] == packetno)       {
150                                 register int count = destsz - len;
151                                 if (count > bufsz) count = bufsz;
152                                 if (count > 0) {
153                                         memcpy (&dest[len], &xbuff[3], count);
154                                         len += count;
155                                 }
156                                 ++packetno;
157                                 retrans = MAXRETRANS+1;
158                         }
159                         if (--retrans <= 0) {
160                                 flushinput();
161                                 _outbyte(CAN);
162                                 _outbyte(CAN);
163                                 _outbyte(CAN);
164                                 return -3; /* too many retry error */
165                         }
166                         _outbyte(ACK);
167                         continue;
168                 }
169 reject:
170                 flushinput();
171                 _outbyte(NAK);
172         }
173 }