blob: 85c6667f664335e007524006dc491f03af2089e0 [file] [log] [blame]
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import collections, logging, os
from autotest_lib.client.bin import test, utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros import rtc, sys_power
# TODO(tbroch) General:
# - What other Ethernet Capabilities should we check
# TODO(tbroch) WOL:
# - Should we test any of the other modes? I chose magic as it meant that only
# the target device should be awaken.
class network_EthCaps(test.test):
version = 1
# If WOL setting changed during test then restore to original during cleanup
_restore_wol = False
def _parse_ethtool_caps(self):
"""Retrieve ethernet capabilities.
Executes ethtool command and parses various capabilities into a
caps = collections.defaultdict(list)
cmd = "ethtool %s" % self._ethname
prev_keyname = None
for ln in utils.system_output(cmd).splitlines():
cap_str = ln.strip()
(keyname, value) = cap_str.split(': ')
prev_keyname = keyname
except ValueError:
# keyname from previous line, add there
if prev_keyname:
for keyname in caps:
logging.debug("cap['%s'] = %s", keyname, caps[keyname])
self._caps = caps
def _check_eth_caps(self):
"""Check necessary LAN capabilities are present.
Hardware and driver should support the following functionality:
1000baseT, 100baseT, 10baseT, half-duplex, full-duplex, auto-neg, WOL
error.TestError if above LAN capabilities are NOT supported.
default_eth_caps = {
'Supported link modes': ['10baseT/Half', '100baseT/Half',
'1000baseT/Half', '10baseT/Full',
'100baseT/Full', '1000baseT/Full'],
'Supports auto-negotiation': ['Yes'],
# TODO(tbroch): Will this order, 'pumbg' remain across other h/w +
# drivers
# TODO(tbroch): Other WOL caps: 'a': arp and 's': magicsecure are
# they important? Are any of these undesirable/security holes?
'Supports Wake-on': ['pumbg']
errors = 0
for keyname in default_eth_caps:
if keyname not in self._caps:
logging.error("\'%s\' not a capability of %s", keyname,
errors += 1
for value in default_eth_caps[keyname]:
if value not in self._caps[keyname]:
logging.error("\'%s\' not a supported mode in \'%s\' of %s",
value, keyname, self._ethname)
errors += 1
if errors:
raise error.TestError("Eth capability checks. See errors")
def _test_wol_magic_packet(self):
"""Check the Wake-on-LAN (WOL) magic packet capabilities of a device.
error.TestError if WOL functionality fails
# Magic number WOL supported
capname = 'Supports Wake-on'
if self._caps[capname][0].find('g') != -1:"%s support magic number WOL", self._ethname)
raise error.TestError('%s should support magic number WOL' %
# Check that WOL works
if self._caps['Wake-on'][0] != 'g':
utils.system_output("ethtool -s %s wol g" % self._ethname)
self._restore_wol = True
# Set RTC as backup to WOL
before_suspend_secs = rtc.get_seconds()
alarm_secs = before_suspend_secs + self._threshold_secs * 2
after_suspend_secs = rtc.get_seconds()
# flush RTC as it may not work subsequently if wake was not RTC
suspend_secs = after_suspend_secs - before_suspend_secs
if suspend_secs > self._threshold_secs:
raise error.TestError("Device woke due to RTC not WOL")
def _verify_wol_magic(self):
"""If possible identify wake source was caused by WOL.
The bits identifying this may be cleared by the time kernel/userspace
gets a change to query. However if the firmware has a log it may expose
the wake source. This method attempts to interrogate the wake source
details if they are present on the system.
True if verified or unable to verify due to system limitations
False otherwise
cmd = "mosys smbios info bios"
bios_info = utils.system_output(cmd).replace(' ', '').split('|')
logging.debug("bios_info = %s", bios_info)
if 'coreboot' not in bios_info:
logging.warn("Unable to verify wake in s/w due to firmware type")
if 'INSYDE' not in bios_info:
raise error.TestError("Unrecognized firmware found")
return True
fw_log = "/sys/firmware/log"
if not os.path.isfile(fw_log):
logging.warn("Unable to verify wake in s/w due to missing log %s",
return True
log_info_str = utils.system_output("egrep '(SMI|PM1|GPE0)_STS:' %s" %
status_dict = {}
for ln in log_info_str.splitlines():
logging.debug("f/w line = %s", ln)
(status_reg, status_values) = ln.strip().split(":")
status_dict[status_reg] = status_values.split()
except ValueError:
# no bits asserted ... empty list
status_dict[status_reg] = list()
for status_reg in status_dict:
logging.debug("status_dict[%s] = %s", status_reg,
return ('PM1' in status_dict['SMI_STS']) and \
('WAK' in status_dict['PM1_STS']) and \
('PCIEXPWAK' in status_dict['PM1_STS']) and \
len(status_dict['GPE0_STS']) == 0
def cleanup(self):
if self._restore_wol:
utils.system_output("ethtool -s %s wol %s" %
(self._ethname, self._caps['Wake-on'][0]))
def run_once(self, ethname=None, threshold_secs=None):
"""Run the test.
ethname: string of ethernet device under test
threshold_secs: integer of seconds to determine whether wake occurred
due to WOL versus RTC
if not ethname:
raise error.TestError("Name of ethernet device must be declared")
self._ethname = ethname
self._threshold_secs = threshold_secs
# TODO(tbroch) There is evidence in the filesystem of the wake source
# for coreboot but its still being flushed out. For now only produce a
# warning for this check.
if not self._verify_wol_magic():
logging.warning("Unable to see evidence of WOL wake in filesystem")