Annotation of nono/vm/rtc.cpp, revision 1.1.1.14

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: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.