| /* |
| * 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, 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 */ |
| }; |