If the peripheral is a passthrough device (ie. one where a second device can be attached to the device, and hence share a parallel port with it), you will need to wake it up by sending a "magic code" before you can communicate with it. This is done by sending a sequence of bytes directly to the data pins (the base port address) without using a handshake. To communicate with the peripheral you will need to select one of the available parallel port protocols. You will likely want to support more than one; typically one of the newer fast protocols and basic SPP transfer. Ideally you should support all protocols that the peripheral is capable of using, since parallel ports are a varied and quirky lot. Peripherals and the kernel both start out in SPP mode when they are switched on.
If your peripheral supports it, you can change modes using the IEEE 1284 negotiation protocol. Here the host and the peripheral handshake to test the availability of the requested mode and the peripheral is instructed to use the mode if it is available. The PPNEGOT call also sets the hosts transfer mode to the proper mode at the same time if the negotiation is successful.
#include <sys/ioctl.h> #include <linux/parport.h> #include <linux/ppdev.h> int mode = IEEE1284_MODE_EPP; int result = ioctl(parportfd,PPNEGOT,&mode); /* 0 on success, -1 on failure */ |
IEEE1284_MODE_COMPAT
IEEE1284_MODE_NIBBLE
IEEE1284_MODE_BYTE
IEEE1284_MODE_EPP
IEEE1284_MODE_ECP
COMPAT and NIBBLE modes differ in that COMPAT is considered to be unidirectional (outbound). There may be support for some of the more exotic parallel port modes as well, such as “fast centronics” and EPP 1.7. Check the kernel parport documentation for details.
If your peripheral does not support IEEE 1284 negotiation you can change modes by using the PPSETMODE ioctl. It's used exactly the same as above except that the ioctl call is changed.
int result = ioctl(parportfd,PPSETMODE,&mode); |
In this case you will presumably have to inform the peripheral of the mode you want to use. Consult the manufacturer's documentation (or the results of your heroic reverse engineering) for details.
To change protocols for an ECP capable port directly, one writes to the three upper bits of the “Extended Control Register” (ECR) which is located at base-address + 0x402.
ECR bits 5-7 | hex | Mode |
000 | 0x0 | SPP |
001 | 0x20 | Byte |
010 | 0x40 | Fast Centronics |
011 | 0x60 | ECP |
100 | 0x80 | EPP |
101 | 0xA0 | reserved |
110 | 0xC0 | FIFO test mode |
111 | 0xE0 | configuration |
The first five bits of the ECR should remain unaltered.
#include <sys/io.h> int addr = 0x378; /* Usual base address for parport0 */ unsigned char EPP_ECR = 0x80; unsigned char old_ECR = inb_p(addr + 0x402); /* Paused IO for example */ unsigned char low_bits = old_ECR & 0x1f; /* save 5 lowest bits */ unsigned char new_ECR = EPP_ECR | low_bits; outb_p(new_ECR, addr + 0x402); /* set the port to EPP */ |
This action only changes the port configuration; the peripheral must be informed of the change separately. If the port is in any mode other than SPP or Byte mode and you wish to set the port to a new mode that is also neither SPP nor Byte (eg. changing the port from EPP to ECP), you must first put the port into one of those two modes. If you really want to implement the IEEE 1284 negotiation protocol, copy the code from the parport driver in the kernel source.