pci_attach_device()

Attach a driver to a PCI device

Synopsis:

#include <hw/pci.h>

void* pci_attach_device(
         void* handle,
         uint32_t flags,
         uint16_t idx,
         struct pci_dev_info* info );

Arguments:

handle
A handle that identifies the device. The first time you call this function, set handle to NULL. This function returns a handle that you can use in a subsequent call to allocate resources for the device.
flags
Flags that tell the PCI server how you want it to handle resources, which resources to scan for, and which resources to allocate; see Flags, below.
idx
The index of the device: 0 for the first device, 1 for the second, and so on.
info
A pointer to a pci_dev_info structure (see below) that specifies the class code, vendor/device ID, or bus number and device/function number that you want to scan for. The function fills in this structure with information about the device.

Library:

libc

Use the -l c option to qcc to link against this library. This library is usually included automatically.

Description:

The pci_attach_device() function attaches a driver to a PCI device.

You must successfully call pci_attach() before calling any of the other PCI functions.

Typically drivers use this function to attach themselves to a PCI device, so that other drivers can't attach to the same device. If you specify the PCI_SHARE flag (see Flags, below), then multiple drivers can attach to the same device.

The server can scan based on a class code, vendor/device ID, or bus number and device/function number. To control the server scanning, initialize the appropriate fields of the info structure and set the appropriate flags.

When you first attach to an uninitialized device, the PCI server assigns all the I/O ports, memory and IRQs required for the device. It also does the IRQ routing for you. Once this has completed successfully, it fills in all these values into your pci_dev_info structure to return these values to your application.

When a driver attaches to a device, the PCI server allocates the necessary resources for the device from procnto using the rsrcdbmgr* calls. On X86 BIOS systems, these resources are normally allocated by the BIOS, but on non-x86 systems, these resources have to be allocated from procnto.

You can detach the device by passing its handle to pci_detach_device() . If you call pci_detach() , any resources that pci_attach_device() allocates are freed.

pci_dev_info structure

This function fills in a pci_dev_info structure that describes an occurrence of a device.

The pci_attach_device() function doesn't map any of the I/O or memory regions into the process's address space. The addresses returned in the pci_dev_info structure are all physical addresses.

This structure has the following members:

uint16_t DeviceId
The device ID (input/output). For a list of supported device IDs, see <hw/pci_devices.h>.
uint16_t VendorId
The vendor ID (input/output). For a list of supported vendor IDs, see <hw/pci_devices.h>.
uint16_t SubsystemId
The subsystem ID (output).
uint16_t SubsystemVendorId
The subsystem vendor ID (output).
uint8_t BusNumber
The bus number (input/output).
uint8_t DevFunc
The device/function number (input/output).
uint8_t Revision
The device revision (output).
uint32_t Class
The class code (input/output). For a list of class codes, see <hw/pci.h>. This field is an ORed combination of a class code and a subclass code (e.g. PCI_CLASS_DISPLAY | PCI_SUBCLASS_DISPLAY_XGA).
uint32_t Irq
The interrupt number (output).
uint64_t CpuIoTranslation
The CPU-to-PCI translation value (pci_addr = cpu_addr - translation).
uint64_t CpuMemTranslation
The CPU-to-PCI memory translation (pci_addr = cpu_addr - translation).
uint64_t CpuIsaTranslation
The CPU-to-ISA memory translation (pci_addr = cpu_addr - translation).
uint64_t CpuBmstrTranslation
The translation from the CPU busmaster address to the PCI busmaster address (pci_addr = cpu_addr + translation).
uint64_t PciBaseAddress [6]
The PCI base address (array of six uint64_t items).
This function decodes bits 1 and 2 to see whether the register is 32 or 64 bits wide, hence the 64-bit values for the base registers.
uint64_t CpuBaseAddress [6]
The CPU base address (an array of six uint64_t items).

Some platforms translate addresses across PCI bridges, so that there's one address on the PCI side of the bridge and another on the CPU side. Under x86, the PciBaseAddress and CpuBaseAddress are the same, but under other platforms, these will be different. In your user application you should always use the CpuBaseAddress.

uint32_t BaseAddressSize [6]
The size of the base address aperture into the board (an array of six uint32_t items).
uint64_t PciRom
The PCI ROM address.
uint64_t CpuRom
The CPU ROM address.
uint32_t RomSize
The size of the aperture into the board.

Flags

The flags parameter tells the PCI server how resources are to be handled, which resources to scan for, and which resources to allocate.

These bits control how resources are handled:

PCI_SHARE
Allow resources to be shared with other drivers. If this isn't set, no other driver can attach to the device.
PCI_PERSIST
Resources persist after the device is detached.

The following bits ask the PCI server to scan for a device based on the fields that you specified in the structure pointed to by info:

PCI_SEARCH_VEND
VendorID
PCI_SEARCH_VENDEV
DeviceId and VendorId
PCI_SEARCH_CLASS
Class
PCI_SEARCH_BUSDEV
BusNumber and DevFunc

These bits specify which members of the structure the server should initialize:

PCI_INIT_IRQ
Irq
PCI_INIT_ROM
PciRom and CpuRom
PCI_INIT_BASE0PCI_INIT_BASE5
The specified entries of the PciBaseAddress and CpuBaseAddress arrays
PCI_INIT_ALL
All members except PciRom and CpuRom

The bits also include:

PCI_MASTER_ENABLE
Enable bus mastering on the device.

If you pass 0 for the flags, the default is PCI_SEARCH_VENDEV.

Testing and converting addresses

To facilitate the testing of addresses returned by the PCI server, at least the following macros are defined in the <hw/pci.h> header file:

PCI_IS_IO( address )
Test whether the address is an I/O address.
PCI_IS_MEM( address )
Test whether the address is a memory address.
PCI_IO_ADDR( address )
Convert the address returned by the PCI server to an I/O address.
PCI_MEM_ADDR( address )
Convert the address returned by the PCI server to a memory address.
PCI_ROM_ADDR( address )
Convert the address returned by the PCI server to a ROM address.

For example:

{
    uint64_t    port;

    /* Test the address returned by the pci server */
    if (PCI_IS_IO(addr))
        port = (PCI_IO_ADDR(addr));
}

Returns:

A handle to be used for other pci_* calls associated with a handle, or NULL if an error occurs (errno is set).

Errors:

EBUSY
An application has already attached to the device. If it's safe to share the device, specify PCI_SHARE in the flags field.
EINVAL
The function couldn't attach a resource to the device.
ENODEV
This device wasn't found.

Examples:

Attach to and allocate all resources for the first occurrence of an Adaptec 2940 adapter:

#include <hw/pci.h>
#include <hw/pci_devices.h>
#include <stdio.h>
#include <stdlib.h>

int main( void )
{
    int pidx;
    void* hdl;
    int phdl;
    struct pci_dev_info inf;

    /* Connect to the PCI server */
    phdl = pci_attach( 0 );
    if( phdl == -1 ) {
        fprintf( stderr, "Unable to initialize PCI\n" );

        return EXIT_FAILURE;
    }

    /* Initialize the pci_dev_info structure */
    memset( &inf, 0, sizeof( inf ) );
    pidx = 0;
    inf.VendorId = PCI_VENDOR_ID_ADAPTEC;
    inf.DeviceId = PCI_DEVICE_ID_ADAPTEC_2940F;

    hdl = pci_attach_device( NULL, PCI_INIT_ALL, pidx, &inf );
    if( hdl == NULL ) {
        fprintf( stderr, "Unable to locate adapter\n" );
    } else {
        /* Do something to the adapter */
        pci_detach_device( hdl );
    }

    /* Disconnect from the PCI server */
    pci_detach( phdl );
    
    return EXIT_SUCCESS;
}

Attach to the first occurrence of an Adapter 2940 adapter and allocate resources in a second call:

#include <hw/pci.h>
#include <hw/pci_devices.h>
#include <stdio.h>
#include <stdlib.h>

int main( void )
{
    int pidx;
    void* hdl;
    void* retval;
    int phdl;
    struct pci_dev_info inf;

    phdl = pci_attach( 0 );
    if( phdl == -1 ) {
        fprintf( stderr, "Unable to initialize PCI\n" );

        return EXIT_FAILURE;
    }

    memset( &inf, 0, sizeof( inf ) );
    pidx = 0;
    inf.VendorId = PCI_VENDOR_ID_ADAPTEC;
    inf.DeviceId = PCI_DEVICE_ID_ADAPTEC_2940F;

    hdl = pci_attach_device( NULL, 0, pidx, &inf );
    if( hdl == NULL ) {
        fprintf( stderr, "Unable to locate adapter\n" );
    }

    retval = pci_attach_device( hdl, PCI_INIT_ALL, pidx, &inf );
    if( retval == NULL ) {
        fprintf( stderr, "Unable allocate resources\n" );
    }
    
    pci_detach( phdl );
    
    return EXIT_SUCCESS;
}

Classification:

QNX Neutrino

Safety:
Cancellation point Yes
Interrupt handler No
Signal handler Yes
Thread Yes