#include <stdio.h>
#include <stdlib.h>
#include <asm/io.h>
#include <termios.h>

#define BASEPORT 0x378

//    INTERFACING AN ADC976 16bit AD converter
//    (see www.analog.com for datasheet)
//    (C) 2003 http://magnetometer.captain.at
//    feel free to use and/or modify this code anywhere, anytime,
//    as long as this header remains unchanged.
//    BE CAREFUL WHEN PLAYING AROUND WITH YOUR PARALLEL PORT
//    BETTER USE AN EXTRA I/O CARD! Replacing a single card 
//    is much cheaper than a new motherboard!

// PARALLEL PORT PINS USED:
// STROBE:     pin 1  ; bit 0   CONTROL REGISTER BASEPORT+2
// AUTOFEED:   pin 14 ; bit 1   CONTROL REGISTER BASEPORT+2
// (ACK:       pin 10 ; bit 6   STATUS  REGISTER BASEPORT+1; not used here)
// PORT:       pin 2-9; bit 0-7 DATA    REGISTER BASEPORT
// STROBE & AUTOFEED  is hardware inverted
//
// CONTROL REGISTER:
// I/O: bit 5 in BASEPORT+2 set to HIGH means the port is in input mode

// compile on linux with i.e. gcc -O2 -o adc adc.c
// this example reads the ADC until a key on the keyboard is pressed

int main(int argc, char **argv) {

	if (ioperm(BASEPORT, 3, 1)) {  	
			// ask for permissions to access the parallel port
			// YOU MUST BE ROOT TO ASK FOR THIS SORT OF PERMISSION, so
			//     don't forget to 'su' yourself ;-)
			// we don't want to interfere with other code
			// usage of ioperm: BASEPORT ... base address we want to use
			//		3 ... the next 3 bytes
			//		1 ... we want the permission
		perror("ioperm open");
		exit(1);
	}

	makeinput();  // set the port to input mode
	
	int res = 0;
	int i = 0;
	
	while (1) {
		// start conversion
		// sets STROBE to HIGH, pin 1 = LOW
		outb(33,BASEPORT+2);
		
		usleep(2000); // wait for conversion
		
		// switch to read-mode
		// sets STROBE to LOW, pin1 to HIGH; STROBE = pin 1, bit0 in BASEPORT+2
		outb(32, BASEPORT + 2);
		
		usleep(2000); // wait
		
		int high =  inb(BASEPORT); //read low byte
		
		// switch to high byte -> AUTOFEED = pin 14, bit 1 in BASEPORT+2
		outb(34, BASEPORT + 2);
		
		usleep(2000); // wait for high byte
		
		int low = inb(BASEPORT); // read high byte
		
		res = high*256 + low; // calculate the 16 bit value
		printf("high: %d low: %d  res=%d\n", high, low,res);

		if (kbhit()) {
			break;
		}
	}		

	freeperm(); // tell the system we do not use the port anymore

	exit(0);
}

int freeperm() {
	if (ioperm(BASEPORT, 3, 0)) { 
		// tell the system we do not use the port anymore
		// usage of ioperm: BASEPORT ... base address we want to use
		//					3 ... the next 3 bytes
		//					0 ... free the permission	
		perror("ioperm close");
	}
}

int makeinput() {  // sets the port to input mode & checks if it was successful

	outb(33, BASEPORT + 2); // set to input mode and make STROBE high 
	
	// we double check if the port is really on input mode
	// some older systems might not support bidirectional mode
	// at least this is what I have read in some docs
	outb(147, BASEPORT);  
	if (inb(BASEPORT) == 147) { 
		printf("your system doesn't support bi-directional mode: err%d\n", 147);
		exit(1);
	}
	
	outb(201, BASEPORT);
	if (inb(BASEPORT) == 201) {
		printf("your system doesn't support bi-directional mode: err%d\n", 201);
		exit(1);
	}
	
	return 1;
}

int kbhit(void) { // check if a key on the keyboard is pressed
	struct termios term, oterm;
	int fd = 0;
	int c = 0;
	tcgetattr(fd, &oterm);
	memcpy(&term, &oterm, sizeof(term));
	term.c_lflag = term.c_lflag & (!ICANON);
	term.c_cc[VMIN] = 0;
	term.c_cc[VTIME] = 1;
	tcsetattr(fd, TCSANOW, &term);
	c=getchar();
	tcsetattr(fd, TCSANOW, &oterm);
	if (c != -1) ungetc(c, stdin);
	return ((c!=-1)?1:0);
}

