diff options
author | Martin Mares <mj@ucw.cz> | 1999-01-22 21:04:45 +0000 |
---|---|---|
committer | Martin Mares <mj@ucw.cz> | 2006-05-05 14:10:01 +0200 |
commit | 727ce158868ed101006ecc5d3dd3faede927165c (patch) | |
tree | b9cdf9b3ccd666ebea8413391c4a85c24ddac4ed /lib/proc.c | |
parent | 6ece6bc6bf4741318d53b298f848b549d063b9f6 (diff) | |
download | pciutils-727ce158868ed101006ecc5d3dd3faede927165c.tar.gz |
Rewrote the PCI Utilities. All PCI configuration space access has been
moved to a library which supports multiple access mechanisms: the current
/proc/bus/pci one, direct port access (needed for debugging of kernel
PCI code and as a nice side-effect this makes pciutils work with 2.0
kernels, although only for root) and reading of configuration dumps.
This has been released as version 1.99.2-alpha. For detailed description
of changes, see the ChangeLog.
Can anybody test it on non-PC architectures, please? (Especially if you
have any 64-bit card.)
Diffstat (limited to 'lib/proc.c')
-rw-r--r-- | lib/proc.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/lib/proc.c b/lib/proc.c new file mode 100644 index 0000000..f5ae883 --- /dev/null +++ b/lib/proc.c @@ -0,0 +1,218 @@ +/* + * $Id: proc.c,v 1.1 1999/01/22 21:05:39 mj Exp $ + * + * The PCI Library -- Configuration Access via /proc/bus/pci + * + * Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> + +#include "internal.h" + +#include <asm/unistd.h> +#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1 +#include <syscall-list.h> +#endif + +/* + * As libc doesn't support pread/pwrite yet, we have to call them directly + * or use lseek/read/write instead. + */ +#if !(defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0) + +#if defined(__GLIBC__) && !(defined(__powerpc__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0) +#ifndef SYS_pread +#define SYS_pread __NR_pread +#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 __NR_pwrite +#endif +static int +pwrite(unsigned int fd, void *buf, size_t size, loff_t where) +{ + return syscall(SYS_pwrite, fd, buf, size, where); +} +#else +static _syscall4(int, pread, unsigned int, fd, void *, buf, size_t, size, loff_t, where); +static _syscall4(int, pwrite, unsigned int, fd, void *, buf, size_t, size, loff_t, where); +#endif + +#endif + +static void +proc_config(struct pci_access *a) +{ + a->method_params[PCI_ACCESS_PROC_BUS_PCI] = PATH_PROC_BUS_PCI; +} + +static int +proc_detect(struct pci_access *a) +{ + char *name = a->method_params[PCI_ACCESS_PROC_BUS_PCI]; + + if (access(name, R_OK)) + { + a->warning("Cannot open %s", name); + return 0; + } + a->debug("...using %s", name); + return 1; +} + +static void +proc_init(struct pci_access *a) +{ + a->fd = -1; +} + +static void +proc_cleanup(struct pci_access *a) +{ + if (a->fd >= 0) + { + close(a->fd); + a->fd = -1; + } +} + +static void +proc_scan(struct pci_access *a) +{ + FILE *f; + char buf[256]; + + if (snprintf(buf, sizeof(buf), "%s/devices", a->method_params[PCI_ACCESS_PROC_BUS_PCI]) == sizeof(buf)) + a->error("File name too long"); + f = fopen(buf, "r"); + if (!f) + a->error("Cannot open %s", buf); + while (fgets(buf, sizeof(buf)-1, f)) + { + struct pci_dev *d = pci_alloc_dev(a); + unsigned int dfn, vend; + + sscanf(buf, "%x %x %x %lx %lx %lx %lx %lx %lx %lx", + &dfn, + &vend, + &d->irq, + &d->base_addr[0], + &d->base_addr[1], + &d->base_addr[2], + &d->base_addr[3], + &d->base_addr[4], + &d->base_addr[5], + &d->rom_base_addr); + d->bus = dfn >> 8U; + d->dev = PCI_SLOT(dfn & 0xff); + d->func = PCI_FUNC(dfn & 0xff); + d->vendor_id = vend >> 16U; + d->device_id = vend & 0xffff; + d->known_fields = a->buscentric ? PCI_FILL_IDENT + : (PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE); + pci_link_dev(a, d); + } + fclose(f); +} + +static int +proc_setup(struct pci_dev *d, int rw) +{ + struct pci_access *a = d->access; + + if (a->cached_dev != d || a->fd_rw < rw) + { + char buf[256]; + 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)) + a->error("File name too long"); + a->fd_rw = a->writeable || rw; + a->fd = open(buf, a->fd_rw ? O_RDWR : O_RDONLY); + if (a->fd < 0) + a->warning("Cannot open %s", buf); + a->cached_dev = d; + } + return a->fd; +} + +static int +proc_read(struct pci_dev *d, int pos, byte *buf, int len) +{ + int fd = proc_setup(d, 0); + int res; + + if (fd < 0) + return 0; + res = pread(fd, buf, len, pos); + if (res < 0) + { + d->access->warning("proc_read: read failed: %s", strerror(errno)); + return 0; + } + else if (res != len) + { + d->access->warning("proc_read: tried to read %d bytes at %d, but got only %d", len, pos, res); + return 0; + } + return 1; +} + +static int +proc_write(struct pci_dev *d, int pos, byte *buf, int len) +{ + int fd = proc_setup(d, 1); + int res; + + if (fd < 0) + return 0; + res = pwrite(fd, buf, len, pos); + if (res < 0) + { + d->access->warning("proc_write: write failed: %s", strerror(errno)); + return 0; + } + else if (res != len) + { + d->access->warning("proc_write: tried to write %d bytes at %d, but got only %d", len, pos, res); + return 0; + } + return 1; +} + +static void +proc_cleanup_dev(struct pci_dev *d) +{ + if (d->access->cached_dev == d) + d->access->cached_dev = NULL; +} + +struct pci_methods pm_linux_proc = { + "/proc/bus/pci", + proc_config, + proc_detect, + proc_init, + proc_cleanup, + proc_scan, + pci_generic_fill_info, + proc_read, + proc_write, + NULL, /* init_dev */ + proc_cleanup_dev +}; |