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

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: 
                      7: #include "rtc.h"
1.1.1.6 ! root        8: #include "config.h"
1.1       root        9: #include <sys/time.h>
                     10: 
1.1.1.2   root       11: std::unique_ptr<RTCDevice> gRTC;
1.1       root       12: 
1.1.1.5   root       13: RTCDevice::RTCDevice(const std::string& objname_)
                     14:        : inherited(objname_)
1.1       root       15: {
                     16: }
                     17: 
                     18: RTCDevice::~RTCDevice()
                     19: {
                     20: }
                     21: 
1.1.1.4   root       22: void
                     23: RTCDevice::ResetHard()
                     24: {
                     25: }
                     26: 
1.1.1.6 ! root       27: bool
        !            28: RTCDevice::Init()
1.1       root       29: {
                     30:        struct timeval tv;
                     31:        struct tm tm;
                     32: 
1.1.1.6 ! root       33:        // TODO: year_epoch, use_localtime を設定可能にするべきかどうか
        !            34: 
        !            35:        // 時刻固定オプション (パフォーマンス測定用)
        !            36:        // パフォーマンス測定をする際に、RTC が実時間を返すと
        !            37:        // ゲストの状態が毎回異なることになるため、
        !            38:        // いつ何時も同じ値を返すことで、世界から隔絶された
        !            39:        // VM を作り、パフォーマンス測定を安定させる。
        !            40:        force_fixed = gConfig->Find(".rtc-force-fixed").AsInt();
        !            41:        if (force_fixed) {
        !            42:                putmsgn("force_fixed");
        !            43:                // 固定の時刻を設定
        !            44:                struct tm fixed {
        !            45:                        .tm_sec = 16,
        !            46:                        .tm_min = 22,
        !            47:                        .tm_hour = 13 - 9,
        !            48:                        .tm_mday = 27,
        !            49:                        .tm_mon = 7 - 1,
        !            50:                        .tm_year = 2018 - 1900,
        !            51:                };
        !            52:                tv.tv_sec = timegm(&fixed);
        !            53:                tv.tv_usec = 0;
        !            54:        } else {
        !            55:                // 起動した時の現在時刻で設定する
        !            56:                gettimeofday(&tv, NULL);
        !            57:        }
        !            58: 
1.1       root       59:        if (use_localtime) {
                     60:                localtime_r(&tv.tv_sec, &tm);
                     61:        } else {
                     62:                gmtime_r(&tv.tv_sec, &tm);
                     63:        }
1.1.1.6 ! root       64: 
        !            65:        // 日付、時刻の直接指定があればここで差し替える。
        !            66:        const ConfigItem item_date = gConfig->Find(".rtc-date");
        !            67:        std::string str_date = item_date.AsString();
        !            68:        if (!str_date.empty()) {
        !            69:                int yy, mm, dd, w;
        !            70:                if (ParseDate(str_date, &yy, &mm, &dd, &w) == false) {
        !            71:                        item_date.Err();
        !            72:                        return false;
        !            73:                }
        !            74:                tm.tm_year = yy - 1900;
        !            75:                tm.tm_mon = mm - 1;
        !            76:                tm.tm_mday = dd;
        !            77:                if (w != -1) {
        !            78:                        tm.tm_wday = w;
        !            79:                }
        !            80:        }
        !            81:        const ConfigItem item_time = gConfig->Find(".rtc-time");
        !            82:        std::string str_time = item_time.AsString();
        !            83:        if (!str_time.empty()) {
        !            84:                int hh, mm, ss;
        !            85:                if (ParseTime(str_time, &hh, &mm, &ss) == false) {
        !            86:                        item_time.Err();
        !            87:                        return false;
        !            88:                }
        !            89:                tm.tm_hour = hh;
        !            90:                tm.tm_min  = mm;
        !            91:                tm.tm_sec  = ss;
        !            92:        }
        !            93: 
1.1       root       94:        // 秒未満の初期値もそれなりの精度にしておく (1000*1000 / 32 = 31250)
1.1.1.6 ! root       95:        cnt = tv.tv_usec / 31250;
        !            96:        SetSec(tm.tm_sec);
        !            97:        SetMin(tm.tm_min);
        !            98:        SetHour(tm.tm_hour);
1.1       root       99:        // wday も tm_wday も日曜が 0
1.1.1.6 ! root      100:        SetWday(tm.tm_wday);
        !           101:        SetMday(tm.tm_mday);
        !           102:        // mon は 1 から、tm_mon は 0 から。
        !           103:        SetMon(tm.tm_mon + 1);
        !           104:        SetYear(tm.tm_year + 1900);
        !           105:        SetLeap(tm.tm_year & 3);
        !           106: 
        !           107:        return true;
1.1       root      108: }
                    109: 
1.1.1.6 ! root      110: // デバッグオプションの日付文字列を解析する。
        !           111: // 書式は "yyyy-mm-dd[-w]"。曜日は省略可能、区切り文字は1文字なら何でもいい。
        !           112: // 入力値の範囲チェックはしない。(出来れば 0A月みたいなのも受け付けたいが)
        !           113: bool
        !           114: RTCDevice::ParseDate(const std::string& str, int *y, int *m, int *d, int *w)
1.1       root      115: {
1.1.1.6 ! root      116:        const char *p = str.data();
        !           117:        char *end;
1.1       root      118: 
1.1.1.6 ! root      119:        errno = 0;
        !           120:        *y = strtoul(p, &end, 10);
        !           121:        if (end == p || *end == '\0' || errno != 0) {
        !           122:                return false;
        !           123:        }
1.1       root      124: 
1.1.1.6 ! root      125:        p = end + 1;
        !           126:        errno = 0;
        !           127:        *m = strtoul(p, &end, 10);
        !           128:        if (end == p || *end == '\0' || errno != 0) {
        !           129:                return false;
        !           130:        }
1.1       root      131: 
1.1.1.6 ! root      132:        p = end + 1;
        !           133:        errno = 0;
        !           134:        *d = strtoul(p, &end, 10);
        !           135:        if (end == p || errno != 0) {
        !           136:                return false;
1.1       root      137:        }
                    138: 
1.1.1.6 ! root      139:        if (*end == 0) {
        !           140:                // 曜日はなくても可
        !           141:                *w = -1;
        !           142:        } else {
        !           143:                // あれば取り出す
        !           144:                p = end + 1;
        !           145:                errno = 0;
        !           146:                *w = strtoul(p, &end, 10);
        !           147:                if (end == p || errno != 0) {
        !           148:                        return false;
        !           149:                }
1.1       root      150:        }
1.1.1.6 ! root      151: 
        !           152:        return true;
1.1       root      153: }
                    154: 
1.1.1.6 ! root      155: // デバッグオプションの時刻文字列を解析する。
        !           156: // 書式は "hh:mm:ss"。区切り文字は1文字なら何でもいい。
        !           157: // 入力値の範囲チェックはしない。
        !           158: bool
        !           159: RTCDevice::ParseTime(const std::string& str, int *h, int *m, int *s)
1.1       root      160: {
1.1.1.6 ! root      161:        const char *p = str.data();
        !           162:        char *end;
1.1       root      163: 
1.1.1.6 ! root      164:        errno = 0;
        !           165:        *h = strtoul(p, &end, 10);
        !           166:        if (end == p || *end == '\0' || errno != 0) {
        !           167:                return false;
1.1       root      168:        }
                    169: 
1.1.1.6 ! root      170:        p = end + 1;
        !           171:        errno = 0;
        !           172:        *m = strtoul(p, &end, 10);
        !           173:        if (end == p || *end == '\0' || errno != 0) {
        !           174:                return false;
        !           175:        }
1.1       root      176: 
1.1.1.6 ! root      177:        p = end + 1;
        !           178:        errno = 0;
        !           179:        *s = strtoul(p, &end, 10);
        !           180:        if (end == p || errno != 0) {
        !           181:                return false;
1.1       root      182:        }
1.1.1.6 ! root      183: 
        !           184:        return true;
1.1       root      185: }
                    186: 
1.1.1.6 ! root      187: // 32Hz のクロック入力。
        !           188: // スケジューラから呼び出される。
        !           189: // 本来の RTC は 32768Hz が入力クロックだが、
        !           190: // それを 1024 分周したものを基準クロック入力として考える。
        !           191: void
        !           192: RTCDevice::ClockIn()
1.1       root      193: {
1.1.1.6 ! root      194:        cnt++;
1.1       root      195: 
1.1.1.6 ! root      196:        Tick32Hz();
        !           197:        if ((cnt & 15) == 0) {
        !           198:                Tick2Hz();
        !           199:                if ((cnt & 31) == 0) {
        !           200:                        // 秒カウントアップは Tick1Hz が行う
        !           201:                        // 秒カウントアップ禁止を実装するため、
        !           202:                        // 基本クラスで直接秒カウントアップはしない。
        !           203:                        Tick1Hz();
        !           204:                }
1.1       root      205:        }
                    206: }
                    207: 
1.1.1.6 ! root      208: // 秒をカウントアップし、繰り上がりを処理。
        !           209: void
        !           210: RTCDevice::CountUpSec()
1.1       root      211: {
1.1.1.6 ! root      212:        uint v;
1.1       root      213: 
1.1.1.6 ! root      214:        if (__predict_false(force_fixed)) {
        !           215:                // 時刻固定モードならカウントアップしない
        !           216:                return;
        !           217:        }
1.1       root      218: 
1.1.1.6 ! root      219:        // BCD の桁ごとにキャリー処理をしてるはずだけど妥協。
        !           220:        // たぶん内部キャリーはビット途中のキャリーで作られていると
        !           221:        // 思うんだけど、ソフトでやるのは大変なので妥協。
        !           222: 
        !           223:        v = GetSec() + 1;
        !           224:        if (v < 60) {
        !           225:                SetSec(v);
        !           226:                return;
1.1       root      227:        }
1.1.1.6 ! root      228:        SetSec(0);
        !           229:        CountUpMin();
1.1       root      230: }
                    231: 
1.1.1.6 ! root      232: // 分をカウントアップし、繰り上がりを処理。
        !           233: void
        !           234: RTCDevice::CountUpMin()
1.1       root      235: {
1.1.1.6 ! root      236:        uint v;
1.1       root      237: 
1.1.1.6 ! root      238:        v = GetMin() + 1;
        !           239:        if (v < 60) {
        !           240:                SetMin(v);
        !           241:                return;
1.1       root      242:        }
1.1.1.6 ! root      243:        SetMin(0);
1.1       root      244: 
1.1.1.6 ! root      245:        v = GetHour() + 1;
        !           246:        if (v < 24) {
        !           247:                SetHour(v);
        !           248:                return;
        !           249:        }
        !           250:        SetHour(0);
        !           251: 
        !           252:        // 曜日は単純7進カウンタ
        !           253:        SetWday((GetWday() + 1) % 7);
1.1       root      254: 
1.1.1.6 ! root      255:        v = GetMday() + 1;
        !           256:        if (v <= GetLastMday()) {
        !           257:                SetMday(v);
        !           258:                return;
        !           259:        }
        !           260:        SetMday(1);
        !           261: 
        !           262:        v = GetMon() + 1;
        !           263:        if (v <= 12) {
        !           264:                SetMon(v);
        !           265:                return;
        !           266:        }
        !           267:        SetMon(1);
1.1       root      268: 
1.1.1.6 ! root      269:        SetLeap((GetLeap() + 1) % 4);
        !           270:        SetYear(GetYear() + 1);
1.1       root      271: }
                    272: 
1.1.1.6 ! root      273: // 現在の月の最終日を返す (月の繰り上がり処理用)
        !           274: int
        !           275: RTCDevice::GetLastMday() const
1.1       root      276: {
1.1.1.6 ! root      277:        // 下記の理由により 0 月が必要。値は適当。
        !           278:        static const int mdays[] = {
        !           279:                30, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
        !           280:        };
        !           281: 
        !           282:        // GetMon() は不正な範囲を取ると規定していることに注意。
        !           283:        // (0月、19月などが許されている)
        !           284:        // 範囲外の値に対するロジックは違うだろうが規定されていないので
        !           285:        // こっち都合で実装しておく。
        !           286:        // m は 0..12
        !           287:        int m = GetMon() % 13;
        !           288:        int lastday = mdays[m];
        !           289:        if (m == 2 && GetLeap() == 0) {
        !           290:                lastday++;
1.1       root      291:        }
1.1.1.6 ! root      292:        return lastday;
1.1       root      293: }
                    294: 
1.1.1.6 ! root      295: // 曜日文字列。
        !           296: // RP5C15、MK48T02 いずれも曜日カウンタと曜日の対応はデータシートに規定がなく
        !           297: // 運用依存であり、X680x0 (RP5C15) は 0..6 (0が日曜) としている。
        !           298: // LUNA (MK48T02) は運用上 0..7(0と7が日曜) となってしまっている。
1.1       root      299: /*static*/ const char * const
1.1.1.6 ! root      300: RTCDevice::wdays[8] = {
1.1       root      301:        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
1.1.1.6 ! root      302:        "Sun",
1.1       root      303: };

unix.superglobalmegacorp.com

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