| /* |
| Copyright 2006 Arastra, Inc. |
| Copyright 2001, 2002 Georges Menie (www.menie.org) |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU Lesser General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <string.h> |
| #include <delay.h> |
| #include <uart8250.h> |
| |
| static int _inbyte(int msec) |
| { |
| while (!uart8250_can_rx_byte(CONFIG_TTYS0_BASE)) { |
| udelay(1000); |
| if (msec-- <= 0) |
| return -1; |
| } |
| return uart8250_rx_byte(CONFIG_TTYS0_BASE); |
| } |
| |
| static void _outbyte(unsigned char c) |
| { |
| uart8250_tx_byte(CONFIG_TTYS0_BASE, c); |
| } |
| |
| static unsigned short crc16_ccitt(const unsigned char *buf, int sz) |
| { |
| unsigned short crc = 0; |
| while (--sz >= 0) { |
| int i; |
| crc ^= (unsigned short) *buf++ << 8; |
| for (i = 0; i < 8; i++) |
| if (crc & 0x8000) |
| crc = crc << 1 ^ 0x1021; |
| else |
| crc <<= 1; |
| } |
| return crc; |
| } |
| |
| #define SOH 0x01 |
| #define STX 0x02 |
| #define EOT 0x04 |
| #define ACK 0x06 |
| #define NAK 0x15 |
| #define CAN 0x18 |
| #define CTRLZ 0x1A |
| |
| #define DLY_1S 1000 |
| #define MAXRETRANS 25 |
| |
| static int check(int crc, const unsigned char *buf, int sz) |
| { |
| if (crc) { |
| unsigned short crc = crc16_ccitt(buf, sz); |
| unsigned short tcrc = (buf[sz]<<8)+buf[sz+1]; |
| if (crc == tcrc) |
| return 1; |
| } |
| else { |
| int i; |
| unsigned char cks = 0; |
| for (i = 0; i < sz; ++i) { |
| cks += buf[i]; |
| } |
| if (cks == buf[sz]) |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static void flushinput(void) |
| { |
| while (_inbyte(((DLY_1S)*3)>>1) >= 0) |
| ; |
| } |
| |
| int xmodemReceive(unsigned char *dest, int destsz) |
| { |
| unsigned char xbuff[1030]; /* 1024 for XModem 1k + 3 head chars + 2 crc + nul */ |
| unsigned char *p; |
| int bufsz, crc = 0; |
| unsigned char trychar = 'C'; |
| unsigned char packetno = 1; |
| int i, c, len = 0; |
| int retry, retrans = MAXRETRANS; |
| |
| for(;;) { |
| for( retry = 0; retry < 16; ++retry) { |
| if (trychar) _outbyte(trychar); |
| if ((c = _inbyte((DLY_1S)<<1)) >= 0) { |
| switch (c) { |
| case SOH: |
| bufsz = 128; |
| goto start_recv; |
| case STX: |
| bufsz = 1024; |
| goto start_recv; |
| case EOT: |
| flushinput(); |
| _outbyte(ACK); |
| return len; /* normal end */ |
| case CAN: |
| if ((c = _inbyte(DLY_1S)) == CAN) { |
| flushinput(); |
| _outbyte(ACK); |
| return -1; /* canceled by remote */ |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| if (trychar == 'C') { trychar = NAK; continue; } |
| flushinput(); |
| _outbyte(CAN); |
| _outbyte(CAN); |
| _outbyte(CAN); |
| return -2; /* sync error */ |
| |
| start_recv: |
| if (trychar == 'C') crc = 1; |
| trychar = 0; |
| p = xbuff; |
| *p++ = c; |
| for (i = 0; i < (bufsz+(crc?1:0)+3); ++i) { |
| if ((c = _inbyte(DLY_1S)) < 0) goto reject; |
| *p++ = c; |
| } |
| |
| if (xbuff[1] == (unsigned char)(~xbuff[2]) && |
| (xbuff[1] == packetno || xbuff[1] == (unsigned char)packetno-1) && |
| check(crc, &xbuff[3], bufsz)) { |
| if (xbuff[1] == packetno) { |
| register int count = destsz - len; |
| if (count > bufsz) count = bufsz; |
| if (count > 0) { |
| memcpy (&dest[len], &xbuff[3], count); |
| len += count; |
| } |
| ++packetno; |
| retrans = MAXRETRANS+1; |
| } |
| if (--retrans <= 0) { |
| flushinput(); |
| _outbyte(CAN); |
| _outbyte(CAN); |
| _outbyte(CAN); |
| return -3; /* too many retry error */ |
| } |
| _outbyte(ACK); |
| continue; |
| } |
| reject: |
| flushinput(); |
| _outbyte(NAK); |
| } |
| } |