blob: 6ce482f1256859a67daaa0b321ebffa36bfb0be2 [file] [log] [blame]
# Copyright (c) 2011-2015 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
from autotest_lib.client.cros.power import sys_power
# 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):
"""Base class of EthCaps test.
Verify Capabilities advertised by an ethernet device work.
We can't verify much in reality though. But we can verify
WOL for built-in devices which is expected to work.
@param test.test: test instance
version = 1
# If WOL setting changed during test then restore to original during cleanup
_restore_wol = False
def _is_usb(self):
"""Determine if device is USB (or not)
Add-on USB devices won't report the same 'Supports Wake-on' value
as built-in (ie PCI) ethernet devices.
if not self._bus_info:
cmd = "ethtool -i %s | awk '/bus-info/ {print $2}'" % self._ethname
self._bus_info = utils.system_output(cmd)
logging.debug("bus_info is %s", self._bus_info)
if not self._bus_info:
logging.error("ethtool -i %s has no bus-info", self._ethname)
# Two bus_info formats are reported by different device drivers:
# 1) "usb-0000:00:1d.0-1.2"
# "0000:00:1d.0" is the "platform" info of the USB host controller
# But it's obvious it's USB since that's the prefix. :)
if self._bus_info.startswith('usb-'):
return True
# 2) "2-1.2" where "2-" is USB host controller instance
return os.path.exists("/sys/bus/usb/devices/%s" % self._bus_info)
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): 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]:
# WOL not required for USB Ethernet plug-in devices
# But all USB Ethernet devices to date report "pg".
# Enforce that.
# RTL8153 can report 'pumbag'.
# AX88178 can report 'pumbg'.
if self._is_usb() and keyname == 'Supports Wake-on':
if (self._caps[keyname][0].find('p') >= 0) and \
(self._caps[keyname][0].find('g') >= 0):
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_secs = rtc.get_seconds()
alarm_secs = before_secs + self._suspend_secs + self._threshold_secs
after_secs = rtc.get_seconds()
# flush RTC as it may not work subsequently if wake was not RTC
suspended_secs = after_secs - before_secs
if suspended_secs >= (self._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 the wake source may be cleared by the time
userspace gets a chance to query the kernel. However, firmware
might have a log and expose the wake source. Attempt 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
fw_log = "/sys/firmware/log"
if not os.path.isfile(fw_log):
logging.warning("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, suspend_secs=5, threshold_secs=10):
"""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
self._suspend_secs = suspend_secs
self._bus_info = None
# ChromeOS does not require WOL support for any USB Ethernet Adapters.
# In fact, WoL only known to work for PCIe Ethernet devices.
# We know _some_ platforms power off all USB ports when suspended.
# USB adapters with "pg" capabilities _might_ WoL on _some_ platforms.
# White list/black listing of platforms will be required to test
# WoL against USB dongles in the future.
if self._is_usb():
logging.debug("Skipping WOL test on USB Ethernet device.")
# 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")