1/* ----------------------------------------------------------------------- *
2 *
3 *   Copyright 2006-2007 Erwan Velu - All Rights Reserved
4 *
5 *   Permission is hereby granted, free of charge, to any person
6 *   obtaining a copy of this software and associated documentation
7 *   files (the "Software"), to deal in the Software without
8 *   restriction, including without limitation the rights to use,
9 *   copy, modify, merge, publish, distribute, sublicense, and/or
10 *   sell copies of the Software, and to permit persons to whom
11 *   the Software is furnished to do so, subject to the following
12 *   conditions:
13 *
14 *   The above copyright notice and this permission notice shall
15 *   be included in all copies or substantial portions of the Software.
16 *
17 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 *   OTHER DEALINGS IN THE SOFTWARE.
25 *
26 * ----------------------------------------------------------------------- */
27
28/*
29 * pci.c
30 *
31 * A module to extract pci informations
32 */
33
34#include <inttypes.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <console.h>
39#include <sys/pci.h>
40#include <com32.h>
41#include <stdbool.h>
42#include <ctype.h>
43#include <syslinux/zio.h>
44#include <dprintf.h>
45
46#define MAX_LINE 512
47
48/* removing any \n found in a string */
49static void remove_eol(char *string)
50{
51    int j = strlen(string);
52    int i = 0;
53    for (i = 0; i < j; i++)
54	if (string[i] == '\n')
55	    string[i] = 0;
56}
57
58/* converting a hexa string into its numerical value */
59static int hex_to_int(char *hexa)
60{
61    return strtoul(hexa, NULL, 16);
62}
63
64/* Try to match any pci device to the appropriate kernel module */
65/* it uses the modules.pcimap from the boot device */
66int get_module_name_from_pcimap(struct pci_domain *domain,
67				char *modules_pcimap_path)
68{
69  char line[MAX_LINE];
70  char module_name[21]; // the module name field is 21 char long
71  char delims[]=" ";    // colums are separated by spaces
72  char vendor_id[16];
73  char product_id[16];
74  char sub_vendor_id[16];
75  char sub_product_id[16];
76  FILE *f;
77  struct pci_device *dev=NULL;
78
79  /* Intializing the linux_kernel_module for each pci device to "unknown" */
80  /* adding a dev_info member if needed */
81  for_each_pci_func(dev, domain) {
82    /* initialize the dev_info structure if it doesn't exist yet. */
83    if (! dev->dev_info) {
84      dev->dev_info = zalloc(sizeof *dev->dev_info);
85      if (!dev->dev_info)
86	return -1;
87    }
88    for (int i=0;i<MAX_KERNEL_MODULES_PER_PCI_DEVICE;i++) {
89     if (strlen(dev->dev_info->linux_kernel_module[i])==0)
90       strlcpy(dev->dev_info->linux_kernel_module[i], "unknown",7);
91    }
92  }
93
94  /* Opening the modules.pcimap (of a linux kernel) from the boot device */
95  f=zfopen(modules_pcimap_path, "r");
96  if (!f)
97    return -ENOMODULESPCIMAP;
98
99  strcpy(vendor_id,"0000");
100  strcpy(product_id,"0000");
101  strcpy(sub_product_id,"0000");
102  strcpy(sub_vendor_id,"0000");
103
104  /* for each line we found in the modules.pcimap */
105  while ( fgets(line, sizeof line, f) ) {
106    /* skipping unecessary lines */
107    if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 10))
108        continue;
109
110    char *result = NULL;
111    int field=0;
112
113    /* looking for the next field */
114    result = strtok(line, delims);
115    while( result != NULL ) {
116       /* if the column is larger than 1 char */
117       /* multiple spaces generates some empty fields */
118       if (strlen(result)>1) {
119	 switch (field) {
120	 /* About case 0, the kernel module name is featuring '_' or '-'
121	  * in the module name whereas modules.alias is only using '_'.
122	  * To avoid kernel modules duplication, let's rename all '-' in '_'
123	  * to match what modules.alias provides */
124	 case 0:chrreplace(result,'-','_');strcpy(module_name,result); break;
125	 case 1:strcpy(vendor_id,result); break;
126	 case 2:strcpy(product_id,result); break;
127	 case 3:strcpy(sub_vendor_id,result); break;
128	 case 4:strcpy(sub_product_id,result); break;
129	 }
130	 field++;
131       }
132       /* Searching the next field */
133       result = strtok( NULL, delims );
134   }
135    int int_vendor_id=hex_to_int(vendor_id);
136    int int_sub_vendor_id=hex_to_int(sub_vendor_id);
137    int int_product_id=hex_to_int(product_id);
138    int int_sub_product_id=hex_to_int(sub_product_id);
139    /* if a pci_device matches an entry, fill the linux_kernel_module with
140       the appropriate kernel module */
141    for_each_pci_func(dev, domain) {
142      if (int_vendor_id == dev->vendor &&
143	  int_product_id == dev->product &&
144	  (int_sub_product_id & dev->sub_product)
145	  == dev->sub_product &&
146	  (int_sub_vendor_id & dev->sub_vendor)
147	  == dev->sub_vendor) {
148	      bool found=false;
149
150	      /* Scan all known kernel modules for this pci device */
151	      for (int i=0; i<dev->dev_info->linux_kernel_module_count; i++) {
152
153       	      /* Try to detect if we already knew the same kernel module*/
154	       if (strstr(dev->dev_info->linux_kernel_module[i], module_name)) {
155		      found=true;
156		      break;
157	       }
158	      }
159	      /* If we don't have this kernel module, let's add it */
160	      if (!found) {
161		strcpy(dev->dev_info->linux_kernel_module[dev->dev_info->linux_kernel_module_count], module_name);
162		dev->dev_info->linux_kernel_module_count++;
163	      }
164      }
165    }
166  }
167  fclose(f);
168  return 0;
169}
170
171/* Try to match any pci device to the appropriate class name */
172/* it uses the pci.ids from the boot device */
173int get_class_name_from_pci_ids(struct pci_domain *domain, char *pciids_path)
174{
175    char line[MAX_LINE];
176    char class_name[PCI_CLASS_NAME_SIZE];
177    char sub_class_name[PCI_CLASS_NAME_SIZE];
178    char class_id_str[5];
179    char sub_class_id_str[5];
180    FILE *f;
181    struct pci_device *dev;
182    bool class_mode = false;
183
184    /* Intializing the vendor/product name for each pci device to "unknown" */
185    /* adding a dev_info member if needed */
186    for_each_pci_func(dev, domain) {
187	/* initialize the dev_info structure if it doesn't exist yet. */
188	if (!dev->dev_info) {
189	    dev->dev_info = zalloc(sizeof *dev->dev_info);
190	    if (!dev->dev_info)
191		return -1;
192	}
193	strlcpy(dev->dev_info->class_name, "unknown", 7);
194    }
195
196    /* Opening the pci.ids from the boot device */
197    f = zfopen(pciids_path, "r");
198    if (!f)
199	return -ENOPCIIDS;
200
201    /* for each line we found in the pci.ids */
202    while (fgets(line, sizeof line, f)) {
203	/* Skipping uncessary lines */
204	if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 10))
205	    continue;
206
207	/* Until we found a line starting with a 'C', we are not parsing classes */
208	if (line[0] == 'C')
209	    class_mode = true;
210	if (class_mode == false)
211	    continue;
212	strlcpy(class_name, "unknown", 7);
213	/* If the line doesn't start with a tab, it means that's a class name */
214	if (line[0] != '\t') {
215
216	    /* ignore the two first char and then copy 2 chars (class id) */
217	    strlcpy(class_id_str, &line[2], 2);
218	    class_id_str[2] = 0;
219
220	    /* the class name is the next field */
221	    strlcpy(class_name, skipspace(strstr(line, " ")),
222		    PCI_CLASS_NAME_SIZE - 1);
223	    remove_eol(class_name);
224
225	    int int_class_id_str = hex_to_int(class_id_str);
226	    /* assign the class_name to any matching pci device */
227	    for_each_pci_func(dev, domain) {
228		if (int_class_id_str == dev->class[2]) {
229		    strlcpy(dev->dev_info->class_name, class_name,
230			    PCI_CLASS_NAME_SIZE - 1);
231		    /* This value is usually the main category */
232		    strlcpy(dev->dev_info->category_name, class_name + 4,
233			    PCI_CLASS_NAME_SIZE - 1);
234		}
235	    }
236	    /* if we have a tab + a char, it means this is a sub class name */
237	} else if ((line[0] == '\t') && (line[1] != '\t')) {
238
239	    /* the sub class name the second field */
240	    strlcpy(sub_class_name, skipspace(strstr(line, " ")),
241		    PCI_CLASS_NAME_SIZE - 1);
242	    remove_eol(sub_class_name);
243
244	    /* the sub class id is first field */
245	    strlcpy(sub_class_id_str, &line[1], 2);
246	    sub_class_id_str[2] = 0;
247
248	    int int_class_id_str = hex_to_int(class_id_str);
249	    int int_sub_class_id_str = hex_to_int(sub_class_id_str);
250	    /* assign the product_name to any matching pci device */
251	    for_each_pci_func(dev, domain) {
252		if (int_class_id_str == dev->class[2] &&
253		    int_sub_class_id_str == dev->class[1])
254		    strlcpy(dev->dev_info->class_name, sub_class_name,
255			    PCI_CLASS_NAME_SIZE - 1);
256	    }
257
258	}
259    }
260    fclose(f);
261    return 0;
262}
263
264/* Try to match any pci device to the appropriate vendor and product name */
265/* it uses the pci.ids from the boot device */
266int get_name_from_pci_ids(struct pci_domain *domain, char *pciids_path)
267{
268    char line[MAX_LINE];
269    char vendor[PCI_VENDOR_NAME_SIZE];
270    char vendor_id[5];
271    char product[PCI_PRODUCT_NAME_SIZE];
272    char product_id[5];
273    char sub_product_id[5];
274    char sub_vendor_id[5];
275    FILE *f;
276    struct pci_device *dev;
277    bool skip_to_next_vendor = false;
278    uint16_t int_vendor_id;
279    uint16_t int_product_id;
280    uint16_t int_sub_product_id;
281    uint16_t int_sub_vendor_id;
282
283    /* Intializing the vendor/product name for each pci device to "unknown" */
284    /* adding a dev_info member if needed */
285    for_each_pci_func(dev, domain) {
286	/* initialize the dev_info structure if it doesn't exist yet. */
287	if (!dev->dev_info) {
288	    dev->dev_info = zalloc(sizeof *dev->dev_info);
289	    if (!dev->dev_info)
290		return -1;
291	}
292	strlcpy(dev->dev_info->vendor_name, "unknown", 7);
293	strlcpy(dev->dev_info->product_name, "unknown", 7);
294    }
295
296    /* Opening the pci.ids from the boot device */
297    f = zfopen(pciids_path, "r");
298    if (!f)
299	return -ENOPCIIDS;
300
301    strlcpy(vendor_id, "0000", 4);
302    strlcpy(product_id, "0000", 4);
303    strlcpy(sub_product_id, "0000", 4);
304    strlcpy(sub_vendor_id, "0000", 4);
305
306    /* for each line we found in the pci.ids */
307    while (fgets(line, sizeof line, f)) {
308	/* Skipping uncessary lines */
309	if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 'C') ||
310	    (line[0] == 10))
311	    continue;
312
313	/* If the line doesn't start with a tab, it means that's a vendor id */
314	if (line[0] != '\t') {
315
316	    /* the 4 first chars are the vendor_id */
317	    strlcpy(vendor_id, line, 4);
318
319	    /* the vendor name is the next field */
320	    vendor_id[4] = 0;
321	    strlcpy(vendor, skipspace(strstr(line, " ")),
322		    PCI_VENDOR_NAME_SIZE - 1);
323
324	    remove_eol(vendor);
325	    /* init product_id, sub_product and sub_vendor */
326	    strlcpy(product_id, "0000", 4);
327	    strlcpy(sub_product_id, "0000", 4);
328	    strlcpy(sub_vendor_id, "0000", 4);
329
330	    /* Unless we found a matching device, we have to skip to the next vendor */
331	    skip_to_next_vendor = true;
332
333	    int_vendor_id = hex_to_int(vendor_id);
334	    /* Iterate in all pci devices to find a matching vendor */
335	    for_each_pci_func(dev, domain) {
336		/* if one device that match this vendor */
337		if (int_vendor_id == dev->vendor) {
338		    /* copy the vendor name for this device */
339		    strlcpy(dev->dev_info->vendor_name, vendor,
340			    PCI_VENDOR_NAME_SIZE - 1);
341		    /* Some pci devices match this vendor, so we have to found them */
342		    skip_to_next_vendor = false;
343		    /* Let's loop on the other devices as some may have the same vendor */
344		}
345	    }
346	    /* if we have a tab + a char, it means this is a product id
347	     * but we only look at it if we own some pci devices of the current vendor*/
348	} else if ((line[0] == '\t') && (line[1] != '\t')
349		   && (skip_to_next_vendor == false)) {
350
351	    /* the product name the second field */
352	    strlcpy(product, skipspace(strstr(line, " ")),
353		    PCI_PRODUCT_NAME_SIZE - 1);
354	    remove_eol(product);
355
356	    /* the product id is first field */
357	    strlcpy(product_id, &line[1], 4);
358	    product_id[4] = 0;
359
360	    /* init sub_product and sub_vendor */
361	    strlcpy(sub_product_id, "0000", 4);
362	    strlcpy(sub_vendor_id, "0000", 4);
363
364	    int_vendor_id = hex_to_int(vendor_id);
365	    int_product_id = hex_to_int(product_id);
366	    /* assign the product_name to any matching pci device */
367	    for_each_pci_func(dev, domain) {
368		if (int_vendor_id == dev->vendor &&
369		    int_product_id == dev->product) {
370		    strlcpy(dev->dev_info->vendor_name, vendor,
371			    PCI_VENDOR_NAME_SIZE - 1);
372		    strlcpy(dev->dev_info->product_name, product,
373			    PCI_PRODUCT_NAME_SIZE - 1);
374		}
375	    }
376
377	    /* if we have two tabs, it means this is a sub product
378	     * but we only look at it if we own some pci devices of the current vendor*/
379	} else if ((line[0] == '\t') && (line[1] == '\t')
380		   && (skip_to_next_vendor == false)) {
381
382	    /* the product name is last field */
383	    strlcpy(product, skipspace(strstr(line, " ")),
384		    PCI_PRODUCT_NAME_SIZE - 1);
385	    strlcpy(product, skipspace(strstr(product, " ")),
386		    PCI_PRODUCT_NAME_SIZE - 1);
387	    remove_eol(product);
388
389	    /* the sub_vendor id is first field */
390	    strlcpy(sub_vendor_id, &line[2], 4);
391	    sub_vendor_id[4] = 0;
392
393	    /* the sub_vendor id is second field */
394	    strlcpy(sub_product_id, &line[7], 4);
395	    sub_product_id[4] = 0;
396
397	    int_vendor_id = hex_to_int(vendor_id);
398	    int_sub_vendor_id = hex_to_int(sub_vendor_id);
399	    int_product_id = hex_to_int(product_id);
400	    int_sub_product_id = hex_to_int(sub_product_id);
401	    /* assign the product_name to any matching pci device */
402	    for_each_pci_func(dev, domain) {
403		if (int_vendor_id == dev->vendor &&
404		    int_product_id == dev->product &&
405		    int_sub_product_id == dev->sub_product &&
406		    int_sub_vendor_id == dev->sub_vendor) {
407		    strlcpy(dev->dev_info->vendor_name, vendor,
408			    PCI_VENDOR_NAME_SIZE - 1);
409		    strlcpy(dev->dev_info->product_name, product,
410			    PCI_PRODUCT_NAME_SIZE - 1);
411		}
412	    }
413	}
414    }
415    fclose(f);
416    return 0;
417}
418
419/* searching if any pcidevice match our query */
420struct match *find_pci_device(const struct pci_domain *domain,
421			      struct match *list)
422{
423    uint32_t did, sid;
424    struct match *m;
425    const struct pci_device *dev;
426
427    /* for all matches we have to search */
428    for (m = list; m; m = m->next) {
429	/* for each pci device we know */
430	for_each_pci_func(dev, domain) {
431	    /* sid & did are the easiest way to compare devices */
432	    /* they are made of vendor/product subvendor/subproduct ids */
433	    sid = dev->svid_sdid;
434	    did = dev->vid_did;
435	    /* if the current device match */
436	    if (((did ^ m->did) & m->did_mask) == 0 &&
437		((sid ^ m->sid) & m->sid_mask) == 0 &&
438		dev->revision >= m->rid_min && dev->revision <= m->rid_max) {
439		dprintf
440		    ("PCI Match: Vendor=%04x Product=%04x Sub_vendor=%04x Sub_Product=%04x Release=%02x\n",
441		     dev->vendor, dev->product, dev->sub_vendor,
442		     dev->sub_product, dev->revision);
443		/* returning the matched pci device */
444		return m;
445	    }
446	}
447    }
448    return NULL;
449}
450
451/* scanning the pci bus to find pci devices */
452struct pci_domain *pci_scan(void)
453{
454    struct pci_domain *domain = NULL;
455    struct pci_bus *bus = NULL;
456    struct pci_slot *slot = NULL;
457    struct pci_device *func = NULL;
458    unsigned int nbus, ndev, nfunc, maxfunc;
459    uint32_t did, sid, rcid;
460    uint8_t hdrtype;
461    pciaddr_t a;
462    int cfgtype;
463
464    cfgtype = pci_set_config_type(PCI_CFG_AUTO);
465
466    dprintf("PCI configuration type %d\n", cfgtype);
467
468    if (cfgtype == PCI_CFG_NONE)
469	return NULL;
470
471    dprintf("Scanning PCI Buses\n");
472
473    for (nbus = 0; nbus < MAX_PCI_BUSES; nbus++) {
474	dprintf("Probing bus 0x%02x... \n", nbus);
475	bus = NULL;
476
477	for (ndev = 0; ndev < MAX_PCI_DEVICES; ndev++) {
478	    maxfunc = 1;	/* Assume a single-function device */
479	    slot = NULL;
480
481	    for (nfunc = 0; nfunc < maxfunc; nfunc++) {
482		a = pci_mkaddr(nbus, ndev, nfunc, 0);
483		did = pci_readl(a);
484
485		if (did == 0xffffffff || did == 0xffff0000 ||
486		    did == 0x0000ffff || did == 0x00000000)
487		    continue;
488
489		hdrtype = pci_readb(a + 0x0e);
490
491		if (hdrtype & 0x80)
492		    maxfunc = MAX_PCI_FUNC;	/* Multifunction device */
493
494		rcid = pci_readl(a + 0x08);
495		sid = pci_readl(a + 0x2c);
496
497		if (!domain) {
498		    domain = zalloc(sizeof *domain);
499		    if (!domain)
500			goto bail;
501		}
502		if (!bus) {
503		    bus = zalloc(sizeof *bus);
504		    if (!bus)
505			goto bail;
506		    domain->bus[nbus] = bus;
507		}
508		if (!slot) {
509		    slot = zalloc(sizeof *slot);
510		    if (!slot)
511			goto bail;
512		    bus->slot[ndev] = slot;
513		}
514		func = zalloc(sizeof *func);
515		if (!func)
516		    goto bail;
517
518		slot->func[nfunc] = func;
519
520		func->vid_did = did;
521		func->svid_sdid = sid;
522		func->rid_class = rcid;
523
524		dprintf
525		    ("Scanning: BUS %02x DID %08x (%04x:%04x) SID %08x RID %02x\n",
526		     nbus, did, did >> 16, (did << 16) >> 16, sid, rcid & 0xff);
527	    }
528	}
529    }
530
531    return domain;
532
533bail:
534    free_pci_domain(domain);
535    return NULL;
536}
537
538/* gathering additional configuration*/
539void gather_additional_pci_config(struct pci_domain *domain)
540{
541    struct pci_device *dev;
542    pciaddr_t pci_addr;
543    int cfgtype;
544
545    cfgtype = pci_set_config_type(PCI_CFG_AUTO);
546    if (cfgtype == PCI_CFG_NONE)
547	return;
548
549    for_each_pci_func3(dev, domain, pci_addr) {
550	if (!dev->dev_info) {
551	    dev->dev_info = zalloc(sizeof *dev->dev_info);
552	    if (!dev->dev_info) {
553		return;
554	    }
555	}
556	dev->dev_info->irq = pci_readb(pci_addr + 0x3c);
557	dev->dev_info->latency = pci_readb(pci_addr + 0x0d);
558    }
559}
560
561void free_pci_domain(struct pci_domain *domain)
562{
563    struct pci_bus *bus;
564    struct pci_slot *slot;
565    struct pci_device *func;
566    unsigned int nbus, ndev, nfunc;
567
568    if (domain) {
569	for (nbus = 0; nbus < MAX_PCI_BUSES; nbus++) {
570	    bus = domain->bus[nbus];
571	    if (bus) {
572		for (ndev = 0; ndev < MAX_PCI_DEVICES; ndev++) {
573		    slot = bus->slot[ndev];
574		    if (slot) {
575			for (nfunc = 0; nfunc < MAX_PCI_FUNC; nfunc++) {
576			    func = slot->func[nfunc];
577			    if (func) {
578				if (func->dev_info)
579				    free(func->dev_info);
580				free(func);
581			    }
582			}
583			free(slot);
584		    }
585		}
586		free(bus);
587	    }
588	}
589	free(domain);
590    }
591}
592
593/* Try to match any pci device to the appropriate kernel module */
594/* it uses the modules.alias from the boot device */
595int get_module_name_from_alias(struct pci_domain *domain, char *modules_alias_path)
596{
597  char line[MAX_LINE];
598  char module_name[21]; // the module name field is 21 char long
599  char delims[]="*";    // colums are separated by spaces
600  char vendor_id[16];
601  char product_id[16];
602  char sub_vendor_id[16];
603  char sub_product_id[16];
604  FILE *f;
605  struct pci_device *dev=NULL;
606
607  /* Intializing the linux_kernel_module for each pci device to "unknown" */
608  /* adding a dev_info member if needed */
609  for_each_pci_func(dev, domain) {
610    /* initialize the dev_info structure if it doesn't exist yet. */
611    if (! dev->dev_info) {
612      dev->dev_info = zalloc(sizeof *dev->dev_info);
613      if (!dev->dev_info)
614	return -1;
615    }
616    for (int i=0;i<MAX_KERNEL_MODULES_PER_PCI_DEVICE;i++) {
617     if (strlen(dev->dev_info->linux_kernel_module[i])==0)
618       strlcpy(dev->dev_info->linux_kernel_module[i], "unknown",7);
619    }
620  }
621
622  /* Opening the modules.pcimap (of a linux kernel) from the boot device */
623  f=zfopen(modules_alias_path, "r");
624  if (!f)
625    return -ENOMODULESALIAS;
626
627  /* for each line we found in the modules.pcimap */
628  while ( fgets(line, sizeof line, f) ) {
629    /* skipping unecessary lines */
630    if ((line[0] == '#') || (strstr(line,"alias pci:v")==NULL))
631        continue;
632
633    /* Resetting temp buffer*/
634    memset(module_name,0,sizeof(module_name));
635    memset(vendor_id,0,sizeof(vendor_id));
636    memset(sub_vendor_id,0,sizeof(sub_vendor_id));
637    memset(product_id,0,sizeof(product_id));
638    memset(sub_product_id,0,sizeof(sub_product_id));
639    strcpy(vendor_id,"0000");
640    strcpy(product_id,"0000");
641    /* ffff will be used to match any device as in modules.alias
642     * a missing subvendor/product have to be considered as  0xFFFF*/
643    strcpy(sub_product_id,"ffff");
644    strcpy(sub_vendor_id,"ffff");
645
646    char *result = NULL;
647    int field=0;
648
649    /* looking for the next field */
650    result = strtok(line+strlen("alias pci:v"), delims);
651    while( result != NULL ) {
652	if (field==0) {
653
654		/* Searching for the vendor separator*/
655		char *temp = strstr(result,"d");
656		if (temp != NULL) {
657			strlcpy(vendor_id,result,temp-result);
658			result+=strlen(vendor_id)+1;
659		}
660
661		/* Searching for the product separator*/
662		temp = strstr(result,"sv");
663		if (temp != NULL) {
664			strlcpy(product_id,result,temp-result);
665			result+=strlen(product_id)+1;
666		}
667
668		/* Searching for the sub vendor separator*/
669		temp = strstr(result,"sd");
670		if (temp != NULL) {
671			strlcpy(sub_vendor_id,result,temp-result);
672			result+=strlen(sub_vendor_id)+1;
673		}
674
675		/* Searching for the sub product separator*/
676		temp = strstr(result,"bc");
677		if (temp != NULL) {
678			strlcpy(sub_product_id,result,temp-result);
679			result+=strlen(sub_product_id)+1;
680		}
681	/* That's the module name */
682	} else if ((strlen(result)>2) &&
683			(result[0]==0x20))
684		strcpy(module_name,result+1);
685		/* We have to replace \n by \0*/
686		module_name[strlen(module_name)-1]='\0';
687	field++;
688
689	/* Searching the next field */
690        result = strtok( NULL, delims );
691    }
692
693    /* Now we have extracted informations from the modules.alias
694     * Let's compare it with the devices we know*/
695    int int_vendor_id=hex_to_int(vendor_id);
696    int int_sub_vendor_id=hex_to_int(sub_vendor_id);
697    int int_product_id=hex_to_int(product_id);
698    int int_sub_product_id=hex_to_int(sub_product_id);
699    /* if a pci_device matches an entry, fill the linux_kernel_module with
700       the appropriate kernel module */
701    for_each_pci_func(dev, domain) {
702      if (int_vendor_id == dev->vendor &&
703	  int_product_id == dev->product &&
704	  (int_sub_product_id & dev->sub_product)
705	  == dev->sub_product &&
706	  (int_sub_vendor_id & dev->sub_vendor)
707	  == dev->sub_vendor) {
708	      bool found=false;
709
710	      /* Scan all known kernel modules for this pci device */
711	      for (int i=0; i<dev->dev_info->linux_kernel_module_count; i++) {
712
713       	      /* Try to detect if we already knew the same kernel module*/
714	       if (strstr(dev->dev_info->linux_kernel_module[i], module_name)) {
715		      found=true;
716		      break;
717	       }
718	      }
719	      /* If we don't have this kernel module, let's add it */
720	      if (!found) {
721		strcpy(dev->dev_info->linux_kernel_module[dev->dev_info->linux_kernel_module_count], module_name);
722		dev->dev_info->linux_kernel_module_count++;
723	      }
724      }
725    }
726  }
727  fclose(f);
728  return 0;
729}
730