delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/1998/02/12/10:44:53

Message-Id: <3.0.1.32.19980212094220.007f7860@yacker.xiotech.com>
Date: Thu, 12 Feb 1998 09:42:20 -0600
To: Nigel Megitt <nigelm AT rd DOT bbc DOT co DOT uk>
From: Randy Maas <randym AT acm DOT org>
Subject: Re: Serial ports?
Cc: djgpp AT delorie DOT com, eliz AT is DOT elta DOT co DOT il
Mime-Version: 1.0

--=====================_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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <io.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/fsext.h>
#include <string.h>
#include <time.h>
#include <dpmi.h>
#include "serio.h"
#include <termios.h>

#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<fd2;i++) putch(buffer[i]);
*/
    /*
    ** Check our ready function
    */
    {
        int fsext_ready =3D 0;
        __FSEXT_Function *func =3D __FSEXT_get_function(fd1);
        printf("Waiting endless for char on com2...\n");
        if(func)
            while(!(fsext_ready & __FSEXT_ready_read))
                func(__FSEXT_ready,&fsext_ready,&fd1);
    }
    close(fd1);


    fd1 =3D open("/dev/ttyS1",O_RDONLY| O_NONBLOCK|O_TEXT);
# ifndef HAVE_NEW_OPEN
    fcntl(fd1,F_SETFD, O_RDONLY|O_NONBLOCK | O_TEXT);
# endif
    printf("fd1 is now %d\n",fd1);
    /* Who has to care about Read/Write?? */
    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);



    /*
    ** Either I do not understand how to
    ** use select(), or there seems to be an error
    ** somewhere.
    */
    tv.tv_sec  =3D 5;
    tv.tv_usec =3D 0;
    FD_ZERO(&readfd);
    FD_SET(fd1,&readfd);

    if(-1 =3D=3D (ready =3D select(fd1+1,&readfd,NULL,NULL,&tv)))
        printf("Select error\n");
    else{
        printf("%d Files ready\n",ready);
        if (ready){
            for(i=3D0;i<=3Dfd1;i++){
                int j;
                if(FD_ISSET(i,&readfd))  printf("Char in %d\n",i);
                if(FD_ISSET(i,&readfd)){
                    fd2 =3D read(fd1,buffer,1024);
                    printf("Got %d bytes errno: %d\n",fd2,errno);
                    for(j=3D0;j<fd2;j++) putch(buffer[i]);
                }
            }
        }
    }
    fd1 =3D close(fd1);
    printf("Close: %d\n",fd1);
}
#endif


--=====================_887319740==_
Content-Type: text/plain; charset="us-ascii"



--=====================_887319740==_--

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019