From 63e086d1f76627693613daf98e426a1efadcc5be Mon Sep 17 00:00:00 2001 From: Juha Niskanen Date: Tue, 17 Oct 2017 16:43:55 +0000 Subject: [PATCH] Merged in juniskane/apps_upstream/examples_alarm_pr (pull request #112) examples/alarm: add options for reading alarm value and canceling it Approved-by: Gregory Nutt --- examples/alarm/alarm_main.c | 236 ++++++++++++++++++++++++++---------- 1 file changed, 174 insertions(+), 62 deletions(-) diff --git a/examples/alarm/alarm_main.c b/examples/alarm/alarm_main.c index 3d8b7a4b2..b50600277 100644 --- a/examples/alarm/alarm_main.c +++ b/examples/alarm/alarm_main.c @@ -55,7 +55,7 @@ ****************************************************************************/ static bool g_alarm_daemon_started; -static pid_t g_alarm_daeon_pid; +static pid_t g_alarm_daemon_pid; static bool g_alarm_received[CONFIG_RTC_NALARMS]; /**************************************************************************** @@ -162,11 +162,11 @@ static int start_daemon(void) { if (!g_alarm_daemon_started) { - g_alarm_daeon_pid = + g_alarm_daemon_pid = task_create("alarm_daemon", CONFIG_EXAMPLES_ALARM_PRIORITY, CONFIG_EXAMPLES_ALARM_STACKSIZE, alarm_daemon, NULL); - if (g_alarm_daeon_pid < 0) + if (g_alarm_daemon_pid < 0) { int errcode = errno; fprintf(stderr, "ERROR: Failed to start alarm_daemon: %d\n", @@ -188,18 +188,16 @@ static int start_daemon(void) static void show_usage(FAR const char *progname) { fprintf(stderr, "USAGE:\n"); -#if CONFIG_RTC_NALARMS > 1 - fprintf(stderr, "\t%s [-a ] \n", progname); + fprintf(stderr, "\t%s [-a ] [-cr] []\n", progname); fprintf(stderr, "Where:\n"); fprintf(stderr, "\t-a \n"); - fprintf(stderr, "\t\t selects the alarm: 0..%d\n", + fprintf(stderr, "\t\t selects the alarm: 0..%d (default: 0)\n", CONFIG_RTC_NALARMS - 1); -#else - fprintf(stderr, "\t%s \n", progname); - fprintf(stderr, "Where:\n"); -#endif + fprintf(stderr, "\t-c\tCancel previously set alarm\n"); + fprintf(stderr, "\t-r\tRead previously set alarm\n"); fprintf(stderr, "\t\n"); fprintf(stderr, "\t\tThe number of seconds until the alarm expires.\n"); + fprintf(stderr, "\t\t(only if no -c or -r option given.)\n"); } /**************************************************************************** @@ -216,10 +214,13 @@ int main(int argc, FAR char *argv[]) int alarm_main(int argc, FAR char *argv[]) #endif { - struct rtc_setrelative_s setrel; - unsigned long seconds; + unsigned long seconds = 0; + bool badarg = false; + bool readmode = false; + bool cancelmode = false; + bool setmode; + int opt; int alarmid = 0; - int secndx; int fd; int ret; @@ -228,47 +229,74 @@ int alarm_main(int argc, FAR char *argv[]) ret = start_daemon(); if (ret < 0) { - fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[1]); + return EXIT_FAILURE; + } + + /* Parse commandline parameters. NOTE: getopt() is not thread safe nor re-entrant. + * To keep its state proper for the next usage, it is necessary to parse to + * the end of the line even if an error occurs. If an error occurs, this + * logic just sets 'badarg' and continues. + */ + + while ((opt = getopt(argc, argv, ":a:cr")) != ERROR) + { + switch (opt) + { + case 'a': /* -a: Select alarm id */ + alarmid = strtol(optarg, NULL, 0); + if (alarmid < 0 || alarmid >= CONFIG_RTC_NALARMS) + { + badarg = true; + } + break; + case 'c': + cancelmode = true; + break; + case 'r': + readmode = true; + break; + default: + fprintf(stderr, "\n\n", opt); + /* fall through */ + case '?': + case ':': + badarg = true; + } + } + + /* Both -r and -c can be given at the same time, setting is exclusive. */ + + setmode = !readmode && !cancelmode; + + if (setmode) + { + if (optind >= argc) + { + badarg = true; + } + } + else if (optind < argc) + { + badarg = true; + } + + if (badarg) + { show_usage(argv[0]); return EXIT_FAILURE; } -#if CONFIG_RTC_NALARMS > 1 - /* Check for an optional alarm ID */ - - if (argc == 4) + if (setmode) { - if (strcmp(argv[1], "-a") != 0) + /* Get the number of seconds until the alarm expiration */ + + seconds = strtoul(argv[optind], NULL, 10); + if (seconds < 1) { - fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[1]); + fprintf(stderr, "ERROR: Invalid number of seconds: %lu\n", seconds); show_usage(argv[0]); return EXIT_FAILURE; } - - alarmid = atoi(argv[2]); - secndx = 3; - } - else -#endif - if (argc == 2) - { - secndx = 1; - } - else - { - fprintf(stderr, "ERROR: Invalid number of arguments: %d\n", argc - 1); - show_usage(argv[0]); - return EXIT_FAILURE; - } - - /* Get the number of seconds until the alarm expiration */ - - seconds = strtoul(argv[secndx], NULL, 10); - if (seconds < 1) - { - fprintf(stderr, "ERROR: Invalid number of seconds: %lu\n", seconds); - show_usage(argv[0]); - return EXIT_FAILURE; } /* Open the RTC driver */ @@ -284,28 +312,112 @@ int alarm_main(int argc, FAR char *argv[]) return EXIT_FAILURE; } - /* Set the alarm */ - - setrel.id = alarmid; - setrel.signo = CONFIG_EXAMPLES_ALARM_SIGNO; - setrel.pid = g_alarm_daeon_pid; - setrel.reltime = (time_t)seconds; - - setrel.sigvalue.sival_int = alarmid; - - ret = ioctl(fd, RTC_SET_RELATIVE, (unsigned long)((uintptr_t)&setrel)); - if (ret < 0) + if (readmode) { - int errcode = errno; + struct rtc_rdalarm_s rd = { 0 }; + long timeleft; + time_t now; - fprintf(stderr, "ERROR: RTC_SET_RELATIVE ioctl failed: %d\n", - errcode); + rd.id = alarmid; + time(&now); - (void)close(fd); - return EXIT_FAILURE; + ret = ioctl(fd, RTC_RD_ALARM, (unsigned long)((uintptr_t)&rd)); + if (ret < 0) + { + int errcode = errno; + + fprintf(stderr, "ERROR: RTC_RD_ALARM ioctl failed: %d\n", + errcode); + + (void)close(fd); + return EXIT_FAILURE; + } + + /* Some of the NuttX RTC implementations do not support alarms + * longer than one month. There RTC_RD_ALARM can return partial + * calendar values without month and year fields. + * TODO: fix this in lower layers? + */ + + if (rd.time.tm_year > 0) + { + /* Normal sane case, assuming we did not actually request an + * alarm expiring in year 1900. + */ + + timeleft = mktime((struct tm *)&rd.time) - now; + } + else + { + struct tm now_tm; + + /* Periodic extend "partial" alarms by "unfolding" months, + * until we get alarm that is in future. Note that mktime() + * normalizes fields that are out of their valid values, + * so we don't have to handle carry to tm_year by ourselves. + */ + + gmtime_r(&now, &now_tm); + rd.time.tm_mon = now_tm.tm_mon; + rd.time.tm_year = now_tm.tm_year; + + do { + timeleft = mktime((struct tm *)&rd.time) - now; + if (timeleft < 0) + { + rd.time.tm_mon++; + } + } while (timeleft < 0); + } + + printf("Alarm %d is %s with %ld seconds to expiration\n", alarmid, + rd.active ? "active" : "inactive", timeleft); } - printf("Alarm %d set in %lu seconds\n", alarmid, seconds); + if (cancelmode) + { + ret = ioctl(fd, RTC_CANCEL_ALARM, (unsigned long)alarmid); + if (ret < 0) + { + int errcode = errno; + + fprintf(stderr, "ERROR: RTC_CANCEL_ALARM ioctl failed: %d\n", + errcode); + + (void)close(fd); + return EXIT_FAILURE; + } + + printf("Alarm %d has been canceled\n", alarmid); + } + + if (setmode) + { + struct rtc_setrelative_s setrel; + + /* Set the alarm */ + + setrel.id = alarmid; + setrel.signo = CONFIG_EXAMPLES_ALARM_SIGNO; + setrel.pid = g_alarm_daemon_pid; + setrel.reltime = (time_t)seconds; + + setrel.sigvalue.sival_int = alarmid; + + ret = ioctl(fd, RTC_SET_RELATIVE, (unsigned long)((uintptr_t)&setrel)); + if (ret < 0) + { + int errcode = errno; + + fprintf(stderr, "ERROR: RTC_SET_RELATIVE ioctl failed: %d\n", + errcode); + + (void)close(fd); + return EXIT_FAILURE; + } + + printf("Alarm %d set in %lu seconds\n", alarmid, seconds); + } (void)close(fd); return EXIT_SUCCESS;