[feat] Add support for BQ/Fnac devices (#4294)

Adds support for devices found in https://blog.bq.com/es/bq-ereaders-developers-program/. Tested on BQ Cervantes 4 (last BQ device from 2017).

It adds a new touch input event handler (discussed in #4275) which should work on other single touch devices (ie: Kobo Touch, Mini, Glo, Aura HD) but wasn't tested.

Includes base bump with: [feat] Add BQ/Fnac device support (https://github.com/koreader/koreader-base/pull/745)
pull/4299/head v2018.11
Martín Fernández 6 years ago committed by Frans de Jonge
parent d1298ff8e5
commit 1e69fae7bc

2
.gitignore vendored

@ -40,6 +40,8 @@ i18n
koreader-android-arm-linux-androideabi*
koreader-android-i686-linux-android*
koreader-cervantes-arm-linux-gnueabi*
koreader-cervantes-arm-cervantes-linux-gnueabi*
koreader-kindle-legacy-arm-kindle-linux-gnueabi*
koreader-kindle-arm-linux-gnueabi*
koreader-kobo-arm-linux-gnueabihf*

@ -35,16 +35,17 @@ INSTALL_DIR=koreader-$(DIST)-$(MACHINE)
# platform directories
PLATFORM_DIR=platform
COMMON_DIR=$(PLATFORM_DIR)/common
ANDROID_DIR=$(PLATFORM_DIR)/android
ANDROID_LAUNCHER_DIR:=$(ANDROID_DIR)/luajit-launcher
APPIMAGE_DIR=$(PLATFORM_DIR)/appimage
CERVANTES_DIR=$(PLATFORM_DIR)/cervantes
KINDLE_DIR=$(PLATFORM_DIR)/kindle
KOBO_DIR=$(PLATFORM_DIR)/kobo
POCKETBOOK_DIR=$(PLATFORM_DIR)/pocketbook
SONY_PRSTUX_DIR=$(PLATFORM_DIR)/sony-prstux
UBUNTUTOUCH_DIR=$(PLATFORM_DIR)/ubuntu-touch
APPIMAGE_DIR=$(PLATFORM_DIR)/appimage
ANDROID_DIR=$(PLATFORM_DIR)/android
ANDROID_LAUNCHER_DIR:=$(ANDROID_DIR)/luajit-launcher
UBUNTUTOUCH_SDL_DIR:=$(UBUNTUTOUCH_DIR)/ubuntu-touch-sdl
WIN32_DIR=$(PLATFORM_DIR)/win32
SONY_PRSTUX_DIR=$(PLATFORM_DIR)/sony-prstux
# appimage setup
APPIMAGETOOL=appimagetool-x86_64.AppImage
@ -390,8 +391,40 @@ sony-prstuxupdate: all
tar -I"gzip --rsyncable" -cah --no-recursion -f ../koreader-sony-prstux-$(MACHINE)-$(VERSION).targz \
-T koreader/ota/package.index
cervantesupdate: all
# ensure that the binaries were built for ARM
file $(INSTALL_DIR)/koreader/luajit | grep ARM || exit 1
# remove old package if any
rm -f koreader-cervantes-$(MACHINE)-$(VERSION).zip
# Cervantes launching scripts
cp $(CERVANTES_DIR)/*.sh $(INSTALL_DIR)/koreader
# create new package
cd $(INSTALL_DIR) && \
zip -9 -r \
../koreader-cervantes-$(MACHINE)-$(VERSION).zip \
koreader -x "koreader/resources/fonts/*" \
"koreader/resources/icons/src/*" "koreader/spec/*" \
$(ZIP_EXCLUDE)
# generate update package index file
zipinfo -1 koreader-cervantes-$(MACHINE)-$(VERSION).zip > \
$(INSTALL_DIR)/koreader/ota/package.index
echo "koreader/ota/package.index" >> $(INSTALL_DIR)/koreader/ota/package.index
# update index file in zip package
cd $(INSTALL_DIR) && zip -u ../koreader-cervantes-$(MACHINE)-$(VERSION).zip \
koreader/ota/package.index
# make gzip cervantes update for zsync OTA update
cd $(INSTALL_DIR) && \
tar -I"gzip --rsyncable" -cah --no-recursion -f ../koreader-cervantes-$(MACHINE)-$(VERSION).targz \
-T koreader/ota/package.index
update:
ifeq ($(TARGET), kindle)
ifeq ($(TARGET), android)
make androidupdate
else ifeq ($(TARGET), appimage)
make appimageupdate
else ifeq ($(TARGET), cervantes)
make cervantesupdate
else ifeq ($(TARGET), kindle)
make kindleupdate
else ifeq ($(TARGET), kindle-legacy)
make kindleupdate
@ -405,10 +438,6 @@ else ifeq ($(TARGET), sony-prstux)
make sony-prstuxupdate
else ifeq ($(TARGET), ubuntu-touch)
make utupdate
else ifeq ($(TARGET), appimage)
make appimageupdate
else ifeq ($(TARGET), android)
make androidupdate
endif
androiddev: androidupdate

@ -1 +1 @@
Subproject commit 42bc013b62c8039d733513e00a92dd6c70d8b421
Subproject commit f13f1ce2ee42a64788ad80adb12d34197406b984

@ -9,12 +9,14 @@ local util = require("ffi/util")
local filemanagerutil = {}
function filemanagerutil.getDefaultDir()
if Device:isKindle() then
if Device:isAndroid() then
return "/sdcard"
elseif Device:isCervantes() then
return "/mnt/public"
elseif Device:isKindle() then
return "/mnt/us/documents"
elseif Device:isKobo() then
return "/mnt/onboard"
elseif Device:isAndroid() then
return "/sdcard"
else
return "."
end

@ -44,7 +44,7 @@ local footerTextGeneratorMap = {
if not Device:hasFrontlight() then return "L: NA" end
local powerd = Device:getPowerDevice()
if powerd:isFrontlightOn() then
if Device:isKobo() then
if Device:isCervantes() or Device:isKobo() then
return ("L: %d%%"):format(powerd:frontlightIntensity())
else
return ("L: %d"):format(powerd:frontlightIntensity())

@ -31,6 +31,11 @@ local function probeDevice()
return require("device/sony-prstux/device")
end
local cervantes_test_stat = lfs.attributes("/usr/bin/ntxinfo")
if cervantes_test_stat then
return require("device/cervantes/device")
end
-- add new ports here:
--
-- if --[[ implement a proper test instead --]] false then

@ -0,0 +1,220 @@
local Generic = require("device/generic/device")
local TimeVal = require("ui/timeval")
local logger = require("logger")
local function yes() return true end
local function getProductId()
local ntxinfo_pcb = io.popen("/usr/bin/ntxinfo /dev/mmcblk0 | grep pcb | cut -d ':' -f2", "r")
if not ntxinfo_pcb then return 0 end
local product_id = tonumber(ntxinfo_pcb:read()) or 0
ntxinfo_pcb:close()
return product_id
end
local Cervantes = Generic:new{
model = "Cervantes",
isCervantes = yes,
isAlwaysPortrait = yes,
isTouchDevice = yes,
touch_legacy = true, -- SingleTouch input events
touch_switch_xy = true,
touch_mirrored_x = true,
touch_probe_ev_epoch_time = true,
hasOTAUpdates = yes,
hasKeys = yes,
internal_storage_mount_point = "/mnt/public/",
}
-- Cervantes Touch
local CervantesTouch = Cervantes:new{
model = "Cervantes Touch",
display_dpi = 167,
}
-- Cervantes TouchLight / Fnac Touch Plus
local CervantesTouchLight = Cervantes:new{
model = "Cervantes TouchLight",
display_dpi = 167,
hasFrontlight = yes,
}
-- Cervantes 2013 / Fnac Touch Light
local Cervantes2013 = Cervantes:new{
model = "Cervantes 2013",
display_dpi = 212,
hasFrontlight = yes,
}
-- Cervantes 3 / Fnac Touch Light 2
local Cervantes3 = Cervantes:new{
model = "Cervantes 3",
display_dpi = 300,
hasFrontlight = yes,
}
-- Cervantes 4
local Cervantes4 = Cervantes:new{
model = "Cervantes 4",
display_dpi = 300,
hasFrontlight = yes,
hasNaturalLight = yes,
frontlight_settings = {
frontlight_white = "/sys/class/backlight/lm3630a_ledb",
frontlight_red = "/sys/class/backlight/lm3630a_leda",
},
}
-- input events
local probeEvEpochTime
-- this function will update itself after the first touch event
probeEvEpochTime = function(self, ev)
local now = TimeVal:now()
-- This check should work as long as main UI loop is not blocked for more
-- than 10 minute before handling the first touch event.
if ev.time.sec <= now.sec - 600 then
-- time is seconds since boot, force it to epoch
probeEvEpochTime = function(_, _ev)
_ev.time = TimeVal:now()
end
ev.time = now
else
-- time is already epoch time, no need to do anything
probeEvEpochTime = function(_, _) end
end
end
function Cervantes:initEventAdjustHooks()
if self.touch_switch_xy then
self.input:registerEventAdjustHook(self.input.adjustTouchSwitchXY)
end
if self.touch_mirrored_x then
self.input:registerEventAdjustHook(
self.input.adjustTouchMirrorX,
self.screen:getWidth()
)
end
if self.touch_probe_ev_epoch_time then
self.input:registerEventAdjustHook(function(_, ev)
probeEvEpochTime(_, ev)
end)
end
if self.touch_legacy then
self.input.handleTouchEv = self.input.handleTouchEvLegacy
end
end
function Cervantes:init()
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
self.powerd = require("device/cervantes/powerd"):new{device = self}
self.input = require("device/input"):new{
device = self,
event_map = {
[61] = "Home",
[116] = "Power",
}
}
self.input.open("/dev/input/event0") -- Keys
self.input.open("/dev/input/event1") -- touchscreen
self.input.open("fake_events") -- usb events
self:initEventAdjustHooks()
Generic.init(self)
end
function Cervantes:setDateTime(year, month, day, hour, min, sec)
if hour == nil or min == nil then return true end
local command
if year and month and day then
command = string.format("date -s '%d-%d-%d %d:%d:%d'", year, month, day, hour, min, sec)
else
command = string.format("date -s '%d:%d'",hour, min)
end
if os.execute(command) == 0 then
os.execute('hwclock -u -w')
return true
else
return false
end
end
function Cervantes:saveSettings()
self.powerd:saveSettings()
end
-- wireless
function Cervantes:initNetworkManager(NetworkMgr)
function NetworkMgr:turnOffWifi(complete_callback)
logger.info("Cervantes: disabling WiFi")
os.execute("./disable-wifi.sh")
if complete_callback then
complete_callback()
end
end
function NetworkMgr:turnOnWifi(complete_callback)
logger.info("Cervantes: enabling WiFi")
os.execute("./enable-wifi.sh")
self:showNetworkMenu(complete_callback)
end
NetworkMgr:setWirelessBackend("wpa_supplicant", {ctrl_interface = "/var/run/wpa_supplicant/eth0"})
function NetworkMgr:obtainIP()
os.execute("./obtain-ip.sh")
end
function NetworkMgr:releaseIP()
os.execute("./release-ip.sh")
end
function NetworkMgr:restoreWifiAsync()
os.execute("./restore-wifi-async.sh")
end
function NetworkMgr:isWifiOn()
return 0 == os.execute("lsmod | grep -q 8189fs")
end
end
-- screensaver
function Cervantes:supportsScreensaver()
return true
end
function Cervantes:intoScreenSaver()
local Screensaver = require("ui/screensaver")
if self.screen_saver_mode == false then
Screensaver:show()
end
self.powerd:beforeSuspend()
self.screen_saver_mode = true
end
function Cervantes:outofScreenSaver()
if self.screen_saver_mode == true then
local Screensaver = require("ui/screensaver")
Screensaver:close()
local UIManager = require("ui/uimanager")
UIManager:nextTick(function() UIManager:setDirty("all", "full") end)
end
self.powerd:afterResume()
self.screen_saver_mode = false
end
-- power functions: suspend, resume, reboot, poweroff
function Cervantes:suspend()
os.execute("./suspend.sh")
end
function Cervantes:resume()
os.execute("./resume.sh")
end
function Cervantes:reboot()
os.execute("reboot")
end
function Cervantes:powerOff()
os.execute("halt")
end
-------------- device probe ------------
local product_id = getProductId()
if product_id == 22 then
return CervantesTouch
elseif product_id == 23 then
return CervantesTouchLight
elseif product_id == 33 then
return Cervantes2013
elseif product_id == 51 then
return Cervantes3
elseif product_id == 68 then
return Cervantes4
else
error("unrecognized Cervantes: board id " .. product_id)
end

@ -0,0 +1,210 @@
local BasePowerD = require("device/generic/powerd")
local SysfsLight = require ("device/sysfs_light")
local PluginShare = require("pluginshare")
local battery_sysfs = "/sys/devices/platform/pmic_battery.1/power_supply/mc13892_bat/"
local CervantesPowerD = BasePowerD:new{
fl = nil,
fl_warmth = nil,
auto_warmth = false,
max_warmth_hour = 23,
fl_min = 0,
fl_max = 100,
capacity_file = battery_sysfs .. 'capacity',
status_file = battery_sysfs .. 'status'
}
function CervantesPowerD:_syncLightOnStart()
-- We can't read value from the OS or hardware.
-- Use last values stored in koreader settings.
local new_intensity = G_reader_settings:readSetting("frontlight_intensity") or nil
local is_frontlight_on = G_reader_settings:readSetting("is_frontlight_on") or nil
local new_warmth, auto_warmth = nil
if self.fl_warmth ~= nil then
new_warmth = G_reader_settings:readSetting("frontlight_warmth") or nil
auto_warmth = G_reader_settings:readSetting("frontlight_auto_warmth") or nil
end
if new_intensity ~= nil then
self.hw_intensity = new_intensity
end
if is_frontlight_on ~= nil then
self.initial_is_fl_on = is_frontlight_on
end
local max_warmth_hour =
G_reader_settings:readSetting("frontlight_max_warmth_hour")
if max_warmth_hour then
self.max_warmth_hour = max_warmth_hour
end
if auto_warmth then
self.auto_warmth = true
self:calculateAutoWarmth()
elseif new_warmth ~= nil then
self.fl_warmth = new_warmth
end
if self.initial_is_fl_on == false and self.hw_intensity == 0 then
self.hw_intensity = 1
end
end
function CervantesPowerD:init()
-- Default values in case self:_syncLightOnStart() does not find
-- any previously saved setting (and for unit tests where it will
-- not be called)
self.hw_intensity = 20
self.initial_is_fl_on = true
self.autowarmth_job_running = false
if self.device.hasFrontlight() then
if self.device.hasNaturalLight() then
local nl_config = G_reader_settings:readSetting("natural_light_config")
if nl_config then
for key,val in pairs(nl_config) do
self.device.frontlight_settings[key] = val
end
end
self.fl = SysfsLight:new(self.device.frontlight_settings)
self.fl_warmth = 0
self:_syncLightOnStart()
else
local kobolight = require("ffi/kobolight")
local ok, light = pcall(kobolight.open)
if ok then
self.fl = light
self:_syncLightOnStart()
end
end
end
end
function CervantesPowerD:saveSettings()
if self.device.hasFrontlight() then
-- Store BasePowerD values into settings (and not our hw_intensity, so
-- that if frontlight was toggled off, we save and restore the previous
-- untoggled intensity and toggle state at next startup)
local cur_intensity = self.fl_intensity
local cur_is_fl_on = self.is_fl_on
local cur_warmth = self.fl_warmth
local cur_auto_warmth = self.auto_warmth
local cur_max_warmth_hour = self.max_warmth_hour
-- Save intensity to koreader settings
G_reader_settings:saveSetting("frontlight_intensity", cur_intensity)
G_reader_settings:saveSetting("is_frontlight_on", cur_is_fl_on)
if cur_warmth ~= nil then
G_reader_settings:saveSetting("frontlight_warmth", cur_warmth)
G_reader_settings:saveSetting("frontlight_auto_warmth", cur_auto_warmth)
G_reader_settings:saveSetting("frontlight_max_warmth_hour", cur_max_warmth_hour)
end
end
end
function CervantesPowerD:frontlightIntensityHW()
return self.hw_intensity
end
function CervantesPowerD:isFrontlightOnHW()
if self.initial_is_fl_on ~= nil then -- happens only once after init()
-- give initial state to BasePowerD, which will
-- reset our self.hw_intensity to 0 if self.initial_is_fl_on is false
local ret = self.initial_is_fl_on
self.initial_is_fl_on = nil
return ret
end
return self.hw_intensity > 0
end
function CervantesPowerD:turnOffFrontlightHW()
self:_setIntensity(0) -- will call setIntensityHW(0)
end
function CervantesPowerD:setIntensityHW(intensity)
if self.fl == nil then return end
if self.fl_warmth == nil then
self.fl:setBrightness(intensity)
else
self.fl:setNaturalBrightness(intensity, self.fl_warmth)
end
self.hw_intensity = intensity
-- Now that we have set intensity, we need to let BasePowerD
-- know about possibly changed frontlight state (if we came
-- from light toggled off to some intensity > 0).
self:_decideFrontlightState()
end
function CervantesPowerD:setWarmth(warmth)
if self.fl == nil then return end
if not warmth and self.auto_warmth then
self:calculateAutoWarmth()
end
self.fl_warmth = warmth or self.fl_warmth
self.fl:setWarmth(self.fl_warmth)
end
function CervantesPowerD:calculateAutoWarmth()
local current_time = os.date("%H") + os.date("%M")/60
local max_hour = self.max_warmth_hour
local diff_time = max_hour - current_time
if diff_time < 0 then
diff_time = diff_time + 24
end
if diff_time < 12 then
-- We are before bedtime. Use a slower progression over 5h.
self.fl_warmth = math.max(20 * (5 - diff_time), 0)
elseif diff_time > 22 then
-- Keep warmth at maximum for two hours after bedtime.
self.fl_warmth = 100
else
-- Between 2-4h after bedtime, return to zero.
self.fl_warmth = math.max(100 - 50 * (22 - diff_time), 0)
end
self.fl_warmth = math.floor(self.fl_warmth + 0.5)
-- Enable background job for setting Warmth, if not already done.
if not self.autowarmth_job_running then
table.insert(PluginShare.backgroundJobs, {
when = 180,
repeated = true,
executable = function()
if self.auto_warmth then
self:setWarmth()
end
end,
})
self.autowarmth_job_running = true
end
end
function CervantesPowerD:getCapacityHW()
return self:read_int_file(self.capacity_file)
end
function CervantesPowerD:isChargingHW()
return self:read_str_file(self.status_file) == "Charging\n"
end
function CervantesPowerD:beforeSuspend()
if self.fl == nil then return end
-- just turn off frontlight without remembering its state
self.fl:setBrightness(0)
end
function CervantesPowerD:afterResume()
if self.fl == nil then return end
-- just re-set it to self.hw_intensity that we haven't change on Suspend
if self.fl_warmth == nil then
self.fl:setBrightness(self.hw_intensity)
else
if self.auto_warmth then
self:calculateAutoWarmth()
end
self.fl:setNaturalBrightness(self.hw_intensity, self.fl_warmth)
end
end
return CervantesPowerD

@ -32,11 +32,12 @@ local Device = {
-- and have device dependent implementations in the corresponting
-- device/<devicetype>/device.lua file
-- (these are functions!)
isAndroid = no,
isCervantes = no,
isKindle = no,
isKobo = no,
isPocketBook = no,
isSonyPRSTUX = no,
isAndroid = no,
isSDL = no,
-- some devices have part of their screen covered by the bezel

@ -147,6 +147,8 @@ function Input:init()
-- set up fake event map
self.event_map[10000] = "IntoSS" -- go into screen saver
self.event_map[10001] = "OutOfSS" -- go out of screen saver
self.event_map[10010] = "UsbPlugIn"
self.event_map[10011] = "UsbPlugOut"
self.event_map[10020] = "Charging"
self.event_map[10021] = "NotCharging"
@ -284,7 +286,9 @@ function Input:handleKeyBoardEv(ev)
keycode = self.rotation_map[self.device.screen:getRotationMode()][keycode]
end
-- fake events
if keycode == "IntoSS" or keycode == "OutOfSS"
or keycode == "UsbPlugIn" or keycode == "UsbPlugOut"
or keycode == "Charging" or keycode == "NotCharging" then
return keycode
end
@ -531,6 +535,42 @@ function Input:handleTouchEvPhoenix(ev)
end
end
end
function Input:handleTouchEvLegacy(ev)
-- Single Touch Protocol. Some devices emit both singletouch and multitouch events.
-- In those devices the 'handleTouchEv' function doesn't work as expected. Use this function instead.
if ev.type == EV_ABS then
if #self.MTSlots == 0 then
table.insert(self.MTSlots, self:getMtSlot(self.cur_slot))
end
if ev.code == ABS_X then
self:setCurrentMtSlot("x", ev.value)
elseif ev.code == ABS_Y then
self:setCurrentMtSlot("y", ev.value)
elseif ev.code == ABS_PRESSURE then
if ev.value ~= 0 then
self:setCurrentMtSlot("id", 1)
else
self:setCurrentMtSlot("id", -1)
end
end
elseif ev.type == EV_SYN then
if ev.code == SYN_REPORT then
for _, MTSlot in pairs(self.MTSlots) do
self:setMtSlot(MTSlot.slot, "timev", TimeVal:new(ev.time))
end
-- feed ev in all slots to state machine
local touch_ges = self.gesture_detector:feedEvent(self.MTSlots)
self.MTSlots = {}
if touch_ges then
self:gestureAdjustHook(touch_ges)
return Event:new("Gesture",
self.gesture_detector:adjustGesCoordinate(touch_ges)
)
end
end
end
end
function Input:handleOasisOrientationEv(ev)
local rotation_mode, screen_mode

@ -61,7 +61,7 @@ common_info.report_bug = {
end
}
if Device:isKindle() or Device:isKobo() then
if Device:isCervantes() or Device:isKindle() or Device:isKobo() then
common_info.sleep = {
text = _("Sleep"),
callback = function()
@ -69,7 +69,7 @@ if Device:isKindle() or Device:isKobo() then
end,
}
end
if Device:isKobo() or Device:isSonyPRSTUX() then
if Device:isCervantes() or Device:isKobo() or Device:isSonyPRSTUX() then
common_info.reboot = {
text = _("Reboot the device"),
keep_menu_open = true,

@ -121,7 +121,7 @@ end
function NetworkMgr:getWifiMenuTable()
return {
text = _("Wi-Fi connection"),
enabled_func = function() return Device:isAndroid() or Device:isKindle() or Device:isKobo() or Device:isSonyPRSTUX() end,
enabled_func = function() return Device:isAndroid() or Device:isCervantes() or Device:isKindle() or Device:isKobo() or Device:isSonyPRSTUX() end,
checked_func = function() return NetworkMgr:isWifiOn() end,
callback = function(touchmenu_instance)
local wifi_status = NetworkMgr:isWifiOn()
@ -187,7 +187,7 @@ function NetworkMgr:getRestoreMenuTable()
return {
text = _("Automatically restore Wi-Fi connection after resume"),
checked_func = function() return G_reader_settings:nilOrTrue("auto_restore_wifi") end,
enabled_func = function() return Device:isKobo() end,
enabled_func = function() return Device:isCervantes() or Device:isKobo() end,
callback = function() G_reader_settings:flipNilOrTrue("auto_restore_wifi") end,
}
end

@ -45,7 +45,11 @@ local ota_channels = {
}
function OTAManager:getOTAModel()
if Device:isKindle() then
if Device:isAndroid() then
return "android"
elseif Device:isCervantes() then
return "cervantes"
elseif Device:isKindle() then
if Device:isTouchDevice() then
return "kindle"
else
@ -55,8 +59,6 @@ function OTAManager:getOTAModel()
return "kobo"
elseif Device:isPocketBook() then
return "pocketbook"
elseif Device:isAndroid() then
return "android"
elseif Device:isSonyPRSTUX() then
return "sony-prstux"
else

@ -190,6 +190,61 @@ function UIManager:init()
self:sendEvent(input_event)
end
end
elseif Device:isCervantes() then
self.event_handlers["Suspend"] = function()
self:_beforeSuspend()
Device:onPowerEvent("Suspend")
end
self.event_handlers["Resume"] = function()
Device:onPowerEvent("Resume")
self:_afterResume()
end
self.event_handlers["PowerPress"] = function()
UIManager:scheduleIn(2, self.poweroff_action)
end
self.event_handlers["PowerRelease"] = function()
if not self._entered_poweroff_stage then
UIManager:unschedule(self.poweroff_action)
self:suspend()
end
end
self.event_handlers["Charging"] = function()
logger.info("USB POWER IN")
--[[self:_beforeCharging()
if Device.screen_saver_mode then
self:suspend()
end]]--
end
self.event_handlers["NotCharging"] = function()
logger.info("USB POWER OUT")
--[[self:_afterNotCharging()
if Device.screen_saver_mode then
self:suspend()
end]]--
end
self.event_handlers["UsbPlugIn"] = function()
logger.info("USB HOST IN")
--[[self:_beforeCharging()
if Device.screen_saver_mode then
self:suspend()
end]]--
end
self.event_handlers["USbPlugOut"] = function()
logger.info("USB HOST OUT")
--[[self:_afterNotCharging()
if Device.screen_saver_mode then
self:suspend()
end]]--
end
self.event_handlers["__default__"] = function(input_event)
-- Suspension in Cervantes can be interrupted by screen updates. We ignore user touch input
-- in screen_saver_mode so screen updates won't be triggered in suspend mode.
-- We should not call self:suspend() in screen_saver_mode lest we stay on forever
-- trying to reschedule suspend. Other systems take care of unintended wake-up.
if not Device.screen_saver_mode then
self:sendEvent(input_event)
end
end
elseif Device:isSDL() then
self.event_handlers["Suspend"] = function()
self:_beforeSuspend()
@ -966,7 +1021,7 @@ end
-- Executes all the operations of a suspending request. This function usually puts the device into
-- suspension.
function UIManager:suspend()
if Device:isKobo() or Device:isSDL() or Device:isSonyPRSTUX() then
if Device:isCervantes() or Device:isKobo() or Device:isSDL() or Device:isSonyPRSTUX() then
self.event_handlers["Suspend"]()
elseif Device:isKindle() then
self.event_handlers["IntoSS"]()
@ -975,7 +1030,7 @@ end
-- Executes all the operations of a resume request. This function usually wakes up the device.
function UIManager:resume()
if Device:isKobo() or Device:isSDL() or Device:isSonyPRSTUX() then
if Device:isCervantes() or Device:isKobo() or Device:isSDL() or Device:isSonyPRSTUX() then
self.event_handlers["Resume"]()
elseif Device:isKindle() then
self.event_handlers["OutOfSS"]()

@ -52,7 +52,7 @@ function FrontLightWidget:init()
self.steps = self.steps + 1
end
self.steps = math.min(self.steps , steps_fl)
self.natural_light = Device:isKobo() and Device:hasNaturalLight()
self.natural_light = (Device:isCervantes() or Device:isKobo()) and Device:hasNaturalLight()
-- button width to fit screen size
local button_margin = Size.margin.tiny

12
kodev

@ -116,6 +116,7 @@ SUPPORTED_TARGETS="
kindlepw2 With compiler optimizations for Kindle models >= Paperwhite 2
kindle-legacy Needed only for Kindle2/3/DXG
kobo
cervantes
sony-prstux
android
pocketbook
@ -168,6 +169,10 @@ ${SUPPORTED_TARGETS}"
check_submodules
case "${1}" in
cervantes)
make TARGET=cervantes
assert_ret_zero $?
;;
kindle)
make TARGET=kindle
assert_ret_zero $?
@ -272,6 +277,9 @@ ${SUPPORTED_TARGETS}"
echo "${CLEAN_HELP_MSG}"
exit 0
;;
cervantes)
make TARGET=cervantes clean
;;
kindle)
make TARGET=kindle clean
;;
@ -389,6 +397,10 @@ ${SUPPORTED_RELEASE_TARGETS}"
kodev-build sony-prstux
make TARGET=sony-prstux update
;;
cervantes)
kodev-build cervantes
make TARGET=cervantes update
;;
kindle-legacy)
kodev-build kindle-legacy
make TARGET=kindle-legacy update

@ -0,0 +1,26 @@
#!/bin/sh
lsmod | grep -q g_file_storage || exit 1
modprobe -r g_file_storage
sleep 1
PCB_ID=$(/usr/bin/ntxinfo /dev/mmcblk0 | grep pcb | cut -d ":" -f2)
DISK=/dev/mmcblk
if [ "$PCB_ID" -eq 22 ] || [ "$PCB_ID" -eq 23 ]; then
PARTITION="${DISK}0p7"
else
PARTITION="${DISK}0p4"
fi
MOUNT_ARGS="noatime,nodiratime,shortname=mixed,utf8"
dosfsck -a -w "$PARTITION" >dosfsck.log 2>&1
mount -o "$MOUNT_ARGS" -t vfat "$PARTITION" /mnt/onboard
PARTITION=${DISK}1p1
[ -e "$PARTITION" ] && mount -o "$MOUNT_ARGS" -t vfat "$PARTITION" /mnt/sd

@ -0,0 +1,9 @@
#!/bin/sh
# disable wifi and remove all modules
killall udhcpc default-script wpa_supplicant 2>/dev/null
ifconfig eth0 down
if lsmod | grep -q 8189fs; then
modprobe -r 8189fs
fi

@ -0,0 +1,33 @@
#!/bin/sh
# based on https://github.com/baskerville/plato/blob/master/scripts/usb-enable.sh
lsmod | grep -q g_file_storage && exit 1
PCB_ID=$(/usr/bin/ntxinfo /dev/mmcblk0 | grep pcb | cut -d ":" -f2)
DISK=/dev/mmcblk
if [ "$PCB_ID" -eq 22 ] || [ "$PCB_ID" -eq 23 ]; then
PRODUCT_ID=${PRODUCT_ID:-"0xAD78"}
PARTITIONS="${DISK}0p7"
else
PRODUCT_ID=${PRODUCT_ID:-"0xAD79"}
PARTITIONS="${DISK}0p4"
fi
[ -e "${DISK}1p1" ] && PARTITIONS="${PARTITIONS},${DISK}1p1"
sync
echo 3 >/proc/sys/vm/drop_caches
for name in public sd; do
DIR=/mnt/"$name"
if grep -q "$DIR" /proc/mounts; then
umount "$DIR" || umount -l "$DIR"
fi
done
MODULE_PARAMETERS="vendor=0x2A47 product=${PRODUCT_ID} vendor_id=BQ product_id=Cervantes"
modprobe g_file_storage file="$PARTITIONS" stall=1 removable=1 "$MODULE_PARAMETERS"
sleep 1

@ -0,0 +1,18 @@
#!/bin/sh
WPA_SUPPLICANT_CONF="/mnt/private/koreader/wpa_supplicant.conf"
CTRL_INTERFACE="/var/run/wpa_supplicant"
# create a new configuration if neccesary.
if [ ! -f "$WPA_SUPPLICANT_CONF" ]; then
echo "ctrl_interface=DIR=${CTRL_INTERFACE}" >"$WPA_SUPPLICANT_CONF"
echo "update_config=1" >>"$WPA_SUPPLICANT_CONF"
sync
fi
if ! lsmod | grep -q 8189fs; then
modprobe 8189fs
sleep 1
fi
ifconfig eth0 up && sleep 1
wpa_supplicant -B -D wext -i eth0 -s -O "$CTRL_INTERFACE" -c "$WPA_SUPPLICANT_CONF" 2>/dev/null

@ -0,0 +1,57 @@
#!/bin/sh
# Standalone KOReader application for BQ Cervantes
# this file is intended to replace /etc/rc.local on BQ developers firmware
# turn off the green flashing led.
echo "ch 4" >/sys/devices/platform/pmic_light.1/lit
echo "cur 0" >/sys/devices/platform/pmic_light.1/lit
echo "dc 0" >/sys/devices/platform/pmic_light.1/lit
# ensure we have a proper time.
if [ "$(date '+%Y')" -lt 2010 ]; then
echo "Fixing date before 2010"
date +%Y%m%d -s "20100101"
hwclock -w
fi
# assign public & private partition devices based on pcb.
PCB_ID=$(/usr/bin/ntxinfo /dev/mmcblk0 | grep pcb | cut -d ":" -f2)
if [ "$PCB_ID" -eq 22 ] || [ "$PCB_ID" -eq 23 ]; then
PRIVATE="/dev/mmcblk0p5"
PUBLIC="/dev/mmcblk0p7"
else
PRIVATE="/dev/mmcblk0p7"
PUBLIC="/dev/mmcblk0p4"
fi
# mount internal partitions
mount $PRIVATE /mnt/private
mount $PUBLIC /mnt/public
# mount sdcard if present
if [ -b /dev/mmcblk1p1 ]; then
mount /dev/mmcblk1p1 /mnt/sd
fi
# remove wireless module since it wastes battery.
if lsmod | grep -q 8189fs; then
modprobe -r 8189fs
fi
# start usbnet using BQ scripts (192.168.4.1/24 w/ hardcoded MAC addr)
/usr/bin/usbup.sh
/usr/sbin/inetd
# check if KOReader script exists.
if [ -x /mnt/private/koreader/koreader.sh ]; then
# yada! KOReader is installed and ready to run.
while true; do
/mnt/private/koreader/koreader.sh
sleep 1
done
else
# nothing to do, leaving rc.local.
exit 1
fi
exit 0

@ -0,0 +1,89 @@
#!/bin/sh
export LC_ALL="en_US.UTF-8"
# working directory of koreader
KOREADER_DIR="${0%/*}"
# we're always starting from our working directory
cd "${KOREADER_DIR}" || exit
# update to new version from OTA directory
ko_update_check() {
NEWUPDATE="${KOREADER_DIR}/ota/koreader.updated.tar"
INSTALLED="${KOREADER_DIR}/ota/koreader.installed.tar"
if [ -f "${NEWUPDATE}" ]; then
#./fbink -q -y -7 -pmh "Updating KOReader"
# NOTE: See frontend/ui/otamanager.lua for a few more details on how we squeeze a percentage out of tar's checkpoint feature
# NOTE: %B should always be 512 in our case, so let stat do part of the maths for us instead of using %s ;).
FILESIZE="$(stat -c %b "${NEWUPDATE}")"
BLOCKS="$((FILESIZE / 20))"
export CPOINTS="$((BLOCKS / 100))"
# shellcheck disable=SC2016
./tar xf "${NEWUPDATE}" --strip-components=1 --no-same-permissions --no-same-owner --checkpoint="${CPOINTS}" --checkpoint-action=exec='./fbink -q -y -6 -P $(($TAR_CHECKPOINT/$CPOINTS))'
fail=$?
# Cleanup behind us...
if [ "${fail}" -eq 0 ]; then
mv "${NEWUPDATE}" "${INSTALLED}"
# ./fbink -q -y -6 -pm "Update successful :)"
# ./fbink -q -y -5 -pm "KOReader will start momentarily . . ."
#else
# # Huh ho...
# ./fbink -q -y -6 -pmh "Update failed :("
# ./fbink -q -y -5 -pm "KOReader may fail to function properly!"
fi
rm -f "${NEWUPDATE}" # always purge newupdate in all cases to prevent update loop
unset BLOCKS CPOINTS
fi
}
# if no args were passed to the script, start the FM on public partition.
if [ "$#" -eq 0 ]; then
args="/mnt/public"
else
args="$*"
fi
# NOTE: Keep doing an initial update check, in addition to one during the restart loop, so we can pickup potential updates of this very script...
ko_update_check
# If an update happened, and was successful, reexec
if [ -n "${fail}" ] && [ "${fail}" -eq 0 ]; then
# By now, we know we're in the right directory, and our script name is pretty much set in stone, so we can forgo using $0
exec ./koreader.sh "${args}"
fi
# load our own shared libraries if possible
export LD_LIBRARY_PATH="${KOREADER_DIR}/libs"
# export trained OCR data directory
export TESSDATA_PREFIX="data"
# export dict directory
export STARDICT_DATA_DIR="data/dict"
# we keep at most 500k worth of crash log
if [ -e crash.log ]; then
tail -c 500000 crash.log >crash.log.new
mv -f crash.log.new crash.log
fi
# check if QBookApp was started before us, then
# restart the application after leaving KOReader
export STANDALONE="true"
if pkill -0 QBookpp; then
STANDALONE="false"
fi
if [ "${STANDALONE}" != "true" ]; then
stopapp.sh >/dev/null 2>&1
fi
RETURN_VALUE=85
while [ "${RETURN_VALUE}" -eq 85 ]; do
./reader.lua "${args}" >>crash.log 2>&1
RETURN_VALUE=$?
done
if [ "${STANDALONE}" != "true" ]; then
restart.sh >/dev/null 2>&1
fi

@ -0,0 +1,6 @@
#!/bin/sh
./release-ip.sh
# Use udhcpc to obtain IP.
udhcpc -S -i eth0 -s /etc/udhcpc/default.script -t15 -T10 -A3 -b -q

@ -0,0 +1,5 @@
#!/bin/sh
# Release IP and shutdown udhcpc.
pkill -9 -f '/bin/sh /etc/udhcpc/default.script'
ifconfig eth0 0.0.0.0

@ -0,0 +1,10 @@
#!/bin/sh
RestoreWifi() {
echo "[$(date)] restore-wifi-async.sh: Restarting WiFi"
./enable-wifi.sh
./obtain-ip.sh
echo "[$(date)] restore-wifi-async.sh: Restarted WiFi"
}
RestoreWifi &

@ -0,0 +1,4 @@
#! /bin/sh
echo 0 >/sys/power/state-extended

@ -0,0 +1,15 @@
#! /bin/sh
# De-activate the touch screen.
echo 1 >/sys/power/state-extended
# Prevent the following error on the last line:
# *write error: Operation not permitted*.
sleep 2
# Synchronize the file system.
sync
# Suspend to RAM.
echo mem >/sys/power/state

@ -1,6 +1,8 @@
local Device = require("device")
if not Device:isKobo() and not Device:isSDL() and not Device:isSonyPRSTUX() then return { disabled = true, } end
if not Device:isCervantes() and not Device:isKobo() and not Device:isSDL() and not Device:isSonyPRSTUX() then
return { disabled = true, }
end
local DataStorage = require("datastorage")
local LuaSettings = require("luasettings")

@ -27,7 +27,7 @@ local function showConfirmBox()
})
end
if Device:isKobo() then
if Device:isCervantes() or Device:isKobo() then
local PluginShare = require("pluginshare")
enable = function() PluginShare.pause_auto_suspend = true end
disable = function() PluginShare.pause_auto_suspend = false end

@ -1,7 +1,7 @@
local Device = require("device")
local with_frontlight = (Device:isKindle() or Device:isKobo()) and Device:hasFrontlight()
local with_natural_light = Device:isKobo() and Device:hasNaturalLight()
local with_frontlight = (Device:isCervantes() or Device:isKindle() or Device:isKobo()) and Device:hasFrontlight()
local with_natural_light = (Device:isCervantes() or Device:isKobo()) and Device:hasNaturalLight()
if not (with_frontlight or Device:isSDL()) then
return { disabled = true, }
end

@ -16,7 +16,7 @@ local SystemStat = {
}
function SystemStat:init()
if Device:isKobo() or Device:isPocketBook() then
if Device:isCervantes() or Device:isKobo() or Device:isPocketBook() then
self.storage_filter = "mmcblk"
elseif Device:isKindle() then
self.storage_filter = "' /mnt/us$'"

@ -4,7 +4,7 @@ local command
-- TODO(hzj-jie): Does pocketbook provide ntpdate?
if Device:isKobo() then
command = "ntpd -q -n -p pool.ntp.org"
elseif Device:isKindle() or Device:isPocketBook() then
elseif Device:isCervantes() or Device:isKindle() or Device:isPocketBook() then
command = "ntpdate pool.ntp.org"
else
return { disabled = true, }

Loading…
Cancel
Save