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