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.
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:
If you pass 0 for the flags, the default is PCI_SEARCH_VENDEV.
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; }
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()