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