/* xmodem.c - implements xmodem protocol Copyright (C) 2001 Jeff Carneal This program is free software; you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include #include #include "xmodem.h" #include "vrerror.h" #include "progress.h" int xmodem_sendfile(char *filename, int outfd) { XM xfer; extern PM pmeter; int mode = 0; struct stat st; struct sigaction sa; /* * Init section */ memset(&xfer,0,sizeof(XM)); xfer.portfd = outfd; xfer.fname = filename; xfer.blocknum = 1; memset(&pmeter,0,sizeof(PM)); if((xfer.filefd = open(xfer.fname, O_RDONLY))<0) { vr_error("Error: Can't open %s for reading", xfer.fname); } if(fstat(xfer.filefd, &st) < 0) { vr_error("Error: unable to stat '%s'", xfer.fname); } if(!S_ISREG(st.st_mode)) { vr_error("Error: File '%s' is not a regular file", xfer.fname); } pmeter.totalkbytes = st.st_size/1024; gettimeofday(&pmeter.start, NULL); /* * Get the NAK (we hope) */ while(xmodem_getbyte(&xfer) != NAK); pm_showmeter(PM_START); sa.sa_handler = (void *)pm_updatemeter; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGALRM, &sa, NULL); alarm(1); while((mode = file_readline(&xfer)) >= 0 ) { xmodem_sendline(&xfer); xmodem_getack(&xfer, 0); xfer.blocknum++; if(xfer.blocknum == 256) xfer.blocknum = 0; pmeter.bytesdone += BLOCK_SIZE; if(mode > 0) { break; } } alarm(0); /* * Done, send EOT and get * final ACK */ xmodem_sendbyte(&xfer, EOT); xmodem_getack(&xfer, EOT); pm_showmeter(PM_END); close(xfer.filefd); return st.st_size; } /* * Read 128 bytes from the file to be transmitted * Return values: * 0: ok * 1: last block, send EOT next */ int file_readline(XM *xfer) { int count = 0; char *buffer = xfer->linebuf+OFFSET; memset(buffer, 0, BLOCK_SIZE); if((count = read(xfer->filefd, (void *)buffer, BLOCK_SIZE))<0) vr_error("Error: unable to read from '%s'", xfer->fname); /* * We read fewer bytes than we have to send * so we pad the rest with '1a' */ if((count >= 0) && (count < BLOCK_SIZE)) { for(; countportfd, &fds); tv.tv_sec = TIMEOUT; tv.tv_usec = 0; if(select((xfer->portfd)+1, &fds, NULL, NULL, &tv) > 0) { if(read(xfer->portfd, (void *)&inbyte, 1)<0) vr_error("Error: xmodem_getbyte failed to read byte"); return inbyte; } /* if(DEBUG && (i<(RETRIES-1))) fprintf(stderr, "\nRead timeout, retrying...\n"); */ } xmodem_timeout(); return -1; } void xmodem_sendbyte(XM *xfer, byte outbyte) { if(write(xfer->portfd, (void *)&outbyte, 1)<0) { vr_error("Error: xmodem_sendbyte failed to write byte"); } } void xmodem_sendline(XM *xfer) { xfer->linebuf[0] = SOH; xfer->linebuf[1] = xfer->blocknum; xfer->linebuf[2] = ~(xfer->blocknum); xfer->linebuf[XLINE_SIZE-1] = xmodem_checksum(xfer); if(write(xfer->portfd, (void *)xfer->linebuf, XLINE_SIZE)<0) { vr_error("Error: xmodem_sendline failed to write line"); } } int xmodem_checksum(XM *xfer) { int sum=0, i=0; char *buffer = xfer->linebuf+OFFSET; for(i=0; i=RETRIES) vr_error("Error: Received NAK too many times. Abort."); return 0; } void xmodem_timeout(void) { vr_error("Error: Read timeout. Abort."); }