Annotation of embedaddon/pciutils/lib/sysfs.c, revision 1.1
1.1 ! misho 1: /*
! 2: * The PCI Library -- Configuration Access via /sys/bus/pci
! 3: *
! 4: * Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx>
! 5: * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz>
! 6: *
! 7: * Can be freely distributed and used under the terms of the GNU GPL.
! 8: */
! 9:
! 10: #define _GNU_SOURCE
! 11:
! 12: #include <stdio.h>
! 13: #include <stdlib.h>
! 14: #include <string.h>
! 15: #include <stdarg.h>
! 16: #include <unistd.h>
! 17: #include <errno.h>
! 18: #include <dirent.h>
! 19: #include <fcntl.h>
! 20: #include <sys/types.h>
! 21:
! 22: #include "internal.h"
! 23: #include "pread.h"
! 24:
! 25: static void
! 26: sysfs_config(struct pci_access *a)
! 27: {
! 28: pci_define_param(a, "sysfs.path", PCI_PATH_SYS_BUS_PCI, "Path to the sysfs device tree");
! 29: }
! 30:
! 31: static inline char *
! 32: sysfs_name(struct pci_access *a)
! 33: {
! 34: return pci_get_param(a, "sysfs.path");
! 35: }
! 36:
! 37: static int
! 38: sysfs_detect(struct pci_access *a)
! 39: {
! 40: if (access(sysfs_name(a), R_OK))
! 41: {
! 42: a->debug("...cannot open %s", sysfs_name(a));
! 43: return 0;
! 44: }
! 45: a->debug("...using %s", sysfs_name(a));
! 46: return 1;
! 47: }
! 48:
! 49: static void
! 50: sysfs_init(struct pci_access *a)
! 51: {
! 52: a->fd = -1;
! 53: a->fd_vpd = -1;
! 54: }
! 55:
! 56: static void
! 57: sysfs_flush_cache(struct pci_access *a)
! 58: {
! 59: if (a->fd >= 0)
! 60: {
! 61: close(a->fd);
! 62: a->fd = -1;
! 63: }
! 64: if (a->fd_vpd >= 0)
! 65: {
! 66: close(a->fd_vpd);
! 67: a->fd_vpd = -1;
! 68: }
! 69: a->cached_dev = NULL;
! 70: }
! 71:
! 72: static void
! 73: sysfs_cleanup(struct pci_access *a)
! 74: {
! 75: sysfs_flush_cache(a);
! 76: }
! 77:
! 78: #define OBJNAMELEN 1024
! 79: static void
! 80: sysfs_obj_name(struct pci_dev *d, char *object, char *buf)
! 81: {
! 82: int n = snprintf(buf, OBJNAMELEN, "%s/devices/%04x:%02x:%02x.%d/%s",
! 83: sysfs_name(d->access), d->domain, d->bus, d->dev, d->func, object);
! 84: if (n < 0 || n >= OBJNAMELEN)
! 85: d->access->error("File name too long");
! 86: }
! 87:
! 88: static int
! 89: sysfs_get_value(struct pci_dev *d, char *object)
! 90: {
! 91: struct pci_access *a = d->access;
! 92: int fd, n;
! 93: char namebuf[OBJNAMELEN], buf[256];
! 94:
! 95: sysfs_obj_name(d, object, namebuf);
! 96: fd = open(namebuf, O_RDONLY);
! 97: if (fd < 0)
! 98: a->error("Cannot open %s: %s", namebuf, strerror(errno));
! 99: n = read(fd, buf, sizeof(buf));
! 100: close(fd);
! 101: if (n < 0)
! 102: a->error("Error reading %s: %s", namebuf, strerror(errno));
! 103: if (n >= (int) sizeof(buf))
! 104: a->error("Value in %s too long", namebuf);
! 105: buf[n] = 0;
! 106: return strtol(buf, NULL, 0);
! 107: }
! 108:
! 109: static void
! 110: sysfs_get_resources(struct pci_dev *d)
! 111: {
! 112: struct pci_access *a = d->access;
! 113: char namebuf[OBJNAMELEN], buf[256];
! 114: FILE *file;
! 115: int i;
! 116:
! 117: sysfs_obj_name(d, "resource", namebuf);
! 118: file = fopen(namebuf, "r");
! 119: if (!file)
! 120: a->error("Cannot open %s: %s", namebuf, strerror(errno));
! 121: for (i = 0; i < 7; i++)
! 122: {
! 123: unsigned long long start, end, size, flags;
! 124: if (!fgets(buf, sizeof(buf), file))
! 125: break;
! 126: if (sscanf(buf, "%llx %llx %llx", &start, &end, &flags) != 3)
! 127: a->error("Syntax error in %s", namebuf);
! 128: if (start)
! 129: size = end - start + 1;
! 130: else
! 131: size = 0;
! 132: flags &= PCI_ADDR_FLAG_MASK;
! 133: if (i < 6)
! 134: {
! 135: d->base_addr[i] = start | flags;
! 136: d->size[i] = size;
! 137: }
! 138: else
! 139: {
! 140: d->rom_base_addr = start | flags;
! 141: d->rom_size = size;
! 142: }
! 143: }
! 144: fclose(file);
! 145: }
! 146:
! 147: static void sysfs_scan(struct pci_access *a)
! 148: {
! 149: char dirname[1024];
! 150: DIR *dir;
! 151: struct dirent *entry;
! 152: int n;
! 153:
! 154: n = snprintf(dirname, sizeof(dirname), "%s/devices", sysfs_name(a));
! 155: if (n < 0 || n >= (int) sizeof(dirname))
! 156: a->error("Directory name too long");
! 157: dir = opendir(dirname);
! 158: if (!dir)
! 159: a->error("Cannot open %s", dirname);
! 160: while ((entry = readdir(dir)))
! 161: {
! 162: struct pci_dev *d;
! 163: unsigned int dom, bus, dev, func;
! 164:
! 165: /* ".", ".." or a special non-device perhaps */
! 166: if (entry->d_name[0] == '.')
! 167: continue;
! 168:
! 169: d = pci_alloc_dev(a);
! 170: if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4)
! 171: a->error("sysfs_scan: Couldn't parse entry name %s", entry->d_name);
! 172: d->domain = dom;
! 173: d->bus = bus;
! 174: d->dev = dev;
! 175: d->func = func;
! 176: if (!a->buscentric)
! 177: {
! 178: sysfs_get_resources(d);
! 179: d->irq = sysfs_get_value(d, "irq");
! 180: /*
! 181: * We could read these faster from the config registers, but we want to give
! 182: * the kernel a chance to fix up ID's and especially classes of broken devices.
! 183: */
! 184: d->vendor_id = sysfs_get_value(d, "vendor");
! 185: d->device_id = sysfs_get_value(d, "device");
! 186: d->device_class = sysfs_get_value(d, "class") >> 8;
! 187: d->known_fields = PCI_FILL_IDENT | PCI_FILL_CLASS | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES;
! 188: }
! 189: pci_link_dev(a, d);
! 190: }
! 191: closedir(dir);
! 192: }
! 193:
! 194: static void
! 195: sysfs_fill_slots(struct pci_access *a)
! 196: {
! 197: char dirname[1024];
! 198: DIR *dir;
! 199: struct dirent *entry;
! 200: int n;
! 201:
! 202: n = snprintf(dirname, sizeof(dirname), "%s/slots", sysfs_name(a));
! 203: if (n < 0 || n >= (int) sizeof(dirname))
! 204: a->error("Directory name too long");
! 205: dir = opendir(dirname);
! 206: if (!dir)
! 207: return;
! 208:
! 209: while (entry = readdir(dir))
! 210: {
! 211: char namebuf[OBJNAMELEN], buf[16];
! 212: FILE *file;
! 213: unsigned int dom, bus, dev;
! 214: struct pci_dev *d;
! 215:
! 216: /* ".", ".." or a special non-device perhaps */
! 217: if (entry->d_name[0] == '.')
! 218: continue;
! 219:
! 220: n = snprintf(namebuf, OBJNAMELEN, "%s/%s/%s", dirname, entry->d_name, "address");
! 221: if (n < 0 || n >= OBJNAMELEN)
! 222: a->error("File name too long");
! 223: file = fopen(namebuf, "r");
! 224: /*
! 225: * Old versions of Linux had a fakephp which didn't have an 'address'
! 226: * file. There's no useful information to be gleaned from these
! 227: * devices, pretend they're not there.
! 228: */
! 229: if (!file)
! 230: continue;
! 231:
! 232: if (!fgets(buf, sizeof(buf), file) || sscanf(buf, "%x:%x:%x", &dom, &bus, &dev) < 3)
! 233: a->warning("sysfs_fill_slots: Couldn't parse entry address %s", buf);
! 234: else
! 235: {
! 236: for (d = a->devices; d; d = d->next)
! 237: if (dom == d->domain && bus == d->bus && dev == d->dev && !d->phy_slot)
! 238: {
! 239: d->phy_slot = pci_malloc(a, strlen(entry->d_name) + 1);
! 240: strcpy(d->phy_slot, entry->d_name);
! 241: }
! 242: }
! 243: fclose(file);
! 244: }
! 245: closedir(dir);
! 246: }
! 247:
! 248: static int
! 249: sysfs_fill_info(struct pci_dev *d, int flags)
! 250: {
! 251: if ((flags & PCI_FILL_PHYS_SLOT) && !(d->known_fields & PCI_FILL_PHYS_SLOT))
! 252: {
! 253: struct pci_dev *pd;
! 254: sysfs_fill_slots(d->access);
! 255: for (pd = d->access->devices; pd; pd = pd->next)
! 256: pd->known_fields |= PCI_FILL_PHYS_SLOT;
! 257: }
! 258: return pci_generic_fill_info(d, flags);
! 259: }
! 260:
! 261: /* Intent of the sysfs_setup() caller */
! 262: enum
! 263: {
! 264: SETUP_READ_CONFIG = 0,
! 265: SETUP_WRITE_CONFIG = 1,
! 266: SETUP_READ_VPD = 2
! 267: };
! 268:
! 269: static int
! 270: sysfs_setup(struct pci_dev *d, int intent)
! 271: {
! 272: struct pci_access *a = d->access;
! 273: char namebuf[OBJNAMELEN];
! 274:
! 275: if (a->cached_dev != d || (intent == SETUP_WRITE_CONFIG && !a->fd_rw))
! 276: {
! 277: sysfs_flush_cache(a);
! 278: a->cached_dev = d;
! 279: }
! 280:
! 281: if (intent == SETUP_READ_VPD)
! 282: {
! 283: if (a->fd_vpd < 0)
! 284: {
! 285: sysfs_obj_name(d, "vpd", namebuf);
! 286: a->fd_vpd = open(namebuf, O_RDONLY);
! 287: /* No warning on error; vpd may be absent or accessible only to root */
! 288: }
! 289: return a->fd_vpd;
! 290: }
! 291:
! 292: if (a->fd < 0)
! 293: {
! 294: sysfs_obj_name(d, "config", namebuf);
! 295: a->fd_rw = a->writeable || intent == SETUP_WRITE_CONFIG;
! 296: a->fd = open(namebuf, a->fd_rw ? O_RDWR : O_RDONLY);
! 297: if (a->fd < 0)
! 298: a->warning("Cannot open %s", namebuf);
! 299: a->fd_pos = 0;
! 300: }
! 301: return a->fd;
! 302: }
! 303:
! 304: static int sysfs_read(struct pci_dev *d, int pos, byte *buf, int len)
! 305: {
! 306: int fd = sysfs_setup(d, SETUP_READ_CONFIG);
! 307: int res;
! 308:
! 309: if (fd < 0)
! 310: return 0;
! 311: res = do_read(d, fd, buf, len, pos);
! 312: if (res < 0)
! 313: {
! 314: d->access->warning("sysfs_read: read failed: %s", strerror(errno));
! 315: return 0;
! 316: }
! 317: else if (res != len)
! 318: return 0;
! 319: return 1;
! 320: }
! 321:
! 322: static int sysfs_write(struct pci_dev *d, int pos, byte *buf, int len)
! 323: {
! 324: int fd = sysfs_setup(d, SETUP_WRITE_CONFIG);
! 325: int res;
! 326:
! 327: if (fd < 0)
! 328: return 0;
! 329: res = do_write(d, fd, buf, len, pos);
! 330: if (res < 0)
! 331: {
! 332: d->access->warning("sysfs_write: write failed: %s", strerror(errno));
! 333: return 0;
! 334: }
! 335: else if (res != len)
! 336: {
! 337: d->access->warning("sysfs_write: tried to write %d bytes at %d, but only %d succeeded", len, pos, res);
! 338: return 0;
! 339: }
! 340: return 1;
! 341: }
! 342:
! 343: #ifdef PCI_HAVE_DO_READ
! 344:
! 345: /* pread() is not available and do_read() only works for a single fd, so we
! 346: * cannot implement read_vpd properly. */
! 347: static int sysfs_read_vpd(struct pci_dev *d, int pos, byte *buf, int len)
! 348: {
! 349: return 0;
! 350: }
! 351:
! 352: #else /* !PCI_HAVE_DO_READ */
! 353:
! 354: static int sysfs_read_vpd(struct pci_dev *d, int pos, byte *buf, int len)
! 355: {
! 356: int fd = sysfs_setup(d, SETUP_READ_VPD);
! 357: int res;
! 358:
! 359: if (fd < 0)
! 360: return 0;
! 361: res = pread(fd, buf, len, pos);
! 362: if (res < 0)
! 363: {
! 364: d->access->warning("sysfs_read_vpd: read failed: %s", strerror(errno));
! 365: return 0;
! 366: }
! 367: else if (res != len)
! 368: return 0;
! 369: return 1;
! 370: }
! 371:
! 372: #endif /* PCI_HAVE_DO_READ */
! 373:
! 374: static void sysfs_cleanup_dev(struct pci_dev *d)
! 375: {
! 376: struct pci_access *a = d->access;
! 377:
! 378: if (a->cached_dev == d)
! 379: sysfs_flush_cache(a);
! 380: }
! 381:
! 382: struct pci_methods pm_linux_sysfs = {
! 383: "linux-sysfs",
! 384: "The sys filesystem on Linux",
! 385: sysfs_config,
! 386: sysfs_detect,
! 387: sysfs_init,
! 388: sysfs_cleanup,
! 389: sysfs_scan,
! 390: sysfs_fill_info,
! 391: sysfs_read,
! 392: sysfs_write,
! 393: sysfs_read_vpd,
! 394: NULL, /* init_dev */
! 395: sysfs_cleanup_dev
! 396: };
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>