mirror of
https://github.com/koreader/koreader
synced 2024-11-10 01:10:34 +00:00
Kobo: Refactor suspend in order to be able to catch input events sent during the 2s window of ntx madness (#12330)
I don't even remember how badly things broke (at least on old devices) without it, despite it making absolutely no sense at all (state-extended just flips a global that dictates whether some things get flagged as wakeup sources or not). So, don't rock the boat too much: we don't remove it, but instead of using a sleep, we use a task deadline instead, which ensures we'll keep processing input events in the right order in the meantime. We'll already have neutered input by this point, so we'll only process power events anyway. That means that the only iffy things are potentially *when* and *where* we have to potentially cancel that task. Resume makes sense, of course, and we log an info message to make the log flow clear; but we also do so in suspend... just in case. With a warning log because that probably indicates something fishy went on. Also cleanup the comments while I'm there, and actually rewrite the wakeup_count stuff properly so it could actually theoretically be used if ntx kernels were actually reliable. Spoiler alert: they're not, this is still horribly broken on at least Mk < 7. Works just fine on a Forma, though, so, yay. Fix #12325
This commit is contained in:
parent
59476d38f4
commit
4264d915b1
@ -727,6 +727,9 @@ function Kobo:init()
|
||||
end
|
||||
end
|
||||
|
||||
-- Just to be safe, we absolutely don't want to call open on this, so just use stat
|
||||
self.has_wakeup_count = util.pathExists("/sys/power/wakeup_count")
|
||||
|
||||
-- Automagic sysfs discovery
|
||||
if self.automagic_sysfs then
|
||||
-- Battery
|
||||
@ -1194,10 +1197,17 @@ end
|
||||
-- NOTE: We overload this to make sure checkUnexpectedWakeup doesn't trip *before* the newly scheduled suspend
|
||||
function Kobo:rescheduleSuspend()
|
||||
UIManager:unschedule(self.suspend)
|
||||
UIManager:unschedule(self._doSuspend)
|
||||
UIManager:unschedule(self.checkUnexpectedWakeup)
|
||||
UIManager:scheduleIn(self.suspend_wait_timeout, self.suspend, self)
|
||||
end
|
||||
|
||||
function Kobo:scheduleUnexpectedWakeupGuard()
|
||||
self.unexpected_wakeup_count = self.unexpected_wakeup_count + 1
|
||||
logger.dbg("Kobo suspend: scheduling unexpected wakeup guard")
|
||||
UIManager:scheduleIn(15, self.checkUnexpectedWakeup, self)
|
||||
end
|
||||
|
||||
function Kobo:checkUnexpectedWakeup()
|
||||
-- Just in case another event like SleepCoverClosed also scheduled a suspend
|
||||
UIManager:unschedule(self.suspend)
|
||||
@ -1309,6 +1319,12 @@ function Kobo:standby(max_duration)
|
||||
end
|
||||
|
||||
function Kobo:suspend()
|
||||
-- If there's a _doSuspend still scheduled, something is going seriously wrong
|
||||
-- (e.g., we caught multiple Suspend events without a Resume in between)...
|
||||
if UIManager:unschedule(self._doSuspend) then
|
||||
logger.warn("Kobo suspend: cancelled a pending suspend request via *suspend*. This is most likely a bug.")
|
||||
end
|
||||
|
||||
-- On MTK, any suspend/standby attempt while plugged-in will hang the kernel... -_-"
|
||||
-- NOTE: isCharging is still true while isCharged!
|
||||
if self:isMTK() and self.powerd:isCharging() then
|
||||
@ -1316,45 +1332,14 @@ function Kobo:suspend()
|
||||
|
||||
-- Do the usual scheduling dance, so we get a chance to fire the UnexpectedWakeupLimit event...
|
||||
UIManager:unschedule(self.checkUnexpectedWakeup)
|
||||
self.unexpected_wakeup_count = self.unexpected_wakeup_count + 1
|
||||
logger.dbg("Kobo suspend: scheduling unexpected wakeup guard")
|
||||
UIManager:scheduleIn(15, self.checkUnexpectedWakeup, self)
|
||||
|
||||
self:scheduleUnexpectedWakeupGuard()
|
||||
return
|
||||
end
|
||||
|
||||
logger.info("Kobo suspend: going to sleep . . .")
|
||||
UIManager:unschedule(self.checkUnexpectedWakeup)
|
||||
-- NOTE: Sleep as little as possible here, sleeping has a tendency to make
|
||||
-- everything mysteriously hang...
|
||||
|
||||
-- Depending on device/FW version, some kernels do not support
|
||||
-- wakeup_count, account for that
|
||||
--
|
||||
-- NOTE: ... and of course, it appears to be broken, which probably
|
||||
-- explains why nickel doesn't use this facility...
|
||||
-- (By broken, I mean that the system wakes up right away).
|
||||
-- So, unless that changes, unconditionally disable it.
|
||||
|
||||
--[[
|
||||
local f, re, err_msg, err_code
|
||||
local has_wakeup_count = false
|
||||
f = io.open("/sys/power/wakeup_count", "re")
|
||||
if f ~= nil then
|
||||
f:close()
|
||||
has_wakeup_count = true
|
||||
end
|
||||
|
||||
-- Clear the kernel ring buffer... (we're missing a proper -C flag...)
|
||||
--dmesg -c >/dev/null
|
||||
|
||||
-- Go to sleep
|
||||
local curr_wakeup_count
|
||||
if has_wakeup_count then
|
||||
curr_wakeup_count = "$(cat /sys/power/wakeup_count)"
|
||||
logger.dbg("Kobo suspend: Current WakeUp count:", curr_wakeup_count)
|
||||
end
|
||||
-]]
|
||||
-- everything mysteriously hang...
|
||||
|
||||
-- NOTE: Sets gSleep_Mode_Suspend to 1. Used as a flag throughout the
|
||||
-- kernel to suspend/resume various subsystems
|
||||
@ -1363,37 +1348,50 @@ function Kobo:suspend()
|
||||
if ret then
|
||||
logger.dbg("Kobo suspend: successfully asked the kernel to put subsystems to sleep")
|
||||
else
|
||||
logger.warn("Kobo suspend: the kernel refused to flag subsystems for suspend!")
|
||||
logger.err("Kobo suspend: the kernel refused to flag subsystems for suspend, aborting this attempt!")
|
||||
-- We'd be going to standby instead of suspend, so, just try again later.
|
||||
self:scheduleUnexpectedWakeupGuard()
|
||||
return
|
||||
end
|
||||
|
||||
ffiUtil.sleep(2)
|
||||
logger.dbg("Kobo suspend: waited for 2s because of reasons...")
|
||||
-- NOTE: As nonsensical as it looks given that the above just flips a global,
|
||||
-- I have traumatic memories of things going awry if we don't sleep between the two writes...
|
||||
logger.dbg("Kobo suspend: waiting for 2s because of reasons...")
|
||||
-- We keep polling for input in order to be able to catch power events with extremely unlucky timing (#12325)...
|
||||
UIManager:scheduleIn(2, self._doSuspend, self)
|
||||
end
|
||||
|
||||
function Kobo:_doSuspend()
|
||||
os.execute("sync")
|
||||
logger.dbg("Kobo suspend: synced FS")
|
||||
|
||||
-- Depending on device/FW version, some kernels do not support wakeup_count, account for that.
|
||||
-- NOTE: ...and of course, it appears to be broken on older devices,
|
||||
-- which probably explains why nickel doesn't use this facility there...
|
||||
-- (By broken, I mean that the system wakes up right away despite the successful write).
|
||||
-- As we can't really divine where and when it'll work properly, unconditionally disable it.
|
||||
--[[
|
||||
if has_wakeup_count then
|
||||
f = io.open("/sys/power/wakeup_count", "we")
|
||||
if not f then
|
||||
logger.err("cannot open /sys/power/wakeup_count")
|
||||
return false
|
||||
if self.has_wakeup_count then
|
||||
self.curr_wakeup_count = self.powerd:read_int_file("/sys/power/wakeup_count")
|
||||
logger.dbg("Kobo suspend: Current WakeUp count:", self.curr_wakeup_count)
|
||||
|
||||
local ret = ffiUtil.writeToSysfs(self.curr_wakeup_count, "/sys/power/wakeup_count")
|
||||
if ret then
|
||||
logger.dbg("Kobo suspend: WakeUp count matched")
|
||||
else
|
||||
logger.err("Kobo suspend: WakeUp count mismatch, aborting this suspend attempt!")
|
||||
-- This means that there was at least one wakeup event since our read,
|
||||
-- abort this attempt (i.e., don't write to state for now) and just schedule the wakeup guard.
|
||||
self:scheduleUnexpectedWakeupGuard()
|
||||
return
|
||||
end
|
||||
re, err_msg, err_code = f:write(tostring(curr_wakeup_count), "\n")
|
||||
logger.dbg("Kobo suspend: wrote WakeUp count:", curr_wakeup_count)
|
||||
if not re then
|
||||
logger.err("Kobo suspend: failed to write WakeUp count:",
|
||||
err_msg,
|
||||
err_code)
|
||||
end
|
||||
f:close()
|
||||
end
|
||||
--]]
|
||||
|
||||
logger.dbg("Kobo suspend: asking for a suspend to RAM . . .")
|
||||
local suspend_time = time.boottime_or_realtime_coarse()
|
||||
|
||||
ret = ffiUtil.writeToSysfs("mem", "/sys/power/state")
|
||||
local ret = ffiUtil.writeToSysfs("mem", "/sys/power/state")
|
||||
|
||||
-- NOTE: At this point, we *should* be in suspend to RAM, as such,
|
||||
-- execution should only resume on wakeup...
|
||||
@ -1406,42 +1404,25 @@ function Kobo:suspend()
|
||||
self:toggleChargingLED(false)
|
||||
end
|
||||
else
|
||||
-- Most of the potential failures ought to be -EBUSY
|
||||
-- (usually, because of the EPDC or touch panel).
|
||||
-- NOTE: On recent enough kernels, with debugfs enabled and mounted, see also
|
||||
-- /sys/kernel/debug/suspend_stats & /sys/kernel/debug/wakeup_sources
|
||||
logger.warn("Kobo suspend: the kernel refused to enter suspend!")
|
||||
-- Reset state-extended back to 0 since we are giving up.
|
||||
-- NOTE: Despite it making little sense,
|
||||
-- we reset state-extended back to 0 to mimic Nickel's own
|
||||
-- 1 -> mem -> 0 loop in case of suspend failures...
|
||||
-- c.f., nickel_suspend_strace.txt for more details.
|
||||
ffiUtil.writeToSysfs("0", "/sys/power/state-extended")
|
||||
if G_reader_settings:isTrue("pm_debug_entry_failure") then
|
||||
self:toggleChargingLED(true)
|
||||
end
|
||||
end
|
||||
|
||||
-- NOTE: Ideally, we'd need a way to warn the user that suspending
|
||||
-- gloriously failed at this point...
|
||||
-- We can safely assume that just from a non-zero return code, without
|
||||
-- looking at the detailed stderr message
|
||||
-- (most of the failures we'll see are -EBUSY anyway)
|
||||
-- For reference, when that happens to nickel, it appears to keep retrying
|
||||
-- to wakeup & sleep ad nauseam,
|
||||
-- which is where the non-sensical 1 -> mem -> 0 loop idea comes from...
|
||||
-- cf. nickel_suspend_strace.txt for more details.
|
||||
-- NOTE: On recent enough kernels, with debugfs enabled and mounted, see also
|
||||
-- /sys/kernel/debug/suspend_stats & /sys/kernel/debug/wakeup_sources
|
||||
|
||||
--[[
|
||||
if has_wakeup_count then
|
||||
logger.dbg("wakeup count: $(cat /sys/power/wakeup_count)")
|
||||
end
|
||||
|
||||
-- Print tke kernel log since our attempt to sleep...
|
||||
--dmesg -c
|
||||
--]]
|
||||
|
||||
-- NOTE: We unflag /sys/power/state-extended in Kobo:resume() to keep
|
||||
-- things tidy and easier to follow
|
||||
|
||||
-- Kobo:resume() will reset unexpected_wakeup_count and unschedule the check to signal a sane wakeup.
|
||||
self.unexpected_wakeup_count = self.unexpected_wakeup_count + 1
|
||||
logger.dbg("Kobo suspend: scheduling unexpected wakeup guard")
|
||||
UIManager:scheduleIn(15, self.checkUnexpectedWakeup, self)
|
||||
-- Kobo:resume() will also reset unexpected_wakeup_count and unschedule the check to signal a sane wakeup.
|
||||
self:scheduleUnexpectedWakeupGuard()
|
||||
end
|
||||
|
||||
function Kobo:resume()
|
||||
@ -1451,6 +1432,10 @@ function Kobo:resume()
|
||||
-- Unschedule the checkUnexpectedWakeup shenanigans.
|
||||
UIManager:unschedule(self.checkUnexpectedWakeup)
|
||||
UIManager:unschedule(self.suspend)
|
||||
-- Cancel any pending suspend request
|
||||
if UIManager:unschedule(self._doSuspend) then
|
||||
logger.info("Kobo resume: cancelled a pending suspend request")
|
||||
end
|
||||
|
||||
-- Now that we're up, unflag subsystems for suspend...
|
||||
-- NOTE: Sets gSleep_Mode_Suspend to 0. Used as a flag throughout the
|
||||
|
@ -1,7 +1,7 @@
|
||||
local BasePowerD = require("device/generic/powerd")
|
||||
local Math = require("optmath")
|
||||
local NickelConf = require("device/kobo/nickel_conf")
|
||||
local SysfsLight = require ("device/sysfs_light")
|
||||
local SysfsLight = require("device/sysfs_light")
|
||||
local UIManager
|
||||
local logger = require("logger")
|
||||
local RTC = require("ffi/rtc")
|
||||
|
Loading…
Reference in New Issue
Block a user