+#include "lptgpib.h"
+
+#include <sys/io.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <sched.h>
+#include <sys/mman.h>
+#include <time.h>
+
+#define DEBUG 3 /* 4 - all, 3 - data, */
+
+#if DEBUG > 3
+ #define DBG_low(...) { fprintf(stderr,__VA_ARGS__); fflush(stderr); }
+#else
+ #define DBG_low(...)
+#endif
+
+#if DEBUG > 2
+ #define DBG(...) { fprintf(stderr,__VA_ARGS__); fflush(stderr); }
+#else
+ #define DBG(...)
+#endif
+
+int lpt_base;
+/*
+ * GPIB uses negative logic,
+ * LPT control lines (except 0x04) are inverted in hw,
+ */
+#define get_data() (~inb(lpt_base))
+#define put_data(x) outb(~(x), lpt_base)
+#define get_control() (inb(lpt_base+2) ^ 0x04)
+#define put_control(x) outb((x) ^ 0x04, lpt_base+2)
+#define TRI 0x20 /* tristate data lines */
+#define IRQ 0x10 /* enable irq */
+
+/* these depend on wiring */
+#define EOI 0x80 /* LPT 8th data bit */
+#define DAV 0x01 /* LPT pin 1 */
+#define NRFD 0x02 /* LPT pin 14 */
+#define NDAC 0x04 /* LPT pin 16 */
+#define ATN 0x08 /* LPT pin 17 */
+
+struct timespec sleeptime = {0, 10000}; /* 10 us*/
+#define BUSYWAIT(cond) while(cond) nanosleep(&sleeptime, NULL);
+
+struct {int val; char *desc; int len;} cmds[] = {
+ {GTL, "Go to local"},
+ {SDC, "Selected device clear"},
+ {PPC, "Parallel poll configure"},
+ {GET, "Group execute trigger"},
+ {TCT, "Take control"},
+ {LLO, "Local lockout"},
+ {DCL, "Device clear"},
+ {PPU, "Parallel poll unconfigure"},
+ {SPE, "Serial poll enable"},
+ {SPD, "Serial poll disable"},
+ {MLA, "My primary listen address", 30},
+ {UNL, "Unlisten"},
+ {MTA, "My primary talk address", 30},
+ {UNT, "Untalk"},
+ {MSA, "My secondary address", 30},
+ {PPE, "Parallel poll enable"},
+ {PPD, "Parallel poll disable"},
+ {0, "Unknown command"},
+};
+
+void lptgpib_print_command(unsigned char val){
+ int i = 0;
+ while (cmds[i].val && !(cmds[i].val <= val &&
+ val <= cmds[i].val+cmds[i].len) ) i++;
+ char *desc = cmds[i].desc;
+ fprintf(stderr,"CMD: %02hx (%s)\n", val, desc);
+}
+
+void sig_handler(int sig){
+ DBG("Caught ^C\n");
+ put_control(TRI);
+ exit(0);
+}
+
+void lptgpib_init(int base){
+ lpt_base = base;
+ ioperm(lpt_base, 4, 1);
+
+ /* set realtime priority and lock us in memory */
+ struct sched_param scp;
+ memset(&scp, 0, sizeof(scp));
+ scp.sched_priority = sched_get_priority_max(SCHED_RR);
+ sched_setscheduler(0, SCHED_RR, &scp);
+
+ mlockall(MCL_FUTURE);
+
+ /*
+ * assert NRFD?
+ * this way everything waits for us
+ * otherwise we act like we are not there
+ */
+ put_control(TRI | NDAC | NRFD );
+
+ signal(SIGINT, sig_handler);
+}
+
+char lptgpib_read_byte(char *_flags){
+ DBG_low("READ: "); /* previous state: TRI | NDAC | NRFD */
+ put_control(TRI | NDAC); /* clear NRFD */
+ BUSYWAIT( !(get_control() & DAV) ) /* wait for DAV */
+
+ DBG_low("dav, ")
+ char value = get_data(); /* read data */
+ char flags = get_control();
+ put_control(TRI | NRFD ); /* clear NDAC and set NRFD back */
+ BUSYWAIT( get_control() & DAV )/* wait for end of DAV */
+
+ DBG_low("done ");
+ put_control(TRI | NDAC | NRFD ); /* back to default state */
+
+ flags = (flags & ATN) | (value & EOI);
+ value = value & (~EOI);
+
+ DBG_low( isprint(value) ? "'%c' %s %s\n" : "'\\x%02hx' %s %s\n",
+ value,
+ flags & EOI ? "EOI" : "",
+ flags & ATN ? "ATN" : "");
+#if DEBUG > 2
+ if (flags & ATN)
+ lptgpib_print_command(value);
+#endif
+ if (_flags)
+ *_flags = flags;
+ return value;
+}
+
+void lptgpib_write_byte(char value, char flags){
+ DBG_low("WRITE: ");
+ put_control( TRI | (flags & ATN) ); /* clear NRFD and NDAC,
+ possibly enable ATN */
+ int tmp; /* wait for all to be ready */
+
+ BUSYWAIT(( tmp = get_control(), !(tmp & NDAC) || (tmp & NRFD) ))
+
+ DBG_low("rfd+ndac, ");
+ put_data(value & ~(flags & EOI) ); /* put data */
+ put_control( (flags & ATN) ); /* clear TRI */
+ put_control( DAV | (flags & ATN) ); /* set DAV*/
+ DBG_low("dav, ");
+ BUSYWAIT( get_control() & NDAC ) /* wait for all to accept */
+ DBG_low("dac ");
+ put_control(TRI | NDAC | NRFD ); /* back to default state */
+
+ DBG_low( isprint(value) ? "'%c' %s %s\n" : "'\\x%02hx' %s %s\n",
+ value,
+ flags & EOI ? "EOI" : "",
+ flags & ATN ? "ATN" : "");
+}
+
+int lptgpib_read_data(char *dest, int size){
+ if ( !dest || ! (size>0) ) return 0;
+ int pos = 0;
+ while (1) {
+ char flags;
+ dest[pos++] = lptgpib_read_byte(&flags);
+ if (flags & ATN){ /* command instead of data */
+ DBG("READ: '%.*s' interrupted by ATN\n", pos, dest);
+ return -pos;
+ }
+ if ( (pos >= size) ||
+ (flags & EOI) ||
+ (dest[pos-1] == EOS)){ /* overflow, EOI or EOS */
+ DBG("READ: '%.*s'\n", pos, dest);
+ return pos;
+ }
+ }
+}
+
+void lptgpib_command(char value){
+ lptgpib_write_byte(value, ATN);
+#if DEBUG > 2
+ lptgpib_print_command(value);
+#endif
+}
+
+void lptgpib_write_data(char *src, int size){
+ if ( !src ) return;
+ int i;
+ for (i=0; i<size; i++)
+ lptgpib_write_byte(src[i], (i+1 == size) ? EOI : 0);
+/* lptgpib_write_byte(src[i], 0);
+ lptgpib_write_byte(0x0a, EOI); */
+ DBG("WRITE: '%.*s'\n", size, src);
+}
+
+int lptgpib_read(int address, char *dest, int size){
+ if (address > 30 || address < 0 || !dest)
+ return 0;
+
+ lptgpib_command(UNL);
+ lptgpib_command(MLA+0);
+ lptgpib_command(MTA+address);
+ return lptgpib_read_data(dest, size);
+}
+
+void lptgpib_write(int address, char *src){
+ if (address > 30 || address < 0)
+ return;
+ lptgpib_command(MTA+0);
+ lptgpib_command(UNL);
+ lptgpib_command(MLA+address);
+ lptgpib_write_data(src, strlen(src));
+}