From 103f074c2a9db614da9bca6445d25b933a615bc5 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Mon, 11 Feb 2008 19:45:21 +0100 Subject: [PATCH] Implemented control options and the caching logic. Everything is still in a very raw form. --- lib/access.c | 8 +- lib/names.c | 326 +++++++++++++++++++++++++++++++++++++++++---------- lib/pci.h | 27 ++++- lspci.c | 4 +- 4 files changed, 296 insertions(+), 69 deletions(-) diff --git a/lib/access.c b/lib/access.c index 4c21aaa..01bdc24 100644 --- a/lib/access.c +++ b/lib/access.c @@ -1,7 +1,7 @@ /* * The PCI Library -- User Access * - * Copyright (c) 1997--2003 Martin Mares + * Copyright (c) 1997--2008 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -67,8 +67,8 @@ pci_alloc(void) memset(a, 0, sizeof(*a)); pci_set_name_list_path(a, PCI_PATH_IDS_DIR "/" PCI_IDS, 0); - a->id_domain = PCI_ID_DOMAIN; - a->network_ids = 1; /* FIXME */ + pci_set_net_domain(a, PCI_ID_DOMAIN, 0); + a->id_lookup_mode = PCI_LOOKUP_CACHE; for(i=0; iconfig) pci_methods[i]->config(a); @@ -185,6 +185,8 @@ pci_cleanup(struct pci_access *a) a->methods->cleanup(a); pci_free_name_list(a); pci_set_name_list_path(a, NULL, 0); + pci_set_net_domain(a, NULL, 0); + pci_set_id_cache(a, NULL, 0); pci_mfree(a); } diff --git a/lib/names.c b/lib/names.c index 3b30bec..30b6b6e 100644 --- a/lib/names.c +++ b/lib/names.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include "internal.h" @@ -64,6 +67,7 @@ struct id_entry { struct id_entry *next; u32 id12, id34; byte cat; + byte src; char name[1]; }; @@ -78,6 +82,13 @@ enum id_entry_type { ID_PROGIF }; +enum id_entry_src { + SRC_UNKNOWN, + SRC_CACHE, + SRC_NET, + SRC_LOCAL, +}; + struct id_bucket { struct id_bucket *next; unsigned int full; @@ -126,6 +137,16 @@ static inline u32 id_pair(unsigned int x, unsigned int y) return ((x << 16) | y); } +static inline unsigned int pair_first(unsigned int x) +{ + return (x >> 16) & 0xffff; +} + +static inline unsigned int pair_second(unsigned int x) +{ + return x & 0xffff; +} + static inline unsigned int id_hash(int cat, u32 id12, u32 id34) { unsigned int h; @@ -134,6 +155,190 @@ static inline unsigned int id_hash(int cat, u32 id12, u32 id34) return h % HASH_SIZE; } +static int id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src) +{ + u32 id12 = id_pair(id1, id2); + u32 id34 = id_pair(id3, id4); + unsigned int h = id_hash(cat, id12, id34); + struct id_entry *n = a->id_hash ? a->id_hash[h] : NULL; + int len = strlen(text); + + while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat)) + n = n->next; + if (n) + return 1; + n = id_alloc(a, sizeof(struct id_entry) + len); + n->id12 = id12; + n->id34 = id34; + n->cat = cat; + n->src = src; + memcpy(n->name, text, len+1); + n->next = a->id_hash[h]; + a->id_hash[h] = n; + return 0; +} + +static char *id_lookup_raw(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4) +{ + struct id_entry *n, *best; + u32 id12 = id_pair(id1, id2); + u32 id34 = id_pair(id3, id4); + + if (a->id_hash) + { + n = a->id_hash[id_hash(cat, id12, id34)]; + best = NULL; + for (; n; n=n->next) + { + if (n->id12 != id12 || n->id34 != id34 || n->cat != cat) + continue; + if (n->src == SRC_LOCAL && (flags & PCI_LOOKUP_SKIP_LOCAL)) + continue; + if (n->src == SRC_NET && !(flags & PCI_LOOKUP_NETWORK)) + continue; + if (n->src == SRC_CACHE && !(flags & PCI_LOOKUP_CACHE)) + continue; + if (!best || best->src < n->src) + best = n; + } + if (best) + return best->name; + } + return NULL; +} + +static const char cache_version[] = "#PCI-CACHE-1.0"; + +static int +pci_id_cache_load(struct pci_access *a, int flags) +{ + char *name; + char line[MAX_LINE]; + const char default_name[] = "/.pciids-cache"; + FILE *f; + int lino; + + a->id_cache_status = 1; + if (!a->id_cache_file) + { + /* Construct the default ID cache name */ + uid_t uid = getuid(); + struct passwd *pw = getpwuid(uid); + if (!pw) + return 0; + name = pci_malloc(a, strlen(pw->pw_dir) + sizeof(default_name)); + sprintf(name, "%s%s", pw->pw_dir, default_name); + pci_set_id_cache(a, name, 1); + } + a->debug("Using cache %s\n", a->id_cache_file); + if (flags & PCI_LOOKUP_REFRESH_CACHE) + { + a->debug("Not loading cache, will refresh everything\n"); + a->id_cache_status = 2; + return 0; + } + + f = fopen(a->id_cache_file, "rb"); + if (!f) + { + a->debug("Cache file does not exist\n"); + return 0; + } + /* FIXME: Compare timestamp with the pci.ids file? */ + + lino = 0; + while (fgets(line, sizeof(line), f)) + { + char *p = strchr(line, '\n'); + lino++; + if (p) + { + *p = 0; + if (lino == 1) + { + if (strcmp(line, cache_version)) + { + a->debug("Unrecognized cache version %s, ignoring\n", line); + break; + } + continue; + } + else + { + int cat, id1, id2, id3, id4, cnt; + if (sscanf(line, "%d%x%x%x%x%n", &cat, &id1, &id2, &id3, &id4, &cnt) >= 5) + { + p = line + cnt; + while (*p && *p == ' ') + p++; + id_insert(a, cat, id1, id2, id3, id4, p, SRC_CACHE); + continue; + } + } + } + a->warning("Malformed cache file %s (line %d), ignoring", a->id_cache_file, lino); + break; + } + + if (ferror(f)) + a->warning("Error while reading %s", a->id_cache_file); + fclose(f); + return 1; +} + +static void +pci_id_cache_dirty(struct pci_access *a) +{ + if (a->id_cache_status >= 1) + a->id_cache_status = 2; +} + +void +pci_id_cache_flush(struct pci_access *a) +{ + int orig_status = a->id_cache_status; + FILE *f; + unsigned int h; + struct id_entry *e, *e2; + + a->id_cache_status = 0; + if (orig_status < 2) + return; + if (!a->id_cache_file) + return; + f = fopen(a->id_cache_file, "wb"); + if (!f) + { + a->warning("Cannot write %s: %s", a->id_cache_file, strerror(errno)); + return; + } + a->debug("Writing cache to %s\n", a->id_cache_file); + fprintf(f, "%s\n", cache_version); + + for (h=0; hid_hash[h]; e; e=e->next) + if (e->src == SRC_CACHE || e->src == SRC_NET) + { + /* Verify that every entry is written at most once */ + for (e2=a->id_hash[h]; e2 != e; e2=e2->next) + if ((e2->src == SRC_CACHE || e2->src == SRC_NET) && + e2->cat == e->cat && + e2->id12 == e->id12 && e2->id34 == e->id34) + break; + if (e2 == e) + fprintf(f, "%d %x %x %x %x %s\n", + e->cat, + pair_first(e->id12), pair_second(e->id12), + pair_first(e->id34), pair_second(e->id34), + e->name); + } + + fflush(f); + if (ferror(f)) + a->warning("Error writing %s", a->id_cache_file); + fclose(f); +} + static char *id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4) { char name[256], dnsname[256], txt[256]; @@ -199,56 +404,40 @@ static char *id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int j += 1+data[j]; a->debug("\t\t%s\n", txt); if (txt[0] == 'i' && txt[1] == '=') - return strdup(txt+2); /* FIXME */ + return strdup(txt+2); } } return NULL; } -static int id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text) +static char *id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4) { - u32 id12 = id_pair(id1, id2); - u32 id34 = id_pair(id3, id4); - unsigned int h = id_hash(cat, id12, id34); - struct id_entry *n = a->id_hash ? a->id_hash[h] : NULL; - int len = strlen(text); - - while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat)) - n = n->next; - if (n) - return 1; - n = id_alloc(a, sizeof(struct id_entry) + len); - n->id12 = id12; - n->id34 = id34; - n->cat = cat; - memcpy(n->name, text, len+1); - n->next = a->id_hash[h]; - a->id_hash[h] = n; - return 0; -} - -static char *id_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4) -{ - struct id_entry *n; - u32 id12 = id_pair(id1, id2); - u32 id34 = id_pair(id3, id4); char *name; - if (a->id_hash) - { - n = a->id_hash[id_hash(cat, id12, id34)]; - while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat)) - n = n->next; - if (n) - return n->name; - } - if (name = id_net_lookup(a, cat, id1, id2, id3, id4)) + while (!(name = id_lookup_raw(a, flags, cat, id1, id2, id3, id4))) { - id_insert(a, cat, id1, id2, id3, id4, name); - return name; + if ((flags & PCI_LOOKUP_CACHE) && !a->id_cache_status) + { + if (pci_id_cache_load(a, flags)) + continue; + } + if (flags & PCI_LOOKUP_NETWORK) + { + if (name = id_net_lookup(a, cat, id1, id2, id3, id4)) + { + id_insert(a, cat, id1, id2, id3, id4, name, SRC_NET); + free(name); + pci_id_cache_dirty(a); + } + else + id_insert(a, cat, id1, id2, id3, id4, "", SRC_NET); /* FIXME: Check that negative caching works */ + /* We want to iterate the lookup to get the allocated ID entry from the hash */ + continue; + } + return NULL; } - return NULL; + return (name[0] ? name : NULL); } static int id_hex(char *p, int cnt) @@ -321,7 +510,7 @@ static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino) { /* Generic subsystem block */ if ((id1 = id_hex(p+2, 4)) < 0 || p[6]) return parse_error; - if (!id_lookup(a, ID_VENDOR, id1, 0, 0, 0)) + if (!id_lookup(a, 0, ID_VENDOR, id1, 0, 0, 0)) return "Vendor does not exist"; cat = ID_GEN_SUBSYSTEM; continue; @@ -400,7 +589,7 @@ static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino) p++; if (!*p) return parse_error; - if (id_insert(a, cat, id1, id2, id3, id4, p)) + if (id_insert(a, cat, id1, id2, id3, id4, p, SRC_LOCAL)) return "Duplicate entry"; } return NULL; @@ -414,7 +603,7 @@ pci_load_name_list(struct pci_access *a) const char *err; pci_free_name_list(a); - a->hash_load_failed = 1; + a->id_load_failed = 1; if (!(f = pci_open(a))) return 0; err = id_parse_list(a, f, &lino); @@ -422,15 +611,17 @@ pci_load_name_list(struct pci_access *a) pci_close(f); if (err) a->error("%s at %s, line %d\n", err, a->id_file_name, lino); - a->hash_load_failed = 0; + a->id_load_failed = 0; return 1; } void pci_free_name_list(struct pci_access *a) { + pci_id_cache_flush(a); pci_mfree(a->id_hash); a->id_hash = NULL; + a->id_cache_status = 0; while (a->current_id_bucket) { struct id_bucket *buck = a->current_id_bucket; @@ -440,15 +631,15 @@ pci_free_name_list(struct pci_access *a) } static char * -id_lookup_subsys(struct pci_access *a, int iv, int id, int isv, int isd) +id_lookup_subsys(struct pci_access *a, int flags, int iv, int id, int isv, int isd) { char *d = NULL; if (iv > 0 && id > 0) /* Per-device lookup */ - d = id_lookup(a, ID_SUBSYSTEM, iv, id, isv, isd); + d = id_lookup(a, flags, ID_SUBSYSTEM, iv, id, isv, isd); if (!d) /* Generic lookup */ - d = id_lookup(a, ID_GEN_SUBSYSTEM, isv, isd, 0, 0); + d = id_lookup(a, flags, ID_GEN_SUBSYSTEM, isv, isd, 0, 0); if (!d && iv == isv && id == isd) /* Check for subsystem == device */ - d = id_lookup(a, ID_DEVICE, iv, id, 0, 0); + d = id_lookup(a, flags, ID_DEVICE, iv, id, 0, 0); return d; } @@ -514,6 +705,7 @@ pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) va_start(args, flags); + flags |= a->id_lookup_mode; if (!(flags & PCI_LOOKUP_NO_NUMBERS)) { if (a->numeric_ids > 1) @@ -524,7 +716,7 @@ pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) if (flags & PCI_LOOKUP_MIXED) flags &= ~PCI_LOOKUP_NUMERIC; - if (!a->id_hash && !(flags & PCI_LOOKUP_NUMERIC) && !a->hash_load_failed) + if (!a->id_hash && !(flags & (PCI_LOOKUP_NUMERIC | PCI_LOOKUP_SKIP_LOCAL)) && !a->id_load_failed) pci_load_name_list(a); switch (flags & 0xffff) @@ -532,23 +724,23 @@ pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) case PCI_LOOKUP_VENDOR: iv = va_arg(args, int); sprintf(numbuf, "%04x", iv); - return format_name(buf, size, flags, id_lookup(a, ID_VENDOR, iv, 0, 0, 0), numbuf, "Unknown vendor"); + return format_name(buf, size, flags, id_lookup(a, flags, ID_VENDOR, iv, 0, 0, 0), numbuf, "Unknown vendor"); case PCI_LOOKUP_DEVICE: iv = va_arg(args, int); id = va_arg(args, int); sprintf(numbuf, "%04x", id); - return format_name(buf, size, flags, id_lookup(a, ID_DEVICE, iv, id, 0, 0), numbuf, "Unknown device"); + return format_name(buf, size, flags, id_lookup(a, flags, ID_DEVICE, iv, id, 0, 0), numbuf, "Unknown device"); case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE: iv = va_arg(args, int); id = va_arg(args, int); sprintf(numbuf, "%04x:%04x", iv, id); - v = id_lookup(a, ID_VENDOR, iv, 0, 0, 0); - d = id_lookup(a, ID_DEVICE, iv, id, 0, 0); + v = id_lookup(a, flags, ID_VENDOR, iv, 0, 0, 0); + d = id_lookup(a, flags, ID_DEVICE, iv, id, 0, 0); return format_name_pair(buf, size, flags, v, d, numbuf); case PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR: isv = va_arg(args, int); sprintf(numbuf, "%04x", isv); - v = id_lookup(a, ID_VENDOR, isv, 0, 0, 0); + v = id_lookup(a, flags, ID_VENDOR, isv, 0, 0, 0); return format_name(buf, size, flags, v, numbuf, "Unknown vendor"); case PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE: iv = va_arg(args, int); @@ -556,21 +748,21 @@ pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) isv = va_arg(args, int); isd = va_arg(args, int); sprintf(numbuf, "%04x", isd); - return format_name(buf, size, flags, id_lookup_subsys(a, iv, id, isv, isd), numbuf, "Unknown device"); + return format_name(buf, size, flags, id_lookup_subsys(a, flags, iv, id, isv, isd), numbuf, "Unknown device"); case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM: iv = va_arg(args, int); id = va_arg(args, int); isv = va_arg(args, int); isd = va_arg(args, int); - v = id_lookup(a, ID_VENDOR, isv, 0, 0, 0); - d = id_lookup_subsys(a, iv, id, isv, isd); + v = id_lookup(a, flags, ID_VENDOR, isv, 0, 0, 0); + d = id_lookup_subsys(a, flags, iv, id, isv, isd); sprintf(numbuf, "%04x:%04x", isv, isd); return format_name_pair(buf, size, flags, v, d, numbuf); case PCI_LOOKUP_CLASS: icls = va_arg(args, int); sprintf(numbuf, "%04x", icls); - cls = id_lookup(a, ID_SUBCLASS, icls >> 8, icls & 0xff, 0, 0); - if (!cls && (cls = id_lookup(a, ID_CLASS, icls >> 8, 0, 0, 0))) + cls = id_lookup(a, flags, ID_SUBCLASS, icls >> 8, icls & 0xff, 0, 0); + if (!cls && (cls = id_lookup(a, flags, ID_CLASS, icls >> 8, 0, 0, 0))) { if (!(flags & PCI_LOOKUP_NUMERIC)) /* Include full class number */ flags |= PCI_LOOKUP_MIXED; @@ -580,7 +772,7 @@ pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) icls = va_arg(args, int); ipif = va_arg(args, int); sprintf(numbuf, "%02x", ipif); - pif = id_lookup(a, ID_PROGIF, icls >> 8, icls & 0xff, ipif, 0); + pif = id_lookup(a, flags, ID_PROGIF, icls >> 8, icls & 0xff, ipif, 0); if (!pif && icls == 0x0101 && !(ipif & 0x70)) { /* IDE controllers have complex prog-if semantics */ @@ -607,3 +799,19 @@ void pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed) a->id_file_name = name; a->free_id_name = to_be_freed; } + +void pci_set_net_domain(struct pci_access *a, char *name, int to_be_freed) +{ + if (a->free_id_domain) + free(a->id_domain); + a->id_domain = name; + a->free_id_domain = to_be_freed; +} + +void pci_set_id_cache(struct pci_access *a, char *name, int to_be_freed) +{ + if (a->free_id_cache_file) + free(a->id_cache_file); + a->id_cache_file = name; + a->free_id_cache_file = to_be_freed; +} diff --git a/lib/pci.h b/lib/pci.h index 37cb1be..9bf8022 100644 --- a/lib/pci.h +++ b/lib/pci.h @@ -1,7 +1,7 @@ /* * The PCI Library * - * Copyright (c) 1997--2007 Martin Mares + * Copyright (c) 1997--2008 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -13,7 +13,7 @@ #include "header.h" #include "types.h" -#define PCI_LIB_VERSION 0x020204 +#define PCI_LIB_VERSION 0x020204 /* FIXME: Update */ /* * PCI Access Structure @@ -42,11 +42,18 @@ struct pci_access { char *method_params[PCI_ACCESS_MAX]; /* Parameters for the methods */ int writeable; /* Open in read/write mode */ int buscentric; /* Bus-centric view of the world */ - char *id_file_name; /* Name of ID list file */ + + char *id_file_name; /* Name of ID list file (use pci_set_name_list_path()) */ int free_id_name; /* Set if id_file_name is malloced */ int numeric_ids; /* Enforce PCI_LOOKUP_NUMERIC (>1 => PCI_LOOKUP_MIXED) */ - int network_ids; /* Try DNS lookups on unknown ID's */ - char *id_domain; /* DNS domain used for the lookups */ /* FIXME: set function? */ + + unsigned int id_lookup_mode; /* pci_lookup_mode flags which are set automatically */ + /* Default: PCI_LOOKUP_CACHE */ + char *id_domain; /* DNS domain used for the lookups (use pci_set_net_domain()) */ + int free_id_domain; /* Set if id_domain is malloced */ + char *id_cache_file; /* Name of the ID cache file (use pci_set_net_cache()) */ + int free_id_cache_file; /* Set if id_cache_file is malloced */ + int debugging; /* Turn on debugging messages */ /* Functions you can override: */ @@ -60,7 +67,8 @@ struct pci_access { struct pci_methods *methods; struct id_entry **id_hash; /* names.c */ struct id_bucket *current_id_bucket; - int hash_load_failed; + int id_load_failed; + int id_cache_status; /* 0=not read, 1=read, 2=dirty */ int fd; /* proc: fd */ int fd_rw; /* proc: fd opened read-write */ struct pci_dev *cached_dev; /* proc: device the fd is for */ @@ -164,6 +172,9 @@ char *pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) int pci_load_name_list(struct pci_access *a); /* Called automatically by pci_lookup_*() when needed; returns success */ void pci_free_name_list(struct pci_access *a); /* Called automatically by pci_cleanup() */ void pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed); +void pci_set_net_domain(struct pci_access *a, char *name, int to_be_freed); +void pci_set_id_cache(struct pci_access *a, char *name, int to_be_freed); +void pci_id_cache_flush(struct pci_access *a); enum pci_lookup_mode { PCI_LOOKUP_VENDOR = 1, /* Vendor name (args: vendorID) */ @@ -174,6 +185,10 @@ enum pci_lookup_mode { PCI_LOOKUP_NUMERIC = 0x10000, /* Want only formatted numbers; default if access->numeric_ids is set */ PCI_LOOKUP_NO_NUMBERS = 0x20000, /* Return NULL if not found in the database; default is to print numerically */ PCI_LOOKUP_MIXED = 0x40000, /* Include both numbers and names */ + PCI_LOOKUP_NETWORK = 0x80000, /* Try to resolve unknown ID's by DNS */ + PCI_LOOKUP_SKIP_LOCAL = 0x100000, /* Do not consult local database */ + PCI_LOOKUP_CACHE = 0x200000, /* Consult the local cache before using DNS */ + PCI_LOOKUP_REFRESH_CACHE = 0x400000, /* Forget all previously cached entries, but still allow updating the cache */ }; #endif diff --git a/lspci.c b/lspci.c index 3110ea0..b0ef90f 100644 --- a/lspci.c +++ b/lspci.c @@ -1,7 +1,7 @@ /* * The PCI Utilities -- List All PCI Devices * - * Copyright (c) 1997--2007 Martin Mares + * Copyright (c) 1997--2008 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -2738,6 +2738,8 @@ main(int argc, char **argv) if (optind < argc) goto bad; + /* FIXME */ + pacc->id_lookup_mode |= PCI_LOOKUP_NETWORK; pci_init(pacc); if (opt_map_mode) map_the_bus(); -- 2.39.5