📄 xmodem.c
字号:
/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*//* this code needs standard functions memcpy() and memset() and input/output functions port_inbyte() and port_outbyte(). the prototypes of the input/output functions are: int port_inbyte(unsigned short timeout); // msec timeout void port_outbyte(int c); */#include "crc16.h"#include <stdio.h>#include <signal.h>#include <setjmp.h>#include <fcntl.h>#include <termios.h> #include <unistd.h>#include <time.h>#include <sys/stat.h>#define SOH 0x01#define STX 0x02#define EOT 0x04#define ACK 0x06#define NAK 0x15#define CAN 0x18#define CTRLZ 0x1A#define DLY_1S 2000#define MAXRETRANS 10#define TRUE 1#define FALSE 0static int last_error = 0;/****************Portting Start *******************/#include "string.h"char buff[50001];int Comfp;struct termios oldtty;int volatile fAppExit = FALSE;//static struct sigaction sig_KILL;static struct sigaction sig_TERM;#define ONE_SECOND 100enum { RET_SUCESS = 0, RET_SYSFAILED, RET_XMCANCEL, RET_XMNOSYNC, RET_XMERROR, //send or receiv error RET_MYCANCEL,};enum { MODE_SEND = 0, MODE_RECV = 1,};uint GetTimeTick(){ static struct timeval stmv = {0, 0}; if (!stmv.tv_sec){ gettimeofday(&stmv, NULL); return 0; } else { struct timeval tmv; gettimeofday(&tmv, NULL); return ((tmv.tv_sec - stmv.tv_sec)*100 + (tmv.tv_usec - stmv.tv_usec)/100000); }}// tick: 10 ms unitvoid SlowWait(int n10ms){ uint etv = GetTimeTick() + n10ms; //sched_yield(); while (1){ if (GetTimeTick() >= etv) break; // sched_yield(); }}int lowLevel_read(unsigned char *c ,int len){ return read(Comfp,c,len);}int lowLevel_write(unsigned char *c ,int len){ return write(Comfp,c,len);}void port_outbyte(unsigned char trychar){ unsigned char buf[2]; buf[0] = trychar; lowLevel_write(buf,1); printf("%d,",trychar);}unsigned char port_inbyte(unsigned int time_out){ unsigned char ch; int i; uint etv = GetTimeTick() + time_out/10; while (1){ if (GetTimeTick() >= etv) break; if(lowLevel_read(&ch,1) == 1){ last_error = 0; return ch; } } last_error = 1; return 0;}/****************Portting End*******************/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]; //printf("chkcrc:0x%0x, reccrc:0x%0x\n",crc,tcrc); 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){ tcflush(Comfp, TCIFLUSH); }static void flushoutput(void){ tcflush(Comfp, TCOFLUSH); }int xmodemReceive(FILE * filep,unsigned char *dest){ 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, filelen=0,len = 0; int lastblk,firstpacket = TRUE; int retry, retrans = MAXRETRANS; for(;;) { for( retry = 0; retry < 30; ++retry) { if(fAppExit) return RET_MYCANCEL; if (trychar) port_outbyte(trychar); flushoutput(); c = port_inbyte((DLY_1S)); //printf("rec:0x%x\n",c); if (last_error == 0) { switch (c) { case SOH: bufsz = 128; //trychar = NAK; goto start_recv; case STX: bufsz = 1024; goto start_recv; case EOT: port_outbyte(ACK); flushoutput(); //deal with file trancate the ctrlz for(i=lastblk-1;i>=0;i--){ if(dest[i] != CTRLZ) break; } //printf("xmodem last packet len:%d\n",i+1); fwrite(dest,1,i+1,filep); filelen -= (lastblk -i-1); return RET_SUCESS; /* normal end */ case CAN: c = port_inbyte(DLY_1S); if (c == CAN) { flushinput(); port_outbyte(ACK); flushoutput(); return RET_XMCANCEL; /* canceled by remote */ } break; default: break; } } } if (trychar == 'C') { trychar = NAK; continue; } flushinput(); port_outbyte(CAN); port_outbyte(CAN); port_outbyte(CAN); flushoutput(); return RET_XMNOSYNC; /* sync error */ start_recv: if(fAppExit) return RET_MYCANCEL; if (trychar == 'C') crc = 1; trychar = 0; p = xbuff; *p++ = c; for (i = 0; i < (bufsz+(crc?1:0)+3); ++i) { c = port_inbyte(DLY_1S); //printf("%d,",c); if (last_error != 0) goto reject; *p++ = c; } //printf("\nrec:%d,cur:%d\n",xbuff[1],packetno); 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) { if(!firstpacket){ fwrite(&dest[len-lastblk],1,lastblk,filep); filelen += lastblk; len = 0; } memcpy (&dest[len], &xbuff[3], bufsz); len += bufsz; lastblk = bufsz; ++packetno; firstpacket = FALSE; retrans = MAXRETRANS+1; } if (--retrans <= 0) { flushinput(); port_outbyte(CAN); port_outbyte(CAN); port_outbyte(CAN); flushoutput(); return RET_XMERROR; /* too many retry error */ } //printf("rec:%d send ack\n",xbuff[1]); port_outbyte(ACK); flushoutput(); continue; } else if(xbuff[1] < packetno){ port_outbyte(ACK); flushoutput(); continue; } reject: flushinput(); port_outbyte(NAK); flushoutput(); }}int xmodemTransmit(int frame,unsigned char *src, int srcsz){ unsigned char xbuff[1030]; /* 1024 for XModem 1k + 3 head chars + 2 crc + nul */ int bufsz, crc = -1; static unsigned char packetno = 1; int i, c, len = 0; int retry; //printf("transmint:%d\n",srcsz); if(frame == 0){ //printf("get handshare\n"); packetno = 1; for( retry = 0; retry < 30; ++retry) { if(fAppExit) return RET_MYCANCEL; c = port_inbyte((DLY_1S)); if(c <= 0) continue; //printf("recv:0x%0x\n",c); if (last_error == 0) { switch (c) { case 'C': crc = 1; goto start_trans; case NAK: crc = 0; goto start_trans; case CAN: c = port_inbyte(DLY_1S); if (c == CAN) { port_outbyte(ACK); flushoutput(); return RET_XMCANCEL; /* canceled by remote */ } break; default: break; } } } port_outbyte(CAN); port_outbyte(CAN); port_outbyte(CAN); flushoutput(); return RET_XMNOSYNC; /* no sync */ } for(;;) { start_trans: //printf("start to transfer\n"); if(fAppExit) return RET_MYCANCEL; /*xbuff[0] = SOH; bufsz = 128; xbuff[1] = packetno; xbuff[2] = ~packetno;*/ xbuff[0] = STX; bufsz = 1024; xbuff[1] = packetno; xbuff[2] = ~packetno; c = srcsz - len; if (c > bufsz) c = bufsz; //printf("send c:%d,frame:%d\n",c,frame); if (c > 0) { memset (&xbuff[3], 0, bufsz); if (c == 0) { xbuff[3] = CTRLZ; } else { memcpy (&xbuff[3], &src[len], c); if (c < bufsz) xbuff[3+c] = CTRLZ; } if (crc) { unsigned short ccrc = crc16_ccitt(&xbuff[3], bufsz); xbuff[bufsz+3] = (ccrc>>8) & 0xFF; xbuff[bufsz+4] = ccrc & 0xFF; } else { unsigned char ccks = 0; for (i = 3; i < bufsz+3; ++i) { ccks += xbuff[i]; } xbuff[bufsz+3] = ccks; } for (retry = 0; retry < MAXRETRANS; ++retry) { if(fAppExit) return RET_MYCANCEL; //printf("send packet:%d\n", bufsz+4+(crc?1:0)); for (i = 0; i < bufsz+4+(crc?1:0); ++i) { port_outbyte(xbuff[i]); } flushoutput(); c = port_inbyte(DLY_1S); //printf("packet:%d,get:%d\n",packetno,c); if (last_error == 0 ) { switch (c) { case ACK: ++packetno; len += bufsz; //printf("send byte:%d suc \n",bufsz); goto start_trans; case CAN: c = port_inbyte(DLY_1S); if ( c == CAN) { port_outbyte(ACK); flushoutput(); return RET_XMCANCEL; /* canceled by remote */ } break; case NAK: default: break; } } } port_outbyte(CAN); port_outbyte(CAN); port_outbyte(CAN); flushoutput(); return RET_XMERROR; /* xmit error */ } else { if(frame == EOT){ for (retry = 0; retry < 6; ++retry) { port_outbyte(EOT); flushoutput(); c = port_inbyte((DLY_1S)); if (c == ACK) break; } return (c == ACK)? RET_SUCESS : RET_XMERROR; }else return RET_SUCESS; } } }int Initial_SerialPort(char *file){ int fd; struct termios options; //printf("wb:Initial_SerialPort:%s\n",file); fd = open( file , O_RDWR |O_NOCTTY |O_NDELAY); if ( fd == -1 ) { /*open error!*/ perror("xmodem Can't open serial port!"); return -1; } /*Get the current options for the port...*/ tcgetattr(fd, &options); oldtty = options; /*Set the baud rates to BAUDRATE...*/ cfsetispeed(&options,B115200); cfsetospeed(&options,B115200); tcsetattr(fd, TCSANOW, &options); if (0 != tcgetattr(fd, &options)) { perror("xmodem SetupSerial error"); return -1; } /* * 8bit Data,no partity,1 stop bit... */ options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; options.c_cflag |= CREAD; tcflush(fd,TCIFLUSH); //close char change options.c_iflag = 0; /***Choosing Raw Input*/ options.c_lflag = 0; //options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); options.c_oflag &= ~OPOST; /* * Set the new options for the port... */ if (0 != tcsetattr(fd, TCSANOW, &options)) { perror("xmodem SetupSerial error"); return -1 ; } return fd ;}static void compmt(char *pmt){ int len; len = strlen(pmt); write(Comfp,pmt,len); flushoutput();}static void sig_handler(int sig){ //printf("get sig:%d\n",sig); if(sig == SIGTERM){ int i; printf("get sig SIGKILL\n"); fAppExit = TRUE; }}void initsiger(){ struct sigaction siga; siga.sa_handler = sig_handler; siga.sa_flags = 0; memset (&siga.sa_mask, 0, sizeof (sigset_t)); sigaction (SIGTERM, &siga, &sig_TERM); signal(SIGCHLD, SIG_IGN); }int main(int argc, char**argv){ unsigned char buff[3000]; char filename[128]; int mode,len,ret = 0; long filelen = 0,pos=0; FILE *fp; struct stat st; Comfp = Initial_SerialPort("/dev/ttyGS0"); if(Comfp < 0){ printf("open /dev/ttyGS0 failed\n"); return RET_SYSFAILED; } fAppExit = FALSE; initsiger(); tcflush(Comfp, TCIOFLUSH); while(!ret){ if(argc != 3){ printf("xmodem agument mode(recv/send) file(pathname)\n"); ret = RET_SYSFAILED; continue; } if(!strcmp(argv[1],"send")) mode = MODE_SEND; else if(!strcmp(argv[1],"recv")) mode = MODE_RECV; else{ ret = RET_SYSFAILED; continue; } strcpy(filename,argv[2]); printf("xmodem %s %s\n",argv[1],argv[2]); //check file and get filelen if (!stat(filename,&st)) filelen = st.st_size; else if(mode == MODE_SEND){ //file no exist ret = RET_SYSFAILED; continue; } if(mode == MODE_SEND){ int frame=0; printf("xmodem sending file:%s,len:%ld\n",filename,filelen); compmt("\n[ABPM] please receive files by xmodem with one minute\n"); if ((fp=fopen(filename, "r")) == NULL){ printf("xmodem open file failed\n"); ret = RET_SYSFAILED; continue; } fseek(fp,pos,SEEK_SET); while(filelen > 0){ printf("filelen:%d\n",filelen); if(filelen <= 1024) frame = EOT; len = fread(buff, sizeof(char),1024, fp); ret = xmodemTransmit(frame,buff,len); printf("xmodemTransmit return :%d\n",ret); if(ret > 0) break; filelen -= len; frame = 1; } fclose(fp); break; } else if(mode == MODE_RECV){ printf("xmodem recving file:%s\n",filename); compmt("\n[ABPM] please send file by xmodem with one minute\n"); if ((fp=fopen(filename, "w")) == NULL){ printf("xmodem open file failed\n"); ret = RET_SYSFAILED; continue; } fseek(fp,pos,SEEK_SET); ret = xmodemReceive(fp,buff); //printf("xmodem return ret:%d\n",ret); fclose(fp); if(ret != 0){ //printf("xmodem recv file failed:%d\n",ret); remove(filename); } break; } break; } if(ret == RET_MYCANCEL){ int i; for(i=0;i<3;i++){ port_outbyte(CAN); port_outbyte(CAN); port_outbyte(CAN); flushoutput(); } } close(Comfp); return ( ret); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -