summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux-4.02/com32/lib/pci/scan.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux-4.02/com32/lib/pci/scan.c')
-rw-r--r--contrib/syslinux-4.02/com32/lib/pci/scan.c751
1 files changed, 751 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/com32/lib/pci/scan.c b/contrib/syslinux-4.02/com32/lib/pci/scan.c
new file mode 100644
index 0000000..2577b32
--- /dev/null
+++ b/contrib/syslinux-4.02/com32/lib/pci/scan.c
@@ -0,0 +1,751 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2006-2007 Erwan Velu - All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * pci.c
+ *
+ * A module to extract pci informations
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <console.h>
+#include <sys/pci.h>
+#include <com32.h>
+#include <stdbool.h>
+#include <syslinux/zio.h>
+
+#ifdef DEBUG
+# define dprintf printf
+#else
+# define dprintf(...) ((void)0)
+#endif
+
+#define MAX_LINE 512
+
+/* searching the next char that is not a space */
+static char *skipspace(char *p)
+{
+ while (*p && *p <= ' ')
+ p++;
+
+ return p;
+}
+
+/* removing any \n found in a string */
+static void remove_eol(char *string)
+{
+ int j = strlen(string);
+ int i = 0;
+ for (i = 0; i < j; i++)
+ if (string[i] == '\n')
+ string[i] = 0;
+}
+
+/* converting a hexa string into its numerical value */
+static int hex_to_int(char *hexa)
+{
+ return strtoul(hexa, NULL, 16);
+}
+
+/* Replace char 'old' by char 'new' in source */
+void chr_replace(char *source, char old, char new)
+{
+ while (*source) {
+ source++;
+ if (source[0] == old) source[0]=new;
+ }
+}
+
+/* Try to match any pci device to the appropriate kernel module */
+/* it uses the modules.pcimap from the boot device */
+int get_module_name_from_pcimap(struct pci_domain *domain,
+ char *modules_pcimap_path)
+{
+ char line[MAX_LINE];
+ char module_name[21]; // the module name field is 21 char long
+ char delims[]=" "; // colums are separated by spaces
+ char vendor_id[16];
+ char product_id[16];
+ char sub_vendor_id[16];
+ char sub_product_id[16];
+ FILE *f;
+ struct pci_device *dev=NULL;
+
+ /* Intializing the linux_kernel_module for each pci device to "unknown" */
+ /* adding a dev_info member if needed */
+ for_each_pci_func(dev, domain) {
+ /* initialize the dev_info structure if it doesn't exist yet. */
+ if (! dev->dev_info) {
+ dev->dev_info = zalloc(sizeof *dev->dev_info);
+ if (!dev->dev_info)
+ return -1;
+ }
+ for (int i=0;i<MAX_KERNEL_MODULES_PER_PCI_DEVICE;i++) {
+ if (strlen(dev->dev_info->linux_kernel_module[i])==0)
+ strlcpy(dev->dev_info->linux_kernel_module[i], "unknown",7);
+ }
+ }
+
+ /* Opening the modules.pcimap (of a linux kernel) from the boot device */
+ f=zfopen(modules_pcimap_path, "r");
+ if (!f)
+ return -ENOMODULESPCIMAP;
+
+ strcpy(vendor_id,"0000");
+ strcpy(product_id,"0000");
+ strcpy(sub_product_id,"0000");
+ strcpy(sub_vendor_id,"0000");
+
+ /* for each line we found in the modules.pcimap */
+ while ( fgets(line, sizeof line, f) ) {
+ /* skipping unecessary lines */
+ if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 10))
+ continue;
+
+ char *result = NULL;
+ int field=0;
+
+ /* looking for the next field */
+ result = strtok(line, delims);
+ while( result != NULL ) {
+ /* if the column is larger than 1 char */
+ /* multiple spaces generates some empty fields */
+ if (strlen(result)>1) {
+ switch (field) {
+ /* About case 0, the kernel module name is featuring '_' or '-'
+ * in the module name whereas modules.alias is only using '_'.
+ * To avoid kernel modules duplication, let's rename all '-' in '_'
+ * to match what modules.alias provides */
+ case 0:chr_replace(result,'-','_');strcpy(module_name,result); break;
+ case 1:strcpy(vendor_id,result); break;
+ case 2:strcpy(product_id,result); break;
+ case 3:strcpy(sub_vendor_id,result); break;
+ case 4:strcpy(sub_product_id,result); break;
+ }
+ field++;
+ }
+ /* Searching the next field */
+ result = strtok( NULL, delims );
+ }
+ int int_vendor_id=hex_to_int(vendor_id);
+ int int_sub_vendor_id=hex_to_int(sub_vendor_id);
+ int int_product_id=hex_to_int(product_id);
+ int int_sub_product_id=hex_to_int(sub_product_id);
+ /* if a pci_device matches an entry, fill the linux_kernel_module with
+ the appropriate kernel module */
+ for_each_pci_func(dev, domain) {
+ if (int_vendor_id == dev->vendor &&
+ int_product_id == dev->product &&
+ (int_sub_product_id & dev->sub_product)
+ == dev->sub_product &&
+ (int_sub_vendor_id & dev->sub_vendor)
+ == dev->sub_vendor) {
+ bool found=false;
+
+ /* Scan all known kernel modules for this pci device */
+ for (int i=0; i<dev->dev_info->linux_kernel_module_count; i++) {
+
+ /* Try to detect if we already knew the same kernel module*/
+ if (strstr(dev->dev_info->linux_kernel_module[i], module_name)) {
+ found=true;
+ break;
+ }
+ }
+ /* If we don't have this kernel module, let's add it */
+ if (!found) {
+ strcpy(dev->dev_info->linux_kernel_module[dev->dev_info->linux_kernel_module_count], module_name);
+ dev->dev_info->linux_kernel_module_count++;
+ }
+ }
+ }
+ }
+ fclose(f);
+ return 0;
+}
+
+/* Try to match any pci device to the appropriate class name */
+/* it uses the pci.ids from the boot device */
+int get_class_name_from_pci_ids(struct pci_domain *domain, char *pciids_path)
+{
+ char line[MAX_LINE];
+ char class_name[PCI_CLASS_NAME_SIZE];
+ char sub_class_name[PCI_CLASS_NAME_SIZE];
+ char class_id_str[5];
+ char sub_class_id_str[5];
+ FILE *f;
+ struct pci_device *dev;
+ bool class_mode = false;
+
+ /* Intializing the vendor/product name for each pci device to "unknown" */
+ /* adding a dev_info member if needed */
+ for_each_pci_func(dev, domain) {
+ /* initialize the dev_info structure if it doesn't exist yet. */
+ if (!dev->dev_info) {
+ dev->dev_info = zalloc(sizeof *dev->dev_info);
+ if (!dev->dev_info)
+ return -1;
+ }
+ strlcpy(dev->dev_info->class_name, "unknown", 7);
+ }
+
+ /* Opening the pci.ids from the boot device */
+ f = zfopen(pciids_path, "r");
+ if (!f)
+ return -ENOPCIIDS;
+
+ /* for each line we found in the pci.ids */
+ while (fgets(line, sizeof line, f)) {
+ /* Skipping uncessary lines */
+ if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 10))
+ continue;
+
+ /* Until we found a line starting with a 'C', we are not parsing classes */
+ if (line[0] == 'C')
+ class_mode = true;
+ if (class_mode == false)
+ continue;
+ strlcpy(class_name, "unknown", 7);
+ /* If the line doesn't start with a tab, it means that's a class name */
+ if (line[0] != '\t') {
+
+ /* ignore the two first char and then copy 2 chars (class id) */
+ strlcpy(class_id_str, &line[2], 2);
+ class_id_str[2] = 0;
+
+ /* the class name is the next field */
+ strlcpy(class_name, skipspace(strstr(line, " ")),
+ PCI_CLASS_NAME_SIZE - 1);
+ remove_eol(class_name);
+
+ int int_class_id_str = hex_to_int(class_id_str);
+ /* assign the class_name to any matching pci device */
+ for_each_pci_func(dev, domain) {
+ if (int_class_id_str == dev->class[2]) {
+ strlcpy(dev->dev_info->class_name, class_name,
+ PCI_CLASS_NAME_SIZE - 1);
+ /* This value is usually the main category */
+ strlcpy(dev->dev_info->category_name, class_name + 4,
+ PCI_CLASS_NAME_SIZE - 1);
+ }
+ }
+ /* if we have a tab + a char, it means this is a sub class name */
+ } else if ((line[0] == '\t') && (line[1] != '\t')) {
+
+ /* the sub class name the second field */
+ strlcpy(sub_class_name, skipspace(strstr(line, " ")),
+ PCI_CLASS_NAME_SIZE - 1);
+ remove_eol(sub_class_name);
+
+ /* the sub class id is first field */
+ strlcpy(sub_class_id_str, &line[1], 2);
+ sub_class_id_str[2] = 0;
+
+ int int_class_id_str = hex_to_int(class_id_str);
+ int int_sub_class_id_str = hex_to_int(sub_class_id_str);
+ /* assign the product_name to any matching pci device */
+ for_each_pci_func(dev, domain) {
+ if (int_class_id_str == dev->class[2] &&
+ int_sub_class_id_str == dev->class[1])
+ strlcpy(dev->dev_info->class_name, sub_class_name,
+ PCI_CLASS_NAME_SIZE - 1);
+ }
+
+ }
+ }
+ fclose(f);
+ return 0;
+}
+
+/* Try to match any pci device to the appropriate vendor and product name */
+/* it uses the pci.ids from the boot device */
+int get_name_from_pci_ids(struct pci_domain *domain, char *pciids_path)
+{
+ char line[MAX_LINE];
+ char vendor[PCI_VENDOR_NAME_SIZE];
+ char vendor_id[5];
+ char product[PCI_PRODUCT_NAME_SIZE];
+ char product_id[5];
+ char sub_product_id[5];
+ char sub_vendor_id[5];
+ FILE *f;
+ struct pci_device *dev;
+ bool skip_to_next_vendor = false;
+ uint16_t int_vendor_id;
+ uint16_t int_product_id;
+ uint16_t int_sub_product_id;
+ uint16_t int_sub_vendor_id;
+
+ /* Intializing the vendor/product name for each pci device to "unknown" */
+ /* adding a dev_info member if needed */
+ for_each_pci_func(dev, domain) {
+ /* initialize the dev_info structure if it doesn't exist yet. */
+ if (!dev->dev_info) {
+ dev->dev_info = zalloc(sizeof *dev->dev_info);
+ if (!dev->dev_info)
+ return -1;
+ }
+ strlcpy(dev->dev_info->vendor_name, "unknown", 7);
+ strlcpy(dev->dev_info->product_name, "unknown", 7);
+ }
+
+ /* Opening the pci.ids from the boot device */
+ f = zfopen(pciids_path, "r");
+ if (!f)
+ return -ENOPCIIDS;
+
+ strlcpy(vendor_id, "0000", 4);
+ strlcpy(product_id, "0000", 4);
+ strlcpy(sub_product_id, "0000", 4);
+ strlcpy(sub_vendor_id, "0000", 4);
+
+ /* for each line we found in the pci.ids */
+ while (fgets(line, sizeof line, f)) {
+ /* Skipping uncessary lines */
+ if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 'C') ||
+ (line[0] == 10))
+ continue;
+
+ /* If the line doesn't start with a tab, it means that's a vendor id */
+ if (line[0] != '\t') {
+
+ /* the 4 first chars are the vendor_id */
+ strlcpy(vendor_id, line, 4);
+
+ /* the vendor name is the next field */
+ vendor_id[4] = 0;
+ strlcpy(vendor, skipspace(strstr(line, " ")),
+ PCI_VENDOR_NAME_SIZE - 1);
+
+ remove_eol(vendor);
+ /* init product_id, sub_product and sub_vendor */
+ strlcpy(product_id, "0000", 4);
+ strlcpy(sub_product_id, "0000", 4);
+ strlcpy(sub_vendor_id, "0000", 4);
+
+ /* Unless we found a matching device, we have to skip to the next vendor */
+ skip_to_next_vendor = true;
+
+ int_vendor_id = hex_to_int(vendor_id);
+ /* Iterate in all pci devices to find a matching vendor */
+ for_each_pci_func(dev, domain) {
+ /* if one device that match this vendor */
+ if (int_vendor_id == dev->vendor) {
+ /* copy the vendor name for this device */
+ strlcpy(dev->dev_info->vendor_name, vendor,
+ PCI_VENDOR_NAME_SIZE - 1);
+ /* Some pci devices match this vendor, so we have to found them */
+ skip_to_next_vendor = false;
+ /* Let's loop on the other devices as some may have the same vendor */
+ }
+ }
+ /* if we have a tab + a char, it means this is a product id
+ * but we only look at it if we own some pci devices of the current vendor*/
+ } else if ((line[0] == '\t') && (line[1] != '\t')
+ && (skip_to_next_vendor == false)) {
+
+ /* the product name the second field */
+ strlcpy(product, skipspace(strstr(line, " ")),
+ PCI_PRODUCT_NAME_SIZE - 1);
+ remove_eol(product);
+
+ /* the product id is first field */
+ strlcpy(product_id, &line[1], 4);
+ product_id[4] = 0;
+
+ /* init sub_product and sub_vendor */
+ strlcpy(sub_product_id, "0000", 4);
+ strlcpy(sub_vendor_id, "0000", 4);
+
+ int_vendor_id = hex_to_int(vendor_id);
+ int_product_id = hex_to_int(product_id);
+ /* assign the product_name to any matching pci device */
+ for_each_pci_func(dev, domain) {
+ if (int_vendor_id == dev->vendor &&
+ int_product_id == dev->product) {
+ strlcpy(dev->dev_info->vendor_name, vendor,
+ PCI_VENDOR_NAME_SIZE - 1);
+ strlcpy(dev->dev_info->product_name, product,
+ PCI_PRODUCT_NAME_SIZE - 1);
+ }
+ }
+
+ /* if we have two tabs, it means this is a sub product
+ * but we only look at it if we own some pci devices of the current vendor*/
+ } else if ((line[0] == '\t') && (line[1] == '\t')
+ && (skip_to_next_vendor == false)) {
+
+ /* the product name is last field */
+ strlcpy(product, skipspace(strstr(line, " ")),
+ PCI_PRODUCT_NAME_SIZE - 1);
+ strlcpy(product, skipspace(strstr(product, " ")),
+ PCI_PRODUCT_NAME_SIZE - 1);
+ remove_eol(product);
+
+ /* the sub_vendor id is first field */
+ strlcpy(sub_vendor_id, &line[2], 4);
+ sub_vendor_id[4] = 0;
+
+ /* the sub_vendor id is second field */
+ strlcpy(sub_product_id, &line[7], 4);
+ sub_product_id[4] = 0;
+
+ int_vendor_id = hex_to_int(vendor_id);
+ int_sub_vendor_id = hex_to_int(sub_vendor_id);
+ int_product_id = hex_to_int(product_id);
+ int_sub_product_id = hex_to_int(sub_product_id);
+ /* assign the product_name to any matching pci device */
+ for_each_pci_func(dev, domain) {
+ if (int_vendor_id == dev->vendor &&
+ int_product_id == dev->product &&
+ int_sub_product_id == dev->sub_product &&
+ int_sub_vendor_id == dev->sub_vendor) {
+ strlcpy(dev->dev_info->vendor_name, vendor,
+ PCI_VENDOR_NAME_SIZE - 1);
+ strlcpy(dev->dev_info->product_name, product,
+ PCI_PRODUCT_NAME_SIZE - 1);
+ }
+ }
+ }
+ }
+ fclose(f);
+ return 0;
+}
+
+/* searching if any pcidevice match our query */
+struct match *find_pci_device(const struct pci_domain *domain,
+ struct match *list)
+{
+ uint32_t did, sid;
+ struct match *m;
+ const struct pci_device *dev;
+
+ /* for all matches we have to search */
+ for (m = list; m; m = m->next) {
+ /* for each pci device we know */
+ for_each_pci_func(dev, domain) {
+ /* sid & did are the easiest way to compare devices */
+ /* they are made of vendor/product subvendor/subproduct ids */
+ sid = dev->svid_sdid;
+ did = dev->vid_did;
+ /* if the current device match */
+ if (((did ^ m->did) & m->did_mask) == 0 &&
+ ((sid ^ m->sid) & m->sid_mask) == 0 &&
+ dev->revision >= m->rid_min && dev->revision <= m->rid_max) {
+ dprintf
+ ("PCI Match: Vendor=%04x Product=%04x Sub_vendor=%04x Sub_Product=%04x Release=%02x\n",
+ dev->vendor, dev->product, dev->sub_vendor,
+ dev->sub_product, dev->revision);
+ /* returning the matched pci device */
+ return m;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* scanning the pci bus to find pci devices */
+struct pci_domain *pci_scan(void)
+{
+ struct pci_domain *domain = NULL;
+ struct pci_bus *bus = NULL;
+ struct pci_slot *slot = NULL;
+ struct pci_device *func = NULL;
+ unsigned int nbus, ndev, nfunc, maxfunc;
+ uint32_t did, sid, rcid;
+ uint8_t hdrtype;
+ pciaddr_t a;
+ int cfgtype;
+
+ cfgtype = pci_set_config_type(PCI_CFG_AUTO);
+
+ dprintf("PCI configuration type %d\n", cfgtype);
+
+ if (cfgtype == PCI_CFG_NONE)
+ return NULL;
+
+ dprintf("Scanning PCI Buses\n");
+
+ for (nbus = 0; nbus < MAX_PCI_BUSES; nbus++) {
+ dprintf("Probing bus 0x%02x... \n", nbus);
+ bus = NULL;
+
+ for (ndev = 0; ndev < MAX_PCI_DEVICES; ndev++) {
+ maxfunc = 1; /* Assume a single-function device */
+ slot = NULL;
+
+ for (nfunc = 0; nfunc < maxfunc; nfunc++) {
+ a = pci_mkaddr(nbus, ndev, nfunc, 0);
+ did = pci_readl(a);
+
+ if (did == 0xffffffff || did == 0xffff0000 ||
+ did == 0x0000ffff || did == 0x00000000)
+ continue;
+
+ hdrtype = pci_readb(a + 0x0e);
+
+ if (hdrtype & 0x80)
+ maxfunc = MAX_PCI_FUNC; /* Multifunction device */
+
+ rcid = pci_readl(a + 0x08);
+ sid = pci_readl(a + 0x2c);
+
+ if (!domain) {
+ domain = zalloc(sizeof *domain);
+ if (!domain)
+ goto bail;
+ }
+ if (!bus) {
+ bus = zalloc(sizeof *bus);
+ if (!bus)
+ goto bail;
+ domain->bus[nbus] = bus;
+ }
+ if (!slot) {
+ slot = zalloc(sizeof *slot);
+ if (!slot)
+ goto bail;
+ bus->slot[ndev] = slot;
+ }
+ func = zalloc(sizeof *func);
+ if (!func)
+ goto bail;
+
+ slot->func[nfunc] = func;
+
+ func->vid_did = did;
+ func->svid_sdid = sid;
+ func->rid_class = rcid;
+
+ dprintf
+ ("Scanning: BUS %02x DID %08x (%04x:%04x) SID %08x RID %02x\n",
+ nbus, did, did >> 16, (did << 16) >> 16, sid, rcid & 0xff);
+ }
+ }
+ }
+
+ return domain;
+
+bail:
+ free_pci_domain(domain);
+ return NULL;
+}
+
+/* gathering additional configuration*/
+void gather_additional_pci_config(struct pci_domain *domain)
+{
+ struct pci_device *dev;
+ pciaddr_t pci_addr;
+ int cfgtype;
+
+ cfgtype = pci_set_config_type(PCI_CFG_AUTO);
+ if (cfgtype == PCI_CFG_NONE)
+ return;
+
+ for_each_pci_func3(dev, domain, pci_addr) {
+ if (!dev->dev_info) {
+ dev->dev_info = zalloc(sizeof *dev->dev_info);
+ if (!dev->dev_info) {
+ return;
+ }
+ }
+ dev->dev_info->irq = pci_readb(pci_addr + 0x3c);
+ dev->dev_info->latency = pci_readb(pci_addr + 0x0d);
+ }
+}
+
+void free_pci_domain(struct pci_domain *domain)
+{
+ struct pci_bus *bus;
+ struct pci_slot *slot;
+ struct pci_device *func;
+ unsigned int nbus, ndev, nfunc;
+
+ if (domain) {
+ for (nbus = 0; nbus < MAX_PCI_BUSES; nbus++) {
+ bus = domain->bus[nbus];
+ if (bus) {
+ for (ndev = 0; ndev < MAX_PCI_DEVICES; ndev++) {
+ slot = bus->slot[ndev];
+ if (slot) {
+ for (nfunc = 0; nfunc < MAX_PCI_FUNC; nfunc++) {
+ func = slot->func[nfunc];
+ if (func) {
+ if (func->dev_info)
+ free(func->dev_info);
+ free(func);
+ }
+ free(slot);
+ }
+ }
+ free(bus);
+ }
+ }
+ free(domain);
+ }
+ }
+}
+
+/* Try to match any pci device to the appropriate kernel module */
+/* it uses the modules.alias from the boot device */
+int get_module_name_from_alias(struct pci_domain *domain, char *modules_alias_path)
+{
+ char line[MAX_LINE];
+ char module_name[21]; // the module name field is 21 char long
+ char delims[]="*"; // colums are separated by spaces
+ char vendor_id[16];
+ char product_id[16];
+ char sub_vendor_id[16];
+ char sub_product_id[16];
+ FILE *f;
+ struct pci_device *dev=NULL;
+
+ /* Intializing the linux_kernel_module for each pci device to "unknown" */
+ /* adding a dev_info member if needed */
+ for_each_pci_func(dev, domain) {
+ /* initialize the dev_info structure if it doesn't exist yet. */
+ if (! dev->dev_info) {
+ dev->dev_info = zalloc(sizeof *dev->dev_info);
+ if (!dev->dev_info)
+ return -1;
+ }
+ for (int i=0;i<MAX_KERNEL_MODULES_PER_PCI_DEVICE;i++) {
+ if (strlen(dev->dev_info->linux_kernel_module[i])==0)
+ strlcpy(dev->dev_info->linux_kernel_module[i], "unknown",7);
+ }
+ }
+
+ /* Opening the modules.pcimap (of a linux kernel) from the boot device */
+ f=zfopen(modules_alias_path, "r");
+ if (!f)
+ return -ENOMODULESALIAS;
+
+ /* for each line we found in the modules.pcimap */
+ while ( fgets(line, sizeof line, f) ) {
+ /* skipping unecessary lines */
+ if ((line[0] == '#') || (strstr(line,"alias pci:v")==NULL))
+ continue;
+
+ /* Resetting temp buffer*/
+ memset(module_name,0,sizeof(module_name));
+ memset(vendor_id,0,sizeof(vendor_id));
+ memset(sub_vendor_id,0,sizeof(sub_vendor_id));
+ memset(product_id,0,sizeof(product_id));
+ memset(sub_product_id,0,sizeof(sub_product_id));
+ strcpy(vendor_id,"0000");
+ strcpy(product_id,"0000");
+ /* ffff will be used to match any device as in modules.alias
+ * a missing subvendor/product have to be considered as 0xFFFF*/
+ strcpy(sub_product_id,"ffff");
+ strcpy(sub_vendor_id,"ffff");
+
+ char *result = NULL;
+ int field=0;
+
+ /* looking for the next field */
+ result = strtok(line+strlen("alias pci:v"), delims);
+ while( result != NULL ) {
+ if (field==0) {
+
+ /* Searching for the vendor separator*/
+ char *temp = strstr(result,"d");
+ if (temp != NULL) {
+ strlcpy(vendor_id,result,temp-result);
+ result+=strlen(vendor_id)+1;
+ }
+
+ /* Searching for the product separator*/
+ temp = strstr(result,"sv");
+ if (temp != NULL) {
+ strlcpy(product_id,result,temp-result);
+ result+=strlen(product_id)+1;
+ }
+
+ /* Searching for the sub vendor separator*/
+ temp = strstr(result,"sd");
+ if (temp != NULL) {
+ strlcpy(sub_vendor_id,result,temp-result);
+ result+=strlen(sub_vendor_id)+1;
+ }
+
+ /* Searching for the sub product separator*/
+ temp = strstr(result,"bc");
+ if (temp != NULL) {
+ strlcpy(sub_product_id,result,temp-result);
+ result+=strlen(sub_product_id)+1;
+ }
+ /* That's the module name */
+ } else if ((strlen(result)>2) &&
+ (result[0]==0x20))
+ strcpy(module_name,result+1);
+ /* We have to replace \n by \0*/
+ module_name[strlen(module_name)-1]='\0';
+ field++;
+
+ /* Searching the next field */
+ result = strtok( NULL, delims );
+ }
+
+ /* Now we have extracted informations from the modules.alias
+ * Let's compare it with the devices we know*/
+ int int_vendor_id=hex_to_int(vendor_id);
+ int int_sub_vendor_id=hex_to_int(sub_vendor_id);
+ int int_product_id=hex_to_int(product_id);
+ int int_sub_product_id=hex_to_int(sub_product_id);
+ /* if a pci_device matches an entry, fill the linux_kernel_module with
+ the appropriate kernel module */
+ for_each_pci_func(dev, domain) {
+ if (int_vendor_id == dev->vendor &&
+ int_product_id == dev->product &&
+ (int_sub_product_id & dev->sub_product)
+ == dev->sub_product &&
+ (int_sub_vendor_id & dev->sub_vendor)
+ == dev->sub_vendor) {
+ bool found=false;
+
+ /* Scan all known kernel modules for this pci device */
+ for (int i=0; i<dev->dev_info->linux_kernel_module_count; i++) {
+
+ /* Try to detect if we already knew the same kernel module*/
+ if (strstr(dev->dev_info->linux_kernel_module[i], module_name)) {
+ found=true;
+ break;
+ }
+ }
+ /* If we don't have this kernel module, let's add it */
+ if (!found) {
+ strcpy(dev->dev_info->linux_kernel_module[dev->dev_info->linux_kernel_module_count], module_name);
+ dev->dev_info->linux_kernel_module_count++;
+ }
+ }
+ }
+ }
+ fclose(f);
+ return 0;
+}