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

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

unix.superglobalmegacorp.com

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