mirror of
https://github.com/apache/nuttx.git
synced 2025-01-13 10:58:49 +08:00
890cf4764f
In some areas with high security requirements such as vehicle control, in order to meet functional safety requirements, the timeout and feeding interval of the watchdog need to be configured in milliseconds Signed-off-by: yaojiaqi <yaojiaqi@lixiang.com>
859 lines
24 KiB
C
859 lines
24 KiB
C
/****************************************************************************
|
|
* drivers/timers/watchdog.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
#include <nuttx/nuttx.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/lib/lib.h>
|
|
#include <nuttx/panic_notifier.h>
|
|
#include <nuttx/power/pm.h>
|
|
#include <nuttx/mutex.h>
|
|
#include <nuttx/wdog.h>
|
|
#include <nuttx/wqueue.h>
|
|
#include <nuttx/timers/oneshot.h>
|
|
#include <nuttx/timers/timer.h>
|
|
#include <nuttx/timers/watchdog.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_WATCHDOG_AUTOMONITOR
|
|
# define WATCHDOG_AUTOMONITOR_TIMEOUT \
|
|
(CONFIG_WATCHDOG_AUTOMONITOR_TIMEOUT)
|
|
# if !defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE) && \
|
|
!defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE)
|
|
# if CONFIG_WATCHDOG_AUTOMONITOR_PING_INTERVAL == 0
|
|
# define WATCHDOG_AUTOMONITOR_PING_INTERVAL \
|
|
(CONFIG_WATCHDOG_AUTOMONITOR_TIMEOUT / 2)
|
|
# else
|
|
# define WATCHDOG_AUTOMONITOR_PING_INTERVAL \
|
|
CONFIG_WATCHDOG_AUTOMONITOR_PING_INTERVAL
|
|
# endif
|
|
# define WATCHDOG_AUTOMONITOR_PING_INTERVAL_MSEC \
|
|
(WATCHDOG_AUTOMONITOR_PING_INTERVAL)
|
|
# define WATCHDOG_AUTOMONITOR_PING_INTERVAL_TICK \
|
|
MSEC2TICK(WATCHDOG_AUTOMONITOR_PING_INTERVAL)
|
|
# endif
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Type Definitions
|
|
****************************************************************************/
|
|
|
|
/* This structure describes the state of the upper half driver */
|
|
|
|
struct watchdog_upperhalf_s
|
|
{
|
|
#ifdef CONFIG_WATCHDOG_PANIC_NOTIFIER
|
|
/* When a crash occurs, stop the watchdog */
|
|
|
|
struct notifier_block nb;
|
|
#endif
|
|
#ifdef CONFIG_WATCHDOG_AUTOMONITOR
|
|
# if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
|
|
FAR struct oneshot_lowerhalf_s *oneshot;
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
|
|
FAR struct timer_lowerhalf_s *timer;
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WDOG)
|
|
struct wdog_s wdog;
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER)
|
|
struct work_s work;
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE)
|
|
struct pm_callback_s idle;
|
|
# endif
|
|
bool monitor;
|
|
#endif
|
|
|
|
uint8_t crefs; /* The number of times the device has been opened */
|
|
mutex_t lock; /* Supports mutual exclusion */
|
|
FAR char *path; /* Registration path */
|
|
|
|
/* The contained lower-half driver */
|
|
|
|
FAR struct watchdog_lowerhalf_s *lower;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int wdog_open(FAR struct file *filep);
|
|
static int wdog_close(FAR struct file *filep);
|
|
static ssize_t wdog_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
static ssize_t wdog_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen);
|
|
static int wdog_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct file_operations g_wdogops =
|
|
{
|
|
wdog_open, /* open */
|
|
wdog_close, /* close */
|
|
wdog_read, /* read */
|
|
wdog_write, /* write */
|
|
NULL, /* seek */
|
|
wdog_ioctl, /* ioctl */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE)
|
|
static int watchdog_automonitor_capture(int irq, FAR void *context,
|
|
FAR void *arg)
|
|
{
|
|
FAR struct watchdog_upperhalf_s *upper = arg;
|
|
FAR struct watchdog_lowerhalf_s *lower = upper->lower;
|
|
|
|
if (upper->monitor)
|
|
{
|
|
lower->ops->keepalive(lower);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
|
|
static void
|
|
watchdog_automonitor_oneshot(FAR struct oneshot_lowerhalf_s *oneshot,
|
|
FAR void *arg)
|
|
{
|
|
FAR struct watchdog_upperhalf_s *upper = arg;
|
|
FAR struct watchdog_lowerhalf_s *lower = upper->lower;
|
|
|
|
if (upper->monitor)
|
|
{
|
|
struct timespec ts =
|
|
{
|
|
WATCHDOG_AUTOMONITOR_PING_INTERVAL, 0
|
|
};
|
|
|
|
lower->ops->keepalive(lower);
|
|
ONESHOT_START(oneshot, watchdog_automonitor_oneshot, upper, &ts);
|
|
}
|
|
}
|
|
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
|
|
static bool watchdog_automonitor_timer(FAR uint32_t *next_interval_us,
|
|
FAR void *arg)
|
|
{
|
|
FAR struct watchdog_upperhalf_s *upper = arg;
|
|
FAR struct watchdog_lowerhalf_s *lower = upper->lower;
|
|
|
|
if (upper->monitor)
|
|
{
|
|
lower->ops->keepalive(lower);
|
|
}
|
|
|
|
return upper->monitor;
|
|
}
|
|
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WDOG)
|
|
static void watchdog_automonitor_wdog(wdparm_t arg)
|
|
{
|
|
FAR struct watchdog_upperhalf_s *upper = (FAR void *)arg;
|
|
FAR struct watchdog_lowerhalf_s *lower = upper->lower;
|
|
|
|
if (upper->monitor)
|
|
{
|
|
lower->ops->keepalive(lower);
|
|
wd_start(&upper->wdog, WATCHDOG_AUTOMONITOR_PING_INTERVAL_TICK,
|
|
watchdog_automonitor_wdog, (wdparm_t)upper);
|
|
}
|
|
}
|
|
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER)
|
|
static void watchdog_automonitor_worker(FAR void *arg)
|
|
{
|
|
FAR struct watchdog_upperhalf_s *upper = arg;
|
|
FAR struct watchdog_lowerhalf_s *lower = upper->lower;
|
|
|
|
if (upper->monitor)
|
|
{
|
|
lower->ops->keepalive(lower);
|
|
work_queue(LPWORK, &upper->work, watchdog_automonitor_worker,
|
|
upper, WATCHDOG_AUTOMONITOR_PING_INTERVAL_TICK);
|
|
}
|
|
}
|
|
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE)
|
|
static void watchdog_automonitor_idle(FAR struct pm_callback_s *cb,
|
|
int domain, enum pm_state_e pmstate)
|
|
{
|
|
FAR struct watchdog_upperhalf_s *upper;
|
|
FAR struct watchdog_lowerhalf_s *lower;
|
|
|
|
upper = container_of(cb, struct watchdog_upperhalf_s, idle);
|
|
lower = upper->lower;
|
|
|
|
if (domain == PM_IDLE_DOMAIN &&
|
|
pmstate != PM_RESTORE && upper->monitor)
|
|
{
|
|
lower->ops->keepalive(lower);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_WATCHDOG_AUTOMONITOR
|
|
# if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
|
|
static void
|
|
watchdog_automonitor_start(FAR struct watchdog_upperhalf_s *upper,
|
|
FAR struct oneshot_lowerhalf_s *oneshot)
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
|
|
static void
|
|
watchdog_automonitor_start(FAR struct watchdog_upperhalf_s *upper,
|
|
FAR struct timer_lowerhalf_s *timer)
|
|
# else
|
|
static void
|
|
watchdog_automonitor_start(FAR struct watchdog_upperhalf_s *upper)
|
|
# endif
|
|
{
|
|
FAR struct watchdog_lowerhalf_s *lower = upper->lower;
|
|
|
|
if (!upper->monitor)
|
|
{
|
|
# if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE)
|
|
lower->ops->capture(lower, watchdog_automonitor_capture);
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
|
|
struct timespec ts =
|
|
{
|
|
WATCHDOG_AUTOMONITOR_PING_INTERVAL, 0
|
|
};
|
|
|
|
upper->oneshot = oneshot;
|
|
ONESHOT_START(oneshot, watchdog_automonitor_oneshot, upper, &ts);
|
|
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
|
|
upper->timer = timer;
|
|
timer->ops->setcallback(timer, watchdog_automonitor_timer, upper);
|
|
timer->ops->settimeout(timer, WATCHDOG_AUTOMONITOR_PING_INTERVAL_MSEC);
|
|
timer->ops->start(timer);
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WDOG)
|
|
wd_start(&upper->wdog, WATCHDOG_AUTOMONITOR_PING_INTERVAL_TICK,
|
|
watchdog_automonitor_wdog, (wdparm_t)upper);
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER)
|
|
work_queue(LPWORK, &upper->work, watchdog_automonitor_worker,
|
|
upper, WATCHDOG_AUTOMONITOR_PING_INTERVAL_TICK);
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE)
|
|
upper->idle.notify = watchdog_automonitor_idle;
|
|
pm_register(&upper->idle);
|
|
# endif
|
|
upper->monitor = true;
|
|
if (lower->ops->settimeout)
|
|
{
|
|
lower->ops->settimeout(lower, WATCHDOG_AUTOMONITOR_TIMEOUT);
|
|
}
|
|
|
|
lower->ops->start(lower);
|
|
}
|
|
}
|
|
|
|
static void watchdog_automonitor_stop(FAR struct watchdog_upperhalf_s *upper)
|
|
{
|
|
FAR struct watchdog_lowerhalf_s *lower = upper->lower;
|
|
|
|
if (upper->monitor)
|
|
{
|
|
upper->monitor = false;
|
|
lower->ops->stop(lower);
|
|
# if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE)
|
|
lower->ops->capture(lower, NULL);
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
|
|
ONESHOT_CANCEL(upper->oneshot, NULL);
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
|
|
upper->timer->ops->stop(upper->timer);
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WDOG)
|
|
wd_cancel(&upper->wdog);
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER)
|
|
work_cancel(LPWORK, &upper->work);
|
|
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE)
|
|
pm_unregister(&upper->idle);
|
|
# endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_WATCHDOG_PANIC_NOTIFIER
|
|
static int wdog_notifier(FAR struct notifier_block *nb, unsigned long action,
|
|
FAR void *data)
|
|
{
|
|
FAR struct watchdog_upperhalf_s *upper =
|
|
(FAR struct watchdog_upperhalf_s *)nb;
|
|
|
|
if (action == PANIC_KERNEL)
|
|
{
|
|
#ifdef CONFIG_WATCHDOG_AUTOMONITOR
|
|
watchdog_automonitor_stop(upper);
|
|
#else
|
|
return upper->lower->ops->stop(upper->lower);
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: wdog_open
|
|
*
|
|
* Description:
|
|
* This function is called whenever the watchdog timer device is opened.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int wdog_open(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct watchdog_upperhalf_s *upper = inode->i_private;
|
|
uint8_t tmp;
|
|
int ret;
|
|
|
|
wdinfo("crefs: %d\n", upper->crefs);
|
|
|
|
/* Get exclusive access to the device structures */
|
|
|
|
ret = nxmutex_lock(&upper->lock);
|
|
if (ret < 0)
|
|
{
|
|
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.
|
|
*/
|
|
|
|
tmp = upper->crefs + 1;
|
|
if (tmp == 0)
|
|
{
|
|
/* More than 255 opens; uint8_t overflows to zero */
|
|
|
|
ret = -EMFILE;
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* Save the new open count */
|
|
|
|
upper->crefs = tmp;
|
|
ret = OK;
|
|
|
|
errout_with_lock:
|
|
nxmutex_unlock(&upper->lock);
|
|
|
|
errout:
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: wdog_close
|
|
*
|
|
* Description:
|
|
* This function is called when the watchdog timer device is closed.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int wdog_close(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct watchdog_upperhalf_s *upper = inode->i_private;
|
|
int ret;
|
|
|
|
wdinfo("crefs: %d\n", upper->crefs);
|
|
|
|
/* Get exclusive access to the device structures */
|
|
|
|
ret = nxmutex_lock(&upper->lock);
|
|
if (ret < 0)
|
|
{
|
|
goto errout;
|
|
}
|
|
|
|
/* Decrement the references to the driver. If the reference count will
|
|
* decrement to 0, then uninitialize the driver.
|
|
*/
|
|
|
|
if (upper->crefs > 0)
|
|
{
|
|
upper->crefs--;
|
|
}
|
|
|
|
nxmutex_unlock(&upper->lock);
|
|
ret = OK;
|
|
|
|
errout:
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: wdog_read
|
|
*
|
|
* Description:
|
|
* A dummy read method. This is provided only to satisfy the VFS layer.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t wdog_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
/* Return zero -- usually meaning end-of-file */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: wdog_write
|
|
*
|
|
* Description:
|
|
* A dummy write method. This is provided only to satisfy the VFS layer.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t wdog_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen)
|
|
{
|
|
#ifdef CONFIG_WATCHDOG_MAGIC_V
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct watchdog_upperhalf_s *upper;
|
|
FAR struct watchdog_lowerhalf_s *lower;
|
|
int err = 0;
|
|
int i;
|
|
|
|
upper = inode->i_private;
|
|
DEBUGASSERT(upper != NULL);
|
|
lower = upper->lower;
|
|
DEBUGASSERT(lower != NULL);
|
|
|
|
nxmutex_lock(&upper->lock);
|
|
|
|
for (i = 0; i < buflen; i++)
|
|
{
|
|
if (buffer[i] == 'V')
|
|
{
|
|
#ifdef CONFIG_WATCHDOG_AUTOMONITOR
|
|
watchdog_automonitor_stop(upper);
|
|
#else
|
|
err = lower->ops->stop(lower);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == buflen)
|
|
{
|
|
err = lower->ops->keepalive(lower);
|
|
}
|
|
|
|
nxmutex_unlock(&upper->lock);
|
|
|
|
if (err < 0)
|
|
{
|
|
return err;
|
|
}
|
|
|
|
#endif
|
|
|
|
return buflen;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: wdog_ioctl
|
|
*
|
|
* Description:
|
|
* The standard ioctl method. This is where ALL of the watchdog timer
|
|
* work is done.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int wdog_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct watchdog_upperhalf_s *upper;
|
|
FAR struct watchdog_lowerhalf_s *lower;
|
|
int ret;
|
|
|
|
wdinfo("cmd: %d arg: %ld\n", cmd, arg);
|
|
upper = inode->i_private;
|
|
DEBUGASSERT(upper != NULL);
|
|
lower = upper->lower;
|
|
DEBUGASSERT(lower != NULL);
|
|
|
|
/* Get exclusive access to the device structures */
|
|
|
|
ret = nxmutex_lock(&upper->lock);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Handle built-in ioctl commands */
|
|
|
|
switch (cmd)
|
|
{
|
|
/* cmd: WDIOC_START
|
|
* Description: Start the watchdog timer
|
|
* Argument: Ignored
|
|
*/
|
|
|
|
case WDIOC_START:
|
|
{
|
|
#ifdef CONFIG_WATCHDOG_AUTOMONITOR
|
|
watchdog_automonitor_stop(upper);
|
|
#endif
|
|
|
|
/* Start the watchdog timer, resetting the time to the current
|
|
* timeout
|
|
*/
|
|
|
|
DEBUGASSERT(lower->ops->start); /* Required */
|
|
ret = lower->ops->start(lower);
|
|
}
|
|
break;
|
|
|
|
/* cmd: WDIOC_STOP
|
|
* Description: Stop the watchdog timer
|
|
* Argument: Ignored
|
|
*/
|
|
|
|
case WDIOC_STOP:
|
|
{
|
|
/* Stop the watchdog timer */
|
|
|
|
DEBUGASSERT(lower->ops->stop); /* Required */
|
|
ret = lower->ops->stop(lower);
|
|
}
|
|
break;
|
|
|
|
/* cmd: WDIOC_GETSTATUS
|
|
* Description: Get the status of the watchdog timer.
|
|
* Argument: A writeable pointer to struct watchdog_status_s.
|
|
*/
|
|
|
|
case WDIOC_GETSTATUS:
|
|
{
|
|
FAR struct watchdog_status_s *status;
|
|
|
|
/* Get the current watchdog timer status */
|
|
|
|
if (lower->ops->getstatus) /* Optional */
|
|
{
|
|
status = (FAR struct watchdog_status_s *)((uintptr_t)arg);
|
|
if (status)
|
|
{
|
|
ret = lower->ops->getstatus(lower, status);
|
|
}
|
|
else
|
|
{
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = -ENOSYS;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* cmd: WDIOC_SETTIMEOUT
|
|
* Description: Reset the watchdog timeout to this value
|
|
* Argument: A 32-bit timeout value in milliseconds.
|
|
*/
|
|
|
|
case WDIOC_SETTIMEOUT:
|
|
{
|
|
/* Set a new timeout value (and reset the watchdog timer) */
|
|
|
|
if (lower->ops->settimeout) /* Optional */
|
|
{
|
|
ret = lower->ops->settimeout(lower, (uint32_t)arg);
|
|
}
|
|
else
|
|
{
|
|
ret = -ENOSYS;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* cmd: WDIOC_CAPTURE
|
|
* Description: Do not reset. Instead, called this handler.
|
|
* Argument: A pointer to struct watchdog_capture_s.
|
|
*/
|
|
|
|
case WDIOC_CAPTURE:
|
|
{
|
|
FAR struct watchdog_capture_s *capture;
|
|
|
|
/* Don't reset on watchdog timer timeout; instead, call this user
|
|
* provider timeout handler. NOTE: Providing handler==NULL will
|
|
* restore the reset behavior.
|
|
*/
|
|
|
|
if (lower->ops->capture) /* Optional */
|
|
{
|
|
capture = (FAR struct watchdog_capture_s *)((uintptr_t)arg);
|
|
if (capture)
|
|
{
|
|
capture->oldhandler =
|
|
lower->ops->capture(lower, capture->newhandler);
|
|
ret = OK;
|
|
}
|
|
else
|
|
{
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = -ENOSYS;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* cmd: WDIOC_KEEPALIVE
|
|
* Description: Reset the watchdog timer ("ping", "pet the dog")
|
|
* Argument: Argument: Ignored
|
|
*/
|
|
|
|
case WDIOC_KEEPALIVE:
|
|
{
|
|
/* Reset the watchdog timer to the current timeout value, prevent
|
|
* any imminent watchdog timeouts. This is sometimes referred as
|
|
* "pinging" the watchdog timer or "petting the dog".
|
|
*/
|
|
|
|
if (lower->ops->keepalive) /* Optional */
|
|
{
|
|
ret = lower->ops->keepalive(lower);
|
|
}
|
|
else
|
|
{
|
|
ret = -ENOSYS;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* Any unrecognized IOCTL commands might be platform-specific ioctl
|
|
* commands
|
|
*/
|
|
|
|
default:
|
|
{
|
|
wdinfo("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.
|
|
*/
|
|
|
|
if (lower->ops->ioctl) /* Optional */
|
|
{
|
|
ret = lower->ops->ioctl(lower, cmd, arg);
|
|
}
|
|
else
|
|
{
|
|
ret = -ENOTTY;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
nxmutex_unlock(&upper->lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: watchdog_register
|
|
*
|
|
* Description:
|
|
* This function binds an instance of a "lower half" watchdog driver with
|
|
* the "upper half" watchdog device and registers that device so that can
|
|
* be usedby application code.
|
|
*
|
|
* When this function is called, the "lower half" driver should be in the
|
|
* disabled state (as if the stop() method had already been called).
|
|
*
|
|
* Input Parameters:
|
|
* dev path - The full path to the driver to be registers in the NuttX
|
|
* pseudo-filesystem. The recommended convention is to name all
|
|
* watchdogdrivers as "/dev/watchdog0", "/dev/watchdog1", etc. where
|
|
* the driverpath differs only in the "minor" number at the end of the
|
|
* device name.
|
|
* lower - A pointer to an instance of lower half watchdog driver. This
|
|
* instance is bound to the watchdog driver and must persists as long as
|
|
* the driver persists.
|
|
*
|
|
* Returned Value:
|
|
* On success, a non-NULL handle is returned to the caller. In the event
|
|
* of any failure, a NULL value is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
|
|
FAR void *watchdog_register(FAR const char *path,
|
|
FAR struct watchdog_lowerhalf_s *lower,
|
|
FAR struct oneshot_lowerhalf_s *oneshot)
|
|
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
|
|
FAR void *watchdog_register(FAR const char *path,
|
|
FAR struct watchdog_lowerhalf_s *lower,
|
|
FAR struct timer_lowerhalf_s *timer)
|
|
#else
|
|
FAR void *watchdog_register(FAR const char *path,
|
|
FAR struct watchdog_lowerhalf_s *lower)
|
|
#endif
|
|
{
|
|
FAR struct watchdog_upperhalf_s *upper;
|
|
int ret;
|
|
|
|
DEBUGASSERT(path && lower);
|
|
wdinfo("Entry: path=%s\n", path);
|
|
|
|
/* Allocate the upper-half data structure */
|
|
|
|
upper = (FAR struct watchdog_upperhalf_s *)
|
|
kmm_zalloc(sizeof(struct watchdog_upperhalf_s));
|
|
if (!upper)
|
|
{
|
|
wderr("Upper half allocation failed\n");
|
|
goto errout;
|
|
}
|
|
|
|
/* Initialize the watchdog timer device structure (it was already zeroed
|
|
* by kmm_zalloc()).
|
|
*/
|
|
|
|
nxmutex_init(&upper->lock);
|
|
upper->lower = lower;
|
|
|
|
/* Copy the registration path */
|
|
|
|
upper->path = strdup(path);
|
|
if (!upper->path)
|
|
{
|
|
wderr("Path allocation failed\n");
|
|
goto errout_with_upper;
|
|
}
|
|
|
|
/* Register the watchdog timer device */
|
|
|
|
ret = register_driver(path, &g_wdogops, 0666, upper);
|
|
if (ret < 0)
|
|
{
|
|
wderr("register_driver failed: %d\n", ret);
|
|
goto errout_with_path;
|
|
}
|
|
|
|
#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
|
|
watchdog_automonitor_start(upper, oneshot);
|
|
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
|
|
watchdog_automonitor_start(upper, timer);
|
|
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR)
|
|
watchdog_automonitor_start(upper);
|
|
#endif
|
|
|
|
#ifdef CONFIG_WATCHDOG_PANIC_NOTIFIER
|
|
upper->nb.notifier_call = wdog_notifier;
|
|
panic_notifier_chain_register(&upper->nb);
|
|
#endif
|
|
|
|
return (FAR void *)upper;
|
|
|
|
errout_with_path:
|
|
lib_free(upper->path);
|
|
|
|
errout_with_upper:
|
|
nxmutex_destroy(&upper->lock);
|
|
kmm_free(upper);
|
|
|
|
errout:
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: watchdog_unregister
|
|
*
|
|
* Description:
|
|
* This function can be called to disable and unregister the watchdog
|
|
* device driver.
|
|
*
|
|
* Input Parameters:
|
|
* handle - This is the handle that was returned by watchdog_register()
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
void watchdog_unregister(FAR void *handle)
|
|
{
|
|
FAR struct watchdog_upperhalf_s *upper;
|
|
FAR struct watchdog_lowerhalf_s *lower;
|
|
|
|
/* Recover the pointer to the upper-half driver state */
|
|
|
|
upper = (FAR struct watchdog_upperhalf_s *)handle;
|
|
DEBUGASSERT(upper != NULL);
|
|
lower = upper->lower;
|
|
DEBUGASSERT(lower != NULL);
|
|
|
|
wdinfo("Unregistering: %s\n", upper->path);
|
|
|
|
#ifdef CONFIG_WATCHDOG_AUTOMONITOR
|
|
watchdog_automonitor_stop(upper);
|
|
#endif
|
|
|
|
/* Disable the watchdog timer */
|
|
|
|
DEBUGASSERT(lower->ops->stop); /* Required */
|
|
lower->ops->stop(lower);
|
|
|
|
/* Unregister the watchdog timer device */
|
|
|
|
unregister_driver(upper->path);
|
|
#ifdef CONFIG_WATCHDOG_PANIC_NOTIFIER
|
|
panic_notifier_chain_unregister(&upper->nb);
|
|
#endif
|
|
|
|
/* Then free all of the driver resources */
|
|
|
|
lib_free(upper->path);
|
|
nxmutex_destroy(&upper->lock);
|
|
kmm_free(upper);
|
|
}
|