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