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

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

unix.superglobalmegacorp.com

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