|
|
1.1 root 1: //
2: // nono
1.1.1.3 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.8 root 7: //
8: // RTC の共通部分
9: //
10:
1.1 root 11: #include "rtc.h"
1.1.1.6 root 12: #include "config.h"
1.1.1.8 root 13: #include "scheduler.h"
1.1.1.9 ! root 14: #include "syncer.h"
1.1 root 15:
1.1.1.8 root 16: // コンストラクタ
1.1.1.9 ! root 17: RTCDevice::RTCDevice()
! 18: : inherited(OBJ_RTC)
1.1 root 19: {
1.1.1.8 root 20: event.Regist("RTC ClockIn");
21: event.time = period;
22: event.func = ToEventCallback(&RTCDevice::Callback);
1.1 root 23: }
24:
1.1.1.8 root 25: // デストラクタ
1.1 root 26: RTCDevice::~RTCDevice()
27: {
28: }
29:
1.1.1.9 ! root 30: // 初期化
1.1.1.6 root 31: bool
32: RTCDevice::Init()
1.1 root 33: {
1.1.1.9 ! root 34: if (inherited::Init() == false) {
! 35: return false;
! 36: }
! 37:
! 38: syncer = GetSyncer();
! 39:
1.1.1.8 root 40: // TODO: use_localtime を設定可能にするべきかどうか
1.1 root 41:
1.1.1.8 root 42: // 時刻固定オプション (パフォーマンス測定用)。
1.1.1.6 root 43: // パフォーマンス測定をする際に、RTC が実時間を返すと
44: // ゲストの状態が毎回異なることになるため、
45: // いつ何時も同じ値を返すことで、世界から隔絶された
46: // VM を作り、パフォーマンス測定を安定させる。
47: force_fixed = gConfig->Find(".rtc-force-fixed").AsInt();
1.1.1.8 root 48:
49: // クロックの同期方法
50: const ConfigItem& item_sync = gConfig->Find("clock-sync");
51: auto clock_sync = item_sync.AsString();
52: if (clock_sync == "real") {
53: sync_rt = true;
54: } else if (clock_sync == "virtual") {
55: sync_rt = false;
56: } else {
57: item_sync.Err();
58: return false;
59: }
60:
61: rtc_tm.tm_year = -1;
62: rtc_tm.tm_mon = -1;
63: rtc_tm.tm_mday = -1;
64: rtc_tm.tm_wday = -1;
65: rtc_tm.tm_hour = -1;
66: rtc_tm.tm_min = -1;
67: rtc_tm.tm_sec = -1;
68:
69: // 日付、時刻の直接指定
70: const ConfigItem item_date = gConfig->Find(".rtc-date");
71: std::string str_date = item_date.AsString();
72: if (!str_date.empty()) {
73: int yy, mm, dd, w;
74: if (ParseDate(str_date, &yy, &mm, &dd, &w) == false) {
75: item_date.Err();
76: return false;
77: }
78: rtc_tm.tm_year = yy - 1900;
79: rtc_tm.tm_mon = mm - 1;
80: rtc_tm.tm_mday = dd;
81: if (w != -1) {
82: rtc_tm.tm_wday = w;
83: }
84: }
85:
86: const ConfigItem item_time = gConfig->Find(".rtc-time");
87: std::string str_time = item_time.AsString();
88: if (!str_time.empty()) {
89: int hh, mm, ss;
90: if (ParseTime(str_time, &hh, &mm, &ss) == false) {
91: item_time.Err();
92: return false;
93: }
94: rtc_tm.tm_hour = hh;
95: rtc_tm.tm_min = mm;
96: rtc_tm.tm_sec = ss;
97: }
98:
99: return true;
100: }
101:
102: // 時間の始まり
103: void
104: RTCDevice::StartTime()
105: {
106: struct timeval tv;
107: struct tm tm;
108:
1.1.1.6 root 109: if (force_fixed) {
110: putmsgn("force_fixed");
111: // 固定の時刻を設定
112: struct tm fixed {
113: .tm_sec = 16,
114: .tm_min = 22,
115: .tm_hour = 13 - 9,
116: .tm_mday = 27,
117: .tm_mon = 7 - 1,
118: .tm_year = 2018 - 1900,
119: };
120: tv.tv_sec = timegm(&fixed);
121: tv.tv_usec = 0;
122: } else {
123: // 起動した時の現在時刻で設定する
124: gettimeofday(&tv, NULL);
125: }
126:
1.1 root 127: if (use_localtime) {
128: localtime_r(&tv.tv_sec, &tm);
129: } else {
130: gmtime_r(&tv.tv_sec, &tm);
131: }
1.1.1.6 root 132:
133: // 日付、時刻の直接指定があればここで差し替える。
1.1.1.8 root 134: if (rtc_tm.tm_year != -1) {
135: tm.tm_year = rtc_tm.tm_year;
136: tm.tm_mon = rtc_tm.tm_mon;
137: tm.tm_mday = rtc_tm.tm_mday;
138: if (rtc_tm.tm_wday != -1) {
139: tm.tm_wday = rtc_tm.tm_wday;
1.1.1.6 root 140: }
141: }
1.1.1.8 root 142: if (rtc_tm.tm_hour != -1) {
143: tm.tm_hour = rtc_tm.tm_hour;
144: tm.tm_min = rtc_tm.tm_min;
145: tm.tm_sec = rtc_tm.tm_sec;
1.1.1.6 root 146: }
147:
1.1 root 148: // 秒未満の初期値もそれなりの精度にしておく (1000*1000 / 32 = 31250)
1.1.1.6 root 149: cnt = tv.tv_usec / 31250;
150: SetSec(tm.tm_sec);
151: SetMin(tm.tm_min);
152: SetHour(tm.tm_hour);
1.1 root 153: // wday も tm_wday も日曜が 0
1.1.1.6 root 154: SetWday(tm.tm_wday);
155: SetMday(tm.tm_mday);
156: // mon は 1 から、tm_mon は 0 から。
157: SetMon(tm.tm_mon + 1);
158: SetYear(tm.tm_year + 1900);
159: SetLeap(tm.tm_year & 3);
160:
1.1.1.9 ! root 161: stime = syncer->GetRealTime();
1.1.1.8 root 162:
1.1.1.9 ! root 163: scheduler->StartEvent(event);
1.1 root 164: }
165:
1.1.1.6 root 166: // デバッグオプションの日付文字列を解析する。
167: // 書式は "yyyy-mm-dd[-w]"。曜日は省略可能、区切り文字は1文字なら何でもいい。
168: // 入力値の範囲チェックはしない。(出来れば 0A月みたいなのも受け付けたいが)
169: bool
170: RTCDevice::ParseDate(const std::string& str, int *y, int *m, int *d, int *w)
1.1 root 171: {
1.1.1.6 root 172: const char *p = str.data();
173: char *end;
1.1 root 174:
1.1.1.6 root 175: errno = 0;
176: *y = strtoul(p, &end, 10);
177: if (end == p || *end == '\0' || errno != 0) {
178: return false;
179: }
1.1 root 180:
1.1.1.6 root 181: p = end + 1;
182: errno = 0;
183: *m = strtoul(p, &end, 10);
184: if (end == p || *end == '\0' || errno != 0) {
185: return false;
186: }
1.1 root 187:
1.1.1.6 root 188: p = end + 1;
189: errno = 0;
190: *d = strtoul(p, &end, 10);
191: if (end == p || errno != 0) {
192: return false;
1.1 root 193: }
194:
1.1.1.6 root 195: if (*end == 0) {
196: // 曜日はなくても可
197: *w = -1;
198: } else {
199: // あれば取り出す
200: p = end + 1;
201: errno = 0;
202: *w = strtoul(p, &end, 10);
203: if (end == p || errno != 0) {
204: return false;
205: }
1.1 root 206: }
1.1.1.6 root 207:
208: return true;
1.1 root 209: }
210:
1.1.1.6 root 211: // デバッグオプションの時刻文字列を解析する。
212: // 書式は "hh:mm:ss"。区切り文字は1文字なら何でもいい。
213: // 入力値の範囲チェックはしない。
214: bool
215: RTCDevice::ParseTime(const std::string& str, int *h, int *m, int *s)
1.1 root 216: {
1.1.1.6 root 217: const char *p = str.data();
218: char *end;
1.1 root 219:
1.1.1.6 root 220: errno = 0;
221: *h = strtoul(p, &end, 10);
222: if (end == p || *end == '\0' || errno != 0) {
223: return false;
1.1 root 224: }
225:
1.1.1.6 root 226: p = end + 1;
227: errno = 0;
228: *m = strtoul(p, &end, 10);
229: if (end == p || *end == '\0' || errno != 0) {
230: return false;
231: }
1.1 root 232:
1.1.1.6 root 233: p = end + 1;
234: errno = 0;
235: *s = strtoul(p, &end, 10);
236: if (end == p || errno != 0) {
237: return false;
1.1 root 238: }
1.1.1.6 root 239:
240: return true;
1.1 root 241: }
242:
1.1.1.8 root 243: // イベントハンドラ
244: void
245: RTCDevice::Callback(Event& ev)
246: {
247: stime += period;
248: ClockIn();
249: if (sync_rt) {
1.1.1.9 ! root 250: scheduler->StartRealtimeEvent(ev, stime, period);
1.1.1.8 root 251: } else {
1.1.1.9 ! root 252: scheduler->StartEvent(ev);
1.1.1.8 root 253: }
254: }
255:
256: uint64
257: RTCDevice::ClockIn_PowerOff()
258: {
259: stime += period;
260:
261: ClockIn();
262:
263: // XXX RealTime ではなく HostTime?
1.1.1.9 ! root 264: uint64 rt = syncer->GetRealTime();
1.1.1.8 root 265: if (rt > stime) {
266: // RTC が現実より遅れているので早回し
267: return 0;
268: } else {
269: // clock-sync=virtual のときには stime が現実よりも多く進んで
270: // いるため、実時間との差をスケジューラが usleep で待つところで
271: // 長時間スリープしてしまう。
272: // たちまち、period * 2 で制限しておく。
273: uint64 dt = stime - rt;
274: if (dt > period * 2) {
275: dt = period * 2;
276: }
277: return dt;
278: }
279: }
280:
1.1.1.6 root 281: // 32Hz のクロック入力。
1.1.1.8 root 282: // イベントから呼び出される。
1.1.1.6 root 283: // 本来の RTC は 32768Hz が入力クロックだが、
284: // それを 1024 分周したものを基準クロック入力として考える。
285: void
286: RTCDevice::ClockIn()
1.1 root 287: {
1.1.1.6 root 288: cnt++;
1.1 root 289:
1.1.1.6 root 290: Tick32Hz();
291: if ((cnt & 15) == 0) {
292: Tick2Hz();
293: if ((cnt & 31) == 0) {
294: // 秒カウントアップは Tick1Hz が行う
295: // 秒カウントアップ禁止を実装するため、
296: // 基本クラスで直接秒カウントアップはしない。
297: Tick1Hz();
298: }
1.1 root 299: }
300: }
301:
1.1.1.8 root 302: // 32Hz で呼び出される関数。
303: void
304: RTCDevice::Tick32Hz()
305: {
306: }
307:
308: // 2Hz で呼び出される関数。
309: void
310: RTCDevice::Tick2Hz()
311: {
312: }
313:
314: // 1Hz で呼び出される関数。
315: void
316: RTCDevice::Tick1Hz()
317: {
318: }
319:
1.1.1.6 root 320: // 秒をカウントアップし、繰り上がりを処理。
321: void
322: RTCDevice::CountUpSec()
1.1 root 323: {
1.1.1.6 root 324: uint v;
1.1 root 325:
1.1.1.6 root 326: // BCD の桁ごとにキャリー処理をしてるはずだけど妥協。
327: // たぶん内部キャリーはビット途中のキャリーで作られていると
328: // 思うんだけど、ソフトでやるのは大変なので妥協。
329:
330: v = GetSec() + 1;
331: if (v < 60) {
332: SetSec(v);
333: return;
1.1 root 334: }
1.1.1.6 root 335: SetSec(0);
336: CountUpMin();
1.1 root 337: }
338:
1.1.1.6 root 339: // 分をカウントアップし、繰り上がりを処理。
340: void
341: RTCDevice::CountUpMin()
1.1 root 342: {
1.1.1.6 root 343: uint v;
1.1 root 344:
1.1.1.6 root 345: v = GetMin() + 1;
346: if (v < 60) {
347: SetMin(v);
348: return;
1.1 root 349: }
1.1.1.6 root 350: SetMin(0);
1.1 root 351:
1.1.1.6 root 352: v = GetHour() + 1;
353: if (v < 24) {
354: SetHour(v);
355: return;
356: }
357: SetHour(0);
358:
359: // 曜日は単純7進カウンタ
360: SetWday((GetWday() + 1) % 7);
1.1 root 361:
1.1.1.6 root 362: v = GetMday() + 1;
363: if (v <= GetLastMday()) {
364: SetMday(v);
365: return;
366: }
367: SetMday(1);
368:
369: v = GetMon() + 1;
370: if (v <= 12) {
371: SetMon(v);
372: return;
373: }
374: SetMon(1);
1.1 root 375:
1.1.1.6 root 376: SetLeap((GetLeap() + 1) % 4);
377: SetYear(GetYear() + 1);
1.1 root 378: }
379:
1.1.1.6 root 380: // 現在の月の最終日を返す (月の繰り上がり処理用)
381: int
382: RTCDevice::GetLastMday() const
1.1 root 383: {
1.1.1.6 root 384: // 下記の理由により 0 月が必要。値は適当。
385: static const int mdays[] = {
386: 30, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
387: };
388:
389: // GetMon() は不正な範囲を取ると規定していることに注意。
390: // (0月、19月などが許されている)
391: // 範囲外の値に対するロジックは違うだろうが規定されていないので
392: // こっち都合で実装しておく。
393: // m は 0..12
394: int m = GetMon() % 13;
395: int lastday = mdays[m];
396: if (m == 2 && GetLeap() == 0) {
397: lastday++;
1.1 root 398: }
1.1.1.6 root 399: return lastday;
1.1 root 400: }
401:
1.1.1.6 root 402: // 曜日文字列。
403: // RP5C15、MK48T02 いずれも曜日カウンタと曜日の対応はデータシートに規定がなく
404: // 運用依存であり、X680x0 (RP5C15) は 0..6 (0が日曜) としている。
405: // LUNA (MK48T02) は運用上 0..7(0と7が日曜) となってしまっている。
1.1 root 406: /*static*/ const char * const
1.1.1.6 root 407: RTCDevice::wdays[8] = {
1.1 root 408: "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
1.1.1.6 root 409: "Sun",
1.1 root 410: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.