OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # | 2 # |
3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. | 3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 import glob | 7 import glob |
8 import imp | |
8 import os | 9 import os |
9 import pprint | 10 import pprint |
10 import re | 11 import re |
11 import sys | 12 import sys |
12 import threading | 13 import threading |
13 | 14 |
14 import flashrom_util | 15 import flashrom_util |
15 import gft_common | 16 import gft_common |
16 import gft_fwhash | 17 import gft_fwhash |
17 import vblock | 18 import vblock |
18 | 19 |
19 from gft_common import DebugMsg, VerboseMsg, WarningMsg, ErrorMsg, ErrorDie | 20 from gft_common import DebugMsg, VerboseMsg, WarningMsg, ErrorMsg, ErrorDie |
20 | 21 |
21 | 22 |
22 def Memorize(f): | |
23 return gft_common.ThreadSafe(gft_common.Memorize(f)) | |
24 | |
25 | |
26 class HardwareComponents(object): | 23 class HardwareComponents(object): |
27 """ Hardware Components Scanner """ | 24 """ Hardware Components Scanner """ |
28 | 25 |
29 # Function names in this class are used for reflection, so please don't change | 26 # Function names in this class are used for reflection, so please don't change |
30 # the function names even if they are not comliant to coding style guide. | 27 # the function names even if they are not comliant to coding style guide. |
31 | 28 |
32 version = 6 | 29 version = 6 |
33 | 30 |
34 # We divide all component IDs (cids) into 5 categories: | 31 # We divide all component IDs (cids) into 5 categories: |
35 # - enumerable: able to get the results by running specific commands; | 32 # - enumerable: able to get the results by running specific commands; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
86 ] | 83 ] |
87 | 84 |
88 # _not_test_cids and _to_be_tested_cids will be re-created for each match. | 85 # _not_test_cids and _to_be_tested_cids will be re-created for each match. |
89 _not_test_cids = [] | 86 _not_test_cids = [] |
90 _to_be_tested_cids = [] | 87 _to_be_tested_cids = [] |
91 | 88 |
92 # TODO(hungte) unify the 'not available' style messages | 89 # TODO(hungte) unify the 'not available' style messages |
93 _not_present = '' | 90 _not_present = '' |
94 _no_match = 'No match' | 91 _no_match = 'No match' |
95 | 92 |
93 # Type id for connection management (compatible to flimflam) | |
94 _type_3g = 'cellular' | |
95 _type_ethernet = 'ethernet' | |
96 _type_wireless = 'wifi' | |
97 | |
98 _flimflam_dir = '/usr/local/lib/flimflam/test' | |
99 | |
96 def __init__(self, verbose=False): | 100 def __init__(self, verbose=False): |
97 self._initialized = False | 101 self._initialized = False |
98 self._verbose = verbose | 102 self._verbose = verbose |
99 self._pp = pprint.PrettyPrinter() | 103 self._pp = pprint.PrettyPrinter() |
100 | 104 |
101 # cache for firmware images | 105 # cache for firmware images |
102 self._flashrom = flashrom_util.flashrom_util( | 106 self._flashrom = flashrom_util.flashrom_util( |
103 verbose_msg=VerboseMsg, | 107 verbose_msg=VerboseMsg, |
104 exception_type=gft_common.GFTError, | 108 exception_type=gft_common.GFTError, |
105 system_output=gft_common.SystemOutput) | 109 system_output=gft_common.SystemOutput) |
106 self._temp_files = [] | 110 self._temp_files = [] |
107 | 111 |
108 # variables for matching | 112 # variables for matching |
109 self._enumerable_system = None | 113 self._enumerable_system = None |
110 self._file_base = None | 114 self._file_base = None |
111 self._system = None | 115 self._system = None |
112 self._failures = None | 116 self._failures = None |
113 | 117 |
114 def __del__(self): | 118 def __del__(self): |
115 for temp_file in self._temp_files: | 119 for temp_file in self._temp_files: |
116 try: | 120 try: |
117 # DebugMsg('gft_hwcomp: delete temp file %s' % temp_file) | 121 # DebugMsg('gft_hwcomp: delete temp file %s' % temp_file) |
118 os.remove(temp_file) | 122 os.remove(temp_file) |
119 except: | 123 except: |
120 pass | 124 pass |
121 | 125 |
122 # -------------------------------------------------------------------- | 126 # -------------------------------------------------------------------- |
123 # System Probing Utilities | 127 # System Probing Utilities |
124 | 128 |
129 def Memorize(f): | |
130 """ Decorator for thread-safe memorization """ | |
131 return gft_common.ThreadSafe(gft_common.Memorize(f)) | |
132 | |
125 @Memorize | 133 @Memorize |
126 def load_module(self, name): | 134 def load_module(self, name): |
127 grep_cmd = ('lsmod 2>/dev/null | grep -q %s' % name) | 135 grep_cmd = ('lsmod 2>/dev/null | grep -q %s' % name) |
128 loaded = (os.system(grep_cmd) == 0) | 136 loaded = (os.system(grep_cmd) == 0) |
129 if not loaded: | 137 if not loaded: |
130 if os.system('modprobe %s >/dev/null 2>&1' % name) != 0: | 138 if os.system('modprobe %s >/dev/null 2>&1' % name) != 0: |
131 ErrorMsg("Cannot load module: %s" % name) | 139 ErrorMsg("Cannot load module: %s" % name) |
132 return loaded | 140 return loaded |
133 | 141 |
134 @Memorize | 142 @Memorize |
(...skipping 24 matching lines...) Expand all Loading... | |
159 usb_devs = glob.glob('/sys/bus/usb/devices/*:*') | 167 usb_devs = glob.glob('/sys/bus/usb/devices/*:*') |
160 for dev in usb_devs: | 168 for dev in usb_devs: |
161 path = os.path.join(os.path.realpath(dev), '..') | 169 path = os.path.join(os.path.realpath(dev), '..') |
162 device_list += ['%s:%s' % | 170 device_list += ['%s:%s' % |
163 (gft_common.ReadOneLine(os.path.join(path, 'idVendor')), | 171 (gft_common.ReadOneLine(os.path.join(path, 'idVendor')), |
164 gft_common.ReadOneLine(os.path.join(path, 'idProduct')))] | 172 gft_common.ReadOneLine(os.path.join(path, 'idProduct')))] |
165 DebugMsg('Legacy device list: ' + ', '.join(device_list)) | 173 DebugMsg('Legacy device list: ' + ', '.join(device_list)) |
166 return device_list | 174 return device_list |
167 | 175 |
168 @Memorize | 176 @Memorize |
177 def import_flimflam_module(self): | |
178 """ Tries to load flimflam module from current system """ | |
179 if not os.path.exists(self._flimflam_dir): | |
180 DebugMsg('no flimflam installed in %s' % self._flimflam_dir) | |
181 return None | |
182 try: | |
183 return imp.load_module('flimflam', | |
184 *imp.find_module('flimflam', [self._flimflam_dir])) | |
185 except ImportError: | |
186 ErrorMsg('Failed to import flimflam.') | |
187 except: | |
188 ErrorMsg('Failed to load flimflam.') | |
189 return None | |
190 | |
191 @Memorize | |
192 def load_flimflam(self): | |
193 """Gets information provided by flimflam (connection manager) | |
194 | |
195 Returns | |
196 (None, None) if failed to load module, otherwise | |
197 (connection_path, connection_info) where | |
198 connection_path is a dict in {type: device_path}, | |
199 connection_info is a dict of {type: {attribute: value}}. | |
200 """ | |
201 flimflam = self.import_flimflam_module() | |
202 if not flimflam: | |
203 return (None, None) | |
204 path = {} | |
205 info = {} | |
206 info_attribute_names = { | |
207 self._type_3g: ['Carrier', 'FirmwareRevision', 'HardwareRevision', | |
208 'ModelID', 'Manufacturer'], | |
209 } | |
210 devices = flimflam.FlimFlam().GetObjectList('Device') | |
211 unpack = flimflam.convert_dbus_value | |
212 # TODO(hungte) support multiple devices existence in future | |
213 for device in devices: | |
214 # populate the 'path' collection | |
215 prop = device.GetProperties() | |
216 prop_type = unpack(prop['Type']) | |
217 path[prop_type] = '/sys/class/net/%s/device' % unpack(prop['Interface']) | |
218 if prop_type not in info_attribute_names: | |
219 continue | |
220 # populate the 'info' collection | |
221 info[prop_type] = dict(( | |
222 (key, unpack(prop['Cellular.%s' % key])) | |
223 for key in info_attribute_names[prop_type] | |
224 if ('Cellular.%s' % key) in prop)) | |
225 return (path, info) | |
226 | |
227 @Memorize | |
169 def _get_all_connection_info(self): | 228 def _get_all_connection_info(self): |
170 """ Probes available connectivity and device information """ | 229 """ Probes available connectivity and device information """ |
171 connection_info = { | 230 connection_info = { |
172 'wireless': '/sys/class/net/wlan0/device', | 231 self._type_wireless: '/sys/class/net/wlan0/device', |
173 'ethernet': '/sys/class/net/eth0/device', | 232 self._type_ethernet: '/sys/class/net/eth0/device', |
174 '3g': '/sys/class/tty/ttyUSB0/device', | 233 # 3g(cellular) may also be /sys/class/net/usb0 |
234 self._type_3g: '/sys/class/tty/ttyUSB0/device', | |
175 } | 235 } |
176 flimflam_scripts_base = '/usr/local/lib/flimflam/test' | 236 (path, _) = self.load_flimflam() |
177 if os.path.exists(flimflam_scripts_base): | 237 if path is not None: |
178 # TODO(hungte) use flimflam to update the device list. | 238 # trust flimflam instead. |
179 # Currently flimflam seem to be not very stable, so let's ignore it. | 239 for k in connection_info: |
180 list_modems = os.path.join(flimflam_scripts_base, 'mm-list-modems') | 240 connection_info[k] = (path[k] if k in path else '') |
181 part_id = gft_common.SystemOutput(list_modems, ignore_status=True).strip() | |
182 if part_id: | |
183 DebugMsg('mm-list-modems reported: %s' % part_id) | |
184 return connection_info | 241 return connection_info |
185 | 242 |
186 def _get_sysfs_device_info(self, path, primary, optional=[]): | 243 def _get_sysfs_device_info(self, path, primary, optional=[]): |
187 """Gets the device information of a sysfs node. | 244 """Gets the device information of a sysfs node. |
188 | 245 |
189 Args | 246 Args |
190 path: the sysfs device path. | 247 path: the sysfs device path. |
191 primary: mandatory list of elements to read. | 248 primary: mandatory list of elements to read. |
192 optional: optional list of elements to read. | 249 optional: optional list of elements to read. |
193 | 250 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
238 return info_string | 295 return info_string |
239 | 296 |
240 def get_sysfs_device_id(self, path): | 297 def get_sysfs_device_id(self, path): |
241 """Gets a sysfs device identifier. (Currently supporting USB/PCI) | 298 """Gets a sysfs device identifier. (Currently supporting USB/PCI) |
242 Args | 299 Args |
243 path: a path to sysfs device (ex, /sys/class/net/wlan0/device) | 300 path: a path to sysfs device (ex, /sys/class/net/wlan0/device) |
244 | 301 |
245 Returns | 302 Returns |
246 An identifier string, or self._not_present if not available. | 303 An identifier string, or self._not_present if not available. |
247 """ | 304 """ |
305 if not path: | |
306 return self._not_present | |
248 path = os.path.realpath(path) | 307 path = os.path.realpath(path) |
249 if not os.path.isdir(path): | 308 if not os.path.isdir(path): |
250 return self._not_present | 309 return self._not_present |
251 | 310 |
252 info = (self._get_pci_device_info(path) or | 311 info = (self._get_pci_device_info(path) or |
253 self._get_usb_device_info(path)) | 312 self._get_usb_device_info(path)) |
254 return info or self._not_present | 313 return info or self._not_present |
255 | 314 |
256 # -------------------------------------------------------------------- | 315 # -------------------------------------------------------------------- |
257 # Firmware Processing Utilities | 316 # Firmware Processing Utilities |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
349 Returns a hash of Read Only (BIOS) firmware parts, | 408 Returns a hash of Read Only (BIOS) firmware parts, |
350 to confirm we have proper keys / boot code / recovery image installed. | 409 to confirm we have proper keys / boot code / recovery image installed. |
351 """ | 410 """ |
352 | 411 |
353 image_file = self.load_main_firmware() | 412 image_file = self.load_main_firmware() |
354 if not image_file: | 413 if not image_file: |
355 ErrorDie('get_hash_ro_firmware: cannot read firmware') | 414 ErrorDie('get_hash_ro_firmware: cannot read firmware') |
356 return gft_fwhash.GetBIOSReadOnlyHash(file_source=image_file) | 415 return gft_fwhash.GetBIOSReadOnlyHash(file_source=image_file) |
357 | 416 |
358 def get_part_id_3g(self): | 417 def get_part_id_3g(self): |
359 device_path = self._get_all_connection_info()['3g'] | 418 device_path = self._get_all_connection_info()[self._type_3g] |
360 return self.get_sysfs_device_id(device_path) or self._not_present | 419 return self.get_sysfs_device_id(device_path) or self._not_present |
361 | 420 |
362 def get_part_id_audio_codec(self): | 421 def get_part_id_audio_codec(self): |
363 cmd = 'grep -R Codec: /proc/asound/* | head -n 1 | sed s/.\*Codec://' | 422 cmd = 'grep -R Codec: /proc/asound/* | head -n 1 | sed s/.\*Codec://' |
364 part_id = gft_common.SystemOutput( | 423 part_id = gft_common.SystemOutput( |
365 cmd, progress_messsage='Searching Audio Codecs: ', | 424 cmd, progress_messsage='Searching Audio Codecs: ', |
366 show_progress=self._verbose).strip() | 425 show_progress=self._verbose).strip() |
367 return part_id | 426 return part_id |
368 | 427 |
369 def get_part_id_bluetooth(self): | 428 def get_part_id_bluetooth(self): |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
429 show_progress=self._verbose, | 488 show_progress=self._verbose, |
430 ignore_status=True).splitlines() | 489 ignore_status=True).splitlines() |
431 for line in res: | 490 for line in res: |
432 match = re.search(r'Found (.*) at', line) | 491 match = re.search(r'Found (.*) at', line) |
433 if match: | 492 if match: |
434 parts.append(match.group(1)) | 493 parts.append(match.group(1)) |
435 part_id = ', '.join(parts) | 494 part_id = ', '.join(parts) |
436 return part_id | 495 return part_id |
437 | 496 |
438 def get_part_id_ethernet(self): | 497 def get_part_id_ethernet(self): |
439 device_path = self._get_all_connection_info()['ethernet'] | 498 device_path = self._get_all_connection_info()[self._type_ethernet] |
440 return self.get_sysfs_device_id(device_path) or self._not_present | 499 return self.get_sysfs_device_id(device_path) or self._not_present |
441 | 500 |
442 def get_part_id_flash_chip(self): | 501 def get_part_id_flash_chip(self): |
443 (chip_id, _) = self._load_firmware('main') | 502 (chip_id, _) = self._load_firmware('main') |
444 return chip_id | 503 return chip_id |
445 | 504 |
446 def get_part_id_hwqual(self): | 505 def get_part_id_hwqual(self): |
447 part_id = gft_common.SystemOutput('crossystem hwid').strip() | 506 part_id = gft_common.SystemOutput('crossystem hwid').strip() |
448 return (part_id if part_id else self._not_present) | 507 return (part_id if part_id else self._not_present) |
449 | 508 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
485 usb_host_info.sort(reverse=True) | 544 usb_host_info.sort(reverse=True) |
486 return ' '.join(usb_host_info) | 545 return ' '.join(usb_host_info) |
487 | 546 |
488 def get_part_id_vga(self): | 547 def get_part_id_vga(self): |
489 return self.get_sysfs_device_id('/sys/class/graphics/fb0/device') | 548 return self.get_sysfs_device_id('/sys/class/graphics/fb0/device') |
490 | 549 |
491 def get_part_id_webcam(self): | 550 def get_part_id_webcam(self): |
492 return self.get_sysfs_device_id('/sys/class/video4linux/video0/device') | 551 return self.get_sysfs_device_id('/sys/class/video4linux/video0/device') |
493 | 552 |
494 def get_part_id_wireless(self): | 553 def get_part_id_wireless(self): |
495 device_path = self._get_all_connection_info()['wireless'] | 554 device_path = self._get_all_connection_info()[self._type_wireless] |
496 return self.get_sysfs_device_id(device_path) or self._not_present | 555 return self.get_sysfs_device_id(device_path) or self._not_present |
497 | 556 |
498 def get_vendor_id_touchpad_synaptics(self): | 557 def get_vendor_id_touchpad_synaptics(self): |
499 part_id = '' | 558 part_id = '' |
500 detect_program = '/opt/Synaptics/bin/syndetect' | 559 detect_program = '/opt/Synaptics/bin/syndetect' |
501 model_string_str = 'Model String' | 560 model_string_str = 'Model String' |
502 firmware_id_str = 'Firmware ID' | 561 firmware_id_str = 'Firmware ID' |
503 if os.path.exists(detect_program): | 562 if os.path.exists(detect_program): |
504 data = gft_common.SystemOutput( | 563 data = gft_common.SystemOutput( |
505 detect_program, | 564 detect_program, |
(...skipping 26 matching lines...) Expand all Loading... | |
532 part_id = '' | 591 part_id = '' |
533 for method in method_list: | 592 for method in method_list: |
534 part_id = getattr(self, 'get_vendor_id_touchpad_%s' % method)() | 593 part_id = getattr(self, 'get_vendor_id_touchpad_%s' % method)() |
535 DebugMsg('get_vendor_id_touchpad: %s: %s' % | 594 DebugMsg('get_vendor_id_touchpad: %s: %s' % |
536 (method, part_id or '<failed>')) | 595 (method, part_id or '<failed>')) |
537 if part_id: | 596 if part_id: |
538 break | 597 break |
539 return part_id | 598 return part_id |
540 | 599 |
541 def get_version_3g_firmware(self): | 600 def get_version_3g_firmware(self): |
542 version = 'Unknown' | 601 (_, attributes) = self.load_flimflam() |
Jason Glasgow
2011/04/15 22:39:44
Looks okay to me. I would have used mm.py and gon
| |
543 # TODO(hungte) use mm.py directly to prevent the need of shell scripting | 602 if attributes and (self._type_3g in attributes): |
544 modem_status = gft_common.SystemOutput('modem status', | 603 # A list of possible version info combination, since the supported fields |
545 ignore_status=True).splitlines() | 604 # may differ for partners. |
546 if not modem_status: | 605 version_formats = [ |
547 return self._not_present | 606 ['Carrier', 'FirmwareRevision'], |
548 | 607 ['FirmwareRevision'], |
549 # status format: | 608 ['HardwareRevision']] |
550 # Manufacturer: $VENDOR | 609 info = attributes[self._type_3g] |
551 # Modem: $MODEM | 610 for version_format in version_formats: |
552 # Version: $VERSION_MAY_BE_MULTILINE | 611 if not set(version_format).issubset(set(info)): |
553 def ParseInfo(name): | 612 continue |
554 data = [line for line in modem_status if line.find('%s: ' % name) >= 0] | 613 # compact all fields into single line with limited space |
555 assert len(data) < 2 | 614 version_string = ' '.join([info[key] for key in version_format]) |
556 return data[0].split(': ', 1)[1].strip() | 615 return re.sub('\s+', ' ', version_string) |
557 | 616 return self._not_present |
558 version = ParseInfo('Version') | |
559 vendor = ParseInfo('Manufacturer') | |
560 modem = ParseInfo('Modem') | |
561 | |
562 if vendor == 'Samsung' and modem == 'GT-Y3300X': | |
563 # The real version is in "Version:" +2 lines | |
564 version = '' | |
565 for i, line in enumerate(modem_status): | |
566 if 'Version: ' in line: | |
567 version = modem_status[i + 2].strip() | |
568 break | |
569 return version or 'Unknown' | |
570 | 617 |
571 def get_version_rw_firmware(self): | 618 def get_version_rw_firmware(self): |
572 """ | 619 """ |
573 Returns the version of Read-Write (writable) firmware from VBLOCK sections. | 620 Returns the version of Read-Write (writable) firmware from VBLOCK sections. |
574 | 621 |
575 If A/B has different version, that means this system needs a reboot + | 622 If A/B has different version, that means this system needs a reboot + |
576 firmwar update so return value is a "error report" in the form "A=x, B=y". | 623 firmwar update so return value is a "error report" in the form "A=x, B=y". |
577 """ | 624 """ |
578 | 625 |
579 versions = [None, None] | 626 versions = [None, None] |
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
817 print 'Starting to match system...' | 864 print 'Starting to match system...' |
818 for arg in compdb_list: | 865 for arg in compdb_list: |
819 (matched, failures) = components.match_current_system(arg) | 866 (matched, failures) = components.match_current_system(arg) |
820 print 'Probed (%s):' % arg | 867 print 'Probed (%s):' % arg |
821 print components.pformat(matched) | 868 print components.pformat(matched) |
822 print 'Failures (%s):' % arg | 869 print 'Failures (%s):' % arg |
823 print components.pformat(failures) | 870 print components.pformat(failures) |
824 | 871 |
825 if __name__ == '__main__': | 872 if __name__ == '__main__': |
826 _main(sys.argv[0], sys.argv[1:]) | 873 _main(sys.argv[0], sys.argv[1:]) |
OLD | NEW |