/**************************************************************************** * examples/alarm/alarm_main.c * * Copyright (C) 2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * Private Data ****************************************************************************/ static bool g_alarm_daemon_started; static pid_t g_alarm_daemon_pid; static bool g_alarm_received[CONFIG_RTC_NALARMS]; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: alarm_handler ****************************************************************************/ static void alarm_handler(int signo, FAR siginfo_t *info, FAR void *ucontext) { int almndx = info->si_value.sival_int; if (almndx >= 0 && almndx < CONFIG_RTC_NALARMS) { g_alarm_received[almndx] = true; } } /**************************************************************************** * Name: alarm_daemon ****************************************************************************/ static int alarm_daemon(int argc, FAR char *argv[]) { struct sigaction act; sigset_t set; int ret; int i; /* Indicate that we are running */ g_alarm_daemon_started = true; printf("alarm_daemon: Running\n"); /* Make sure that the alarm signal is unmasked */ sigemptyset(&set); sigaddset(&set, CONFIG_EXAMPLES_ALARM_SIGNO); ret = sigprocmask(SIG_UNBLOCK, &set, NULL); if (ret != OK) { int errcode = errno; fprintf(stderr, "ERROR: sigprocmask failed: %d\n", errcode); goto errout; } /* Register alarm signal handler */ act.sa_sigaction = alarm_handler; act.sa_flags = SA_SIGINFO; sigfillset(&act.sa_mask); sigdelset(&act.sa_mask, CONFIG_EXAMPLES_ALARM_SIGNO); ret = sigaction(CONFIG_EXAMPLES_ALARM_SIGNO, &act, NULL); if (ret < 0) { int errcode = errno; fprintf(stderr, "ERROR: sigaction failed: %d\n", errcode); goto errout; } /* Now loop forever, waiting for alarm signals */ for (; ; ) { /* Check if any alarms fired. * * NOTE that there are race conditions here... if we missing an alarm, * we will just report it a half second late. */ for (i = 0; i < CONFIG_RTC_NALARMS; i++) { if (g_alarm_received[i]) { printf("alarm_demon: alarm %d received\n", i) ; g_alarm_received[i] = false; } } /* Now wait a little while and poll again. If a signal is received * this should cause us to awken earlier. */ usleep(500*1000L); } errout: g_alarm_daemon_started = false; printf("alarm_daemon: Terminating\n"); return EXIT_FAILURE; } /**************************************************************************** * Name: start_daemon ****************************************************************************/ static int start_daemon(void) { if (!g_alarm_daemon_started) { g_alarm_daemon_pid = task_create("alarm_daemon", CONFIG_EXAMPLES_ALARM_PRIORITY, CONFIG_EXAMPLES_ALARM_STACKSIZE, alarm_daemon, NULL); if (g_alarm_daemon_pid < 0) { int errcode = errno; fprintf(stderr, "ERROR: Failed to start alarm_daemon: %d\n", errcode); return -errcode; } printf("alarm_daemon started\n"); usleep(500*1000L); } return OK; } /**************************************************************************** * Name: show_usage ****************************************************************************/ static void show_usage(FAR const char *progname) { fprintf(stderr, "USAGE:\n"); 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 (default: 0)\n", CONFIG_RTC_NALARMS - 1); 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"); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * alarm_main ****************************************************************************/ int main(int argc, FAR char *argv[]) { unsigned long seconds = 0; bool badarg = false; bool readmode = false; bool cancelmode = false; bool setmode; int opt; int alarmid = 0; int fd; int ret; /* Make sure that the alarm daemon is running */ ret = start_daemon(); if (ret < 0) { 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 (setmode) { /* Get the number of seconds until the alarm expiration */ seconds = strtoul(argv[optind], 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 */ printf("Opening %s\n", CONFIG_EXAMPLES_ALARM_DEVPATH); fd = open(CONFIG_EXAMPLES_ALARM_DEVPATH, O_WRONLY); if (fd < 0) { int errcode = errno; fprintf(stderr, "ERROR: Failed to open %s: %d\n", CONFIG_EXAMPLES_ALARM_DEVPATH, errcode); return EXIT_FAILURE; } if (readmode) { struct rtc_rdalarm_s rd = { 0 }; long timeleft; time_t now; rd.id = alarmid; time(&now); 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); 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); } 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); 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.pid = g_alarm_daemon_pid; setrel.reltime = (time_t)seconds; setrel.event.sigev_notify = SIGEV_SIGNAL; setrel.event.sigev_signo = CONFIG_EXAMPLES_ALARM_SIGNO; setrel.event.sigev_value.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); close(fd); return EXIT_FAILURE; } printf("Alarm %d set in %lu seconds\n", alarmid, seconds); } close(fd); return EXIT_SUCCESS; }