EPP

The EPP, or Enhanced Parallel Port protocol, uses hardware handshaking in order to speed transfers and minimize cpu cycles. It allows 16 and 32 bit words to be transferred with a single instruction as well, but the data transfer rate is, in any case, the same, with one byte transferred per ISA cycle. EPP ports allow a separate “address” write so data can be sent to any one of several channels. This can ease access to control registers on a peripheral's controller chip. Addresses are written in single bytes, with values from 0-255.

EPP with PPDEV

Using PPDEV, EPP transfers are virtually identical to the slow-mode transfers demonstrated in section 5.1 except that the ioctl mode becomes IEEE1284_MODE_EPP. To distinguish between address and data cycles, the header file parport.h contains the flags IEEE1284_ADDR and IEEE1284_DATA. The latter is equal to 0, so if the base channel is used, it need not be set.

int addr_mode = IEEE_1284_MODE_EPP | IEEE1284_ADDR ;
int data_mode = IEEE_1284_MODE_EPP | IEEE1284_DATA ;

unsigned char addr = 0x2; /* Arbitrary choice */
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 result = ioctl(parportfd,PPNEGOT,&addr_mode); /*or PPSETMODE */
int bytes_writen = write(parportfd,addr,sizeof(addr)); /*select channel 0x2 */
int result = ioctl(parportfd,PPNEGOT,&data_mode);
int bytes_read = read(parportfd,buf,120); /* reads from channel 0x2. */
int bytes_written = write(parportfd,buf,sizeof(buf)); /*or writes to it */

EPP via direct IO

Making EPP transfers by direct IO is very easy. An EPP address is written to the port's base address (ie. 0x378) + 3, and a data write is done by writing to the base address + 4. Before doing anything with EPP, one should initialize the port by writing xxxx0100 to the control lines.

int BASE = 0x378; /* or whatever */
int statusport = BASE + 1;
int controlport = BASE + 2;
unsigned char status;
unsigned char ctl;

ctl = inb(controlport)
ctl = (ctl &= 0xF0) | 0x4;
outb(ctl, controlport);

First one must clear any possible EPP timeouts. Unfortunately EPP ports are not standardized in this area, but the following code covers all varieties.

if ((inb(statusport) & 0x01)) {
  status = inb(statusport(port)); 
  outb(status | 0x01, statusport); /* Some reset by writing 1 */ 
  outb(status & 0xfe, statusport); /* Others by writing 0 */
}

Timeouts should be cleared at the beginning of each block of data to be read or written. It is not necessary to do so for every byte. After this it's trivial to read data.

unsigned char data, address=0x4; /* read from register #4- this is arbitrary */

outb(address, (BASE + 3)); /* select the register */
data = inb(BASE + 4); /* repeat this operation for as many bytes as you need */

To write data is virtually identical.

unsigned char data;

outb(data, (BASE + 4)); /* that's it */

short data_16;
int data_32;

outw(data_16, (BASE + 4)); /* sends a 16 bit word */
outl(data_32, (BASE + 4)); /* sends a 32 bit word */

If it is necessary to simulate an EPP address write in slow mode, this can be done easily enough.

unsigned char STROBE = 0x1;
unsigned char SELECTIN = 0x8;
unsigned char addr;

outb_p(addr,port); 
ctl = inb_p(controlport); 
ctl |= STROBE; 
outb_p(ctl,controlport); 
ctl |= SELECTIN; 
outb_p(ctl,controlport); 
ctl &= ˜SELECTIN; 
outb_p(ctl,controlport); 
ctl &= ˜STROBE; 
outb_p(ctl,controlport);

The data character than can be sent using a normal slow-mode write.