#include <unistd.h>
#include <stdio.h>
#include "wdcnfg.h"

#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")

#ifdef REALLY_SLOW_IO
#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; }
#else
#define SLOW_DOWN_IO __SLOW_DOWN_IO
#endif

inline void outb(char value, unsigned short port)
{
__asm__ __volatile__ ("outb %%al,%%dx"
		::"a" ((char) value),"d" ((unsigned short) port));
}

inline unsigned int inb(unsigned short port)
{
	unsigned int _v;
__asm__ __volatile__ ("inb %%dx,%%al"
		:"=a" (_v):"d" ((unsigned short) port),"0" (0));
	return _v;
}

inline void outb_p(char value, unsigned short port)
{
__asm__ __volatile__ ("outb %%al,%%dx"
		::"a" ((char) value),"d" ((unsigned short) port));
	SLOW_DOWN_IO;
}

inline unsigned int inb_p(unsigned short port)
{
	unsigned int _v;
__asm__ __volatile__ ("inb %%dx,%%al"
		:"=a" (_v):"d" ((unsigned short) port),"0" (0));
	SLOW_DOWN_IO;
	return _v;
}


#define PERM_OFF 0
#define PERM_ON 1
#define WD_JUMPERS 6
#define version "0.2"

unsigned char eedata[8];

void show_lan_address_regs(int ioaddr)
{
 int i,val;
 for (i=0;i < 8; i++)
 {
  printf("%02x ",eedata[i]);
 }
}

void put_back_lan_address(int ioaddr)
{
  /* put back the lan address */
  outb_p((inb_p(ioaddr+1) & 0xc) | 2,ioaddr+1); /* set other bit */
  outb_p((inb_p(ioaddr+3) & 0xf) | (8<<4),ioaddr+3); /* set page 8 */
  outb_p((inb_p(ioaddr+1) & 0xc) | 0x12,ioaddr+1); /* set rla, other bit */
  while (inb_p(ioaddr+1) & 0x10); /* wait for recall */
}

void recall_eeprom(int ioaddr, int eerow)
{
  int i;
  outb_p((inb_p(ioaddr+1) & 0xc) | 2,ioaddr+1); /* set other bit */
  outb_p((inb_p(ioaddr+3) & 0xf) | (eerow<<4),ioaddr+3); /* set page */
  outb_p((inb_p(ioaddr+1) & 0xc) | 0x12,ioaddr+1); /* set rla, other bit */
  while (inb_p(ioaddr+1) & 0x10); /* wait for recall */
  for (i = 0; i < 8; i++) {
    eedata[i] = inb_p(ioaddr + 8 + i);
  }
  put_back_lan_address(ioaddr);
}

void store_eeprom(int ioaddr, int eerow)
{
  int i;
  outb_p((inb_p(ioaddr+1) & 0xc) | 2,ioaddr+1); /* set other bit */
  outb_p((inb_p(ioaddr+3) & 0xf) | (eerow<<4),ioaddr+3); /* set page */
  outb_p((inb_p(ioaddr+1) & 0xc) | 0x82,ioaddr+1); /* set sto, other bit */
  while (inb_p(ioaddr+1) & 0x80); /* wait for store */
  put_back_lan_address(ioaddr);
}

void show_eeprom(int ioaddr)
{
 int row;
 for (row=0;row<16;row++) {
   printf("eeprom addr %02x  ",row<<3);
   recall_eeprom(ioaddr,row);
   show_lan_address_regs(ioaddr);
   printf("\n");
 }
}

void show_regs(int ioaddr)
{
  int i,page,csav;
  unsigned int reg;

  printf("ASIC regs: ");
  for (i = 0; i < 0x10; i++) {
    reg = inb_p(ioaddr + i);
    printf("%02x ",reg);
  }
  printf("\n");
  csav = inb_p(ioaddr + 0x10) & 0xc0;
  for (page = 0; page < 4; page++) {
    outb(page<<6,ioaddr + 0x10); /* set page */
    printf("NIC page %d ",page);
    for (i = 0; i < 0x10; i++) {
      reg = inb_p(ioaddr + 0x10 + i);
      printf("%02x ",reg);
    }
    printf("\n");
  }
  outb_p(csav, ioaddr + 0x10);
}

static int board_there(ioaddr)
{
  int i;
  unsigned char csum;
  csum = 0;
  for (i = 0; i < 8; i++) csum += inb_p(ioaddr + 8 + i);
  if (csum != 0xff) return 0;
  return 1;
}

static unsigned char irqlist[8] = {9,3,5,7,10,11,15,4};

main(int argc, char *argv[])
{
 unsigned char boz,response[80];
 unsigned int iobase;
 int i,newval,itsok,jumpers,ee_io,eestart,eeirq,eeramstart,eeramsize;
 int eerombase,eeromsize;
 unsigned short board_id;
 CNFG_Adapter smc_card_info;
 int argok,cmd_eeprom,cmd_regs,cmd_regs_verbose;
 int part1,part2,numfound;
 int found_addr[20];
 int media,eemedia;

 iobase = 0;
 printf("Setup for Western Digital and SMC ethercards, version %s\n",version);
 if (iopl(3)) {
   perror("io-perm2");
   exit (-1);
 }
 if (argc > 1)
 for (i = 1; i < argc; i++) {
   argok = 0;
   if (strcmp(argv[i],"-e") == 0) cmd_eeprom = argok = 1;
   if (strcmp(argv[i],"-r") == 0) cmd_regs = argok = 1;
   if (strcmp(argv[i],"-v") == 0) cmd_regs_verbose = argok = 1;
   if ((strncmp(argv[i],"-a",2) == 0) && (argc > (i + 1))) {
     sscanf(argv[++i],"%x",&newval);
     if ((newval & 0xe3e0) == newval) {
       iobase = newval;
       argok = 1;
     }
     else {
       printf("addr must be [02468ace][0-3][02468ace]0\n");
       exit(-1);
     }
   }
   if (!argok) {
     printf("Usage: wdsetup [-e] [-r] [-a addr]\n");
     exit(-1);
   }
 }

 if (iobase == 0) {
   /* look for cards at all possible addresses */
   numfound = 0;
   for (part1 = 0; part1 <= 7; part1++) {
     for (part2 = 0; part2 <= 0x1f; part2++) {
       iobase = (part1 << 13) + (part2 << 5);
       if (iobase > 0x100) { 
	 if (board_there(iobase)) {
	   /* make sure */
	   if (board_there(iobase) && board_there(iobase) && board_there(iobase))
	     found_addr[numfound++] = iobase;
	 }
       }
     }
   }
   if (numfound == 0) {
     printf("no cards recognized.\n");
     exit(-1);
   }
   if (numfound == 1) iobase = found_addr[0];
   if (numfound > 1) {
     printf("%d cards recognized.\naddresses = ",numfound);
     for (i = 0; i < numfound; i++) {
       printf("%04x ",found_addr[i]);
     }
     printf("\nWhich address do you want to set-up ? ");
     fgets(response,80,stdin);
     sscanf(response,"%x",&iobase);
   }
 }
 smc_card_info.base_io = iobase;
 i = smc_get_config(&smc_card_info);
 if (i == -1) {
   printf("no card found\n");
   return;
 }
 if (i==1) {
   printf("Your card does not have readable configuration information.\n");
   printf("The only way to configure it is to remove the card and set");
   printf("the jumpers. See your manual for more information.");
   return;
 }
 if (cmd_regs) {
   show_regs(iobase);
   exit(0);
 }
 if (cmd_eeprom) {
   show_eeprom(iobase);
   exit(0);
 }
 if (cmd_regs_verbose) {
   printf("Your operating system has configured the card:\n");
   printf("i/o base address\t0x%04x\n\n",iobase);
   printf("irq\t\t\t%d\n\n",smc_card_info.irq_line);
   printf("ram size\t\t%d K\n\n",smc_card_info.ram_size);
   printf("ram start\t\t%06x\n\n",smc_card_info.ram_base);
   printf("\n");
   exit(0);
 }

/* printf("bid = 0x%08x\n",smc_card_info.full_bid); */
 printf("Board type:\t%s\n",smc_card_info.name);
 printf("node address: ");
 for (i = 0; i < 6; i++) printf("%02x",inb_p(iobase + 8 + i));
 printf("\n\n\t\t\tcurrent setup\n");
 if (!(smc_card_info.full_bid & INTERFACE_584_CHIP)) {
   printf("Adapter has a 583 ASIC, which I don't have a data sheet for. Sorry.");
   return;
 }
 jumpers = (inb_p(iobase + WD_JUMPERS) & 7) ^ 7;
 recall_eeprom(iobase,0); /* get soft config page */
 ee_io = ((eedata[2] & 0xe0) << 8) + ((eedata[2] & 0x1f) << 5);
 printf("i/o base addr\t\t%04x\n\n",ee_io);
 eeirq = 0;
 if ((smc_card_info.full_bid & INTERFACE_CHIP_MASK) != INTERFACE_5X3_CHIP) {
   eeirq = (eedata[1] & 4);
   }
 eeirq += ((eedata[4] & 0x60) >> 5);
 if ((eeirq == 2) && !(smc_card_info.full_bid & ALTERNATE_IRQ_BIT))
    eeirq = 4;
  else eeirq = irqlist[eeirq];
 printf("irq\t\t\t%s%d\n\n", (eedata[4] & 0x80) ? "" : "(Disabled) ",eeirq);
 eeramsize = 8 << ((eedata[1] & 8) >> 2);
 if (smc_card_info.full_bid & BOARD_16BIT) eeramsize <<= 1;
 printf("ram size\t\t%d K\n\n",eeramsize);
 eeramstart = ((eedata[5] & 0x1f) << 19) + ((eedata[0] & 0x3f) << 13);
 printf("ram base address\t%06x\n\n",eeramstart);
 printf("add wait states\t\t%s\n\n",(eedata[4] & 1) ? "no" : "yes");
 media = smc_card_info.full_bid & MEDIA_MASK;
 eemedia = eedata[4] & 4;
 printf("network connection\t");
 switch(eemedia) {
   case 0: printf("aui");
     break;
   case 4: printf("bnc");
   break;
   default: printf("unknown");
   break;
   }
 printf("\n\n");
 newval = (eedata[3] & 0xc0) >> 6;
 switch(newval) {
 case 0: eeromsize = 0;
   break;
 case 1: eeromsize = 16;
   break;
 case 2: eeromsize = 32;
   break;
 case 3: eeromsize = 64;
   break;
 }
 if (eeromsize == 0) printf("rom size\t\t(Disabled)\n\n");
 else printf("rom size\t\t%d K\n\n",eeromsize);
 eerombase = (eedata[3] & 0x3e) << 14;
 printf("rom base address\t%05x\n\n",eerombase);
 printf("change the soft configuration in eeprom? (y) ");
 fgets(response,80,stdin);
 if ((response[0] == 'y') || (response[0] == 'Y') || (response[0] == '\n')) {
   itsok = 0;
   while (!itsok) {
     printf("What io address do you want (%x) : ",ee_io);
     fgets(response,80,stdin);
     itsok = 1;
     if (response[0] != '\n') {
       sscanf(response,"%x",&newval);
       if ((newval & 0xe3e0) == newval) {
	 eedata[2] = ((newval >> 5) & 0x1f) + ((newval >> 8) & 0xe0);
       }
       else {
	 itsok = 0;
	 printf("addr must be [02468ace][0-3][02468ace]0\n");
       }
     }
   }
   itsok = 0;
   while (!itsok) {
     printf("What irq do you want (%d) : ",eeirq);
     fgets(response,80,stdin);
     itsok = 1;
     if (response[0] != '\n') {
       sscanf(response,"%d",&newval);
       switch(newval) {
       case 3:
	 eedata[1] &= ~4;
	 eedata[4] &= ~0x60;
	 eedata[4] |= 0x20;
	 break;
       case 4:
	 eedata[1] |= 4;
	 eedata[4] |= 0x60;
	 break;
       case 5:
	 eedata[1] &= ~4;
	 eedata[4] |= 0x40;
	 break;
       case 7:
	 eedata[1] &= ~4;
	 eedata[4] |= 0x60;
	 break;
       case 9:
	 eedata[1] &= ~4;
	 eedata[4] &= ~0x60;
	 break;
       case 10:
	 eedata[1] |= 4;
	 eedata[4] &= ~0x60;
	 break;
       case 11:
	 eedata[1] |= 4;
	 eedata[4] &= ~0x60;
	 eedata[4] |= 0x20;
	 break;
       case 15:
	 eedata[1] |= 4;
	 eedata[4] &= ~0x60;
	 eedata[4] |= 0x40;
	 break;
       default:
	 itsok = 0;
	 break;
       }
     }
   }
   itsok = 0;
   while (!itsok) {
     printf("enter ram start address (%06x) : ",eeramstart);
     fgets(response,80,stdin);
     itsok = 1;
     if (response[0] != '\n') {
       sscanf(response,"%x",&newval);
       if ((newval & 0xffe000) == newval) {
	 eedata[0] &= 0xc0;
	 eedata[0] |= (newval & 0x07e000) >> 13;
	 eedata[5] &= 0xe0;
	 eedata[5] |= (newval & 0xf80000) >> 19;
       }
       else itsok = 0;
     }
   }
   itsok = 0;
   while (!itsok) {
     printf("add wait states ? (%s) : ",(eedata[4] & 1) ? "no" : "yes");
     fgets(response,80,stdin);
     itsok = 1;
     if (response[0] != '\n') {
       if ((response[0] == 'y') || (response[0] == 'Y')) {
	 eedata[4] &= ~1;
       }
       else {
	 if ((response[0] == 'n') || (response[0] == 'N')) {
	   eedata[4] |= 1;
	 }
	 else itsok = 0;
       }
     }
   }
   itsok = 0;
   while (!itsok) {
     if ((media == ETHERNET_MEDIA) || (media == EW_MEDIA)) {
       printf("\n1 = aui");
       printf("\n2 = bnc");
     }
     if ((media == TWISTED_PAIR_MEDIA) || (media == EW_MEDIA)) {
       printf("\n3 = twisted pair");
     }
     printf("\nnetwork connection ? ");
     switch(eemedia) {
     case 0: printf("(aui)");
       break;
     case 4: printf("(bnc)");
       break;
     default: printf("(unknown)");
       break;
     }
     printf(" : ");
     fgets(response,80,stdin);
     if (response[0] != '\n') {
       switch(response[0]) {
       case '1':
	 if ((media == ETHERNET_MEDIA) || (media == EW_MEDIA)) {
	   eedata[4] &= ~4;
	   itsok = 1;
	 }
	 break;
       case '2':
	 if ((media == ETHERNET_MEDIA) || (media == EW_MEDIA)) {
	   eedata[4] |= 4;
	   itsok = 1;
	 }
	 break;
       case '3': 
	 if ((media == TWISTED_PAIR_MEDIA) || (media == EW_MEDIA)) {
	   /* need info to implement this */
	   itsok = 1;
	 }
       }
     }
   }
   
   for (i = 0; i < 8; i++) outb_p(eedata[i],iobase + 8 + i);
   store_eeprom(iobase,0);
   printf("ok, new values stored into eeprom 00-07\n");
   if (jumpers == 0) {
     printf("The board will configure itself from the new values in eeprom\n");
     printf("the next time you reboot.\n");
   }
   else {
     printf("Your jumpers are set to one of the hard configuration values.\n");
     printf("you have to change the jumpers before the soft configuration\n");
     printf("will be used by the board.\n");
   }
 }
}
