看门狗,是一个确保嵌入式平台稳定性的必要措施,虽然正常操作不应触发看门狗,但在一些特殊场景,比如ESD等情况导致的系统跑飞,就需要看门狗做复位了。 源码分析 源码路径 view plaincopy to clipboardprint?
- components\drivers\watchdog\watchdog.c
注册入口 view plaincopy to clipboardprint?
- #ifdef RT_USING_DEVICE_OPS
- const static struct rt_device_ops wdt_ops =
- {
- rt_watchdog_init,
- rt_watchdog_open,
- rt_watchdog_close,
- RT_NULL,
- RT_NULL,
- rt_watchdog_control,
- };
- #endif
-
- rt_err_t rt_hw_watchdog_register(struct rt_watchdog_device *wtd,
- const char *name,
- rt_uint32_t flag,
- void *data)
- {
- struct rt_device *device;
- RT_ASSERT(wtd != RT_NULL);
-
- device = &(wtd->parent);
-
- device->type = RT_Device_Class_WDT;
- device->rx_indicate = RT_NULL;
- device->tx_complete = RT_NULL;
-
- #ifdef RT_USING_DEVICE_OPS
- device->ops = &wdt_ops;
- #else
- device->init = rt_watchdog_init;
- device->open = rt_watchdog_open;
- device->close = rt_watchdog_close;
- device->read = RT_NULL;
- device->write = RT_NULL;
- device->control = rt_watchdog_control;
- #endif
- device->user_data = data;
-
- return rt_device_register(device, name, flag);
- }
查看了这么多设备驱动,其实我们会发现,注册接口其实都差不多,基本上就设置设备类型,指定device的操作函数表(不同类型设备实现方法不一),之后注册设备。唯一可能有差异的是部分设备,框架层存在一些操作,需要初始化一些框架层维护的数据。看门狗设备也不例外符合这么个规律。 初始化入口 view plaincopy to clipboardprint?
- static rt_err_t rt_watchdog_init(struct rt_device *dev)
- {
- rt_watchdog_t *wtd;
-
- RT_ASSERT(dev != RT_NULL);
- wtd = (rt_watchdog_t *)dev;
- if (wtd->ops->init)
- {
- return (wtd->ops->init(wtd));
- }
-
- return (-RT_ENOSYS);
- }
从初始化入口看,基本上看门狗框架本身不维护任何信息,所有信息的维护都是交由驱动自己维护了。 打开入口 view plaincopy to clipboardprint?
- static rt_err_t rt_watchdog_open(struct rt_device *dev, rt_uint16_t oflag)
- {
- return (RT_EOK);
- }
看起来这个入口并没有实现任何功能,而实际上,这个函数所实现的功能已通过control接口实现,而不知为何open函数未调用control实现的接口。 关闭入口 view plaincopy to clipboardprint?
- static rt_err_t rt_watchdog_close(struct rt_device *dev)
- {
- rt_watchdog_t *wtd;
-
- RT_ASSERT(dev != RT_NULL);
- wtd = (rt_watchdog_t *)dev;
-
- if (wtd->ops->control(wtd, RT_DEVICE_CTRL_WDT_STOP, RT_NULL) != RT_EOK)
- {
- rt_kprintf(" This watchdog can not be stoped\n");
-
- return (-RT_ERROR);
- }
-
- return (RT_EOK);
- }
关闭入口需要驱动实现control中的RT_DEVICE_CTRL_WDT_STOP,具体功能是停止看门狗功能。 控制入口 view plaincopy to clipboardprint?
- static rt_err_t rt_watchdog_control(struct rt_device *dev,
- int cmd,
- void *args)
- {
- rt_watchdog_t *wtd;
-
- RT_ASSERT(dev != RT_NULL);
- wtd = (rt_watchdog_t *)dev;
-
- return (wtd->ops->control(wtd, cmd, args));
- }
控制入口的实现,算是现在看的这么多驱动之中,实现最为迷惑的部分了,从实现上看,看不出任何对驱动层的实现要求,但实际上,还是有要求的,这些要求需要跳转到看门狗的头文件中查看。 view plaincopy to clipboardprint?
- #define RT_DEVICE_CTRL_WDT_GET_TIMEOUT (RT_DEVICE_CTRL_BASE(WDT) + 1) /* 获取秒级的超时时间 */
- #define RT_DEVICE_CTRL_WDT_SET_TIMEOUT (RT_DEVICE_CTRL_BASE(WDT) + 2) /* 设置秒级的超时时间 */
- #define RT_DEVICE_CTRL_WDT_GET_TIMELEFT (RT_DEVICE_CTRL_BASE(WDT) + 3) /* 获取秒级的剩余时间(至重启) */
- #define RT_DEVICE_CTRL_WDT_KEEPALIVE (RT_DEVICE_CTRL_BASE(WDT) + 4) /* 刷新看门狗计时 */
- #define RT_DEVICE_CTRL_WDT_START (RT_DEVICE_CTRL_BASE(WDT) + 5) /* 启动看门狗 */
- #define RT_DEVICE_CTRL_WDT_STOP (RT_DEVICE_CTRL_BASE(WDT) + 6) /* 关闭看门狗 */
这些变量就是control接口的参数cmd,也就是说,驱动需要实现这些入口。 总结 至此,我们基本上可以梳理出驱动层的看门狗实现框架。即: view plaincopy to clipboardprint?
- #include <board.h>
-
- #ifdef RT_USING_WDT
-
- //#define DRV_DEBUG
- #define LOG_TAG "drv.wdt"
- #include <drv_log.h>
-
- struct wdt_obj
- {
- rt_watchdog_t watchdog;
- // TODO: wdt inside param
- };
-
- static rt_err_t wdt_init(rt_watchdog_t *wdt)
- {
- return RT_EOK;
- }
-
- static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg)
- {
- switch (cmd)
- {
- case RT_DEVICE_CTRL_WDT_KEEPALIVE:
- // TODO: 执行喂狗动作
- break;
- /* set watchdog timeout */
- case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
- // TODO:设置看门狗超时时间
- break;
- case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
- // TODO:获取设置的超时时间
- break;
- case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
- // TODO:获取看门狗的剩余计时
- break;
- case RT_DEVICE_CTRL_WDT_START:
- // TODO:启动看门狗
- break;
- case RT_DEVICE_CTRL_WDT_STOP:
- // TODO:关闭看门狗
- break;
- default:
- LOG_W("This command is not supported.");
- return -RT_ERROR;
- }
- return RT_EOK;
- }
-
- static struct rt_watchdog_ops ops =
- {
- .init = wdt_init,
- .control = wdt_control,
- };
-
- int rt_wdt_init(void)
- {
- struct wdt_obj *wdt;
-
- wdt = (struct wdt_obj *)rt_malloc(sizeof(struct wdt_obj));
- if(wdt == RT_NULL)
- {
- LOG_E("wdt device malloc failed.");
- return -RT_ERROR;
- }
- wdt.watchdog.ops.ops = &ops;
- if (rt_hw_watchdog_register(&wdt.watchdog, "wdt", RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK)
- {
- LOG_E("wdt device register failed.");
- return -RT_ERROR;
- }
- LOG_D("wdt device register success.");
- return RT_EOK;
- }
- INIT_BOARD_EXPORT(rt_wdt_init);
-
- #endif /* RT_USING_WDT */
|