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>