]> mj.ucw.cz Git - pciutils.git/blob - lib/names-hash.c
9661d03b308d13be31f22899f0f6eb2f8f21c8b3
[pciutils.git] / lib / names-hash.c
1 /*
2  *      The PCI Library -- ID to Name Hash
3  *
4  *      Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz>
5  *
6  *      Can be freely distributed and used under the terms of the GNU GPL.
7  */
8
9 #include <string.h>
10
11 #include "internal.h"
12 #include "names.h"
13
14 #ifdef PCI_HAVE_HWDB
15 #include <libudev.h>
16 #include <stdio.h>
17 #endif
18
19 struct id_bucket {
20   struct id_bucket *next;
21   unsigned int full;
22 };
23
24 #ifdef __GNUC__
25 #define BUCKET_ALIGNMENT __alignof__(struct id_bucket)
26 #else
27 union id_align {
28   struct id_bucket *next;
29   unsigned int full;
30 };
31 #define BUCKET_ALIGNMENT sizeof(union id_align)
32 #endif
33 #define BUCKET_ALIGN(n) ((n)+BUCKET_ALIGNMENT-(n)%BUCKET_ALIGNMENT)
34
35 static void *id_alloc(struct pci_access *a, unsigned int size)
36 {
37   struct id_bucket *buck = a->current_id_bucket;
38   unsigned int pos;
39
40   if (!a->id_hash)
41     {
42       a->id_hash = pci_malloc(a, sizeof(struct id_entry *) * HASH_SIZE);
43       memset(a->id_hash, 0, sizeof(struct id_entry *) * HASH_SIZE);
44     }
45
46   if (!buck || buck->full + size > BUCKET_SIZE)
47     {
48       buck = pci_malloc(a, BUCKET_SIZE);
49       buck->next = a->current_id_bucket;
50       a->current_id_bucket = buck;
51       buck->full = BUCKET_ALIGN(sizeof(struct id_bucket));
52     }
53   pos = buck->full;
54   buck->full = BUCKET_ALIGN(buck->full + size);
55   return (byte *)buck + pos;
56 }
57
58 static inline unsigned int id_hash(int cat, u32 id12, u32 id34)
59 {
60   unsigned int h;
61
62   h = id12 ^ (id34 << 3) ^ (cat << 5);
63   return h % HASH_SIZE;
64 }
65
66 int
67 pci_id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src)
68 {
69   u32 id12 = id_pair(id1, id2);
70   u32 id34 = id_pair(id3, id4);
71   unsigned int h = id_hash(cat, id12, id34);
72   struct id_entry *n = a->id_hash ? a->id_hash[h] : NULL;
73   int len = strlen(text);
74
75   while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat))
76     n = n->next;
77   if (n)
78     return 1;
79   n = id_alloc(a, sizeof(struct id_entry) + len);
80   n->id12 = id12;
81   n->id34 = id34;
82   n->cat = cat;
83   n->src = src;
84   memcpy(n->name, text, len+1);
85   n->next = a->id_hash[h];
86   a->id_hash[h] = n;
87   return 0;
88 }
89
90 char
91 *pci_id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4)
92 {
93   struct id_entry *n, *best;
94   u32 id12, id34;
95
96 #ifdef PCI_HAVE_HWDB
97   if (!(flags & PCI_LOOKUP_SKIP_LOCAL))
98     {
99       char modalias[64];
100       const char *key = NULL;
101       struct udev *udev = udev_new();
102       struct udev_hwdb *hwdb = udev_hwdb_new(udev);
103       struct udev_list_entry *entry;
104
105       switch(cat)
106         {
107         case ID_VENDOR:
108           sprintf(modalias, "pci:v%08X*", id1);
109           key = "ID_VENDOR_FROM_DATABASE";
110           break;
111         case ID_DEVICE:
112           sprintf(modalias, "pci:v%08Xd%08X*", id1, id2);
113           key = "ID_MODEL_FROM_DATABASE";
114           break;
115         case ID_SUBSYSTEM:
116           sprintf(modalias, "pci:v%08Xd%08Xsv%08Xsd%08X*", id1, id2, id3, id4);
117           key = "ID_MODEL_FROM_DATABASE";
118           break;
119         case ID_GEN_SUBSYSTEM:
120           sprintf(modalias, "pci:v*d*sv%08Xsd%08X*", id1, id2);
121           key = "ID_MODEL_FROM_DATABASE";
122           break;
123         case ID_CLASS:
124           sprintf(modalias, "pci:v*d*sv*sd*bc%02X*", id1);
125           key = "ID_PCI_CLASS_FROM_DATABASE";
126           break;
127         case ID_SUBCLASS:
128           sprintf(modalias, "pci:v*d*sv*sd*bc%02Xsc%02X*", id1, id2);
129           key = "ID_PCI_SUBCLASS_FROM_DATABASE";
130           break;
131         case ID_PROGIF:
132           sprintf(modalias, "pci:v*d*sv*sd*bc%02Xsc%02Xi%02X*", id1, id2, id3);
133           key = "ID_PCI_INTERFACE_FROM_DATABASE";
134           break;
135         }
136
137       if (key)
138           udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, modalias, 0))
139               if (strcmp(udev_list_entry_get_name(entry), key) == 0)
140                 return udev_list_entry_get_value(entry);
141     }
142 #endif
143
144   id12 = id_pair(id1, id2);
145   id34 = id_pair(id3, id4);
146
147   if (a->id_hash)
148     {
149       n = a->id_hash[id_hash(cat, id12, id34)];
150       best = NULL;
151       for (; n; n=n->next)
152         {
153           if (n->id12 != id12 || n->id34 != id34 || n->cat != cat)
154             continue;
155           if (n->src == SRC_LOCAL && (flags & PCI_LOOKUP_SKIP_LOCAL))
156             continue;
157           if (n->src == SRC_NET && !(flags & PCI_LOOKUP_NETWORK))
158             continue;
159           if (n->src == SRC_CACHE && !(flags & PCI_LOOKUP_CACHE))
160             continue;
161           if (!best || best->src < n->src)
162             best = n;
163         }
164       if (best)
165         return best->name;
166     }
167   return NULL;
168 }
169
170 void
171 pci_id_hash_free(struct pci_access *a)
172 {
173   pci_mfree(a->id_hash);
174   a->id_hash = NULL;
175   while (a->current_id_bucket)
176     {
177       struct id_bucket *buck = a->current_id_bucket;
178       a->current_id_bucket = buck->next;
179       pci_mfree(buck);
180     }
181 }