diff options
author | Ian C <ianc@noddybox.co.uk> | 2007-05-07 02:05:54 +0000 |
---|---|---|
committer | Ian C <ianc@noddybox.co.uk> | 2007-05-07 02:05:54 +0000 |
commit | 024f069d1f0feb3ecb2eea6b1443536defb01f35 (patch) | |
tree | fd108ce1dcc82fe14029db250f4505ea7f4321ff /src/serial.c | |
parent | c657944c91fae5f581839912cb0a55bee84c9278 (diff) |
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'src/serial.c')
-rw-r--r-- | src/serial.c | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/src/serial.c b/src/serial.c new file mode 100644 index 0000000..6f25892 --- /dev/null +++ b/src/serial.c @@ -0,0 +1,472 @@ +/* + + 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 <pthread.h> + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <termios.h> +#include <fcntl.h> +#include <errno.h> +#include <time.h> + +#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 */ |