目录
在一个系统中,如何访问pcie外设?
在linux 内核中已经封装了接口,不必关系具体实现,直接用即可;针对X86, 则可通过常用的寄存器进行访问。
通用接口层注册
作为通用的pci设备扫描及读写工具,pci utils对此接口层进行了同一的封装,具体代码在
lib/ init.c 中。 根据不同的编译宏,可以选择不同的底层访问接口。例如针对带有linux sysfs的,则通过sysfs接口进行访问。
static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = {
NULL,
#ifdef PCI_HAVE_PM_LINUX_SYSFS
&pm_linux_sysfs,
#else
NULL,
#endif
#ifdef PCI_HAVE_PM_LINUX_PROC
&pm_linux_proc,
#else
NULL,
#endif
#ifdef PCI_HAVE_PM_INTEL_CONF
&pm_intel_conf1,
&pm_intel_conf2,
#else
NULL,
NULL,
#endif
#ifdef PCI_HAVE_PM_FBSD_DEVICE
&pm_fbsd_device,
#else
NULL,
#endif
#ifdef PCI_HAVE_PM_AIX_DEVICE
&pm_aix_device,
#else
NULL,
#endif
#ifdef PCI_HAVE_PM_NBSD_LIBPCI
&pm_nbsd_libpci,
#else
NULL,
#endif
#ifdef PCI_HAVE_PM_OBSD_DEVICE
&pm_obsd_device,
#else
NULL,
#endif
#ifdef PCI_HAVE_PM_DUMP
&pm_dump,
#else
NULL,
#endif
#ifdef PCI_HAVE_PM_DARWIN_DEVICE
&pm_darwin,
#else
NULL,
#endif
#ifdef PCI_HAVE_PM_SYLIXOS_DEVICE
&pm_sylixos_device,
#else
NULL,
#endif
#ifdef PCI_HAVE_PM_HURD_CONF
&pm_hurd,
#else
NULL,
#endif
#ifdef PCI_HAVE_PM_WIN32_CFGMGR32
&pm_win32_cfgmgr32,
#else
NULL,
#endif
};
具体接口层实现
以通过 sysfs访问pcie为例,其代码在 lib/sysfs.c
static int sysfs_read(struct pci_dev *d, int pos, byte *buf, int len)
{
int fd = sysfs_setup(d, SETUP_READ_CONFIG);
int res;
if (fd < 0)
return 0;
res = do_read(d, fd, buf, len, pos);
if (res < 0)
{
d->access->warning("sysfs_read: read failed: %s", strerror(errno));
return 0;
}
else if (res != len)
return 0;
return 1;
}
设备节点
sysfs_setup(struct pci_dev *d, int intent)
{
struct pci_access *a = d->access;
char namebuf[OBJNAMELEN];
if (a->cached_dev != d || (intent == SETUP_WRITE_CONFIG && !a->fd_rw))
{
sysfs_flush_cache(a);
a->cached_dev = d;
}
if (intent == SETUP_READ_VPD)
{
if (a->fd_vpd < 0)
{
sysfs_obj_name(d, "vpd", namebuf);
a->fd_vpd = open(namebuf, O_RDONLY);
/* No warning on error; vpd may be absent or accessible only to root */
}
return a->fd_vpd;
}
if (a->fd < 0)
{
sysfs_obj_name(d, "config", namebuf); //这个接口用于确定pcie设备sysfs config的节点位置
a->fd_rw = a->writeable || intent == SETUP_WRITE_CONFIG;
a->fd = open(namebuf, a->fd_rw ? O_RDWR : O_RDONLY);
if (a->fd < 0)
a->warning("Cannot open %s", namebuf);
a->fd_pos = 0;
}
return a->fd;
}
即sysfs中通过此config文件来访问pcie配置空间。
/sys/bus/pci/devices/0000:00:01.0$ ll config
-rw-r--r-- 1 root root 256 May 16 00:12 config
sysfs_obj_name(struct pci_dev *d, char *object, char *buf)
{
int n = snprintf(buf, OBJNAMELEN, "%s/devices/%04x:%02x:%02x.%d/%s",
sysfs_name(d->access), d->domain, d->bus, d->dev, d->func, object);
if (n < 0 || n >= OBJNAMELEN)
d->access->error("File name too long");
}
读取或者写入
通过在lib/pread.h 中的pread pwrite进行读写
/* glibc 2.1 or newer -> pread/pwrite supported automatically */
系统中glibc版本查询:
ldd --version
ldd (Ubuntu GLIBC 2.27-3ubuntu1.2) 2.27
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
参考
5. Accessing PCI device resources through sysfs — The Linux Kernel documentation