devLib2  2.12
PCI Usage

When working with a PCI device the first step is finding the identifiers used by the device. These will likely be included in the card vendor's documentation, but can be verified by observing the OS boot messages or with the devPCIShow() function.

Including this identifying information in your code provides an important safe guard against user mis-configuration. This provides an easy way to prevent your code from trying to access the wrong type of device.

PCI devices are uniquely identified by the triplet bus:device.function. While it is possible to automatically detect cards it is preferable to have a user defined mapping between physical id (b:d.f) and logical id (Asyn port, device ID, etc.). This allows for consistent naming even if cards are added or removed from the system.

For EPICS drivers initialization will usually be done with an IOC shell function. For example:

myPCICardSetup("dac", 2, 1, 0 )

Would set card "dac" to be function 0 of device 1 on bus 2. Since all PCI devices will be automatically configured by the system this is the only piece of information required to access the card.

Below is an example implementation of myPCICardSetup().

static const epicsPCIID mydevids[] = {...}
int
myPCICardSetup(const char* port,
int b,
int d,
int f)
{
volatile void* bar;
devpriv *priv;
if( portExists(port) ) return 1;
if (devPCIFindBDF(mydevids, b,d,f, &dev, 0))
return 2;

The first step is to probe the PCI location to ensure that the card is present. If the location is empty or populated with an unsupported card then we will abort here.

if (devPCIToLocalAddr(dev, 2, &bar, 0))
return 3;

Next we get a pointer for bar #2.

priv=calloc(1, sizeof(mydevpriv));
if (!priv) return 4;
if (devPCIConnectInterrupt(dev, &myisr, priv))
return 5;

Then connect the interrupt service routine.

portCreate(port, priv);
return 0;
}

And that is it.

Note on PCI I/O Types

The PCI bus has the notion of two different I/O address types: Memory Mapped, and Port. This distinction comes from the x86 processor for which these are two separate address spaces which must be accessed using different instructions (load/store vs. out/in).

Each BAR must either be accessed using Memory Mapped I/O (MMIO), or Port I/O (PIO) operations. This can be determined by inspecting the 'ioport' member of the PCIBar structure.

If the value is false (0) then MMIO operations should be used.

If however, the value is true (1) then the pointer returned by devPCIToLocalAddr() needs to be treated as a PIO address. It should be cast to the appropriate type and access using OS defined PIO operations (ie inb/outb).