Support "other storage device" PCI class.
[bootmanager.git] / source / systeminfo.py
index 2faf3c2..8de50b7 100755 (executable)
@@ -64,9 +64,9 @@
 import string
 import os
 import popen2
-from merge_hw_tables import merge_hw_tables
-
+import merge_hw_tables
 
+hwdatapath = "usr/share/hwdata"
 class systeminfo:
     """
     a utility class for finding and returning information about
@@ -93,10 +93,15 @@ class systeminfo:
     MODULE_CLASS_NETWORK= "network"
     MODULE_CLASS_SCSI= "scsi"
 
-    PCI_CLASS_NETWORK= "0200"
-    PCI_CLASS_RAID= "0104"
-    PCI_CLASS_RAID2= "0100"
+    PCI_CLASS_NETWORK_ETHERNET=0x0200L
+    PCI_CLASS_STORAGE_SCSI=0x0100L
+    PCI_CLASS_STORAGE_IDE=0x0101L
+    PCI_CLASS_STORAGE_FLOPPY=0x0102L
+    PCI_CLASS_STORAGE_IPI=0x0103L
+    PCI_CLASS_STORAGE_RAID=0x0104L
+    PCI_CLASS_STORAGE_OTHER=0x0180L
 
+    PCI_ANY=0xffffffffL
 
     def get_total_phsyical_mem(self):
         """
@@ -222,12 +227,6 @@ class systeminfo:
             
             device= parts[3]
 
-            # if the last char in device is a number, its
-            # a partition, and we ignore it
-            
-            if device[len(device)-1].isdigit():
-                continue
-
             dev_name= "/dev/%s" % device
             
             try:
@@ -237,6 +236,10 @@ class systeminfo:
             except ValueError, err:
                 continue
 
+            # skip and ignore any partitions
+            if minor != 0:
+                continue
+                
             gb_size= blocks/self.BLOCKS_PER_GB
             
             # parse the output of hdparm <disk> to get the readonly flag;
@@ -289,42 +292,50 @@ class systeminfo:
 
 
 
-    def get_system_modules( self, install_root ):
+    def get_system_modules( self, install_root, kernel_version= None ):
         """
         Return a list of kernel modules that this system requires.
         This requires access to the installed system's root
         directory, as the following files must exist and are used:
         <install_root>/usr/share/hwdata/pcitable
-        <install_root>/lib/modules/(first entry)/modules.pcimap
-        <install_root>/lib/modules/(first entry)/modules.dep
+        <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
+        <install_root>/lib/modules/(first entry if kernel version unspecified)/modules.dep
 
-        Note, that this assumes there is only one kernel
-        that is installed. If there are more than one, then
-        only the first one in a directory listing is used.
+        If there are more than one kernels installed, and the kernel
+        version is not specified, then only the first one in
+        /lib/modules is used.
 
         Returns a dictionary, keys being the type of module:
          - scsi       MODULE_CLASS_SCSI
          - network    MODULE_CLASS_NETWORK
         The value being the kernel module name to load.
+
+        Some sata devices show up under an IDE device class,
+        hence the reason for checking for ide devices as well.
+        If there actually is a match in the pci -> module lookup
+        table, and its an ide device, its most likely sata,
+        as ide modules are built in to the kernel.
         """
 
         # get the kernel version we are assuming
-        try:
-            kernel_version= os.listdir( "%s/lib/modules/" % install_root )
-        except OSError, e:
-            return
+        if kernel_version is None:
+            try:
+                kernel_version= os.listdir( "%s/lib/modules/" % install_root )
+            except OSError, e:
+                return
 
-        if len(kernel_version) == 0:
-            return
+            if len(kernel_version) == 0:
+                return
+
+            if len(kernel_version) > 1:
+                print( "WARNING: We may be returning modules for the wrong kernel." )
 
-        if len(kernel_version) > 1:
-            print( "WARNING: We may be returning modules for the wrong kernel." )
+            kernel_version= kernel_version[0]
 
-        kernel_version= kernel_version[0]
         print( "Using kernel version %s" % kernel_version )
 
         # test to make sure the three files we need are present
-        pcitable_path = "%s/usr/share/hwdata/pcitable" % install_root
+        pcitable_path = "%s/%s/pcitable" % (install_root,hwdatapath)
         modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
                               (install_root,kernel_version)
         modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
@@ -337,15 +348,13 @@ class systeminfo:
 
         # now, with those three files, merge them all into one easy to
         # use lookup table
-        all_modules= merge_hw_tables().merge_files( modules_dep_path,
-                                                    modules_pcimap_path,
-                                                    pcitable_path )
-
+        (all_pci_ids, all_modules) = merge_hw_tables.merge_files( modules_dep_path,
+                                                                  modules_pcimap_path,
+                                                                  pcitable_path )
         if all_modules is None:
             print( "Unable to merge pci id tables." )
             return
 
-
         # this is the actual data structure we return
         system_mods= {}
 
@@ -367,6 +376,9 @@ class systeminfo:
         else:
             print( "Successfully ran %s" % self.LSPCI_CMD )
             
+        # for every lspci line, parse in the four tuple PCI id and the
+        # search for the corresponding driver from the dictionary
+        # generated by merge_hw_tables
         for line in lspci_prog.fromchild:
             if string.strip(line) == "":
                 continue
@@ -375,27 +387,111 @@ class systeminfo:
 
             try:
                 classid= self.remove_quotes(parts[2])
+                classid= long(classid,16)
+            except IndexError:
+                print( "Skipping invalid classid:", string.strip(line) )
+                continue
+            except ValueError, e:
+                print( "Skipping invalid classid:", string.strip(line) )
+                continue
+
+
+            if classid not in (self.PCI_CLASS_NETWORK_ETHERNET,
+                               self.PCI_CLASS_STORAGE_SCSI,
+                               self.PCI_CLASS_STORAGE_RAID,
+                               self.PCI_CLASS_STORAGE_OTHER,
+                               self.PCI_CLASS_STORAGE_IDE):
+                continue
+
+            vendorid    = self.PCI_ANY
+            deviceid    = self.PCI_ANY
+            subvendorid = self.PCI_ANY
+            subdeviceid = self.PCI_ANY
+
+            # parse in vendorid
+            try:
                 vendorid= self.remove_quotes(parts[3])
-                deviceid= self.remove_quotes(parts[4])
+                vendorid= long(vendorid,16)
             except IndexError:
-                print( "Skipping invalid line:", string.strip(line) )
+                print( "Skipping invalid vendorid:", string.strip(line) )
+                continue
+            except ValueError, e:
+                print( "Skipping invalid vendorid:", string.strip(line) )
                 continue
 
-            if classid not in (self.PCI_CLASS_NETWORK,
-                               self.PCI_CLASS_RAID,
-                               self.PCI_CLASS_RAID2):                    
+            # parse in deviceid
+            try:
+                deviceid= self.remove_quotes(parts[4])
+                deviceid= long(deviceid,16)
+            except IndexError:
+                print( "Skipping invalid deviceid:", string.strip(line) )
                 continue
-            
-            full_deviceid= "%s:%s" % (vendorid,deviceid)
-
-            for module in all_modules.keys():
-                if full_deviceid in all_modules[module]:
-                    if classid == self.PCI_CLASS_NETWORK:
-                        network_mods.append(module)
-                    elif classid in (self.PCI_CLASS_RAID,
-                                     self.PCI_CLASS_RAID2):
-                        scsi_mods.append(module)
-                        
+            except ValueError, e:
+                print( "Skipping invalid deviceid:", string.strip(line) )
+                continue
+
+            # Now get the subvendor & subdevice portion by searching
+            # parts[5:] of the lspci output. Note that we have to skip
+            # the portions of the lspci output string that indicate
+            # revision info.
+
+            # parse in subvendorid
+            subvendorindex = -1
+            for i in range(5,len(parts)):
+                p = self.remove_quotes(parts[i])
+                if p[0] != '-':
+                    subvendorindex = i
+                    break
+
+            if subvendorindex != -1:
+                try:
+                    subvendorid= self.remove_quotes(parts[subvendorindex])
+                    subvendorid= long(subvendorid,16)
+                except IndexError:
+                    print( "Skipping invalid line:", string.strip(line) )
+                    continue
+                except ValueError, e:
+                    print( "Skipping invalid line:", string.strip(line) )
+                    continue
+
+            # parse in subdeviceid
+            subdeviceindex = -1
+            for i in range(subvendorindex+1,len(parts)):
+                p = self.remove_quotes(parts[i])
+                if p[0] != '-':
+                    subdeviceindex = i
+                    break
+            if subdeviceindex != -1:
+                error_msg = "Skipping invalid subdeviceid:"
+                try:
+                    subdeviceid= self.remove_quotes(parts[subdeviceindex])
+                    subdeviceid= long(subdeviceid,16)
+                except IndexError:
+                    print( error_msg, string.strip(line) )
+                    continue
+                except ValueError, e:
+                    print( error_msg, string.strip(line) )
+                    continue
+
+            # search for driver that most closely matches the full_id
+            # to drivers that can handle any subvendor/subdevice
+            # version of the hardware.
+            full_ids = ((vendorid,deviceid,subvendorid,subdeviceid),
+                        (vendorid,deviceid,subvendorid,self.PCI_ANY),
+                        (vendorid,deviceid,self.PCI_ANY,self.PCI_ANY))
+
+            for full_id in full_ids:
+                module = all_pci_ids.get(full_id, None)
+                if module is not None:
+                    if classid == self.PCI_CLASS_NETWORK_ETHERNET:
+                        network_mods.append(module[0])
+                    elif classid in (self.PCI_CLASS_STORAGE_SCSI,
+                                     self.PCI_CLASS_STORAGE_RAID,
+                                     self.PCI_CLASS_STORAGE_OTHER,
+                                     self.PCI_CLASS_STORAGE_IDE):
+                        scsi_mods.append(module[0])
+                    break
+
         system_mods[self.MODULE_CLASS_SCSI]= scsi_mods
         system_mods[self.MODULE_CLASS_NETWORK]= network_mods
         
@@ -437,7 +533,13 @@ if __name__ == "__main__":
         
 
     print ""
-    modules= info.get_system_modules("/")
+
+    import sys
+    kernel_version = None
+    if len(sys.argv) > 2:
+        kernel_version = sys.argv[1]
+        
+    modules= info.get_system_modules("/",kernel_version)
     if not modules:
         print "unable to list system modules"
     else: