blob: 18270599d28543cdf73aa9d2e1e6d69527a53eda [file] [log] [blame]
# Copyright 2014 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.
"""Provides Linux kernel Watchdog interface.
https://www.kernel.org/doc/Documentation/watchdog/watchdog-api.txt
"""
import array
import fcntl
import struct
IO_WRITE = 0x40000000
IO_READ = 0x80000000
IO_READ_WRITE = 0xC0000000
IO_SIZE_INT = 0x00040000
IO_SIZE_40 = 0x00280000
IO_TYPE_WATCHDOG = ord('W') << 8
WDR_INT = IO_READ | IO_SIZE_INT | IO_TYPE_WATCHDOG
WDR_40 = IO_READ | IO_SIZE_40 | IO_TYPE_WATCHDOG
WDWR_INT = IO_READ_WRITE | IO_SIZE_INT | IO_TYPE_WATCHDOG
WDIOC_GETSUPPORT = 0 | WDR_40
WDIOC_GETSTATUS = 1 | WDR_INT
WDIOC_GETBOOTSTATUS = 2 | WDR_INT
WDIOC_GETTEMP = 3 | WDR_INT
WDIOC_SETOPTIONS = 4 | WDWR_INT
WDIOC_KEEPALIVE = 5 | WDR_INT
WDIOC_SETTIMEOUT = 6 | WDWR_INT
WDIOC_GETTIMEOUT = 7 | WDR_INT
WDIOC_SETPRETIMEOUT = 8 | WDWR_INT
WDIOC_GETPRETIMEOUT = 9 | WDR_INT
WDIOC_GETTIMELEFT = 10 | WDR_INT
WDIOF_OVERHEAT = 0x0001
WDIOF_FANFAULT = 0x0002
WDIOF_EXTERN1 = 0x0004
WDIOF_EXTERN2 = 0x0008
WDIOF_POWERUNDER = 0x0010
WDIOF_CARDRESET = 0x0020
WDIOF_POWEROVER = 0x0040
WDIOF_SETTIMEOUT = 0x0080
WDIOF_MAGICCLOSE = 0x0100
WDIOF_PRETIMEOUT = 0x0200
WDIOF_ALARMONLY = 0x0400
WDIOF_KEEPALIVEPING = 0x8000
WDIOS_DISABLECARD = 0x0001
WDIOS_ENABLECARD = 0x0002
WDIOS_TEMPPANIC = 0x0004
WATCHDOG_DEVICE = '/dev/watchdog'
WATCHDOG_STOP = 'V'
WATCHDOG_START = 'S'
class Watchdog:
"""Linux Kernel watchdog class."""
def __init__(self, stop=True):
super(Watchdog, self).__init__()
self.fd = open(WATCHDOG_DEVICE, 'w', buffering=0)
self.stop = stop
if stop:
self.Stop()
def Stop(self):
self.fd.write(WATCHDOG_STOP)
def Start(self):
self.stop = False
self.fd.write(WATCHDOG_START)
def GetSupport(self):
"""Gets watchdog info.
Watchdog info structure:
{
int32_t options;
int32_t firmware_version;
char identity[32];
}
Returns:
A dict of watchdog support info.
"""
buf = array.array('B', '\0' * 40)
fcntl.ioctl(self.fd, WDIOC_GETSUPPORT, buf, True)
options = struct.unpack_from('I', buf[0:4])[0]
firmware_version = struct.unpack_from('I', buf[4:8])[0]
identity = buf[8:].tostring().rstrip('\0')
return {'options': options,
'firmware_version': firmware_version,
'identity': identity}
def _IoctlInt(self, cmd, write_value=0):
"""Writes and reads integer.
Args:
cmd: ioctl command contains read/write flag.
write_value: the 32bit value to write.
Returns:
32bit integer from ioctl mutable buffer.
"""
buf = array.array('I', [write_value])
start_temporary = self.stop
# For stopped watchdog, we need to start it temporary to set a value as
# some hardware accepts commands only when watchdog is running.
if start_temporary:
self.Start()
fcntl.ioctl(self.fd, cmd, buf, True)
if start_temporary:
self.Stop()
return buf[0]
def GetStatus(self):
"""Gets watchdog status flags.
The status and boot status are used to fetch the current status, and the
status at the last reboot. Enabled options are described in GetSupport()
return value. Status/option WDIOF_flags:
WDIOF_OVERHEAT: CPU overheat triggered.
WDIOF_FANFAULT: Fan failed triggered.
WDIOF_EXTERN1/2: External source triggered.
WDIOF_POWEROVER: Overvoltage detected.
WDIOF_KEEPALIVEPING: Got a keep alive ping after last query.
WDIOF_TIMEOUT: Reset due to timeout.
"""
return self._IoctlInt(WDIOC_GETSTATUS)
def GetBootStatus(self):
return self._IoctlInt(WDIOC_GETBOOTSTATUS)
def GetTemp(self):
return self._IoctlInt(WDIOC_GETTEMP)
def SetOptions(self, options):
return self._IoctlInt(WDIOC_SETOPTIONS, options)
def KeepAlive(self):
if self.stop:
self.Start()
return self._IoctlInt(WDIOC_KEEPALIVE)
def SetTimeout(self, timeout):
timeout = int(timeout)
if timeout <= 0:
raise ValueError('timeout <= 0')
return self._IoctlInt(WDIOC_SETTIMEOUT, timeout)
def GetTimeout(self):
return self._IoctlInt(WDIOC_GETTIMEOUT)
def SetPreTimeout(self, timeout):
timeout = int(timeout)
if timeout <= 0:
raise ValueError('timeout <= 0')
return self._IoctlInt(WDIOC_SETPRETIMEOUT, timeout)
def GetPreTimeout(self):
return self._IoctlInt(WDIOC_GETPRETIMEOUT)
def GetTimeLeft(self):
return self._IoctlInt(WDIOC_GETTIMELEFT)