Index: EPRoxygen/Makefile
===================================================================
--- EPRoxygen/Makefile	(revision d09030d6371a65f7116c4fa868b68a815a092daa)
+++ EPRoxygen/Makefile	(revision 34c57e88ff8ccc137eced9ad94274c9233a19d06)
@@ -1,5 +1,5 @@
 eproxygen: main.o at29c040.o
-	g++ $^ -o $@
+	g++ -Wall -g -O3 -fno-rtti -fno-exceptions $^ -o $@
 
 %.o: %.cpp
-	g++ -c $^ -o $@
+	g++ -Wall -g -save-temps -O3 -fno-rtti -fno-exceptions -c $^ -o $@
Index: EPRoxygen/at29c040.cpp
===================================================================
--- EPRoxygen/at29c040.cpp	(revision d09030d6371a65f7116c4fa868b68a815a092daa)
+++ EPRoxygen/at29c040.cpp	(revision 34c57e88ff8ccc137eced9ad94274c9233a19d06)
@@ -1,13 +1,107 @@
 #include "at29c040.h"
+
+#include <stdio.h>
+#include <unistd.h>
+
+
+AT29C040::AT29C040()
+	: Device()
+{
+}
+
+void AT29C040::power()
+{
+	Device::write(CTRL, 0b10011111 | CTRLmask);
+	Device::write(VOLT, 0b11001000 | VOLTmask);
+}
+
+void Device::shutdown()
+{
+	// All pins of the ROM are put either in high-Z, or 0V when it's not
+	// possible
+	Device::write(CTRL, 0x62);
+	Device::write(VOLT, 0x7F);
+	outb(port + 2, NONE);
+}
 
 void AT29C040::read(const char* filename)
 {
+	if (!checkId()) {
+		return;
+	}
 }
 
 void AT29C040::write(const char* filename)
 {
+	if (!checkId()) {
+		return;
+	}
 }
 
 void AT29C040::erase(void)
 {
+	if (!checkId()) {
+		return;
+	}
+
+	puts("Perfomring chip erase...");
 }
+
+bool AT29C040::checkId(void)
+{
+	// Chip enable
+	Device::write(CTRL, CTRLmask | CE);
+		// Address bits low, WE = 1, CE = 0, OE = 1
+		// TC and TD = 1
+	Device::write(VOLT, VOLTmask);
+		// D and A OE = 0
+		// VPP config ?
+		// TA, TB, TE, TF = ?
+
+	wr16(0x5555, 0xAA);
+	wr16(0x2AAA, 0x55);
+	wr16(0x5555, 0x90);
+
+	// clear DOE for reading
+	Device::write(VOLT, DOE | VOLTmask);
+	
+	usleep(10000);
+
+	// Read the result now
+	Device::write(ADR0, 0x00);
+	Device::write(ADR1, 0x00);
+	Device::write(CTRL, WE | CE | CTRLmask);
+	uint8_t manufacturer = Device::read();
+
+	getchar();
+
+	Device::write(CTRL, OE | WE | CE | CTRLmask);
+	Device::write(ADR0, 0x01);
+	Device::write(CTRL, WE | CE | CTRLmask);
+
+	uint8_t product = Device::read();
+
+	// re enable DOE, we are going to write again...
+	Device::write(CTRL, WE | OE | CE | CTRLmask);
+	Device::write(VOLT, VOLTmask);
+
+	wr16(0x5555, 0xAA);
+	wr16(0x2AAA, 0x55);
+	wr16(0x5555, 0xF0);
+	usleep(10000);
+
+	// Chip disable - we're done !
+	Device::write(CTRL, 0xFF | CTRLmask);
+	
+	bool ok = (manufacturer == 0x1F && product == 0xA4);
+
+	if (!ok)
+	{
+		fprintf(stderr, "Chip ID %X:%X does not match expected %X:%X\n",
+			manufacturer, product, 0x1F, 0xA4);
+	} else {
+		fprintf(stderr, "AT29C040 detected successfully!\n");
+	}
+
+	return ok;
+}
Index: EPRoxygen/at29c040.h
===================================================================
--- EPRoxygen/at29c040.h	(revision d09030d6371a65f7116c4fa868b68a815a092daa)
+++ EPRoxygen/at29c040.h	(revision 34c57e88ff8ccc137eced9ad94274c9233a19d06)
@@ -4,7 +4,29 @@
 {
 	public:
+		AT29C040();
+
+		void power(void);
+		void shutdown(void);
 		void read(const char* filename);
 		void write(const char* filename);
 		void erase(void);
+	private:
+		bool checkId(void);
+
+		// Write a byte to a 16-bit address (bits 16 17 18 are 0)
+		inline void wr16(uint16_t addr, uint8_t val)
+		{
+			Device::write(ADR0, addr);
+			Device::write(ADR1, addr >> 8);
+
+			Device::write(CTRL, OE | CTRLmask); 
+
+			Device::write(DATA, val);
+
+			Device::write(CTRL, WE | OE | CTRLmask);
+		}
+
+		static const uint8_t CTRLmask = 0b01100000;
+		static const uint8_t VOLTmask = 0b10110011;
 };
 
Index: EPRoxygen/device.h
===================================================================
--- EPRoxygen/device.h	(revision d09030d6371a65f7116c4fa868b68a815a092daa)
+++ EPRoxygen/device.h	(revision 34c57e88ff8ccc137eced9ad94274c9233a19d06)
@@ -4,9 +4,120 @@
 #pragma once
 
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+
+// FreeBSD
+#include <sys/types.h>
+#include <machine/cpufunc.h>
+#include <machine/sysarch.h>
+
+
 class Device {
 	public:
+		Device() 
+		{};
+
+		void shutdown();
+		virtual void power() = 0;
+
 		virtual void read(const char* file) = 0;
 		virtual void write(const char* file) = 0;
 		virtual void erase(void) = 0;
+
+		int setup(const int port_base) {
+			port = port_base;
+			if (i386_set_ioperm(port_base, 3, true) != 0)
+			{
+				perror("Unable to get access to parallel port");
+				return errno;
+			}
+			shutdown();
+
+			return 0;
+		}
+
+		virtual ~Device() {};
+	
+	protected:
+		typedef enum {
+			NONE =  0b0101, /* Nothing selected */
+			NYBLE = 0b0010, /* Read low nyble */
+				/* When 0, status reg shows D0.3
+				 * When 1, status reg shows D4.7
+				 */
+			CTRL =  0b0011, /* Control reg. */
+				/* b0: A16
+				 * b1: /A17
+				 * b2: WE
+				 * b3: A18
+				 * b4: OE
+				 * b5: VPP on pin 25 (0 = enable)
+				 * b6: VPP on pin 24 (0 = enable)
+				 * b7: CE
+				 */
+			VOLT =  0b0110, /* Power config reg. */
+				/* b0: VPP on pin 1 (0 = enable)
+				 * b1: VPP on pin 3 (0 = enable)
+				 * b2: VCC on pin 32 (0 = enable)
+				 * b3: Data reg. output enable (and green led)
+				 * b4: VPP config
+				 * b5: VPP config
+				 * 		00 = 24V
+				 * 		01 = ??
+				 * 		10 = ??
+				 * 		11 = 12V
+				 * b6: Address reg. output enable
+				 * b7: VCC enable (1 = power ON)
+				 */
+			ADR0 =  0b1011, /* Low address byte A0.7 */
+			DATA =  0b1110, /* Data byte */
+			ADR1 =  0b1111, /* High address byte A8.15 */
+		} Register;
+
+		typedef enum {
+			A16 = 1,
+			A17 = 2,
+			WE = 4,
+			A18 = 8,
+			OE = 16,
+			VPP25 = 32,
+			VPP24 = 64,
+			CE = 128
+		} CTRLbits;
+
+		typedef enum {
+			VPP1 = 1,
+			VPP3 = 2,
+			VCC32 = 4,
+			DOE = 8,
+
+			V24 = 0,
+			V12 = 48,
+
+			AOE = 64,
+			VCC30 = 128
+		} VOLTbits;
+
+		static inline void write(const Register reg, const uint8_t val)
+		{
+			// The register latches the data only when another register gets
+			// selected. If this is a problem, select the NONE reg after
+			// writing !
+			outb(port + 2, reg);
+			outb(port, val);
+		}
+
+		static inline uint8_t read()
+		{
+			uint8_t byte = (inb(port + 1) ^ 0x80) & 0xF0;
+
+			outb(port+2, NYBLE); 
+			byte |= (inb(port + 1) ^ 0x80) >> 4;
+			return byte;
+		}
+
+	private:
+		static int port;
 };
 
Index: EPRoxygen/main.cpp
===================================================================
--- EPRoxygen/main.cpp	(revision d09030d6371a65f7116c4fa868b68a815a092daa)
+++ EPRoxygen/main.cpp	(revision 34c57e88ff8ccc137eced9ad94274c9233a19d06)
@@ -9,26 +9,16 @@
 #include <getopt.h>
 
-// FreeBSD
-#include <osreldate.h>
-#include <sys/types.h>
-#include <machine/cpufunc.h>
-#include <machine/sysarch.h>
-
 #include "device.h"
 #include "at29c040.h"
 
-/* TODO
-Linux compatibility (ioperm, reversed outb)
-wrap IOs in a class to hide the mess (BSD/Linux)
-wrap ROM access in a class as well (read or write a byte/block, and so on)
-*/
 
 int main(int argc, char* argv[])
 {
 	char c;
-	const char* file;
+	const char* file = NULL;
 	char opcode = 0;
 	int port_base = 0x378;
 	Device* device = NULL;
+	int error = 0;
 
 	puts("EPRoxygen - UNIX driver for SEEIT EPR-02 ROM programmer\n"
@@ -37,5 +27,5 @@
 	);
 
-	while(c = getopt(argc, argv, "p:d:r:w:e"))
+	while((c = getopt(argc, argv, "p:d:r:w:e")) >= 0)
 	{
 		switch(c)
@@ -45,5 +35,6 @@
 				fprintf(stderr, "Unrecognized option -%c\n", optopt);
 				// TODO print help
-				exit(-1);
+				error = -1;
+				goto abort;
 			case 'p':
 				// Set port
@@ -51,5 +42,6 @@
 				{
 					fprintf(stderr, "Invalid I/O port address\n");
-					exit(-4);
+					error = -4;
+					goto abort;
 				}
 				break;
@@ -59,5 +51,6 @@
 				{
 					fprintf(stderr,"Unknown device (only AT29C040 is supported)\n");
-					exit(-3);
+					error = -3;
+					goto abort;
 				}
 				device = new AT29C040();
@@ -73,5 +66,6 @@
 				{
 					fprintf(stderr, "Multiple operations requested (only one of r,w,e is allowed)\n");
-					exit(-2);
+					error = -2;
+					goto abort;
 				}
 				opcode = c;
@@ -82,12 +76,27 @@
 	{
 		fprintf(stderr, "No operation specified (one of r,w,e). Aborting.\n");
-		exit(-5);
+		error = -5;
+		goto abort;
 	}
 
-	i386_set_ioperm(port_base, 3, true);
+	if (device == NULL)
+	{
+		fprintf(stderr, "No device specified.\n");
+		error = -6;
+		goto abort;
+	}
 
 	// Configure voltage and powerpins according to device
+	if(device->setup(port_base) != 0)
+	{
+		error = -7;
+		goto abort;
+	}
 
-	// Ask user to put ROM on socket
+	// TODO Ask user to put ROM on socket
+	puts("Now insert chip into socket, then press any key...");
+	getchar();
+
+	device->power();
 
 	// Do action (program/erase/write)	
@@ -103,9 +112,15 @@
 			device->erase();
 			break;
+		default:
+			fprintf(stderr, "!!? invalid operation ?!\n");
 	}
 
-	exit(0);
+abort:
+	device->shutdown();
+	delete device;
+
+	exit(error);
 }
 
+int Device::port;
 
-
