213ac00d27110c22c71fdbbb2bb6e565ea89ff43
[bootmanager.git] / source / merge_hw_tables.py
1 #!/usr/bin/env python
2
3 """
4 The point of this small utility is to take a file in the format
5 of /lib/modules/`uname -r`/modules.pcimap and /usr/share/hwdata/pcitable
6 and output a condensed, more easily used format for module detection. This is
7 done by first getting a list of all the built modules, then loading the
8 pci ids for each of those modules from modules.pcimap, then finally merging
9 in the contents of pcitable (for built modules only). The result should be
10 a file with a pretty comprehensive mapping of pci ids to module names.
11
12 The output is used by the PlanetLab boot cd (3.0+) and the pl_hwinit script
13 to load all the applicable modules by scanning lspci output.
14
15
16
17 Expected format of file modules.dep includes lines of:
18
19 /full/module/path/mod.ko: <dependencies>
20
21 Expected format of file modules.pcimap includes lines of:
22
23 # pci_module vendor device subvendor subdevice class class_mask driver_data
24 cciss 0x00000e11 0x0000b060 0x00000e11 0x00004070 0x00000000 0x00000000 0x0
25 cciss 0x00000e11 0x0000b178 0x00000e11 0x00004070 0x00000000 0x00000000 0x0
26
27 Expected format of file pcitable includes lines of:
28
29 # ("%d\t%d\t%s\t"%s"\n", vendid, devid, moduleName, cardDescription)
30 # or ("%d\t%d\t%d\t%d\t%s\t"%s"\n", vendid, devid, subvendid, subdevid, moduleNa
31 # me, cardDescription)
32 0x0e11  0x0508  "tmspci"        "Compaq|Netelligent 4/16 Token Ring"
33 0x1000  0x0407  0x8086  0x0532  "megaraid"      "Storage RAID Controller SRCU42X"
34
35 Lines listing a module name of ignore or unknown from pcitable are skipped
36
37
38
39 Output format, for each line that matches the above lines:
40 cciss 0e11:b060 0e11:b178
41
42 """
43
44 import os, sys
45 import string
46
47
48
49 class merge_hw_tables:
50     
51     def merge_files(self, modules_dep_path, modules_pcimap_path, pcitable_path):
52         """
53         merge the three files as described above, and return a dictionary.
54         keys are module names, value is a list of pci ids for that module,
55         in the form "0e11:b178"
56         """
57
58         try:
59             modulesdep_file= file(modules_dep_path,"r")
60         except IOError:
61             sys.stderr.write( "Unable to open modules.dep: %s\n" %
62                               modules_dep_path )
63             return
64
65         try:
66             pcimap_file= file(modules_pcimap_path,"r")
67         except IOError:
68             sys.stderr.write( "Unable to open modules.pcimap: %s\n" %
69                               modules_pcimap_path )
70             return
71
72         try:
73             pcitable_file= file(pcitable_path,"r")
74         except IOError:
75             sys.stderr.write( "Unable to open pcitable: %s\n" %
76                               pcitable_path )
77             return
78
79
80         # associative array to store all matches of module -> ['vendor:device',..]
81         # entries
82         all_modules= {}
83
84
85         # first step, create an associative array of all the built modules
86         for line in modulesdep_file:
87             parts= string.split(line,":")
88             if len(parts) < 2:
89                 continue
90
91             full_mod_path= parts[0]
92             parts= string.split(full_mod_path,"/")
93             module_name= parts[len(parts)-1]
94             module_name_len= len(module_name)
95             if module_name[module_name_len-3:] == ".ko":
96                 all_modules[module_name[:-3]]= []
97
98
99         # now, parse the pcimap and add devices
100         line_num= 0
101         for line in pcimap_file:
102             line_num= line_num+1
103             
104             # skip blank lines, or lines that begin with # (comments)
105             line= string.strip(line)
106             if len(line) == 0:
107                 continue
108
109             if line[0] == "#":
110                 continue
111
112             line_parts= string.split(line)
113             if line_parts is None or len(line_parts) != 8:
114                 sys.stderr.write( "Skipping line %d in pcimap " \
115                                   "(incorrect format)\n" % line_num )
116                 continue
117
118             # first two parts are always vendor / device id
119             module= line_parts[0]
120             vendor_id= line_parts[1]
121             device_id= line_parts[2]
122
123
124             # valid vendor and devices are 10 chars (0xXXXXXXXX) and begin with 0x
125             if len(vendor_id) != 10 or len(device_id) != 10:
126                 sys.stderr.write( "Skipping line %d in pcimap " \
127                                   "(invalid vendor/device id length)\n" %
128                                   line_num )
129                 continue
130             
131             if string.lower(vendor_id[:2]) != "0x" \
132                    or string.lower(device_id[:2]) != "0x":
133                 sys.stderr.write( "Skipping line %d in pcimap " \
134                                   "(invalid vendor/device id format)\n" % line_num )
135                 continue
136
137             # cut down the ids, only need last 4 bytes
138             # start at 6 = (10 total chars - 4 last chars need)
139             vendor_id= string.lower(vendor_id[6:])
140             device_id= string.lower(device_id[6:])
141             
142             full_id= "%s:%s" % (vendor_id, device_id)
143             
144             if all_modules.has_key(module):
145                 if full_id not in all_modules[module]:
146                     all_modules[module].append( full_id )
147             else:
148                 # normally shouldn't get here, as the list is
149                 # prepopulated with all the built modules
150                 all_modules[module]= [full_id,]
151
152
153         # parse pcitable, add any more ids for the devices
154         line_num= 0
155         for line in pcitable_file:
156             line_num= line_num+1
157             
158             # skip blank lines, or lines that begin with # (comments)
159             line= string.strip(line)
160             if len(line) == 0:
161                 continue
162
163             if line[0] == "#":
164                 continue
165
166             line_parts= string.split(line)
167             if line_parts is None or len(line_parts) <= 2:
168                 sys.stderr.write( "Skipping line %d in pcitable " \
169                                   "(incorrect format 1)\n" % line_num )
170                 continue
171
172             # vendor id is always the first field, device the second. also,
173             # strip off first two chars (the 0x)
174             vendor_id= string.lower(line_parts[0][2:])
175             device_id= string.lower(line_parts[1][2:])
176             
177             full_id= "%s:%s" % (vendor_id, device_id)
178             
179             # if the first char of the third field is a double
180             # quote, the third field is a module, else if the first
181             # char of the third field is a 0 (zero), the fifth field is
182             # the module name. it would nice if there was any easy way
183             # to split a string on spaces, but recognize quoted strings,
184             # so they wouldn't be split up. that is the reason for this wierd check
185             if line_parts[2][0] == '"':
186                 module= line_parts[2]
187             elif line_parts[2][0] == '0':
188                 try:
189                     module= line_parts[4]
190                 except ValueError, e:
191                     sys.stderr.write( "Skipping line %d in pcitable " \
192                                       "(incorrect format 2)\n" % line_num )
193                     continue
194             else:
195                 sys.stderr.write( "Skipping line %d in pcitable " \
196                                   "(incorrect format 3)\n" % line_num )
197                 continue
198
199             # remove the first and last char of module (quote marks)
200             module= module[1:]
201             module= module[:len(module)-1]
202             
203             # now add it if we don't already have this module -> id mapping
204             if all_modules.has_key(module):
205                 if full_id not in all_modules[module]:
206                     all_modules[module].append( full_id )
207             else:
208                 # don't add any modules from pcitable that we don't
209                 # already know about
210                 pass
211
212         pcitable_file.close()
213         pcimap_file.close()
214         modulesdep_file.close()
215
216         return all_modules
217     
218
219
220 if __name__ == "__main__":
221     def usage():
222         print( "\nUsage:" )
223         print( "rewrite-pcitable.py <modules.dep> <modules.pcimap> " \
224                "<pcitable> [<output>]" )
225         print( "" )
226
227         
228     if len(sys.argv) < 4:
229         usage()
230         sys.exit(1)
231
232
233     if len(sys.argv) > 4:
234         output_file_name= sys.argv[4]
235         try:
236             output_file= file(output_file_name,"w")
237         except IOError:
238             sys.stderr.write( "Unable to open %s for writing.\n" % output_file )
239             sys.exit(1)
240     else:
241         output_file= sys.stdout
242
243
244     all_modules= merge_hw_tables().merge_files( sys.argv[1], sys.argv[2],
245                                                 sys.argv[3] )
246
247     if all_modules is not None:
248         for module in all_modules.keys():
249             devices= all_modules[module]
250             if len(devices) > 0:
251                 devices_str= string.join( all_modules[module], " " )
252                 output_file.write( "%s %s\n" % (module,devices_str) )
253     else:
254         sys.stderr.write( "Unable to list modules.\n" )
255
256     output_file.close()