/*  ewrk3setup.c: A set up utility for the DIGITAL EtherWORKS 3 ethernet cards.

    Written 1994 by David C. Davies.

    Copyright 1994 Digital Equipment Corporation.

    This software may be used and distributed according to the terms of
    the GNU Public License, incorporated herein by reference.

    This setup   utility is written for  the  Digital  Equipment Corporation
    series of EtherWORKS 3 ethernet cards:

	DE203 Turbo (BNC)
	DE204 Turbo (TP)
	DE205 Turbo (TP BNC)

    The author may    be  reached as davies@wanton.lkg.dec.com  or   Digital
    Equipment Corporation, 550 King Street, Littleton MA 01460.

    =========================================================================
    This set up utility is designed to replace the DOS NICSETUP utility.

    TO DO:
    ------


    Revision History
    ----------------

    Version   Date        Description
  
      0.10   15-sep-94     Initial writing. ALPHA code release.
      0.20    1-nov-94     BETA code release.

    =========================================================================
*/

static char *version = "ewrk3setup.c:v0.20 11/1/94 davies@wanton.lkg.dec.com\n";

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <string.h>
#include <linux/if.h>
#include "../ewrk3.h"

#define CLRSCR printf("\e[2J\e[0H")
#define SHMEM_START 0xc8000
#define SHMEM_STOP  0xdffff
#define SHMEM_STEP  0x800

static unsigned char EEPROMCrc(char *dev, unsigned char *data, unsigned short len);
static char *MakeShadow(char *dev,unsigned char *data, unsigned short len);
static int ShadowDiffs(unsigned char *shadow, unsigned char *data, unsigned short len);
static int ShowEEPROM(char *dev, unsigned char *data, unsigned short len);
static int ChangeEntry(char *dev, unsigned char *data, unsigned char item);
static long int MemCalc(unsigned char val, int *len);
static int intChoose(char *prompt, long init, long *dst, long min, long max, long step);
static int Choose(char *prompt, char *src, char srcLen, char init, char *dst, char dstFlags);
static int strChoose(char *prompt, char **src, char srcLen, char init, char *dst, char dstFlags);

/* What's in the EEPROM */
enum {MemBase,IoBase,EisaId0,EisaId1,EisaId2,EisaId3,Misc0,Misc1,
      Pnam7,Pnam6,Pnam5,Pnam4,Pnam3,Pnam2,Pnam1,Pnam0,SwFlags,HwCatRev,
      NetMan2,EepromRev,NetMan0,NetMan1,ChipVerRev,SetUp,
      Paddr0,Paddr1,Paddr2,Paddr3,Paddr4,Paddr5,PaCrc,ChkSum,Cmr,
      Par0,Par1,Par2,Par3,Par4,Par5};

/* What we display */
enum {_IoBase = 1,_MemLen,_MemBase,_Irq,_FastBus,_16Bits,_Sqe,_Lab,
      _RemBoot,_RbTimOut,_RbProtocol,_NetCtrs,_RdAhead,_WrBehind,_0ws};

static unsigned char irq[] = {5,0,10,3,11,9,15,12};
static char *choice[2] = {"Dis","En"};
static char *MemLen[4] = {"N/A (I/O Accesses Only)", "2kB", "32kB", "64kB"};
static char *RbTim[2] = {"2.5 minutes","30 seconds"};
static char *RbProt[4] = {"No ROM","Protocol 1","Protocol 2","Protocol 3"};
static int lemac;

main(int argc, char **argv)
{
  union {
    unsigned char buff[1024];
    unsigned long pkt[256];
  } data;
  int i, skfd, exitFlag, maxItems;
  char *shadow, line[133];
  float pc;
  struct ifreq ifr;
  struct ewrk3_ioctl *ioc;

  if (getuid() == 0) {
    if (argc > 1) {
      if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
    
	strcpy(ifr.ifr_name, argv[1]);
	ioc = (struct ewrk3_ioctl *) &ifr.ifr_data;
	ioc->data = data.buff;

	ioc->cmd = EWRK3_GET_EEPROM;
	if (ioctl(skfd, EWRK3IOCTL, &ifr) != -1) {

	  if ((char) (EEPROMCrc(argv[1], data.buff, EEPROM_MAX) + data.buff[ChkSum]) == 0) {
	    shadow = MakeShadow(argv[1], data.buff, ioc->len);
	    lemac = data.buff[ChipVerRev];
	    CLRSCR;
	    maxItems = ShowEEPROM(argv[1], data.buff, ioc->len);

	    for (exitFlag=0; !exitFlag;) {
	      printf("\n\n Choose an option to change (hit RETURN to continue): ");
	      gets(line);
	      if (strlen(line) > 0) {
		sscanf(line,"%ld",&i);
		if ((i < maxItems) && (i > 0)) {
		  ChangeEntry(argv[1], data.buff, i);
		} else {
		  printf("Item number (%d) out of range! Please re-enter...\007\n",i);
		  sleep(2);
		}
		CLRSCR;
		maxItems = ShowEEPROM(argv[1], data.buff, ioc->len);
	      } else {
		exitFlag = 1;
		data.buff[ChkSum] = ~(EEPROMCrc(argv[1], data.buff, EEPROM_MAX)) + 1;
	      }
	    }
	    if (ShadowDiffs(shadow, data.buff, EEPROM_MAX)) {
	      printf("\nAre you sure you want to make these changes (Y/<N>): ");
	      gets(line);
	      if ((line[0]=='y') || (line[0]=='Y')) {
		data.buff[SwFlags] |= SW_INIT;     /* Ensure set.... */
		ioc->cmd = EWRK3_SET_EEPROM;       /* Write out the new data */
		ioc->len = EEPROM_MAX;
		if (ioctl(skfd, EWRK3IOCTL, &ifr) == -1) {
		  perror("ewrk3setup()\007");
		} else {
		  printf("\n\nRemember to reboot (or unload/reload the module) for the changes to take effect.\007\n\n");
		}
	      } else {
		printf("Changes were NOT written to EEPROM.\n\n");
	      }
	    } else {
	      printf("[No changes needed to be written to EEPROM.]\n\n");
	    }
	    free(shadow);
	  } else {
	    printf("ewrk3setup(): Bad EEPROM Data.\007\n");
	  }
	} else {
	  perror("ewrk3setup()\007");
	}
      } else {
	perror("ewrk3setup()\007");
      }
    } else {
      printf("ewrk3setup(): Need a device name\007\n");
    }
  } else {
    printf("ewrk3setup(): Only root can run this utility!\007\n");
  }
}

static unsigned char EEPROMCrc(char *dev, unsigned char *data, unsigned short len)
{
  int i;
  unsigned char j;

  for (j=0, i=0; i<EEPROM_MAX-1; i++) {
    j += data[i];
  }

  return j;
}

static char *MakeShadow(char *dev, unsigned char *data, unsigned short len)
{
  char *p;

  p = calloc(len, sizeof(char));

  bcopy(data, p, len);

  return p;
}

static int ShadowDiffs(unsigned char *shadow, unsigned char *data, unsigned short len)
{
  int i, j;

  for (j=0, i=0; i<len && j==0; i++) {
    if (*shadow++ != *data++) j=1;
  }

  return j;
}

static int ShowEEPROM(char *dev, unsigned char *data, unsigned short len)
{
  int i,j, index;
  char ManCode[8];
  char Jumper[2][4] = {"In","Out"};
  char *str;

  j = 1;

  printf("\n\n\n");
  printf("\t\t%s: %8s EEPROM CONFIGURATION\n",dev, &data[Pnam7]);
  printf("\t\t%.*s===============================\n\n",strlen(dev),"========");

  printf("%2d. I/O Base Address:\t\t 0x%02x\n",j++,data[IoBase]<<5);

  i = MemCalc(data[MemBase], &index);
  printf("%2d. Shared Memory Length:\t %s\n",j++, MemLen[index]);
  printf("%2d. Memory Base Address:\t %02x\n",j++, i);
  printf("%2d. IRQ:\t\t\t %d\n",j++,irq[(data[Misc0]>>4)&0x07]);

  printf("%2d. Fast Bus:\t\t\t %sabled\n",j++,choice[(data[Misc0]>>3)&0x01]);
  printf("%2d. 16 Bit Bus:\t\t\t %sabled\n",j++,choice[(data[Misc0]>>2)&0x01]);
  printf("%2d. S.Q.E.:\t\t\t %sabled\n",j++,choice[(data[SwFlags]>>4)&0x01]);
  printf("%2d. L.A.B.:\t\t\t %sabled\n",j++,choice[(data[SwFlags]>>3)&0x01]);
  printf("%2d. Remote Boot:\t\t %sabled\n",j++,choice[(data[SwFlags])&0x01]);
  printf("%2d. RB Timeout:\t\t\t %s\n",j++,RbTim[(data[SwFlags]>>1)&0x01]);
  printf("%2d. RB Protocol:\t\t %s\n",j++,RbProt[(data[SwFlags]>>5)&0x03]);
  printf("%2d. Net Counters:\t\t %sabled\n",j++,choice[(data[NetMan0])&0x01]);

  if (lemac == LeMAC2) {
    printf("%2d. Read Ahead:\t\t\t %sabled\n",j++,choice[(data[Misc0]>>7)&0x01]);
    printf("%2d. Write Behind:\t\t %sabled\n",j++,choice[(data[Misc0]>>1)&0x01]);
    printf("%2d. Zero Wait State:\t\t %sabled\n",j++,choice[(data[Misc0])&0x01]);
  }
  printf("\n");
  printf("    TP Link Status:\t\t %sabled\n",choice[!(data[Cmr]>>5)&0x01]);

  if (lemac == LeMAC2) {
    printf("    Hard Strap Jumper:\t\t %s\n",Jumper[(data[Cmr]>>3)&0x01]);
    printf("    Plug 'n Play Jumper:\t %s\n",Jumper[(data[Cmr]>>2)&0x01]);
  } else {
    printf("    Board Jumper Settings:\t W1 %s, W2 %s\n",
	              Jumper[(data[Cmr]>>3)&0x01],Jumper[(data[Cmr]>>2)&0x01]);
  }

  printf("    Hardware Address:\t\t ");
  if (lemac == LeMAC2) {
    for (i=0; i<5; i++) {
      printf("%02x:",data[Paddr0+i]);
    }
    printf("%02x\n",data[Paddr0+i]);
  } else {
    for (i=0; i<5; i++) {
      printf("%02x:",data[Par0+i]);
    }
    printf("%02x\n",data[Par0+i]);
  }
  
  ManCode[0]=(((data[EisaId0]>>2)&0x1f)+0x40);
  ManCode[1]=(((data[EisaId1]&0xe0)>>5)+((data[EisaId0]&0x03)<<3)+0x40);
  ManCode[2]=((data[EisaId1]&0x1f)+0x40);
  ManCode[3]=(((data[EisaId2]>>4)&0x0f)+0x30);
  ManCode[4]=((data[EisaId2]&0x0f)+0x30);
  ManCode[5]=(((data[EisaId3]>>4)&0x0f)+0x30);
  ManCode[6]=((data[EisaId3]&0x0f)+0x30);
  ManCode[7]='\0';
  printf("    EISA ID:\t\t\t %s\n",ManCode);

  return j;
}

static int ChangeEntry(char *dev, unsigned char *data, unsigned char item)
{
  int j, status = 0;
  unsigned long i, tmp;
  char k, c, *str, *p, line[133];

  switch (item) {

  case _IoBase:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = intChoose("\rNew I/O Base Address: 0x%02x", (short)(data[IoBase]<<5), &tmp, 0x100 , 0x3e0, 0x20);
    data[IoBase] = (tmp >> 5);

    break;
  case _MemBase:
  case _MemLen:
    i = MemCalc(data[MemBase], (int *) &k);
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rNew Shared Memory Length: %s", MemLen, 4, k, &k, 0x0);
    if (k == 0) {
      data[MemBase] = 0;
    } else {
      status = intChoose("\rNew Shared Memory Base Address: %02x", i, &tmp, SHMEM_START , SHMEM_STOP, SHMEM_STEP);
      if (tmp == 0) {
	data[MemBase] = 0;
      } else if (k == 1) {
	data[MemBase] = (tmp - 0x80000) / 0x800;
      } else if (k == 2) {
	data[MemBase] = tmp / 0x8000;
      } else if (k == 3) {
	data[MemBase] = tmp / 0x10000;
      }
    }

    break;

  case _Irq:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = Choose("\rNew IRQ: %2d", irq, sizeof(irq),((data[Misc0]>>4)&0x07), &data[Misc0], 0x70);

    break;
  case _FastBus:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rFast Bus: %sabled", choice, 2,((data[Misc0]>>3)&0x01), &data[Misc0], FAST_BUS);

    break;
  case _16Bits:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rNew 16 Bit Bus: %sabled", choice, 2,((data[Misc0]>>2)&0x01), &data[Misc0], ENA_16);

    break;
  case _Sqe:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rNew S.Q.E.: %sabled", choice, 2,((data[SwFlags]>>4)&0x01), &data[SwFlags], SW_SQE);

    break;
  case _Lab:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rNew L.A.B.: %sabled", choice, 2,((data[SwFlags]>>3)&0x01), &data[SwFlags], SW_LAB);

    break;
  case _RemBoot:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rNew Remote Boot: %sabled", choice, 2,((data[SwFlags])&0x01), &data[SwFlags], SW_REMOTE);

    break;
  case _RbTimOut:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rNew Remote Boot Time Out: %s", RbTim, 2,((data[SwFlags]>>1)&0x01), &data[SwFlags], SW_TIMEOUT);

    break;
  case _RbProtocol:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rNew RB Protocol: %s", RbProt, 4,((data[SwFlags]>>5)&0x03), &data[SwFlags], 0x60);

    break;
  case _NetCtrs:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rNew Net Counters: %sabled", choice, 2,((data[NetMan0])&0x01), &data[NetMan0], NETMAN_CCE);

    break;
  case _RdAhead:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rNew Read Ahead: %sabled", choice, 2,((data[Misc0]>>7)&0x01), &data[Misc0], READ_AHEAD);

    break;
  case _WrBehind:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rNew Write Behind: %sabled", choice, 2,((data[Misc0]>>6)&0x01), &data[Misc0], WRITE_BEHIND);

    break;
  case _0ws:
    printf("\nPress SPACE to scroll forward, 'b' to scroll back, RETURN to accept.\n\n");
    status = strChoose("\rNew Zero Wait State: %sabled", choice, 2,((data[Misc0])&0x01), &data[Misc0], _0WS_ENA);

    break;
  default:

  }

  return status;
}

static long int MemCalc(unsigned char val, int *len)
{
  unsigned long i;
  int j=0;

  if (val == 0) {
    i = 0;
  } else {
    j++;
    if ((val >= 0x40) && (val <= 0xff)) {
      i = (unsigned long)val * 0x800 + 0x80000;
    } else {
      j++;
      if ((val >= 0x14) && (val <= 0x1f)) {
	i = (unsigned long)val * 0x8000;
      } else {
	j++;
	if ((val >= 0x0a) && (val <= 0x0f)) {
	  i = (unsigned long)val * 0x10000;
	}
      }
    }
  }
  *len = j;

  return i;
}

static int intChoose(char *prompt, long init, long *dst, long min, long max, long step)
{
  long i, j, dir = 1, status = 0;
  char line[80], c;
  struct termios save_termios, buf;

  if (step > 0) {
    tcgetattr(STDIN_FILENO, &save_termios);
    buf = save_termios;
    buf.c_lflag &= ~(ECHO | ICANON);
    buf.c_cc[VMIN] = 1;
    buf.c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &buf);

    for (j = init; ; j+= dir*step) {
      j = ((j - min) % (max+step-min)) + min;
      if (j < min) j=max;
      sprintf(line, prompt, j);
      if (write(STDOUT_FILENO, line, strlen(line)) == strlen(line)) {
	if (read(STDIN_FILENO, &c, 1) == 1) {
	  if (c == '\n') {
	    *dst = j;
	    break;
	  } else if (c == 'b') {
	    dir = -1;
	  } else {
	    dir = 1;
	  }
	} else {
	  status = -1;
	  break;
	}
      } else {
	status = -1;
	break;
      }
    }
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &save_termios);

  } else {
    do {
      sprintf(line, prompt, init);
      if (write(STDOUT_FILENO, line, strlen(line)) == strlen(line)) {
	gets(line);
	if (strlen(line) > 0) {
	  if (sscanf(line,"%lx", &j) != 1) {
	    j = -1;
	  }
	} else {
	  j = init;
	}
      }
    } while ((j<min) || (j>max));
    *dst = j;
  }

  return status;
}

static int Choose(char *prompt, char *src, char srcLen, char init, char *dst, char dstFlags)
{
  int i, j, dir = 1, status = 0;
  char line[80], c;
  struct termios save_termios, buf;

  tcgetattr(STDIN_FILENO, &save_termios);
  buf = save_termios;
  buf.c_lflag &= ~(ECHO | ICANON);
  buf.c_cc[VMIN] = 1;
  buf.c_cc[VTIME] = 0;
  tcsetattr(STDIN_FILENO, TCSAFLUSH, &buf);

  for (i = init; ; i+=dir) {
    i = i % srcLen;
    if (i < 0) {
      i = srcLen - 1;
      if (lemac != LeMAC2) --i;
    }
      
    sprintf(line, prompt, src[i]);
    if (write(STDOUT_FILENO, line, strlen(line)) == strlen(line)) {
      if (read(STDIN_FILENO, &c, 1) == 1) {
	if (c == '\n') {
	  *dst &= ~(dstFlags);
	  for (j=dstFlags; !(j &0x01); i<<=1, j>>=1);
	  *dst |= (char)i;
	  break;
	} else if (c == 'b') {
	  dir = -1;
	} else {
	  dir = 1;
	}
      } else {
	status = -1;
	break;
      }
    } else {
      status = -1;
      break;
    }
    if (lemac != LeMAC2) i+=dir;
  }
  buf.c_lflag |= (ECHO | ICANON);
  tcsetattr(STDOUT_FILENO, TCSAFLUSH, &buf);

  tcsetattr(STDIN_FILENO, TCSAFLUSH, &save_termios);

  return status;
}

static int strChoose(char *prompt, char **src, char srcLen, char init, char *dst, char dstFlags)
{
  int i, j, dir = 1, len = 0, status = 0;
  char line[80], c, backspace = '', space = ' ', kill = '';
  char erase[3] = {'', ' ', ''};
/*  char line[80], c, kill = '', erase = '';*/
  struct termios save_termios, buf;

  tcgetattr(STDIN_FILENO, &save_termios);
  buf = save_termios;
  buf.c_lflag &= ~(ICANON);
  buf.c_cc[VMIN] = 1;
  buf.c_cc[VTIME] = 0;
  tcsetattr(STDIN_FILENO, TCSAFLUSH, &buf);

  for (i = init; ; i+=dir) {
    i = i % srcLen;
    if (i < 0) i = srcLen - 1;
    sprintf(line, prompt, src[i]);
    len = strlen(line);
    if (write(STDOUT_FILENO, line, len) == len) {
      if (read(STDIN_FILENO, &c, 1) == 1) {
	if (c == '\n') {
	  if (dstFlags) {
	    *dst &= ~(dstFlags);
	    for (j=dstFlags; !(j &0x01); i<<=1, j>>=1);
	    *dst |= (char)i;
	  } else {
	    *dst = i;
	  }
	  break;
	} else {
	  for (j=strlen(src[i]); j>0; j--) {
	    if (write(STDOUT_FILENO, &backspace, 1) != 1) {
	      status = -1;
	      break;
	    }
	  }
	  for (j=strlen(src[i]); j>0; j--) {
	    if (write(STDOUT_FILENO, &space, 1) != 1) {
	      status = -1;
	      break;
	    }
	  }
	  if (c == 'b') {
	    dir = -1;
	  } else {
	    dir = 1;
	  }
	}
      } else {
	status = -1;
	break;
      }
    } else {
      status = -1;
      break;
    }
  }

  tcsetattr(STDIN_FILENO, TCSAFLUSH, &save_termios);

  return status;
}
