/* Program iotest.c - test the i/o for pdprp.c
   written March-May, 1997 by Scot J. Kleinman
   on a Linux PC (i486; kernel 2.0.27; Redhat 4.1)
   with a Computer Boards Inc. CIO-DIO192 digital IO board.

   Program must be compiled  with a -O (or -O2 or higher)
   for the outb and inb commands to work.

   ioperm() needs to be run as root - a setuid is included
   here to accomplish it, but the program must have the "s"
   bit set by root - chmod 4711: see Linux Journal issue 37, May 97,
   pages 10-11.

   commands are like the following:

   a3h  -> set bit 3 of port a high
   b4l  -> set bit 4 of port b low
   rc   -> read port c
   +2   -> Switch to accessing Ports A2,B2,C2
   q    -> quit

   Currently configured for A&B output and C input */

#include<stdio.h>
#include<asm/io.h>  /*needed for outb()*/
#include<unistd.h>  /*needed for ioperm() and g/setuid*/

int intialize(void);  /*set up ports to be accessed*/
int inparse(void);    /*parse and deal with user input*/
void addbit(int bitnum, unsigned char *byte, int on); /*set bitnum in byte*/
void closeshop(void); /*closing routines*/

#define BASE 0x300UL
unsigned long Base = BASE;      /*base address of the DIO board*/
unsigned long PortA = BASE+0;   /*address of port A on DIO board*/
unsigned long PortB = BASE+1;   /*address of port B on DIO board*/
unsigned long PortC = BASE+2;   /*address of port C on DIO board*/
unsigned long Control = BASE+3; /*control address on DIO board*/

unsigned char Obyte = 0;           /*port A output byte (initially all low)*/

main()

    {
    int ioerr;  /*return value from IO calls*/
    int perr;   /*return value from inparse routine*/

    /* set up IO ports */
    if (ioerr=initialize()) 
        return(ioerr);

    /*process user input*/
    while ((perr=inparse())!=1);

    closeshop();
    return(0);
    }

/* function intialize sets up the ioports for access */
int initialize()

    {
    int err;
    uid_t user,user2; /*current user id*/

    /*give permission to acess IO ports
      0x300 is base port address
      +0 = Port A
      +1 = Port B
      +2 = Port C
      +3 = Control */
    
    PortA=Base+0;
    PortB=Base+1;
    PortC=Base+2;
    Control=Base+3;
 
    /*ioperm must have root permission to run sucessfully*/
    setuid(0);

    err=ioperm(Base,4,1);
    

    if (err!=0)     /*error in ioperm call*/
        {
        printf("ioperm returned %d.\n",err);
        printf("The program probably does not have root permission,\n");
        printf("required by ioperm.\n");
        return(err);
        }

    /* now set up control for I/O modes:Port A&B are output; C is input*/
    outb(137,Control);

    return (0);
    }

/* function inparse gets user input and does stuff with it */
int inparse()

    {
    int c;
    int off = 0;
    int bit = 0;
    int high = 0;
    int read = 0;
    int binc = 0; /*flag for incrementing Base*/
    int boff = 0; /*how much to increment Base*/
    unsigned char ibyte; /*read input byte*/
    int ioerr;

    while ((c=getchar())!='\n')
        switch (c)
            {
            case 'a': off=0;
                      printf("port %c",c);
                      break;
            case 'b': off=1;
                      printf("port %c",c);
                      break;
            case 'c': off=2;
                      printf("port %c",c);
                      break;
            case '+': binc=1;
                      printf("setting Port base to ");
                      break;
            case '0': case '1': case '2': case '3': case '4': case '5':
            case '6': case '7': case '8':
                      if (binc)
                          boff=(c-'0')*4-4;
                      else
                          bit = c-'0';
                      break;
            case 'h': high=1;
                      break;
            case 'l': high=0;
                      break;
            case 'q': return(1);
                      break;
            case 'r': read = 1;
                      break;
            }
    if (read)
        {
        ibyte=inb(Base+off);
        printf(" reads as %d.\n",ibyte);
        }
    else
        if (binc)
            {
            printf("%d\n",boff/4+1);
            binc=0;
            Base=0x300UL+boff;
            closeshop();
            if (ioerr=initialize()) 
                return(ioerr);
            }
        else
            {
            addbit(bit,&Obyte,high);
            outb(Obyte,Base+off);
            printf(" bit %d set to %d.\n",bit,high);
            }
    return (0);
    }

/* procedure addbit sets bit nbit in nbyte to the value in on */
void addbit( int nbit, unsigned char *nbyte,  int on)

    {
    unsigned char mask;

    mask = 1 << nbit;
    if (on)
        *nbyte|=mask;
    else
        *nbyte&=~mask;
    return;
    }

/*procedure closeshop sets things nicely for exit*/
void closeshop()
    {
    /* put all ports all bits back to low*/
    outb(0,PortA);
    outb(0,PortB);
    outb(0,PortC);

    return;
    }
