/* atarisio - A UNIX backend for an Atari SIO2PC lead. Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) 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 ------------------------------------------------------------------------- Serial wrappers. */ static const char ident[]="$Id$"; #include #include #include #include #include #include #include #include #include #include "serial.h" #include "token.h" static const char ident_h[]=ATARISIO_SERIAL_H; /* ---------------------------------------- MACROS */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define WHICH (pthread_equal(pthread_self(),thread) ? "READ":"MAIN") /* Just as I'm always forgetting how many... */ #define MILLI2MICRO(x) ((x)*1000) /* ---------------------------------------- EXTERN */ const uchar SIO_ACK=0x41; const uchar SIO_NACK=0x4e; const uchar SIO_COMPLETE=0x43; const uchar SIO_ERROR=0x45; /* ---------------------------------------- STATICS */ static int fd=-1; static SIOCallback devtable[256]={0}; static int debug=FALSE; static int thread_created=FALSE; static int do_debug; static pthread_mutex_t mutex; static pthread_t thread; /* ---------------------------------------- PRIVATE FUNCTIONS */ static void Error(const char *p) { perror(p); if (fd!=-1) { if (pthread_equal(pthread_self(),thread)) { printf("WARNING: Reader thread exiting - " "serial port is still open\n"); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } else { printf("Trying to closing serial device\n"); SerialClose(); } } } static void Fatal(const char *p) { fprintf(stderr,"FATAL: "); perror(p); exit(EXIT_FAILURE); } static void Lock(void) { /* printf("[LOCK %s]\n",WHICH); */ pthread_mutex_lock(&mutex); } static void Unlock(void) { /* printf("[UNLOCK %s]\n",WHICH); */ pthread_mutex_unlock(&mutex); } void SerialPutchar(uchar c) { if (do_debug) printf("PUTCHAR: %2.2x (%c)\n",c,isprint(c) ? c:'?'); if (write(fd,&c,1)==-1) { Error("write:"); return; } } void SerialWrite(const uchar *p, size_t len) { uchar csum; if (do_debug) { printf("SENDING:\n"); HexDump(p,len); } csum=Checksum(p,len); while(len>0) { ssize_t w; w=write(fd,p,len); if (w==-1) { Error("write:"); return; } p+=w; len-=w; } write(fd,&csum,1); if (do_debug) printf("CHECKSUM: %2.2x\n",csum); } int SerialRead(uchar *p, size_t len) { uchar *orig; uchar csum; orig=p; while(len>0) { ssize_t r; r=read(fd,p,len); if (r==-1) { Error("read:"); return FALSE; } p+=r; len-=r; } if (read(fd,&csum,1)!=1) { Error("read:"); return FALSE; } return Checksum(orig,len)==csum; } /* ---------------------------------------- READER */ static void *ReaderThread(void *p) { SIOCommand cmd; time_t start; int st; int no; uchar frame[5]; uchar c; uchar csum; cmd.putchar=SerialPutchar; cmd.write=SerialWrite; cmd.read=SerialRead; while(TRUE) { Lock(); do_debug=debug; Unlock(); no=0; while(no<5) { if (read(fd,&c,1)==-1) Error("read"); if (ioctl(fd,TIOCMGET,&st)==-1) Error("ioctl(TIOCMGET)"); if (st&TIOCM_RNG) { if (do_debug) printf("Got byte %d (%c) and RNG set\n", c,isprint(c) ? c:'?'); if (no==0) start=time(NULL); frame[no++]=c; } else if (do_debug) { printf("Got byte %d (%c) and RNG *NOT* set\n", c,isprint(c) ? c:'?'); } } if ((time(NULL)-start)>1) { printf("Reader: Got 5 bytes, but too slowly - ignored\n"); } else { Lock(); csum=Checksum(frame,4); if (csum==frame[4]) { if (do_debug) { printf("Frame:\n"); HexDump(frame,5); } if (devtable[frame[0]]) { /* Fill up the command structure */ cmd.device=frame[0]; cmd.cmd=frame[1]; cmd.aux1=frame[2]; cmd.aux2=frame[3]; /* Let the device do its work */ if (do_debug) printf("Calling handler for device %2.2x\n",cmd.device); devtable[cmd.device](&cmd); } } else { printf("Reader: Bad checksum, expected %2.2x. Frame:\n",csum); HexDump(frame,5); } Unlock(); } } } /* ---------------------------------------- COMMAND HANDLERS */ static void Register(int argc, char *argv[]) { int f; for(f=0;f<256;f++) if (devtable[f]) printf("Device %2.2x regsitered\n",f); } /* ---------------------------------------- COMMAND TABLE */ static Command cmd[]= { { "register", 1,1, "register", "Show registered devices", Register } }; /* ---------------------------------------- INTERFACES */ void SerialInit(void) { if (pthread_mutex_init(&mutex,NULL)==-1) Fatal("pthread_mutex_init"); TokenRegister(sizeof cmd/sizeof cmd[0],cmd); } void SerialOpen(const char *path) { struct termios ios; if (fd!=-1) { printf("Serial device already open\n"); return; } fd=open(path,O_RDWR|O_NOCTTY|O_NDELAY); if (fd==-1) { Error(path); return; } if (fcntl(fd,F_SETFL,0)==-1) { Error("fcntl(F_SETFL,0)"); return; } if (tcgetattr(fd,&ios)==-1) { Error("tcgetattr"); return; } cfsetispeed(&ios,B19200); cfsetospeed(&ios,B19200); ios.c_cflag|=(CLOCAL|CREAD); ios.c_cflag&=~PARENB; ios.c_cflag&=~CSTOPB; ios.c_cflag&=~CSIZE; ios.c_cflag|=CS8; /* Not that it'll work if we can't disable it... */ #ifdef CRTSCTS ios.c_cflag&=~CRTSCTS; #endif ios.c_lflag&=~(ICANON|ECHO|ECHOE|ISIG); ios.c_iflag&=~(IXON|IXOFF|IXANY); ios.c_oflag&=~OPOST; if (tcsetattr(fd,TCSANOW,&ios)) { Error("tcsetattr"); return; } if (pthread_create(&thread,NULL,ReaderThread,NULL)==-1) Fatal("pthread_create"); thread_created=TRUE; } void SerialRegister(int device, SIOCallback func) { if (device<0 || device>255) { printf("Illegal device number %d registered\n",device); return; } Lock(); devtable[device]=func; Unlock(); } void SerialDeregister(int device) { if (device<0 || device>255) { printf("Illegal device number %d deregistered\n",device); return; } Lock(); devtable[device]=NULL; Unlock(); } void SerialDebug(int mode) { Lock(); debug=mode; Unlock(); } void SerialClose(void) { if (fd==-1) { printf("Serial not open\n"); } Lock(); if (thread_created) { printf("Cancelling reader thread..."); fflush(stdout); pthread_cancel(thread); pthread_join(thread,NULL); printf("Done\n"); } Unlock(); close(fd); fd=-1; thread_created=FALSE; } /* END OF FILE */