nuttx-apps/examples/chrono/chrono_main.c
Xiang Xiao 12a5a5cce9 examples: Ignore the default action if app call sigwait
Because the configured signo may have the default action(e.g. SIGPIPE),
and then will generate the bad side effect before the caller wakeup.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2020-11-28 12:19:39 +01:00

473 lines
13 KiB
C

/****************************************************************************
* examples/chrono/chrono_main.c
*
* Copyright (C) 2008, 2011-2012 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <nuttx/config.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <nuttx/input/buttons.h>
#include <nuttx/lcd/slcd_ioctl.h>
#include <nuttx/lcd/slcd_codec.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define BUTTON_SIGNO 13
#define BUTTON_STACKSIZE 2048
#define BUTTON_PRIORITY 100
#define BUTTON_DEVPATH "/dev/buttons"
#define SLCD_DEVNAME "/dev/slcd0"
#define SLCD_BUFSIZE 8
/****************************************************************************
* Private Types
****************************************************************************/
/* What is the current chronometer status? */
enum chronostate_e
{
CHRONO_RESETED = 0,
CHRONO_RUNNING,
CHRONO_STOPPED,
};
/* All state information for this test is kept within the following structure
* in order create a namespace and to minimize the possibility of name
* collisions.
*
* NOTE: stream must be the first element of struct slcd_chrono_s to support
* casting between these two types.
*/
struct slcd_chrono_s
{
struct lib_outstream_s stream; /* Stream to use for all output */
struct slcd_attributes_s attr; /* Size of the SLCD (rows x columns) */
int fd; /* File descriptor or the open SLCD device */
bool initialized; /* TRUE: Initialized */
struct timespec ts_start; /* Store the initial time */
struct timespec ts_end; /* Store the final time */
int state;
/* The I/O buffer */
uint8_t buffer[SLCD_BUFSIZE];
};
/****************************************************************************
* Private Data
****************************************************************************/
static struct slcd_chrono_s g_slcd;
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: chrono_daemon
****************************************************************************/
static int chrono_daemon(int argc, char *argv[])
{
FAR struct slcd_chrono_s *priv = &g_slcd;
struct btn_notify_s btnevents;
btn_buttonset_t supported;
int ret;
int fd;
int i;
UNUSED(i);
printf("chrono_daemon: Running\n");
/* Open the BUTTON driver */
printf("chrono_daemon: Opening %s\n", BUTTON_DEVPATH);
fd = open(BUTTON_DEVPATH, O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
int errcode = errno;
printf("chrono_daemon: ERROR: Failed to open %s: %d\n",
BUTTON_DEVPATH, errcode);
goto errout;
}
/* Get the set of BUTTONs supported */
ret = ioctl(fd, BTNIOC_SUPPORTED,
(unsigned long)((uintptr_t)&supported));
if (ret < 0)
{
int errcode = errno;
printf("chrono_daemon: ERROR: ioctl(BTNIOC_SUPPORTED) failed: %d\n",
errcode);
goto errout_with_fd;
}
printf("chrono_daemon: Supported BUTTONs 0x%02x\n",
(unsigned int)supported);
/* Define the notifications events */
btnevents.bn_press = supported;
btnevents.bn_release = supported;
btnevents.bn_event.sigev_notify = SIGEV_SIGNAL;
btnevents.bn_event.sigev_signo = BUTTON_SIGNO;
/* Register to receive a signal when buttons are pressed/released */
ret = ioctl(fd, BTNIOC_REGISTER,
(unsigned long)((uintptr_t)&btnevents));
if (ret < 0)
{
int errcode = errno;
printf("chrono_daemon: ERROR: ioctl(BTNIOC_SUPPORTED) failed: %d\n",
errcode);
goto errout_with_fd;
}
/* Ignore the default signal action */
signal(BUTTON_SIGNO, SIG_IGN);
/* Now loop forever, waiting BUTTONs events */
for (; ; )
{
struct siginfo value;
sigset_t set;
/* Wait for a signal */
sigemptyset(&set);
sigaddset(&set, BUTTON_SIGNO);
ret = sigwaitinfo(&set, &value);
if (ret < 0)
{
int errcode = errno;
printf("chrono_daemon: ERROR: sigwaitinfo() failed: %d\n",
errcode);
goto errout_with_fd;
}
if (priv->state == CHRONO_STOPPED)
{
clock_gettime(CLOCK_MONOTONIC, &priv->ts_start);
priv->state = CHRONO_RUNNING;
}
else
{
priv->state = CHRONO_STOPPED;
}
/* Make sure that everything is displayed */
fflush(stdout);
usleep(1000);
}
errout_with_fd:
close(fd);
errout:
printf("chrono_daemon: Terminating\n");
return EXIT_FAILURE;
}
/****************************************************************************
* Name: slcd_flush
****************************************************************************/
static int slcd_flush(FAR struct lib_outstream_s *stream)
{
FAR struct slcd_chrono_s *priv = (FAR struct slcd_chrono_s *)stream;
FAR const uint8_t *buffer;
ssize_t nwritten;
ssize_t remaining;
/* Loop until all bytes were written (handling both partial and interrupted
* writes).
*/
remaining = stream->nput;
buffer = priv->buffer;
while (remaining > 0)
{
nwritten = write(priv->fd, buffer, remaining);
if (nwritten < 0)
{
int errcode = errno;
printf("write failed: %d\n", errcode);
if (errcode != EINTR)
{
exit(EXIT_FAILURE);
}
}
else
{
remaining -= nwritten;
buffer += nwritten;
}
}
/* Reset the stream */
stream->nput = 0;
return OK;
}
/****************************************************************************
* Name: slcd_putc
****************************************************************************/
static void slcd_putc(FAR struct lib_outstream_s *stream, int ch)
{
FAR struct slcd_chrono_s *priv = (FAR struct slcd_chrono_s *)stream;
/* Write the character to the buffer */
priv->buffer[stream->nput] = ch;
stream->nput++;
priv->buffer[stream->nput] = '\0';
/* If the buffer is full, flush it */
if (stream->nput >= CONFIG_EXAMPLES_SLCD_BUFSIZE)
{
slcd_flush(stream);
}
}
/****************************************************************************
* Name: slcd_puts
****************************************************************************/
static void slcd_puts(FAR struct lib_outstream_s *outstream,
FAR const char *str)
{
for (; *str; str++)
{
slcd_put((int)*str, outstream);
}
}
/****************************************************************************
* chrono_main
****************************************************************************/
int main(int argc, FAR char *argv[])
{
FAR struct slcd_chrono_s *priv = &g_slcd;
FAR char str[8] = "00:00.0";
int fd;
int ret;
long sec;
long min;
/* Create a thread to wait for the button events */
ret = task_create("chrono_daemon", BUTTON_PRIORITY,
BUTTON_STACKSIZE, chrono_daemon, NULL);
if (ret < 0)
{
int errcode = errno;
printf("buttons_main: ERROR: Failed to start chrono_daemon: %d\n",
errcode);
return EXIT_FAILURE;
}
/* Open the SLCD device */
printf("Opening %s for read/write access\n", SLCD_DEVNAME);
fd = open(SLCD_DEVNAME, O_RDWR);
if (fd < 0)
{
printf("Failed to open %s: %d\n", SLCD_DEVNAME, errno);
goto errout;
}
/* Get the attributes of the SCLD device */
ret = ioctl(fd, SLCDIOC_GETATTRIBUTES, (unsigned long)&priv->attr);
if (ret < 0)
{
printf("ioctl(SLCDIOC_GETATTRIBUTES) failed: %d\n", errno);
goto errout_with_fd;
}
/* Are we already initialized? */
if (!priv->initialized)
{
/* Initialize the output stream */
memset(priv, 0, sizeof(struct slcd_chrono_s));
priv->stream.put = slcd_putc;
#ifdef CONFIG_STDIO_LINEBUFFER
priv->stream.flush = slcd_flush;
#endif
/* Get the attributes of the SCLD device */
ret = ioctl(fd, SLCDIOC_GETATTRIBUTES, (unsigned long)&priv->attr);
if (ret < 0)
{
printf("ioctl(SLCDIOC_GETATTRIBUTES) failed: %d\n", errno);
goto errout_with_fd;
}
printf("Attributes:\n");
printf(" rows: %d columns: %d nbars: %d\n",
priv->attr.nrows, priv->attr.ncolumns, priv->attr.nbars);
printf(" max contrast: %d max brightness: %d\n",
priv->attr.maxcontrast, priv->attr.maxbrightness);
/* Home the cursor and clear the display */
printf("Clear screen\n");
slcd_encode(SLCDCODE_CLEAR, 0, &priv->stream);
slcd_flush(&priv->stream);
priv->initialized = true;
}
/* Save the file descriptor in a place where slcd_flush can find it */
priv->fd = fd;
/* Set the cursor to the beginning of the current row by homing the cursor
* then going down as necessary, and erase to the end of the line.
*/
slcd_encode(SLCDCODE_HOME, 0, &priv->stream);
slcd_encode(SLCDCODE_ERASEEOL, 0, &priv->stream);
priv->state = CHRONO_RESETED;
for (; ; )
{
/* If the device is reset, show 00.. and assume it is initial time */
if (priv->state == CHRONO_RESETED)
{
/* Copy the initial value */
strncpy(str, "00:00.0", 7);
/* Print the initial reset value */
slcd_puts(&priv->stream, str);
slcd_flush(&priv->stream);
/* Move to stopped state to avoid wasting time here */
priv->state = CHRONO_STOPPED;
}
/* If the device is stopped nothing to do here */
if (priv->state == CHRONO_STOPPED)
{
}
/* If the chronometer is running, get the elapsed time and how it */
if (priv->state == CHRONO_RUNNING)
{
/* Get the current time */
clock_gettime(CLOCK_MONOTONIC, &priv->ts_end);
/* How many seconds passed from initial time? */
sec = priv->ts_end.tv_sec - priv->ts_start.tv_sec;
/* Get the amount of minutes */
min = sec / 60;
/* Get the remaining seconds */
sec = sec % 60;
sprintf(str, "%02ld:%02ld:%01ld", min, sec,
(priv->ts_end.tv_nsec / 100000000));
/* Print it into LCD */
slcd_encode(SLCDCODE_HOME, 0, &priv->stream);
slcd_puts(&priv->stream, str);
slcd_flush(&priv->stream);
}
/* usleep(1000); */
}
/* Normal exit */
close(fd);
return 0;
errout_with_fd:
close(priv->fd);
errout:
priv->initialized = false;
exit(EXIT_FAILURE);
}