summaryrefslogtreecommitdiff
path: root/com32
diff options
context:
space:
mode:
authorErwan Velu <erwan.velu@free.fr>2009-04-27 16:41:07 +0200
committerErwan Velu <erwan.velu@free.fr>2009-04-27 16:41:07 +0200
commita335437d0d97ad87db72a26fa4d4392853cd65d2 (patch)
tree600a37b5d80ecc7c26609e0c10df1f734b2a605a /com32
parent8518be2b8e1a9b04d71239610757da22758cc363 (diff)
downloadsyslinux-a335437d0d97ad87db72a26fa4d4392853cd65d2.tar.gz
pci: Adding get_module_name_from_alias()
Impact: Users can now use modules.alias to find kernel modules get_module_name_from_alias() parses modules.alias to find the appropriate kernel modules required by the local pci devices.
Diffstat (limited to 'com32')
-rw-r--r--com32/include/sys/pci.h4
-rw-r--r--com32/lib/pci/scan.c125
2 files changed, 128 insertions, 1 deletions
diff --git a/com32/include/sys/pci.h b/com32/include/sys/pci.h
index b44db62f..dc86ad86 100644
--- a/com32/include/sys/pci.h
+++ b/com32/include/sys/pci.h
@@ -18,7 +18,8 @@ typedef uint32_t pciaddr_t;
enum {
ENOPCIIDS = 100,
- ENOMODULESPCIMAP
+ ENOMODULESPCIMAP,
+ ENOMODULESALIAS
};
/* a structure for extended pci information */
@@ -145,6 +146,7 @@ struct match * find_pci_device(const struct pci_domain *pci_domain,
struct match *list);
int get_name_from_pci_ids(struct pci_domain *pci_domain, char *pciids_path);
int get_module_name_from_pcimap(struct pci_domain *pci_domain, char *modules_pcimap_path);
+int get_module_name_from_alias(struct pci_domain *pci_domain, char *modules_alias_path);
int get_class_name_from_pci_ids(struct pci_domain *pci_domain, char *pciids_path);
void gather_additional_pci_config(struct pci_domain *domain);
#endif /* _SYS_PCI_H */
diff --git a/com32/lib/pci/scan.c b/com32/lib/pci/scan.c
index 07794102..1b3e9d1f 100644
--- a/com32/lib/pci/scan.c
+++ b/com32/lib/pci/scan.c
@@ -570,3 +570,128 @@ void free_pci_domain(struct pci_domain *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++) {
+ strlcpy(dev->dev_info->linux_kernel_module[i], "unknown",7);
+ }
+ }
+
+ /* Opening the modules.pcimap (of a linux kernel) from the boot device */
+ f=fopen(modules_alias_path, "r");
+ if (!f)
+ return -ENOMODULESALIAS;
+
+ dev->dev_info->linux_kernel_module_count=0;
+
+ /* 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) {
+ strncpy(vendor_id,result,temp-result);
+ result+=strlen(vendor_id)+1;
+ }
+
+ /* Searching for the product separator*/
+ temp = strstr(result,"sv");
+ if (temp != NULL) {
+ strncpy(product_id,result,temp-result);
+ result+=strlen(product_id)+1;
+ }
+
+ /* Searching for the sub vendor separator*/
+ temp = strstr(result,"sd");
+ if (temp != NULL) {
+ strncpy(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) {
+ strncpy(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) {
+ 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;
+}