local Generic = require("device/generic/device") local logger = require("logger") local function yes() return true end local function no() return false 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 = ntxinfo_pcb:read("*number") or 0 ntxinfo_pcb:close() return product_id end local function isConnected() -- read carrier state from sysfs (for eth0) local file = io.open("/sys/class/net/eth0/carrier", "rb") -- file exists while Wi-Fi module is loaded. if not file then return 0 end -- 0 means not connected, 1 connected local out = file:read("*number") file:close() return out or 0 end local function isMassStorageSupported() -- we rely on 3rd party package for that. It should be installed as part of KOReader prerequisites, local safemode_version = io.open("/usr/share/safemode/version", "rb") if not safemode_version then return false end safemode_version:close() return true end local Cervantes = Generic:extend{ model = "Cervantes", isCervantes = yes, isAlwaysPortrait = yes, isTouchDevice = yes, touch_legacy = true, -- SingleTouch input events touch_switch_xy = true, touch_mirrored_x = true, hasOTAUpdates = yes, hasFastWifiStatusQuery = yes, hasKeys = yes, hasWifiManager = yes, canReboot = yes, canPowerOff = yes, canSuspend = yes, supportsScreensaver = yes, home_dir = "/mnt/public", -- do we support usb mass storage? canToggleMassStorage = function() return isMassStorageSupported() end, -- all devices, except the original Cervantes Touch, have frontlight hasFrontlight = yes, -- currently only Cervantes 4 has coloured frontlight hasNaturalLight = no, hasNaturalLightMixer = no, -- HW inversion is generally safe on Cervantes, except on a few boards/kernels canHWInvert = yes, } -- Cervantes Touch local CervantesTouch = Cervantes:extend{ model = "CervantesTouch", display_dpi = 167, hasFrontlight = no, hasMultitouch = no, } -- Cervantes TouchLight / Fnac Touch Plus local CervantesTouchLight = Cervantes:extend{ model = "CervantesTouchLight", display_dpi = 167, hasMultitouch = no, } -- Cervantes 2013 / Fnac Touch Light local Cervantes2013 = Cervantes:extend{ model = "Cervantes2013", display_dpi = 212, hasMultitouch = no, --- @fixme: Possibly requires canHWInvert = no, as it seems to be based on a similar board as the Kobo Aura... } -- Cervantes 3 / Fnac Touch Light 2 local Cervantes3 = Cervantes:extend{ model = "Cervantes3", display_dpi = 300, hasMultitouch = no, } -- Cervantes 4 local Cervantes4 = Cervantes:extend{ model = "Cervantes4", display_dpi = 300, hasNaturalLight = yes, frontlight_settings = { frontlight_white = "/sys/class/backlight/mxc_msp430_fl.0/brightness", frontlight_mixer = "/sys/class/backlight/lm3630a_led/color", nl_min = 0, nl_max = 10, nl_inverted = true, }, } -- input events function Cervantes:initEventAdjustHooks() if self.touch_switch_xy and self.touch_mirrored_x then self.input:registerEventAdjustHook( self.input.adjustTouchSwitchAxesAndMirrorX, (self.screen:getWidth() - 1) ) 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, is_always_portrait = self.isAlwaysPortrait()} -- Automagically set this so we never have to remember to do it manually ;p if self:hasNaturalLight() and self.frontlight_settings and self.frontlight_settings.frontlight_mixer then self.hasNaturalLightMixer = yes end 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 Wi-Fi") self:releaseIP() os.execute("./disable-wifi.sh") if complete_callback then complete_callback() end end function NetworkMgr:turnOnWifi(complete_callback) logger.info("Cervantes: enabling Wi-Fi") os.execute("./enable-wifi.sh") self:reconnectOrShowNetworkMenu(complete_callback) end function NetworkMgr:getNetworkInterfaceName() return "eth0" 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 1 == isConnected() end 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("sleep 1 && reboot &") end function Cervantes:powerOff() os.execute("sleep 1 && halt &") end -- This method is the same as the one in kobo/device.lua except the sleep cover part. function Cervantes:setEventHandlers(UIManager) -- We do not want auto suspend procedure to waste battery during -- suspend. So let's unschedule it when suspending, and restart it after -- resume. Done via the plugin's onSuspend/onResume handlers. UIManager.event_handlers.Suspend = function() self:_beforeSuspend() self:onPowerEvent("Suspend") end UIManager.event_handlers.Resume = function() -- MONOTONIC doesn't tick during suspend, -- invalidate the last battery capacity pull time so that we get up to date data immediately. self:getPowerDevice():invalidateCapacityCache() self:onPowerEvent("Resume") self:_afterResume() end UIManager.event_handlers.PowerPress = function() -- Always schedule power off. -- Press the power button for 2+ seconds to shutdown directly from suspend. UIManager:scheduleIn(2, UIManager.poweroff_action) end UIManager.event_handlers.PowerRelease = function() if not UIManager._entered_poweroff_stage then UIManager:unschedule(UIManager.poweroff_action) -- resume if we were suspended if self.screen_saver_mode then if self.screen_saver_lock then logger.dbg("Pressed power while awake in screen saver mode, going back to suspend...") self:_beforeSuspend() self.powerd:beforeSuspend() -- this won't be run by onPowerEvent because we're in screen_saver_mode self:onPowerEvent("Suspend") else UIManager.event_handlers.Resume() end else UIManager.event_handlers.Suspend() end end end UIManager.event_handlers.Light = function() self:getPowerDevice():toggleFrontlight() end -- USB plug events with a power-only charger UIManager.event_handlers.Charging = function() self:_beforeCharging() -- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep. if self.screen_saver_mode then UIManager.event_handlers.Suspend() end end UIManager.event_handlers.NotCharging = function() -- We need to put the device into suspension, other things need to be done before it. self:usbPlugOut() self:_afterNotCharging() if self.screen_saver_mode then UIManager.event_handlers.Suspend() end end -- USB plug events with a data-aware host UIManager.event_handlers.UsbPlugIn = function() self:_beforeCharging() -- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep. if self.screen_saver_mode then UIManager.event_handlers.Suspend() else -- Potentially start an USBMS session local MassStorage = require("ui/elements/mass_storage") MassStorage:start() end end UIManager.event_handlers.UsbPlugOut = function() -- We need to put the device into suspension, other things need to be done before it. self:usbPlugOut() self:_afterNotCharging() if self.screen_saver_mode then UIManager.event_handlers.Suspend() else -- Potentially dismiss the USBMS ConfirmBox local MassStorage = require("ui/elements/mass_storage") MassStorage:dismiss() end end 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