Chromium Code Reviews| 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 |