DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK
 
UDI driver coding basics

Programmed I/O (PIO)

The udi_cmos driver uses PIO to read and write values on the CMOS RAM device. This is done by first specifying the I/O characteristics of the device.

"cmos_udi.c PIO device properties"
   /*
    * PIO properties of the physical I/O registers on the CMOS device.
    */
   #define CMOS_REGSET	1	/* first (and only) register set */
   #define CMOS_BASE	0	/* base address within this regset */
   #define CMOS_LENGTH	2	/* two bytes worth of registers */
   #define CMOS_PACE	5	/* wait 5 microseconds between accesses
   				 * (not really need for this device,
   				 * but illustrates use of pacing)
   				 */

The above definitions are used in calls to udi_pio_map in the driver subroutines to establish device characteristics for the CMOS RAM device. This is called from a driver subroutine defined later. The set of definition needed for a given device are usually quite specific to that device.

"cmos_udi.c PIO offsets"
   /*
    * PIO offsets for various device registers, relative to the base of
    * the device's register set.
    */
   #define CMOS_ADDR	0	/* address register */
   #define CMOS_DATA	1	/* data register */

The address and data register definitions are used in the transaction lists described below.

"cmos_udi.c other device properties"
   /*
    * Other device properties.
    */
   #define CMOS_DEVSIZE	0x40	/* # data bytes supported by device */
   #define CMOS_RDONLY_SZ	0x0E	/* first 14 bytes are read-only */

The device size definitions are used in binding, transfer, and write subroutine calls defined later in the driver, to avoid overflowing the device or writing to read-only device memory.

"cmos_udi.c sample code (cont.)"
   /*
    * PIO trans lists for access to the CMOS device.
    */
   static udi_pio_trans_t cmos_trans_read[] = {
   	/* R0 <- SCRATCH_ADDR {offset into scratch of address} */
   	{ UDI_PIO_LOAD_IMM+UDI_PIO_R0, UDI_PIO_2BYTE, SCRATCH_ADDR },
   	/* R1 <- address */
   	{ UDI_PIO_LOAD+UDI_PIO_SCRATCH+UDI_PIO_R0,
   				UDI_PIO_1BYTE, UDI_PIO_R1 },
   	/* R0 <- SCRATCH_COUNT {offset into scratch of count} */
   	{ UDI_PIO_LOAD_IMM+UDI_PIO_R0, UDI_PIO_2BYTE, SCRATCH_COUNT },
   	/* R2 <- count */
   	{ UDI_PIO_LOAD+UDI_PIO_SCRATCH+UDI_PIO_R0,
   				UDI_PIO_1BYTE, UDI_PIO_R2 },
   	/* R0 <- 0 {current buffer offset} */
   	{ UDI_PIO_LOAD_IMM+UDI_PIO_R0, UDI_PIO_2BYTE, 0 },
   	/* begin main loop */
   	{ UDI_PIO_LABEL, 0, 1 },
   	/* output address from R1 to address register */
   	{ UDI_PIO_OUT+UDI_PIO_DIRECT+UDI_PIO_R1,
   				UDI_PIO_1BYTE, CMOS_ADDR },
   	/* input value from data register into next buffer location */
   	{ UDI_PIO_IN+UDI_PIO_BUF+UDI_PIO_R0,
   				UDI_PIO_1BYTE, CMOS_DATA },
   	/* decrement count (in R2) */
   	{ UDI_PIO_ADD_IMM+UDI_PIO_R2, UDI_PIO_1BYTE, (udi_ubit8_t)-1 },
   	/* if count is zero, we're done */
   	{ UDI_PIO_CSKIP+UDI_PIO_R2, UDI_PIO_1BYTE, UDI_PIO_NZ },
   	{ UDI_PIO_END_IMM, UDI_PIO_2BYTE, 0 },
   	/* increment address and buffer offset */
   	{ UDI_PIO_ADD_IMM+UDI_PIO_R1, UDI_PIO_1BYTE, 1 },
   	{ UDI_PIO_ADD_IMM+UDI_PIO_R0, UDI_PIO_1BYTE, 1 },
   	/* go back to main loop */
   	{ UDI_PIO_BRANCH, 0, 1 }
   };
   static udi_pio_trans_t cmos_trans_write[] = {
   	/* R0 <- SCRATCH_ADDR {offset into scratch of address} */
   	{ UDI_PIO_LOAD_IMM+UDI_PIO_R0, UDI_PIO_2BYTE, SCRATCH_ADDR },
   	/* R1 <- address */
   	{ UDI_PIO_LOAD+UDI_PIO_SCRATCH+UDI_PIO_R0,
   				UDI_PIO_1BYTE, UDI_PIO_R1 },
   	/* R0 <- SCRATCH_COUNT {offset into scratch of count} */
   	{ UDI_PIO_LOAD_IMM+UDI_PIO_R0, UDI_PIO_2BYTE, SCRATCH_COUNT },
   	/* R2 <- count */
   	{ UDI_PIO_LOAD+UDI_PIO_SCRATCH+UDI_PIO_R0,
   				UDI_PIO_1BYTE, UDI_PIO_R2 },
   	/* R0 <- 0 {current buffer offset} */
   	{ UDI_PIO_LOAD_IMM+UDI_PIO_R0, UDI_PIO_2BYTE, 0 },
   	/* begin main loop */
   	{ UDI_PIO_LABEL, 0, 1 },
   	/* output address from R1 to address register */
   	{ UDI_PIO_OUT+UDI_PIO_DIRECT+UDI_PIO_R1,
   				UDI_PIO_1BYTE, CMOS_ADDR },
   	/* output value from next buffer location to data register */
   	{ UDI_PIO_OUT+UDI_PIO_BUF+UDI_PIO_R0,
   				UDI_PIO_1BYTE, CMOS_DATA },
   	/* decrement count (in R2) */
   	{ UDI_PIO_ADD_IMM+UDI_PIO_R2, UDI_PIO_1BYTE, (udi_ubit8_t)-1 },
   	/* if count is zero, we're done */
   	{ UDI_PIO_CSKIP+UDI_PIO_R2, UDI_PIO_1BYTE, UDI_PIO_NZ },
   	{ UDI_PIO_END_IMM, UDI_PIO_2BYTE, 0 },
   	/* increment address and buffer offset */
   	{ UDI_PIO_ADD_IMM+UDI_PIO_R1, UDI_PIO_1BYTE, 1 },
   	{ UDI_PIO_ADD_IMM+UDI_PIO_R0, UDI_PIO_1BYTE, 1 },
   	/* go back to main loop */
   	{ UDI_PIO_BRANCH, 0, 1 }
   };

PIO transaction lists for cmos_udi

The figure above shows the transaction lists for the read and write PIO operations to the CMOS RAM device. Each of the array members of the cmos_trans_read[] array contains three values that specifies an atomic operation on the device. When the trans_read PIO transaction handle is specified with the udi_pio_trans environment call, the list of operations specified in the cmos_trans_read[] array is performed on the device. This is done in the driver subroutines described later.

Similar processing occurs with the cmos_trans_write[] array when the trans_write transaction handle is passed to udi_pio_trans.

As with device characteristics, the transaction lists necessary for any device are unique to that device.

The operation, register, and address mode constants used in the transaction lists ared defined in the Physical I/O Specification description of which documents transaction list syntax. The remainder are defined in the driver code.

Each cmos_trans_read[] and cmos_trans_write[] array element is a udi_pio_trans_t structure with three members:

   typedef struct {
   udi_ubit8_t pio_op ;
   udi_ubit8_t tran_size ;
   udi_ubit16_t operand ;
   } udi_pio_trans_t ;

Simply put, these specify a PIO operation (such as a logical OR of a register and an operand), a transaction size specifying the size of the result of the operation, and any operand required by the PIO operation.

The cmos_trans_read[] PIO list, described below in detail, reads a data value from CMOS RAM and copies it to the scratch space in the control block passed as part of the channel operation invoking the PIO read.

Note that the UDI environment provides eight special registers for use in constructing transaction operations. We'll refer to these internal UDI registers in the same manner as the UDI Specification does, using the notations R0 through R7. We'll refer to registers on the device by spelling out the word ``register'' before the number.

  1. Load R0 with a two-byte address value, an offset into scratch space. UDI_PIO_LOAD_IMM is a PIO operation that sets one of R0 through R7 (in this case R0), to the value of the supplied operand. UDI_PIO_2BYTE gives the size of the transfer as two bytes. SCRATCH_ADDR is a constant set elsewhere in the driver module code (see ``Scratch space, offsets, and requirements''), and contains the offset of the addr element in the cmos_gio_xfer_scratch_t control block scratch space.

  2. Load R1 with the addr value from scratch space. UDI_PIO_LOAD is a PIO operation that sets on of R0 through R7, named by the 3 low order bits of the provided operand, to the value found at a provided address, using a specified ``addressing mode''. UDI_PIO_SCRATCH is the addressing mode, specifying that the address used is a 32-bit offset into the scratch space of the control block passed as part of the channel operation that invoked the read transaction. UDI_PIO_R0 is R0, which currently holds the offset into scratch space from transaction 1. UDI_PIO_1BYTE is the size of the data transferred from the address. UDI_PIO_R1 is R1, which gets loaded with the value from scratch space.

  3. Transaction 3 is similar to transaction 1, execpt this time R0 is loaded with the offset of the count element in the cmos_gio_xfer_scratch_t control block scratch space.

  4. Transaction 4 is similar to transaction 2, except now R2 is loaded with the count value from scratch space.

  5. Register 0 is now initialized to the current buffer offset (0). This operation uses the same format as transactions 1 and 3.

  6. This transaction labels the beginning of a loop which reads count bytes (the value currently in R2) from the address on the CMOS RAM device (held in R1), into the buffer at the offset specified in R0 (which gets incremented as data is read byte by byte).

    UDI_PIO_LABEL sets a label to which a subsequent UDI_PIO_BRANCH can switch control. The tran_size element is unused and must be set to zero. The operand element is set to a unique integer to be used as the operand of a subsequent UDI_PIO_BRANCH.

  7. Output the value in R1 directly to register 0 (the address register) of the CMOS RAM device (this sets the data register on the device to the value at the specified address in device memory). UDI_PIO_OUT is a PIO operation that sets the location on the PIO device specified by the operand to a value held in a UDI internal register, UDI_PIO_DIRECT is the addressing mode, which specifies that the content of UDI_PIO_R1 (R1) is to be used as the data (not the address of the data). UDI_PIO_1BYTE specifies a one-byte transfer. CMOS_ADDR is the previously set constant containing the offset into device address space for the address register (see ``trans_read/trans_write'').

  8. Read the data from the CMOS device data register into the buffer. UDI_PIO_IN is the reverse of UDI_PIO_OUT from the previous transaction. UDI_PIO_IN sets the location specified by the value in R0 (using the UDI_PIO_BUF addressing mode), with the byte of data located on the PIO device at the offset specified by the operand CMOS_DATA. UDI_PIO_BUF specifies that the value in R0 is a 32-bit offset into the data buffer.

  9. The count held in R2 is decremented to indicate that one byte has been read from the device to the buffer. This is done using the previously described UDI_PIO_ADD_IMM operation, this time with an explicit cast of the operand (-1) to a one-byte UDI fundamental type. [No subtraction operation is supplied, so a negative value is added instead.]

  10. Compare the count to zero to see if all bytes have been read from the device; if the count is not zero, then skip the next transaction and continue. UDI_PIO_CSKIP is the conditional skip PIO operation. The comparison specifed by the ``condition code'' operand (UDI_PIO_NZ) is carried out on the one-byte (UDI_PIO_1BYTE) value in UDI_PIO_R2 (R2). If the result of the comparison is TRUE, then the next transaction is skipped. UDI_PIO_NZ equates to the expression reg!=0, where reg is one of R0 through R7. In this case, as long as R2 does not equal zero, the next transaction is skipped.

  11. This transaction gets executed only when the count in R2 reaches zero, meaning all bytes have been read from the CMOS RAM device. UDI_PIO_END terminates processing of the tranascation list and sets the result code (an arbitrary 8-bit code that is defined by the driver) in the udi_pio_trans_call_t callback to the low byte of the operand value. In this case, the result code is a two-byte zero.

  12. If the count in R2 has not yet reached zero, then increment the address (R1) to read the address of the next byte in CMOS RAM. This is done with the previously described UDI_PIO_ADD_IMM operation to add 1 to the value in R1.

  13. Increment the buffer offset (R0) to write the next byte from CMOS RAM. This is done with the previously described UDI_PIO_ADD_IMM operation to add 1 to the value in R0.

  14. Branch bake to the UDI_PIO_LABEL transaction with the specified operand (in this case, 1). This starts the process of reading the next byte from CMOS RAM. Note that the last transaction in a transaction list array must be either a UDI_PIO_END or a UDI_PIO_BRANCH transaction.

The transaction list for the cmos_trans_write[] array ia exactly the same as the read array described above, with the exception of transaction 8. In the write operation, a byte is read from the buffer into the CMOS RAM memory at the specified address. UDI_PIO_OUT sets the location on the PIO device at the offset specified by the operand (CMOS_DATA, or the CMOS RAM data register) to the one-byte value in the buffer at the 32-bit buffer offset in R0.

Further information on transaction lists

Transaction list usage is discussed further on, in the description of the CMOS RAM driver subroutines. More complex transaction lists will be discussed in the sections of this document on the SCSI HBA and NIC sample drivers.

In the UDI Specifications, see in the Physical I/O Specification for a complete list of operations, operation classes, addressing modes, and transaction list syntax.


Next topic: Channel operations indexes
Previous topic: Control block indexes and structures

© 2005 The SCO Group, Inc. All rights reserved.
OpenServer 6 and UnixWare (SVR5) HDK - 19 June 2005