diff options
Diffstat (limited to 'lib/aix-device.c')
-rw-r--r-- | lib/aix-device.c | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/lib/aix-device.c b/lib/aix-device.c new file mode 100644 index 0000000..9355395 --- /dev/null +++ b/lib/aix-device.c @@ -0,0 +1,274 @@ +/* + * The PCI Library -- AIX /dev/pci[0-n] access + * + * Copyright (c) 1999 Jari Kirma <kirma@cs.hut.fi> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +/* + * Read functionality of this driver is briefly tested, and seems + * to supply basic information correctly, but I promise no more. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include <sys/types.h> +#include <sys/mdio.h> + +#include "internal.h" + +#define AIX_LSDEV_CMD "/usr/sbin/lsdev -C -c bus -t pci\\* -S available -F name" +#define AIX_ODMGET_CMD \ + "/usr/bin/odmget -q 'name=%s and attribute=bus_number' CuAt | \ + /usr/bin/awk '$1 == \"value\" { print $3 }'" + + +/* AIX PCI bus device information */ + +typedef struct aix_pci_bus { + char *bus_name; + int bus_number; + int bus_fd; +} aix_pci_bus; + +#define PCI_BUS_MAX 16 /* arbitrary choice */ +static aix_pci_bus pci_buses[PCI_BUS_MAX]; +static int pci_bus_count = 0; + + +/* Utility Routines */ + +static aix_pci_bus * +aix_find_bus(struct pci_access *a, int bus_number) +{ + int i; + + for (i = 0; i < pci_bus_count; i++) + { + if (pci_buses[i].bus_number == bus_number) + { + return &pci_buses[i]; + } + } + + a->error("aix_find_bus: bus number %d not found", bus_number); +} + +static int +aix_bus_open(struct pci_dev *d) +{ + struct pci_access *a = d->access; + aix_pci_bus *bp = aix_find_bus(a, d->bus); + + if (bp->bus_fd < 0) + { + char devbuf[256]; + int mode = a->writeable ? O_RDWR : O_RDONLY; + + snprintf(devbuf, sizeof (devbuf), "/dev/%s", bp->bus_name); + bp->bus_fd = open(devbuf, mode, 0); + if (bp->bus_fd < 0) + a->error("aix_open_bus: %s open failed", devbuf); + } + + return bp->bus_fd; +} + +static int +aix_bus_number(char *name) +{ + int bus_number; + FILE *odmget_pipe; + char command[256]; + char buf[256]; + char *bp; + char *ep; + + snprintf(command, sizeof (command), AIX_ODMGET_CMD, name); + odmget_pipe = popen(command, "r"); + if (odmget_pipe == NULL) + { + /* popen failed */ + return -1; + } + + if (fgets(buf, sizeof (buf) - 1, odmget_pipe) != NULL) + { + bp = buf + 1; /* skip leading double quote */ + bus_number = strtol(bp, &ep, 0); + if (bp == ep) + { + /* strtol failed */ + bus_number = -1; + } + } + else + { + /* first PCI bus_number is not recorded in ODM CuAt; default to 0 */ + bus_number = 0; + } + + (void) pclose(odmget_pipe); + + return bus_number; +} + + +/* Method entries */ + +static int +aix_detect(struct pci_access *a) +{ + int len; + int mode = a->writeable ? W_OK : R_OK; + char *command = AIX_LSDEV_CMD; + FILE *lsdev_pipe; + char buf[256]; + char *name; + + lsdev_pipe = popen(command, "r"); + if (lsdev_pipe == NULL) + { + a->error("aix_config: popen(\"%s\") failed", command); + } + + while (fgets(buf, sizeof (buf) - 1, lsdev_pipe) != NULL) + { + len = strlen(buf); + while (buf[len-1] == '\n' || buf[len-1] == '\r') + len--; + buf[len] = '\0'; /* clobber the newline */ + + name = (char *) pci_malloc(a, len + 1); + strcpy(name, buf); + pci_buses[pci_bus_count].bus_name = name; + pci_buses[pci_bus_count].bus_number = 0; + pci_buses[pci_bus_count].bus_fd = -1; + if (!pci_bus_count) + a->debug("...using %s", name); + else + a->debug(", %s", name); + pci_bus_count++; + if (pci_bus_count >= PCI_BUS_MAX) + break; + } + + (void) pclose(lsdev_pipe); + + return pci_bus_count; +} + +static void +aix_init(struct pci_access *a) +{ + char *name; + int i; + + for (i = 0; i < pci_bus_count; i++) + { + name = pci_buses[i].bus_name; + pci_buses[i].bus_number = aix_bus_number(name); + } +} + +static void +aix_cleanup(struct pci_access *a) +{ + aix_pci_bus *bp; + + while (pci_bus_count-- > 0) + { + bp = &pci_buses[pci_bus_count]; + (void) free(bp->bus_name); + if (bp->bus_fd >= 0) + { + (void) close(bp->bus_fd); + bp->bus_fd = -1; + } + } +} + +void +aix_scan(struct pci_access *a) +{ + int i; + int bus_number; + byte busmap[256]; + + memset(busmap, 0, sizeof(busmap)); + for (i = 0; i < pci_bus_count; i++) + { + bus_number = pci_buses[i].bus_number; + if (!busmap[bus_number]) + { + pci_generic_scan_bus(a, busmap, 0, bus_number); + } + } +} + +static int +aix_read(struct pci_dev *d, int pos, byte *buf, int len) +{ + struct mdio mdio; + int fd; + + if (d->domain || pos + len > 256) + return 0; + + fd = aix_bus_open(d); + mdio.md_addr = (ulong) pos; + mdio.md_size = len; + mdio.md_incr = MV_BYTE; + mdio.md_data = (char *) buf; + mdio.md_sla = PCI_DEVFN(d->dev, d->func); + + if (ioctl(fd, MIOPCFGET, &mdio) < 0) + d->access->error("aix_read: ioctl(MIOPCFGET) failed"); + + return 1; +} + +static int +aix_write(struct pci_dev *d, int pos, byte *buf, int len) +{ + struct mdio mdio; + int fd; + + if (d->domain || pos + len > 256) + return 0; + + fd = aix_bus_open(d); + mdio.md_addr = (ulong) pos; + mdio.md_size = len; + mdio.md_incr = MV_BYTE; + mdio.md_data = (char *) buf; + mdio.md_sla = PCI_DEVFN(d->dev, d->func); + + if (ioctl(fd, MIOPCFPUT, &mdio) < 0) + { + d->access->error("aix_write: ioctl(MIOPCFPUT) failed"); + } + + return 1; +} + +struct pci_methods pm_aix_device = { + "aix-device", + "AIX /dev/pci[0-n]", + NULL, + aix_detect, + aix_init, + aix_cleanup, + aix_scan, + pci_generic_fill_info, + aix_read, + aix_write, + NULL, /* read_vpd */ + NULL, /* dev_init */ + NULL /* dev_cleanup */ +}; |