/*
 * Simple general purpose I/O port control program for Linux
 * Written and copyright by Tomi Engdahl 2005
 * (e-mail: tomi.engdahl@hut.fi)
 *
 *
 * Supported port identifiers
 *  LPT1DATA
 *  LPT1STATUS
 *  LPT1HANDSHAKE
 *  JOYSTICK
 *   
 *
 * Supported commands
 *
 *  PRINT DEC       Read data, gives output as decimal number
 *  PRINT HEX       Read data, gives output as hexadecimal number
 *  PRINT BIN       Read data, gives output as binary number
 *  PRINTBITS bits  Reads the specified bits in the specified order (0..7)
 * 
 *  WRITE           Writes register value to port
 *  READ            Reads value from specified port to register
 *
 *  SETVALUE value  Sets the given value to register
 *  AND value       Performs AND with given value and register
 *  OR value        Performs OR with given value and register
 *  XOR value       Performs XOR with given value and register
 *  SETBITS bits    Sets given bits to 1 in register
 *  RESETBITS bits  Sets given bits to 0 in register
 *  TOGGLEBITS bits Toggles specified bit values  
 *
 * value can be gives as decimal number or heaxadecimal starting with 0x
 * bits is a list of bit position identifiers from 0 to 7
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/io.h>

#define MAX_ARGUMENT_LEN 8

/*
 * String compare routine, gives 1 when strings match, 0 otherwise
 */
int stringMatch(char * str1, char * str2)
{
  if (strcmp(str1,str2) == 0)
  {
    return 1; 
  }
  return 0;
}


/* 
 * Routine to convert port 
 */
int PortIDtoAddress(char * portID)
{
  if (stringMatch(portID, "LPT1DATA")) 
    {
      return 0x0378;
    }
  if (stringMatch(portID, "LPT1STATUS"))
    {
      return (0x0379 + 1);
    }  
  if (stringMatch(portID, "LPT1HANDSHAKE"))
    {
      return (0x0378 + 2);
    }  
  if (stringMatch(portID, "JOYSTICK"))
    {
      return 0x0201;
    } 
  fprintf(stderr, "Error: Invalid port name %s\n", portID), exit(1);
  return 0;
}


int ReadPort(char * portID)
{
  int iobase;

  iobase = PortIDtoAddress(portID);
  if (iobase==0) fprintf(stderr, "Error: Invalid port address %s\n", portID), exit(1);
  if (ioperm(iobase,1,1)) 
    {
      fprintf(stderr, "Error: Couldn't get the port at %x\n", iobase), exit(1);
    }
  else
    {
      return inb(iobase);
    }
  return 0;
}

int WritePort(char * portID, int value)
{
  int base;


  base = PortIDtoAddress(portID);

  if (base==0) fprintf(stderr, "Error: Invalid port address\n", base), exit(1);
  if (ioperm(base,1,1)) 
    {
      fprintf(stderr, "Error: Couldn't get the port at %x\n", base), exit(1);
    }
  else
    {
      outb(value, base);
    }
  return 0;
}

void bin_prnt_byte(int x)
{
   int n;
   for(n=0; n<8; n++)
   {
      if((x & 0x80) !=0)
      {
         printf("1");
      }
      else
      {
         printf("0");
      }
      x = x<<1;
   }
}

int printBits(int value,char *specifier)
{
  int index=0;
  int mask=0;

  while ((index < MAX_ARGUMENT_LEN) && (specifier[index]!=0))
    {
      switch (specifier[index])
	{
	case '0':
	  if ((value & 1) != 0) printf("1"); else printf("0");
	  break;
	case '1':
	  if ((value & 2) != 0) printf("1"); else printf("0");
	  break;
	case '2':
	  if ((value & 4) != 0) printf("1"); else printf("0");
	  break;
	case '3':
	  if ((value & 8) != 0) printf("1"); else printf("0");
	  break;
	case '4':
	  if ((value & 16) != 0) printf("1"); else printf("0");
	  break;
	case '5':
	  if ((value & 32) != 0) printf("1"); else printf("0");
	  break;
	case '6':
	  if ((value & 64) != 0) printf("1"); else printf("0");
	  break;
	case '7':
	  if ((value & 128) != 0) printf("1"); else printf("0");
	  break;
	default:
	  fprintf(stderr, "Error: Wrong bit position specifier.\n"), exit(1);
	  break;
	}
      index++;
    }
  printf("\n");
  return 0;
}

/* 
 * Converts bit string like 0123456789
 */
int bitsToMask(char * specifier)
{
  int index=0;
  int mask=0;

  while ((index < MAX_ARGUMENT_LEN) && (specifier[index]!=0))
    {
      switch (specifier[index])
	{
	case '0':
	  mask = mask | 1;
	  break;
	case '1':
	  mask = mask | 2;
	  break;
	case '2':
	  mask = mask | 4;
	  break;
	case '3':
	  mask = mask | 8;
	  break;
	case '4':
	  mask = mask | 16;
	  break;
	case '5':
	  mask = mask | 32;
	  break;
	case '6':
	  mask = mask | 64;
	  break;
	case '7':
	  mask = mask | 128;
	  break;
	default:
	  fprintf(stderr, "Error: Wrong bit position specifier.\n"), exit(1);
	  break;
	}
      index++;
    }
  return mask;
}

int processCommand(char * command, char * argument, int * argcount, char * portId, int * portData)
{
  int mask;
  *argcount=1; // defualt takes on argument

  if (stringMatch(command,"read"))
    {
      *argcount=0; // this command did no use any arguments
      *portData=ReadPort(portId);
    }
  else if (stringMatch(command,"write"))
    {
      *argcount=0; // this command did no use any arguments
      WritePort(portId, *portData);
    }
  else if (stringMatch(command,"print"))
    {
      if (stringMatch(argument,"dec"))
	{
	  printf("%i\n",*portData);
	}
      else if (stringMatch(argument,"hex"))
	{
	  printf("%x\n",*portData);
	}
      else if (stringMatch(argument,"bin"))
	{
          bin_prnt_byte(*portData);  
	  printf("\n");
	}
      else 
	{
	  fprintf(stderr, "Error: Wrong PRINT argument.\n"), exit(1);
	}
    } 
  else if (stringMatch(command,"printbits"))
    {
      printBits(*portData,argument);
    }
  else if (stringMatch(command,"setvalue"))
    {
      *portData=atoi(argument);
    }
  else if (stringMatch(command,"and"))
    {
      mask=atoi(argument);
      *portData = (*portData) & mask;
    }
  else if (stringMatch(command,"or"))
    {
      mask=atoi(argument);
      *portData = (*portData) | mask;
    }
  else if (stringMatch(command,"xor"))
    {
      mask=atoi(argument);
      *portData = (*portData) ^ mask;
    }
  else if (stringMatch(command,"setbits") || stringMatch(command,"setbit"))
    {
      mask=bitsToMask(argument);
      *portData = (*portData) | mask;
    }
  else if (stringMatch(command,"resetbits") || stringMatch(command,"resetbit"))
    {
      mask=bitsToMask(argument) ^ 0xFF;
      *portData = (*portData) & mask;
    }

  else if (stringMatch(command,"togglebits") || stringMatch(command,"togglebit"))
    {
      mask=bitsToMask(argument);
      *portData = (*portData) ^ mask;
    }


  return 0;  
}

int main(int argc, char **argv)
{                    
  int value;
  int argIndex;
  int acount;
  int portData;

  char * portId;

  if (argc<2)
    fprintf(stderr, "Error: Wrong number of arguments.\n"), exit(1);
  portId=argv[1];


  portData=0;
  argIndex=2; // start from first argument after port definition
  while (argIndex < argc)
    {
      if ((argIndex+1) < argc) 
	{
	  processCommand(argv[argIndex],argv[argIndex+1],&acount,portId,&portData);
	}
      else 
	{
	  processCommand(argv[argIndex],argv[argIndex+1],&acount,portId,&portData);
	}
      argIndex=argIndex+acount+1;
    }
  return 0;
}