/* * リアルタイムクロックドライバ * テストプログラム日本語版 * * * Copyright (C) 2002, Takahiko KAWASAKI. * * * Paul Gortmaker さんが書かれた 'Real Time * Clock Driver Test/Example Program' を、 * 日本語用に読みやすく書きかえたものです。 * * 更新割込み、アラーム、周期割込み、の例は、 * それぞれ、update_interrupt_test(), * alarm_test(), periodic_interrupt_test() * にあります。 * * GNU General Public License, version 2 で * ライセンスされます。 */ /* * [ 2.4.17 時点の include/linux/rtc.h より ] * * RTC_AIE_ON アラーム割込みを有効にする * RTC_AIE_OFF アラーム割込みを無効にする * RTC_UIE_ON 更新割込みを有効にする * RTC_UIE_OFF 更新割込みを無効にする * RTC_PIE_ON 周期割込みを有効にする * RTC_PIE_OFF 周期割込みを無効にする * RTC_WIE_ON ウォッチドッグ割込みを有効にする * RTC_WIE_OFF ウォッチドッグ割込みを有効にする * * RTC_ARM_SET アラーム時刻をセットする * RTC_ARM_READ アラーム時刻を読み出す * RTC_RD_TIME RTC 時刻を読み出す * RTC_SET_TIME RTC 時刻をセットする * RTC_IRQP_READ IRQ 周波数を読み出す * RTC_IRQP_SET IRQ 周波数をセットする * RTC_EPOCH_READ エポック時刻を読み出す * RTC_EPOCH_SET エポック時刻をセットする * * RTC_WKALM_SET 起床アラームをセットする * RTC_WKALM_RD 起床アラームを取得する */ /**************************************** ヘッダファイル ****************************************/ #include #include #include #include #include #include #include #include #include #include /**************************************** static 関数プロトタイプ宣言 ****************************************/ static void do_tests(void); static void perror_exit(const char *msg); static void print_msg(const char *fmt, ...); /* 更新割込みテスト */ static void update_interrupt_test(void); static void count_by_reading(void); static void count_by_selecting(void); /* アラームテスト */ static void alarm_test(void); static void set_alarm(int sec); static void read_current_rtc(struct rtc_time *rtc_tm); static void add_sec_to_rtc_time(struct rtc_time *rtc_tm, int sec); /* 周期割込みテスト */ static void print_periodic_irq_rate(void); static void periodic_interrupt_test(void); static void count_periodic_interrupt(void); static void count_one_pirq(unsigned long pirq_rate); /* ラッパー関数 */ static int Open(const char *path, int flags); static int Ioctl(int fd, int request, void *arg); static ssize_t Read(int fd, void *buf, size_t count); static int Select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tout); /**************************************** static 変数 ****************************************/ static unsigned long m_irqcount; /* 割込み発生回数 */ static int m_fd; /* /dev/rtc ファイル記述子 */ /************************************************** main() **************************************************/ int main(void) { /************************************************************ テスト開始メッセージ ************************************************************/ print_msg("\n\t\t\tRTC ドライバテスト\n\n"); /************************************************************ 各種テストを実行する。 ************************************************************/ do_tests(); /************************************************************ テスト終了メッセージ ************************************************************/ print_msg("\n\n\t\t\t *** テスト完了 ***\n\n"); print_msg( "IRQ 8 に %lu 以上のイベントが追加されていることを\n" "'cat /proc/interrupts' で確認することができます。\n\n", m_irqcount ); return EXIT_SUCCESS; } /************************************************** do_tests() **************************************************/ static void do_tests(void) { /************************************************************ RTC のデバイスファイル '/dev/rtc' をオープンする。 ************************************************************/ m_fd = Open("/dev/rtc", O_RDONLY); /************************************************************ 更新割込みテスト ************************************************************/ update_interrupt_test(); /************************************************************ アラームテスト ************************************************************/ alarm_test(); /************************************************************ 周期割込みテスト ************************************************************/ periodic_interrupt_test(); /************************************************************ RTC のデバイスファイル '/dev/rtc' をクローズする。 ************************************************************/ close(m_fd); } /************************************************** perror_exit() **************************************************/ static void perror_exit(const char *msg) { perror(msg); exit(errno); } /************************************************** print_msg() **************************************************/ static void print_msg(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); /************************************************************ stderr はオープン時にはバッファリングされていないので、 わざわざ fflush(stderr) は実行しない。 ************************************************************/ va_end(ap); } /* * * * 更新割込みテスト * * */ /************************************************** update_interrupt_test() **************************************************/ static void update_interrupt_test(void) { print_msg("更新割込みテスト\n"); /************************************************************ 一秒間に一回のペースで更新割込みを発生させるようにする。 ************************************************************/ Ioctl(m_fd, RTC_UIE_ON, 0); /************************************************************ /dev/rtc を read することで RTC の値を読み出す。 ************************************************************/ count_by_reading(); /************************************************************ /dev/rtc が読出し可能であることを select で確認してから、 /dev/rtc を read し、RTC の値を読み出す。 ************************************************************/ count_by_selecting(); /************************************************************ 更新割込みの発生をやめる。 ************************************************************/ Ioctl(m_fd, RTC_UIE_OFF, 0); } /************************************************** count_by_reading() **************************************************/ static void count_by_reading(void) { int i; unsigned long data; print_msg( "\t* 更新割込み (1回/秒) を /dev/rtc を read することにより\n" "\t 5 回数えます:" ); /************************************************************ RTC を 5 回読み出す。 ************************************************************/ for ( i = 1; i < 6; i++ ) { /************************************************************ RTC を読み出す。 ************************************************************/ Read(m_fd, &data, sizeof(unsigned long)); print_msg(" %d", i); /* 割込み回数をカウントする */ m_irqcount++; } } /************************************************** count_by_selecting() **************************************************/ static void count_by_selecting(void) { int i; unsigned long data; struct timeval tv; fd_set readfds; print_msg( "\n\n\t* 同じテストですが、read を実行する前に /dev/rtc が読込み\n" "\t 可能であるかどうかを select(2) で調べます。更新割込みを\n" "\t 5 回数えます:" ); /************************************************************ RTC を 5 回読み出す。 ************************************************************/ for ( i = 1; i < 6; i++ ) { /************************************************************ select のタイムアウトを 5 秒に設定する。 ************************************************************/ tv.tv_sec = 5; tv.tv_usec = 0; /************************************************************ m_fd が読込み可能になったかどうかを調べるための準備をする。 ************************************************************/ FD_ZERO(&readfds); FD_SET(m_fd, &readfds); /************************************************************ select は、更新割込みが発生するまで待つ (ただし 5 秒たって も割込みが発生しなければタイムアウトする) 。 ************************************************************/ Select(m_fd+1, &readfds, NULL, NULL, &tv); /************************************************************ 前の例とは異なり、次の read はブロックしない。 ************************************************************/ Read(m_fd, &data, sizeof(unsigned long)); print_msg(" %d", i); /* 割込み回数をカウントする */ m_irqcount++; } } /* * * * アラームテスト * * */ /************************************************** alarm_test() **************************************************/ static void alarm_test(void) { unsigned long data; print_msg("\n\nアラームテスト\n"); /************************************************************ 5 秒後にアラームが発生するように設定する。 ************************************************************/ set_alarm(5); print_msg("\t* アラームが発生するまで 5 秒間待ちます..."); /************************************************************ RTC の値を読み出す。 ************************************************************/ Read(m_fd, &data, sizeof(unsigned long)); print_msg(" 成功。\n\t アラームが鳴りました。\n"); /* 割込み回数をカウントする */ m_irqcount++; /************************************************************ アラーム割込みを無効にする。 ************************************************************/ Ioctl(m_fd, RTC_AIE_OFF, 0); } /************************************************** set_alarm() **************************************************/ static void set_alarm(int sec) { struct rtc_time rtc_tm; /************************************************************ 現在の RTC 時刻を読み出して rtc_tm にセットする。 ************************************************************/ read_current_rtc(&rtc_tm); /************************************************************ rtc_tm の値を sec 秒進める。 ************************************************************/ add_sec_to_rtc_time(&rtc_tm, sec); /************************************************************ sec 秒後にアラームが発生するように設定する。 ************************************************************/ Ioctl(m_fd, RTC_ALM_SET, &rtc_tm); /************************************************************ たった今設定したアラームの設定値を読み出す。 ************************************************************/ Ioctl(m_fd, RTC_ALM_READ, &rtc_tm); /************************************************************ 設定されているアラームの情報を表示する。 ************************************************************/ print_msg("\t* アラーム時刻が '%02d 時 %02d 分 %02d 秒' に設定されました。\n", rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec ); /************************************************************ アラーム割込みを有効にする。 ************************************************************/ Ioctl(m_fd, RTC_AIE_ON, 0); } /************************************************** read_current_rtc() **************************************************/ static void read_current_rtc(struct rtc_time *rtc_tm) { /************************************************************ 現在の RTC 時刻を読み出して rtc_tm にセットする。 ************************************************************/ Ioctl(m_fd, RTC_RD_TIME, rtc_tm); /************************************************************ 現在の RTC 時刻を表示する。 ************************************************************/ print_msg("\t* RTC 現在時刻: %d 年 %d 月 %d 日 %02d 時 %02d 分 %02d 秒\n", rtc_tm->tm_year + 1900, rtc_tm->tm_mon + 1, rtc_tm->tm_mday, rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec ); } /************************************************** add_sec_to_rtc_time() **************************************************/ static void add_sec_to_rtc_time(struct rtc_time *rtc_tm, int sec) { rtc_tm->tm_sec += sec; if ( rtc_tm->tm_sec >= 60 ) { rtc_tm->tm_sec %= 60; rtc_tm->tm_min++; } if ( rtc_tm->tm_min == 60 ) { rtc_tm->tm_min = 0; rtc_tm->tm_hour++; } if ( rtc_tm->tm_hour == 24 ) { rtc_tm->tm_hour = 0; } } /* * * * 周期割込みテスト * * */ /************************************************** periodic_interrupt_test() **************************************************/ static void periodic_interrupt_test(void) { print_msg("\n周期割込みテスト\n"); /************************************************************ 周期 IRQ レートを表示する。 ************************************************************/ print_periodic_irq_rate(); /************************************************************ 周期 IRQ レートを変化させながら、RTC の読込みテストを行う。 ************************************************************/ count_periodic_interrupt(); } /************************************************** print_periodic_irq_rate() **************************************************/ static void print_periodic_irq_rate(void) { unsigned long pirq_rate; /************************************************************ 周期 IRQ レートを読み出す。 ************************************************************/ Ioctl(m_fd, RTC_IRQP_READ, &pirq_rate); /************************************************************ 周期 IRQ レートを表示する。 ************************************************************/ print_msg("\t* 周期 IRQ レートは %luHz でした。\n", pirq_rate); } /************************************************** count_periodic_interrupt() **************************************************/ static void count_periodic_interrupt(void) { unsigned long pirq_rate; unsigned long rate_max; print_msg("\t* 周波数を変化させながら RTC 読取りテストを実行します。\n"); /************************************************************ テストする周波数の最大値を決める。このプロセスの実効ユーザ ID が root の場合は、8192Hz までテストする。root ではない 場合は、64Hz までのテストとする。 ************************************************************/ if ( geteuid() == 0 ) { rate_max = 8192; /* 8192Hz */ } else { rate_max = 64; /* 64Hz */ } /************************************************************ 周波数を 2Hz, 4Hz, 8Hz, 16Hz, 32Hz, 64Hz に順次変えて テストをおこなう。128Hz, 256Hz, ... 8192Hz は、root のみに許されている。 ************************************************************/ for ( pirq_rate = 2; pirq_rate <= rate_max; pirq_rate *= 2 ) { count_one_pirq(pirq_rate); } } /************************************************** count_one_pirq() **************************************************/ static void count_one_pirq(unsigned long pirq_rate) { int i; unsigned long data; /************************************************************ RTC の周波数を設定する。 ************************************************************/ Ioctl(m_fd, RTC_IRQP_SET, (void *)pirq_rate); print_msg("\n\t%4luHz:\t", pirq_rate); /************************************************************ 周期割込みを有効にする。 ************************************************************/ Ioctl(m_fd, RTC_PIE_ON, 0); /************************************************************ 20 回 RTC を読み出す。 ************************************************************/ for ( i = 1; i < 21; i++ ) { /************************************************************ RTC を読み出す。 ************************************************************/ Read(m_fd, &data, sizeof(unsigned long)); print_msg(" %d", i); /* 割込み回数をカウントする */ m_irqcount++; } /************************************************************ 周期割込みを無効にする。 ************************************************************/ Ioctl(m_fd, RTC_PIE_OFF, 0); } /* * * * ラッパー関数 * * */ /************************************************** Open() **************************************************/ static int Open(const char *path, int flags) { int fd; fd = open(path, flags); if ( fd == -1 ) { perror_exit("open"); } return fd; } /************************************************** Ioctl() **************************************************/ static int Ioctl(int fd, int request, void *arg) { int ret; ret = ioctl(fd, request, arg); if ( ret == -1 ) { perror_exit("ioctl"); } return ret; } /************************************************** Read() **************************************************/ static ssize_t Read(int fd, void *buf, size_t count) { ssize_t n; n = read(fd, buf, count); if ( n == -1 ) { perror_exit("read"); } return n; } /************************************************** Select() **************************************************/ static int Select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tout) { int ret; ret = select(n, rfds, wfds, efds, tout); if ( ret == -1 ) { perror_exit("select"); } return ret; }