1 /*
2 xmodem.c - implements xmodem protocol
3 Copyright (C) 2001 Jeff Carneal
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #include "config.h"
21 #include <stdio.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <signal.h>
28 #include "xmodem.h"
29 #include "vrerror.h"
30 #include "progress.h"
31
xmodem_sendfile(char * filename,int outfd)32 int xmodem_sendfile(char *filename, int outfd) {
33 XM xfer;
34 extern PM pmeter;
35 int mode = 0;
36 struct stat st;
37 struct sigaction sa;
38
39 /*
40 * Init section
41 */
42 memset(&xfer,0,sizeof(XM));
43 xfer.portfd = outfd;
44 xfer.fname = filename;
45 xfer.blocknum = 1;
46
47 memset(&pmeter,0,sizeof(PM));
48
49 if((xfer.filefd = open(xfer.fname, O_RDONLY))<0) {
50 vr_error("Error: Can't open %s for reading", xfer.fname);
51 }
52
53 if(fstat(xfer.filefd, &st) < 0) {
54 vr_error("Error: unable to stat '%s'", xfer.fname);
55 }
56 if(!S_ISREG(st.st_mode)) {
57 vr_error("Error: File '%s' is not a regular file", xfer.fname);
58 }
59
60 pmeter.totalkbytes = st.st_size/1024;
61 gettimeofday(&pmeter.start, NULL);
62
63 /*
64 * Get the NAK (we hope)
65 */
66 while(xmodem_getbyte(&xfer) != NAK);
67
68 pm_showmeter(PM_START);
69
70 sa.sa_handler = (void *)pm_updatemeter;
71 sigemptyset(&sa.sa_mask);
72 sa.sa_flags = SA_RESTART;
73 sigaction(SIGALRM, &sa, NULL);
74 alarm(1);
75
76 while((mode = file_readline(&xfer)) >= 0 ) {
77 xmodem_sendline(&xfer);
78
79 xmodem_getack(&xfer, 0);
80
81 xfer.blocknum++;
82 if(xfer.blocknum == 256)
83 xfer.blocknum = 0;
84
85 pmeter.bytesdone += BLOCK_SIZE;
86
87 if(mode > 0) {
88 break;
89 }
90 }
91 alarm(0);
92
93 /*
94 * Done, send EOT and get
95 * final ACK
96 */
97 xmodem_sendbyte(&xfer, EOT);
98 xmodem_getack(&xfer, EOT);
99
100 pm_showmeter(PM_END);
101
102 close(xfer.filefd);
103 return st.st_size;
104 }
105
106 /*
107 * Read 128 bytes from the file to be transmitted
108 * Return values:
109 * 0: ok
110 * 1: last block, send EOT next
111 */
file_readline(XM * xfer)112 int file_readline(XM *xfer) {
113 int count = 0;
114 char *buffer = xfer->linebuf+OFFSET;
115
116 memset(buffer, 0, BLOCK_SIZE);
117
118 if((count = read(xfer->filefd, (void *)buffer, BLOCK_SIZE))<0)
119 vr_error("Error: unable to read from '%s'", xfer->fname);
120
121 /*
122 * We read fewer bytes than we have to send
123 * so we pad the rest with '1a'
124 */
125 if((count >= 0) && (count < BLOCK_SIZE)) {
126 for(; count<BLOCK_SIZE; count++)
127 buffer[count] = XM_EOF;
128 return 1;
129 }
130
131 return 0;
132 }
133
xmodem_getbyte(XM * xfer)134 byte xmodem_getbyte(XM *xfer) {
135 byte inbyte;
136 fd_set fds;
137 struct timeval tv;
138 int i;
139
140 for(i=0; i<RETRIES; i++) {
141 FD_ZERO(&fds);
142 FD_SET(xfer->portfd, &fds);
143 tv.tv_sec = TIMEOUT;
144 tv.tv_usec = 0;
145 if(select((xfer->portfd)+1, &fds, NULL, NULL, &tv) > 0) {
146 if(read(xfer->portfd, (void *)&inbyte, 1)<0)
147 vr_error("Error: xmodem_getbyte failed to read byte");
148
149 return inbyte;
150 }
151
152 /*
153 if(DEBUG && (i<(RETRIES-1)))
154 fprintf(stderr, "\nRead timeout, retrying...\n");
155 */
156 }
157
158 xmodem_timeout();
159 return -1;
160 }
161
xmodem_sendbyte(XM * xfer,byte outbyte)162 void xmodem_sendbyte(XM *xfer, byte outbyte) {
163
164 if(write(xfer->portfd, (void *)&outbyte, 1)<0) {
165 vr_error("Error: xmodem_sendbyte failed to write byte");
166 }
167 }
168
xmodem_sendline(XM * xfer)169 void xmodem_sendline(XM *xfer) {
170
171 xfer->linebuf[0] = SOH;
172 xfer->linebuf[1] = xfer->blocknum;
173 xfer->linebuf[2] = ~(xfer->blocknum);
174 xfer->linebuf[XLINE_SIZE-1] = xmodem_checksum(xfer);
175
176 if(write(xfer->portfd, (void *)xfer->linebuf, XLINE_SIZE)<0) {
177 vr_error("Error: xmodem_sendline failed to write line");
178 }
179 }
180
xmodem_checksum(XM * xfer)181 int xmodem_checksum(XM *xfer) {
182 int sum=0, i=0;
183 char *buffer = xfer->linebuf+OFFSET;
184
185 for(i=0; i<BLOCK_SIZE; i++) {
186 sum += buffer[i];
187 }
188
189 return sum&0377;
190 }
191
xmodem_getack(XM * xfer,byte outbyte)192 int xmodem_getack(XM *xfer, byte outbyte) {
193 byte inchar;
194 int retry = 0;
195
196 while(((inchar = xmodem_getbyte(xfer)) != ACK) &&
197 (retry<RETRIES)) {
198 if(inchar == CAN) {
199 vr_error("Error: Transferred cancelled by remote side");
200 } else {
201 if(inchar != NAK) {
202 fprintf(stderr, "\nWarning: received unknown xmodem code from remote: '%02x'\n", inchar);
203 }
204 retry++;
205 if(outbyte) {
206 xmodem_sendbyte(xfer, outbyte);
207 } else {
208 xmodem_sendline(xfer);
209 }
210 }
211 }
212 if(retry>=RETRIES)
213 vr_error("Error: Received NAK too many times. Abort.");
214
215 return 0;
216 }
217
xmodem_timeout(void)218 void xmodem_timeout(void) {
219 vr_error("Error: Read timeout. Abort.");
220 }
221