|
|
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"
! 14: #include "sync.h"
1.1 root 15:
1.1.1.8 ! root 16: // グローバル参照用
! 17: RTCDevice *gRTC;
1.1 root 18:
1.1.1.8 ! root 19: // コンストラクタ
1.1.1.5 root 20: RTCDevice::RTCDevice(const std::string& objname_)
21: : inherited(objname_)
1.1 root 22: {
1.1.1.8 ! root 23: event.Regist("RTC ClockIn");
! 24: event.time = period;
! 25: event.func = ToEventCallback(&RTCDevice::Callback);
1.1 root 26: }
27:
1.1.1.8 ! root 28: // デストラクタ
1.1 root 29: RTCDevice::~RTCDevice()
30: {
1.1.1.8 ! root 31: gRTC = NULL;
1.1 root 32: }
33:
1.1.1.6 root 34: bool
35: RTCDevice::Init()
1.1 root 36: {
1.1.1.8 ! root 37: // TODO: use_localtime を設定可能にするべきかどうか
1.1 root 38:
1.1.1.8 ! root 39: // 時刻固定オプション (パフォーマンス測定用)。
1.1.1.6 root 40: // パフォーマンス測定をする際に、RTC が実時間を返すと
41: // ゲストの状態が毎回異なることになるため、
42: // いつ何時も同じ値を返すことで、世界から隔絶された
43: // VM を作り、パフォーマンス測定を安定させる。
44: force_fixed = gConfig->Find(".rtc-force-fixed").AsInt();
1.1.1.8 ! root 45:
! 46: // クロックの同期方法
! 47: const ConfigItem& item_sync = gConfig->Find("clock-sync");
! 48: auto clock_sync = item_sync.AsString();
! 49: if (clock_sync == "real") {
! 50: sync_rt = true;
! 51: } else if (clock_sync == "virtual") {
! 52: sync_rt = false;
! 53: } else {
! 54: item_sync.Err();
! 55: return false;
! 56: }
! 57:
! 58: rtc_tm.tm_year = -1;
! 59: rtc_tm.tm_mon = -1;
! 60: rtc_tm.tm_mday = -1;
! 61: rtc_tm.tm_wday = -1;
! 62: rtc_tm.tm_hour = -1;
! 63: rtc_tm.tm_min = -1;
! 64: rtc_tm.tm_sec = -1;
! 65:
! 66: // 日付、時刻の直接指定
! 67: const ConfigItem item_date = gConfig->Find(".rtc-date");
! 68: std::string str_date = item_date.AsString();
! 69: if (!str_date.empty()) {
! 70: int yy, mm, dd, w;
! 71: if (ParseDate(str_date, &yy, &mm, &dd, &w) == false) {
! 72: item_date.Err();
! 73: return false;
! 74: }
! 75: rtc_tm.tm_year = yy - 1900;
! 76: rtc_tm.tm_mon = mm - 1;
! 77: rtc_tm.tm_mday = dd;
! 78: if (w != -1) {
! 79: rtc_tm.tm_wday = w;
! 80: }
! 81: }
! 82:
! 83: const ConfigItem item_time = gConfig->Find(".rtc-time");
! 84: std::string str_time = item_time.AsString();
! 85: if (!str_time.empty()) {
! 86: int hh, mm, ss;
! 87: if (ParseTime(str_time, &hh, &mm, &ss) == false) {
! 88: item_time.Err();
! 89: return false;
! 90: }
! 91: rtc_tm.tm_hour = hh;
! 92: rtc_tm.tm_min = mm;
! 93: rtc_tm.tm_sec = ss;
! 94: }
! 95:
! 96: return true;
! 97: }
! 98:
! 99: // 時間の始まり
! 100: void
! 101: RTCDevice::StartTime()
! 102: {
! 103: struct timeval tv;
! 104: struct tm tm;
! 105:
1.1.1.6 root 106: if (force_fixed) {
107: putmsgn("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.8 ! root 158: stime = gSync->GetRealTime();
! 159:
! 160: gScheduler->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) {
! 247: gScheduler->StartRealtimeEvent(ev, stime, period);
! 248: } else {
! 249: gScheduler->StartEvent(ev);
! 250: }
! 251: }
! 252:
! 253: uint64
! 254: RTCDevice::ClockIn_PowerOff()
! 255: {
! 256: stime += period;
! 257:
! 258: ClockIn();
! 259:
! 260: // XXX RealTime ではなく HostTime?
! 261: uint64 rt = gSync->GetRealTime();
! 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: // 現在の月の最終日を返す (月の繰り上がり処理用)
378: int
379: RTCDevice::GetLastMday() const
1.1 root 380: {
1.1.1.6 root 381: // 下記の理由により 0 月が必要。値は適当。
382: static const int mdays[] = {
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
391: int m = GetMon() % 13;
392: int lastday = mdays[m];
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.