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

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

unix.superglobalmegacorp.com

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