#include <string.h>
#include <semaphore.h>
#include <fcntl.h>
+#include <signal.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
/* This structure describes the state of the upper half driver */
struct timer_upperhalf_s {
- uint8_t crefs; /* The number of times the device has been opened */
-//sem_t exclsem; /* Supports mutual exclusion */
- FAR char *path; /* Registration path */
+ uint8_t crefs; /* The number of times the device has been opened */
+ uint8_t signo; /* The signal number to use in the notification */
+ pid_t pid; /* The ID of the task/thread to receive the signal */
+ FAR void *arg; /* An argument to pass with the signal */
+ FAR char *path; /* Registration path */
/* The contained lower-half driver */
* Private Function Prototypes
****************************************************************************/
-static int timer_open(FAR struct file *filep);
-static int timer_close(FAR struct file *filep);
-static ssize_t timer_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
-static ssize_t timer_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
-static int timer_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+static bool timer_notifier(FAR uint32_t *next_interval_us, FAR void *arg);
+static int timer_open(FAR struct file *filep);
+static int timer_close(FAR struct file *filep);
+static ssize_t timer_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen);
+static ssize_t timer_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen);
+static int timer_ioctl(FAR struct file *filep, int cmd,
+ unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_timerops = {
- timer_open, /* open */
- timer_close, /* close */
- timer_read, /* read */
- timer_write, /* write */
- 0, /* seek */
- timer_ioctl /* ioctl */
+ timer_open, /* open */
+ timer_close, /* close */
+ timer_read, /* read */
+ timer_write, /* write */
+ NULL, /* seek */
+ timer_ioctl /* ioctl */
#ifndef CONFIG_DISABLE_POLL
- , 0 /* poll */
+ , NULL /* poll */
+#endif
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+ , NULL /* unlink */
#endif
};
* Private Functions
****************************************************************************/
-/************************************************************************************
+/****************************************************************************
+ * Name: timer_notifier
+ *
+ * Description:
+ * Notify the application via a signal when the timer interrupt occurs
+ *
+ * REVISIT: This function prototype is insufficient to support signaling
+ *
+ ****************************************************************************/
+
+static bool timer_notifier(FAR uint32_t *next_interval_us, FAR void *arg)
+{
+ FAR struct timer_upperhalf_s *upper = (FAR struct timer_upperhalf_s *)arg;
+#ifdef CONFIG_CAN_PASS_STRUCTS
+ union sigval value;
+#endif
+
+ DEBUGASSERT(upper != NULL);
+
+ /* Signal the waiter.. if there is one */
+
+#ifdef CONFIG_CAN_PASS_STRUCTS
+ value.sival_ptr = upper->arg;
+ (void)sigqueue(upper->pid, upper->signo, value);
+#else
+ (void)sigqueue(upper->pid, upper->signo, upper->arg);
+#endif
+
+ return true;
+}
+
+/****************************************************************************
* Name: timer_open
*
* Description:
* This function is called whenever the timer device is opened.
*
- ************************************************************************************/
+ ****************************************************************************/
static int timer_open(FAR struct file *filep)
{
- FAR struct inode *inode = filep->f_inode;
+ FAR struct inode *inode = filep->f_inode;
FAR struct timer_upperhalf_s *upper = inode->i_private;
- uint8_t tmp;
- int ret;
-
- tmrvdbg("crefs: %d\n", upper->crefs);
+ uint8_t tmp;
+ int ret;
- /* Get exclusive access to the device structures */
+ tmrinfo("crefs: %d\n", upper->crefs);
- ret = 1; //sem_wait(&upper->exclsem);
- if (ret < 0) {
- ret = -get_errno();
- goto errout;
- }
-
- /* Increment the count of references to the device. If this the first
- * time that the driver has been opened for this device, then initialize
- * the device.
+ /*
+ * Increment the count of references to the device. If this the first
+ * time that the driver has been opened for this device, then
+ * initialize the device.
*/
tmp = upper->crefs + 1;
/* More than 255 opens; uint8_t overflows to zero */
ret = -EMFILE;
- goto errout_with_sem;
+ goto errout;
}
/* Save the new open count */
upper->crefs = tmp;
ret = OK;
-errout_with_sem:
- /* sem_post(&upper->exclsem); */
-
errout:
return ret;
}
-/************************************************************************************
+/****************************************************************************
* Name: timer_close
*
* Description:
* This function is called when the timer device is closed.
*
- ************************************************************************************/
+ ****************************************************************************/
static int timer_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct timer_upperhalf_s *upper = inode->i_private;
- int ret;
-
- tmrvdbg("crefs: %d\n", upper->crefs);
-
- /* Get exclusive access to the device structures */
- ret = 1; //sem_wait(&upper->exclsem);
- if (ret < 0) {
- ret = -get_errno();
- goto errout;
- }
+ tmrinfo("crefs: %d\n", upper->crefs);
- /* Decrement the references to the driver. If the reference count will
- * decrement to 0, then uninitialize the driver.
+ /*
+ * Decrement the references to the driver. If the reference count
+ * will decrement to 0, then uninitialize the driver.
*/
if (upper->crefs > 0) {
upper->crefs--;
}
- //sem_post(&upper->exclsem);
- ret = OK;
-errout:
- return ret;
+ return OK;
}
-/************************************************************************************
+/****************************************************************************
* Name: timer_read
*
* Description:
* A dummy read method. This is provided only to satisfy the VFS layer.
*
- ************************************************************************************/
+ ****************************************************************************/
-static ssize_t timer_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+static ssize_t timer_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen)
{
- /* Return zero -- usually meaning end-of-file */
+ /* Return zero - usually meaning end-of-file */
return 0;
}
-/************************************************************************************
+/****************************************************************************
* Name: timer_write
*
* Description:
* A dummy write method. This is provided only to satisfy the VFS layer.
*
- ************************************************************************************/
+ ****************************************************************************/
-static ssize_t timer_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
+static ssize_t timer_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen)
{
return 0;
}
-/************************************************************************************
+/****************************************************************************
* Name: timer_ioctl
*
* Description:
* The standard ioctl method. This is where ALL of the timer work is
* done.
*
- ************************************************************************************/
+ ****************************************************************************/
static int timer_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
- FAR struct inode *inode = filep->f_inode;
+ FAR struct inode *inode = filep->f_inode;
FAR struct timer_upperhalf_s *upper = inode->i_private;
FAR struct timer_lowerhalf_s *lower = upper->lower;
- int ret;
+ int ret;
- tmrvdbg("cmd: %d arg: %ld\n", cmd, arg);
+ tmrinfo("cmd: %d arg: %ld\n", cmd, arg);
DEBUGASSERT(upper && lower);
- /* Get exclusive access to the device structures */
-
- ret = 1; //sem_wait(&upper->exclsem);
- if (ret < 0) {
- return ret;
- }
-
/* Handle built-in ioctl commands */
switch (cmd) {
- /* cmd: TCIOC_START
+
+ /*
+ * cmd: TCIOC_START
* Description: Start the timer
* Argument: Ignored
*/
}
break;
- /* cmd: TCIOC_STOP
+ /*
+ * cmd: TCIOC_STOP
* Description: Stop the timer
* Argument: Ignored
*/
case TCIOC_STOP: {
/* Stop the timer */
- if (lower->ops->start) {
+ if (lower->ops->stop) {
ret = lower->ops->stop(lower);
} else {
ret = -ENOSYS;
}
break;
- /* cmd: TCIOC_GETSTATUS
+ /*
+ * cmd: TCIOC_GETSTATUS
* Description: Get the status of the timer.
* Argument: A writeable pointer to struct timer_status_s.
*/
/* Get the current timer status */
- if (lower->ops->getstatus) { /* Optional */
+ if (lower->ops->getstatus) { /* Optional */
status = (FAR struct timer_status_s *)((uintptr_t)arg);
if (status) {
ret = lower->ops->getstatus(lower, status);
}
break;
- /* cmd: TCIOC_SETTIMEOUT
+ /*
+ * cmd: TCIOC_SETTIMEOUT
* Description: Reset the timeout to this value
* Argument: A 32-bit timeout value in microseconds.
*
}
break;
- /* cmd: TCIOC_SETHANDLER
- * Description: Call this handler on timeout
- * Argument: A pointer to struct timer_sethandler_s.
- *
- * NOTE: This ioctl cannot be support in the kernel build mode. In that
- * case direct callbacks from kernel space into user space is forbidden.
- */
+ /*
+ * cmd: TCIOC_NOTIFICATION
+ * Description: Notify application via a signal when the timer expires.
+ * Argument: signal number
+ *
+ * NOTE: This ioctl cannot be support in the kernel build mode. In
+ * that case direct callbacks from kernel space into user space is
+ * forbidden.
+ */
-#if !defined(CONFIG_BUILD_PROTECTED) && !defined(CONFIG_BUILD_KERNEL)
- case TCIOC_SETHANDLER: {
- FAR struct timer_sethandler_s *sethandler;
+ case TCIOC_NOTIFICATION: {
+ FAR struct timer_notify_s *notify =
+ (FAR struct timer_notify_s *)((uintptr_t)arg);
- /* Don't reset on timer timeout; instead, call this user
- * provider timeout handler. NOTE: Providing handler==NULL will
- * restore the reset behavior.
- */
+ if (notify != NULL) {
+ upper->signo = notify->signo;
+ upper->pid = notify->pid;
+ upper->arg = notify->arg;
- if (lower->ops->sethandler) { /* Optional */
- sethandler = (FAR struct timer_sethandler_s *)((uintptr_t)arg);
- if (sethandler) {
- sethandler->oldhandler = lower->ops->sethandler(lower, sethandler->newhandler);
- ret = OK;
- } else {
- ret = -EINVAL;
- }
+ ret = timer_setcallback((FAR void *)upper,
+ timer_notifier, upper);
} else {
- ret = -ENOSYS;
+ ret = -EINVAL;
}
- }
- break;
-#endif
+ }
+ break;
/* Any unrecognized IOCTL commands might be platform-specific ioctl commands */
default: {
- tmrvdbg("Forwarding unrecognized cmd: %d arg: %ld\n", cmd, arg);
+ tmrinfo("Forwarding unrecognized cmd: %d arg: %ld\n", cmd, arg);
- /* An ioctl commands that are not recognized by the "upper-half"
- * driver are forwarded to the lower half driver through this
- * method.
+ /*
+ * An ioctl commands that are not recognized by the
+ * "upper-half" driver are forwarded to the lower half driver
+ * through this method.
*/
- if (lower->ops->ioctl) { /* Optional */
+ if (lower->ops->ioctl) { /* Optional */
ret = lower->ops->ioctl(lower, cmd, arg);
} else {
ret = -ENOSYS;
break;
}
- //sem_post(&upper->exclsem);
return ret;
}
*
****************************************************************************/
-FAR void *timer_register(FAR const char *path, FAR struct timer_lowerhalf_s *lower)
+FAR void *timer_register(FAR const char *path,
+ FAR struct timer_lowerhalf_s *lower)
{
FAR struct timer_upperhalf_s *upper;
int ret;
DEBUGASSERT(path && lower);
- tmrvdbg("Entry: path=%s\n", path);
+ tmrinfo("Entry: path=%s\n", path);
/* Allocate the upper-half data structure */
upper = (FAR struct timer_upperhalf_s *)
kmm_zalloc(sizeof(struct timer_upperhalf_s));
if (!upper) {
- tmrdbg("Upper half allocation failed\n");
+ tmrerr("ERROR: Upper half allocation failed\n");
goto errout;
}
- /* Initialize the timer device structure (it was already zeroed
+ /*
+ * Initialize the timer device structure (it was already zeroed
* by kmm_zalloc()).
*/
- //sem_init(&upper->exclsem, 0, 1);
upper->lower = lower;
/* Copy the registration path */
upper->path = strdup(path);
if (!upper->path) {
- tmrdbg("Path allocation failed\n");
+ tmrerr("ERROR: Path allocation failed\n");
goto errout_with_upper;
}
ret = register_driver(path, &g_timerops, 0666, upper);
if (ret < 0) {
- tmrdbg("register_driver failed: %d\n", ret);
+ tmrerr("ERROR: register_driver failed: %d\n", ret);
goto errout_with_path;
}
kmm_free(upper->path);
errout_with_upper:
- //sem_destroy(&upper->exclsem);
kmm_free(upper);
errout:
/* Recover the pointer to the upper-half driver state */
upper = (FAR struct timer_upperhalf_s *)handle;
+ DEBUGASSERT(upper != NULL && upper->lower != NULL);
lower = upper->lower;
- DEBUGASSERT(upper && lower);
- tmrvdbg("Unregistering: %s\n", upper->path);
+ tmrinfo("Unregistering: %s\n", upper->path);
/* Disable the timer */
- DEBUGASSERT(lower->ops->stop); /* Required */
+ DEBUGASSERT(lower->ops->stop); /* Required */
(void)lower->ops->stop(lower);
/* Unregister the timer device */
/* Then free all of the driver resources */
kmm_free(upper->path);
- //sem_destroy(&upper->exclsem);
kmm_free(upper);
}
-#endif /* CONFIG_TIMER */
+/****************************************************************************
+ * Name: timer_setcallback
+ *
+ * Description:
+ * This function can be called to add a callback into driver-related code
+ * to handle timer expirations. This is a strictly OS internal interface
+ * and may NOT be used by application code.
+ *
+ * Input parameters:
+ * handle - This is the handle that was returned by timer_register()
+ * callback - The new timer interrupt callback
+ * arg - Argument to be provided with the callback
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+int timer_setcallback(FAR void *handle, tccb_t callback, FAR void *arg)
+{
+ FAR struct timer_upperhalf_s *upper;
+ FAR struct timer_lowerhalf_s *lower;
+
+ /* Recover the pointer to the upper-half driver state */
+
+ upper = (FAR struct timer_upperhalf_s *)handle;
+ DEBUGASSERT(upper != NULL && upper->lower != NULL);
+ lower = upper->lower;
+ DEBUGASSERT(lower->ops != NULL);
+
+ /* Check if the lower half driver supports the setcallback method */
+
+ if (lower->ops->setcallback != NULL) { /* Optional */
+ /* Yes.. Defer the handler attachment to the lower half driver */
+ lower->ops->setcallback(lower, callback, arg);
+ return OK;
+ }
+
+ return -ENOSYS;
+}
+#endif /* CONFIG_TIMER */
#include <tinyara/compiler.h>
#include <tinyara/irq.h>
#include <tinyara/fs/ioctl.h>
+#include <stdbool.h>
+#include <sys/types.h>
#ifdef CONFIG_TIMER
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
+
/* IOCTL Commands ***********************************************************/
/* The timer driver uses a standard character driver framework. However,
* since the timer driver is a device control interface and not a data
*
* These are detected and handled by the "upper half" timer driver.
*
- * TCIOC_START - Start the timer
- * Argument: Ignored
- * TCIOC_STOP - Stop the timer
- * Argument: Ignored
- * TCIOC_GETSTATUS - Get the status of the timer.
- * Argument: A writeable pointer to struct timer_status_s.
- * TCIOC_SETTIMEOUT - Reset the timer timeout to this value
- * Argument: A 32-bit timeout value in microseconds.
- * TCIOC_SETHANDLER - Call this handler on timer expiration
- * Argument: A pointer to struct timer_sethandler_s.
+ * TCIOC_START - Start the timer
+ * Argument: Ignored
+ * TCIOC_STOP - Stop the timer
+ * Argument: Ignored
+ * TCIOC_GETSTATUS - Get the status of the timer.
+ * Argument: A writeable pointer to struct timer_status_s.
+ * TCIOC_SETTIMEOUT - Reset the timer timeout to this value
+ * Argument: A 32-bit timeout value in microseconds.
+ * TCIOC_NOTIFICATION - Set up to notify an application via a signal when
+ * the timer expires.
+ * Argument: A read-only pointer to an instance of
+ * struct timer_notify_s.
*
* WARNING: May change TCIOC_SETTIMEOUT to pass pointer to 64bit nanoseconds
* or timespec structure.
* NOTE: The TCIOC_SETHANDLER ioctl cannot be supported in the kernel build
* mode. In that case direct callbacks from kernel space into user space is
* forbidden.
+ *
+ * NOTE: _TCIOC(0x0001) through _TCIOC(0x001f) are reserved for use by the
+ * timer driver to assure that the values are unique. Other timer drivers,
+ * such as the oneshot timer, must not use IOCTL commands in this numeric
+ * range.
*/
-#define TCIOC_START _TCIOC(0x001)
-#define TCIOC_STOP _TCIOC(0x002)
-#define TCIOC_GETSTATUS _TCIOC(0x003)
-#define TCIOC_SETTIMEOUT _TCIOC(0x004)
-#define TCIOC_SETHANDLER _TCIOC(0x005)
+#define TCIOC_START _TCIOC(0x0001)
+#define TCIOC_STOP _TCIOC(0x0002)
+#define TCIOC_GETSTATUS _TCIOC(0x0003)
+#define TCIOC_SETTIMEOUT _TCIOC(0x0004)
+#define TCIOC_NOTIFICATION _TCIOC(0x0005)
/* Bit Settings *************************************************************/
/* Bit settings for the struct timer_status_s flags field */
-#define TCFLAGS_ACTIVE (1 << 0) /* 1=The timer is running */
-#define TCFLAGS_HANDLER (1 << 1) /* 1=Call the user function when the
- * timer expires */
+#define TCFLAGS_ACTIVE (1 << 0) /* 1=The timer is running */
+#define TCFLAGS_HANDLER (1 << 1) /* 1=Call the user function when the
+ * timer expires */
/****************************************************************************
* Public Types
****************************************************************************/
-/* User function prototype. Returns true to reload the timer, and the
+/* Upper half callback prototype. Returns true to reload the timer, and the
* function can modify the next interval if desired.
*/
-typedef bool(*tccb_t)(FAR uint32_t *next_interval_us);
-
-/* This is the type of the argument passed to the TCIOC_SETHANDLER ioctl */
-
-struct timer_sethandler_s {
- CODE tccb_t newhandler; /* The new timer interrupt handler */
- CODE tccb_t oldhandler; /* The previous timer interrupt handler (if any) */
-};
+typedef CODE bool (*tccb_t)(FAR uint32_t *next_interval_us, FAR void *arg);
/* This is the type of the argument passed to the TCIOC_GETSTATUS ioctl and
* and returned by the "lower half" getstatus() method.
*/
struct timer_status_s {
- uint32_t flags; /* See TCFLAGS_* definitions above */
- uint32_t timeout; /* The current timeout setting (in microseconds) */
- uint32_t timeleft; /* Time left until the timer expiration
- * (in microseconds) */
+ uint32_t flags; /* See TCFLAGS_* definitions above */
+ uint32_t timeout; /* The current timeout setting (in microseconds) */
+ uint32_t timeleft; /* Time left until the timer expiration
+ * (in microseconds) */
+};
+
+/* This is the type of the argument passed to the TCIOC_NOTIFICATION ioctl */
+
+struct timer_notify_s
+{
+ FAR void *arg; /* An argument to pass with the signal */
+ pid_t pid; /* The ID of the task/thread to receive the signal */
+ uint8_t signo; /* The signal number to use in the notification */
};
/* This structure provides the "lower-half" driver operations available to
struct timer_lowerhalf_s;
struct timer_ops_s {
- /* Required methods ******************************************************* */
+ /* Required methods *************************************************/
/* Start the timer, resetting the time to the current timeout */
CODE int (*start)(FAR struct timer_lowerhalf_s *lower);
/* Get the current timer status */
- CODE int (*getstatus)(FAR struct timer_lowerhalf_s *lower, FAR struct timer_status_s *status);
+ CODE int (*getstatus)(FAR struct timer_lowerhalf_s *lower,
+ FAR struct timer_status_s *status);
/* Set a new timeout value (and reset the timer) */
- CODE int (*settimeout)(FAR struct timer_lowerhalf_s *lower, uint32_t timeout);
+ CODE int (*settimeout)(FAR struct timer_lowerhalf_s *lower,
+ uint32_t timeout);
- /* Call this user provider timeout handler on timeout.
- * NOTE: Providing handler==NULL disable.
+ /* Call the TinyARA INTERNAL timeout callback on timeout.
+ * NOTE: Providing callback==NULL disable.
+ * NOT to call back into applications.
*/
- CODE tccb_t (*sethandler)(FAR struct timer_lowerhalf_s *lower, CODE tccb_t handler);
+ CODE void (*setcallback)(FAR struct timer_lowerhalf_s *lower,
+ CODE tccb_t callback, FAR void *arg);
- /* Any ioctl commands that are not recognized by the "upper-half" driver
- * are forwarded to the lower half driver through this method.
+ /* Any ioctl commands that are not recognized by the "upper-half"
+ * driver are forwarded to the lower half driver through this method.
*/
- CODE int (*ioctl)(FAR struct timer_lowerhalf_s *lower, int cmd, unsigned long arg);
+ CODE int (*ioctl)(FAR struct timer_lowerhalf_s *lower, int cmd,
+ unsigned long arg);
};
/* This structure provides the publicly visible representation of the
struct timer_lowerhalf_s {
/* Publicly visible portion of the "lower-half" driver state structure. */
- FAR const struct timer_ops_s *ops; /* Lower half operations */
+ FAR const struct timer_ops_s *ops; /* Lower half operations */
/* The remainder of the structure is used by the "lower-half" driver
* for whatever state storage that it may need.
#ifdef __cplusplus
#define EXTERN extern "C"
-extern "C" {
+extern "C"
+{
#else
#define EXTERN extern
#endif
*
****************************************************************************/
-FAR void *timer_register(FAR const char *path, FAR struct timer_lowerhalf_s *lower);
+FAR void *timer_register(FAR const char *path,
+ FAR struct timer_lowerhalf_s *lower);
/****************************************************************************
* Name: timer_unregister
void timer_unregister(FAR void *handle);
/****************************************************************************
+ * Kernel internal interfaces. These may not be used by application logic.
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: timer_setcallback
+ *
+ * Description:
+ * This function can be called to add a callback into driver-related code
+ * to handle timer expirations. This is a strictly OS internal interface
+ * and may NOT be used by application code.
+ *
+ * Input parameters:
+ * handle - This is the handle that was returned by timer_register()
+ * callback - The new timer interrupt callback
+ * arg - Argument provided when the callback is called.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef __KERNEL__
+int timer_setcallback(FAR void *handle, tccb_t callback, FAR void *arg);
+#endif
+
+/****************************************************************************
* Platform-Independent "Lower-Half" Timer Driver Interfaces
****************************************************************************/
}
#endif
-#endif /* CONFIG_TIMER */
-#endif /* __INCLUDE_TIMER_H */
+#endif /* CONFIG_TIMER */
+#endif /* __INCLUDE_TIMER_H */