Assorted bag'o tweaks & fixes (#9569)

* UIManager: Support more specialized update modes for corner-cases:
  * A2, which we'll use for the VirtualKeyboards keys (they'd... inadvertently switched to UI with the highlight refactor).
  * NO_MERGE variants of ui & partial (for sunxi). Use `[ui]` in ReaderHighlight's popup, because of a Sage kernel bug that could otherwise make it translucent, sometimes completely so (*sigh*).
* UIManager: Assorted code cleanups & simplifications.
* Logger & dbg: Unify logging style, and code cleanups.
* SDL: Unbreak suspend/resume outside of the emulator (fix #9567).
* NetworkMgr: Cache the network status, and allow it to be queried. (Used by AutoSuspend to avoid repeatedly poking the system when computing the standby schedule delay).
* OneTimeMigration: Don't forget about `NETWORK_PROXY` & `STARDICT_DATA_DIR` when migrating `defaults.persistent.lua` (fix #9573)
* WakeupMgr: Workaround an apparent limitation of the RTC found on i.MX5 Kobo devices, where setting a wakealarm further than UINT16_MAX seconds in the future would apparently overflow and wraparound... (fix #8039, many thanks to @yfede for the extensive deep-dive and for actually accurately pinpointing the issue!).
* Kobo: Handle standby transitions at full CPU clock speeds, in order to limit the latency hit.
* UIManager: Properly quit on reboot & exit. This ensures our exit code is preserved, as we exit on our own terms (instead of being killed by the init system). This is important on platforms where exit codes are semantically meaningful (e.g., Kobo).
* UIManager: Speaking of reboot & exit, make sure the Screensaver shows in all circumstances (e.g., autoshutdown, re: #9542)), and that there aren't any extraneous refreshes triggered. (Additionally, fix a minor regression since #9448 about tracking this very transient state on Kobo & Cervantes).
* Kindle: ID the upcoming Scribe.
* Bump base (https://github.com/koreader/koreader-base/pull/1524)
This commit is contained in:
NiLuJe
2022-10-02 03:01:49 +02:00
committed by GitHub
parent 5d9f036331
commit 9bf19d1bb3
28 changed files with 678 additions and 465 deletions

View File

@@ -1,6 +1,8 @@
local Generic = require("device/generic/device")
local Geom = require("ui/geometry")
local UIManager -- Updated on UIManager init
local WakeupMgr = require("device/wakeupmgr")
local time = require("ui/time")
local ffiUtil = require("ffi/util")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
@@ -27,6 +29,31 @@ local function koboEnableWifi(toggle)
end
end
-- NOTE: Cheap-ass way of checking if Wi-Fi seems to be enabled...
-- Since the crux of the issues lies in race-y module unloading, this is perfectly fine for our usage.
local function koboIsWifiOn()
local needle = os.getenv("WIFI_MODULE") or "sdio_wifi_pwr"
local nlen = #needle
-- /proc/modules is usually empty, unless Wi-Fi or USB is enabled
-- We could alternatively check if lfs.attributes("/proc/sys/net/ipv4/conf/" .. os.getenv("INTERFACE"), "mode") == "directory"
-- c.f., also what Cervantes does via /sys/class/net/eth0/carrier to check if the interface is up.
-- That said, since we only care about whether *modules* are loaded, this does the job nicely.
local f = io.open("/proc/modules", "re")
if not f then
return false
end
local found = false
for haystack in f:lines() do
if haystack:sub(1, nlen) == needle then
found = true
break
end
end
f:close()
return found
end
-- checks if standby is available on the device
local function checkStandby()
logger.dbg("Kobo: checking if standby is possible ...")
@@ -63,6 +90,47 @@ local function writeToSys(val, file)
return nw == bytes
end
-- Return the highest core number
local function getCPUCount()
local fd = io.open("/sys/devices/system/cpu/possible", "re")
if fd then
local str = fd:read("*line")
fd:close()
-- Format is n-N, where n is the first core, and N the last (e.g., 0-3)
return tonumber(str:match("%d+$")) or 1
else
return 1
end
end
local function getCPUGovernor(knob)
local fd = io.open(knob, "re")
if fd then
local str = fd:read("*line")
fd:close()
-- If we're currently using the userspace governor, fudge that to conservative, as we won't ever standby with Wi-Fi on.
-- (userspace is only used on i.MX5 for DVFS shenanigans when Wi-Fi is enabled)
if str == "userspace" then
str = "conservative"
end
return str
else
return nil
end
end
local function getRTCName()
local fd = io.open("/sys/class/rtc/rtc0/name", "re")
if fd then
local str = fd:read("*line")
fd:close()
return str
else
return nil
end
end
local Kobo = Generic:new{
model = "Kobo",
isKobo = yes,
@@ -74,6 +142,7 @@ local Kobo = Generic:new{
canReboot = yes,
canPowerOff = yes,
canSuspend = yes,
supportsScreensaver = yes,
-- most Kobos have X/Y switched for the touch screen
touch_switch_xy = true,
-- most Kobos have also mirrored X coordinates
@@ -477,7 +546,6 @@ end
function Kobo:getKeyRepeat()
-- Sanity check (mostly for the testsuite's benefit...)
if not self.ntx_fd then
self.hasKeyRepeat = false
return false
end
@@ -485,20 +553,14 @@ function Kobo:getKeyRepeat()
if C.ioctl(self.ntx_fd, C.EVIOCGREP, self.key_repeat) < 0 then
local err = ffi.errno()
logger.warn("Device:getKeyRepeat: EVIOCGREP ioctl failed:", ffi.string(C.strerror(err)))
self.hasKeyRepeat = false
return false
else
self.hasKeyRepeat = true
logger.dbg("Key repeat is set up to repeat every", self.key_repeat[C.REP_PERIOD], "ms after a delay of", self.key_repeat[C.REP_DELAY], "ms")
return true
end
return self.hasKeyRepeat
end
function Kobo:disableKeyRepeat()
if not self.hasKeyRepeat then
return
end
-- NOTE: LuaJIT zero inits, and PERIOD == 0 with DELAY == 0 disables repeats ;).
local key_repeat = ffi.new("unsigned int[?]", C.REP_CNT)
if C.ioctl(self.ntx_fd, C.EVIOCSREP, key_repeat) < 0 then
@@ -508,10 +570,6 @@ function Kobo:disableKeyRepeat()
end
function Kobo:restoreKeyRepeat()
if not self.hasKeyRepeat then
return
end
if C.ioctl(self.ntx_fd, C.EVIOCSREP, self.key_repeat) < 0 then
local err = ffi.errno()
logger.warn("Device:restoreKeyRepeat: EVIOCSREP ioctl failed:", ffi.string(C.strerror(err)))
@@ -606,6 +664,35 @@ function Kobo:init()
end
end
-- NOTE: i.MX5 devices have a wonky RTC that doesn't like alarms set further away that UINT16_MAX seconds from now...
-- (c.f., WakeupMgr for more details).
-- NOTE: getRTCName is currently hardcoded to rtc0 (which is also WakeupMgr's default).
local dodgy_rtc = false
if getRTCName() == "pmic_rtc" then
-- This *should* match the 'RTC' (46) NTX HWConfig field being set to 'MSP430' (0).
dodgy_rtc = true
end
-- Detect the various CPU governor sysfs knobs...
if util.pathExists("/sys/devices/system/cpu/cpufreq/policy0") then
self.cpu_governor_knob = "/sys/devices/system/cpu/cpufreq/policy0/scaling_governor"
else
self.cpu_governor_knob = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
end
self.default_cpu_governor = getCPUGovernor(self.cpu_governor_knob)
-- NOP unsupported methods
if not self.default_cpu_governor then
self.performanceCPUGovernor = function() end
self.defaultCPUGovernor = function() end
end
-- And while we're on CPU-related endeavors...
self.cpu_count = self:isSMP() and getCPUCount() or 1
-- NOP unsupported methods
if self.cpu_count == 1 then
self.enableCPUCores = function() end
end
-- Automagically set this so we never have to remember to do it manually ;p
if self:hasNaturalLight() and self.frontlight_settings and self.frontlight_settings.frontlight_mixer then
self.hasNaturalLightMixer = yes
@@ -651,7 +738,9 @@ function Kobo:init()
main_finger_slot = self.main_finger_slot or 0,
pressure_event = self.pressure_event,
}
self.wakeup_mgr = WakeupMgr:new()
self.wakeup_mgr = WakeupMgr:new{
dodgy_rtc = dodgy_rtc,
}
Generic.init(self)
@@ -677,7 +766,17 @@ function Kobo:init()
self:initEventAdjustHooks()
-- See if the device supports key repeat
self:getKeyRepeat()
if not self:getKeyRepeat() then
-- NOP unsupported methods
self.disableKeyRepeat = function() end
self.restoreKeyRepeat = function() end
end
-- NOP unsupported methods
if not self:canToggleChargingLED() then
self.toggleChargingLED = function() end
self.setupChargingLED = function() end
end
-- We have no way of querying the current state of the charging LED, so, start from scratch.
-- Much like Nickel, start by turning it off.
@@ -754,34 +853,9 @@ function Kobo:initNetworkManager(NetworkMgr)
os.execute("./restore-wifi-async.sh")
end
-- NOTE: Cheap-ass way of checking if Wi-Fi seems to be enabled...
-- Since the crux of the issues lies in race-y module unloading, this is perfectly fine for our usage.
function NetworkMgr:isWifiOn()
local needle = os.getenv("WIFI_MODULE") or "sdio_wifi_pwr"
local nlen = #needle
-- /proc/modules is usually empty, unless Wi-Fi or USB is enabled
-- We could alternatively check if lfs.attributes("/proc/sys/net/ipv4/conf/" .. os.getenv("INTERFACE"), "mode") == "directory"
-- c.f., also what Cervantes does via /sys/class/net/eth0/carrier to check if the interface is up.
-- That said, since we only care about whether *modules* are loaded, this does the job nicely.
local f = io.open("/proc/modules", "re")
if not f then
return false
end
local found = false
for haystack in f:lines() do
if haystack:sub(1, nlen) == needle then
found = true
break
end
end
f:close()
return found
end
NetworkMgr.isWifiOn = koboIsWifiOn
end
function Kobo:supportsScreensaver() return true end
function Kobo:setTouchEventHandler()
if self.touch_snow_protocol then
self.input.snow_protocol = true
@@ -872,14 +946,12 @@ end
-- NOTE: We overload this to make sure checkUnexpectedWakeup doesn't trip *before* the newly scheduled suspend
function Kobo:rescheduleSuspend()
local UIManager = require("ui/uimanager")
UIManager:unschedule(self.suspend)
UIManager:unschedule(self.checkUnexpectedWakeup)
UIManager:scheduleIn(self.suspend_wait_timeout, self.suspend, self)
end
function Kobo:checkUnexpectedWakeup()
local UIManager = require("ui/uimanager")
-- Just in case another event like SleepCoverClosed also scheduled a suspend
UIManager:unschedule(self.suspend)
@@ -911,6 +983,20 @@ end
--- The function to put the device into standby, with enabled touchscreen.
-- max_duration ... maximum time for the next standby, can wake earlier (e.g. Tap, Button ...)
function Kobo:standby(max_duration)
-- NOTE: Switch to the performance CPU governor, in order to speed up the resume process so as to lower its latency cost...
-- (It won't have any impact on power efficiency *during* suspend, so there's not really any drawback).
self:performanceCPUGovernor()
--[[
-- On most devices, attempting to PM with a Wi-Fi module loaded will horribly crash the kernel, so, don't?
-- NOTE: Much like suspend, our caller should ensure this never happens, hence this being commented out ;).
if koboIsWifiOn() then
-- AutoSuspend relies on NetworkMgr:getWifiState to prevent this, so, if we ever trip this, it's a bug ;).
logger.err("Kobo standby: cannot standby with Wi-Fi modules loaded! (NetworkMgr is confused: this is a bug)")
return
end
--]]
-- We don't really have anything to schedule, we just need an alarm out of WakeupMgr ;).
local function standby_alarm()
end
@@ -919,7 +1005,6 @@ function Kobo:standby(max_duration)
self.wakeup_mgr:addTask(max_duration, standby_alarm)
end
local time = require("ui/time")
logger.info("Kobo standby: asking to enter standby . . .")
local standby_time = time.boottime_or_realtime_coarse()
@@ -949,11 +1034,13 @@ function Kobo:standby(max_duration)
--]]
self.wakeup_mgr:removeTasks(nil, standby_alarm)
end
-- And restore the standard CPU scheduler once we're done dealing with the wakeup event.
UIManager:tickAfterNext(self.defaultCPUGovernor, self)
end
function Kobo:suspend()
logger.info("Kobo suspend: going to sleep . . .")
local UIManager = require("ui/uimanager")
UIManager:unschedule(self.checkUnexpectedWakeup)
-- NOTE: Sleep as little as possible here, sleeping has a tendency to make
-- everything mysteriously hang...
@@ -1020,7 +1107,6 @@ function Kobo:suspend()
end
--]]
local time = require("ui/time")
logger.info("Kobo suspend: asking for a suspend to RAM . . .")
local suspend_time = time.boottime_or_realtime_coarse()
@@ -1072,7 +1158,6 @@ function Kobo:resume()
-- Reset unexpected_wakeup_count ASAP
self.unexpected_wakeup_count = 0
-- Unschedule the checkUnexpectedWakeup shenanigans.
local UIManager = require("ui/uimanager")
UIManager:unschedule(self.checkUnexpectedWakeup)
UIManager:unschedule(self.suspend)
@@ -1122,11 +1207,11 @@ function Kobo:powerOff()
self.wakeup_mgr:unsetWakeupAlarm()
-- Then shut down without init's help
os.execute("poweroff -f")
os.execute("sleep 1 && poweroff -f &")
end
function Kobo:reboot()
os.execute("reboot")
os.execute("sleep 1 && reboot &")
end
function Kobo:toggleGSensor(toggle)
@@ -1138,10 +1223,6 @@ function Kobo:toggleGSensor(toggle)
end
function Kobo:toggleChargingLED(toggle)
if not self:canToggleChargingLED() then
return
end
-- We have no way of querying the current state from the HW!
if toggle == nil then
return
@@ -1204,33 +1285,20 @@ function Kobo:toggleChargingLED(toggle)
end
-- Return the highest core number
local function getCPUCount()
function Kobo:getCPUCount()
local fd = io.open("/sys/devices/system/cpu/possible", "re")
if fd then
local str = fd:read("*line")
fd:close()
-- Format is n-N, where n is the first core, and N the last (e.g., 0-3)
return tonumber(str:match("%d+$")) or 0
return tonumber(str:match("%d+$")) or 1
else
return 0
return 1
end
end
function Kobo:enableCPUCores(amount)
if not self:isSMP() then
return
end
if not self.cpu_count then
self.cpu_count = getCPUCount()
end
-- Not actually SMP or getCPUCount failed...
if self.cpu_count == 0 then
return
end
-- CPU0 is *always* online ;).
for n=1, self.cpu_count do
local path = "/sys/devices/system/cpu/cpu" .. n .. "/online"
@@ -1249,6 +1317,14 @@ function Kobo:enableCPUCores(amount)
end
end
function Kobo:performanceCPUGovernor()
writeToSys("performance", self.cpu_governor_knob)
end
function Kobo:defaultCPUGovernor()
writeToSys(self.default_cpu_governor, self.cpu_governor_knob)
end
function Kobo:isStartupScriptUpToDate()
-- Compare the hash of the *active* script (i.e., the one in /tmp) to the *potential* one (i.e., the one in KOREADER_DIR)
local current_script = "/tmp/koreader.sh"
@@ -1258,15 +1334,18 @@ function Kobo:isStartupScriptUpToDate()
return md5.sumFile(current_script) == md5.sumFile(new_script)
end
function Kobo:setEventHandlers(UIManager)
function Kobo:setEventHandlers(uimgr)
-- Update our module-local
UIManager = uimgr
-- We do not want auto suspend procedure to waste battery during
-- suspend. So let's unschedule it when suspending, and restart it after
-- resume. Done via the plugin's onSuspend/onResume handlers.
UIManager.event_handlers["Suspend"] = function()
UIManager.event_handlers.Suspend = function()
self:_beforeSuspend()
self:onPowerEvent("Suspend")
end
UIManager.event_handlers["Resume"] = function()
UIManager.event_handlers.Resume = function()
-- MONOTONIC doesn't tick during suspend,
-- invalidate the last battery capacity pull time so that we get up to date data immediately.
self:getPowerDevice():invalidateCapacityCache()
@@ -1274,59 +1353,59 @@ function Kobo:setEventHandlers(UIManager)
self:onPowerEvent("Resume")
self:_afterResume()
end
UIManager.event_handlers["PowerPress"] = function()
UIManager.event_handlers.PowerPress = function()
-- Always schedule power off.
-- Press the power button for 2+ seconds to shutdown directly from suspend.
UIManager:scheduleIn(2, UIManager.poweroff_action)
end
UIManager.event_handlers["PowerRelease"] = function()
if not self._entered_poweroff_stage then
UIManager.event_handlers.PowerRelease = function()
if not UIManager._entered_poweroff_stage then
UIManager:unschedule(UIManager.poweroff_action)
-- resume if we were suspended
if self.screen_saver_mode then
UIManager.event_handlers["Resume"]()
UIManager.event_handlers.Resume()
else
UIManager.event_handlers["Suspend"]()
UIManager.event_handlers.Suspend()
end
end
end
UIManager.event_handlers["Light"] = function()
UIManager.event_handlers.Light = function()
self:getPowerDevice():toggleFrontlight()
end
-- USB plug events with a power-only charger
UIManager.event_handlers["Charging"] = function()
UIManager.event_handlers.Charging = function()
self:_beforeCharging()
-- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep.
if self.screen_saver_mode then
UIManager.event_handlers["Suspend"]()
UIManager.event_handlers.Suspend()
end
end
UIManager.event_handlers["NotCharging"] = function()
UIManager.event_handlers.NotCharging = function()
-- We need to put the device into suspension, other things need to be done before it.
self:usbPlugOut()
self:_afterNotCharging()
if self.screen_saver_mode then
UIManager.event_handlers["Suspend"]()
UIManager.event_handlers.Suspend()
end
end
-- USB plug events with a data-aware host
UIManager.event_handlers["UsbPlugIn"] = function()
UIManager.event_handlers.UsbPlugIn = function()
self:_beforeCharging()
-- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep.
if self.screen_saver_mode then
UIManager.event_handlers["Suspend"]()
UIManager.event_handlers.Suspend()
else
-- Potentially start an USBMS session
local MassStorage = require("ui/elements/mass_storage")
MassStorage:start()
end
end
UIManager.event_handlers["UsbPlugOut"] = function()
UIManager.event_handlers.UsbPlugOut = function()
-- We need to put the device into suspension, other things need to be done before it.
self:usbPlugOut()
self:_afterNotCharging()
if self.screen_saver_mode then
UIManager.event_handlers["Suspend"]()
UIManager.event_handlers.Suspend()
else
-- Potentially dismiss the USBMS ConfirmBox
local MassStorage = require("ui/elements/mass_storage")
@@ -1337,25 +1416,25 @@ function Kobo:setEventHandlers(UIManager)
if G_reader_settings:isTrue("ignore_power_sleepcover") then
-- NOTE: The hardware event itself will wake the kernel up if it's in suspend (:/).
-- Let the unexpected wakeup guard handle that.
UIManager.event_handlers["SleepCoverClosed"] = nil
UIManager.event_handlers["SleepCoverOpened"] = nil
UIManager.event_handlers.SleepCoverClosed = nil
UIManager.event_handlers.SleepCoverOpened = nil
elseif G_reader_settings:isTrue("ignore_open_sleepcover") then
-- Just ignore wakeup events, and do NOT set is_cover_closed,
-- so device/generic/device will let us use the power button to wake ;).
UIManager.event_handlers["SleepCoverClosed"] = function()
UIManager.event_handlers["Suspend"]()
UIManager.event_handlers.SleepCoverClosed = function()
UIManager.event_handlers.Suspend()
end
UIManager.event_handlers["SleepCoverOpened"] = function()
UIManager.event_handlers.SleepCoverOpened = function()
self.is_cover_closed = false
end
else
UIManager.event_handlers["SleepCoverClosed"] = function()
UIManager.event_handlers.SleepCoverClosed = function()
self.is_cover_closed = true
UIManager.event_handlers["Suspend"]()
UIManager.event_handlers.Suspend()
end
UIManager.event_handlers["SleepCoverOpened"] = function()
UIManager.event_handlers.SleepCoverOpened = function()
self.is_cover_closed = false
UIManager.event_handlers["Resume"]()
UIManager.event_handlers.Resume()
end
end
end