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