Message-Id: <3.0.1.32.19980212094220.007f7860@yacker.xiotech.com> Date: Thu, 12 Feb 1998 09:42:20 -0600 To: Nigel Megitt From: Randy Maas Subject: Re: Serial ports? Cc: djgpp AT delorie DOT com, eliz AT is DOT elta DOT co DOT il Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=====================_887319740==_" Precedence: bulk --=====================_887319740==_ Content-Type: text/plain; charset="us-ascii" Hello, I should have posted this response earlier -- I have attached a piece of code (written mostly by Thomas Demmer) that makes a file system extension of the serial ports. I plan on modifying it more in the future to match the linux serial port. The original fsext is availabe from ftp://ftp.lstm.ruhr-uni-bochum.de/pub/djgpp/ and is meant to work with svasync and djgpp v2.01. It does not support termios, which would be a more portable way of setting the baud rate. This fsext I've attached is meant to interface with Bill Currie's serial code. You'll probably want to get a hold of him at bcurrie AT tssc DOT co DOT nz or at bcurrie AT taniwha DOT tssc DOT co DOT nz and ask him for the latest version (it fixes some bugs that are in the archived versions). The attached code also has one pretty big drawback -- it is designed to work with the DJGPP v2.02. (It doesn't support termios either) All in all, I hope this helps Randy randym AT acm DOT org --=====================_887319740==_ Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="fsxcom.c" /* ** Example on how to use the File System Extensions of ** DJGPP V2.0 ** It might emulate UNIX's /dev/ttySXX stuff in the (near) future ** ** According to german copyright law this is ** Copyright by Thomas Demmer, 1996 ** just because I have written it. Anyway, do with it what you ** like. If you want to own the copyright on this stuff, take a ** text editor, search & replace all identifiers to something that ** vaguely resembles line-noise, and remove the copyright notice. ** This means you have done a "substantial creative work". ** ** Finding bugs and improving this will _NOT_ give you the copyright. ** If you find this logical, quit hacking and become a german lawyer. ** ** Tom (demmer AT lstm DOT ruhr-uni-bochum DOT de) ** ** Modified ** 1998/1/21 rcm -- removed fd2com, now __FSEXT_get_data ** 1997/11/16 Randy Maas (rcm: randym AT acm DOT org) Modified to only use ** Bill Curries excellent BCSERIO20 package. Slight and drastic ** modifications to internal calling conventions to allow lots of ** this power ** ** BUGS: - We cannot set the com bits/xon/xoff and so on ** We do not catch this right now. ** - We don't check the line status to send back exceptions. ** - We don't do a lot of things that should be done. ** $Id: fsxcom.c 1.1 1996/06/27 12:02:38 DEMMER Exp DEMMER $ $Log: fsxcom.c $ * Revision 1.1 1996/06/27 12:02:38 DEMMER * Initial revision * */ #include #include #include #include #include #include #include #include #include #include #include #include "serio.h" #include #define TTY_NAMES "/dev/ttyS" # define TCSETS (0x10) # define TCSETSW (0x11) # define TCSETSF (0x12) # define TCGETS (0x13) /* ** HAVE_NEW_OPEN : define if your open() does not block high bits ** DEBUG : define if you know what you're doing */ #define HAVE_NEW_OPEN #define EMULATED 1 #define DOSIO 0 #define TO_TIME 5 /* seconds */ #define DEF_BAUD (19200) #define DEF_BITS 8 #define DEF_STOP 1 #define DEF_PARITY 'N' typedef struct { int mode; SioPort* SPort; } sport_t; static void __attribute__((constructor)) lock_sections(void) { extern char sltext[] asm ("sltext"); extern char eltext[] asm ("eltext"); extern char sldata[] asm ("sldata"); extern char eldata[] asm ("eldata"); _go32_dpmi_lock_code(sltext,eltext-sltext); _go32_dpmi_lock_data(sldata,eldata-sldata); } /* ** Internal function: Shutdown Interrupt handler ** If port is not open, ** return EBADF, else 0 */ static int _dev_ttyS_close(sport_t* SPtr){ if (SPtr){ sio_closeport(SPtr->SPort); SPtr->SPort =3D NULL; return 0; } return EBADF; } /* ** Internal function ** Description ** Basically this scans a parameter string, opens a port with those= settings ** Check parameters: ** If parameters don't make sense (ha..) return EINVAL ** Right now, most of it is accepted ** No error: ** InstallIntrHandler for this port and return 0 ** Return Value ** 0 on error ** nnnzero otherwise */ static int _dev_ttyS_open(const char* params){ int stops=3D0, bits=3D0, baud=3D0, base=3D0, irq=3D0; char parity =3D DEF_PARITY; enum sioWordSize _Bits; enum sioParity _Parity; enum sioStopBits _Stop; SioPort* _SPort; if (sscanf(params, "%x,%x,%d,%d,%1c,%d", &base, &irq, &baud, &bits, &parity, &stops) < 2) return EINVAL; _SPort =3D sio_openport(base, irq); sio_setspeed(_SPort, 115200/baud); /* for standard ports only?*/ =20 switch(bits){ case 8: _Bits =3D sio8Bits; break; case 7: _Bits =3D sio7Bits; break; case 6: _Bits =3D sio6Bits; break; case 5: _Bits =3D sio5Bits; break; default: return EINVAL; } switch(tolower(parity)){ case 'n': _Parity =3D sioNoParity; break; case 'e': _Parity =3D sioEvenParity; break; case 'o': _Parity =3D sioOddParity; break; case 'm': _Parity =3D sioMarkParity; break; case 's': _Parity =3D sioSpaceParity; break; default : return EINVAL; } _Stop =3D (stops =3D=3D 2) ? sio2StopBits : sio1StopBit; sio_setparms(_SPort, _Bits, _Parity, _Stop); return 0; } /* ** Internal function ** Read from a port. ** If Non-Blocking I/O: ** Read everything in the queue, at most len bytes, ** if less set errno to EAGAIN, return number of ** Bytes got in got. ** Blocking I/O: ** Read until we have len bytes or a timeout occurs. ** return what we've got, set errno to EIO if timeout. ** UNIX has ETIMEDOUT, we don't. ** */ static int _dev_ttyS_read(sport_t* SPtr, char *buffer,size_t len,int *got){ struct timeval now,then; *got =3D 0; #ifdef DEBUG printf("_dev_ttyS_read: Len: %d\n", (int)len); #endif if (!SPtr) return EBADF; if((SPtr->mode & O_WRONLY) =3D=3D O_WRONLY){ return EACCES; } if(SPtr->mode & O_NONBLOCK){ #ifdef DEBUG printf("_dev_ttyS_read: Non-Blocking\n"); #endif /* Non blocking IO */ while(*got < len && sio_charready(SPtr->SPort)){ *buffer++ =3D sio_get(SPtr->SPort); *got +=3D 1; } #ifdef DEBUG printf("_dev_ttyS_read: got %d\n",*got); #endif if (*got =3D=3D len ) return 0; else return EAGAIN; } else{ #ifdef DEBUG printf("_dev_ttyS_read: Blocking\n"); #endif while( *got < len ){ gettimeofday(&now,0); then.tv_sec =3D now.tv_sec + TO_TIME; while (!sio_charready(SPtr->SPort)){ __dpmi_yield(); gettimeofday(&now,0); if( now.tv_sec > then.tv_sec ){ /* ** For lack of any better value */ return EIO; } } *buffer++ =3D sio_get(SPtr->SPort); *got +=3D 1; } return 0; } } /* ** Internal function ** write to a port. ** This is a quick hack -- will block while transmitting (bug) ** */ static int _dev_ttyS_write(sport_t* SPtr, char *buffer, size_t len,int= *written){ int NWritten; if (!SPtr) return EBADF; if( ((SPtr->mode & (O_RDONLY|O_WRONLY|O_RDWR)) =3D=3D O_RDONLY)){ return EACCES; } for (*written =3D 0; *written < len; *written +=3D NWritten) { NWritten =3D sio_write(SPtr->SPort, buffer+*written, len - *written); if (NWritten =3D=3D 0) break; /* The outgoing queue is full */ } return 0; } /* Description Basically this handles the @code{termios} functionality Of course, djgpp does not redirect this to the serial thing yet, but that is life. This also supports the absolute minimum -- termios structure, no termio. */ static int _dev_ttyS_ioctl(sport_t* SPtr, int cmd, va_list args) { struct termios* tios =3D va_arg(args, struct termios*); int val; switch(cmd) { case TCSETSF: /* deep-six all incoming characters */ while (sio_charready(SPtr->SPort)) sio_get(SPtr->SPort); case TCSETSW: /* First wait for the current transmit to complete */ while (!sio_senddone(SPtr->SPort)) _dpmi_yield(); =20 case TCSETS: /* Change the settings based termios structure */ switch(tios->c_ispeed){ case B0 : val =3D 0; break; case B50 : val =3D 50; break; case B75 : val =3D 75; break; case B110 : val =3D 110; break; case B134 : val =3D 134; break; case B150 : val =3D 150; break; case B200 : val =3D 200; break; case B300 : val =3D 300; break; case B600 : val =3D 600; break; case B1200 : val =3D 1200; break; case B1800 : val =3D 1800; break; case B2400 : val =3D 2400; break; case B4800 : val =3D 4800; break; case B9600 : val =3D 9600; break; case B19200 : val =3D 19200; break; case B38400 : val =3D 38400; break; default: return -1; } sio_setspeed(SPtr->SPort, 115200/val); return 0; break; case TCGETS: /* We only update the speed field. */ #ifdef DEBUG printf("speed is %d\n", 115200/sio_getspeed(SPtr->SPort)); #endif switch(115200/sio_getspeed(SPtr->SPort)){ case 0 : tios->c_ispeed =3D B0 ; break; case 50 : tios->c_ispeed =3D B50 ; break; case 75 : tios->c_ispeed =3D B75 ; break; case 110 : tios->c_ispeed =3D B110 ; break; case 134 : tios->c_ispeed =3D B134 ; break; case 150 : tios->c_ispeed =3D B150 ; break; case 200 : tios->c_ispeed =3D B200 ; break; case 300 : tios->c_ispeed =3D B300 ; break; case 600 : tios->c_ispeed =3D B600 ; break; case 1200 : tios->c_ispeed =3D B1200 ; break; case 1800 : tios->c_ispeed =3D B1800 ; break; case 2400 : tios->c_ispeed =3D B2400 ; break; case 4800 : tios->c_ispeed =3D B4800 ; break; case 9600 : tios->c_ispeed =3D B9600 ; break; case 19200 : tios->c_ispeed =3D B19200 ; break; case 38400 : tios->c_ispeed =3D B38400 ; break; default : return -1; } tios->c_ospeed =3D tios->c_ispeed; break; default: return -1; /* We didn't do it. */ } return 0; } /* ** ** The dispatcher for the driver. This function is called ** on every open(). If we consider the filename a ttyS, we return ** EMULATED to signal that we do IO. We also call __FSEXT_alloc_fd ** to signal read()/write()/close() to call us first. ** @txh @txhcat=3Dfsext */ int _dev_ttyS_handler(__FSEXT_Fnumber n,int *rv, va_list args) { /* Description This is file system extension that emulates the @file{/dev/ttyS%d} interface from LINUX (@pxref{/dev}). To use it, make sure it is added to the open handlers: @example __FSEXT_add_open_handler(_dev_ttyS_handler); @end example @end txh */ sport_t* SPtr =3D NULL; switch(n){ case __FSEXT_nop: /* ** This is probably never called */ *rv =3D 0; /* Or what ?? */ break; case __FSEXT_creat: /* ** It's pretty hard for a program to create ** a COM port, so we just refuse it. */ *rv =3D -1; errno =3D EINVAL; break; case __FSEXT_open:{ char *fn; int mode; int baud, bits; char parity; fn =3D va_arg(args, char *); /* ** If the filename starts with TTY_NAMES ** this could be for us. */ if( !fn|| !*fn || strncmp(TTY_NAMES,fn,strlen(TTY_NAMES))) /* Nope, refuse it */ return DOSIO; /* ** Depending on mode, we should behave ** differently sometimes. We ignore O_BINARY ** and O_TEXT and so on. And this is correct (by chance) ** We do _read and _write, and those functions do ** not know anything about text/binary. */ mode =3D va_arg(args, int); # ifdef DEBUG printf("Handler: Open %s, mode %d\n",fn,mode); # endif /* Preset to default values */ baud =3D DEF_BAUD; bits =3D DEF_BITS; parity =3D DEF_PARITY ; /* ** Already open? */ // if(SPorts[port].fd !=3D -1){ // *rv =3D -1; // errno =3D EACCES; // return EMULATED; // } /* ** Get a handle from DOS */ *rv =3D __FSEXT_alloc_fd(_dev_ttyS_handler); if(*rv < 0 ){ /* No more handles available */ errno =3D ENFILE; return EMULATED; } /* ** _KLUDGE_ : Get Com settings from ** Filename. */ if(_dev_ttyS_open(fn + strlen(TTY_NAMES))){ /* We have to release the file handle, because ** DOS has only 256 of them. So, we set the handler ** to the default and close the NUL device */ __FSEXT_set_function(*rv,NULL); _close(*rv); /* ** return -1, set errno and close port locally. ** This assumes the ttyS_open routine does not ** install the IntHandler before checking parameters */ *rv =3D -1; errno =3D EINVAL; return EMULATED; } /* ** Store fd & mode */ SPtr =3D __FSEXT_get_data(*rv); if (!SPtr) { SPtr =3D malloc(sizeof(sport_t)); __FSEXT_set_data(*rv, SPtr); } SPtr->mode =3D mode; } break; case __FSEXT_read:{ int fd; char *buf; int len; int lrv; fd =3D va_arg(args,int); buf =3D va_arg(args, char *); len =3D va_arg(args, size_t); SPtr =3D __FSEXT_get_data(fd); #ifdef DEBUG printf("Read request for %d Bytes on fd %d\n", len,fd); #endif /* fd connected to a port? */ if(!SPtr){ errno =3D EBADF; *rv =3D -1; return EMULATED; } if( (lrv =3D _dev_ttyS_read(SPtr,buf,len,rv))){ errno =3D lrv; } } break; case __FSEXT_write:{ int fd; char *buf; int len; int lrv; fd =3D va_arg(args,int); buf =3D va_arg(args, char *); len =3D va_arg(args, size_t); /* fd connected to a port ? */ SPtr =3D __FSEXT_get_data(fd); #ifdef DEBUG printf("Write request for %d Bytes on fd %d\n", len,fd); #endif if(!SPtr){ errno =3D EBADF; *rv =3D -1; return EMULATED; } if( (lrv =3D _dev_ttyS_write(SPtr,buf,len,rv))){ errno =3D lrv; } } break; case __FSEXT_ready:{ int fd; fd =3D va_arg(args,int); SPtr =3D __FSEXT_get_data(fd); #ifdef DEBUG printf("Ready_req on fd %d\n",fd); #endif if(!SPtr){ errno =3D EBADF; *rv =3D -1; return EMULATED; } /* ** _MISSING_ : status report! */ *rv =3D 0; if(sio_charready(SPtr->SPort)) *rv |=3D __FSEXT_ready_read; /* ** Here we say we are ready for output, ** but that may be wrong. */ *rv |=3D __FSEXT_ready_write; } break; case __FSEXT_close:{ int fd =3D va_arg(args,int); sport_t* SPtr=3D__FSEXT_get_data(fd); if (!SPtr){ *rv =3D errno =3D EBADF; } else{ _dev_ttyS_close(SPtr); free(SPtr); __FSEXT_set_function(fd,NULL); *rv =3D _close(fd); } } break; case __FSEXT_ioctl:{ int fd =3D va_arg(args,int); sport_t* SPtr =3D __FSEXT_get_data(fd); int cmd =3D va_arg(args,int); *rv =3D _dev_ttyS_ioctl(SPtr, cmd, args); } break; case __FSEXT_fcntl:{ int fd =3D va_arg(args,int); int cmd =3D va_arg(args,int); int what=3D va_arg(args,int); #ifdef DEBUG printf("fcntl on fd %d, cmd %x, what %x\n",fd,cmd,what); #endif SPtr=3D __FSEXT_get_data(fd); *rv =3D 0; switch(cmd){ case F_SETFD: SPtr->mode =3D what; *rv =3D 0; break; case F_GETFD: *rv =3D SPtr->mode; break; default: *rv =3D -1; } } break; default: fprintf(stderr,"fsxcom PANIC: Invalid request\n"); fflush(stderr); return DOSIO; } return EMULATED; } #ifdef TEST int main(int argc,char **argv){ int fd1,fd2; char buffer[1024]; fd_set readfd; struct timeval tv; unsigned s; int ready; int i; /* ** Install our handler */ Init_dev_ttyS(); fd1 =3D open("/dev/ttyS1",O_RDWR| O_NONBLOCK|O_TEXT); # ifndef HAVE_NEW_OPEN fcntl(fd1,F_SETFD, O_RDWR|O_NONBLOCK | O_TEXT); # endif if(fd1 < 0){ perror("open:"); return 0; } printf("fd: %d\n",fd1); sprintf(buffer,"Hello World\nHow are you?\n=94=84=81=99=8E=9A=E1\n"); fd2=3Dwrite(fd1,buffer,strlen(buffer)); printf("Sent %d Bytes\n",fd2); /* fd2 =3D0; while (! (fd2 =3D read(fd1,buffer,20))) ; printf("Got %d Bytes\n",fd2); for(i=3D0;i