1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4import glob, logging, os, re, shutil, time 5from autotest_lib.client.bin import site_utils, utils 6from autotest_lib.client.common_lib import base_utils 7from autotest_lib.client.common_lib import error 8from autotest_lib.client.cros import upstart 9 10 11# Possible display power settings. Copied from chromeos::DisplayPowerState 12# in Chrome's dbus service constants. 13DISPLAY_POWER_ALL_ON = 0 14DISPLAY_POWER_ALL_OFF = 1 15DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON = 2 16DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF = 3 17# for bounds checking 18DISPLAY_POWER_MAX = 4 19 20 21def get_x86_cpu_arch(): 22 """Identify CPU architectural type. 23 24 Intel's processor naming conventions is a mine field of inconsistencies. 25 Armed with that, this method simply tries to identify the architecture of 26 systems we care about. 27 28 TODO(tbroch) grow method to cover processors numbers outlined in: 29 http://www.intel.com/content/www/us/en/processors/processor-numbers.html 30 perhaps returning more information ( brand, generation, features ) 31 32 Returns: 33 String, explicitly (Atom, Core, Celeron) or None 34 """ 35 cpuinfo = utils.read_file('/proc/cpuinfo') 36 37 if re.search(r'Intel.*Atom.*[NZ][2-6]', cpuinfo): 38 return 'Atom' 39 if re.search(r'Intel.*Celeron.*N2[89][0-9][0-9]', cpuinfo): 40 return 'Celeron N2000' 41 if re.search(r'Intel.*Celeron.*N3[0-9][0-9][0-9]', cpuinfo): 42 return 'Celeron N3000' 43 if re.search(r'Intel.*Celeron.*[0-9]{3,4}', cpuinfo): 44 return 'Celeron' 45 if re.search(r'Intel.*Core.*i[357]-[234][0-9][0-9][0-9]', cpuinfo): 46 return 'Core' 47 48 logging.info(cpuinfo) 49 return None 50 51 52def has_rapl_support(): 53 """Identify if platform supports Intels RAPL subsytem. 54 55 Returns: 56 Boolean, True if RAPL supported, False otherwise. 57 """ 58 cpu_arch = get_x86_cpu_arch() 59 if cpu_arch and ((cpu_arch is 'Celeron') or (cpu_arch is 'Core')): 60 return True 61 return False 62 63 64def _call_dbus_method(destination, path, interface, method_name, args): 65 """Performs a generic dbus method call.""" 66 command = ('dbus-send --type=method_call --system ' 67 '--dest=%s %s %s.%s %s') % (destination, path, interface, 68 method_name, args) 69 utils.system_output(command) 70 71 72def call_powerd_dbus_method(method_name, args=''): 73 """ 74 Calls a dbus method exposed by powerd. 75 76 Arguments: 77 @param method_name: name of the dbus method. 78 @param args: string containing args to dbus method call. 79 """ 80 _call_dbus_method(destination='org.chromium.PowerManager', 81 path='/org/chromium/PowerManager', 82 interface='org.chromium.PowerManager', 83 method_name=method_name, args=args) 84 85def call_chrome_dbus_method(method_name, args=''): 86 """ 87 Calls a dbus method exposed by chrome. 88 89 Arguments: 90 @param method_name: name of the dbus method. 91 @param args: string containing args to dbus method call. 92 """ 93 _call_dbus_method(destination='org.chromium.LibCrosService', 94 path='/org/chromium/LibCrosService', 95 interface='org.chomium.LibCrosServiceInterface', 96 method_name=method_name, args=args) 97 98def get_power_supply(): 99 """ 100 Determine what type of power supply the host has. 101 102 Copied from server/host/cros_hosts.py 103 104 @returns a string representing this host's power supply. 105 'power:battery' when the device has a battery intended for 106 extended use 107 'power:AC_primary' when the device has a battery not intended 108 for extended use (for moving the machine, etc) 109 'power:AC_only' when the device has no battery at all. 110 """ 111 try: 112 psu = utils.system_output('mosys psu type') 113 except Exception: 114 # The psu command for mosys is not included for all platforms. The 115 # assumption is that the device will have a battery if the command 116 # is not found. 117 return 'power:battery' 118 119 psu_str = psu.strip() 120 if psu_str == 'unknown': 121 return None 122 123 return 'power:%s' % psu_str 124 125def get_sleep_state(): 126 """ 127 Returns the current powerd configuration of the sleep state. 128 Can be "freeze" or "mem". 129 """ 130 cmd = 'check_powerd_config --suspend_to_idle' 131 result = base_utils.run(cmd, ignore_status=True) 132 return 'freeze' if result.exit_status == 0 else 'mem' 133 134def has_battery(): 135 """Determine if DUT has a battery. 136 137 Returns: 138 Boolean, False if known not to have battery, True otherwise. 139 """ 140 rv = True 141 power_supply = get_power_supply() 142 if power_supply == 'power:battery': 143 # TODO(tbroch) if/when 'power:battery' param is reliable 144 # remove board type logic. Also remove verbose mosys call. 145 _NO_BATTERY_BOARD_TYPE = ['CHROMEBOX', 'CHROMEBIT', 'CHROMEBASE'] 146 board_type = site_utils.get_board_type() 147 if board_type in _NO_BATTERY_BOARD_TYPE: 148 logging.warn('Do NOT believe type %s has battery. ' 149 'See debug for mosys details', board_type) 150 psu = utils.system_output('mosys -vvvv psu type', 151 ignore_status=True) 152 logging.debug(psu) 153 rv = False 154 elif power_supply == 'power:AC_only': 155 rv = False 156 157 return rv 158 159 160class BacklightException(Exception): 161 """Class for Backlight exceptions.""" 162 163 164class Backlight(object): 165 """Class for control of built-in panel backlight. 166 167 Public methods: 168 set_level: Set backlight level to the given brightness. 169 set_percent: Set backlight level to the given brightness percent. 170 set_resume_level: Set backlight level on resume to the given brightness. 171 set_resume_percent: Set backlight level on resume to the given brightness 172 percent. 173 set_default: Set backlight to CrOS default. 174 175 get_level: Get backlight level currently. 176 get_max_level: Get maximum backight level. 177 get_percent: Get backlight percent currently. 178 restore: Restore backlight to initial level when instance created. 179 180 Public attributes: 181 default_brightness_percent: float of default brightness 182 183 Private methods: 184 _try_bl_cmd: run a backlight command. 185 186 Private attributes: 187 _init_level: integer of backlight level when object instantiated. 188 _can_control_bl: boolean determining whether backlight can be controlled 189 or queried 190 """ 191 # Default brightness is based on expected average use case. 192 # See http://www.chromium.org/chromium-os/testing/power-testing for more 193 # details. 194 def __init__(self, default_brightness_percent=0): 195 """Constructor. 196 197 attributes: 198 """ 199 cmd = "mosys psu type" 200 result = utils.system_output(cmd, ignore_status=True).strip() 201 self._can_control_bl = not result == "AC_only" 202 203 self._init_level = self.get_level() 204 self.default_brightness_percent = default_brightness_percent 205 206 logging.debug("device can_control_bl: %s", self._can_control_bl) 207 if not self._can_control_bl: 208 return 209 210 if not self.default_brightness_percent: 211 cmd = "get_powerd_initial_backlight_level 2>/dev/null" 212 try: 213 level = float(utils.system_output(cmd).rstrip()) 214 self.default_brightness_percent = \ 215 (level / self.get_max_level()) * 100 216 logging.info("Default backlight brightness percent = %f", 217 self.default_brightness_percent) 218 except error.CmdError: 219 self.default_brightness_percent = 40.0 220 logging.warning("Unable to determine default backlight " 221 "brightness percent. Setting to %f", 222 self.default_brightness_percent) 223 224 225 def _try_bl_cmd(self, arg_str): 226 """Perform backlight command. 227 228 Args: 229 arg_str: String of additional arguments to backlight command. 230 231 Returns: 232 String output of the backlight command. 233 234 Raises: 235 error.TestFail: if 'cmd' returns non-zero exit status. 236 """ 237 if not self._can_control_bl: 238 return 0 239 cmd = 'backlight_tool %s' % (arg_str) 240 logging.debug("backlight_cmd: %s", cmd) 241 try: 242 return utils.system_output(cmd).rstrip() 243 except error.CmdError: 244 raise error.TestFail(cmd) 245 246 247 def set_level(self, level): 248 """Set backlight level to the given brightness. 249 250 Args: 251 level: integer of brightness to set 252 """ 253 self._try_bl_cmd('--set_brightness=%d' % (level)) 254 255 256 def set_percent(self, percent): 257 """Set backlight level to the given brightness percent. 258 259 Args: 260 percent: float between 0 and 100 261 """ 262 self._try_bl_cmd('--set_brightness_percent=%f' % (percent)) 263 264 265 def set_resume_level(self, level): 266 """Set backlight level on resume to the given brightness. 267 268 Args: 269 level: integer of brightness to set 270 """ 271 self._try_bl_cmd('--set_resume_brightness=%d' % (level)) 272 273 274 def set_resume_percent(self, percent): 275 """Set backlight level on resume to the given brightness percent. 276 277 Args: 278 percent: float between 0 and 100 279 """ 280 self._try_bl_cmd('--set_resume_brightness_percent=%f' % (percent)) 281 282 283 def set_default(self): 284 """Set backlight to CrOS default. 285 """ 286 self.set_percent(self.default_brightness_percent) 287 288 289 def get_level(self): 290 """Get backlight level currently. 291 292 Returns integer of current backlight level or zero if no backlight 293 exists. 294 """ 295 return int(self._try_bl_cmd('--get_brightness')) 296 297 298 def get_max_level(self): 299 """Get maximum backight level. 300 301 Returns integer of maximum backlight level or zero if no backlight 302 exists. 303 """ 304 return int(self._try_bl_cmd('--get_max_brightness')) 305 306 307 def get_percent(self): 308 """Get backlight percent currently. 309 310 Returns float of current backlight percent or zero if no backlight 311 exists 312 """ 313 return float(self._try_bl_cmd('--get_brightness_percent')) 314 315 316 def restore(self): 317 """Restore backlight to initial level when instance created.""" 318 self.set_level(self._init_level) 319 320 321class KbdBacklightException(Exception): 322 """Class for KbdBacklight exceptions.""" 323 324 325class KbdBacklight(object): 326 """Class for control of keyboard backlight. 327 328 Example code: 329 kblight = power_utils.KbdBacklight() 330 kblight.set(10) 331 print "kblight % is %.f" % kblight.get_percent() 332 333 Public methods: 334 set_percent: Sets the keyboard backlight to a percent. 335 get_percent: Get current keyboard backlight percentage. 336 set_level: Sets the keyboard backlight to a level. 337 get_default_level: Get default keyboard backlight brightness level 338 339 Private attributes: 340 _default_backlight_level: keboard backlight level set by default 341 342 """ 343 344 def __init__(self): 345 cmd = 'check_powerd_config --keyboard_backlight' 346 result = base_utils.run(cmd, ignore_status=True) 347 if result.exit_status: 348 raise KbdBacklightException('Keyboard backlight support' + 349 'is not enabled') 350 cmd = 'get_powerd_initial_backlight_level --keyboard 2>/dev/null' 351 self._default_backlight_level = int( 352 utils.system_output(cmd).rstrip()) 353 logging.info("Default keyboard backlight brightness level = %d", 354 self._default_backlight_level) 355 356 357 358 def get_percent(self): 359 """Get current keyboard brightness setting percentage. 360 361 Returns: 362 float, percentage of keyboard brightness in the range [0.0, 100.0]. 363 """ 364 cmd = 'backlight_tool --keyboard --get_brightness_percent' 365 return float(utils.system_output(cmd).strip()) 366 367 368 def get_default_level(self): 369 """ 370 Returns the default backlight level. 371 372 Returns: 373 The default keyboard backlight level. 374 """ 375 return self._default_backlight_level 376 377 378 def set_percent(self, percent): 379 """Set keyboard backlight percent. 380 381 Args: 382 @param percent: float value in the range [0.0, 100.0] 383 to set keyboard backlight to. 384 """ 385 cmd = ('backlight_tool --keyboard --set_brightness_percent=' + 386 str(percent)) 387 utils.system(cmd) 388 389 390 def set_level(self, level): 391 """ 392 Set keyboard backlight to given level. 393 Args: 394 @param level: level to set keyboard backlight to. 395 """ 396 cmd = 'backlight_tool --keyboard --set_brightness=' + str(level) 397 utils.system(cmd) 398 399 400class BacklightController(object): 401 """Class to simulate control of backlight via keyboard or Chrome UI. 402 403 Public methods: 404 increase_brightness: Increase backlight by one adjustment step. 405 decrease_brightness: Decrease backlight by one adjustment step. 406 set_brightness_to_max: Increase backlight to max by calling 407 increase_brightness() 408 set_brightness_to_min: Decrease backlight to min or zero by calling 409 decrease_brightness() 410 411 Private attributes: 412 _max_num_steps: maximum number of backlight adjustment steps between 0 and 413 max brightness. 414 """ 415 416 def __init__(self): 417 self._max_num_steps = 16 418 419 420 def decrease_brightness(self, allow_off=False): 421 """ 422 Decrease brightness by one step, as if the user pressed the brightness 423 down key or button. 424 425 Arguments 426 @param allow_off: Boolean flag indicating whether the brightness can be 427 reduced to zero. 428 Set to true to simulate brightness down key. 429 set to false to simulate Chrome UI brightness down button. 430 """ 431 call_powerd_dbus_method('DecreaseScreenBrightness', 432 'boolean:%s' % \ 433 ('true' if allow_off else 'false')) 434 435 436 def increase_brightness(self): 437 """ 438 Increase brightness by one step, as if the user pressed the brightness 439 up key or button. 440 """ 441 call_powerd_dbus_method('IncreaseScreenBrightness') 442 443 444 def set_brightness_to_max(self): 445 """ 446 Increases the brightness using powerd until the brightness reaches the 447 maximum value. Returns when it reaches the maximum number of brightness 448 adjustments 449 """ 450 num_steps_taken = 0 451 while num_steps_taken < self._max_num_steps: 452 self.increase_brightness() 453 num_steps_taken += 1 454 455 456 def set_brightness_to_min(self, allow_off=False): 457 """ 458 Decreases the brightness using powerd until the brightness reaches the 459 minimum value (zero or the minimum nonzero value). Returns when it 460 reaches the maximum number of brightness adjustments. 461 462 Arguments 463 @param allow_off: Boolean flag indicating whether the brightness can be 464 reduced to zero. 465 Set to true to simulate brightness down key. 466 set to false to simulate Chrome UI brightness down button. 467 """ 468 num_steps_taken = 0 469 while num_steps_taken < self._max_num_steps: 470 self.decrease_brightness(allow_off) 471 num_steps_taken += 1 472 473 474class DisplayException(Exception): 475 """Class for Display exceptions.""" 476 477 478def set_display_power(power_val): 479 """Function to control screens via Chrome. 480 481 Possible arguments: 482 DISPLAY_POWER_ALL_ON, 483 DISPLAY_POWER_ALL_OFF, 484 DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON, 485 DISPLAY_POWER_INTERNAL_ON_EXTENRAL_OFF 486 """ 487 if (not isinstance(power_val, int) 488 or power_val < DISPLAY_POWER_ALL_ON 489 or power_val >= DISPLAY_POWER_MAX): 490 raise DisplayException('Invalid display power setting: %d' % power_val) 491 call_chrome_dbus_method('SetDisplayPower', 'int32:%d' % power_val) 492 493 494class PowerPrefChanger(object): 495 """ 496 Class to temporarily change powerd prefs. Construct with a dict of 497 pref_name/value pairs (e.g. {'disable_idle_suspend':0}). Destructor (or 498 reboot) will restore old prefs automatically.""" 499 500 _PREFDIR = '/var/lib/power_manager' 501 _TEMPDIR = '/tmp/autotest_powerd_prefs' 502 503 def __init__(self, prefs): 504 shutil.copytree(self._PREFDIR, self._TEMPDIR) 505 for name, value in prefs.iteritems(): 506 utils.write_one_line('%s/%s' % (self._TEMPDIR, name), value) 507 utils.system('mount --bind %s %s' % (self._TEMPDIR, self._PREFDIR)) 508 upstart.restart_job('powerd') 509 510 511 def finalize(self): 512 """finalize""" 513 if os.path.exists(self._TEMPDIR): 514 utils.system('umount %s' % self._PREFDIR, ignore_status=True) 515 shutil.rmtree(self._TEMPDIR) 516 upstart.restart_job('powerd') 517 518 519 def __del__(self): 520 self.finalize() 521 522 523class Registers(object): 524 """Class to examine PCI and MSR registers.""" 525 526 def __init__(self): 527 self._cpu_id = 0 528 self._rdmsr_cmd = 'iotools rdmsr' 529 self._mmio_read32_cmd = 'iotools mmio_read32' 530 self._rcba = 0xfed1c000 531 532 self._pci_read32_cmd = 'iotools pci_read32' 533 self._mch_bar = None 534 self._dmi_bar = None 535 536 def _init_mch_bar(self): 537 if self._mch_bar != None: 538 return 539 # MCHBAR is at offset 0x48 of B/D/F 0/0/0 540 cmd = '%s 0 0 0 0x48' % (self._pci_read32_cmd) 541 self._mch_bar = int(utils.system_output(cmd), 16) & 0xfffffffe 542 logging.debug('MCH BAR is %s', hex(self._mch_bar)) 543 544 def _init_dmi_bar(self): 545 if self._dmi_bar != None: 546 return 547 # DMIBAR is at offset 0x68 of B/D/F 0/0/0 548 cmd = '%s 0 0 0 0x68' % (self._pci_read32_cmd) 549 self._dmi_bar = int(utils.system_output(cmd), 16) & 0xfffffffe 550 logging.debug('DMI BAR is %s', hex(self._dmi_bar)) 551 552 def _read_msr(self, register): 553 cmd = '%s %d %s' % (self._rdmsr_cmd, self._cpu_id, register) 554 return int(utils.system_output(cmd), 16) 555 556 def _read_mmio_read32(self, address): 557 cmd = '%s 0x%x' % (self._mmio_read32_cmd, address) 558 return int(utils.system_output(cmd), 16) 559 560 def _read_dmi_bar(self, offset): 561 self._init_dmi_bar() 562 return self._read_mmio_read32(self._dmi_bar + int(offset, 16)) 563 564 def _read_mch_bar(self, offset): 565 self._init_mch_bar() 566 return self._read_mmio_read32(self._mch_bar + int(offset, 16)) 567 568 def _read_rcba(self, offset): 569 return self._read_mmio_read32(self._rcba + int(offset, 16)) 570 571 def _shift_mask_match(self, reg_name, value, match): 572 expr = match[1] 573 bits = match[0].split(':') 574 operator = match[2] if len(match) == 3 else '==' 575 hi_bit = int(bits[0]) 576 if len(bits) == 2: 577 lo_bit = int(bits[1]) 578 else: 579 lo_bit = int(bits[0]) 580 581 value >>= lo_bit 582 mask = (1 << (hi_bit - lo_bit + 1)) - 1 583 value &= mask 584 585 good = eval("%d %s %d" % (value, operator, expr)) 586 if not good: 587 logging.error('FAILED: %s bits: %s value: %s mask: %s expr: %s ' + 588 'operator: %s', reg_name, bits, hex(value), mask, 589 expr, operator) 590 return good 591 592 def _verify_registers(self, reg_name, read_fn, match_list): 593 errors = 0 594 for k, v in match_list.iteritems(): 595 r = read_fn(k) 596 for item in v: 597 good = self._shift_mask_match(reg_name, r, item) 598 if not good: 599 errors += 1 600 logging.error('Error(%d), %s: reg = %s val = %s match = %s', 601 errors, reg_name, k, hex(r), v) 602 else: 603 logging.debug('ok, %s: reg = %s val = %s match = %s', 604 reg_name, k, hex(r), v) 605 return errors 606 607 def verify_msr(self, match_list): 608 """ 609 Verify MSR 610 611 @param match_list: match list 612 """ 613 errors = 0 614 for cpu_id in xrange(0, max(utils.count_cpus(), 1)): 615 self._cpu_id = cpu_id 616 errors += self._verify_registers('msr', self._read_msr, match_list) 617 return errors 618 619 def verify_dmi(self, match_list): 620 """ 621 Verify DMI 622 623 @param match_list: match list 624 """ 625 return self._verify_registers('dmi', self._read_dmi_bar, match_list) 626 627 def verify_mch(self, match_list): 628 """ 629 Verify MCH 630 631 @param match_list: match list 632 """ 633 return self._verify_registers('mch', self._read_mch_bar, match_list) 634 635 def verify_rcba(self, match_list): 636 """ 637 Verify RCBA 638 639 @param match_list: match list 640 """ 641 return self._verify_registers('rcba', self._read_rcba, match_list) 642 643 644class USBDevicePower(object): 645 """Class for USB device related power information. 646 647 Public Methods: 648 autosuspend: Return boolean whether USB autosuspend is enabled or False 649 if not or unable to determine 650 651 Public attributes: 652 vid: string of USB Vendor ID 653 pid: string of USB Product ID 654 whitelisted: Boolean if USB device is whitelisted for USB auto-suspend 655 656 Private attributes: 657 path: string to path of the USB devices in sysfs ( /sys/bus/usb/... ) 658 659 TODO(tbroch): consider converting to use of pyusb although not clear its 660 beneficial if it doesn't parse power/control 661 """ 662 def __init__(self, vid, pid, whitelisted, path): 663 self.vid = vid 664 self.pid = pid 665 self.whitelisted = whitelisted 666 self._path = path 667 668 669 def autosuspend(self): 670 """Determine current value of USB autosuspend for device.""" 671 control_file = os.path.join(self._path, 'control') 672 if not os.path.exists(control_file): 673 logging.info('USB: power control file not found for %s', dir) 674 return False 675 676 out = utils.read_one_line(control_file) 677 logging.debug('USB: control set to %s for %s', out, control_file) 678 return (out == 'auto') 679 680 681class USBPower(object): 682 """Class to expose USB related power functionality. 683 684 Initially that includes the policy around USB auto-suspend and our 685 whitelisting of devices that are internal to CrOS system. 686 687 Example code: 688 usbdev_power = power_utils.USBPower() 689 for device in usbdev_power.devices 690 if device.is_whitelisted() 691 ... 692 693 Public attributes: 694 devices: list of USBDevicePower instances 695 696 Private functions: 697 _is_whitelisted: Returns Boolean if USB device is whitelisted for USB 698 auto-suspend 699 _load_whitelist: Reads whitelist and stores int _whitelist attribute 700 701 Private attributes: 702 _wlist_file: path to laptop-mode-tools (LMT) USB autosuspend 703 conf file. 704 _wlist_vname: string name of LMT USB autosuspend whitelist 705 variable 706 _whitelisted: list of USB device vid:pid that are whitelisted. 707 May be regular expressions. See LMT for details. 708 """ 709 def __init__(self): 710 self._wlist_file = \ 711 '/etc/laptop-mode/conf.d/board-specific/usb-autosuspend.conf' 712 self._wlist_vname = '$AUTOSUSPEND_USBID_WHITELIST' 713 self._whitelisted = None 714 self.devices = [] 715 716 717 def _load_whitelist(self): 718 """Load USB device whitelist for enabling USB autosuspend 719 720 CrOS whitelists only internal USB devices to enter USB auto-suspend mode 721 via laptop-mode tools. 722 """ 723 cmd = "source %s && echo %s" % (self._wlist_file, 724 self._wlist_vname) 725 out = utils.system_output(cmd, ignore_status=True) 726 logging.debug('USB whitelist = %s', out) 727 self._whitelisted = out.split() 728 729 730 def _is_whitelisted(self, vid, pid): 731 """Check to see if USB device vid:pid is whitelisted. 732 733 Args: 734 vid: string of USB vendor ID 735 pid: string of USB product ID 736 737 Returns: 738 True if vid:pid in whitelist file else False 739 """ 740 if self._whitelisted is None: 741 self._load_whitelist() 742 743 match_str = "%s:%s" % (vid, pid) 744 for re_str in self._whitelisted: 745 if re.match(re_str, match_str): 746 return True 747 return False 748 749 750 def query_devices(self): 751 """.""" 752 dirs_path = '/sys/bus/usb/devices/*/power' 753 dirs = glob.glob(dirs_path) 754 if not dirs: 755 logging.info('USB power path not found') 756 return 1 757 758 for dirpath in dirs: 759 vid_path = os.path.join(dirpath, '..', 'idVendor') 760 pid_path = os.path.join(dirpath, '..', 'idProduct') 761 if not os.path.exists(vid_path): 762 logging.debug("No vid for USB @ %s", vid_path) 763 continue 764 vid = utils.read_one_line(vid_path) 765 pid = utils.read_one_line(pid_path) 766 whitelisted = self._is_whitelisted(vid, pid) 767 self.devices.append(USBDevicePower(vid, pid, whitelisted, dirpath)) 768 769 770class DisplayPanelSelfRefresh(object): 771 """Class for control and monitoring of display's PSR. 772 773 TODO(tbroch) support devices that don't use i915 drivers but have PSR 774 """ 775 psr_status_file = '/sys/kernel/debug/dri/0/i915_edp_psr_status' 776 777 def __init__(self, init_time=time.time()): 778 """Initializer. 779 780 @Public attributes: 781 supported: Boolean of whether PSR is supported or not 782 783 @Private attributes: 784 _init_time: time when PSR class was instantiated. 785 _init_counter: integer of initial value of residency counter. 786 _keyvals: dictionary of keyvals 787 """ 788 self._init_time = init_time 789 self._init_counter = self._get_counter() 790 self._keyvals = {} 791 self.supported = (self._init_counter != None) 792 793 def _get_counter(self): 794 """Get the current value of the system PSR counter. 795 796 This counts the number of milliseconds the system has resided in PSR. 797 798 @returns: amount of time PSR has been active since boot in ms, or None if 799 the performance counter can't be read. 800 """ 801 try: 802 count = utils.get_field(utils.read_file(self.psr_status_file), 0, 803 linestart='Performance_Counter:') 804 except IOError: 805 logging.info("Can't find or read PSR status file") 806 return None 807 808 logging.debug("PSR performance counter: %s", count) 809 return int(count) if count else None 810 811 def _calc_residency(self): 812 """Calculate the PSR residency.""" 813 if not self.supported: 814 return 0 815 816 tdelta = time.time() - self._init_time 817 cdelta = self._get_counter() - self._init_counter 818 return cdelta / (10 * tdelta) 819 820 def refresh(self): 821 """Refresh PSR related data.""" 822 self._keyvals['percent_psr_residency'] = self._calc_residency() 823 824 def get_keyvals(self): 825 """Get keyvals associated with PSR data. 826 827 @returns dictionary of keyvals 828 """ 829 return self._keyvals 830