timer driver: Use signal to notify of timer expiration. Add generic argument so that there can be additional usage.

This commit is contained in:
Sebastien Lorquet 2016-11-17 14:38:21 -06:00 committed by Gregory Nutt
parent 18ad40b98c
commit 197cec58d2
3 changed files with 98 additions and 123 deletions

View File

@ -3,9 +3,11 @@
*
* Copyright (C) 2015 Wail Khemir. All rights reserved.
* Copyright (C) 2015 Omni Hoverboards Inc. All rights reserved.
* Copyright (C) 2016 Sebastien Lorquet All rights reserved.
* Authors: Wail Khemir <khemirwail@gmail.com>
* Paul Alexander Patience <paul-a.patience@polymtl.ca>
* dev@ziggurat29.com
* Sebastien Lorquet <sebastien@lorquet.fr>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -91,7 +93,8 @@ struct stm32l4_lowerhalf_s
{
FAR const struct timer_ops_s *ops; /* Lower half operations */
FAR struct stm32l4_tim_dev_s *tim; /* stm32 timer driver */
tccb_t usrhandler; /* Current user interrupt handler */
tccb_t callback; /* Current upper half interrupt callback */
FAR void *arg; /* Argument passed to upper half callback */
const xcpt_t timhandler; /* Current timer interrupt handler */
bool started; /* True: Timer has been started */
const uint8_t resolution; /* Number of bits in the timer (16 or 32 bits) */
@ -145,8 +148,8 @@ static int stm32l4_start(FAR struct timer_lowerhalf_s *lower);
static int stm32l4_stop(FAR struct timer_lowerhalf_s *lower);
static int stm32l4_settimeout(FAR struct timer_lowerhalf_s *lower,
uint32_t timeout);
static tccb_t stm32l4_sethandler(FAR struct timer_lowerhalf_s *lower,
tccb_t handler);
static tccb_t stm32l4_setcallback(FAR struct timer_lowerhalf_s *lower,
tccb_t callback, FAR void *arg);
/****************************************************************************
* Private Data
@ -155,110 +158,110 @@ static tccb_t stm32l4_sethandler(FAR struct timer_lowerhalf_s *lower,
static const struct timer_ops_s g_timer_ops =
{
.start = stm32l4_start,
.stop = stm32l4_stop,
.getstatus = NULL,
.settimeout = stm32l4_settimeout,
.sethandler = stm32l4_sethandler,
.ioctl = NULL,
.start = stm32l4_start,
.stop = stm32l4_stop,
.getstatus = NULL,
.settimeout = stm32l4_settimeout,
.setcallback = stm32l4_setcallback,
.ioctl = NULL,
};
#ifdef CONFIG_STM32L4_TIM1
static struct stm32l4_lowerhalf_s g_tim1_lowerhalf =
{
.ops = &g_timer_ops,
.timhandler = stm32l4_tim1_interrupt,
.resolution = STM32L4_TIM1_RES,
.ops = &g_timer_ops,
.timhandler = stm32l4_tim1_interrupt,
.resolution = STM32L4_TIM1_RES,
};
#endif
#ifdef CONFIG_STM32L4_TIM2
static struct stm32l4_lowerhalf_s g_tim2_lowerhalf =
{
.ops = &g_timer_ops,
.timhandler = stm32l4_tim2_interrupt,
.resolution = STM32L4_TIM2_RES,
.ops = &g_timer_ops,
.timhandler = stm32l4_tim2_interrupt,
.resolution = STM32L4_TIM2_RES,
};
#endif
#ifdef CONFIG_STM32L4_TIM3
static struct stm32l4_lowerhalf_s g_tim3_lowerhalf =
{
.ops = &g_timer_ops,
.timhandler = stm32l4_tim3_interrupt,
.resolution = STM32L4_TIM3_RES,
.ops = &g_timer_ops,
.timhandler = stm32l4_tim3_interrupt,
.resolution = STM32L4_TIM3_RES,
};
#endif
#ifdef CONFIG_STM32L4_TIM4
static struct stm32l4_lowerhalf_s g_tim4_lowerhalf =
{
.ops = &g_timer_ops,
.timhandler = stm32l4_tim4_interrupt,
.resolution = STM32L4_TIM4_RES,
.ops = &g_timer_ops,
.timhandler = stm32l4_tim4_interrupt,
.resolution = STM32L4_TIM4_RES,
};
#endif
#ifdef CONFIG_STM32L4_TIM5
static struct stm32l4_lowerhalf_s g_tim5_lowerhalf =
{
.ops = &g_timer_ops,
.timhandler = stm32l4_tim5_interrupt,
.resolution = STM32L4_TIM5_RES,
.ops = &g_timer_ops,
.timhandler = stm32l4_tim5_interrupt,
.resolution = STM32L4_TIM5_RES,
};
#endif
#ifdef CONFIG_STM32L4_TIM6
static struct stm32l4_lowerhalf_s g_tim6_lowerhalf =
{
.ops = &g_timer_ops,
.timhandler = stm32l4_tim6_interrupt,
.resolution = STM32L4_TIM6_RES,
.ops = &g_timer_ops,
.timhandler = stm32l4_tim6_interrupt,
.resolution = STM32L4_TIM6_RES,
};
#endif
#ifdef CONFIG_STM32L4_TIM7
static struct stm32l4_lowerhalf_s g_tim7_lowerhalf =
{
.ops = &g_timer_ops,
.timhandler = stm32l4_tim7_interrupt,
.resolution = STM32L4_TIM7_RES,
.ops = &g_timer_ops,
.timhandler = stm32l4_tim7_interrupt,
.resolution = STM32L4_TIM7_RES,
};
#endif
#ifdef CONFIG_STM32L4_TIM8
static struct stm32l4_lowerhalf_s g_tim8_lowerhalf =
{
.ops = &g_timer_ops,
.timhandler = stm32l4_tim8_interrupt,
.resolution = STM32L4_TIM8_RES,
.ops = &g_timer_ops,
.timhandler = stm32l4_tim8_interrupt,
.resolution = STM32L4_TIM8_RES,
};
#endif
#ifdef CONFIG_STM32L4_TIM15
static struct stm32l4_lowerhalf_s g_tim15_lowerhalf =
{
.ops = &g_timer_ops,
.timhandler = stm32l4_tim15_interrupt,
.resolution = STM32L4_TIM15_RES,
.ops = &g_timer_ops,
.timhandler = stm32l4_tim15_interrupt,
.resolution = STM32L4_TIM15_RES,
};
#endif
#ifdef CONFIG_STM32L4_TIM16
static struct stm32l4_lowerhalf_s g_tim16_lowerhalf =
{
.ops = &g_timer_ops,
.timhandler = stm32l4_tim16_interrupt,
.resolution = STM32L4_TIM16_RES,
.ops = &g_timer_ops,
.timhandler = stm32l4_tim16_interrupt,
.resolution = STM32L4_TIM16_RES,
};
#endif
#ifdef CONFIG_STM32L4_TIM17
static struct stm32l4_lowerhalf_s g_tim17_lowerhalf =
{
.ops = &g_timer_ops,
.timhandler = stm32l4_tim17_interrupt,
.resolution = STM32L4_TIM17_RES,
.ops = &g_timer_ops,
.timhandler = stm32l4_tim17_interrupt,
.resolution = STM32L4_TIM17_RES,
};
#endif
@ -369,7 +372,7 @@ static int stm32l4_timer_handler(FAR struct stm32l4_lowerhalf_s *lower)
STM32L4_TIM_ACKINT(lower->tim, 0);
if (lower->usrhandler(&next_interval_us))
if (lower->callback(&next_interval_us, lower->arg))
{
if (next_interval_us > 0)
{
@ -407,7 +410,7 @@ static int stm32l4_start(FAR struct timer_lowerhalf_s *lower)
{
STM32L4_TIM_SETMODE(priv->tim, STM32L4_TIM_MODE_UP);
if (priv->usrhandler != NULL)
if (priv->callback != NULL)
{
STM32L4_TIM_SETISR(priv->tim, priv->timhandler, 0);
STM32L4_TIM_ENABLEINT(priv->tim, 0);
@ -505,11 +508,12 @@ static int stm32l4_settimeout(FAR struct timer_lowerhalf_s *lower,
* Call this user provided timeout handler.
*
* Input Parameters:
* lower - A pointer the publicly visible representation of the "lower-half"
* driver state structure.
* newhandler - The new timer expiration function pointer. If this
* function pointer is NULL, then the reset-on-expiration
* behavior is restored,
* lower - A pointer the publicly visible representation of the "lower-half"
* driver state structure.
* callback - The new timer expiration function pointer. If this
* function pointer is NULL, then the reset-on-expiration
* behavior is restored,
* arg - Argument that will be provided in the callback
*
* Returned Values:
* The previous timer expiration function pointer or NULL is there was
@ -517,22 +521,18 @@ static int stm32l4_settimeout(FAR struct timer_lowerhalf_s *lower,
*
****************************************************************************/
static tccb_t stm32l4_sethandler(FAR struct timer_lowerhalf_s *lower,
tccb_t newhandler)
static void stm32l4_setcallback(FAR struct timer_lowerhalf_s *lower,
tccb_t callback, FAR void *arg)
{
FAR struct stm32l4_lowerhalf_s *priv = (FAR struct stm32l4_lowerhalf_s *)lower;
irqstate_t flags = enter_critical_section();
/* Get the old handler return value */
/* Save the new callback */
tccb_t oldhandler = priv->usrhandler;
priv->callback = callback;
priv->arg = arg;
/* Save the new handler */
priv->usrhandler = newhandler;
if (newhandler != NULL && priv->started)
if (callback != NULL && priv->started)
{
STM32L4_TIM_SETISR(priv->tim, priv->timhandler, 0);
STM32L4_TIM_ENABLEINT(priv->tim, 0);
@ -544,7 +544,6 @@ static tccb_t stm32l4_sethandler(FAR struct timer_lowerhalf_s *lower,
}
leave_critical_section(flags);
return oldhandler;
}
/****************************************************************************
@ -636,9 +635,9 @@ int stm32l4_timer_initialize(FAR const char *devpath, int timer)
/* Initialize the elements of lower half state structure */
lower->started = false;
lower->usrhandler = NULL;
lower->tim = stm32l4_tim_init(timer);
lower->started = false;
lower->callback = NULL;
lower->tim = stm32l4_tim_init(timer);
if (lower->tim == NULL)
{

View File

@ -46,6 +46,7 @@
#include <string.h>
#include <semaphore.h>
#include <fcntl.h>
#include <signal.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
@ -66,11 +67,9 @@
struct timer_upperhalf_s
{
uint8_t crefs; /* The number of times the device has been opened */
#ifdef HAVE_NOTIFICATION
uint8_t signal; /* The signal number to use in the notification */
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 */
#endif
FAR char *path; /* Registration path */
/* The contained lower-half driver */
@ -82,12 +81,7 @@ struct timer_upperhalf_s
* Private Function Prototypes
****************************************************************************/
#ifdef HAVE_NOTIFICATION
/* REVISIT: This function prototype is insufficient to support signaling */
static bool timer_notifier(FAR uint32_t *next_interval_us);
#endif
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,
@ -131,15 +125,18 @@ static const struct file_operations g_timerops =
*
************************************************************************************/
#ifdef HAVE_NOTIFICATION
static bool timer_notifier(FAR uint32_t *next_interval_us)
static bool timer_notifier(FAR uint32_t *next_interval_us, FAR void *arg)
{
FAR struct timer_upperhalf_s *upper = HOW?;
FAR struct timer_upperhalf_s *upper = (FAR struct timer_upperhalf_s *)arg;
#ifdef CONFIG_CAN_PASS_STRUCTS
union sigval value;
#endif
int ret;
DEBUGASSERT(upper != NULL);
/* Signal the waiter.. if there is one */
#ifdef CONFIG_CAN_PASS_STRUCTS
value.sival_ptr = upper->arg;
ret = sigqueue(upper->pid, upper->signo, value);
@ -149,7 +146,6 @@ static bool timer_notifier(FAR uint32_t *next_interval_us)
return ret == OK;
}
#endif
/************************************************************************************
* Name: timer_open
@ -363,7 +359,6 @@ static int timer_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
}
break;
#ifdef HAVE_NOTIFICATION
/* cmd: TCIOC_NOTIFICATION
* Description: Notify application via a signal when the timer expires.
* Argument: signal number
@ -375,15 +370,15 @@ static int timer_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
case TCIOC_NOTIFICATION:
{
FAR struct timer_notify_s *notify =
(FAR struct timer_notify_s *)((uintptr_t)arg)
(FAR struct timer_notify_s *)((uintptr_t)arg);
if (notify != NULL)
{
upper->signo = notify->signal;
upper->get = notify->signal;
upper->arg = noify->arg;
upper->signo = notify->signo;
upper->pid = notify->pid;
upper->arg = notify->arg;
ret = timer_sethandler((FAR void *handle)upper, timer_notifier, NULL);
ret = timer_setcallback((FAR void *)upper, timer_notifier, upper);
}
else
{
@ -391,7 +386,6 @@ static int timer_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
}
}
break;
#endif
/* Any unrecognized IOCTL commands might be platform-specific ioctl commands */
@ -548,7 +542,7 @@ void timer_unregister(FAR void *handle)
}
/****************************************************************************
* Name: timer_sethandler
* Name: timer_setcallback
*
* Description:
* This function can be called to add a callback into driver-related code
@ -556,21 +550,20 @@ void timer_unregister(FAR void *handle)
* and may NOT be used by appliction code.
*
* Input parameters:
* handle - This is the handle that was returned by timer_register()
* newhandler - The new timer interrupt handler
* oldhandler - The previous timer interrupt handler (if any)
* handle - This is the handle that was returned by timer_register()
* newcallback - The new timer interrupt callback
* oldcallback - The previous timer interrupt callback (if any)
* arg - Argument to be provided with the callback
*
* Returned Value:
* None
*
****************************************************************************/
int timer_sethandler(FAR void *handle, tccb_t newhandler,
FAR tccb_t *oldhandler)
int timer_setcallback(FAR void *handle, tccb_t newcallback, FAR void *arg)
{
FAR struct timer_upperhalf_s *upper;
FAR struct timer_lowerhalf_s *lower;
tccb_t tmphandler;
/* Recover the pointer to the upper-half driver state */
@ -579,21 +572,13 @@ int timer_sethandler(FAR void *handle, tccb_t newhandler,
lower = upper->lower;
DEBUGASSERT(lower->ops != NULL);
/* Check if the lower half driver supports the sethandler method */
/* Check if the lower half driver supports the setcallback method */
if (lower->ops->sethandler != NULL) /* Optional */
if (lower->ops->setcallback != NULL) /* Optional */
{
/* Yes.. Defer the hander attachment to the lower half driver */
tmphandler = lower->ops->sethandler(lower, newhandler);
/* Return the oldhandler if a location to return it was provided */
if (oldhandler != NULL)
{
*oldhandler = tmphandler;
}
lower->ops->setcallback(lower, newcallback, arg);
return OK;
}

View File

@ -52,11 +52,6 @@
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* It would require some interface modifcations in order to support
* notifications.
*/
#undef HAVE_NOTIFICATION
/* IOCTL Commands ***********************************************************/
/* The timer driver uses a standard character driver framework. However,
@ -96,9 +91,7 @@
#define TCIOC_STOP _TCIOC(0x0002)
#define TCIOC_GETSTATUS _TCIOC(0x0003)
#define TCIOC_SETTIMEOUT _TCIOC(0x0004)
#ifdef HAVE_NOTIFICATION
#define TCIOC_NOTIFICATION _TCIOC(0x0005)
#endif
/* Bit Settings *************************************************************/
/* Bit settings for the struct timer_status_s flags field */
@ -111,11 +104,11 @@
* 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 CODE bool (*tccb_t)(FAR uint32_t *next_interval_us);
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.
@ -129,16 +122,14 @@ struct timer_status_s
* (in microseconds) */
};
#ifdef HAVE_NOTIFICATION
/* 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 signal; /* The signal number to use in the notification */
uint8_t signo; /* The signal number to use in the notification */
};
#endif
/* This structure provides the "lower-half" driver operations available to
* the "upper-half" driver.
@ -166,12 +157,13 @@ struct timer_ops_s
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 NuttX 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.
@ -272,7 +264,7 @@ void timer_unregister(FAR void *handle);
****************************************************************************/
/****************************************************************************
* Name: timer_sethandler
* Name: timer_setcallback
*
* Description:
* This function can be called to add a callback into driver-related code
@ -280,9 +272,9 @@ void timer_unregister(FAR void *handle);
* and may NOT be used by appliction code.
*
* Input parameters:
* handle - This is the handle that was returned by timer_register()
* newhandler - The new timer interrupt handler
* oldhandler - The previous timer interrupt handler (if any)
* 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
@ -290,8 +282,7 @@ void timer_unregister(FAR void *handle);
****************************************************************************/
#ifdef __KERNEL__
int timer_sethandler(FAR void *handle, tccb_t newhandler,
FAR tccb_t *oldhandler);
int timer_setcallback(FAR void *handle, tccb_t callback, FAR void *arg);
#endif
/****************************************************************************