/* parspi
 * Reads an ADNS-2050/ADNS-2051 sensor attached to the parallel port.
 * January 8, 2006
 * Joshua Wise <joshua@joshuawise.com>
 *
 * Copyright (c) 2006 Joshua Wise.
 *
 * You are free to copy this source verbatim or modified, as long as this
 * copyright statement is preserved. You are also free to copy any binaries
 * produced, so long as the source accompanies them.
 *
 */

#include <stdio.h>
#include <fcntl.h>
#include <linux/ioctl.h>
#include <linux/ppdev.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#define  XK_MISCELLANY
#include <X11/keysymdef.h>
#include <stdlib.h>

int pfd;
Display *mydisplay;
Window mywindow;
GC mygc;
XEvent myevent;
XSizeHints myhint;
XWMHints mywmhint;
int myscreen;
unsigned long colf,colb;

void openport()
{
  pfd = open("/dev/parport0", O_RDWR);
  if (pfd < 0)
  {
    perror("open(parport0)");
    exit(1);
  }
  if ((ioctl(pfd, PPEXCL) < 0) || (ioctl(pfd, PPCLAIM) < 0))
  {
    perror("lock parport0");
    close(pfd);
    exit(1);
  }
}

void relport()
{
  ioctl(pfd, PPRELEASE);
  close(pfd);
}

void vusleep(int i)
{
  struct timespec t;
  t.tv_sec = 0;
  t.tv_nsec = i * 1000;
}

int dclock(int i)
{
  unsigned char data = (i)?8:0;
  if (ioctl(pfd, PPWDATA, &data))
  {
    perror("PPWDATA");
    relport();
    exit(1);
  }
  data |= 0x80;
  vusleep(10);
  if (ioctl(pfd, PPWDATA, &data))
  {
    perror("PPWDATA");
    relport();
    exit(1);
  }
  vusleep(01);
  if (ioctl(pfd, PPRSTATUS, &data) < 0)
  {
    perror("PPRSTATUS");
    relport();
    exit(1);
  }
  vusleep(10);
  return (data & 0x20) ? 1 : 0;
}

void send(unsigned char a,unsigned char d)
{
  int i;
  a |= 0x80;
  
  for (i=7; i>=0; i--)
    dclock((a>>i) & 0x1);
  for (i=7; i>=0; i--)
    dclock((d>>i) & 0x1);
}

unsigned char recv(unsigned char a)
{
  int i;
  unsigned char d;
  a = a & 0x7F;
  for (i=7; i>=0; i--)
    dclock((a>>i) & 0x1);
  d=0;
  for (i=7; i>=0; i--)
    d |= dclock(1) << i;
  return d;
}

unsigned char recvinv(unsigned char a)
{
  int i;
  unsigned char d;
  a = a & 0x7F;
  a=~a;
  for (i=7; i>=0; i--)
    dclock((a>>i) & 0x1);
  d=0;
  for (i=7; i>=0; i--)
    d |= dclock(1) << i;
  return d;
}

void initX();
void setstat(int,int,int);


int main()
{
  int i; unsigned char d;
  int a;

  openport();
reset:
  send(0x0A /* CONFIG */, 0x80);
  send(0x0A, 0x01);

  d = 0xFF;
  i=0;
  while (d != 0x02)
  {
    d = recv(0x00);
    if (i > 16384)
    {
      printf("Timeout while waiting for device to come out of reset\n");
      relport();
      exit(1);
    }
    i++;
  }

  printf("Register dump:\n");
  for (a=0; a<0x80; a++)
  {
    printf("%02x ",recv(a));
    if ((a % 16) == 15)
      printf("\n");
  }
  
  send(0x0A /* CONFIG */, 0x08 | 0x01);
  recv(0x0A);
  initX();

  while (1)
  {
    unsigned char arr[256];
    int a,d;
    
    i=0;
    a=recv(0x0C);
    d=recv(0x0D);
    while (d & 0x80)
    {
      d = recv(0x0C);
      if (i > 2000)
      {
        printf("Timeout while waiting for data validity; resetting chip\n");
        goto reset;
      }
      i++;
    }
    arr[a] = d;
    usleep(200);
    
    if (a == 0xFF)
    {
      int j;
      for (i=0; i<0xFF; i+=16)
        for (j=0;j<16;j++)
          setstat(i >> 4, j, arr[i+j]);
      XFlush(mydisplay);
    }
  }
  exit(0);
}

/* X stuff */
const char title[] = {"ADNS-2050 Dump"};

#define SCALE 16

void initX()
{
  mydisplay = XOpenDisplay("");
  if(mydisplay==NULL) {fprintf(stderr,"\033[2J\033[HCan't connect to %s\n",
                      XDisplayName("")); getchar(); return;}
  myscreen = DefaultScreen(mydisplay);
  colf = 0xFFFFFF;
  colb = BlackPixel(mydisplay,myscreen);
  myhint.x=200;myhint.y=0;
  myhint.width=16*SCALE+1;myhint.height=16*SCALE+1;
  myhint.flags=PPosition|PSize;
  mywmhint.flags=InputHint;
  mywmhint.input=True;

  mywindow=XCreateSimpleWindow(mydisplay,
      DefaultRootWindow(mydisplay),
      myhint.x,myhint.y,myhint.width,myhint.height,
      5,colf,colb);
  XSetStandardProperties(mydisplay,mywindow,title,title,
      None,NULL,0,&myhint);
  XSetWMHints(mydisplay,mywindow,&mywmhint);

  mygc=XCreateGC(mydisplay,mywindow,0,0);
  XSetBackground(mydisplay,mygc,colb);
  XSetForeground(mydisplay,mygc,colf);

  XSelectInput(mydisplay,mywindow,
      ButtonPressMask|KeyPressMask|KeyReleaseMask|ExposureMask);

  XMapRaised(mydisplay,mywindow);
  //cmap=DefaultColormap(mydisplay,myscreen);

  XClearWindow(mydisplay,mywindow);
  XFlush(mydisplay);
}

void setstat(int x, int y, int c)
{
  int color = (c*4) << 16 | (c*4) << 8 | (c*4);
  XSetForeground(mydisplay, mygc, color);
  XFillRectangle(mydisplay, mywindow, mygc, x*SCALE, y*SCALE, SCALE, SCALE);
}

