File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / pciutils / lib / sysfs.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Fri Feb 17 15:18:43 2012 UTC (13 years, 1 month ago) by misho
Branches: pciutils, MAIN
CVS tags: v3_1_9, HEAD
pciutils

    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>