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

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

unix.superglobalmegacorp.com

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