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