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