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