/*
   debug.c: a handy utility to view registers & memory for NetWinder
   Copyright 1998  woody@corelcomputer.com

   Changes for Linux 2.2 Pat Beirne
      mostly involve change the access from mem_read/write to mem_mmap
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

#define	DEB_BYTE	1
#define	DEB_WORD        2
#define	DEB_DWORD       4

#define	CMD_DUMP	'd'
#define	CMD_EDIT	'e'
#define	CMD_INPUT	'i'
#define	CMD_OUTPUT	'o'
#define	CMD_SUPER_IN	"si"
#define	CMD_SUPER_OUT	"so"
#define	CMD_CPLD_READ	"cr"
#define	CMD_CPLD_WRITE	"cw"

#define VERSION		"1.2"

/* these globals handle the differences between Linux 2.0.x
 and 2.2.x */
static int kernel_version = 20;  /* or 22 */
#define IO_BASE_20 0xE0000000
#define IO_BASE_22 0x7c000000
static long io_base;
#define PAGE_SIZE 0x1000


static char* _pIO;

#define MAP_IO(h) _pIO = mmap(0,PAGE_SIZE,PROT_READ|PROT_WRITE,MAP_FILE|MAP_SHARED,h,io_base)
#define UNMAP_IO munmap(_pIO,PAGE_SIZE)
#define IS_OK_IO _pIO
#define outb(p, v)    *(_pIO+(p)) = (char)v
#define inb(p) *(_pIO+(p))

#define UNLOCK    outb(0x370, 0x87); outb(0x370, 0x87);
#define LOCK      outb(0x370, 0xAA);

extern int errno;

int tmpSuperIO;

#define TOASCII(a) (((a)>' ' && (a)<0x7F) ? a : '.')
/*
 display as bytes, shorts or longs

 bdata   pointer to the data to read
 bcount  number of bytes
 buff    a text buffer to fill for display
 */
void 
db (int f_mem, long bdata, int bcount, char *buff, int bytes_at_a_time)
{
  int bposition;
  int i;
  char asciibuf[17];
  char *pmmap,*p;
  int count;
  int offset;

  if (!bcount)
    return;

  /* shorts and longs must be aligned */
  bdata &= ~(bytes_at_a_time-1);

  /* option to skip the first few reads in a line */
  bposition = bdata & 0x0F;

  /* map in the zone, rounding down to nearest page */
  offset = bdata & (PAGE_SIZE-1);
  pmmap = mmap(0,bcount+offset,PROT_READ,MAP_FILE|MAP_SHARED,
               f_mem,bdata - offset);
  /* and create a pointer to the exact location within the mmap */
  p = pmmap + offset;
  
  for (count=bcount; count>0;) {
      strcpy(asciibuf,"                ");

      buff += sprintf(buff,"%08X  ",bdata & 0xfffffff0);
      /* if the first few bytes need to be skipped .... */
      while (bposition) {
          buff += sprintf(buff,"   ");
          bposition--;
      }
      do {
          unsigned int a;
          switch(bytes_at_a_time) {
          case 1:
              a = *(unsigned char*)p;
              buff += sprintf(buff,"%02X%c",a,
                              (bdata & 15) == 7 ? '-' :
                              ((bdata & 7) == 3 ? '.' : ' '));
              asciibuf[bdata & 0xf] = TOASCII(a);
              bdata++;
              break;
          case 2:
              a = *(unsigned short*)p;
              buff += sprintf(buff,"%04X%c ",a,
                              (bdata & 15) == 6 ? '-' :
                              ((bdata & 7) == 2 ? '.' : ' '));
              asciibuf[bdata & 0xf] = TOASCII(a & 255);
              bdata++;
              asciibuf[bdata & 0xf] = TOASCII(a >> 8);
              bdata++;
              break;
          case 4:
              a = *(unsigned long*)p;
              buff += sprintf(buff,"%08X %c  ",a,
                              (bdata & 15) == 4 ? '-' :
                              ((bdata & 7) == 2 ? '.' : ' '));
              asciibuf[bdata & 0xf] = TOASCII(a & 255);
              bdata++;
              asciibuf[bdata & 0xf] = TOASCII((a>>8) & 255);
              bdata++;
              asciibuf[bdata & 0xf] = TOASCII((a>>16) & 255);
              bdata++;
              asciibuf[bdata & 0xf] = TOASCII(a>>24);
              bdata++;
              break;
          default: return;
          }
          p += bytes_at_a_time;
          count-=bytes_at_a_time;
      } while (bdata & 0xF);
      buff += sprintf(buff,"  [%16s]\n",asciibuf);
  }
  munmap(pmmap,bcount+offset);
}

/* write to memory

returns true to continue
modifies the first parameter on return
*/
int eb(long* paddr, int f_mem, int deb_mode, char* scan_buffer) {
    char* pmmap, *p;
    long addr = *paddr;
    unsigned char btemp;
    unsigned long dtemp;
    int i;
    int ret = 1;

    /* mmap a page just below deb_add */
    pmmap = mmap(0,PAGE_SIZE,PROT_READ | PROT_WRITE,
                 MAP_FILE | MAP_SHARED, f_mem,
                 (long) addr & ~(PAGE_SIZE - 1));
    if (pmmap==0) {
        printf("Memory access error!\n");
        return 0;
    }

    switch (deb_mode) {
    case DEB_BYTE:
        btemp = *(char*) (pmmap + ((long)addr & (PAGE_SIZE-1)));

        printf ("Byte %p: %02X -> ", addr, btemp & 0xFF);

        fgets (scan_buffer, 127, stdin);
        i = sscanf (scan_buffer, "%02X", &dtemp);
        if (i == -1) {
            ret = 0;
            break;
        }
        *(char*) (pmmap + ((long)addr & (PAGE_SIZE-1))) = dtemp;
        addr++;
        break;
    case DEB_WORD:
        dtemp = *(unsigned short*)(pmmap + ((long)addr & (PAGE_SIZE-1)));

        printf ("Dword %p: %04X -> ", addr, dtemp);
        fgets (scan_buffer, 127, stdin);
        i = sscanf (scan_buffer, "%04X", &dtemp);
        if (i == -1) {
            ret = 0;
            break;
        }
        *(unsigned short*)(pmmap + ((long)addr & (PAGE_SIZE-1))) = dtemp;
        addr += 2;
        break;
    case DEB_DWORD:
        dtemp = *(unsigned long*)(pmmap + ((long)addr & (PAGE_SIZE-1)));

        printf ("Dword %p: %08X -> ", addr, dtemp);
        fgets (scan_buffer, 127, stdin);
        i = sscanf (scan_buffer, "%08X", &dtemp);
        if (i == -1) {
            ret = 0;
            break;
        }

        *(unsigned long*)(pmmap + ((long)addr & (PAGE_SIZE-1))) = dtemp;
        addr += 4;
        break;
    } // switch
    munmap(pmmap,PAGE_SIZE);
    /* pass the new address back to the caller */
    *paddr = addr;
    return ret;
}

int
main (int argc, char **argv)
{
  char textline[2560];
  char kbuff[128];
  char *ptext;
  void *deb_addr = &textline[0];
  int f_mem;
  int deb_mode = DEB_BYTE;
  int i;
  int last_cmd = 'd';
  int temp;
  int in_edit = 0;
  int dtemp;
  int ldev;
  int j;
  int btemp;

  printf ("\nNetwinder Low Level Debug Tool V.%s, Woody, Corel Computer 1998.\n", VERSION);

  io_base = IO_BASE_20;
  i=open("/proc/version",O_RDONLY);
  if (i==-1) {
      printf("\n/proc directory is not mounted; assuming Linux 2.0.x");
  } else {
      read(i,textline,256);
      close(i);
      if (strstr(textline,"2.2.")) {
          kernel_version = 22;
          printf("Using Linux 2.2 memory map\n");
          io_base = IO_BASE_22;
      }
  }

  if (getuid() != 0)
    {
      printf ("This program must be run as root!\n\n");
      return EXIT_FAILURE;
    }

  f_mem = open ("/dev/mem", O_RDWR);
  ptext = &textline[0];

  while (1)
    {
      fflush (stdin);
      fprintf (stdout, "\n->");
      fgets (kbuff, 127, stdin);

      for (i = 0; i < 127; i++)
	if (kbuff[i] != 0x20)
	  break;

      if (kbuff[i] == '\n')
	{
	  *(int *) &kbuff[i] = last_cmd;
	}

      switch (kbuff[i])
      {
      case 'e':
      case 'E':
          i++;
          last_cmd = CMD_EDIT;

          if (tolower (kbuff[i]) == 'd')
          {
              deb_mode = DEB_DWORD;
              i++;
          }
          else if (tolower (kbuff[i]) == 'w')
          {
              deb_mode = DEB_WORD;
              i++;
          }
          else if (tolower (kbuff[i]) == 'b')
          {
              deb_mode = DEB_BYTE;
              i++;
          }

          i = sscanf (&kbuff[i], "%p", &deb_addr);

          while(eb((long*)&deb_addr,f_mem,deb_mode,kbuff));
          
          break;  // letter E

      case 'd':
      case 'D':
          i++;
          last_cmd = CMD_DUMP;

          if (tolower (kbuff[i]) == 'd')
	    {
	      deb_mode = DEB_DWORD;
	      i++;
	    }
	  else if (tolower (kbuff[i]) == 'w')
	    {
	      deb_mode = DEB_WORD;
	      i++;
	    }
	  else if (tolower (kbuff[i]) == 'b')
	    {
	      deb_mode = DEB_BYTE;
	      i++;
	    }

	  i = sscanf (&kbuff[i], "%p", &deb_addr);

	  if (i == -1)
	    deb_addr += 160;

          if (deb_addr) {
              db (f_mem, (long)deb_addr, 160, ptext, deb_mode);
              printf(ptext);
          }
	  break;

	case 'i':
	case 'I':
	  i++;
          last_cmd = CMD_INPUT;

          i = sscanf (&kbuff[i], "%p", &deb_addr);

          //          if (i == -1)        //if just i - keep using this port
          //              deb_addr ++;
          {
              MAP_IO(f_mem);

              if (!IS_OK_IO)
                  printf ("Memory access error!\n");
              btemp = inb((long)deb_addr);
              
              UNMAP_IO;
              
              i = sprintf (textline, "Port %04X", (unsigned int) deb_addr & 0xFFFF);
              *(textline + i) = ' ';

              sprintf (&textline[i + 1], " : %02X.\n", (unsigned char) btemp);
              printf (textline);
          }

	  break;

	case 'o':
	case 'O':
	  i++;
	  last_cmd = CMD_OUTPUT;

	  i = sscanf (&kbuff[i], "%04p %02X", &deb_addr, &btemp);

	  if (i != -1)
          {
              MAP_IO(f_mem);

              if (!IS_OK_IO)
                  printf ("Port access error (%04X != %04X)!\n", i & 0xFFFF, (unsigned int)deb_addr);
              else
                  outb((long)deb_addr,btemp);
              UNMAP_IO;
	    }
	  break;

	case 's':
	case 'S':
	  i++;
	  if (kbuff[i] == 'i' || kbuff[i] == 'I')
	    {
	      i++;
	      last_cmd = (int) CMD_SUPER_IN;

	      i = sscanf (&kbuff[i], "%02X %04p", &ldev, &deb_addr);

              MAP_IO(f_mem);
	      UNLOCK;
	      outb (0x370, 0x07);
	      outb (0x371, ldev);
	      outb (0x370, (unsigned int) deb_addr);
              btemp = inb (0x371);
              UNMAP_IO;

	      printf ("Logical dev. %02X, Reg %02X : %02X.\n",
		      ldev, (unsigned int) deb_addr & 0xFFFF, btemp);

	      break;
	    }
	  else if (kbuff[i] == 'o' || kbuff[i] == 'O')
	    {
	      i++;
	      last_cmd = (int) CMD_SUPER_OUT;

	      i = sscanf (&kbuff[i], " %X %04p %02X", &ldev, &deb_addr, &btemp);

	      printf ("Logical dev. %02X, Reg %02X : %02X.\n",
		      ldev, (unsigned int) deb_addr, btemp);

              MAP_IO(f_mem);
	      UNLOCK;
	      outb (0x370, 0x07);
	      outb (0x371, ldev);
	      outb (0x370, (int) deb_addr);
              outb (0x371, (char) btemp);
              UNMAP_IO;

	      break;
	    }
	  else
	    break;


	case 'c':
	case 'C':
	  i++;
	  if (kbuff[i] == 'r' || kbuff[i] == 'R')
	    {
	      i++;
	      last_cmd = (int) CMD_CPLD_READ;

	      ldev = open ("/dev/nwdebug", O_RDONLY);
	      if (ldev < 0)
		{
		  printf ("Error %d opening /dev/nwdebug\n", ldev);
		  break;
		}
	      else
		{
		  ioctl (ldev, 0x5D, &btemp);	//CMD_READ_CPLD

		  if (!errno)
		    printf ("CPLD: 0x%X (DS1620 rst:%s, 7111:%s, Mute:%s, Flash_wrt:N/A).\n",
			    btemp, btemp & 0x08 ? "OFF" : "ON", btemp & 0x04 ? "disabled" : "enabled", btemp & 0x02 ? "OFF" : "ON");
		  close (ldev);
		}
	      break;
	    }
	  else if (kbuff[i] == 'w' || kbuff[i] == 'W')
	    {
	      i++;
	      last_cmd = (int) CMD_CPLD_WRITE;

	      i = sscanf (&kbuff[i], " %02X", &btemp);

	      if (i != -1)
		{
		  btemp &= 0xEF;

		  ldev = open ("/dev/nwdebug", O_RDWR);
		  if (ldev < 0)
		    {
		      printf ("Error %d opening /dev/nwdebug\n", ldev);
		      break;
		    }
		  else
		    {
		      ioctl (ldev, 0x5D, &j);	//CMD_READ_CPLD

		      ioctl (ldev, 0x5E, &btemp);	//CMD_WRITE_CPLD

		      if (!errno)
			{
			  ioctl (ldev, 0x5D, &btemp);	//CMD_READ_CPLD

			  printf ("CPLD: old 0x%X, new 0x%X (DS1620 rst:%s, 7111:%s, Mute:%s, Flash_wrt:N/A).\n",
				  j, btemp, btemp & 0x08 ? "OFF" : "ON", btemp & 0x04 ? "disabled" : "enabled", btemp & 0x02 ? "OFF" : "ON");
			}
		    }
		  close (ldev);
		}
	      break;
	    }
	  else
	    break;


	case 'q':
	case 'Q':
	  close (f_mem);

	  exit (0);
	  break;

	case 'm':
      case 'M':
          if (kernel_version == 20) {
//printf("               ...MEMORY MAP...\n");
	  printf ("                        Debuger\n");
	  printf ("Phys Start  End         Start(2)   PCI Start(1) Contents\n");
	  printf ("\n");
	  printf ("0000.0000   01FF.FFFF   0                       32 meg SDRAM\n");
	  printf ("4000.0000   4000.FFFF   FFF0.xxxx               DRAM mode registers\n");
	  printf ("4100.0000   413F.FFFF   E18x.xxxx               1-4 meg flash ROM\n");
	  printf ("4200.0000   420F.FFFF   E10x.xxxx               CSR\n");
	  printf ("5000.0000   5000.0FFF   E130.0xxx               StrongARM cache flush\n");
	  printf ("7800.0000   7800.0FFF   E120.0xxx               PCI write flush\n");
	  printf ("7900.0000   7900.0003   E110.0000               PCI IACK address\n");
	  printf ("7B00.0000   7BFF.FFFF   E0nn.xxxx               PCI config\n");
	  printf ("            VGA:E018..Ether100:E020,..SCSI:E030..IDE:E050..Ether10:E090..\n");
	  printf ("7C00.0000   7C00.0FFF   E000.0xxx               PCI I/O space\n");
	  printf ("7C00.xxxx               D080.xxxx               VGA config regs\n");
	  printf ("8000.0000   80FF.FFFF   D0xx.xxxx   08xx.xxxx   VGA\n");
//	  printf ("9000.0000   9000.FFFF   D100.xxxx   1000.xxxx   modem codec (optional)\n");
	  printf ("A000.0000   A000.00FF   E140.00xx   2000.00xx   Ethernet 100\n");
	  printf ("A001.0000   A001.00FF   E141.00xx   2001.00xx   SCSI\n");
	  printf ("\n");
	  printf ("(1) PCI bus masters see these addresses.\n");
	  printf ("(2) These are the addresses used by Linux code, after the memory management\n");
	  printf ("unit has been enabled. Note that the SDRAM is owned by the Linux kernel and\n");
	  printf ("is managed at address C000.0000. When memory is dished out to user code, it\n");
          printf ("appears duplicated at 0-BFFF.FFFF in the user space.\n");
          } else {
	  printf ("Debugger Addrs          Kernel\n");
	  printf ("Start       End         Addr(2)    PCI Start(1) Contents\n");
	  printf ("\n");
	  printf ("0000.0000   01FF.FFFF   C0xx.xxxx               32 meg SDRAM\n");
	  printf ("4000.0000   4000.FFFF                           DRAM mode registers\n");
	  printf ("4100.0000   413F.FFFF   D8xx.xxxx               1-4 meg flash ROM\n");
	  printf ("4200.0000   420F.FFFF   FE0x.xxxx               CSR\n");
	  printf ("5000.0000   5000.0FFF   DF00.0xxx               StrongARM cache flush\n");
	  printf ("7800.0000   7800.0FFF   FD00.0xxx               PCI write flush\n");
	  printf ("7900.0000   7900.0003   FC00.0000               PCI IACK address\n");
	  printf ("7B00.0000   7BFF.FFFF   F8nn.xxxx               PCI config\n");
	  printf ("            VGA:7B08..Ether100:7B10,..SCSI:7B20..IDE:7B40..Ether10:7B80..\n");
	  printf ("7C00.0000   7C00.0FFF   FF00.0xxx               PCI I/O space\n");
	  printf ("7C00.xxxx               D080.xxxx               VGA config regs\n");
	  printf ("8100.0000   81FF.FFFF   E1xx.xxxx   01xx.xxxx   VGA\n");
	  printf ("8080.0000   8080.00FF   E080.00xx   0080.00xx   Ethernet 100\n");
	  printf ("80A0.0000   80A0.1FFF   E0A0.xxxx   00A0.00xx   SCSI\n");
	  printf ("\n");
	  printf ("(1) PCI bus masters see these addresses.\n");
	  printf ("(2) These are the addresses used by Linux code, after the memory management\n");
	  printf ("unit has been enabled. Note that the SDRAM is owned by the Linux kernel and\n");
	  printf ("is managed at address C000.0000. When memory is dished out to user code, it\n");
          printf ("appears duplicated at 0-BFFF.FFFF in the user space.\n");
          }
              

	  break;


	case '?':
	case 'h':
	case 'H':
	  printf ("Debug commands (all values in hex):\n");
	  printf ("dd addr          - dump dword\n");
	  printf ("dw addr          - dump word\n");
	  printf ("db addr          - dump byte\n");
	  printf ("ed addr          - edit dword\n");
	  printf ("ew addr          - edit word\n");
	  printf ("eb addr          - edit byte\n");
	  printf ("i  addr          - in port (byte)\n");
	  printf ("o  addr val      - out port (byte)\n");
	  printf ("si ldev reg      - superIO ldev reg in (byte)\n");
	  printf ("so ldev reg val  - superIO ldev reg out (byte)\n");
	  printf ("cr               - read CPLD (3 bits)\n");
	  printf ("cw val           - write CPLD (3 bits)\n");
	  printf ("m                - display memory map\n");

	  break;


	default:
	  break;
	}
    }

}

