Appendix B. New and Updated Reference Pages

This appendix contains the text of new and updated reference pages that have been created since IRIX 6.3 initially shipped.

Address/Length List Reference Pages

  • Example B-1 displays the text of an overview of address/length lists.

  • Example B-2 displays the reference page listing operations on alenlists.

    Example B-1. alenlist(d4x)


    NAME
         alenlist - overview of Address/Length Lists
    
    DESCRIPTION
         An Address/Length List or alenlist is an abstract object containing a
         sequential list of address/length pairs.
     
         All addresses in the list belong to the same address space, for example
         kernel virtual address space, physical memory address space, PCI DMA
         space, VME DMA space, and so on.  All lengths are byte counts.  Each
         address/length pair added to a list describes one contiguous segment in
         the relevant address space.  Together, all the pairs in the list describe
         a region of memory, typically an I/O buffer, that is logically, but not
         necessarily physically, contiguous.
     
         When a driver receives a request for I/O, the request may be specified in
         one of several forms, for example a kernel virtual address, a user
         virtual address, or a vector of addresses from a device register array.
         The driver can convert any of these forms into a single alenlist for easy
         management.
     
         Other kernel layers (notably, the PCI infrastructure, see pciio(D3))
         accept alenlists.  Translation from one address space to another, a
         constant challenge for device drivers, can be performed on an entire
         alenlist in a single operation, avoiding repeated bouncing up and down
         through layers of software for each segment.
     
         An alenlist is created by the driver when needed.  It expands
         automatically as address/length pairs are added to the list.  Its memory
         can easily be released for recycling, or a list can be cleared and re-
         used.  There are operations to append a new pair to a list, and to load a
         list based on a user or kernel virtual buffer address and size.
     
         Address/length pairs can be read out from a list in sequence, and in
         length units different from the units that were initially loaded (for
         example, load the list based on a user-space buffer, then read out pairs
         in 512-byte segments).
     
         A cursor marks a position within an alenlist, and is used to scan through
         the pairs in the list.  A cursor can point to the beginning of its list,
         any number of bytes into the list, or at the end of the list.  Every
         alenlist contains an implicit cursor for simple management, but there are
         operations to create, destroy, and initialize additional cursors, so
         multiple positions can be maintained at once.
     
       Usage
         In order to use any of the Address/Length types or interfaces, a driver
         should include these header files in this order:
         #include <sys/types.h>
         #include <sys/alenlist.h>
         #include <sys/ddi.h>
     
       Types
         The following abstract types are used and returned by alenlist functions.
     
         alenaddr_t  An abstract address in some address space.  This is the type
                     of any address added to a list.  It is guaranteed to be big
                     enough to hold an address in any supported address space
                     (currently 64 bits).
     
         size_t      The standard byte-count type.  Every address/length pair in a
                     list comprises one alenaddr_t and one size_t.
     
         alenlist_t  A handle to an alenlist.
     
         alenlist_cursor_t
                     A handle to a cursor, an object that designates a position
                     within an alenlist.  An alenlist_cursor_t value of NULL
                     always designates the implicit cursor of a list.  Non-null
                     values designate cursor objects allocated separate from their
                     lists.
     
       Operations
         The functions that operate on alenlists are detailed in
         alenlist_ops(D3X).  Operations to allocate and destroy alenlists are as
         follows:
     
         o   alenlist_create() creates a new, empty list.
     
         o   alenlist_destroy() recycles the storage used by an alenlist.
     
         o   alenlist_cursor_create() creates a new cursor object.
     
         o   alenlist_cursor_destroy() recycles the storage used by a cursor.
     
         Operations to clear and load pairs into an alenlist are as follows:
     
         o   alenlist_clear() empties a list.
     
         o   alenlist_append() appends one address/length pair to a list.
     
         o   kvaddr_to_alenlist() converts a kernel virtual address and length
             into physical memory address/length pairs and appends the pairs to a
             list.
     
         o   uvaddr_to_alenlist() converts a user process virtual address into
             physical memory address/length pairs and appends the pairs to a list.
     
         o   buf_to_alenlist() examines a buf structure (see buf(D4)) and appends
             physical memory address/length pairs to describe the buffer to a
             list.
     
         In addition, pciio_dma(D3) documents pciio_dmamap_list(), a function that
         translates an alenlist of physical memory addresses into an alenlist of
         PCI bus addresses for DMA use.
     
         Operations to read out address/length pairs from an alenlist are as
         follows:
     
         o   alenlist_get() returns an address/length pair from a list, based on
             the implicit cursor or on a specified cursor, and optionally advances
             the cursor.
     
         o   alenlist_cursor_init() initializes a specific cursor or an implicit
             cursor to a specified offset.
     
         o   alenlist_cursor_offset() returns the current offset of a cursor.
     
       Flags
         The following flag values are used with alenlist functions:
    
         AL_NOSLEEP  Indicates that the caller does not wish to be put to sleep
                     while waiting for resources such as memory.  If an operation
                     would entail sleeping, the function returns failure.
    
         AL_NOCOMPACT
                     Indicates that adjacent address/length pairs that happen to
                     be contiguous in their address space should not be compacted
                     and treated as a single unit (which is the default behavior).
                     Rather, they should be treated as if there is a
                     discontiguity.  This flag is not needed by most drivers.
    
         AL_LEAVE_CURSOR
                     Indicates that the operation should not affect the position
                     of the internal cursor.
    
       Error Codes
         The following error codes are used by all alenlist functions:
    
         ALENLIST_SUCCESS
                     Indicates a successful operation.
    
         ALENLIST_FAILURE
                     Indicates a failed operation.
    
    SEE ALSO
         alenlist_ops(D3X), pciio_dma(D3), IRIX Device Driver Programmer's Guide
    
    
    
    

    Example B-2. alenlist_ops(d3x)


    NAME
         alenlist_ops: alenlist_append, alenlist_clear, alenlist_create,
         alenlist_cursor_create, alenlist_cursor_destroy, alenlist_cursor_init,
         alenlist_cursor_offset, alenlist_destroy, alenlist_get,
         kvaddr_to_alenlist, uvaddr_to_alenlist, buf_to_alenlist - operations on
         address/length lists
     
    SYNOPSIS
         #include <sys/types.h>
         #include <sys/alenlist.h>
         #include <sys/ddi.h>
     
         alenlist_t
         alenlist_create(unsigned flags);
     
         void
         alenlist_clear(alenlist_t plist);
     
         void
         alenlist_destroy(alenlist_t plist);
     
         alenlist_cursor_t
         alenlist_cursor_create(alenlist_t plist, unsigned flags);
     
         void
         alenlist_cursor_destroy(alenlist_cursor_t ocursor);
     
         int
         alenlist_get(alenlist_t plist,
                   alenlist_cursor_t icursor,
                   size_t maxlength,
                   alenaddr_t *paddr,
                   size_t *plength);
     
         int
         alenlist_cursor_init(alenlist_t plist,
                   size_t offset,
                   alenlist_cursor_t icursor);
     
         size_t
         alenlist_cursor_offset(alenlist_t plist, alenlist_cursor_t icursor);
     
         int
         alenlist_append(alenlist_t plist,
                   alenaddr_t address,
                   size_t length);
     
         alenlist_t
         kvaddr_to_alenlist(caddr_t kvaddr, size_t length);
     
         alenlist_t
         uvaddr_to_alenlist( alenlist_t plist,
                   uvaddr_t uvaddr,
                   size_t length);
     
         alenlist_t
         buf_to_alenlist(buf_t *buf);
     
       Arguments
         address     An address in some address space.
     
         buf         Address of a buf struct.
     
         flags       A Boolean combination of the flags declared in
                     sys/alenlist.h.
     
         icursor     A handle to an existing cursor, or NULL to indicate the
                     implicit cursor in plist.
     
         kvaddr      A valid address in kernel virtual memory.
     
         length      The length related to an address.
     
         maxlength   The maximum length allowed in the returned address/length
                     pair, or 0 to indicate no maximum applies.
     
         ocursor     A handle to an existing cursor, the target of the operation.
     
         offset      An initial byte offset for icursor, usually 0.
     
         paddr       A pointer to a variable to receive the address from an
                     address/length pair.
     
         plength     A pointer to a variable to receive the length from an
                     address/length pair.
     
         plist       A handle to an existing alenlist.
     
         uvaddr      A valid address in user virtual memory for the in-context
                     process.
     
    DESCRIPTION
         For an overview of address/length lists (alenlists) see alenlist(D4X).
     
       Allocation and Release
         Create an empty list using alenlist_create().  The AL_NOSLEEP flag
         ensures that the caller will not sleep waiting for memory.  If memory
         cannot be allocated, NULL is returned.  The returned list has no pairs in
         it, and its implicit cursor is initialized to point to the start of the
         (empty) list.
     
         The functions kvaddr_to_alenlist(), uvaddr_to_alenlist(), and
         buf_to_alenlist(), when the plist argument is NULL, allocate a new
         alenlist and return it.  However, these functions do not honor
         AL_NOSLEEP, either when creating a list or when getting memory to extend
         an existing list.  If it is important not to sleep, preallocate the list.
     
         Empty a list by applying alenlist_clear().  The implicit cursor is reset
         to the start of the (empty) list.
     
         Release a list using alenlist_destroy().  This lets the system know that
         the specified List is no longer in use.
     
         Create a cursor by calling alenlist_cursor_create().  Pass AL_NOSLEEP to
         avoid sleeping on memory allocation.  When a cursor cannot be created,
         NULL is returned.  The new cursor is associated with plist and is
         initialized to point to the head of that list.  More than one cursor can
         point to a given list.
     
         Use alenlist_cursor_destroy() to tell the system the cursor is no longer
         needed.  The cursor must not be used after this call is made.
     
       Reading a List
         Use the alenlist_get() function to retrieve pairs from the list.  An
         address/length pair from plist is stored based on paddr and plength.  The
         pair to retrieve is established by a cursor, either icursor or the
         implicit cursor in plist when icursor is NULL.  The cursor used is
         updated by the length returned, provided the AL_LEAVE_CURSOR flag is not
         used.
     
         It is not necessary to read out exactly the pairs that were added to the
         list.  When maxlength is nonzero, it establishes the maximum length
         retrieved.  When maxlength is also an integral power of 2, the returned
         length is further constrained so that the returned address and length do
         not cross a maxlength boundary.  For example, when maxlength is 512, the
         address/length values returned are such that the next pair returned will
         begin on a 512-boundary.
     
         Returns ALENLIST_SUCCESS or ALENLIST_FAILURE.  The normal cause for
         failure is that the list is exhausted.
     
         Call alenlist_cursor_init() to initialize a cursor to a specified offset
         (usually 0).  If cursorp is NULL, the implicit internal cursor of plist
         is initialized.
     
         To retrieve the current byte offset of a cursor, call
         alenlist_cursor_offset().  When icursor is NULL, the offset of the
         implicit cursor in plist is returned.
     
       Loading a List
         The basic operation to add an address/length pair to a list is
         alenlist_append().  The list is expanded if necessary.  Use the
         AL_NOSLEEP flag to prevent sleeping on memory allocation that might be
         necessary to resize the list.  Use AL_NOCOMPACT to prevent the added pair
         from being merged into a logically-adjacent preceding pair.  Returns
         ALENLIST_SUCCESS or ALENLIST_FAILURE.
     
         To build an alenlist that describes a buffer in kernel virtual memory,
         call kvaddr_to_alenlist().  The specified kernel virtual address is
         converted into a list of physical address/length pairs and the pairs are
         appended to an alenlist.  A handle to the alenlist is returned.  When
         plist is NULL, a new list is created, and this list is returned.
     
         To build an alenlist that describes a buffer in user virtual memory, call
         uvaddr_to_alenlist().  The specified user virtual address for the
         specified length is converted into a list of physical address/length
         pairs and the pairs are appended to an alenlist.  A handle to the
         alenlist is returned.  When plist is NULL, a new list is created, and
         this list is returned.
     
         To build an alenlist that describes a buffer mapped by a buf structure,
         call buf_to_alenlist().  The memory described by the buf is converted
         into a list of physical address/length pairs and the pairs are appended
         to an alenlist.  A handle to the alenlist is returned.  When plist is
         NULL, a new list is created, and this list is returned.
     
    SEE ALSO
         alenlist(D4X), buf(D4), IRIX Device Driver Programmer's Guide
     
    NOTES
         In IRIX 6.3, the declaration of buf_to_alenlist() was omitted from the
         header file.  Declare it manually as follows:
     
          /*  temporary   */
          extern alenlist_t buf_to_alenlist(buf_t *);
     
         This prototype for buf_to_alenlist(), as well as the prototypes shown
         above for uvaddr_to_alenlist(), kvaddr_to_alenlist(), alenlist_append(), and
         alenlist_get() are all different in future releases of IRIX.  There is no simple
         workaround.  These functions are extremely useful, but you should place a
         comment near each one indicating that when porting to IRIX 6.4 or later,
         additional arguments are required.
     
    

PCI Infrastructure Reference Pages

  • Example B-3 displays the reference page for registering and unregistering a PCI driver.

  • Example B-4 displays the reference page about configuration space access.

  • Example B-5 displays the reference page about setting up and using DMA maps.

  • Example B-6 displays the reference page documenting the PCI error handler.

  • Example B-7 displays the reference page for PCI query functions.

  • Example B-8 displays the reference page documenting the PCI interrupt hander.

  • Example B-9 displays the reference page about setting up and using PIO maps.

    Example B-3. pciio(d3)


    NAME
         pciio: pciio_add_attach, pciio_driver_register, pciio_driver_unregister -
         control PCI driver infrastructure
     
    SYNOPSIS
         #include <sys/PCI/pciio.h>
      
         int
         pciio_add_attach(__int32_t (*attach)(vertex_hdl_t),
                  __int32_t (*detach)(vertex_hdl_t),
                          pciio_error_handler_f *error,
                          char  *driver_prefix,
                          int   major)
     
         int
         pciio_driver_register(
                         pciio_vendor_id_t vendor_id,
                         pciio_device_id_t device_id,
                         char *driver_prefix,
                         unsigned flags);
     
         void
         pciio_driver_unregister(char *driver_prefix);
     
         void
         pciio_reset (vertex_hdl_t pconn);
     
       Arguments
         attach      Address of the driver's attach() entry point.
         detach      Address of the driver's detach() entry point, or NULL.
         error       Address of the driver's error() entry point, or NULL.
         driver_prefix
                     The prefix string for the driver's standard entry points as
                     configured in /var/sysgen/system.
         major       Major device number configured for this driver.
         vendor_id
         device_id   Values that the PCI device will present in its configuration
                     space as its vendor and device ID codes.
     
         flags       Normally passed as zero.
     
         pconn       is an appropriate PCI connection point.
     
    DESCRIPTION
         The PCI infrastructure is a package of kernel services used by drivers
         for PCI devices to set up services for their devices.  These services
         include:
     
         o    Manipulating the PCI configuration space for the device (see
              pciio_config(D3)).
     
         o    Constructing physical addresses to use for PIO access to the device
              (see pciio_pio(D3)).
     
         o    Constructing PCI addresses for the device to use for DMA access to
              memory (see pciio_dma(D3)).
     
         o    Arranging for a function to be called when the device requests
              interrupt service (see pciio_intr(D3)).
     
         o    Arranging for a function to be called when an error occurs during
              PIO to, or DMA from the device (see pciio_error(D3)).
     
         o    Accessing useful fields in some otherwise opaque data structures
              (see pciio_get(D3)).
     
       Driver Registration
         The first function a PCI driver must call, usually in the init() entry
         point, is pciio_add_attach(), to introduce the driver to the PCI
         infrastructure.
     
         NOTE:  This function is only called in the IRIX 6.3 implementation on the
         O2 workstation.  The call to this function can be enclosed in a
         conditional test of the variable _EARLY_PCI, as follows:
     
          xxxx_init() {
          #ifdef _EARLY_PCI /* 6.3, must do add_attach */
             pciio_add_attach(xxxx_attach,NULL,NULL,"xxxx",XXXX_MAJNO);
          #endif
     
         pciio_driver_register() is used by a PCI driver in IRIX 6.3 or any later
         release to inform the infrastructure that it handles all PCI devices
         designated by specified device_id and vendor_id values.  The
         infrastructure associates the specified ID numbers with the specified
         device driver prefix.  When a device with these IDs is discovered, the
         infrastructure calls the attach entry point for the driver with that
         driver prefix, passing the hardware graph connection point vertex as the
         only parameter.  This connection point is then used in most calls to the
         infrastructure to identify the PCI device of interest.
     
         A loadable device driver calls pciio_driver_register() from its reg()
         entry point.  A driver prelinked into the kernel should also make the
         call from reg() for consistency, but may call from the init() entry point
         if necessary.
     
         Device drivers may make multiple calls with different vendor and device
         ID numbers, representing several compatible PCI devices.
     
         Wildcard values PCIIO_VENDOR_ID_NONE and PCIIO_DEVICE_ID_NONE may be used
         if cards from any vendor or cards with any device code are of supported.
         When both vendor and device are wildcarded, the attach() routine is
         called for every PCI device connected to the system.
     
         When a loadable device driver calls pciio_driver_register(), one or more
         calls to the driver's attach() function can occur before the
         infrastructure returns control to the caller.  On some large systems, the
         attach() calls can be executed by other threads and possibly on other
         processors, concurrently with continued execution of the reg() entry
         point.
     
         pciio_driver_unregister() should be called by any unloadable device
         driver from within the driver's unreg() entry point.  This triggers calls
         to the driver's detach() entry point, and removes the association between
         the driver and any vendor and device IDs.
     
       Resetting a PCI card
         pciio_reset() is used to attempt to activate the PCI Reset line connected
         to a specific card without affecting other devices on the PCI bus.  When
         reset is possible, the device is reset and basic configuration
         information is reloaded.
     
    EXAMPLES
         Here is how a typical driver might make use of these functions:
     
          static char    pcifoo_prefix[] = "pcifoo_";
          static char    pcifoo_edge[] = "foo";
          #ifdef _EARLY_PCI /* 6.3, must do add_attach, need init() */
          pcifoo_init(void)
          {
             pciio_add_attach(pcifoo_attach,NULL,NULL,"pcifoo",42);
          }
          #endif
          pcifoo_reg(void)
          {
               pciio_driver_register(
                PCIFOO_VENDOR_ID,
                PCIFOO_DEVICE_ID,
                "pcifoo", 0);
          }
          pcifoo_unreg(void)
          {
               pciio_driver_unregister("pcifoo");
          }
     
    SEE ALSO
         pciio_config(D3), pciio_dma(D3), pciio_error(D3), pciio_get(D3),
         pciio_intr(D3), pciio_pio(D3).
    
    
    
    

    Example B-4. pciio_config(d3)


    NAME
         pciio_config: pciio_config_get, pciio_config_set - access PCI
         Configuration register
     
    SYNOPSIS
         #include <sys/PCI/pciio.h>
     
         u_int32_t
         pciio_config_get(
                       volatile unsigned char *bus_addr,
                       int cfg_reg)
     
         int
         pciio_config_set(
                       volatile unsigned char *bus_addr,
                       int cfg_reg,
                       __int32_t value)
     
       Arguments
         bus_addr    PIO target address of PCI configuration space for a device,
                     as returned by pciio_piomap_addr() or pciio_piotrans_addr().
     
         cfg_reg     Byte offset of the register of interest in the PCI address
                     space.
     
         value       Value to be written to the specified register.
     
    DESCRIPTION
         Various SGI platforms introduce complexities and restrictions in how
         Configuration Space cycles are generated on the PCI bus.  Some platforms
         may require all PCI Configuration accesses to be done using 32-bit wide
         accesses.  Others require more than a simple load or store to trigger the
         actual cycle, so that configuration access cannot be performed using
         normal PIO loads and stores.  (Both of these restrictions are true of the
         O2 workstation.)
     
         The functions described here were introduced to allow the kernel to
         trigger a PCI bus Configuration Cycle based on a PIO address.
     
         The cfg_reg value specifies the offset of the target value in
         configuration space.  Registers defined by the PCI standard are 1, 2, 3,
         4, or 8 bytes, but these functions support only 1-4 bytes.  (Eight-byte
         registers can be fetched in two calls.)
    
         Because configuration space is accessed in 32-bit units on 32-bit
         boundaries, when reg specifies a standard PCI configuration register,
         pciio_config_get() shifts and masks appropriately to return just the
         value of the register.  Similarly, pciio_config_set() executes a read-
         merge-write operation to place the value data in the correct portion of
         the word.
     
       Standard PCI Configuration Registers
         To access vendor-specific registers, specify the base address in PCI
         configuration space, bearing in mind that PCI places the least
         significant data in the lowest offset.
     
         The following constants are declared in the header file
         sys/PCI/PCI_defs.h for use as the cfg_reg value to specify a standard
         register in the Type 00 PCI configuration space:
     
                PCI_CFG_VENDOR_ID
                PCI_CFG_DEVICE_ID
                PCI_CFG_COMMAND
                PCI_CFG_STATUS
     
                PCI_CFG_REV_ID
                PCI_CFG_BASE_CLASS
                PCI_CFG_SUB_CLASS
                PCI_CFG_PROG_IF
     
                PCI_CFG_CACHE_LINE
                PCI_CFG_LATENCY_TIMER
                PCI_CFG_HEADER_TYPE
                PCI_CFG_BISTPCI_CFG_BIST
     
                PCI_CFG_BASE_ADDR_0
                PCI_CFG_BASE_ADDR_1
                PCI_CFG_BASE_ADDR_2
                PCI_CFG_BASE_ADDR_3
                PCI_CFG_BASE_ADDR_4
                PCI_CFG_BASE_ADDR_5
                PCI_CFG_BASE_ADDR(n)
     
                PCI_CFG_CARDBUS_CIS
                PCI_CFG_SUBSYS_VEND_ID
                PCI_CFG_SUBSYS_ID
                PCI_EXPANSION_ROM
     
                PCI_INTR_LINE
                PCI_INTR_PIN
                PCI_MIN_GNT
                PCI_MAX_LAT
     
         Use PCI_CFG_VEND_SPECIFIC to specify the first vendor-specific register
         word.
     
         The configuration functions deduce the length of the register from its
         offset.  For a nonstandard or vendor-specific register the functions
         assume a 32-bit word.
     
    NOTES
         Logical byte order is preserved; that is, the most significant byte of a
         (big-endian) program word is sent as the most significant byte of a
         (little-endian) PCI bus word.
     
         Writes to registers of less than 32 bits are synthesized by reading the
         word containing the register, modifying the proper bits in the word, then
         rewriting the entire bus word.  The read-modify-write code knows about
         the special handling of the STATUS register.  However, if other registers
         in your card's configuration space are sensitive to being rewritten, you
         should access them using full four-byte-wide accesses, manipulating the
         word data appropriately.
     
       Subsequent Releases
         In all platforms supported IRIX 6.4, configuration access is possible
         with normal PIO.  Accordingly, these functions were not defined in IRIX
         6.4 and will result in an unresolved extern if porting is attempted.  In
         releases after IRIX 6.4, these configuration access functions are
         returned, but with different arguments and with more capabilities:
     
         o   They take the register size as an argument, from 1-8 bytes.
     
         o   They do not require use of a mapped PIO address, but take only the
             vertex handle of the device.
     
         It is possible to code configuration access macros so that they compile
         properly in all releases from 6.3 onward.  The macro code would be
         similar to the following:
     
              /* PCI Config Space Access Macros
              ** for source compatibility in drivers
              ** that need to use the same source
              ** for IRIX 6.3, IRIX 6.4, and IRIX 6.5
              **
              ** PCI_CFG_BASE(conn)
              ** PCI_CFG_GET(conn,base,offset,type)
              ** PCI_CFG_SET(conn,base,offset,type,value)
              **
              ** Use PCI_CFG_BASE once during attach to get the
              ** base value to be used for the specific device.
              ** Later, use PCI_CFG_GET to read and PCI_CFG_SET
              ** to write config registers.
              **
              ** NOTE: Irix 6.3 determines the size of the register
              ** directly on its own, based on the layout of a Type 00
              ** PCI Configuration Space Header. If you specify a
              ** nonstandard size, you will get different results
              ** depending on the system revision number.
              */
              #if IRIX6_3
              #define   PCI_CFG_BASE(c)          pciio_piotrans_addr(c,0,PCIIO_SPACE_CFG,0,256,0)
              #define   PCI_CFG_GET(c,b,o,t)     pciio_config_get(b,o)
              #define   PCI_CFG_SET(c,b,o,t,v)   pciio_config_set(b,o,v)
              #elif IRIX6_4
              #define   PCI_CFG_BASE(c)          pciio_piotrans_addr(c,0,PCIIO_SPACE_CFG,0,256,0)
              #define   PCI_CFG_GET(c,b,o,t)     ((*(t *)((char *)(b)+(o))))
              #define   PCI_CFG_SET(c,b,o,t,v)   ((*(t *)((char *)(b)+(o))) = v)
              #else     /* starting in IRIX 6.5 */
              #define   PCI_CFG_BASE(c)          NULL
              #define   PCI_CFG_GET(c,b,o,t)     pciio_config_get(c,o,sizeof(t))
              #define   PCI_CFG_SET(c,b,o,t,v)   pciio_config_set(c,o,sizeof(t),v)
              #endif
     
         The macros would be used approximately as follows:
     
              pcifoo_attach(vertex_hdl_t conn)
              {
                   void * config_base = PCI_CFG_BASE(conn);
                   ...
                   /* retrieve current device revision */
                   foo_soft->fs_revision =
                        PCI_CFG_GET(conn, config_base, PCI_CFG_REV_ID, uchar);
                   ...
                   /* write 0x5555AAAA test pattern to first
                   ** vendor specific register */
                   PCI_CFG_SET(conn, config_base, PCI_CFG_VEND_SPECIFIC, uint32_t,
                        0x5555AAAA);
              }
     
    SEE ALSO
         pciio(D3), pciio_config(D3), pciio_dma(D3), pciio_error(D3),
         pciio_get(D3), pciio_intr(D3).  pciio_pio(D3).
    
    
    
    

    Example B-5. pciio_dma(d3)


    NAME
         pciio_dma: pciio_dmatrans_addr, pciio_dmatrans_list, pciio_dmamap_alloc,
         pciio_dmamap_addr, pciio_dmamap_list, pciio_dmamap_done,
         pciio_dmamap_free, - manage DMA on PCI bus
     
    SYNOPSIS
         #include <sys/PCI/pciio.h>
     
         iopaddr_t
         pciio_dmatrans_addr(
              vertex_hdl_t vhdl,
              device_desc_t desc,
              iopaddr_t addr,
              size_t size,
              unsigned flags)
     
         alenlist_t
         pciio_dmatrans_list(
              vertex_hdl_t vhdl,
              device_desc_t desc,
              alenlist_t list,
              unsigned flags)
     
         pciio_dmamap_t
         pciio_dmamap_alloc(
              vertex_hdl_t vhdl,
              device_desc_t desc,
              size_t max,
              unsigned flags)
     
         iopaddr_t
         pciio_dmamap_addr(
              pciio_dmamap_t map,
              iopaddr_t addr,
              size_t size);
     
         alenlist_t
         pciio_dmamap_list(
              pciio_dmamap_t map,
              alenlist_t list);
     
         void
         pciio_dmamap_done(pciio_dmamap_t map)
     
         void
         pciio_dmamap_free(pciio_dmamap_t map)
     
       Arguments
         addr The DMA buffer address in system physical address space.
     
         desc A device descriptor, usually zero.
     
         flags
              Attributes of the mapping.
     
         list An address/length list as prepared by one of the alenlist
              construction functions (see alenlist(D4)).
     
         map  A dma map as returned by pciio_dmamap_alloc().
     
         max     The maximum range of addresses this map will cover at any one time.
     
         size    The size of the mapped buffer in bytes.
     
         vhdl    The device connection point as passed to the attach() entry
                 point.
     
    DESCRIPTION
         When a device driver wishes to use Direct Memory Access (DMA) to
         communicate with a device, the system needs to have a chance to set up
         any appropriate mapping registers.  The work to be done varies with the
         available hardware and with the version of IRIX.
     
         The functions described here provide an abstract interface to the
         creation of DMA mapping objects that is consistent across most hardware.
         These functions always do the least possible work given the available
         hardware.  (In IRIX 6.3 and on the O2 hardware, the amount of work is
         minimal.  In later releases and on multiprocessor platforms, the work can
         be considerable.)
     
         There are two different models for setting up a DMA map, one simple but
         fallible and the other more general.  In both models, the final goal is
         to retrieve an address in PCI bus address space that can be used by a PCI
         device to write into, or read from, system physical memory.
     
       Simple Model
         The simple model provides permanent mappings through fixed mapping
         resources that may or may not exist in a given system at a given time.
         pciio_dmatrans_addr() is the one-stop shopping place for using system
         fixed shareable mapping resources to construct a DMA address.  Such
         resources are not always available, in which case, the function returns
         NULL.
     
         pciio_dmatrans_list() is similar, but operates on an address/length list
         of blocks of memory and returns a list of blocks in PCI address space.
     
         When they work, these functions allow the driver to set up DMA with the
         fewest complications.  Typically the functions always succeed in some
         platforms (those having simple hardware mappings of PCI to memory), and
         always fail in other platforms (where multiple layers of hardware
         mappings must be configured dynamically).  However, drivers that hope to
         be portable must be coded as if the functions could succeed or fail
         alternately in the same system.
     
       General Model
         It is not always possible to establish DMA mappings using common shared
         system resources, so the concept of a DMA channel that preallocates
         scarce mapping resources is provided.
     
         Such a channel is allocated using pciio_dmamap_alloc(), which is given
         the maximum size to be mapped.  pciio_dmamap_addr() or
         pciio_dmamap_list() is then applied to the map to actually establish the
         proper mappings for a DMA target.  Given the base address and block size
         of the buffer for DMA (or a list of buffers), the functions hand back the
         base PCI address to use for accessing that buffer (or a list of PCI
         addresses).
     
         When all DMA to a given buffer (or list) is complete, pciio_dmamap_done()
         should be called to idle any mapping hardware (and possibly flush out any
         pipes or buffers along the path that might do unexpected things when
         mapping registers are modified).  Later, pciio_dmamap_addr() or
         pciio_dmamap_list() can again be called, specifying the same or a
         different buffer area.
     
         When a driver is completely finished with a DMA channel -- because the
         channel is used only for initialization of the device, because the
         driver's close() entry point is called so it is known that the device
         will be idle for some time, or because the device or the driver is being
         shut down -- the DMA channel resources should be released using
         pciio_dmamap_free().
     
       DMA Attribute Flags
         The following attributes can be specified in the flags argument:
     
         PCIIO_DMAMAP_CMD  This map is primarily for transfer of device-to-driver
                           command and status data.  (Flag ignored in this
                           release.)
     
         PCIIO_DMAMAP_DATA This map is primarily for transfer of user data.  (Flag
                           ignored in this release.)
     
         PCIIO_DMAMAP_A64  The device is capable of using PCI-64 addressing.
                           (Flag ignored in this release.)
     
         PCIIO_DMAMAP_LITTLEEND
                           demands that any byte-swapping hardware along this DMA
                           path be organized so that an ordered stream of bytes
                           from the device are deposited in order in system
                           memory.  This is the typical setting for data streams.
                           If this endianness cannot be supplied, then the service
                           call fails.
     
         PCIIO_DMAMAP_BIGEND
                           demands that any byte-swapping hardware along this DMA
                           path be initialized so that 32-bit quantities on PCI-
                           bus 32-bit boundaries maintain their binary values.
                           This is the typical setting for command-type
                           transactions because command words exchanged with a
                           little-endian PCI device retain their binary values.
                           If this endianness cannot be supplied, then the service
                           call fails.
     
         When PCIIO_DMAMAP_LITTLEEND is used, the bytes of multibyte values
         embedded in input data are found at their original offsets.  Multibyte
         values from little-endian devices may require programmed swapping before
         use.
     
         When PCIIO_DMAMAP_BIGEND is used,
     
         o   Single bytes in input data are found at the offset the device places
             them, exclusive-or with 3.
     
         o   16-bit quantities in input data are found at the offset used by the
             device, exclusive-or with 2, and do not need to be byteswapped.
     
         o   32-bit values are found at the expected offset, and do not need to be
             byteswapped.
     
         o   64-bit values are found at the expected offset, and their 32-bit
             halves need to be swapped before use.
     
       Source Compatibility With IRIX 6.4
         In IRIX 6.4 and subsequent releases, the flag names given above are
         renamed, and additional useful flags are defined and supported.  For easy
         source compatibility with later releases, insert the following code:
     
              #ifdef _EARLY_PCI
              #  define PCIIO_DMA_CMD PCIIO_DMAMAP_CMD
              #  define PCIIO_DMA_DATA PCIIO_DMAMAP_DATA
              #  define PCIIO_DMA_A64 PCIIO_DMAMAP_A64
              #  define PCIIO_BYTE_STREAM PCIIO_DMAMAP_LITTLEEND
              #  define PCIIO_WORD_VALUES PCIIO_DMAMAP_BIGEND
              #endif
     
         Then use the defined names such as PCIIO_BYTE_STREAM when creating DMA
         maps, instead of the flag values mentioned above.
     
         When porting to a later release of IRIX, be sure to read this reference
         page for that release to see what flags are supported.
     
         In IRIX 6.4 and later releases, pciio_dmamap_list() takes a third
         argument, flags, with the same meaning as the other flags arguments.  In
         order to simplify source compatibility with later releases, you could use
         the _EARLY_PCI identifier to code a macro in this form:
     
              #ifdef _EARLY_PCI
              #   define PCIIO_DMAMAP_LIST(a,b) pciio_dmamap_list(a,b)
              #else
              #   define PCIIO_DMAMAP_LIST(a,b) pciio_dmamap_list(a,b,0)
              #endif
     
         However, the flags supported in later releases are sufficiently useful
         you should probably recode the calls to use nonzero flags.
     
    EXAMPLES
         Here is one way that a driver might make use of dmamap and dmatrans
         calls.
     
              #ifdef _EARLY_PCI
              #  define PCIIO_DMA_CMD PCIIO_DMAMAP_CMD
              #  define PCIIO_DMA_DATA PCIIO_DMAMAP_DATA
              #  define PCIIO_DMA_A64 PCIIO_DMAMAP_A64
              #  define PCIIO_BYTE_STREAM PCIIO_DMAMAP_LITTLEEND
              #  define PCIIO_WORD_VALUES PCIIO_DMAMAP_BIGEND
              #endif
              pcifoo_attach(vertex_hdl_t vhdl)
              {
                   pciio_dmamap_t command_map;
                   iopaddr_t command_dma;
                   struct pcifoo_regs  *reg_pio;
                   struct pcifoo_ring  *command_ring;
                   ...
                   /*
                    * This driver has decided to use a dmamap
                    * to get to its command rings, which contain
                    * things like DMA addresses and counts; we
                    * set PCIIO_WORD_VALUES so we don't have to
                    * byteswap the 32-bit values.
                    *
                    * We still have to swap the upper and lower
                    * halves of the 64-bit values.
                    */
                   /* allocate the channel
                    */
                   command_map = pciio_dmamap_alloc(
                        vhdl, 0,
                        RINGBYTES,
                        PCIIO_DMA_CMD | PCIIO_WORD_VALUES);
                   command_dma = pciio_dmamap_addr(
                        command_map,
                        kvtophys(command_ring),
                        RINGBYTES);
                   /* tell the device where it can find
                    * it's command rings.
                    */
                   reg_pio->command_dma = command_dma;
                   ...
              }
              {
                   caddr_t   data_buffer;
                   size_t    data_size;
                   ...
                   data_dma = pciio_dmatrans_addr(
                        vhdl, 0,
                        kvtophys(data_buffer), data_size,
                        PCIIO_DMA_DATA | PCIIO_DMA_A64 | PCIIO_BYTE_STREAM);
                   command_ring->data_dma_lo = data_dma & 0xFFFFFFFF;
                   command_ring->data_dma_hi = data_dma >> 32;
                   command_ring->data_dma_size = data_size;
                   command_ring->ready = 1;
              }
     
    SEE ALSO
         alenlist(D3), pciio(D3), pciio_config(D3), pciio_error(D3),
         pciio_get(D3), pciio_intr(D3), pciio_pio(D3).
     
    DIAGNOSTICS
         pciio_dmatrans_addr() returns zero if shared (fixed) resources can not be
         used to construct a valid PCI address that maps to the desired range of
         physical addresses.  (Fixed resources are always available in IRIX 6.3
         for O2, but may not be in other systems.)
     
         pciio_dmatrans_list() returns a null pointer if any of the requested
         physical address blocks can not be reached using shared fixed resources,
         or if unable to allocate a return list.
     
         pciio_dmamap_alloc() returns a null pointer if resources can not be
         allocated to establish DMA mappings of the requested size, or if the
         parameters are inconsistant.
     
         pciio_dmamap_addr() returns zero if the specified target address can not
         be mapped using the specified DMA channel.  This would usually be due to
         specifying a target block that is outside the previously specified target
         area or is larger than the previously specified maximum mapping size.  It
         may also return a null pointer if the DMA channel is currently in use and
         has not been marked idle by a call to pciio_dmamap_done().
     
         pciio_dmamap_list() can return a null pointer for all the reasons
         mentioned above, or if it is unable to allocate the return list.
    
    
    
    

    Example B-6. pciio_error(d3)


    NAME
         pciio_error - IRIX 6.3 PCI error interface
     
    SYNOPSIS
         #include <sys/PCI/pciio.h>
     
         int
         typedef int
         (pciio_error_handler_f)( vertex_hdl_t   vhdl,
                         int           error_code,
                         ioerror_mode_t mode,
                         ioerror_t     *ioerror);
     
       Arguments
         vhdl    The connection point of the PCI device with the error.
     
         error_code
                 Bit-set describing the error type.
     
         mode    Code for the type of device access when the error was detected.
     
         ioerror Error mode structure with more information.
     
    DESCRIPTION
         A PCI device driver can pass the address of an error-handling function to
         the pciio_add_attach() function documented in pciio(d3).  The error-
         handling function must has the prototype shown; that is, it must agree
         with type pciio_error_handler_f.  When a NULL is passed to
         pciio_add_attach(), the PCI infrastructure handles all errors.
     
         When an error occurs, the handler is called.  The error_code value
         contains a set of the bits defined in sys/mace.h, as follows:
     
              #define PERR_MASTER_ABORT            0x80000000
              #define PERR_TARGET_ABORT            0x40000000
              #define PERR_DATA_PARITY_ERR         0x20000000
              #define PERR_RETRY_ERR               0x10000000
              #define PERR_ILLEGAL_CMD             0x08000000
              #define PERR_SYSTEM_ERR              0x04000000
              #define PERR_INTERRUPT_TEST          0x02000000
              #define PERR_PARITY_ERR              0x01000000
              #define PERR_OVERRUN                 0x00800000
              #define PERR_RSVD                    0x00400000
              #define PERR_MEMORY_ADDR             0x00200000
              #define PERR_CONFIG_ADDR             0x00100000
              #define PERR_MASTER_ABORT_ADDR_VALID 0x00080000
              #define PERR_TARGET_ABORT_ADDR_VALID 0x00040000
              #define PERR_DATA_PARITY_ADDR_VALID  0x00020000
              #define PERR_RETRY_ADDR_VALID        0x00010000
     
         The mode value is one of the following, defined in sys/PCI/pci_compat.h
         (which in turn is included by sys/PCI/pciio.h):
     
              typedef enum {
                   MODE_DEVPROBE,      /* Probing mode. Errors not fatal */
                   MODE_DEVERROR,      /* Error while system is running */
                   MODE_DEVREENABLE    /* Reenable pass        */
              }ioerror_mode_t;
     
         The structure addressed by ioerror is also declared in
         sys/PCI/pci_compat.h and contains numerous fields that may give the
         handler additional information.
     
         The handler returns nonzero when it has handled the error adequately.
         The handler returns zero when it wants the PCI infrastructure to handle
         the error.
     
    NOTES
         This error-handling interface is unique to IRIX 6.3 for O2. In IRIX 6.4
         and onward, a different interface is used:  the driver calls a function
         pciio_error_register(), to register an error handler that takes fewer,
         and different, arguments.
     
         The error-handling function in IRIX 6.3 receives information that is
         unique to the hardware of the O2 workstation.  For example, all the
         declarations in sys/mace.h are unique to the MACE chip that manages bus
         and memory access in the O2.  None of this information is relevant in
         other platforms such as the OCTANE workstation.  None of this information
         is available in later releases of IRIX.
     
         There is no simple way to make PCI error-handling in IRIX 6.3 source-
         compatible with later releases.
     
    SEE ALSO
         pciio(D3), pciio_config(D3), pciio_dma(D3), pciio_get(D3),
         pciio_intr(D3), pciio_pio(D3).
     
    
    
    
    

    Example B-7. pciio_get(d3)


    NAME
         pciio_get: pciio_info_get, pciio_info_bus_get, pciio_intr_cpu_get,
         pciio_dma_dev_get, pciio_info_dev_get, pciio_intr_dev_get,
         pciio_pio_dev_get, pciio_dma_slot_get, pciio_info_slot_get,
         pciio_pio_slot_get, pciio_pio_mapsz_get, pciio_pio_pciaddr_get,
         pciio_pio_space_get, pciio_info_vendor_id_get, pciio_info_device_id_get,
         pciio_info_function_get - interrogate PCI infrastructure
     
    SYNOPSIS
         #include <sys/PCI/pciio.h>
     
         vertex_hdl_t
         pciio_intr_cpu_get(pciio_intr_t intr)
     
         vertex_hdl_t
         pciio_intr_dev_get(pciio_intr_t intr)
     
         vertex_hdl_t
         pciio_pio_dev_get(pciio_piomap_t piomap)
     
         pciio_slot_t
         pciio_pio_slot_get(pciio_piomap_t piomap)
     
         pciio_space_t
         pciio_pio_space_get(pciio_piomap_t piomap)
     
         iopaddr_t
         pciio_pio_pciaddr_get(pciio_piomap_t piomap)
     
         ulong
         pciio_pio_mapsz_get(pciio_piomap_t piomap)
     
         vertex_hdl_t
         pciio_dma_dev_get(pciio_dmamap_t dmamap)
     
         pciio_slot_t
         pciio_dma_slot_get(pciio_dmamap_t dmamap)
     
         pciio_info_t
         pciio_info_get(vertex_hdl_t vhdl)
     
         vertex_hdl_t
         pciio_info_dev_get(pciio_info_t info)
      
         pciio_slot_t
         pciio_info_bus_get(pciio_info_t info)
     
         pciio_function_t
         pciio_info_function_get(pciio_info_t info)
     
         pciio_slot_t
         pciio_info_slot_get(pciio_info_t info)
     
         pciio_vendor_id_t
         pciio_info_vendor_id_get(pciio_info_t info)
     
         pciio_device_id_t
         pciio_info_device_id_get(pciio_info_t info)
     
       Arguments
         intr    A PCI interrupt object handle returned by pciio_intr_alloc().
     
         piomap  A PCI PIO map returned by pciio_piomap_alloc().
     
         dmamap  is a pciio_dmamap_t that was created by pciio_dmamap_alloc()
     
         vhdl    A pci connection point in the hardware graph, obtained as the
                 parameter to the attach call.
     
         info    A PCI info object returned by pciio_info_get().
     
    DESCRIPTION
         These routines are used to pull specific useful bits of information out
         of the various opaque data structures used by the PCI infrastructure.
         Few drivers will need to make use of these routines, but having them
         available might save the driver from doing extra bookkeeping.
     
       Interrupt Queries
         Two functions fetch parameters from an interrupt object:
     
         o   pciio_intr_dev_get() returns the connection point of the interrupt
             device.
     
         o   pciio_intr_cpu_get() returns the CPU that is the target of interrupts
             for that PCI bus.
     
       PIO Map Queries
         Several functions return items based on a PIO map (see pciio_pio(D3)):
     
         o   pciio_pio_dev_get() returns the connection point of the mapped
             device.
      
         o   pciio_pio_mapsz_get() returns the map maximum size.
     
         o   pciio_pio_pciaddr_get() returns the base address specified for the
             map.
     
         o   pciio_pio_space_get() returns the target address space that was
             specified.
     
         o   pciio_pio_slot_get() returns the slot number on the PCI bus for a
             device.
     
       DMA Map Queries
         Two functions return items based on a DMA as map (see pciio_dma(D3)):
     
         o   pciio_dma_dev_get() returns the connection point of the mapped
             device.
     
         o   pciio_dma_slot_get() returns the slot number on the PCI bus for a
             device.
     
       Info Structure Queries
         The PCI infrastructure stores a version-dependent information structure
         in the connection point for a PCI device.  Several functions are provided
         to retrieve and interrogate this structure.  Those most likely to be
         useful to a device driver are:
     
         o   pciio_info_get() returns a handle to the information structure.  The
             driver can save this handle at attach time to avoid the small
             overhead of looking it up each time it is needed.
     
         o   pciio_info_dev_get() returns the vertex handle of the connection
             point (from which the information structure was originally
             retrieved).
     
         o   pciio_info_bus_get() returns the bus number, always 0 unless the
             system has more than one PCI bus.  Bus numbers are arbitrary, not
             necessarily sequential.
     
         o   pciio_info_slot_get() returns the PCI card slot number of the device.
     
         o   pciio_info_function_get() returns the PCI function number, 0 unless
             the device is part of a multifunction card.
     
         o   pciio_info_vendor_id_get() returns the vendor ID configuration value
             of the device.
     
         o   pciio_info_device_id_get() returns the device ID configuration value
             of the device.
     
    SEE ALSO
         pciio(D3), pciio_config(D3), pciio_dma(D3), pciio_error(D3),
         pciio_intr(D3), pciio_pio(D3).
     
    DIAGNOSTICS
         pciio_info_get() returns NULL if there is no pciio info structure
         attached to that vertex.
     
         Do not pass info as NULL to any of these functions, that would cause a
         kernel panic.
    
    
    
    

    Example B-8. pciio_intr(d3)


    NAME
         pciio_intr: pciio_intr_alloc, pciio_intr_connect, pciio_intr_disconnect,
         pciio_intr_free - manage PCI Interrupts
     
    SYNOPSIS
         #include <sys/PCI/pciio.h>
     
         pciio_intr_t
         pciio_intr_alloc(
              vertex_hdl_t vhdl,
              device_desc_t desc,
              pciio_intr_line_t lines,
              vertex_hdl_t owner)
     
         int
         pciio_intr_connect(
              pciio_intr_t intr,
              intr_func_t func,
              intr_arg_t arg,
              void *thread)
     
         void
         pciio_intr_disconnect(pciio_intr_t intr)
     
         void
         pciio_intr_free(pciio_intr_t intr)
     
       Arguments
         arg     A parameter to pass to func() when this particular interrupt
                 occurs, commonly a pointer to a driver-private data structure.
     
         desc    A device descriptor containing an interrupt priority level.
     
         func    The function to perform interrupt service.
     
         intr    The interrupt channel handle returned by pciio_intr_alloc().
     
         lines   Specifies one or more of the PCI Interrupt pins used by the
                 device.
     
         owner   An appropriate vertex handle to use when printing messages about
                 this particular interrupt, and is usually a vertex created by the
                 device driver.
     
         vhdl    The PCI device connection point as passed to the driver attach()
                 entry point.
     
         thread  Reserved, should be NULL.
     
    DESCRIPTION
         When a device driver wishes to accept interrupt events from a device, the
         system needs to make sure that there is a path from the PCI interrupt pin
         to the appropriate CPU interrupt hardware.  This is split into two phases
         -- establishing the channel and connecting a service function -- so that
         the service function can be changed or disconnected without losing the
         allocated hardware resources.
     
         The driver is responsible for connecting an interrupt handler when the
         device needs one, and for disconnecting the handler when it does not.
     
         The interrupt delivery mechanism depends on the address of the interrupt
         function.  It is important to disconnect interrupts before a driver
         unloads, otherwise the PCI infrastructure might call a nonexistent
         function.  (A driver cannot be auto-loaded when an interrupt occurs.)
     
         The necessary sequence of calls is based on the use of the driver entry
         points, as follows.
     
         At the reg() or init() entry point the driver registers to handle a class
         of PCI devices, triggering attach() calls.
     
         At the attach() entry, the driver calls pciio_intr_alloc() to establish
         interrupt connectivity between the device and the processor.  The
         designated interrupts are disabled at this point.  If interrupts can
         occur and are needed at this time, a call to pciio_intr_connect() enables
         interrupts and directs them to the designated handler.
     
         At the unload() entry, the driver text is going to be removed, so it is
         important for all interrupts to be disconnected by calling
         pciio_intr_disconnect() as appropriate.  It is not necessary to call
         pciio_intr_free() at this time.
     
         Some devices do not require interrupt service when they are not open.
         Leaving an interrupt allocated but not connected keeps the interrupt
         disabled, possibly reducing impact on the system from handling interrupts
         from devices that do not actually need service.
     
         If this is the situation, then the scenario above may be somewhat
         simplified:
     
         attach()    Allocate the interrupt to establish a connection and disable
                     the interrupt.  Only connect the interrupt if interrupts are
                     required as part of device initialization; then disconnect
                     it.
     
         open()      If the interrupt is not yet connected, connect it.
     
         close()     No processes have the device open; disconnect the interrupt
                     when all pending I/O is complete or purged.
     
         unload()    The driver is not called to unload when one of its devices is
                     open, so no interrupts should be connected.
     
       Specifying PCI Interrupt Lines
         The lines parameter is formed by or-ing together appropriate flags:
     
              PCIIO_INTR_LINE_A
              PCIIO_INTR_LINE_B
              PCIIO_INTR_LINE_C
              PCIIO_INTR_LINE_D
     
       Specifying the Device Descriptor
         The desc value must be the address of a device_desc_t structure in which
         the intr_swlevel field has been assigned a value.  The data type of this
         field, pl_t, is declared in sys/types.h.  The header sys/ddi.h includes
         sys/types.h, and also declares several external objects of type pl_t.
     
         In IRIX 6.4 and later, it is not required to supply a device descriptor
         to this function; NULL may be passed instead.  Also in IRIX 6.4 and
         later, a default device descriptor is readily available by a function
         call.  The following example shows how to code the call to
         pciio_intr_alloc() in a source-compatible way:
     
              foo_attach(vertex_hdl_t connpt)
              {
              #ifdef _EARLY_PCI
                  device_desc_t work_desc = {0};
              #endif
                  device_desc_t *pdesc;
                  vertex_hdl_t  foo_chardev; /* our char device */
                  pciio_intr_t intr;
                  ...
                  /* allocate an interrupt object. This device
                   * uses both INTA and INTB, and routes both
                   * interrupts to the same function.
                   */
              #ifdef _EARLY_PCI
                  pdesc = &work_desc;
                  pdesc -> intr_swlevel = plhi; /* in ddi.h */
              #else
                  pdesc = NULL; /* or: pdesc = device_desc_default_get(connpt) */
              #endif
                   intr = pciio_intr_alloc(connpt, pdesc,
                        PCIIO_INTR_LINE_A | PCIIO_INTR_LINE_B,
                        foo_chardev);
                   pciio_intr_connect(intr,
                        foo_int_hdlr,
                        (intr_arg_t)foo_dev_info,
                        (void *)0);
                  ...
     
    SEE ALSO
         pciio(D3), pciio_config(D3), pciio_dma(D3), pciio_error(D3),
         pciio_get(D3), pciio_pio(D3).
     
    DIAGNOSTICS
         pciio_intr_alloc() returns a null value if it can not allocate memory.
     
         pciio_intr_connect() returns a zero for success or a negative value on
         failure.  Since the channel is preallocated, the only interesting failure
         for this function is the attempt to use a null interrupt handle value.
    
    
    
    

    Example B-9. pciio_pio(d3)


    NAME
         pciio_pio: pciio_piotrans_addr, pciio_piomap_alloc, pciio_piomap_addr,
         pciio_piomap_done, pciio_piomap_free, pciio_piospace_alloc,
         pciio_piospace_free - programmed I/O to PCI bus
     
    SYNOPSIS
         #include <sys/PCI/pciio.h>
     
         caddr_t
         pciio_piotrans_addr(
              vertex_hdl_t vhdl,
              device_desc_t desc,
              pciio_space_t space,
              iopaddr_t addr,
              size_t size,
              unsigned flags)
     
         pciio_piomap_t
         pciio_piomap_alloc(
              vertex_hdl_t vhdl,
              device_desc_t desc,
              pciio_space_t space,
              iopaddr_t addr,
              size_t size,
              size_t max,
              unsigned flags)
     
         caddr_t
         pciio_piomap_addr(
              pciio_piomap_t map,
              iopaddr_t addr,
              size_t size);
     
         void
         pciio_piomap_done(pciio_piomap_t map)
     
         void
         pciio_piomap_free(pciio_piomap_t map)
     
         iopaddr_t
         pciio_piospace_alloc(
              vertex_hdl_t vhdl,
              device_desc_t desc,
              pciio_space_t space,
              size_t size,
              size_t align)
     
         void
         pciio_piospace_free(
              vertex_hdl_t vhdl,
              pciio_space_t space,
              iopaddr_t addr,
             size_t size)
     
       Arguments
         addr    The offset within the given space.
     
         align   A desired alignment in PCI address space.
     
         desc    A device descriptor with the one field intr_swlevel set to plhi.
     
         flags   Flags describing the use of the PIO map.
     
         max     The maximum size within space to be mapped at any one time.
     
         map     The map address returned by pciio_piomap_alloc().
     
         mapp    A pointer variable to receive the address of an allocated map.
     
         size    The size of the region to be mapped.
     
         space   Specifies the target PCI address space.
     
         vhdl    The PCI connection point as given to the attach() entry point.
     
    DESCRIPTION
         When a device driver wishes to use Programmed I/O (PIO) to communicate
         with a device, the system needs to have a chance to set up any
         appropriate mapping registers.  The work to be done varies with the
         available hardware and with the version of IRIX.  The functions described
         here provide an abstract interface that is consistent across most
         hardware.  These functions always do the least possible work given the
         available hardware.
     
         There are two models for setting up a PIO map, one simple but fallible,
         and one more general.  In both models, the final goal is to retrieve a
         physical address that, when used as the operand of a store or fetch, will
         access a word in PCI bus address space rather than in CPU memory address
         space.
     
       Simple Model
         The simple model provides permanent mappings through fixed mapping
         resources that may or may not exist in a given system at a given time.
         pciio_piotrans_addr() attempts to use shared hardware resources to
         construct a physical address that, whenever used, routes the transaction
         to the proper target on the PCI bus.  This is not always possible.  When
         it is not, the function returns NULL.
     
         When it works, pciio_piotrans_addr() allows the driver to do PIO with the
         fewest complications.  Typically pciio_piotrans_addr() always succeeds in
         some platforms (those having a simple mapping of PCI bus to memory), and
         always fails in others (where multiple layers of hardware mappings must
         be configured dynamically).  However, a driver that uses it should be
         coded as if it could succeed or fail alternately in the same system
         (which it could).
     
       General Model
         It is not always possible to establish a PIO mapping using common shared
         system resources, so the concept of a PIO channel that preallocates
         scarce mapping resources is provided.
     
         Such a channel is allocated using pciio_piomap_alloc(), which is given
         the limits of the region that will be mapped and the maximum size to be
         mapped at any time within that region.  The model assumes that many
         channels may be created, but that not all channels will be actively in
         use at any time.
     
         pciio_piomap_addr() is used to actually establish the proper mappings for
         a PIO target.  Given the offset within the target address space and the
         size of the region for PIO, it returns the base address to be used for
         accessing that region.
     
         After all PIO transactions to that region are executed,
         pciio_piomap_done() should be called to idle any mapping hardware and
         possibly to flush out any pipes or buffers along the path that might do
         unexpected things when mapping registers are modified.
     
         Later, pciio_piomap_addr() can again be called, specifying the same or a
         new target area.
     
         When a driver is completely finished with a PIO channel -- either because
         the channel is used only for initialization of the device, or because the
         device or the driver is being shut down -- the PIO channel resources
         should be released using pciio_piomap_free().
     
       Utility Functions
         pciio_piospace_alloc() can be used to find a block of PCI address space
         that nobody else is using, which can then be used for whatever the device
         and driver wish to use it for.  The PCI infrastructure preallocates PCI
         address space regions based on the device configuration BASE registers at
         the time the bus is discovered.  As a result this function is needed only
         to manage a device that does not completely declare its address space
         usage in its hardware configuration registers.
     
         pciio_piospace_free() is used to release an allocation made previously by
         pciio_piospace_alloc().
     
       Specifying PCI Address Spaces
         The space parameter takes on of the following values:
     
         PCIIO_SPACE_WIN(n)
                     specifies one of the regions on the PCI bus decoded by the
                     PCI card's BASE registers.  The address specified is the
                     offset within the decoded area, and the entire PIO region
                     must fit within the decoded area.
     
         PCIIO_SPACE_CFG
                     requests a pointer handle that can be used to access the
                     configuration space for the card, via the pciio_config_get()
                     and pciio_config_set() functions documented in
                     pciio_config(D3).
     
         Other space types are rarely needed but can be used:
     
         PCIIO_SPACE_IO
                     requests a mapping into somewhere in the PCI bus I/O address
                     space.
     
         PCIIO_SPACE_MEM
                     requests a mapping into somewhere in the PCI bus Memory
                     space.  Since PCI bus address space is preallocated by the
                     kernel, this is a dangerous function to use.
     
       PIO Attribute Flags
         There are no useful values for the flags argument in this release.
         Specify the argument as 0.  In IRIX 6.4 and onward, some usable flags are
         available.
     
    EXAMPLES
         Here is a contrived example of how one might initialize a very strange
         PCI card.  It is not clear that this would be the best way to do it, but
         it does give an example of the relationship between the various
         functions.
     
              pcifoo_attach(vertex_hdl_t vhdl)
              {
                   unsigned  *cfgspace;
                   struct pcifoo_devregs *devregs;
                   pciio_piomap_t pmap;
                   pciio_piomap_t cmap;
                   struct pcifoo_chan_config *tune;
                   ...
                   /* Get the configuration space base
                    * pointer.
                    */
                   cfgspace = pciio_piotrans_addr
                        (vhdl, 0, PCIIO_SPACE_CFG, 0, 256, 0);
                   if (cfgspace == NULL) {
                        cmn_err(CE_ALERT,
                             "pcifoo_attach: pciio_piotrans_addr failed");
                        return -1;
                   }
                   /* Get a pointer we can use for PIO to our
                    * device's control registers. This call
                    * attempts to use fixed shared resources,
                    * but will allocate unshared mapping resources
                    * if required.
                    */
                  devregs = pciio_pio_addr
                        (vhdl, 0,
                        PCIIO_SPACE_WIN(0), 0,
                        sizeof (struct pcifoo_devregs),
                        &pmap, 0);
                   if (devregs == NULL) {
                        cmn_err(CE_ALERT,
                             "pcifoo_attach: pciio_pio_addr failed");
                        return -1;
                   }
                   /* save cfgspace and devregs for use;
                    * save pmap for pciio_dmamap_free
                    * call if/when we are unregistered.
                    */
                   ...
                   /* pretend our "channel" space is too big
                    * to successfully map with piotrans, so
                    * we have to use piomap, and that it is
                    * too big for us to get it in one call
                    * to piomap_addr.
                    */
                   cmap = pciio_piomap_alloc(vhdl, 0,
                        PCIIO_SPACE_WIN(2), 0, CHAN_SEP * CHANS,
                        sizeof (struct pcifoo_chan_config), 0);
                   for (chan = 0; chan < chans; ++chan) {
                        tune = (struct pcifoo_chan_config *)
                             pciio_piomap_addr(cmap, CHAN_SEP * chan,
                                  sizeof (struct pcifoo_chan_config));
                        /* now fiddle with this particular channel */
                        tune->chan = chan + 2;
                        tune->volume = 5;
                        tune->balance = 0;
                        pciio_piomap_done(cmap);
                   }
                   pciio_piomap_free(cmap);
                   ...
              }
     
    NOTES
         It is not necessary to separately establish mappings for each individual
         PIO target register.  It is customary and more efficient to use a single
         mapping to cover the entire register set of a device.
     
    SEE ALSO
         pciio(D3), pciio_config(D3), pciio_dma(D3), pciio_error(D3),
         pciio_get(D3), pciio_intr(D3).
     
    DIAGNOSTICS
         pciio_piotrans_addr() returns a null pointer when shared (fixed)
         resources can not be used to construct a valid physical address that maps
         to the desired range of PCI addresses.
     
         pciio_pio_addr() returns a null pointer when the target PCI address can
         not be mapped either with shared (fixed) resources, or with unshared
         mapping resources.  If this happens, and the object being mapped is
         large, it might be possible to set up mappings to smaller regions of the
         target space.
     
         pciio_piomap_alloc() returns a null pointer when resources can not be
         allocated to establish PIO mappings to the described region, or if the
         function parameters are inconsistant.
     
         pciio_piomap_addr() returns a null pointer when the specified target
         address can not be mapped using the specified PIO channel.  This would
         usually be due to specifying a target block that is outside the
         previously specified target area or is larger than the previously
         specified maximum mapping size.  It may also return a null pointer if the
         PIO channel is currently in use and has not been marked idle by a
         pciio_piomap_done() call.