WdmIo code snippet. If they are not equal, the interrupt must be intended for another driver.

// See if interrupt is ours

UCHAR StatusReg = ReadByte(dx, dx->InterruptReg);

if ((StatusReg&dx->InterruptRegMask) != dx->InterruptRegValue) return FALSE; // Not ours

Suppose the Status register really did reset its bit 2 to 0 when it generated an interrupt. Specifying 0x04, as the mask would isolate bit 2. If the ANDed result is equal to 0x00, the interrupt is ours. Therefore, specifying a PHDIO_IRQ_CONNECT mask of 0x04 and value of 0x00 would have correctly detected when the parallel port interrupted.

However, as stated earlier, I found that there is no interrupt indication in the Status register. I simply have to assume that if an interrupt arrives, it came from the correct parallel port. To persuade the WdmIo code to continue regardless, a mask of 0x00 and a value of 0x00 was specified in the ConnectToInterrupts code.

Storing the Write Byte Commands

The WdmIo driver needs to be told a series of commands that write a single byte of data. These commands are used both to write the first output byte and to process an interrupt to send another byte.

IOCTL_PHDIO_CMDS_FOR_WRITE is used to store the commands used to write data. This IOCTL is issued using DeviceIoControl in the normal way. The commands are in the input buffer. No output buffer need be specified.

Listing 15.3 shows the WriteByte commands that WdmIoTest tells WdmIo to use to write a data byte. The first PHDIO_WRITE command ensures that the STROBE output signal in bit 0 of the Control register is off. The PHDIO_WRITE_NEXT command is used to write the next byte in the output buffer to the Data port. PHDIO_DELAY is used to delay for 1µs while the output signals settle. The STROBE signal is then set. A delay of 1µs is used before turning STROBE off again. A last delay of 1µs is introduced just to be on the safe side. Finally, the Status register is read; I shall show later on how to access this value.

Listing 15.3 Stored write byte commands

BYTE WriteByte[] = {

 PHDIO_WRITE, PARPORT_CONTROL, 0xDC, // Ensure STROBE off

 PHDIO_WRITE_NEXT, PARPORT_DATA, // Write next byte

 PHDIO_DELAY, 1, // Delay 1us

 PHDIO_WRITE, PARPORT_CONTROL., 0xDD, // STROBE on

 PHDIO_DELAY, 1, // Delay 1us

 PHDIO_WRITE, PARPORT_C0NTROL. 0xDC, // STROBE off

 PHDIO_DELAY, 1, // Delay 1us

 PHDIO_READ, PARPORT_STATUS, // Read status

};

The PHDIO_WRITE_NEXT command is crucial to these data transfer commands. The WdmIo driver keeps track of the current position in the output buffer passed by WriteFile. WdmIo correctly runs the WriteByte commands until all the bytes have been transferred.

Writing Data

The WdmIoTest program is finally ready to output a message to the printer. The following code snippet shows that WriteFile is used in the normal way to output data.

char* Msg = 'Hello from WdmIo example Chris Cant, PHD Computer Consultants Ltd ';

DWORD Ten = strlen(Msg);

if (!WriteFile(hWdmIo, Msg, Ten, &BytesReturned, NULL)) printf('XXX Could not write message %d ', GetLastError());

else if (BytesReturned==len) printf(' Write succeeded ');

else printf('XXX Wrong number of bytes written: %d ', BytesReturned);

The WdmIo driver uses the WriteByte commands to output the first byte, H. It then expects an interrupt when each byte has been printed. The WriteByte commands are run again by the interrupt handler to output each following byte. Assuming all goes well, when an interrupt is received after the last byte has been sent, WdmIo completes the WriteFile call successfully.

If there is a problem, WriteFile returns an error. The most likely error is ERROR_NOT_READY, which is returned if the write times out. If there is a problem running the write byte commands, ERROR_GEN_FAILURE is returned. Retrieve the WriteFile results to find the source of the problem.

Getting WriteFile Results

Each time the write commands are run, WdmIo gives its command processor a 5-byte output buffer. The first two words (4 bytes) are used for the error code and location. The fifth byte is filled with any output data that the commands produce. In WdmIoTest, the WriteByte commands read the Status register just after each byte is output. If the write commands attempt to output more than one byte, the command run will be aborted with error code PHDIO_NO_OUTPUT_ROOM.

However, what is really wanted is the value of the Status register after each byte is processed by the printer. At this point, the Status register contains some useful information, such as whether the printer is off-line or has run out of paper.

While WdmIoTest can just issue the ReadStatus commands again, it is more convenient if the Status register value is returned along with the command output data. Therefore, the register that the interrupt handler read is stored as a sixth byte in the WriteFile results buffer.

IOCTL_PHDIO_GET_RW_RESULTS is used to obtain the write (and read) results. Table 15.5 recaps the contents of the 6-byte buffer that is returned. Listing 15.4 shows how DeviceIoControl is used to read and display the results. When WdmIoTest is run successfully, the cmd status is 0x5F and the int status is 0xDF. This is expected. When a byte has just been output to the printer, BUSY# (the top bit of the Status register) goes low. When it has been printed, BUSY# goes high and an interrupt is generated.

Note that the command output buffer and the last interrupt register value locations are reused each time an interrupt occurs and the commands are run. This is not a problem. As soon as any fault occurs, processing stops with the most useful information in the results buffer.

Table 15.5 Read/Write results

Bytes Description
2 Command error code
2 Command error offset
1 Command output value
1 Last interrupt register

Listing 15.4 Getting Write/Read command results

if (DeviceIoControl(hWdmIo, IOCTL_PHDIO_GET_RW_RESULTS,

 NULL, 0, // Input

 rv, sizeof(rv), // Output

 &BytesReturned, NULL)) {

 printf(' Get RW Results OK. rv=%d at %d ', rv[0], rv[l]);

 BYTE* pbuf = (BYTE*)(&rv[2]);

 printf(' cmd status=%02x ', pbuf[0]);

 printf(' int status=%02x ', pbuf[l]);

}

WdmIoTest finally disables interrupts by running the DisableInterrupts commands and closes its handle to the WdmIo device.

Reading Data Using Interrupt Driven I/O

Reading data is a process similar to writing data. This time, two sets of commands need to be passed to WdmIo before the actual ReadFile is issued. The first set of commands starts the read process. The second set is used to process interrupts. Two sets are needed, as it is highly likely that different commands will be needed.

Use IOCTL_PHDIO_CMDS_FOR_READ_START to tell WdmIo which commands to use to start the read process. Typically, these commands might simply enable input interrupts from the device. IOCTL_PHDIO_CMDS_FOR_READ passes WdmIo the commands used to process each interrupt.

The ReadFile call passes the read buffer. The WdmIo driver can only handle fixed length transfers. However, a time-out of only one or two seconds could be used to ensure that errors are caught reasonably quickly.

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату
×