diff --git a/.gitignore b/.gitignore index 30f41fb2d..3c598d9be 100644 --- a/.gitignore +++ b/.gitignore @@ -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* diff --git a/Makefile b/Makefile index 39e961b8b..3229f0f86 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/base b/base index 42bc013b6..f13f1ce2e 160000 --- a/base +++ b/base @@ -1 +1 @@ -Subproject commit 42bc013b62c8039d733513e00a92dd6c70d8b421 +Subproject commit f13f1ce2ee42a64788ad80adb12d34197406b984 diff --git a/frontend/apps/filemanager/filemanagerutil.lua b/frontend/apps/filemanager/filemanagerutil.lua index 8fb9289df..15caf89e4 100644 --- a/frontend/apps/filemanager/filemanagerutil.lua +++ b/frontend/apps/filemanager/filemanagerutil.lua @@ -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 diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index 1b0ed10e7..32c5d473a 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -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()) diff --git a/frontend/device.lua b/frontend/device.lua index 3ca5751eb..e13e7c02d 100644 --- a/frontend/device.lua +++ b/frontend/device.lua @@ -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 diff --git a/frontend/device/cervantes/device.lua b/frontend/device/cervantes/device.lua new file mode 100644 index 000000000..e724342a7 --- /dev/null +++ b/frontend/device/cervantes/device.lua @@ -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 diff --git a/frontend/device/cervantes/powerd.lua b/frontend/device/cervantes/powerd.lua new file mode 100644 index 000000000..61e793b1b --- /dev/null +++ b/frontend/device/cervantes/powerd.lua @@ -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 diff --git a/frontend/device/generic/device.lua b/frontend/device/generic/device.lua index 3e9b2a3ea..be88b6196 100644 --- a/frontend/device/generic/device.lua +++ b/frontend/device/generic/device.lua @@ -32,11 +32,12 @@ local Device = { -- and have device dependent implementations in the corresponting -- device//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 diff --git a/frontend/device/input.lua b/frontend/device/input.lua index 4540fefda..bd96fe612 100755 --- a/frontend/device/input.lua +++ b/frontend/device/input.lua @@ -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 diff --git a/frontend/ui/elements/common_info_menu_table.lua b/frontend/ui/elements/common_info_menu_table.lua index 609651c90..6fe313bdb 100644 --- a/frontend/ui/elements/common_info_menu_table.lua +++ b/frontend/ui/elements/common_info_menu_table.lua @@ -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, diff --git a/frontend/ui/network/manager.lua b/frontend/ui/network/manager.lua index 38c5da04e..f0607789f 100644 --- a/frontend/ui/network/manager.lua +++ b/frontend/ui/network/manager.lua @@ -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 diff --git a/frontend/ui/otamanager.lua b/frontend/ui/otamanager.lua index a37d61f21..5e883c541 100644 --- a/frontend/ui/otamanager.lua +++ b/frontend/ui/otamanager.lua @@ -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 diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index f7eb24e48..33a13e6bf 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -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"]() diff --git a/frontend/ui/widget/frontlightwidget.lua b/frontend/ui/widget/frontlightwidget.lua index 62a7f0427..94ab712b2 100644 --- a/frontend/ui/widget/frontlightwidget.lua +++ b/frontend/ui/widget/frontlightwidget.lua @@ -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 diff --git a/kodev b/kodev index 620b4cbfd..c2d5522a2 100755 --- a/kodev +++ b/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 diff --git a/platform/cervantes/disable-usbms.sh b/platform/cervantes/disable-usbms.sh new file mode 100644 index 000000000..86a317f8f --- /dev/null +++ b/platform/cervantes/disable-usbms.sh @@ -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 + diff --git a/platform/cervantes/disable-wifi.sh b/platform/cervantes/disable-wifi.sh new file mode 100755 index 000000000..e1d73b26e --- /dev/null +++ b/platform/cervantes/disable-wifi.sh @@ -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 diff --git a/platform/cervantes/enable-usbms.sh b/platform/cervantes/enable-usbms.sh new file mode 100644 index 000000000..a43fe5be0 --- /dev/null +++ b/platform/cervantes/enable-usbms.sh @@ -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 diff --git a/platform/cervantes/enable-wifi.sh b/platform/cervantes/enable-wifi.sh new file mode 100755 index 000000000..23a157695 --- /dev/null +++ b/platform/cervantes/enable-wifi.sh @@ -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 diff --git a/platform/cervantes/koreader-standalone.sh b/platform/cervantes/koreader-standalone.sh new file mode 100755 index 000000000..21925085b --- /dev/null +++ b/platform/cervantes/koreader-standalone.sh @@ -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 diff --git a/platform/cervantes/koreader.sh b/platform/cervantes/koreader.sh new file mode 100755 index 000000000..ae22b082b --- /dev/null +++ b/platform/cervantes/koreader.sh @@ -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 diff --git a/platform/cervantes/obtain-ip.sh b/platform/cervantes/obtain-ip.sh new file mode 100755 index 000000000..a253fe44b --- /dev/null +++ b/platform/cervantes/obtain-ip.sh @@ -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 diff --git a/platform/cervantes/release-ip.sh b/platform/cervantes/release-ip.sh new file mode 100755 index 000000000..b0b9c43d1 --- /dev/null +++ b/platform/cervantes/release-ip.sh @@ -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 diff --git a/platform/cervantes/restore-wifi-async.sh b/platform/cervantes/restore-wifi-async.sh new file mode 100755 index 000000000..9e45bd53b --- /dev/null +++ b/platform/cervantes/restore-wifi-async.sh @@ -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 & diff --git a/platform/cervantes/resume.sh b/platform/cervantes/resume.sh new file mode 100755 index 000000000..e689b8bf5 --- /dev/null +++ b/platform/cervantes/resume.sh @@ -0,0 +1,4 @@ +#! /bin/sh + +echo 0 >/sys/power/state-extended + diff --git a/platform/cervantes/suspend.sh b/platform/cervantes/suspend.sh new file mode 100755 index 000000000..5b19fa722 --- /dev/null +++ b/platform/cervantes/suspend.sh @@ -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 + diff --git a/plugins/autosuspend.koplugin/main.lua b/plugins/autosuspend.koplugin/main.lua index ff3ee0c6b..ca73fef1e 100644 --- a/plugins/autosuspend.koplugin/main.lua +++ b/plugins/autosuspend.koplugin/main.lua @@ -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") diff --git a/plugins/keepalive.koplugin/main.lua b/plugins/keepalive.koplugin/main.lua index d6ded6e25..a36b2c259 100644 --- a/plugins/keepalive.koplugin/main.lua +++ b/plugins/keepalive.koplugin/main.lua @@ -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 diff --git a/plugins/kobolight.koplugin/main.lua b/plugins/kobolight.koplugin/main.lua index 3dcfb7b6b..bd6206264 100644 --- a/plugins/kobolight.koplugin/main.lua +++ b/plugins/kobolight.koplugin/main.lua @@ -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 diff --git a/plugins/systemstat.koplugin/main.lua b/plugins/systemstat.koplugin/main.lua index ae0a31f45..f1a3d5aa5 100644 --- a/plugins/systemstat.koplugin/main.lua +++ b/plugins/systemstat.koplugin/main.lua @@ -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$'" diff --git a/plugins/timesync.koplugin/main.lua b/plugins/timesync.koplugin/main.lua index a60fdc41e..b83566534 100644 --- a/plugins/timesync.koplugin/main.lua +++ b/plugins/timesync.koplugin/main.lua @@ -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, }