|
|
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:
7: #include "rtc.h"
1.1.1.6 root 8: #include "config.h"
1.1 root 9: #include <sys/time.h>
10:
1.1.1.2 root 11: std::unique_ptr<RTCDevice> gRTC;
1.1 root 12:
1.1.1.5 root 13: RTCDevice::RTCDevice(const std::string& objname_)
14: : inherited(objname_)
1.1 root 15: {
16: }
17:
18: RTCDevice::~RTCDevice()
19: {
20: }
21:
1.1.1.6 root 22: bool
23: RTCDevice::Init()
1.1 root 24: {
25: struct timeval tv;
26: struct tm tm;
27:
1.1.1.6 root 28: // TODO: year_epoch, use_localtime を設定可能にするべきかどうか
29:
30: // 時刻固定オプション (パフォーマンス測定用)
31: // パフォーマンス測定をする際に、RTC が実時間を返すと
32: // ゲストの状態が毎回異なることになるため、
33: // いつ何時も同じ値を返すことで、世界から隔絶された
34: // VM を作り、パフォーマンス測定を安定させる。
35: force_fixed = gConfig->Find(".rtc-force-fixed").AsInt();
36: if (force_fixed) {
37: putmsgn("force_fixed");
38: // 固定の時刻を設定
39: struct tm fixed {
40: .tm_sec = 16,
41: .tm_min = 22,
42: .tm_hour = 13 - 9,
43: .tm_mday = 27,
44: .tm_mon = 7 - 1,
45: .tm_year = 2018 - 1900,
46: };
47: tv.tv_sec = timegm(&fixed);
48: tv.tv_usec = 0;
49: } else {
50: // 起動した時の現在時刻で設定する
51: gettimeofday(&tv, NULL);
52: }
53:
1.1 root 54: if (use_localtime) {
55: localtime_r(&tv.tv_sec, &tm);
56: } else {
57: gmtime_r(&tv.tv_sec, &tm);
58: }
1.1.1.6 root 59:
60: // 日付、時刻の直接指定があればここで差し替える。
61: const ConfigItem item_date = gConfig->Find(".rtc-date");
62: std::string str_date = item_date.AsString();
63: if (!str_date.empty()) {
64: int yy, mm, dd, w;
65: if (ParseDate(str_date, &yy, &mm, &dd, &w) == false) {
66: item_date.Err();
67: return false;
68: }
69: tm.tm_year = yy - 1900;
70: tm.tm_mon = mm - 1;
71: tm.tm_mday = dd;
72: if (w != -1) {
73: tm.tm_wday = w;
74: }
75: }
76: const ConfigItem item_time = gConfig->Find(".rtc-time");
77: std::string str_time = item_time.AsString();
78: if (!str_time.empty()) {
79: int hh, mm, ss;
80: if (ParseTime(str_time, &hh, &mm, &ss) == false) {
81: item_time.Err();
82: return false;
83: }
84: tm.tm_hour = hh;
85: tm.tm_min = mm;
86: tm.tm_sec = ss;
87: }
88:
1.1 root 89: // 秒未満の初期値もそれなりの精度にしておく (1000*1000 / 32 = 31250)
1.1.1.6 root 90: cnt = tv.tv_usec / 31250;
91: SetSec(tm.tm_sec);
92: SetMin(tm.tm_min);
93: SetHour(tm.tm_hour);
1.1 root 94: // wday も tm_wday も日曜が 0
1.1.1.6 root 95: SetWday(tm.tm_wday);
96: SetMday(tm.tm_mday);
97: // mon は 1 から、tm_mon は 0 から。
98: SetMon(tm.tm_mon + 1);
99: SetYear(tm.tm_year + 1900);
100: SetLeap(tm.tm_year & 3);
101:
102: return true;
1.1 root 103: }
104:
1.1.1.6 root 105: // デバッグオプションの日付文字列を解析する。
106: // 書式は "yyyy-mm-dd[-w]"。曜日は省略可能、区切り文字は1文字なら何でもいい。
107: // 入力値の範囲チェックはしない。(出来れば 0A月みたいなのも受け付けたいが)
108: bool
109: RTCDevice::ParseDate(const std::string& str, int *y, int *m, int *d, int *w)
1.1 root 110: {
1.1.1.6 root 111: const char *p = str.data();
112: char *end;
1.1 root 113:
1.1.1.6 root 114: errno = 0;
115: *y = strtoul(p, &end, 10);
116: if (end == p || *end == '\0' || errno != 0) {
117: return false;
118: }
1.1 root 119:
1.1.1.6 root 120: p = end + 1;
121: errno = 0;
122: *m = strtoul(p, &end, 10);
123: if (end == p || *end == '\0' || errno != 0) {
124: return false;
125: }
1.1 root 126:
1.1.1.6 root 127: p = end + 1;
128: errno = 0;
129: *d = strtoul(p, &end, 10);
130: if (end == p || errno != 0) {
131: return false;
1.1 root 132: }
133:
1.1.1.6 root 134: if (*end == 0) {
135: // 曜日はなくても可
136: *w = -1;
137: } else {
138: // あれば取り出す
139: p = end + 1;
140: errno = 0;
141: *w = strtoul(p, &end, 10);
142: if (end == p || errno != 0) {
143: return false;
144: }
1.1 root 145: }
1.1.1.6 root 146:
147: return true;
1.1 root 148: }
149:
1.1.1.6 root 150: // デバッグオプションの時刻文字列を解析する。
151: // 書式は "hh:mm:ss"。区切り文字は1文字なら何でもいい。
152: // 入力値の範囲チェックはしない。
153: bool
154: RTCDevice::ParseTime(const std::string& str, int *h, int *m, int *s)
1.1 root 155: {
1.1.1.6 root 156: const char *p = str.data();
157: char *end;
1.1 root 158:
1.1.1.6 root 159: errno = 0;
160: *h = strtoul(p, &end, 10);
161: if (end == p || *end == '\0' || errno != 0) {
162: return false;
1.1 root 163: }
164:
1.1.1.6 root 165: p = end + 1;
166: errno = 0;
167: *m = strtoul(p, &end, 10);
168: if (end == p || *end == '\0' || errno != 0) {
169: return false;
170: }
1.1 root 171:
1.1.1.6 root 172: p = end + 1;
173: errno = 0;
174: *s = strtoul(p, &end, 10);
175: if (end == p || errno != 0) {
176: return false;
1.1 root 177: }
1.1.1.6 root 178:
179: return true;
1.1 root 180: }
181:
1.1.1.6 root 182: // 32Hz のクロック入力。
183: // スケジューラから呼び出される。
184: // 本来の RTC は 32768Hz が入力クロックだが、
185: // それを 1024 分周したものを基準クロック入力として考える。
186: void
187: RTCDevice::ClockIn()
1.1 root 188: {
1.1.1.6 root 189: cnt++;
1.1 root 190:
1.1.1.6 root 191: Tick32Hz();
192: if ((cnt & 15) == 0) {
193: Tick2Hz();
194: if ((cnt & 31) == 0) {
195: // 秒カウントアップは Tick1Hz が行う
196: // 秒カウントアップ禁止を実装するため、
197: // 基本クラスで直接秒カウントアップはしない。
198: Tick1Hz();
199: }
1.1 root 200: }
201: }
202:
1.1.1.6 root 203: // 秒をカウントアップし、繰り上がりを処理。
204: void
205: RTCDevice::CountUpSec()
1.1 root 206: {
1.1.1.6 root 207: uint v;
1.1 root 208:
1.1.1.6 root 209: if (__predict_false(force_fixed)) {
210: // 時刻固定モードならカウントアップしない
211: return;
212: }
1.1 root 213:
1.1.1.6 root 214: // BCD の桁ごとにキャリー処理をしてるはずだけど妥協。
215: // たぶん内部キャリーはビット途中のキャリーで作られていると
216: // 思うんだけど、ソフトでやるのは大変なので妥協。
217:
218: v = GetSec() + 1;
219: if (v < 60) {
220: SetSec(v);
221: return;
1.1 root 222: }
1.1.1.6 root 223: SetSec(0);
224: CountUpMin();
1.1 root 225: }
226:
1.1.1.6 root 227: // 分をカウントアップし、繰り上がりを処理。
228: void
229: RTCDevice::CountUpMin()
1.1 root 230: {
1.1.1.6 root 231: uint v;
1.1 root 232:
1.1.1.6 root 233: v = GetMin() + 1;
234: if (v < 60) {
235: SetMin(v);
236: return;
1.1 root 237: }
1.1.1.6 root 238: SetMin(0);
1.1 root 239:
1.1.1.6 root 240: v = GetHour() + 1;
241: if (v < 24) {
242: SetHour(v);
243: return;
244: }
245: SetHour(0);
246:
247: // 曜日は単純7進カウンタ
248: SetWday((GetWday() + 1) % 7);
1.1 root 249:
1.1.1.6 root 250: v = GetMday() + 1;
251: if (v <= GetLastMday()) {
252: SetMday(v);
253: return;
254: }
255: SetMday(1);
256:
257: v = GetMon() + 1;
258: if (v <= 12) {
259: SetMon(v);
260: return;
261: }
262: SetMon(1);
1.1 root 263:
1.1.1.6 root 264: SetLeap((GetLeap() + 1) % 4);
265: SetYear(GetYear() + 1);
1.1 root 266: }
267:
1.1.1.6 root 268: // 現在の月の最終日を返す (月の繰り上がり処理用)
269: int
270: RTCDevice::GetLastMday() const
1.1 root 271: {
1.1.1.6 root 272: // 下記の理由により 0 月が必要。値は適当。
273: static const int mdays[] = {
274: 30, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
275: };
276:
277: // GetMon() は不正な範囲を取ると規定していることに注意。
278: // (0月、19月などが許されている)
279: // 範囲外の値に対するロジックは違うだろうが規定されていないので
280: // こっち都合で実装しておく。
281: // m は 0..12
282: int m = GetMon() % 13;
283: int lastday = mdays[m];
284: if (m == 2 && GetLeap() == 0) {
285: lastday++;
1.1 root 286: }
1.1.1.6 root 287: return lastday;
1.1 root 288: }
289:
1.1.1.6 root 290: // 曜日文字列。
291: // RP5C15、MK48T02 いずれも曜日カウンタと曜日の対応はデータシートに規定がなく
292: // 運用依存であり、X680x0 (RP5C15) は 0..6 (0が日曜) としている。
293: // LUNA (MK48T02) は運用上 0..7(0と7が日曜) となってしまっている。
1.1 root 294: /*static*/ const char * const
1.1.1.6 root 295: RTCDevice::wdays[8] = {
1.1 root 296: "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
1.1.1.6 root 297: "Sun",
1.1 root 298: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.