-
Notifications
You must be signed in to change notification settings - Fork 104
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This will allow for scheduling wakeups. Prerequisite for <koreader/koreader#5335>.
- Loading branch information
Showing
1 changed file
with
165 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
--[[-- | ||
Module for interfacing with the RTC (real time clock). | ||
This module provides the ability to schedule wakeups through RTC. | ||
@module ffi.rtc | ||
]] | ||
|
||
local ffi = require("ffi") | ||
local bor = bit.bor | ||
local C = ffi.C | ||
|
||
local dummy = require("ffi/posix_h") | ||
local dummy = require("ffi/rtc_h") | ||
|
||
----------------------------------------------------------------- | ||
|
||
local RTC = { | ||
_wakeup_scheduled = false, | ||
_wakeup_scheduled_ptm = nil, | ||
} | ||
|
||
function RTC:secondsFromNowToEpoch(seconds_from_now) | ||
local t = ffi.new("time_t[1]") | ||
t[0] = C.time(nil) | ||
t[0] = t[0] + seconds_from_now | ||
local epoch = C.mktime(C.localtime(t)) | ||
return epoch | ||
end | ||
|
||
function RTC:setWakeupAlarm(epoch, enabled) | ||
enabled = (enabled ~= nil) and enabled or true | ||
|
||
local ptm = C.gmtime(ffi.new("int[1]", epoch)) | ||
self._wakeup_scheduled_ptm = ptm | ||
|
||
local wake = ffi.new("struct rtc_wkalrm") | ||
wake.time.tm_sec = ptm.tm_sec | ||
wake.time.tm_min = ptm.tm_min | ||
wake.time.tm_hour = ptm.tm_hour | ||
wake.time.tm_mday = ptm.tm_mday | ||
wake.time.tm_mon = ptm.tm_mon | ||
wake.time.tm_year = ptm.tm_year | ||
-- wday, yday, and isdst fields are unused by Linux | ||
wake.time.tm_wday = -1 | ||
wake.time.tm_yday = -1 | ||
wake.time.tm_isdst = -1 | ||
|
||
wake.enabled = enabled and 1 or 0 | ||
|
||
local err | ||
local rtc0 = C.open("/dev/rtc0", bor(C.O_RDONLY, C.O_NONBLOCK)) | ||
if rtc0 == -1 then | ||
err = ffi.string(C.strerror(ffi.errno())) | ||
print("setWakeupAlarm open /dev/rtc0", rtc0, err) | ||
end | ||
local re = C.ioctl(rtc0, C.RTC_WKALM_SET, wake) | ||
if re == -1 then | ||
err = ffi.string(C.strerror(ffi.errno())) | ||
print("setWakeupAlarm ioctl RTC_WKALM_SET", re, err) | ||
end | ||
re = C.close(rtc0) | ||
if re == -1 then | ||
err = ffi.string(C.strerror(ffi.errno())) | ||
print("setWakeupAlarm close /dev/rtc0", re, err) | ||
end | ||
|
||
if re == 0 then | ||
if enabled then | ||
self._wakeup_scheduled = true | ||
else | ||
self._wakeup_scheduled = false | ||
self._wakeup_scheduled_ptm = nil | ||
end | ||
return true | ||
else | ||
return nil, re, err | ||
end | ||
end | ||
|
||
function RTC:unsetWakeupAlarm() | ||
self:setWakeupAlarm(-1, false) | ||
self._wakeup_scheduled = false | ||
self._wakeup_scheduled_ptm = nil | ||
end | ||
|
||
--- Get wakealarm as set by us. | ||
function RTC:getWakeupAlarm() | ||
return self._wakeup_scheduled_ptm | ||
end | ||
|
||
--- Get RTC wakealarm from system. | ||
function RTC:getWakeupAlarmSys() | ||
local wake = ffi.new("struct rtc_wkalrm") | ||
|
||
local err, re | ||
local rtc0 = C.open("/dev/rtc0", C.O_RDONLY) | ||
if rtc0 == -1 then | ||
err = ffi.string(C.strerror(ffi.errno())) | ||
print("getWakeupAlarm open /dev/rtc0", rtc0, err) | ||
end | ||
re = C.ioctl(rtc0, C.RTC_WKALM_RD, wake) | ||
if re == -1 then | ||
err = ffi.string(C.strerror(ffi.errno())) | ||
print("getWakeupAlarm ioctl RTC_WKALM_RD", re, err) | ||
end | ||
re = C.close(rtc0) | ||
if re == -1 then | ||
err = ffi.string(C.strerror(ffi.errno())) | ||
print("getWakeupAlarm close /dev/rtc0", re, err) | ||
end | ||
|
||
if wake ~= -1 then | ||
local t = ffi.new("time_t[1]") | ||
t[0] = C.time(nil) | ||
local tm = ffi.new("struct tm") -- luacheck: ignore | ||
tm = C.gmtime(t) | ||
tm.tm_sec = wake.time.tm_sec | ||
tm.tm_min = wake.time.tm_min | ||
tm.tm_hour = wake.time.tm_hour | ||
tm.tm_mday = wake.time.tm_mday | ||
tm.tm_mon = wake.time.tm_mon | ||
tm.tm_year = wake.time.tm_year | ||
return tm | ||
end | ||
end | ||
|
||
function RTC:validateWakeupAlarmByProximity(task_alarm_epoch, proximity) | ||
-- In principle alarm time and current time should match within a second, | ||
-- but let's be absurdly generous and assume anything within 30 is a match. | ||
proximity = proximity or 30 | ||
|
||
local alarm = self:getWakeupAlarm() | ||
local alarm_epoch | ||
local alarm_sys = self:getWakeupAlarmSys() | ||
local alarm_sys_epoch | ||
|
||
-- this seems a bit roundabout | ||
local current_time = ffi.new("time_t[1]") | ||
current_time[0] = C.time(nil) | ||
local current_time_epoch = C.mktime(C.gmtime(current_time)) | ||
|
||
if not (alarm and alarm_sys) then return end | ||
|
||
alarm_epoch = C.mktime(alarm) | ||
alarm_sys_epoch = C.mktime(alarm_sys) | ||
|
||
print("validateWakeupAlarmByProximity", task_alarm_epoch, alarm_epoch, alarm_sys_epoch, current_time_epoch) | ||
|
||
-- If our stored alarm and the system alarm don't match, we didn't set it. | ||
if not (alarm_epoch == alarm_sys_epoch) then return end | ||
|
||
-- If our stored alarm and the provided task alarm don't match, | ||
-- we're not talking about the same task. This should never happen. | ||
if task_alarm_epoch and not (alarm_epoch == task_alarm_epoch) then return end | ||
|
||
local diff = current_time_epoch - alarm_epoch | ||
if diff >= 0 and diff < proximity then return true end | ||
end | ||
|
||
function RTC:isWakeupAlarmScheduled() | ||
return self._wakeup_scheduled | ||
end | ||
|
||
return RTC |