From 1102c030fa2b75ffe66d3a00138ae983a8942eff Mon Sep 17 00:00:00 2001 From: yparitcher Date: Wed, 3 May 2023 10:26:35 -0400 Subject: [PATCH] Kindle: toggle cover events Allow disabling the hall efect sensor via the sysfs knob, so the kindle system wont sleep & wake the device for those of use that stay in koreader, are caseless and have get spurious wakeups --- frontend/device/kindle/device.lua | 9 +++++ frontend/device/kindle/powerd.lua | 20 +++++++++- frontend/device/kobo/device.lua | 37 +++++-------------- frontend/device/sysfs_light.lua | 37 ++++--------------- .../elements/common_settings_menu_table.lua | 12 ++++++ .../ui/elements/filemanager_menu_order.lua | 1 + frontend/ui/elements/reader_menu_order.lua | 1 + frontend/util.lua | 24 ++++++++++++ spec/unit/device_spec.lua | 3 -- 9 files changed, 81 insertions(+), 63 deletions(-) diff --git a/frontend/device/kindle/device.lua b/frontend/device/kindle/device.lua index 0528edfe3..14f0dfe99 100644 --- a/frontend/device/kindle/device.lua +++ b/frontend/device/kindle/device.lua @@ -726,6 +726,7 @@ function KindlePaperWhite2:init() fl_intensity_file = "/sys/class/backlight/max77696-bl/brightness", batt_capacity_file = "/sys/devices/system/wario_battery/wario_battery0/battery_capacity", is_charging_file = "/sys/devices/system/wario_charger/wario_charger0/charging", + hall_file = "/sys/devices/system/wario_hall/wario_hall0/hall_enable", } Kindle.init(self) @@ -740,6 +741,7 @@ function KindleBasic:init() device = self, batt_capacity_file = "/sys/devices/system/wario_battery/wario_battery0/battery_capacity", is_charging_file = "/sys/devices/system/wario_charger/wario_charger0/charging", + hall_file = "/sys/devices/system/wario_hall/wario_hall0/hall_enable", } Kindle.init(self) @@ -755,6 +757,7 @@ function KindleVoyage:init() fl_intensity_file = "/sys/class/backlight/max77696-bl/brightness", batt_capacity_file = "/sys/devices/system/wario_battery/wario_battery0/battery_capacity", is_charging_file = "/sys/devices/system/wario_charger/wario_charger0/charging", + hall_file = "/sys/devices/system/wario_hall/wario_hall0/hall_enable", } self.input = require("device/input"):new{ device = self, @@ -814,6 +817,7 @@ function KindlePaperWhite3:init() fl_intensity_file = "/sys/class/backlight/max77696-bl/brightness", batt_capacity_file = "/sys/devices/system/wario_battery/wario_battery0/battery_capacity", is_charging_file = "/sys/devices/system/wario_charger/wario_charger0/charging", + hall_file = "/sys/devices/system/wario_hall/wario_hall0/hall_enable", } Kindle.init(self) @@ -870,6 +874,7 @@ function KindleOasis:init() -- NOTE: Points to the embedded battery. The one in the cover is codenamed "soda". batt_capacity_file = "/sys/devices/system/wario_battery/wario_battery0/battery_capacity", is_charging_file = "/sys/devices/system/wario_charger/wario_charger0/charging", + hall_file = "/sys/devices/system/wario_hall/wario_hall0/hall_enable", } self.input = require("device/input"):new{ @@ -1130,6 +1135,7 @@ function KindleBasic2:init() batt_capacity_file = "/sys/class/power_supply/bd7181x_bat/capacity", is_charging_file = "/sys/class/power_supply/bd7181x_bat/charging", batt_status_file = "/sys/class/power_supply/bd7181x_bat/status", + hall_file = "/sys/devices/system/heisenberg_hall/heisenberg_hall0/hall_enable", } Kindle.init(self) @@ -1146,6 +1152,7 @@ function KindlePaperWhite4:init() batt_capacity_file = "/sys/class/power_supply/bd71827_bat/capacity", is_charging_file = "/sys/class/power_supply/bd71827_bat/charging", batt_status_file = "/sys/class/power_supply/bd71827_bat/status", + hall_file = "/sys/bus/platform/drivers/hall_sensor/rex_hall/hall_enable", } Kindle.init(self) @@ -1172,6 +1179,7 @@ function KindleBasic3:init() batt_capacity_file = "/sys/class/power_supply/bd71827_bat/capacity", is_charging_file = "/sys/class/power_supply/bd71827_bat/charging", batt_status_file = "/sys/class/power_supply/bd71827_bat/status", + hall_file = "/sys/bus/platform/drivers/hall_sensor/rex_hall/hall_enable", } Kindle.init(self) @@ -1193,6 +1201,7 @@ function KindlePaperWhite5:init() batt_capacity_file = "/sys/class/power_supply/bd71827_bat/capacity", is_charging_file = "/sys/class/power_supply/bd71827_bat/charging", batt_status_file = "/sys/class/power_supply/bd71827_bat/status", + hall_file = "/sys/devices/platform/eink_hall/hall_enable", } -- Enable the so-called "fast" mode, so as to prevent the driver from silently promoting refreshes to REAGL. diff --git a/frontend/device/kindle/powerd.lua b/frontend/device/kindle/powerd.lua index dcfcd5d2b..173f01dc3 100644 --- a/frontend/device/kindle/powerd.lua +++ b/frontend/device/kindle/powerd.lua @@ -1,6 +1,7 @@ local BasePowerD = require("device/generic/powerd") local WakeupMgr = require("device/wakeupmgr") local logger = require("logger") +local util = require("util") -- liblipclua, see require below local KindlePowerD = BasePowerD:new{ @@ -98,11 +99,12 @@ function KindlePowerD:setIntensityHW(intensity) -- NOTE: when intensity is 0, we want to *really* kill the light, so do it manually -- (asking lipc to set it to 0 would in fact set it to > 0 on ! canTurnFrontlightOff Kindles). -- We do *both* to make the fl restore on resume less jarring on devices where lipc 0 != off. - os.execute("printf '%s' ".. intensity .." > " .. self.fl_intensity_file) + util.writeToSysfs(intensity, self.fl_intensity_file) -- And in case there are two LED groups... + -- This should never happen as all warmth devices so far canTurnFrontlightOff if self.warmth_intensity_file then - os.execute("printf '%s' ".. intensity .." > " .. self.warmth_intensity_file) + util.writeToSysfs(intensity, self.warmth_intensity_file) end end end @@ -161,6 +163,20 @@ function KindlePowerD:isChargedHW() return false end +function KindlePowerD:hasHallSensor() + return self.hall_file ~= nil +end + +function KindlePowerD:isHallSensorEnabled() + local int = self:read_int_file(self.hall_file) + return int == 1 +end + +function KindlePowerD:onToggleHallSensor() + local stat = self:isHallSensorEnabled() + util.writeToSysfs(stat and 0 or 1, self.hall_file) +end + function KindlePowerD:_readFLIntensity() return self:read_int_file(self.fl_intensity_file) end diff --git a/frontend/device/kobo/device.lua b/frontend/device/kobo/device.lua index f307a5e69..c16491e26 100644 --- a/frontend/device/kobo/device.lua +++ b/frontend/device/kobo/device.lua @@ -47,25 +47,6 @@ local function checkStandby() return no end -local function writeToSys(val, file) - -- NOTE: We do things by hand via ffi, because io.write uses fwrite, - -- which isn't a great fit for procfs/sysfs (e.g., we lose failure cases like EBUSY, - -- as it only reports failures to write to the *stream*, not to the disk/file!). - local fd = C.open(file, bit.bor(C.O_WRONLY, C.O_CLOEXEC)) -- procfs/sysfs, we shouldn't need O_TRUNC - if fd == -1 then - logger.err("Cannot open file `" .. file .. "`:", ffi.string(C.strerror(ffi.errno()))) - return - end - local bytes = #val - local nw = C.write(fd, val, bytes) - if nw == -1 then - logger.err("Cannot write `" .. val .. "` to file `" .. file .. "`:", ffi.string(C.strerror(ffi.errno()))) - end - C.close(fd) - -- NOTE: Allows the caller to possibly handle short writes (not that these should ever happen here). - return nw == bytes -end - -- Return the highest core number local function getCPUCount() local fd = io.open("/sys/devices/system/cpu/possible", "re") @@ -1064,7 +1045,7 @@ function Kobo:standby(max_duration) logger.dbg("Kobo standby: asking to enter standby . . .") local standby_time = time.boottime_or_realtime_coarse() - local ret = writeToSys("standby", "/sys/power/state") + local ret = util.writeToSysfs("standby", "/sys/power/state") self.last_standby_time = time.boottime_or_realtime_coarse() - standby_time self.total_standby_time = self.total_standby_time + self.last_standby_time @@ -1140,7 +1121,7 @@ function Kobo:suspend() -- NOTE: Sets gSleep_Mode_Suspend to 1. Used as a flag throughout the -- kernel to suspend/resume various subsystems -- c.f., state_extended_store @ kernel/power/main.c - local ret = writeToSys("1", "/sys/power/state-extended") + local ret = util.writeToSysfs("1", "/sys/power/state-extended") if ret then logger.dbg("Kobo suspend: successfully asked the kernel to put subsystems to sleep") else @@ -1174,7 +1155,7 @@ function Kobo:suspend() logger.dbg("Kobo suspend: asking for a suspend to RAM . . .") local suspend_time = time.boottime_or_realtime_coarse() - ret = writeToSys("mem", "/sys/power/state") + ret = util.writeToSysfs("mem", "/sys/power/state") -- NOTE: At this point, we *should* be in suspend to RAM, as such, -- execution should only resume on wakeup... @@ -1189,7 +1170,7 @@ function Kobo:suspend() else logger.warn("Kobo suspend: the kernel refused to enter suspend!") -- Reset state-extended back to 0 since we are giving up. - writeToSys("0", "/sys/power/state-extended") + util.writeToSysfs("0", "/sys/power/state-extended") if G_reader_settings:isTrue("pm_debug_entry_failure") then self:toggleChargingLED(true) end @@ -1238,7 +1219,7 @@ function Kobo:resume() -- kernel to suspend/resume various subsystems -- cf. kernel/power/main.c @ L#207 -- Among other things, this sets up the wakeup pins (e.g., resume on input). - local ret = writeToSys("0", "/sys/power/state-extended") + local ret = util.writeToSysfs("0", "/sys/power/state-extended") if ret then logger.dbg("Kobo resume: successfully asked the kernel to resume subsystems") else @@ -1254,7 +1235,7 @@ function Kobo:resume() -- c.f., neo_ctl @ drivers/input/touchscreen/zforce_i2c.c, -- basically, a is wakeup (for activate), d is sleep (for deactivate), and we don't care about s (set res), -- and l (led signal level, actually a NOP on NTX kernels). - writeToSys("a", self.hasIRGridSysfsKnob) + util.writeToSysfs("a", self.hasIRGridSysfsKnob) end -- A full suspend may have toggled the LED off. @@ -1367,16 +1348,16 @@ function Kobo:enableCPUCores(amount) up = "1" end - writeToSys(up, path) + util.writeToSysfs(up, path) end end function Kobo:performanceCPUGovernor() - writeToSys("performance", self.cpu_governor_knob) + util.writeToSysfs("performance", self.cpu_governor_knob) end function Kobo:defaultCPUGovernor() - writeToSys(self.default_cpu_governor, self.cpu_governor_knob) + util.writeToSysfs(self.default_cpu_governor, self.cpu_governor_knob) end function Kobo:isStartupScriptUpToDate() diff --git a/frontend/device/sysfs_light.lua b/frontend/device/sysfs_light.lua index 78d3653c9..c0bec9eac 100644 --- a/frontend/device/sysfs_light.lua +++ b/frontend/device/sysfs_light.lua @@ -2,12 +2,8 @@ -- This also supports the natural light, which consists of additional -- red and green light LEDs. -local logger = require("logger") local dbg = require("dbg") - -local ffi = require("ffi") -local C = ffi.C -require("ffi/posix_h") +local util = require("util") local SysfsLight = { frontlight_white = nil, @@ -77,15 +73,15 @@ function SysfsLight:setNaturalBrightness(brightness, warmth) if self.frontlight_ioctl then self.frontlight_ioctl:setBrightness(brightness) else - self:_write_value(self.frontlight_white, brightness) + util.writeToSysfs(brightness, self.frontlight_white) end end -- The mixer might be using inverted values... (cold is nl_max, warm is nl_min) if set_warmth then if self.nl_inverted then - self:_write_value(self.frontlight_mixer, self.nl_max - warmth) + util.writeToSysfs(self.nl_max - warmth, self.frontlight_mixer) else - self:_write_value(self.frontlight_mixer, warmth) + util.writeToSysfs(warmth, self.frontlight_mixer) end end else @@ -128,30 +124,11 @@ function SysfsLight:_set_light_value(sysfs_directory, value) if not sysfs_directory then return end -- bl_power is '31' when the light is turned on, '0' otherwise. if (value > 0) then - self:_write_value(sysfs_directory .. "/bl_power", 31) + util.writeToSysfs(31, sysfs_directory .. "/bl_power") else - self:_write_value(sysfs_directory .. "/bl_power", 0) - end - self:_write_value(sysfs_directory .. "/brightness", value) -end - -function SysfsLight:_write_value(file, val) - local fd = C.open(file, bit.bor(C.O_WRONLY, C.O_CLOEXEC)) -- procfs/sysfs, we shouldn't need O_TRUNC - if fd == -1 then - logger.err("Cannot open file `" .. file .. "`:", ffi.string(C.strerror(ffi.errno()))) - return false - end - val = tostring(val) - local bytes = #val - local nw = C.write(fd, val, bytes) - if nw == -1 then - logger.err("Cannot write `" .. val .. "` to file `" .. file .. "`:", ffi.string(C.strerror(ffi.errno()))) - C.close(fd) - return false + util.writeToSysfs(0, sysfs_directory .. "/bl_power") end - C.close(fd) - -- NOTE: Allows the caller to possibly handle short writes (not that these should ever happen here). - return nw == bytes + util.writeToSysfs(value, sysfs_directory .. "/brightness") end return SysfsLight diff --git a/frontend/ui/elements/common_settings_menu_table.lua b/frontend/ui/elements/common_settings_menu_table.lua index a3bb70f1b..0a3e21661 100644 --- a/frontend/ui/elements/common_settings_menu_table.lua +++ b/frontend/ui/elements/common_settings_menu_table.lua @@ -4,6 +4,7 @@ local Event = require("ui/event") local InfoMessage = require("ui/widget/infomessage") local Language = require("ui/language") local NetworkMgr = require("ui/network/manager") +local PowerD = Device:getPowerDevice() local UIManager = require("ui/uimanager") local _ = require("gettext") local N_ = _.ngettext @@ -236,6 +237,17 @@ if Device:isKobo() then } end +if Device:isKindle() and PowerD:hasHallSensor() then + common_settings.cover_events = { + text = _("Disable Kindle cover events"), + help_text = _([[Toggle the Hall effect sensor. +This is used to detect if the cover is closed, which will automatically sleep and wake the device. If there is no cover present the sensor may cause spurious wakeups when located next to a magnetic source.]]), + keep_menu_open = true, + checked_func = function() return not PowerD:isHallSensorEnabled() end, + callback = function() PowerD:onToggleHallSensor() end, + } +end + common_settings.night_mode = { text = _("Night mode"), checked_func = function() return G_reader_settings:isTrue("night_mode") end, diff --git a/frontend/ui/elements/filemanager_menu_order.lua b/frontend/ui/elements/filemanager_menu_order.lua index 6e170ac8f..b6f2e89a1 100644 --- a/frontend/ui/elements/filemanager_menu_order.lua +++ b/frontend/ui/elements/filemanager_menu_order.lua @@ -58,6 +58,7 @@ local order = { "autoshutdown", "ignore_sleepcover", "ignore_open_sleepcover", + "cover_events", "ignore_battery_optimizations", "mass_storage_settings", -- if Device:canToggleMassStorage() "file_ext_assoc", diff --git a/frontend/ui/elements/reader_menu_order.lua b/frontend/ui/elements/reader_menu_order.lua index df057ee67..99fd03ebe 100644 --- a/frontend/ui/elements/reader_menu_order.lua +++ b/frontend/ui/elements/reader_menu_order.lua @@ -102,6 +102,7 @@ local order = { "autoshutdown", "ignore_sleepcover", "ignore_open_sleepcover", + "cover_events", "ignore_battery_optimizations", "mass_storage_settings", -- if Device:canToggleMassStorage() "file_ext_assoc", diff --git a/frontend/util.lua b/frontend/util.lua index fe5c2af21..e19a1141d 100644 --- a/frontend/util.lua +++ b/frontend/util.lua @@ -5,9 +5,13 @@ This module contains miscellaneous helper functions for the KOReader frontend. local BaseUtil = require("ffi/util") local Utf8Proc = require("ffi/utf8proc") local lfs = require("libs/libkoreader-lfs") +local logger = require("logger") local _ = require("gettext") local C_ = _.pgettext local T = BaseUtil.template +local ffi = require("ffi") +local C = ffi.C +require("ffi/posix_h") local lshift = bit.lshift local rshift = bit.rshift @@ -829,6 +833,26 @@ function util.removeFile(file) end end +function util.writeToSysfs(val, file) + -- NOTE: We do things by hand via ffi, because io.write uses fwrite, + -- which isn't a great fit for procfs/sysfs (e.g., we lose failure cases like EBUSY, + -- as it only reports failures to write to the *stream*, not to the disk/file!). + local fd = C.open(file, bit.bor(C.O_WRONLY, C.O_CLOEXEC)) -- procfs/sysfs, we shouldn't need O_TRUNC + if fd == -1 then + logger.err("Cannot open file `" .. file .. "`:", ffi.string(C.strerror(ffi.errno()))) + return + end + val = tostring(val) + local bytes = #val + local nw = C.write(fd, val, bytes) + if nw == -1 then + logger.err("Cannot write `" .. val .. "` to file `" .. file .. "`:", ffi.string(C.strerror(ffi.errno()))) + end + C.close(fd) + -- NOTE: Allows the caller to possibly handle short writes (not that these should ever happen here). + return nw == bytes +end + -- Gets total, used and available bytes for the mountpoint that holds a given directory. -- @string path of the directory -- @treturn table with total, used and available bytes diff --git a/spec/unit/device_spec.lua b/spec/unit/device_spec.lua index a41ebb6ba..ab7d05397 100644 --- a/spec/unit/device_spec.lua +++ b/spec/unit/device_spec.lua @@ -40,7 +40,6 @@ describe("device module", function() after_each(function() mock_input.open:revert() os.getenv:revert() - os.execute:revert() os.getenv = osgetenv io.open = iopen @@ -278,8 +277,6 @@ describe("device module", function() assert.is.same(kindle_dev.powerd.fl_intensity, 5) kindle_dev.powerd:toggleFrontlight() - assert.stub(os.execute).was_called_with( - "printf '%s' 0 > /sys/class/backlight/max77696-bl/brightness") -- Here be shenanigans: we don't override powerd's fl_intensity when we turn the light off, -- so that we can properly turn it back on at the previous intensity ;) assert.is.same(kindle_dev.powerd.fl_intensity, 5)