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

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

unix.superglobalmegacorp.com

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