1 /* alarmtimer suspend test 2 * John Stultz (john.stultz@linaro.org) 3 * (C) Copyright Linaro 2013 4 * Licensed under the GPLv2 5 * 6 * This test makes sure the alarmtimer & RTC wakeup code is 7 * functioning. 8 * 9 * To build: 10 * $ gcc alarmtimer-suspend.c -o alarmtimer-suspend -lrt 11 * 12 * This program is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation, either version 2 of the License, or 15 * (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 */ 22 23 24 #include <stdio.h> 25 #include <unistd.h> 26 #include <time.h> 27 #include <string.h> 28 #include <signal.h> 29 #include <stdlib.h> 30 #include <pthread.h> 31 #include "../kselftest.h" 32 33 #define CLOCK_REALTIME 0 34 #define CLOCK_MONOTONIC 1 35 #define CLOCK_PROCESS_CPUTIME_ID 2 36 #define CLOCK_THREAD_CPUTIME_ID 3 37 #define CLOCK_MONOTONIC_RAW 4 38 #define CLOCK_REALTIME_COARSE 5 39 #define CLOCK_MONOTONIC_COARSE 6 40 #define CLOCK_BOOTTIME 7 41 #define CLOCK_REALTIME_ALARM 8 42 #define CLOCK_BOOTTIME_ALARM 9 43 #define CLOCK_HWSPECIFIC 10 44 #define CLOCK_TAI 11 45 #define NR_CLOCKIDS 12 46 47 48 #define NSEC_PER_SEC 1000000000ULL 49 #define UNREASONABLE_LAT (NSEC_PER_SEC * 5) /* hopefully we resume in 5 secs */ 50 51 #define SUSPEND_SECS 15 52 int alarmcount; 53 int alarm_clock_id; 54 struct timespec start_time; 55 56 57 char *clockstring(int clockid) 58 { 59 switch (clockid) { 60 case CLOCK_REALTIME: 61 return "CLOCK_REALTIME"; 62 case CLOCK_MONOTONIC: 63 return "CLOCK_MONOTONIC"; 64 case CLOCK_PROCESS_CPUTIME_ID: 65 return "CLOCK_PROCESS_CPUTIME_ID"; 66 case CLOCK_THREAD_CPUTIME_ID: 67 return "CLOCK_THREAD_CPUTIME_ID"; 68 case CLOCK_MONOTONIC_RAW: 69 return "CLOCK_MONOTONIC_RAW"; 70 case CLOCK_REALTIME_COARSE: 71 return "CLOCK_REALTIME_COARSE"; 72 case CLOCK_MONOTONIC_COARSE: 73 return "CLOCK_MONOTONIC_COARSE"; 74 case CLOCK_BOOTTIME: 75 return "CLOCK_BOOTTIME"; 76 case CLOCK_REALTIME_ALARM: 77 return "CLOCK_REALTIME_ALARM"; 78 case CLOCK_BOOTTIME_ALARM: 79 return "CLOCK_BOOTTIME_ALARM"; 80 case CLOCK_TAI: 81 return "CLOCK_TAI"; 82 } 83 return "UNKNOWN_CLOCKID"; 84 } 85 86 87 long long timespec_sub(struct timespec a, struct timespec b) 88 { 89 long long ret = NSEC_PER_SEC * b.tv_sec + b.tv_nsec; 90 91 ret -= NSEC_PER_SEC * a.tv_sec + a.tv_nsec; 92 return ret; 93 } 94 95 int final_ret = 0; 96 97 void sigalarm(int signo) 98 { 99 long long delta_ns; 100 struct timespec ts; 101 102 clock_gettime(alarm_clock_id, &ts); 103 alarmcount++; 104 105 delta_ns = timespec_sub(start_time, ts); 106 delta_ns -= NSEC_PER_SEC * SUSPEND_SECS * alarmcount; 107 108 printf("ALARM(%i): %ld:%ld latency: %lld ns ", alarmcount, ts.tv_sec, 109 ts.tv_nsec, delta_ns); 110 111 if (delta_ns > UNREASONABLE_LAT) { 112 printf("[FAIL]\n"); 113 final_ret = -1; 114 } else 115 printf("[OK]\n"); 116 117 } 118 119 int main(void) 120 { 121 timer_t tm1; 122 struct itimerspec its1, its2; 123 struct sigevent se; 124 struct sigaction act; 125 int signum = SIGRTMAX; 126 127 /* Set up signal handler: */ 128 sigfillset(&act.sa_mask); 129 act.sa_flags = 0; 130 act.sa_handler = sigalarm; 131 sigaction(signum, &act, NULL); 132 133 /* Set up timer: */ 134 memset(&se, 0, sizeof(se)); 135 se.sigev_notify = SIGEV_SIGNAL; 136 se.sigev_signo = signum; 137 se.sigev_value.sival_int = 0; 138 139 for (alarm_clock_id = CLOCK_REALTIME_ALARM; 140 alarm_clock_id <= CLOCK_BOOTTIME_ALARM; 141 alarm_clock_id++) { 142 143 alarmcount = 0; 144 if (timer_create(alarm_clock_id, &se, &tm1) == -1) { 145 printf("timer_create failed, %s unsupported?\n", 146 clockstring(alarm_clock_id)); 147 break; 148 } 149 150 clock_gettime(alarm_clock_id, &start_time); 151 printf("Start time (%s): %ld:%ld\n", clockstring(alarm_clock_id), 152 start_time.tv_sec, start_time.tv_nsec); 153 printf("Setting alarm for every %i seconds\n", SUSPEND_SECS); 154 its1.it_value = start_time; 155 its1.it_value.tv_sec += SUSPEND_SECS; 156 its1.it_interval.tv_sec = SUSPEND_SECS; 157 its1.it_interval.tv_nsec = 0; 158 159 timer_settime(tm1, TIMER_ABSTIME, &its1, &its2); 160 161 while (alarmcount < 5) 162 sleep(1); /* First 5 alarms, do nothing */ 163 164 printf("Starting suspend loops\n"); 165 while (alarmcount < 10) { 166 int ret; 167 168 sleep(3); 169 ret = system("echo mem > /sys/power/state"); 170 if (ret) 171 break; 172 } 173 timer_delete(tm1); 174 } 175 if (final_ret) 176 return ksft_exit_fail(); 177 return ksft_exit_pass(); 178 } 179