io<base>,<length> | Mandatory | I/O ports <base> and <length> in hex |
irq<number> | Optional IRQ | <number> in decimal |
override | Optional | Use these resources even if they cannot be allocated |
Listing 15.1 shows the commands that initialize the printer. It also shows how
First, three constants are defined to represent the offsets to each register in the parallel port electronics.
The InitPrinter BYTE array stores the commands to initialize the printer. Each line has one command and its parameters. First, the Control port INIT# line is set low by writing 0xC8 using the PHDIO_WRITE command. The PHDIO_DELAY command then waits for 60µs. The INIT# signal is set high. The write value of 0xDC also selects the printer and enables interrupts. A further delay of 60us completes the operation.
The
If there is an output buffer that is big enough, the first two 16-bit words in the output indicate any problems that the WdmIo driver found. The first word is an error code. The second word is the zero-based index into the command buffer in which the problem was found. Both are zero if there were no problems. The possible error codes are also found in Ioctl.h in the WdmIoSys directory.
The WdmIoTest code rather sloppily does not bother to check the returned error code. If using this driver for real, make sure that you check all error codes.
Listing 15.1 WdmIo Test issuing commands to run straightaway
const BYTE PARPORT_DATA = 0;
const BYTE PARPORT_STATUS = 1;
const BYTE PARPORT_CONTROL = 2;
BYTE InitPrinter[] = {
PHDIO_WRITE, PARPORT_CONTROL, 0xC8, // Take INIT# low
PHDIO_DELAY, 60, // Delay 60us
PHDIO_WRITE, PARPORT_CONTROL, 0xDC, // INIT# high, select printer,
// enable interrupts
PHDIO_DELAY, 60, // Delay 60us
};
int main(int argc, char* argv[]) {
// …
DWORD BytesReturned;
WORD rv[3];
if (DeviceIoControl(hWdmIo, IOCTL_PHDIO_RUN_CMDS,
InitPrinter, length(InitPrinter), // Input
rv, sizeof(rv), // Output
&BytesReturned, NULL)) {
printf(' InitPrinter OK. rv=%d at %d
', rv[0], rv[13);
} else {
printf('XXX InitPrinter failed %d
',GetLastError());
goto fail;
}
// …
Reading Data
If you use the PHDIO_READ or PHDIO_READS commands, you must provide an output buffer that is big enough to receive the read data. Remember that the first four bytes of the output buffer are always used for the error code and location.
Listing 15.2 shows how the ReadStatus commands are issued. It simply reads a byte value from the Status port. After
Listing 15.2 Reading data
BYTE ReadStatus[] = {
PHDIO_READ, PARPORT_STATUS, // Read status
};
int main(int argc, char* argv[]) { //…
DWORD BytesReturned;
WORD rv[3];
if (DeviceIoControl(hWdmIo, IOCTL_PHDIO_RUN_CMDS,
ReadStatus, length(ReadStatus), // Input
rv, sizeof(rv), // Output
&BytesReturned, NULL)) {
PBYTE pbrv = (PBYTE)&rv[2];
printf(' ReadStatus OK. rv=%d at %d status=%02X
', rv[0], rv[l], *pbrv);
if ( (*pbrv&0x88)==0x88) {
busy = false;
break;
}
}
The
Connecting to an Interrupt
When the WdmIo device is started, it is told which interrupt to use. However, it does not connect to the interrupt (i.e., install its interrupt handler), as it does not yet know how to handle the interrupt.
The ConnectToInterrupts commands shown here are used to initialize WdmIo's interrupt handling. The first write command tells the parallel port hardware not to generate interrupts. The second command tells the WdmIo driver to use a time-out of 10 seconds when processing subsequent
BYTE ConnectToInterrupts[] = {
PHDIO_WRITE, PARPORT_CONTROL, 0xCC, // Disable interrupts
PHDICO_TIMEOUT, 10, // Write time-out in seconds
PHDIO_IRQ_CONNECT, PARPORT_STATUS, 0x00, 0x00, // Connect to interrupt
};
The last command, PHDIO_IRQ_CONNECT, connects the WdmIo driver to its interrupt. As mentioned before, the actual interrupt number is passed as a resource when the WdmIo device is started. WdmIo starts servicing a hardware interrupt by reading a hardware register; in this case, the Status register is read. It must determine whether the interrupt was caused by its hardware or not.
The two final parameters to the PHDIO_IRQ_CONNECT command are a mask and a value. The register contents are ANDed with the mask and compared to the value, as shown in the following