2003-12-27 Martin Mares <mj@ucw.cz>
+ * lib/pci.h (PCIADDR_PORT_FMT): Use %llx instead of %Lx, because the latter
+ is not supported by all C libraries.
+
+ * Makefile: Always enter the lib directory (remember that we don't have
+ full dependecies for the library in the top-level Makefile; hmmm, another
+ thing to rewrite some day).
+
+ * lib/sysfs.c: Added Linux sysfs access method based on the patch
+ written by Matthew Wilcox <willy@fc.hp.com>.
+
+ * lib/proc.c: Renamed the access method name from "/proc/bus/pci" to "Linux-proc".
+
+ * lib/pread.h: The hacks to support pread on various versions
+ of Linux libc moved there.
+
+ * lib/proc.c (proc_setup): The return value of snprintf() varies
+ between glibc versions, so we need to check both for a negative
+ values and for too large values.
+
* Removed last few references to the "Linux PCI Utilities", the
package is pretty cross-platform now :)
all: $(PCILIB) lspci setpci lspci.8 setpci.8 update-pciids update-pciids.8 pci.ids
-$(PCILIB): $(PCIINC)
+$(PCILIB): $(PCIINC) force
$(MAKE) -C lib all
+force:
+
lib/config.h:
cd lib && ./configure $(SHAREDIR) $(VERSION)
get-ids:
cp ~/tree/pciids/pci.ids pci.ids
-.PHONY: all clean distclean install uninstall get-ids
+.PHONY: all clean distclean install uninstall get-ids force
"available only to root" things in the man page.
- pci.ids: "Unknown mass storage controller" -> "Mass storage controller".
+
+- names.c: rewrite
+
+- support for domains:
+ - pci_get_dev
+ - filters
PCILIB=libpci.a
+ifdef HAVE_PM_LINUX_SYSFS
+OBJS += sysfs.o
+endif
+
ifdef HAVE_PM_LINUX_PROC
OBJS += proc.o
endif
ranlib $@
access.o: access.c $(INCL)
-i386-ports.o: i386-ports.c $(INCL)
-proc.o: proc.c $(INCL)
+i386-ports.o: i386-ports.c $(INCL) i386-io-hurd.h i386-io-linux.h i386-io-sunos.h
+proc.o: proc.c $(INCL) pread.h
+sysfs.o: sysfs.c $(INCL) pread.h
generic.o: generic.c $(INCL)
syscalls.o: syscalls.c $(INCL)
fbsd-device.o: fbsd-device.c $(INCL)
static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = {
NULL,
+#ifdef HAVE_PM_LINUX_SYSFS
+ &pm_linux_sysfs,
+#else
+ NULL,
+#endif
#ifdef HAVE_PM_LINUX_PROC
&pm_linux_proc,
#else
case $sys in
Linux)
case $rel in
- 2.[1-9]*|[3-9]*) echo_n " proc"
+ 2.[1-9]*|[3-9]*) echo_n "sysfs proc"
+ echo >>$c '#define HAVE_PM_LINUX_SYSFS'
echo >>$c '#define HAVE_PM_LINUX_PROC'
echo >>$c '#define HAVE_LINUX_BYTEORDER_H'
echo >>$c '#define PATH_PROC_BUS_PCI "/proc/bus/pci"'
+ echo >>$c '#define PATH_SYS_BUS_PCI "/sys/bus/pci"'
ok=1
;;
esac
int pci_link_dev(struct pci_access *, struct pci_dev *);
extern struct pci_methods pm_intel_conf1, pm_intel_conf2, pm_linux_proc,
- pm_fbsd_device, pm_aix_device, pm_nbsd_libpci, pm_dump;
+ pm_fbsd_device, pm_aix_device, pm_nbsd_libpci, pm_dump, pm_linux_sysfs;
#define PCIADDR_PORT_FMT "%04lx"
#else
typedef unsigned long long u64;
-#define PCIADDR_T_FMT "%016Lx"
-#define PCIADDR_PORT_FMT "%04Lx"
+#define PCIADDR_T_FMT "%016llx"
+#define PCIADDR_PORT_FMT "%04llx"
#endif
typedef u64 pciaddr_t;
#else
enum pci_access_type {
/* Known access methods, remember to update access.c as well */
PCI_ACCESS_AUTO, /* Autodetection (params: none) */
+ PCI_ACCESS_SYS_BUS_PCI, /* Linux /sys/bus/pci (params: path) */
PCI_ACCESS_PROC_BUS_PCI, /* Linux /proc/bus/pci (params: path) */
PCI_ACCESS_I386_TYPE1, /* i386 ports, type 1 (params: none) */
PCI_ACCESS_I386_TYPE2, /* i386 ports, type 2 (params: none) */
struct pci_dev {
struct pci_dev *next; /* Next device in the chain */
- word bus; /* Higher byte can select host bridges */
+ word domain; /* PCI domain (host bridge) */
+ byte bus; /* Bus inside domain */
byte dev, func; /* Device and function */
/* These fields are set by pci_fill_info() */
--- /dev/null
+/*
+ * The PCI Library -- Portable interface to pread() and pwrite()
+ *
+ * Copyright (c) 1997--2003 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/*
+ * We'd like to use pread/pwrite for configuration space accesses, but
+ * unfortunately it isn't simple at all since all libc's until glibc 2.1
+ * don't define it.
+ */
+
+#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
+/* glibc 2.1 or newer -> pread/pwrite supported automatically */
+
+#elif defined(i386) && defined(__GLIBC__)
+/* glibc 2.0 on i386 -> call syscalls directly */
+#include <asm/unistd.h>
+#include <syscall-list.h>
+#ifndef SYS_pread
+#define SYS_pread 180
+#endif
+static int pread(unsigned int fd, void *buf, size_t size, loff_t where)
+{ return syscall(SYS_pread, fd, buf, size, where); }
+#ifndef SYS_pwrite
+#define SYS_pwrite 181
+#endif
+static int pwrite(unsigned int fd, void *buf, size_t size, loff_t where)
+{ return syscall(SYS_pwrite, fd, buf, size, where); }
+
+#elif defined(i386)
+/* old libc on i386 -> call syscalls directly the old way */
+#include <asm/unistd.h>
+static _syscall5(int, pread, unsigned int, fd, void *, buf, size_t, size, u32, where_lo, u32, where_hi);
+static _syscall5(int, pwrite, unsigned int, fd, void *, buf, size_t, size, u32, where_lo, u32, where_hi);
+static int do_read(struct pci_dev *d UNUSED, int fd, void *buf, size_t size, int where) { return pread(fd, buf, size, where, 0); }
+static int do_write(struct pci_dev *d UNUSED, int fd, void *buf, size_t size, int where) { return pwrite(fd, buf, size, where, 0); }
+#define HAVE_DO_READ
+
+#else
+/* In all other cases we use lseek/read/write instead to be safe */
+#define make_rw_glue(op) \
+ static int do_##op(struct pci_dev *d, int fd, void *buf, size_t size, int where) \
+ { \
+ struct pci_access *a = d->access; \
+ int r; \
+ if (a->fd_pos != where && lseek(fd, where, SEEK_SET) < 0) \
+ return -1; \
+ r = op(fd, buf, size); \
+ if (r < 0) \
+ a->fd_pos = -1; \
+ else \
+ a->fd_pos = where + r; \
+ return r; \
+ }
+make_rw_glue(read)
+make_rw_glue(write)
+#define HAVE_DO_READ
+#endif
+
+#ifndef HAVE_DO_READ
+#define do_read(d,f,b,l,p) pread(f,b,l,p)
+#define do_write(d,f,b,l,p) pwrite(f,b,l,p)
+#endif
#include <sys/types.h>
#include "internal.h"
-
-/*
- * We'd like to use pread/pwrite for configuration space accesses, but
- * unfortunately it isn't simple at all since all libc's until glibc 2.1
- * don't define it.
- */
-
-#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
-/* glibc 2.1 or newer -> pread/pwrite supported automatically */
-
-#elif defined(i386) && defined(__GLIBC__)
-/* glibc 2.0 on i386 -> call syscalls directly */
-#include <asm/unistd.h>
-#include <syscall-list.h>
-#ifndef SYS_pread
-#define SYS_pread 180
-#endif
-static int pread(unsigned int fd, void *buf, size_t size, loff_t where)
-{ return syscall(SYS_pread, fd, buf, size, where); }
-#ifndef SYS_pwrite
-#define SYS_pwrite 181
-#endif
-static int pwrite(unsigned int fd, void *buf, size_t size, loff_t where)
-{ return syscall(SYS_pwrite, fd, buf, size, where); }
-
-#elif defined(i386)
-/* old libc on i386 -> call syscalls directly the old way */
-#include <asm/unistd.h>
-static _syscall5(int, pread, unsigned int, fd, void *, buf, size_t, size, u32, where_lo, u32, where_hi);
-static _syscall5(int, pwrite, unsigned int, fd, void *, buf, size_t, size, u32, where_lo, u32, where_hi);
-static int do_read(struct pci_dev *d UNUSED, int fd, void *buf, size_t size, int where) { return pread(fd, buf, size, where, 0); }
-static int do_write(struct pci_dev *d UNUSED, int fd, void *buf, size_t size, int where) { return pwrite(fd, buf, size, where, 0); }
-#define HAVE_DO_READ
-
-#else
-/* In all other cases we use lseek/read/write instead to be safe */
-#define make_rw_glue(op) \
- static int do_##op(struct pci_dev *d, int fd, void *buf, size_t size, int where) \
- { \
- struct pci_access *a = d->access; \
- int r; \
- if (a->fd_pos != where && lseek(fd, where, SEEK_SET) < 0) \
- return -1; \
- r = op(fd, buf, size); \
- if (r < 0) \
- a->fd_pos = -1; \
- else \
- a->fd_pos = where + r; \
- return r; \
- }
-make_rw_glue(read)
-make_rw_glue(write)
-#define HAVE_DO_READ
-#endif
-
-#ifndef HAVE_DO_READ
-#define do_read(d,f,b,l,p) pread(f,b,l,p)
-#define do_write(d,f,b,l,p) pwrite(f,b,l,p)
-#endif
+#include "pread.h"
static void
proc_config(struct pci_access *a)
if (a->cached_dev != d || a->fd_rw < rw)
{
char buf[1024];
+ int e;
if (a->fd >= 0)
close(a->fd);
- if (snprintf(buf, sizeof(buf), "%s/%02x/%02x.%d", a->method_params[PCI_ACCESS_PROC_BUS_PCI],
- d->bus, d->dev, d->func) == sizeof(buf))
+ e = snprintf(buf, sizeof(buf), "%s/%02x/%02x.%d",
+ a->method_params[PCI_ACCESS_PROC_BUS_PCI],
+ d->bus, d->dev, d->func);
+ if (e < 0 || e >= (int) sizeof(buf))
a->error("File name too long");
a->fd_rw = a->writeable || rw;
a->fd = open(buf, a->fd_rw ? O_RDWR : O_RDONLY);
}
struct pci_methods pm_linux_proc = {
- "/proc/bus/pci",
+ "Linux-proc",
proc_config,
proc_detect,
proc_init,
--- /dev/null
+/*
+ * The PCI Library -- Configuration Access via /sys/bus/pci
+ *
+ * Copyright (c) 2003 Matthew Wilcox <willy@fc.hp.com>
+ * Copyright (c) 1997--2003 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include "internal.h"
+#include "pread.h"
+
+static void
+sysfs_config(struct pci_access *a)
+{
+ a->method_params[PCI_ACCESS_SYS_BUS_PCI] = PATH_SYS_BUS_PCI;
+}
+
+static inline char *
+sysfs_name(struct pci_access *a)
+{
+ return a->method_params[PCI_ACCESS_SYS_BUS_PCI];
+}
+
+static int
+sysfs_detect(struct pci_access *a)
+{
+ if (access(sysfs_name(a), R_OK))
+ {
+ a->debug("Cannot open %s", sysfs_name(a));
+ return 0;
+ }
+ a->debug("...using %s", sysfs_name(a));
+ return 1;
+}
+
+static void
+sysfs_init(struct pci_access *a)
+{
+ a->fd = -1;
+}
+
+static void
+sysfs_cleanup(struct pci_access *a)
+{
+ if (a->fd >= 0)
+ {
+ close(a->fd);
+ a->fd = -1;
+ }
+}
+
+#define OBJNAMELEN 1024
+static void
+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");
+}
+
+static int
+sysfs_get_value(struct pci_dev *d, char *object)
+{
+ struct pci_access *a = d->access;
+ int fd, n;
+ char namebuf[OBJNAMELEN], buf[256];
+
+ sysfs_obj_name(d, object, namebuf);
+ fd = open(namebuf, O_RDONLY);
+ if (fd < 0)
+ a->error("Cannot open %s: %s", namebuf, strerror(errno));
+ n = read(fd, buf, sizeof(buf));
+ close(fd);
+ if (n < 0)
+ a->error("Error reading %s: %s", namebuf, strerror(errno));
+ if (n >= (int) sizeof(buf))
+ a->error("Value in %s too long", namebuf);
+ buf[n] = 0;
+ return strtol(buf, NULL, 0);
+}
+
+static void
+sysfs_get_resources(struct pci_dev *d)
+{
+ struct pci_access *a = d->access;
+ char namebuf[OBJNAMELEN], buf[256];
+ FILE *file;
+ int i;
+
+ sysfs_obj_name(d, "resource", namebuf);
+ file = fopen(namebuf, "r");
+ if (!file)
+ a->error("Cannot open %s: %s", namebuf, strerror(errno));
+ for (i = 0; i < 8; i++)
+ {
+ unsigned long long start, end, size;
+ if (!fgets(buf, sizeof(buf), file))
+ break;
+ if (sscanf(buf, "%llx %llx", &start, &end) != 2)
+ a->error("Syntax error in %s", namebuf);
+ if (start != (unsigned long long)(pciaddr_t) start ||
+ end != (unsigned long long)(pciaddr_t) end)
+ {
+ a->warning("Resource %d in %s has a 64-bit address, ignoring", namebuf);
+ start = end = 0;
+ }
+ if (start)
+ size = end - start + 1;
+ else
+ size = 0;
+ if (i < 7)
+ {
+ d->base_addr[i] = start;
+ d->size[i] = size;
+ }
+ else
+ {
+ d->rom_base_addr = start;
+ d->rom_size = size;
+ }
+ }
+ fclose(file);
+}
+
+static void sysfs_scan(struct pci_access *a)
+{
+ char dirname[1024];
+ DIR *dir;
+ struct dirent *entry;
+ int n;
+
+ n = snprintf(dirname, sizeof(dirname), "%s/devices", sysfs_name(a));
+ if (n < 0 || n >= (int) sizeof(dirname))
+ a->error("Directory name too long");
+ dir = opendir(dirname);
+ if (!dir)
+ a->error("Cannot open %s", dirname);
+ while ((entry = readdir(dir)))
+ {
+ struct pci_dev *d;
+ unsigned int dom, bus, dev, func;
+
+ /* ".", ".." or a special non-device perhaps */
+ if (entry->d_name[0] == '.')
+ continue;
+
+ d = pci_alloc_dev(a);
+ if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4)
+ a->error("sysfs_scan: Couldn't parse entry name %s", entry->d_name);
+ d->domain = dom;
+ d->bus = bus;
+ d->dev = dev;
+ d->func = func;
+ d->hdrtype = pci_read_byte(d, PCI_HEADER_TYPE) & 0x7f;
+ if (!a->buscentric)
+ {
+ sysfs_get_resources(d);
+ d->vendor_id = sysfs_get_value(d, "vendor");
+ d->device_id = sysfs_get_value(d, "device");
+ d->irq = sysfs_get_value(d, "irq");
+ d->known_fields = PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES;
+ }
+ pci_link_dev(a, d);
+ }
+ closedir(dir);
+}
+
+static int
+sysfs_setup(struct pci_dev *d, int rw)
+{
+ struct pci_access *a = d->access;
+
+ if (a->cached_dev != d || a->fd_rw < rw)
+ {
+ char namebuf[OBJNAMELEN];
+ if (a->fd >= 0)
+ close(a->fd);
+ sysfs_obj_name(d, "config", namebuf);
+ a->fd_rw = a->writeable || rw;
+ a->fd = open(namebuf, a->fd_rw ? O_RDWR : O_RDONLY);
+ if (a->fd < 0)
+ a->warning("Cannot open %s", namebuf);
+ a->cached_dev = d;
+ a->fd_pos = 0;
+ }
+ return a->fd;
+}
+
+static int sysfs_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int fd = sysfs_setup(d, 0);
+ 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)
+ {
+ d->access->warning("sysfs_read: tried to read %d bytes at %d, but got only %d", len, pos, res);
+ return 0;
+ }
+ return 1;
+}
+
+static int sysfs_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+ int fd = sysfs_setup(d, 1);
+ int res;
+
+ if (fd < 0)
+ return 0;
+ res = do_write(d, fd, buf, len, pos);
+ if (res < 0)
+ {
+ d->access->warning("sysfs_write: write failed: %s", strerror(errno));
+ return 0;
+ }
+ else if (res != len)
+ {
+ d->access->warning("sysfs_write: tried to write %d bytes at %d, but got only %d", len, pos, res);
+ return 0;
+ }
+ return 1;
+}
+
+static void sysfs_cleanup_dev(struct pci_dev *d)
+{
+ struct pci_access *a = d->access;
+
+ if (a->cached_dev == d)
+ {
+ a->cached_dev = NULL;
+ close(a->fd);
+ a->fd = -1;
+ }
+}
+
+struct pci_methods pm_linux_sysfs = {
+ "Linux-sysfs",
+ sysfs_config,
+ sysfs_detect,
+ sysfs_init,
+ sysfs_cleanup,
+ sysfs_scan,
+ pci_generic_fill_info,
+ sysfs_read,
+ sysfs_write,
+ NULL, /* init_dev */
+ sysfs_cleanup_dev
+};