SPP, Compatibility, Nibble and Byte Mode Transfers

These modes are grouped together because they use the same outbound data-transfer handshake. The term SPP (Standard Parallel Port) covers both compatibility and nibble modes.

Slow mode transfers using PPDEV

Just negotiate or set the port to the desired transfer mode. Then you can read from or write to the port as to any other file.

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/parport.h>
#include <linux/ppdev.h>

int bytes_read, bytes_written;
unsigned char buf[120]; /* The size is arbitrary */
int parportfd = open(“/dev/parport0”, O_RDWR);
int result = ioctl(parportfd,PPEXCL);
int mode = IEEE1284_MODE_BYTE; /* or IEEE1284_MODE_NIBBLE, etc. */
int result = ioctl(parportfd,PPNEGOT,&mode);
int bytes_read = read(parportfd,buf,120); /* to get bytes from the peripheral */
int bytes_written = write(parportfd,buf,sizeof(buf)); /*or send bytes to it */

Slow mode transfers using direct IO

The outbound handshake is the same for all of these modes when done in this way.

Outbound data transfers

The basic sequence for writing a character in the slower modes is:

unsigned char data; /* The byte that you want to send to the peripheral */
unsigned char oldctl;
unsigned char ctl;
unsigned char status, i, maxtries = 100;

unsigned char STROBE = 0x1;
unsigned char DIRECTION = 0x20; 
unsigned char nBUSY = 0x80;

int BASE = 0x378; /* or whatever */
int statusport = BASE + 1;
int controlport = BASE + 2;

oldctl = inb(controlport); /* get the current control port reading */
ctl = oldctl &= ˜DIRECTION;
outb_p((ctl, controlport); /* Only if you leave a bidirectional
                                          port in the inward direction */
outb_p(data, BASE); /* write a byte to the data lines */
for (i = 0; i < maxtries; i++) { 
  status = inb(statusport); /* if there is any possibility of the computer
                           getting ahead of the peripheral, this should be 
                           tested repeatedly until it is clear */
  if((status & nBUSY) == 0) break;
}
outb_p((ctl | STROBE), controlport); /* set the strobe flag */
outb_p((ctl &= ˜STROBE), controlport); /* and clear it again */
outb_p(oldctl, controlport); /* restore the port to its original condition */
   

Inbound transfers using IEEE 1284 Nibble-Mode

Nibble mode allows inbound data transfers 4 bits at a time (hence the name “nibble”) through the status lines, rather than the data lines. This invention permitted parallel ports designed for output only to have a limited input capability.

unsigned char INIT = 0x4;

unsigned char oldctl, ctl, data, low_nibble = 0, high_nibble = 0;

oldctl = inb(controlport);

ctl = oldctl | STROBE;
outb(ctl, controlport); /* Ask for the low nibble */
low_nibble = (inb(statusport) & 0xF);
ctl = ctl &= ˜STROBE; /* Ask for the high nibble */
high_nibble = (inb(statusport) & 0xF);
data = ((low_nibble >> 4) + high_nibble) ^ 0x88;
/* Adds the two nibbles together, 0x88 fixes the inverted lines */


for (i = 0; i < 2; i++) 
  {
  ctl |= AUTOFEED;
  if (i == 0)
             ctl &= ˜INIT;
  buf[i] = inb(statusport); / *Get the data from the status lines. */
  ctl = ˜AUTOFEED; /* Host tells peripheral that it has the data. */
  ctl |= INIT;
  outb (ctl, controlport);
  /* Now turn the status line values into a coherent nibble */
  buf[i] = (0xf & & (((buf[i] & ˜nACK) >> 3) | (˜buf[i] & nBUSY) >> 4));
  }
buf[0] = buf[0] | (buf[1] << 4); /* Put two nibbles together into a byte. */

Inbound data transfers using IEEE1284 Byte-Mode (also known as Bidirectional or PS/2 mode).

The inbound transfer is similar to the outbound in Byte mode. In some circumstances you can get away with writing the function like SPP outbound mode, substituting AUTOFEED for STROBE. In other cases it's necessary to use the complete handshake as shown below.

unsigned char data; /* The byte that you want to read from the peripheral */
unsigned char oldctl;
unsigned char ctl;
unsigned char busy, i, maxtries = 100; /* number 100 here is arbitrary */

unsigned char STROBE = 0x1;
unsigned char AUTOFEED = 0x2;
unsigned char DIRECTION = 0x20; 
unsigned char nBUSY = 0x80;
unsigned char nACK = 0x40;

int BASE = 0x378; /* or whatever */
int statusport = BASE + 1;
int controlport = BASE + 2;

oldctl = inb(controlport); /* get the current control port reading */
ctl = oldctl | DIRECTION;
outb((ctl, controlport); /* set the port to inbound transfer */
outb((ctl | AUTOFEED ), controlport); /* signals that the host is ready */ 
for(i=0;i = maxtries;i++){/* Check for a valid byte- can be skipped for speed */
  status = inb(statusport); 
  if(status & nACK != 0) break;
}
data = inb(BASE); /* grab the byte you want */
outb(ctl & ˜AUTOFEED); /* tells the peripheral that the byte has been received */
for(i=0; i = maxtries;i++) { /* if timing is OK, this can also be skipped */
  status = inb(statusport);
  if(status & nACK == 0) break; /*nACK is cleared to acknowledge host response*/
}
outb((ctl | STROBE), controlport); /*acknowledge nACK clear by pulsing STROBE */
outb((ctl & ˜STROBE),controlport);