diff options
Diffstat (limited to 'nuttx/drivers/ramlog.c')
-rw-r--r-- | nuttx/drivers/ramlog.c | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/nuttx/drivers/ramlog.c b/nuttx/drivers/ramlog.c new file mode 100644 index 000000000..79f57636c --- /dev/null +++ b/nuttx/drivers/ramlog.c @@ -0,0 +1,590 @@ +/**************************************************************************** + * drivers/ramlog.c + * + * Copyright (C) 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/types.h> +#include <sys/ioctl.h> + +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <poll.h> +#include <errno.h> +#include <assert.h> +#include <debug.h> + +#include <nuttx/fs.h> +#include <nuttx/kmalloc.h> +#include <nuttx/fs.h> +#include <nuttx/arch.h> +//#include <nuttx/ramlog.h> + +#include <arch/irq.h> + +#ifdef CONFIG_RAMLOG + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* Configuration ************************************************************/ +/* CONFIG_RAMLOG - Enables the RAM logging feature + * CONFIG_RAMLOG_CONSOLE - Use the RAM logging device as a system console. + * CONFIG_RAMLOG_SYSLOG - Use the RAM logging device for the syslogging + * interface. This should have: + * CONFIG_SYSLOG=ramlog + * CONFIG_RAMLOG_NPOLLWAITERS - The number of threads than can be waiting + * for this driver on poll(). Default: 4 + */ + +#ifndef CONFIG_RAMLOG_NPOLLWAITERS +# define CONFIG_RAMLOG_NPOLLWAITERS 4 +#endif + +#ifndef CONFIG_SYSLOG +# undef CONFIG_RAMLOG_SYSLOG +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ramlog_dev_s +{ + volatile uint8_t rl_nwaiters; /* Number of threads waiting for data */ + volatile uint16_t rl_head; /* The head index (where data is added) */ + volatile uint16_t rl_tail; /* The tail index (where data is remove) */ + sem_t rl_exclsem; /* Enforces mutually exclusive access */ + sem_t rl_waitsem; /* Used to wait for data */ + size_t rl_bufsize; /* Size of the RAM buffer */ + FAR char *rl_buffer; /* Circular RAM buffer */ + + /* The following is a list if poll structures of threads waiting for + * driver events. The 'struct pollfd' reference for each open is also + * retained in the f_priv field of the 'struct file'. + */ + +#ifndef CONFIG_DISABLE_POLL + struct pollfd *rl_fds[CONFIG_RAMLOG_NPOLLWAITERS]; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static ssize_t ramlog_read(FAR struct file *, FAR char *, size_t); +static ssize_t ramlog_write(FAR struct file *, FAR const char *, size_t); +#ifndef CONFIG_DISABLE_POLL +static int ramlog_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_ramlogfops = +{ + 0, /* open */ + 0, /* close */ + ramlog_read, /* read */ + ramlog_write, /* write */ + 0, /* seek */ + 0 /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , ramlog_poll /* poll */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ramlog_pollnotify + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static void ramlog_pollnotify(FAR struct ramlog_dev_s *priv, pollevent_t eventset) +{ + FAR struct pollfd *fds; + irqstate_t flags; + int i; + + /* This function may be called from an interrupt handler */ + + for (i = 0; i < CONFIG_RAMLOG_NPOLLWAITERS; i++) + { + flags = irqsave(); + fds = priv->rl_fds[i]; + if (fds) + { + fds->revents |= (fds->events & eventset); + if (fds->revents != 0) + { + sem_post(fds->sem); + } + } + irqrestore(flags); + } +} +#else +# define ramlog_pollnotify(priv,event) +#endif + +/**************************************************************************** + * Name: ramlog_read + ****************************************************************************/ + +static ssize_t ramlog_read(FAR struct file *filep, FAR char *buffer, size_t len) +{ + struct inode *inode = filep->f_inode; + struct ramlog_dev_s *priv; + ssize_t nread; + char ch; + int ret; + + /* Some sanity checking */ + + DEBUGASSERT(inode && inode->i_private); + priv = inode->i_private; + + /* If the circular buffer is empty, then wait for something to be written + * to it. This function may NOT be called from an interrupt handler. + */ + + DEBUGASSERT(!up_interrupt_context()); + + /* Get exclusive access to the rl_tail index */ + + ret = sem_wait(&priv->rl_exclsem); + if (ret < 0) + { + return ret; + } + + /* Loop until something is read */ + + for (nread = 0; nread < len; ) + { + /* Get the next byte from the buffer */ + + if (priv->rl_head == priv->rl_tail) + { + /* The circular buffer is empty. Did we read anything? */ + + if (nread > 0) + { + /* Yes.. re-enable interrupts and the break out to return what + * we have. + */ + + break; + } + + /* If the driver was opened with O_NONBLOCK option, then don't wait. + * Re-enable interrupts and return EGAIN. + */ + + if (filep->f_oflags & O_NONBLOCK) + { + nread = -EAGAIN; + break; + } + + /* Otherwise, wait for something to be written to the circular + * buffer. Increment the number of waiters so that the ramlog_write() + * will not that it needs to post the semaphore to wake us up. + */ + + sched_lock(); + priv->rl_nwaiters++; + sem_post(&priv->rl_exclsem); + + /* We may now be pre-empted! But that should be okay because we + * have already incremented nwaiters. Pre-emptions is disabled + * but will be re-enabled while we are waiting. + */ + + ret = sem_wait(&priv->rl_waitsem); + + /* Interrupts will be disabled when we return. So the decrementing + * rl_nwaiters here is safe. + */ + + priv->rl_nwaiters--; + sched_unlock(); + + /* Did we successfully get the rl_waitsem? */ + + if (ret >= 0) + { + /* Yes... then retake the mutual exclusion semaphore */ + + ret = sem_wait(&priv->rl_exclsem); + } + + /* Was the semaphore wait successful? Did we successful re-take the + * mutual exclusion semaphore? + */ + + if (ret < 0) + { + /* No.. One of the two sem_wait's failed. */ + + int errval = errno; + + /* Were we awakened by a signal? Did we read anything before + * we received the signal? + */ + + if (errval != EINTR || nread >= 0) + { + /* Yes.. return the error. */ + + nread = -errval; + } + + /* Break out to return what we have. Note, we can't exactly + * "break" out because whichever error occurred, we do not hold + * the exclusion semaphore. + */ + + goto errout_without_sem; + } + } + else + { + /* The circular buffer is not empty, get the next byte from the + * tail index. + */ + + ch = priv->rl_buffer[priv->rl_tail]; + + /* Increment the tail index and re-enable interrupts */ + + if (++priv->rl_tail >= priv->rl_bufsize) + { + priv->rl_tail = 0; + } + + /* Add the character to the user buffer */ + + buffer[nread] = ch; + nread++; + } + } + + /* Relinquish the mutual exclusion semaphore */ + + sem_post(&priv->rl_exclsem); + + /* Notify all poll/select waiters that they can write to the FIFO */ + +errout_without_sem: + if (nread > 0) + { + ramlog_pollnotify(priv, POLLOUT); + } + return nread; +} + +/**************************************************************************** + * Name: ramlog_write + ****************************************************************************/ + +static ssize_t ramlog_write(FAR struct file *filep, FAR const char *buffer, size_t len) +{ + struct inode *inode = filep->f_inode; + struct ramlog_dev_s *priv; + irqstate_t flags; + ssize_t nwritten; + int nexthead; + int i; + + /* Some sanity checking */ + + DEBUGASSERT(inode && inode->i_private); + priv = inode->i_private; + + /* Loop until all of the bytes have been written. This function may be + * called from an interrupt handler! Semaphores cannot be used! + * + * The write logic only needs to modify the rl_head index. Therefore, + * there is a difference in the way that rl_head and rl_tail are protected: + * rl_tail is protected with a semaphore; rl_tail is protected by disabling + * interrupts. + */ + + for (nwritten = 0; nwritten < len; nwritten++) + { + /* Disable interrupts (in case we are NOT called from interrupt handler) */ + + flags = irqsave(); + + /* Calculate the write index AFTER the next byte is written */ + + nexthead = priv->rl_head + 1; + if (nexthead >= priv->rl_bufsize) + { + nexthead = 0; + } + + /* Would the next write overflow the circular buffer? */ + + if (nexthead == priv->rl_tail) + { + /* Yes... then break out of the loop to return the number of bytes + * written. The data to be written is dropped on the floor. + */ + + return nwritten; + } + + /* No... copy the byte and re-enable interrupts */ + + priv->rl_buffer[priv->rl_head] = buffer[nwritten]; + priv->rl_head = nexthead; + irqrestore(flags); + } + + /* Was anything written? */ + + if (nwritten > 0) + { + /* Are there threads waiting for read data? */ + + flags = irqsave(); + for (i = 0; i < priv->rl_nwaiters; i++) + { + /* Yes.. Notify all of the waiting readers that more data is available */ + + sem_post(&priv->rl_waitsem); + } + + /* Notify all poll/select waiters that they can write to the FIFO */ + + ramlog_pollnotify(priv, POLLIN); + } + + /* Return the number of bytes written */ + + return nwritten; +} + +/**************************************************************************** + * Name: ramlog_poll + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +int ramlog_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct ramlog_dev_s *priv; + pollevent_t eventset; + int ndx; + int ret; + int i; + + /* Some sanity checking */ + + DEBUGASSERT(inode && inode->i_private); + priv = inode->i_private; + + /* Get exclusive access to the poll structures */ + + ret = sem_wait(&priv->rl_exclsem); + if (ret < 0) + { + int errval = errno; + return -errval; + } + + /* Are we setting up the poll? Or tearing it down? */ + + if (setup) + { + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference + */ + + for (i = 0; i < CONFIG_RAMLOG_NPOLLWAITERS; i++) + { + /* Find an available slot */ + + if (!priv->rl_fds[i]) + { + /* Bind the poll structure and this slot */ + + priv->rl_fds[i] = fds; + fds->priv = &priv->rl_fds[i]; + break; + } + } + + if (i >= CONFIG_RAMLOG_NPOLLWAITERS) + { + fds->priv = NULL; + ret = -EBUSY; + goto errout; + } + + /* Should immediately notify on any of the requested events? + * First, check if the xmit buffer is full. + */ + + eventset = 0; + + ndx = priv->rl_head + 1; + if (ndx >= priv->rl_bufsize) + { + ndx = 0; + } + + if (ndx != priv->rl_tail) + { + eventset |= POLLOUT; + } + + /* Check if the receive buffer is empty */ + + if (priv->rl_head != priv->rl_tail) + { + eventset |= POLLIN; + } + + if (eventset) + { + ramlog_pollnotify(priv, eventset); + } + + } + else if (fds->priv) + { + /* This is a request to tear down the poll. */ + + struct pollfd **slot = (struct pollfd **)fds->priv; + +#ifdef CONFIG_DEBUG + if (!slot) + { + ret = -EIO; + goto errout; + } +#endif + + /* Remove all memory of the poll setup */ + + *slot = NULL; + fds->priv = NULL; + } + +errout: + sem_post(&priv->rl_exclsem); + return ret; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ramlog_register + * + * Description: + * Create the RAM logging device and register it at the specified path. + * Mostly likely this path will be /dev/console + * + ****************************************************************************/ + +int ramlog_register(FAR const char *devpath, FAR char *buffer, size_t buflen) +{ + FAR struct ramlog_dev_s *priv; + int ret = -ENOMEM; + + /* Sanity checking */ + + DEBUGASSERT(devpath && buffer && buflen > 1); + + /* Allocate a RAM logging device structure */ + + priv = (struct ramlog_dev_s *)kzalloc(sizeof(struct ramlog_dev_s)); + if (priv) + { + /* Initialize the non-zero values in the RAM logging device structure */ + + sem_init(&priv->rl_exclsem, 0, 1); + sem_init(&priv->rl_waitsem, 0, 0); + priv->rl_bufsize = buflen; + priv->rl_buffer = buffer; + + /* Register the character driver */ + + ret = register_driver(devpath, &g_ramlogfops, 0666, priv); + if (ret < 0) + { + kfree(priv); + } + } + + return ret; +} + +/**************************************************************************** + * Name: ramlog + * + * Description: + * This is the low-level system logging interface. The debugging/syslogging + * interfaces are lib_rawprintf() and lib_lowprinf(). The difference is + * the lib_rawprintf() writes to fd=1 (stdout) and lib_lowprintf() uses + * a lower level interface that works from interrupt handlers. This + * function is a a low-level interface used to implement lib_lowprintf() + * when CONFIG_RAMLOG_SYSLOG=y and CONFIG_SYSLOG=ramlog + * + ****************************************************************************/ + +#ifdef CONFIG_RAMLOG_SYSLOG +# warning "Missing logic" +#endif + +#endif /* CONFIG_RAMLOG */ |