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