]> git.ipfire.org Git - thirdparty/pciutils.git/commitdiff
Added sysfs support
authorMartin Mares <mj@ucw.cz>
Sat, 27 Dec 2003 19:42:38 +0000 (19:42 +0000)
committerMartin Mares <mj@ucw.cz>
Fri, 5 May 2006 12:18:16 +0000 (14:18 +0200)
* 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.
git-archimport-id: mj@ucw.cz--public/pciutils--main--2.2--patch-33

ChangeLog
Makefile
TODO
lib/Makefile
lib/access.c
lib/configure
lib/internal.h
lib/pci.h
lib/pread.h [new file with mode: 0644]
lib/proc.c
lib/sysfs.c [new file with mode: 0644]

index 0947e9656b0926e9c6ad459be6d064d371249a67..1e51c6c76d7b2aaf1f01237545f24ae4487ef738 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,24 @@
 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 :)
 
index fb5eeca3805606347c800fb43f83450291cc36ac..f8a869b4c91037f93f2dc94714328c24566935aa 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -31,9 +31,11 @@ export
 
 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)
 
@@ -73,4 +75,4 @@ uninstall: all
 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
diff --git a/TODO b/TODO
index b239f633413e1b57d73d97a1740ec0a0c4f76aa6..86c2b090a0d51347ae4be27de7480e378ec521a1 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2,3 +2,9 @@
   "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
index b48c4ce811ab545566b4080ae1f0a7d2822bd7e0..b0a2de828413f25cc1ae4428ead1e6bd475f53c3 100644 (file)
@@ -8,6 +8,10 @@ INCL=internal.h pci.h config.h
 
 PCILIB=libpci.a
 
+ifdef HAVE_PM_LINUX_SYSFS
+OBJS += sysfs.o
+endif
+
 ifdef HAVE_PM_LINUX_PROC
 OBJS += proc.o
 endif
@@ -53,8 +57,9 @@ $(PCILIB): $(OBJS)
        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)
index c0e04ef2960bf5f502a2dcad38b4c8da00ef0652..55060b5dd79f6c0e0ec6d1fdf6d11cde1e927dc0 100644 (file)
 
 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
index 250350f58ad5ca5efad11aa05f90940bb3f623a2..6fd652a3310d942ecd52bea9c1ded526e7fdf58c 100755 (executable)
@@ -33,10 +33,12 @@ echo_n "Looking for access methods..."
 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
index 8f1f8f8a37608fe0f6eed9f22dac11dae7282b25..b18ccd17acfc2401b7ab7a473b35128d9a91c0b1 100644 (file)
@@ -99,4 +99,4 @@ struct pci_dev *pci_alloc_dev(struct pci_access *);
 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;
index 782d6bcdbaf5fdc65066c751580eaa0630a16673..50c29615b314de78402a4821bc23939b4edc1ea9 100644 (file)
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -32,8 +32,8 @@ typedef unsigned long u64;
 #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
@@ -61,6 +61,7 @@ struct nl_entry;
 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) */
@@ -114,7 +115,8 @@ void pci_free_dev(struct pci_dev *);
 
 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() */
diff --git a/lib/pread.h b/lib/pread.h
new file mode 100644 (file)
index 0000000..4637078
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *     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
index 9f43fcee2da376fbfa68c98c23a2078cb67e5343..8afe7a3bd3197936d7b0b95485ed5045bddbb083 100644 (file)
 #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)
@@ -179,10 +121,13 @@ proc_setup(struct pci_dev *d, int rw)
   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);
@@ -246,7 +191,7 @@ proc_cleanup_dev(struct pci_dev *d)
 }
 
 struct pci_methods pm_linux_proc = {
-  "/proc/bus/pci",
+  "Linux-proc",
   proc_config,
   proc_detect,
   proc_init,
diff --git a/lib/sysfs.c b/lib/sysfs.c
new file mode 100644 (file)
index 0000000..70511b1
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ *     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
+};