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

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

unix.superglobalmegacorp.com

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