![]() |
![]() |
![]() |
![]() |
Attach a driver to a PCI device
#include <hw/pci.h> void* pci_attach_device( void* handle, uint32_t flags, uint16_t idx, struct pci_dev_info* info );
libc
Use the -l c option to qcc to link against this library. This library is usually included automatically.
The pci_attach_device() function attaches a driver to a PCI device.
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.
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:
![]() |
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. |
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.
The flags parameter tells the PCI server how resources are to be handled, which resources to scan for, and which resources to allocate. If you pass 0 for the flags, the default is PCI_SEARCH_VENDEV.
These bits control how resources are handled:
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:
These bits specify which members of the structure the server should initialize:
The bits also include:
To determine if a device supports MSI or MSI-X, run the pci -v command and look for MSI and MSI-X in the device's capabilities. To do this programatically, you have to traverse the device's capabilities structure (see the “Examples” section below).
![]() |
|
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:
For example:
{ uint64_t port; /* Test the address returned by the pci server */ if (PCI_IS_IO(addr)) port = (PCI_IO_ADDR(addr)); }
A handle to be used for other pci_* calls associated with a handle, or NULL if an error occurs (errno is set).
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; }
This code shows how the devnp-e1000.so driver determines whether or not the device supports MSI or MSI-X:
static int check_capabilities (i82544_dev_t *i82544, uint8_t bus, uint8_t devfn) { uint8_t cap, cap_ptr = 0; uint16_t device, dev_ctrl; int offset; if (pci_read_config8 (bus, devfn, offsetof (struct _pci_config_regs, Capabilities_Pointer), 1, &cap_ptr) != PCI_SUCCESS) return (-1); if (pci_read_config16 (bus, devfn, offsetof (struct _pci_config_regs, Device_ID), 1, &device) != PCI_SUCCESS) return (-1); if (cap_ptr) { while (1) { if (pci_read_config8 (bus, devfn, cap_ptr, 1, &cap)) return (-1); if (cap == PCI_CAP_MSI) i82544->msi_cap = 1; if (cap == PCI_CAP_MSI_X) { i82544->msi_cap = 2; i82544->msix_cap_ptr = cap_ptr; } if ((cap == PCI_CAP_PCI_EXPRESS) && (i82544->max_read != -1)) { offset = offsetof (struct _pci_capability_pci_express, Device_Control); if (pci_read_config16 (bus, devfn, cap_ptr + offset, 1, &dev_ctrl) != PCI_SUCCESS) return (-1); dev_ctrl &= 0x8fff; dev_ctrl |= (i82544->max_read << 12); pci_write_config16 (bus, devfn, cap_ptr + offset, 1, &dev_ctrl); } if (pci_read_config8 (bus, devfn, cap_ptr + 1, 1, &cap_ptr)) return (-1); if (! cap_ptr) { break; } } } return (EOK); } /* ... */ flags = PCI_INIT_ALL | PCI_INIT_IRQ | PCI_MASTER_ENABLE; if (i82544->msi_cap) flags |= (i82544->msi_cap == 1) ? PCI_USE_MSI : PCI_USE_MSIX; if ((i82544->pci_dev_hdl = pci_attach_device (NULL, flags, busindex, &info)) == NULL) { slogf (_SLOGC_NETWORK, _SLOG_ERROR, "pci_attach_device failed"); err = errno; i82544_pci_detach_cleanup (i82544, 3); return (err); }
Safety: | |
---|---|
Cancellation point | Yes |
Interrupt handler | No |
Signal handler | Yes |
Thread | Yes |
pci_attach(), pci_detach(), pci_detach_device(), pci_find_class(), pci_find_device(), pci_present(), pci_read_config(), pci_read_config8(), pci_read_config16(), pci_read_config32(), pci_rescan_bus(), pci_write_config()
pci-bios-v2, startup-apic in the Utilities Reference
![]() |
![]() |
![]() |
![]() |