mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Support auto-detection of input devices via fbink_input (#11807)
* Kobo: Drop a bunch of if ladder crap and switch to auto-detection of input devices via fbink_input * Kindle: Drop an even larger bundle of crap to do the same ;p. (re: #11392) * ExternalKeyboard: Switch to fbink_input to whitelist keyboards instead of the manual parsing of caps via its FindKeyboard class * Input: Extended open/close wrappers to handle logging & tracking of dupe open/close calls.
This commit is contained in:
2
base
2
base
Submodule base updated: d75933ef2d...c69ef86a42
@@ -569,7 +569,7 @@ function Device:exit()
|
||||
G_reader_settings:close()
|
||||
|
||||
-- I/O teardown
|
||||
require("ffi/input"):closeAll()
|
||||
self.input.teardown()
|
||||
end
|
||||
|
||||
-- Lifted from busybox's libbb/inet_cksum.c
|
||||
|
||||
@@ -195,6 +195,10 @@ local Input = {
|
||||
setClipboardText = function(text)
|
||||
_internal_clipboard_text = text or ""
|
||||
end,
|
||||
|
||||
-- open'ed input devices hashmap (key: path, value: fd number)
|
||||
-- Must be a class member, both because Input is a singleton and that state is process-wide anyway.
|
||||
opened_devices = {},
|
||||
}
|
||||
|
||||
function Input:new(o)
|
||||
@@ -295,14 +299,74 @@ function Input:disableRotationMap()
|
||||
end
|
||||
|
||||
--[[--
|
||||
Wrapper for FFI input open.
|
||||
Wrapper for our Lua/C input module's open.
|
||||
|
||||
Note that we adhere to the "." syntax here for compatibility.
|
||||
|
||||
@todo Clean up separation FFI/this.
|
||||
The `name` argument is optional, and used for logging purposes only.
|
||||
--]]
|
||||
function Input.open(device, is_emu_events)
|
||||
return input.open(device, is_emu_events and 1 or 0)
|
||||
function Input.open(path, name)
|
||||
-- Make sure we don't open the same device twice.
|
||||
if not Input.opened_devices[path] then
|
||||
local fd = input.open(path)
|
||||
if fd then
|
||||
Input.opened_devices[path] = fd
|
||||
if name then
|
||||
logger.dbg("Opened fd", fd, "for input device", name, "@", path)
|
||||
else
|
||||
logger.dbg("Opened fd", fd, "for input device @", path)
|
||||
end
|
||||
end
|
||||
-- No need to log failures, input will have raised an error already,
|
||||
-- and we want to make those fatal, so we don't protect this call.
|
||||
return fd
|
||||
end
|
||||
end
|
||||
|
||||
--[[--
|
||||
Wrapper for our Lua/C input module's close.
|
||||
|
||||
Note that we adhere to the "." syntax here for compatibility.
|
||||
--]]
|
||||
function Input.close(path)
|
||||
-- Make sure we actually know about this device
|
||||
local fd = Input.opened_devices[path]
|
||||
if fd then
|
||||
local ok, err = input.close(fd)
|
||||
if ok or err == C.ENODEV then
|
||||
-- Either the call succeeded,
|
||||
-- or the backend had already caught an ENODEV in waitForInput and closed the fd internally.
|
||||
-- (Because the EvdevInputRemove Event comes from an UsbDevicePlugOut uevent forwarded as an... *input* EV_KEY event ;)).
|
||||
-- Regardless, that device is gone, so clear its spot in the hashmap.
|
||||
Input.opened_devices[path] = nil
|
||||
end
|
||||
else
|
||||
logger.warn("Tried to close an unknown input device @", path)
|
||||
end
|
||||
end
|
||||
|
||||
--[[--
|
||||
Wrapper for our Lua/C input module's closeAll.
|
||||
|
||||
Note that we adhere to the "." syntax here for compatibility.
|
||||
--]]
|
||||
function Input.teardown()
|
||||
input.closeAll()
|
||||
Input.opened_devices = {}
|
||||
end
|
||||
|
||||
-- Wrappers for the custom FFI implementations with no concept of paths or fd
|
||||
if input.is_ffi then
|
||||
-- Pass args as-is. None of 'em actually *take* arguments, but some may be invoked as methods...
|
||||
function Input.open(...)
|
||||
return input.open(...)
|
||||
end
|
||||
function Input.close(...)
|
||||
return input.close(...)
|
||||
end
|
||||
function Input.teardown(...)
|
||||
return input.closeAll(...)
|
||||
end
|
||||
end
|
||||
|
||||
--[[--
|
||||
|
||||
@@ -3,7 +3,6 @@ local UIManager
|
||||
local time = require("ui/time")
|
||||
local lfs = require("libs/libkoreader-lfs")
|
||||
local logger = require("logger")
|
||||
local util = require("util")
|
||||
|
||||
-- We're going to need a few <linux/fb.h> & <linux/input.h> constants...
|
||||
local ffi = require("ffi")
|
||||
@@ -11,6 +10,7 @@ local C = ffi.C
|
||||
require("ffi/linux_fb_h")
|
||||
require("ffi/linux_input_h")
|
||||
require("ffi/posix_h")
|
||||
require("ffi/fbink_input_h")
|
||||
|
||||
local function yes() return true end
|
||||
local function no() return false end -- luacheck: ignore
|
||||
@@ -206,6 +206,58 @@ function Kindle:supportsScreensaver()
|
||||
end
|
||||
end
|
||||
|
||||
function Kindle:openInputDevices()
|
||||
-- Auto-detect input devices (via FBInk's fbink_input_scan)
|
||||
local ok, FBInkInput = pcall(ffi.load, "fbink_input")
|
||||
if not ok then
|
||||
-- NOP fallback for the testsuite...
|
||||
FBInkInput = { fbink_input_scan = function() end }
|
||||
end
|
||||
local dev_count = ffi.new("size_t[1]")
|
||||
-- We care about: the touchscreen, a properly scaled stylus, pagination buttons and a home button.
|
||||
local match_mask = bit.bor(C.INPUT_TOUCHSCREEN, C.INPUT_SCALED_TABLET, C.INPUT_PAGINATION_BUTTONS, C.INPUT_HOME_BUTTON)
|
||||
local devices = FBInkInput.fbink_input_scan(match_mask, 0, C.SCAN_ONLY, dev_count)
|
||||
if devices ~= nil then
|
||||
for i = 0, tonumber(dev_count[0]) - 1 do
|
||||
local dev = devices[i]
|
||||
if dev.matched then
|
||||
self.input.open(ffi.string(dev.path), ffi.string(dev.name))
|
||||
end
|
||||
end
|
||||
C.free(devices)
|
||||
else
|
||||
-- Auto-detection failed, warn and fall back to defaults
|
||||
logger.warn("We failed to auto-detect the proper input devices, input handling may be inconsistent!")
|
||||
if self.touch_dev then
|
||||
-- We've got a preferred path specified for the touch panel
|
||||
self.input.open(self.touch_dev)
|
||||
else
|
||||
-- That generally works out well enough on legacy devices...
|
||||
self.input.open("/dev/input/event0")
|
||||
self.input.open("/dev/input/event1")
|
||||
end
|
||||
end
|
||||
|
||||
-- Getting the device where rotation events end up without catching a bunch of false-positives is... trickier,
|
||||
-- thanks to the inane event code being used...
|
||||
if self:hasGSensor() then
|
||||
-- i.e., we want something that reports EV_ABS:ABS_PRESSURE that isn't *also* a pen (because those are pretty much guaranteed to report pressure...).
|
||||
-- And let's add that isn't also a touchscreen to the mix, because while not true at time of writing, that's an event touchscreens sure can support...
|
||||
devices = FBInkInput.fbink_input_scan(C.INPUT_ROTATION_EVENT, bit.bor(C.INPUT_TABLET, C.INPUT_TOUCHSCREEN), bit.bor(C.SCAN_ONLY, C.NO_RECAP), dev_count)
|
||||
if devices ~= nil then
|
||||
for i = 0, tonumber(dev_count[0]) - 1 do
|
||||
local dev = devices[i]
|
||||
if dev.matched then
|
||||
self.input.open(ffi.string(dev.path), ffi.string(dev.name))
|
||||
end
|
||||
end
|
||||
C.free(devices)
|
||||
end
|
||||
end
|
||||
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
function Kindle:init()
|
||||
-- Check if the device supports deep sleep/quick boot
|
||||
if lfs.attributes("/sys/devices/platform/falconblk/uevent", "mode") == "file" then
|
||||
@@ -234,6 +286,14 @@ function Kindle:init()
|
||||
self.canDeepSleep = false
|
||||
end
|
||||
|
||||
-- If the device-specific init hasn't done so already (devices without keys don't), instantiate Input.
|
||||
if not self.input then
|
||||
self.input = require("device/input"):new{ device = self }
|
||||
end
|
||||
|
||||
-- Auto-detect & open input devices
|
||||
self:openInputDevices()
|
||||
|
||||
Generic.init(self)
|
||||
end
|
||||
|
||||
@@ -683,9 +743,6 @@ function Kindle2:init()
|
||||
device = self,
|
||||
event_map = require("device/kindle/event_map_keyboard"),
|
||||
}
|
||||
self.input.open("/dev/input/event0")
|
||||
self.input.open("/dev/input/event1")
|
||||
self.input.open("fake_events")
|
||||
Kindle.init(self)
|
||||
end
|
||||
|
||||
@@ -700,9 +757,6 @@ function KindleDXG:init()
|
||||
event_map = require("device/kindle/event_map_keyboard"),
|
||||
}
|
||||
self.keyboard_layout = require("device/kindle/keyboard_layout")
|
||||
self.input.open("/dev/input/event0")
|
||||
self.input.open("/dev/input/event1")
|
||||
self.input.open("fake_events")
|
||||
Kindle.init(self)
|
||||
end
|
||||
|
||||
@@ -718,9 +772,6 @@ function Kindle3:init()
|
||||
event_map = require("device/kindle/event_map_kindle4"),
|
||||
}
|
||||
self.keyboard_layout = require("device/kindle/keyboard_layout")
|
||||
self.input.open("/dev/input/event0")
|
||||
self.input.open("/dev/input/event1")
|
||||
self.input.open("fake_events")
|
||||
Kindle.init(self)
|
||||
end
|
||||
|
||||
@@ -735,9 +786,6 @@ function Kindle4:init()
|
||||
device = self,
|
||||
event_map = require("device/kindle/event_map_kindle4"),
|
||||
}
|
||||
self.input.open("/dev/input/event0")
|
||||
self.input.open("/dev/input/event1")
|
||||
self.input.open("fake_events")
|
||||
Kindle.init(self)
|
||||
end
|
||||
|
||||
@@ -757,11 +805,6 @@ function KindleTouch:init()
|
||||
-- Kindle Touch needs event modification for proper coordinates
|
||||
self.input:registerEventAdjustHook(self.input.adjustTouchScale, {x=600/4095, y=800/4095})
|
||||
|
||||
-- event0 in KindleTouch is "WM8962 Beep Generator" (useless)
|
||||
-- event1 in KindleTouch is "imx-yoshi Headset" (useless)
|
||||
self.input.open("/dev/input/event2") -- Home button
|
||||
self.input.open(self.touch_dev) -- touchscreen
|
||||
self.input.open("fake_events")
|
||||
Kindle.init(self)
|
||||
end
|
||||
|
||||
@@ -775,9 +818,6 @@ function KindlePaperWhite:init()
|
||||
}
|
||||
|
||||
Kindle.init(self)
|
||||
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
function KindlePaperWhite2:init()
|
||||
@@ -791,9 +831,6 @@ function KindlePaperWhite2:init()
|
||||
}
|
||||
|
||||
Kindle.init(self)
|
||||
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
function KindleBasic:init()
|
||||
@@ -806,9 +843,6 @@ function KindleBasic:init()
|
||||
}
|
||||
|
||||
Kindle.init(self)
|
||||
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
function KindleVoyage:init()
|
||||
@@ -859,11 +893,7 @@ function KindleVoyage:init()
|
||||
|
||||
Kindle.init(self)
|
||||
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("/dev/input/event2") -- WhisperTouch
|
||||
self.input.open("fake_events")
|
||||
|
||||
-- reenable WhisperTouch keys when started without framework
|
||||
-- Re-enable WhisperTouch keys when started without framework
|
||||
if self.framework_lipc_handle then
|
||||
self.framework_lipc_handle:set_int_property("com.lab126.deviced", "fsrkeypadEnable", 1)
|
||||
self.framework_lipc_handle:set_int_property("com.lab126.deviced", "fsrkeypadPrevEnable", 1)
|
||||
@@ -882,9 +912,6 @@ function KindlePaperWhite3:init()
|
||||
}
|
||||
|
||||
Kindle.init(self)
|
||||
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
-- HAL for gyro orientation switches (EV_ABS:ABS_PRESSURE (?!) w/ custom values to EV_MSC:MSC_GYRO w/ our own custom values)
|
||||
@@ -992,21 +1019,6 @@ function KindleOasis:init()
|
||||
return this:handleGyroEv(ev)
|
||||
end
|
||||
end
|
||||
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("/dev/input/by-path/platform-gpiokey.0-event")
|
||||
|
||||
-- get rotate dev by EV=d
|
||||
local std_out = io.popen("grep -e 'Handlers\\|EV=' /proc/bus/input/devices | grep -B1 'EV=d' | grep -o 'event[0-9]'", "r")
|
||||
if std_out then
|
||||
local rotation_dev = std_out:read("*line")
|
||||
std_out:close()
|
||||
if rotation_dev then
|
||||
self.input.open("/dev/input/"..rotation_dev)
|
||||
end
|
||||
end
|
||||
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
-- HAL for gyro orientation switches (EV_ABS:ABS_PRESSURE (?!) w/ custom values to EV_MSC:MSC_GYRO w/ our own custom values)
|
||||
@@ -1118,21 +1130,6 @@ function KindleOasis2:init()
|
||||
return this:handleGyroEv(ev)
|
||||
end
|
||||
end
|
||||
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("/dev/input/by-path/platform-gpio-keys-event")
|
||||
|
||||
-- Get accelerometer device by looking for EV=d
|
||||
local std_out = io.popen("grep -e 'Handlers\\|EV=' /proc/bus/input/devices | grep -B1 'EV=d' | grep -o 'event[0-9]\\{1,2\\}'", "r")
|
||||
if std_out then
|
||||
local rotation_dev = std_out:read("*line")
|
||||
std_out:close()
|
||||
if rotation_dev then
|
||||
self.input.open("/dev/input/"..rotation_dev)
|
||||
end
|
||||
end
|
||||
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
function KindleOasis3:init()
|
||||
@@ -1201,21 +1198,6 @@ function KindleOasis3:init()
|
||||
return this:handleGyroEv(ev)
|
||||
end
|
||||
end
|
||||
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("/dev/input/by-path/platform-gpio-keys-event")
|
||||
|
||||
-- Get accelerometer device by looking for EV=d
|
||||
local std_out = io.popen("grep -e 'Handlers\\|EV=' /proc/bus/input/devices | grep -B1 'EV=d' | grep -o 'event[0-9]\\{1,2\\}'", "r")
|
||||
if std_out then
|
||||
local rotation_dev = std_out:read("*line")
|
||||
std_out:close()
|
||||
if rotation_dev then
|
||||
self.input.open("/dev/input/"..rotation_dev)
|
||||
end
|
||||
end
|
||||
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
function KindleBasic2:init()
|
||||
@@ -1229,9 +1211,6 @@ function KindleBasic2:init()
|
||||
}
|
||||
|
||||
Kindle.init(self)
|
||||
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
function KindlePaperWhite4:init()
|
||||
@@ -1246,19 +1225,6 @@ function KindlePaperWhite4:init()
|
||||
}
|
||||
|
||||
Kindle.init(self)
|
||||
|
||||
-- So, look for a goodix TS input device (c.f., #5110)...
|
||||
local std_out = io.popen("grep -e 'Handlers\\|Name=' /proc/bus/input/devices | grep -A1 'goodix-ts' | grep -o 'event[0-9]'", "r")
|
||||
if std_out then
|
||||
local goodix_dev = std_out:read("*line")
|
||||
std_out:close()
|
||||
if goodix_dev then
|
||||
self.touch_dev = "/dev/input/" .. goodix_dev
|
||||
end
|
||||
end
|
||||
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
function KindleBasic3:init()
|
||||
@@ -1278,29 +1244,6 @@ function KindleBasic3:init()
|
||||
-- so we have to rely on contact lift detection via BTN_TOUCH:0,
|
||||
-- c.f., https://github.com/koreader/koreader/issues/5070
|
||||
self.input.snow_protocol = true
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
local function findInputDevices()
|
||||
-- Walk /sys/class/input and pick up any evdev input device with *any* EV_ABS capabilities
|
||||
local devices = {}
|
||||
for evdev in lfs.dir("/sys/class/input/") do
|
||||
if evdev:match("event.*") then
|
||||
local abs_cap = "/sys/class/input/" .. evdev .. "/device/capabilities/abs"
|
||||
local f = io.open(abs_cap, "r")
|
||||
if f then
|
||||
local bitmap_str = f:read("l")
|
||||
f:close()
|
||||
if bitmap_str ~= "0" then
|
||||
logger.info("Potential input device found at", evdev, "because of ABS caps:", bitmap_str)
|
||||
table.insert(devices, "/dev/input/" .. evdev)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return devices
|
||||
end
|
||||
|
||||
function KindlePaperWhite5:init()
|
||||
@@ -1319,25 +1262,10 @@ function KindlePaperWhite5:init()
|
||||
self.screen:_MTK_ToggleFastMode(true)
|
||||
|
||||
Kindle.init(self)
|
||||
|
||||
-- Some HW/FW variants stash their input device without a by-path symlink...
|
||||
if util.pathExists("/dev/input/by-path/platform-1001e000.i2c-event") then
|
||||
self.touch_dev = "/dev/input/by-path/platform-1001e000.i2c-event"
|
||||
self.input.open(self.touch_dev)
|
||||
else
|
||||
local devices = findInputDevices()
|
||||
for _, touch in ipairs(devices) do
|
||||
-- There should only be one match on the PW5 anyway...
|
||||
self.touch_dev = touch
|
||||
self.input.open(touch)
|
||||
end
|
||||
end
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
function KindleBasic4:init()
|
||||
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
||||
-- TBD, assume PW5 for now
|
||||
self.powerd = require("device/kindle/powerd"):new{
|
||||
device = self,
|
||||
fl_intensity_file = "/sys/class/backlight/fp9966-bl1/brightness",
|
||||
@@ -1351,20 +1279,6 @@ function KindleBasic4:init()
|
||||
self.screen:_MTK_ToggleFastMode(true)
|
||||
|
||||
Kindle.init(self)
|
||||
|
||||
-- Some HW/FW variants stash their input device without a by-path symlink...
|
||||
if util.pathExists("/dev/input/by-path/platform-1001e000.i2c-event") then
|
||||
self.touch_dev = "/dev/input/by-path/platform-1001e000.i2c-event"
|
||||
self.input.open(self.touch_dev)
|
||||
else
|
||||
local devices = findInputDevices()
|
||||
for _, touch in ipairs(devices) do
|
||||
-- There should only be one match on the PW5 anyway...
|
||||
self.touch_dev = touch
|
||||
self.input.open(touch)
|
||||
end
|
||||
end
|
||||
self.input.open("fake_events")
|
||||
end
|
||||
|
||||
function KindleScribe:init()
|
||||
@@ -1384,11 +1298,11 @@ function KindleScribe:init()
|
||||
hall_file = "/sys/devices/platform/eink_hall/hall_enable",
|
||||
}
|
||||
|
||||
Kindle.init(self)
|
||||
|
||||
-- Enable the so-called "fast" mode, so as to prevent the driver from silently promoting refreshes to REAGL.
|
||||
self.screen:_MTK_ToggleFastMode(true)
|
||||
|
||||
Kindle.init(self)
|
||||
|
||||
--- @note The same quirks as on the Oasis 2 and 3 apply ;).
|
||||
local haslipc, lipc = pcall(require, "liblipclua")
|
||||
if haslipc and lipc then
|
||||
@@ -1424,23 +1338,9 @@ function KindleScribe:init()
|
||||
return this:handleGyroEv(ev)
|
||||
end
|
||||
end
|
||||
-- Get accelerometer device
|
||||
local std_out = io.popen("grep -A4 'acc' /proc/bus/input/devices | grep -o 'event[0-9]'", "r")
|
||||
if std_out then
|
||||
local gyro_dev = std_out:read("*line")
|
||||
std_out:close()
|
||||
logger.dbg("gyro_dev", gyro_dev)
|
||||
if gyro_dev then
|
||||
self.input.open("/dev/input/"..gyro_dev)
|
||||
end
|
||||
end
|
||||
|
||||
self.input.open(self.touch_dev)
|
||||
self.input.open("fake_events")
|
||||
|
||||
-- Setup pen input
|
||||
self.input.wacom_protocol = true
|
||||
self.input.open("/dev/input/event4")
|
||||
end
|
||||
|
||||
function KindleTouch:exit()
|
||||
|
||||
@@ -15,6 +15,7 @@ local C = ffi.C
|
||||
require("ffi/linux_fb_h")
|
||||
require("ffi/linux_input_h")
|
||||
require("ffi/posix_h")
|
||||
require("ffi/fbink_input_h")
|
||||
|
||||
local function yes() return true end
|
||||
local function no() return false end
|
||||
@@ -140,17 +141,16 @@ local Kobo = Generic:extend{
|
||||
battery_sysfs = "/sys/class/power_supply/mc13892_bat",
|
||||
-- Stable path to the NTX input device
|
||||
ntx_dev = "/dev/input/event0",
|
||||
ntx_fd = nil,
|
||||
-- Stable path to the Touch input device
|
||||
touch_dev = "/dev/input/event1",
|
||||
-- Stable path to the Power Button input device
|
||||
power_dev = nil,
|
||||
-- Event code to use to detect contact pressure
|
||||
pressure_event = nil,
|
||||
-- Device features multiple CPU cores
|
||||
isSMP = no,
|
||||
-- Device supports "eclipse" waveform modes (i.e., optimized for nightmode).
|
||||
hasEclipseWfm = no,
|
||||
-- Device ships with various hardware revisions under the same device code, requiring automatic hardware detection...
|
||||
-- Device ships with various hardware revisions under the same device code, requiring automatic hardware detection (PMIC & FL)...
|
||||
automagic_sysfs = false,
|
||||
-- The standard "standby" power state
|
||||
standby_state = "standby",
|
||||
@@ -417,8 +417,6 @@ local KoboEuropa = Kobo:extend{
|
||||
display_dpi = 227,
|
||||
boot_rota = C.FB_ROTATE_CCW,
|
||||
battery_sysfs = "/sys/class/power_supply/battery",
|
||||
ntx_dev = "/dev/input/by-path/platform-ntx_event0-event",
|
||||
touch_dev = "/dev/input/by-path/platform-0-0010-event",
|
||||
isSMP = yes,
|
||||
}
|
||||
|
||||
@@ -448,8 +446,6 @@ local KoboCadmus = Kobo:extend{
|
||||
battery_sysfs = "/sys/class/power_supply/battery",
|
||||
hasAuxBattery = yes,
|
||||
aux_battery_sysfs = "/sys/class/misc/cilix",
|
||||
ntx_dev = "/dev/input/by-path/platform-ntx_event0-event",
|
||||
touch_dev = "/dev/input/by-path/platform-0-0010-event",
|
||||
isSMP = yes,
|
||||
-- Much like the Libra 2, there are at least two different HW revisions, with different PMICs...
|
||||
automagic_sysfs = true,
|
||||
@@ -503,7 +499,6 @@ local KoboGoldfinch = Kobo:extend{
|
||||
nl_inverted = true,
|
||||
},
|
||||
battery_sysfs = "/sys/class/power_supply/battery",
|
||||
power_dev = "/dev/input/by-path/platform-bd71828-pwrkey-event",
|
||||
-- Board is eerily similar to the Libra 2, so, it inherits the same quirks...
|
||||
-- c.f., https://github.com/koreader/koreader/issues/9552#issuecomment-1293000313
|
||||
hasReliableMxcWaitFor = no,
|
||||
@@ -530,9 +525,6 @@ local KoboCondor = Kobo:extend{
|
||||
nl_inverted = true,
|
||||
},
|
||||
battery_sysfs = "/sys/class/power_supply/bd71827_bat",
|
||||
touch_dev = "/dev/input/by-path/platform-2-0010-event",
|
||||
ntx_dev = "/dev/input/by-path/platform-ntx_event0-event",
|
||||
power_dev = "/dev/input/by-path/platform-bd71828-pwrkey.6.auto-event",
|
||||
isSMP = yes,
|
||||
}
|
||||
|
||||
@@ -557,9 +549,9 @@ local KoboMonza = Kobo:extend{
|
||||
nl_max = 10,
|
||||
nl_inverted = true,
|
||||
},
|
||||
battery_sysfs = "/sys/class/power_supply/bd71827_bat",
|
||||
isSMP = yes,
|
||||
hasColorScreen = yes,
|
||||
automagic_sysfs = true,
|
||||
}
|
||||
|
||||
-- Kobo Clara B/W:
|
||||
@@ -579,7 +571,7 @@ local KoboSpaBW = Kobo:extend{
|
||||
nl_max = 10,
|
||||
nl_inverted = true,
|
||||
},
|
||||
automagic_sysfs = true,
|
||||
battery_sysfs = "/sys/class/power_supply/bd71827_bat",
|
||||
}
|
||||
|
||||
-- Kobo Clara Colour:
|
||||
@@ -599,9 +591,9 @@ local KoboSpaColour = Kobo:extend{
|
||||
nl_max = 10,
|
||||
nl_inverted = true,
|
||||
},
|
||||
battery_sysfs = "/sys/class/power_supply/bd71827_bat",
|
||||
isSMP = yes,
|
||||
hasColorScreen = yes,
|
||||
automagic_sysfs = true,
|
||||
}
|
||||
|
||||
function Kobo:setupChargingLED()
|
||||
@@ -623,7 +615,7 @@ function Kobo:getKeyRepeat()
|
||||
self.key_repeat = ffi.new("unsigned int[?]", C.REP_CNT)
|
||||
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)))
|
||||
logger.warn("Device:getKeyRepeat: EVIOCGREP ioctl on fd", self.ntx_fd, "failed:", ffi.string(C.strerror(err)))
|
||||
return false
|
||||
else
|
||||
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")
|
||||
@@ -637,14 +629,14 @@ function Kobo:disableKeyRepeat()
|
||||
local key_repeat = ffi.new("unsigned int[?]", C.REP_CNT)
|
||||
if C.ioctl(self.ntx_fd, C.EVIOCSREP, key_repeat) < 0 then
|
||||
local err = ffi.errno()
|
||||
logger.warn("Device:disableKeyRepeat: EVIOCSREP ioctl failed:", ffi.string(C.strerror(err)))
|
||||
logger.warn("Device:disableKeyRepeat: EVIOCSREP ioctl on fd", self.ntx_fd, "failed:", ffi.string(C.strerror(err)))
|
||||
end
|
||||
end
|
||||
|
||||
function Kobo:restoreKeyRepeat()
|
||||
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)))
|
||||
logger.warn("Device:restoreKeyRepeat: EVIOCSREP ioctl on fd", self.ntx_fd, "failed:", ffi.string(C.strerror(err)))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -661,7 +653,7 @@ function Kobo:toggleKeyRepeat(toggle)
|
||||
-- Check the current (kernel) state to know what to do
|
||||
if C.ioctl(self.ntx_fd, C.EVIOCGREP, key_repeat) < 0 then
|
||||
local err = ffi.errno()
|
||||
logger.warn("Device:toggleKeyRepeat: EVIOCGREP ioctl failed:", ffi.string(C.strerror(err)))
|
||||
logger.warn("Device:toggleKeyRepeat: EVIOCGREP ioctl on fd", self.ntx_fd, "failed:", ffi.string(C.strerror(err)))
|
||||
return false
|
||||
else
|
||||
if key_repeat[C.REP_DELAY] == 0 and key_repeat[C.REP_PERIOD] == 0 then
|
||||
@@ -674,7 +666,7 @@ function Kobo:toggleKeyRepeat(toggle)
|
||||
|
||||
if C.ioctl(self.ntx_fd, C.EVIOCSREP, key_repeat) < 0 then
|
||||
local err = ffi.errno()
|
||||
logger.warn("Device:toggleKeyRepeat: EVIOCSREP ioctl failed:", ffi.string(C.strerror(err)))
|
||||
logger.warn("Device:toggleKeyRepeat: EVIOCSREP ioctl on fd", self.ntx_fd, "failed:", ffi.string(C.strerror(err)))
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -758,46 +750,6 @@ function Kobo:init()
|
||||
self.frontlight_settings.frontlight_mixer = "/sys/class/backlight/tlc5947_bl/color"
|
||||
end
|
||||
end
|
||||
|
||||
-- Touch panel input
|
||||
if util.fileExists("/dev/input/by-path/platform-2-0010-event") then
|
||||
-- Elan (HWConfig TouchCtrl is ekth6) on i2c bus 2
|
||||
self.touch_dev = "/dev/input/by-path/platform-2-0010-event"
|
||||
elseif util.fileExists("/dev/input/by-path/platform-1-0010-event") then
|
||||
-- Elan (HWConfig TouchCtrl is ekth6) on i2c bus 1
|
||||
self.touch_dev = "/dev/input/by-path/platform-1-0010-event"
|
||||
elseif util.fileExists("/dev/input/by-path/platform-0-0010-event") then
|
||||
-- Elan (HWConfig TouchCtrl is ekth6) on i2c bus 0
|
||||
self.touch_dev = "/dev/input/by-path/platform-0-0010-event"
|
||||
else
|
||||
self.touch_dev = "/dev/input/event1"
|
||||
end
|
||||
|
||||
-- Physical buttons & synthetic NTX events
|
||||
if util.fileExists("/dev/input/by-path/platform-gpio-keys-event") then
|
||||
-- Libra 2 w/ a BD71828 PMIC
|
||||
self.ntx_dev = "/dev/input/by-path/platform-gpio-keys-event"
|
||||
elseif util.fileExists("/dev/input/by-path/platform-ntx_event0-event") then
|
||||
-- MTK, sunxi & Mk. 7
|
||||
self.ntx_dev = "/dev/input/by-path/platform-ntx_event0-event"
|
||||
elseif util.fileExists("/dev/input/by-path/platform-mxckpd-event") then
|
||||
-- circa Mk. 5 i.MX
|
||||
self.ntx_dev = "/dev/input/by-path/platform-mxckpd-event"
|
||||
else
|
||||
self.ntx_dev = "/dev/input/event0"
|
||||
end
|
||||
|
||||
-- Power button (this usually ends up in ntx_dev, except with some PMICs)
|
||||
if util.fileExists("/dev/input/by-path/platform-bd71828-pwrkey-event") then
|
||||
-- Libra 2 & Nia w/ a BD71828 PMIC
|
||||
self.power_dev = "/dev/input/by-path/platform-bd71828-pwrkey-event"
|
||||
elseif util.fileExists("/dev/input/by-path/platform-bd71828-pwrkey.4.auto-event") then
|
||||
-- Sage w/ a BD71828 PMIC
|
||||
self.power_dev = "/dev/input/by-path/platform-bd71828-pwrkey.4.auto-event"
|
||||
elseif util.fileExists("/dev/input/by-path/platform-bd71828-pwrkey.6.auto-event") then
|
||||
-- MTK w/ a BD71828 PMIC
|
||||
self.power_dev = "/dev/input/by-path/platform-bd71828-pwrkey.6.auto-event"
|
||||
end
|
||||
end
|
||||
|
||||
-- NOTE: i.MX5 devices have a wonky RTC that doesn't like alarms set further away that UINT16_MAX seconds from now...
|
||||
@@ -904,14 +856,41 @@ function Kobo:init()
|
||||
-- And then handle the extra shenanigans if necessary.
|
||||
self:initEventAdjustHooks()
|
||||
|
||||
-- Various HW Buttons, Switches & Synthetic NTX events
|
||||
self.ntx_fd = self.input.open(self.ntx_dev)
|
||||
-- Dedicated Power Button input device (if any)
|
||||
if self.power_dev then
|
||||
self.input.open(self.power_dev)
|
||||
-- Auto-detect input devices (via FBInk's fbink_input_scan)
|
||||
local ok, FBInkInput = pcall(ffi.load, "fbink_input")
|
||||
if not ok then
|
||||
-- NOP fallback for the testsuite...
|
||||
FBInkInput = { fbink_input_scan = NOP }
|
||||
end
|
||||
-- Touch panel
|
||||
self.input.open(self.touch_dev)
|
||||
local dev_count = ffi.new("size_t[1]")
|
||||
-- We care about: the touchscreen, the stylus, the power button, the sleep cover, and pagination buttons
|
||||
-- (and technically rotation events, but we'll get it with the device that provides the buttons on NTX).
|
||||
-- We exclude keyboards to play nice with the ExternalKeyboard plugin, which will handle potential keyboards on its own.
|
||||
local match_mask = bit.bor(C.INPUT_TOUCHSCREEN, C.INPUT_TABLET, C.INPUT_POWER_BUTTON, C.INPUT_SLEEP_COVER, C.INPUT_PAGINATION_BUTTONS)
|
||||
local devices = FBInkInput.fbink_input_scan(match_mask, C.INPUT_KEYBOARD, C.SCAN_ONLY, dev_count)
|
||||
if devices ~= nil then
|
||||
for i = 0, tonumber(dev_count[0]) - 1 do
|
||||
local dev = devices[i]
|
||||
if dev.matched then
|
||||
-- We need to single out whichever device provides pagination buttons or sleep cover events, as we'll want to tweak key repeat there...
|
||||
-- The first one will do, as it's extremely likely to be event0, and that's pretty fairly set in stone on NTX boards.
|
||||
if (bit.band(dev.type, C.INPUT_PAGINATION_BUTTONS) ~= 0 or bit.band(dev.type, C.INPUT_SLEEP_COVER) ~= 0) and not self.ntx_fd then
|
||||
self.ntx_fd = self.input.open(ffi.string(dev.path), ffi.string(dev.name))
|
||||
else
|
||||
self.input.open(ffi.string(dev.path), ffi.string(dev.name))
|
||||
end
|
||||
end
|
||||
end
|
||||
C.free(devices)
|
||||
else
|
||||
-- Auto-detection failed, warn and fall back to defaults
|
||||
logger.warn("We failed to auto-detect the proper input devices, input handling may be inconsistent!")
|
||||
-- Various HW Buttons, Switches & Synthetic NTX events
|
||||
self.ntx_fd = self.input.open(self.ntx_dev)
|
||||
-- Touch panel
|
||||
self.input.open(self.touch_dev)
|
||||
end
|
||||
|
||||
-- NOTE: On devices with a gyro, there may be a dedicated input device outputting the raw accelerometer data
|
||||
-- (3-Axis Orientation/Motion Detection).
|
||||
-- We skip it because we don't need it (synthetic rotation change events are sent to the main ntx input device),
|
||||
|
||||
@@ -56,9 +56,7 @@ local PocketBook = Generic:extend{
|
||||
-- Works same as input.event_map, but for raw input EV_KEY translation
|
||||
keymap = { [scan] = event },
|
||||
}]]
|
||||
-- Runtime state: whether raw input is actually used
|
||||
--- @fixme: Never actually set anywhere?
|
||||
is_using_raw_input = nil,
|
||||
-- We'll nil raw_input at runtime if it cannot be used.
|
||||
|
||||
-- InkView may have started translating button codes based on rotation on newer devices...
|
||||
-- That historically wasn't the case, hence this defaulting to false.
|
||||
@@ -242,7 +240,9 @@ function PocketBook:init()
|
||||
-- NOTE: This all happens in ffi/input_pocketbook.lua
|
||||
|
||||
self._model_init()
|
||||
if (not self.input.raw_input) or (not pcall(self.input.open, self.input, self.raw_input)) then
|
||||
-- NOTE: This is the odd one out actually calling input.open as a *method*,
|
||||
-- which the imp supports to get access to self.input.raw_input
|
||||
if (not self.input.raw_input) or (not pcall(self.input.open, self.input)) then
|
||||
inkview.OpenScreen()
|
||||
-- Raw mode open failed (no permissions?), so we'll run the usual way.
|
||||
-- Disable touch coordinate translation as inkview will do that.
|
||||
|
||||
@@ -62,11 +62,15 @@ function UIManager:init()
|
||||
UsbDevicePlugIn = function(input_event)
|
||||
-- Retrieve the argument set by Input:handleKeyBoardEv
|
||||
local evdev = table.remove(Input.fake_event_args[input_event])
|
||||
self:broadcastEvent(Event:new("EvdevInputInsert", evdev))
|
||||
local path = "/dev/input/event" .. tostring(evdev)
|
||||
|
||||
self:broadcastEvent(Event:new("EvdevInputInsert", path))
|
||||
end,
|
||||
UsbDevicePlugOut = function(input_event)
|
||||
local evdev = table.remove(Input.fake_event_args[input_event])
|
||||
self:broadcastEvent(Event:new("EvdevInputRemove", evdev))
|
||||
local path = "/dev/input/event" .. tostring(evdev)
|
||||
|
||||
self:broadcastEvent(Event:new("EvdevInputRemove", path))
|
||||
end,
|
||||
}
|
||||
self.poweroff_action = function()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
local Event = require("ui/event")
|
||||
local FindKeyboard = require("find-keyboard")
|
||||
local Device = require("device")
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local InputText = require("ui/widget/inputtext")
|
||||
@@ -14,6 +13,7 @@ local _ = require("gettext")
|
||||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
require("ffi/posix_h")
|
||||
require("ffi/fbink_input_h")
|
||||
|
||||
-- The include/linux/usb/role.h calls the USB roles "host" and "device".
|
||||
local USB_ROLE_DEVICE = "device"
|
||||
@@ -224,18 +224,17 @@ function ExternalKeyboard:onExit()
|
||||
end
|
||||
end
|
||||
|
||||
function ExternalKeyboard:_onEvdevInputInsert(evdev)
|
||||
self:setupKeyboard("/dev/input/event" .. tostring(evdev))
|
||||
function ExternalKeyboard:_onEvdevInputInsert(event_path)
|
||||
self:setupKeyboard(event_path)
|
||||
end
|
||||
|
||||
function ExternalKeyboard:onEvdevInputInsert(evdev)
|
||||
function ExternalKeyboard:onEvdevInputInsert(path)
|
||||
-- Leave time for the kernel to actually create the device
|
||||
UIManager:scheduleIn(0.5, self._onEvdevInputInsert, self, evdev)
|
||||
UIManager:scheduleIn(0.5, self._onEvdevInputInsert, self, path)
|
||||
end
|
||||
|
||||
function ExternalKeyboard:_onEvdevInputRemove(evdev)
|
||||
function ExternalKeyboard:_onEvdevInputRemove(event_path)
|
||||
-- Check that a keyboard we know about really was disconnected. Another input device could've been unplugged.
|
||||
local event_path = "/dev/input/event" .. tostring(evdev)
|
||||
if not ExternalKeyboard.keyboard_fds[event_path] then
|
||||
logger.dbg("ExternalKeyboard:onEvdevInputRemove:", event_path, "was not a keyboard we knew about")
|
||||
return
|
||||
@@ -248,6 +247,9 @@ function ExternalKeyboard:_onEvdevInputRemove(evdev)
|
||||
return
|
||||
end
|
||||
|
||||
-- Close our Input handle on it
|
||||
Device.input.close(event_path)
|
||||
|
||||
ExternalKeyboard.keyboard_fds[event_path] = nil
|
||||
ExternalKeyboard.connected_keyboards = ExternalKeyboard.connected_keyboards - 1
|
||||
logger.dbg("ExternalKeyboard: USB keyboard", event_path, "was disconnected; total:", ExternalKeyboard.connected_keyboards)
|
||||
@@ -277,34 +279,84 @@ function ExternalKeyboard:_onEvdevInputRemove(evdev)
|
||||
self:_broadcastDisconnected()
|
||||
end
|
||||
|
||||
function ExternalKeyboard:onEvdevInputRemove(path)
|
||||
UIManager:scheduleIn(0.5, self._onEvdevInputRemove, self, path)
|
||||
end
|
||||
|
||||
ExternalKeyboard._broadcastDisconnected = UIManager:debounce(0.5, false, function()
|
||||
InputText.initInputEvents()
|
||||
UIManager:broadcastEvent(Event:new("PhysicalKeyboardDisconnected"))
|
||||
end)
|
||||
|
||||
-- Implement FindKeyboard:find & check via FBInkInput
|
||||
local function findKeyboards()
|
||||
local keyboards = {}
|
||||
|
||||
local FBInkInput = ffi.load("fbink_input")
|
||||
local dev_count = ffi.new("size_t[1]")
|
||||
local devices = FBInkInput.fbink_input_scan(C.INPUT_KEYBOARD, 0, C.SCAN_ONLY, dev_count)
|
||||
if devices ~= nil then
|
||||
for i = 0, tonumber(dev_count[0]) - 1 do
|
||||
local dev = devices[i]
|
||||
if dev.matched then
|
||||
-- Check if it provides a DPad, too.
|
||||
local has_dpad = bit.band(dev.type, C.INPUT_DPAD) ~= 0
|
||||
table.insert(keyboards, { event_path = ffi.string(dev.path), has_dpad = has_dpad })
|
||||
end
|
||||
end
|
||||
C.free(devices)
|
||||
end
|
||||
|
||||
return keyboards
|
||||
end
|
||||
|
||||
local function checkKeyboard(path)
|
||||
local keyboard
|
||||
|
||||
local FBInkInput = ffi.load("fbink_input")
|
||||
local dev = FBInkInput.fbink_input_check(path, C.INPUT_KEYBOARD, 0, C.SCAN_ONLY)
|
||||
if dev ~= nil then
|
||||
if dev.matched then
|
||||
keyboard = {
|
||||
event_path = ffi.string(dev.path),
|
||||
has_dpad = bit.band(dev.type, C.INPUT_DPAD) ~= 0
|
||||
}
|
||||
end
|
||||
C.free(dev)
|
||||
end
|
||||
|
||||
return keyboard
|
||||
end
|
||||
|
||||
-- The keyboard events with the same key codes would override the original events.
|
||||
-- That may cause embedded buttons to lose their original function and produce letters,
|
||||
-- as we cannot tell which device a key press comes from.
|
||||
function ExternalKeyboard:findAndSetupKeyboards()
|
||||
local keyboards = FindKeyboard:find()
|
||||
local keyboards = findKeyboards()
|
||||
|
||||
-- A USB keyboard may be recognized as several devices under a hub. And several of them may
|
||||
-- have keyboard capabilities set. Yet, only one would emit the events. The solution is to open all of them.
|
||||
for __, keyboard_info in ipairs(keyboards) do
|
||||
self:setupKeyboard(keyboard_info.event_path)
|
||||
self:setupKeyboard(keyboard_info)
|
||||
end
|
||||
end
|
||||
|
||||
function ExternalKeyboard:onEvdevInputRemove(evdev)
|
||||
UIManager:scheduleIn(0.5, self._onEvdevInputRemove, self, evdev)
|
||||
end
|
||||
function ExternalKeyboard:setupKeyboard(data)
|
||||
local keyboard_info
|
||||
if type(data) == "table" then
|
||||
-- We came from findAndSetupKeyboards, no need to-re-check the device
|
||||
keyboard_info = data
|
||||
else
|
||||
-- We came from a USB hotplug event handler, check the specified path
|
||||
local event_path = data
|
||||
|
||||
function ExternalKeyboard:setupKeyboard(event_path)
|
||||
local keyboard_info = FindKeyboard:check(event_path:match(".+/(.+)")) -- FindKeyboard only wants eventN, not the full path
|
||||
if not keyboard_info then
|
||||
logger.dbg("ExternalKeyboard:setupKeyboard:", event_path, "doesn't look like a keyboard")
|
||||
return
|
||||
keyboard_info = checkKeyboard(event_path)
|
||||
if not keyboard_info then
|
||||
logger.dbg("ExternalKeyboard:setupKeyboard:", event_path, "doesn't look like a keyboard")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local has_dpad_func = Device.hasDPad
|
||||
|
||||
logger.dbg("ExternalKeyboard:setupKeyboard", keyboard_info.event_path, "has_dpad", keyboard_info.has_dpad)
|
||||
|
||||
Reference in New Issue
Block a user