/*
    Device driver for Diamond A/D converter
    Copyright Kristian Mueller 2005
*/

#include <kernel.h>
#include "drv_io.h"
#include <sys/file.h>		// fuer open (braucht filepointer)
#include <dldd.h>		// zum dynamischen Laden des Treibers
#include "ad_converterinfo.h"	// device information structure

#define MINOR_DEVS      2
#define WAITCYCLE       2

// STATIC
struct ad_converterstatics {
	long iPortBase;		// Base address of the A/D-C. Board
	int iDMALevel;		// indivates the DMA level - 3 would have lowest priority
	int iADGainSetting;	// the gain [1=User; 2=x10; 3=x5; 4=x2; 5=x1; 6=x0.5] 6 is default
	unsigned char bOpened;	// indactes the current use of the device
	unsigned char bConverting;	// indactes the current use the converter
	unsigned char bADInit;	// is the A/D converter initialized?

	long ctrl_port;
	u_int8_t pwm[MINOR_DEVS];
	u_int8_t ctrl[MINOR_DEVS];
};

// EXTERNS
extern char *sysbrk(long);
extern void sysfree(char *, long);
extern int kkprintf(char *, ...);
extern void bcopy(const char *, char *, int);

// ENTRY POINTS (Declaration)                                                
char *ad_converterinstall(struct ad_converterinfo *info);
int ad_converteruninstall(struct ad_converterstatics *s);
int ad_converteropen(struct ad_converterstatics *s, int devno,
		     struct file *f);
int ad_converterclose(struct ad_converterstatics *s, struct file *f);
int ad_converterwrite(struct ad_converterstatics *s, struct file *f,
		      char *buf, int count);
int ad_converterread(struct ad_converterstatics *s, struct file *f,
		     char *buf, int count);
int ad_converterioctl(struct ad_converterstatics *s, struct file *f,
		      int comand, char *arg);
int ad_converterselect(struct ad_converterstatics *s, struct file *f,
		       int which, struct sel *ffs);

// DLDD Structure
// using static here would create an statical loadable x86 driver
struct dldd entry_points = {
	ad_converteropen,
	ad_converterclose,
	ad_converterread,
	ad_converterwrite,
	ad_converterselect,
	ad_converterioctl,
	ad_converterinstall,
	ad_converteruninstall,
	(char *) 0
};

// ENTRY POINTS
char *ad_converterinstall(struct ad_converterinfo *aInfo)
{
	struct ad_converterstatics *aStatics;

	kkprintf("Installing the ADC driver - init the static data!\n");

	// create the static structure
	aStatics =
	    (struct ad_converterstatics *) sysbrk((long)
						  sizeof(*aStatics));
	aStatics->bADInit = 0;

/*
  OUTB(info->port_base+PAR_DATA,PORTTEST)
  INB(info->port_base+PAR_DATA,ch )
    return (char*) SYSERR;

  // Statics Structure initialisieren
  //  bzero(s, sizeof(*s));
  s->data_port = info->port_base + PAR_DATA;
  s->ctrl_port = info->port_base + PAR_CONTROL;
  s->opened[MOT_1]=FALSE;
  s->opened[MOT_2]=FALSE;
  s->pwm[MOT_1] =0x10;
  s->pwm[MOT_2] =0x10;
  s->ctrl[MOT_1]=PWM_DEFAULT;
  s->ctrl[MOT_2]=PWM_DEFAULT;
*/

	return ((char *) aStatics);
}

// UNINSTALL
int ad_converteruninstall(struct ad_converterstatics *aStatics)
{
	kkprintf
	    ("Uninstalling the ADC driver - freeing the static data.\n");

	sysfree((char *) aStatics, (long) sizeof(*aStatics));

	return OK;
}

// OPEN
int ad_converteropen(struct ad_converterstatics *aStatics, int iDevNumber,
		     struct file *aFile)
{
	kkprintf("Opened ADC\n");

	return OK;
}

// CLOSE
int ad_converterclose(struct ad_converterstatics *aStatics,
		      struct file *aFile)
{
	kkprintf("Closed ADC\n");

	return OK;
}

// WRITE
int ad_converterwrite(struct ad_converterstatics *aStatics,
		      struct file *aFile, char *sBuffer, int iLength)
{
	kkprintf("Writing to ADC\n");

	return OK;
}

// READ
int ad_converterread(struct ad_converterstatics *aStatics,
		     struct file *aFile, char *sBuffer, int iLenght)
{
	int iPoll = 1;
	unsigned char iReadValue = 0x00;
	int iADCValue = 0;
	unsigned short iMSB = 0;
	unsigned short iLSB = 0;

//    kkprintf("Reading from ADC\n");    

	if (iLenght < 2)
		kkprintf("Need 2 Bytes to set data there!");
	else {
		OUTB(START_CONVERSION, 0xFF);
		while (iPoll == 1) {
			INB(STATUS_REGISTER, iReadValue);
			if ((iReadValue & 0x80) == 0x80)
				iPoll = 0;
		}
		INB(AD_MSB, iMSB);
		INB(AD_LSB, iLSB);
		iMSB = iMSB / 16;
		iLSB = iLSB * 16;
		iADCValue = iMSB + iLSB;

		sBuffer[0] = 0xff & ((unsigned char) (iADCValue >> 8));
		sBuffer[1] = 0xff & ((unsigned char) iADCValue);

	}
	return OK;
}

// IOCTL
int ad_converterioctl(struct ad_converterstatics *aStatics,
		      struct file *aFile, int iCommand, char *sArgument)
{
	int iWait;

	kkprintf("ADC IOCTL was called - cmd was <%d> :-)\n", iCommand);

	switch (iCommand) {
	case 0:
		OUTB(DIGITAL_OUTPUT_PORT, 0xAA);
		break;
	case 1:
		OUTB(DIGITAL_OUTPUT_PORT, 0x55);
		break;
	case CMD_CLEAR_DOUT:
		OUTB(DIGITAL_OUTPUT_PORT, 0x00);
		break;
	case CMD_SET_DOUT:
		OUTB(DIGITAL_OUTPUT_PORT, sArgument[0]);
		break;
	case CMD_INIT_AD1:
		// should init the first input of the lower chanle to be input for read
		OUTB(DIGITAL_OUTPUT_PORT, 0x01);
		// wait 10us
		for (iWait = 0; iWait < 10000; iWait++);
		aStatics->bADInit = 1;
		break;
	case CMD_START_CONVERSATION:
		if (aStatics->bADInit != 1) {
			kkprintf
			    ("start of A/D conversion requested although A/D was not initialized!");
		} else {
			OUTB(START_CONVERSION, 0xFF);
		}
		break;
	}
	return OK;
}

// SELECT
int ad_converterselect(struct ad_converterstatics *aStatics,
		       struct file *aFile, int iTarget,
		       struct sel *aSelectedFFS)
{
	kkprintf("ADC select was called... :-)\n");

	return OK;
}

