Access PCI device registry from Linux user space


In case you need to modify registry of some PCI devices from Linux user space you can easily do by access to the /proc/bus/pci path where all PCI devices found on the system are listed.





The first step is to "scan" this path for collect a list of all PCI devices available. Inside this list you can look for the device you want to work on. Usually PCI device is referred using the two standard value DeviceID and VendorID set as first two words of each PCI device registry space (check here for a compete explanation about how a PCI registry space is organized). Faster way to get PCI devices list is to parse the data returned by read the /proc/bus/pci/devices file, you can see and example by typing:

cat /proc/bus/pci/devices

However, as usually happen with different Linux distributions, this file is not present on all systems than the more secure and compatible way is to "manually" scan all the device files by code as show in the following snippet:

struct dirent *pPciFile, *pDevFile;
string BusPath, DevPath;
DIR *pPciDir, *pDevDir;

pPciDir = opendir("/proc/bus/pci");

while((pPciFile = readdir(pPciDir)) != NULL)
{
    if(string(pPciFile->d_name) == "." || string(pPciFile->d_name) == "..") continue;

    BusPath = PciPath + string("/") + string(pPciFile->d_name);

    pDevDir = opendir(BusPath.c_str());

    if(pDevDir != NULL)
    {
        const unsigned int Bus = strtoul(pPciFile->d_name, NULL, 16);

        while((pDevFile = readdir(pDevDir)) != NULL)
        {
            if(string(pDevFile->d_name) == "." || string(pDevFile->d_name) == "..") continue;

            DevPath = BusPath + string("/") + string(pDevFile->d_name);

            hDev = open(DevPath.c_str(), O_RDONLY);

            if(hDev != -1)
            {
                const unsigned int DevNum  = strtoul(string(pDevFile->d_name).substr(0, 2).c_str(), NULL, 16);
                const unsigned int FuncNum = strtoul(string(pDevFile->d_name).substr(3, 1).c_str(), NULL, 16);
                unsigned int RegData;

                lseek(hDev, 0, SEEK_SET);
                read(hDev, &RegData, 4);

                // Here you have the PCI device info, store somewhere...
                unsigned char DeviceBus   = Bus;
                unsigned char DeviceFunc  = PCI_DEVFN(DevNum,FuncNum);
                unsigned short VendorID   = static_cast<unsigned short>(RegData);
                unsigned short DeviceID   = static_cast<unsigned short>(RegData >> 16);

                close(hDev);
            }
        }

        closedir(pDevDir);
    }
}

closedir(pPciDir);

Now you have collected all the required info of all PCI devices you can select through VendorID and DeviceID the device you are interested in. Once found it you can access to the device registry as normal file in read/write mode by composing the file path as follow:

char Path[256];
int hDev;

unsigned int Offset = 50;
unsigned char pData[10]
unsigned int Size = 10;

sprintf(Path, "/proc/bus/pci/%02x/%02x.%x", DeviceBus, PCI_SLOT(DeviceFunc), PCI_FUNC(DeviceFunc));

hDev = open(Path, O_RDWR);

lseek(hDev, Offset, SEEK_SET)
read(hDev, pData, Size);

lseek(hDev, Offset, SEEK_SET)
write(hDev, pData, Size);

close(hDev);

In this example we read and write same 10 bytes data at offset 50. Please note a standard PCI device have a maximum registry space size of 256 bytes, keep attention to don't read/write over this limit.

Comments

Popular posts from this blog

Access GPIO from Linux user space

Android: adb push and read-only file system error

Tree in SQL database: The Nested Set Model