1# Watchdog 2 3## 概述 4 5### 功能简介 6 7看门狗(Watchdog),又称看门狗计时器(Watchdog timer),是一种硬件计时设备。一般有一个输入,叫做喂狗,一个输出到系统的复位端。当系统主程序发生错误导致未及时清除看门狗计时器的计时值时,看门狗计时器就会对系统发出复位信号,使系统从悬停状态恢复到正常运作状态。 8 9Watchdog接口定义了看门狗操作的通用方法集合,包括: 10 11- 打开/关闭看门狗设备 12- 启动/停止看门狗设备 13- 设置/获取看门狗设备超时时间 14- 获取看门狗设备状态 15- 喂狗 16 17### 基本概念 18 19系统正常工作的时候,每隔一段时间输出一个信号到喂狗端,给看门狗清零,这个操作就叫做喂狗。如果超过规定的时间不喂狗,看门狗定时超时,就会给出一个复位信号到系统,使系统复位。 20 21### 运作机制 22 23在HDF框架中,Watchdog模块接口适配模式采用独立服务模式,如图1所示。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDF设备管理器的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。 24 25独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: 26 27- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 28- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 29 30Watchdog模块各分层作用: 31 32- 接口层提供打开看门狗设备、获取看门狗设备状态、启动看门狗设备、设置看门狗设备超时时间、获取看门狗设备超时时间、喂狗、停止看门狗设备超时时间、关闭看门狗设备的接口。 33- 核心层主要提供看门狗控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。 34- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 35 36**图 1** Watchdog独立服务模式结构图 37 38 39 40## 使用指导 41 42### 场景介绍 43 44对于无法直接观测到的软件异常,我们可以使用看门狗进行自动检测,并在异常产生时及时重置。 45 46### 接口说明 47 48Watchdog模块提供的主要接口如表1所示。 49 50**表1** 看门狗API接口功能介绍 51 52| 接口名 | 描述 | 53| -------- | -------- | 54| int32_t WatchdogOpen(int16_t wdtId, DevHandle *handle) | 打开看门狗 | 55| void WatchdogClose(DevHandle handle) | 关闭看门狗 | 56| int32_t WatchdogStart(DevHandle handle) | 启动看门狗 | 57| int32_t WatchdogStop(DevHandle handle) | 停止看门狗 | 58| int32_t WatchdogSetTimeout(DevHandle handle, uint32_t seconds) | 设置看门狗超时时间 | 59| int32_t WatchdogGetTimeout(DevHandle handle, uint32_t *seconds) | 获取看门狗超时时间 | 60| int32_t WatchdogGetStatus(DevHandle handle, int32_t *status) | 获取看门狗状态 | 61| int32_t WatchdogFeed(DevHandle handle) | 清除看门狗定时器(喂狗) | 62 63>  **说明:**<br> 64> 本文涉及的看门狗的所有接口,支持内核态及用户态使用。 65 66### 开发步骤 67 68使用看门狗的一般流程如下图所示。 69 70**图2** 看门狗使用流程图 71 72 73 74#### 打开看门狗设备 75 76在操作看门狗之前,需要调用WatchdogOpen打开看门狗设备,一个系统可能有多个看门狗,通过看门狗ID号来打开指定的看门狗设备: 77 78```c 79DevHandle WatchdogOpen(int16_t wdtId, DevHandle *handle); 80``` 81 82**表2** WatchdogOpen参数和返回值描述 83 84| **参数** | **参数描述** | 85| -------- | -------- | 86| wdtId | 看门狗设备号 | 87| handle | 看门狗设备句柄 | 88| **返回值** | **返回值描述** | 89| HDF_SUCCESS | 打开看门狗设备成功 | 90| 负数 | 打开看门狗设备失败 | 91 92```c 93int16_t wdtId = 0; 94int32_t ret; 95DevHandle *handle = NULL; 96 97ret = WatchdogOpen(wdtId, handle); // 打开0号看门狗设备 98if (ret != HDF_SUCCESS) { 99 HDF_LOGE("WatchdogOpen: open watchdog_%hd failed, ret:%d\n", wdtId, ret); 100 return ret; 101} 102``` 103 104#### 获取看门狗状态 105 106```c 107int32_t WatchdogGetStatus(DevHandle handle, int32_t *status); 108``` 109 110**表3** WatchdogGetStatus参数和返回值描述 111 112| **参数** | **参数描述** | 113| -------- | -------- | 114| handle | 看门狗设备句柄 | 115| status | 获取到的看门狗状态 | 116| **返回值** | **返回值描述** | 117| HDF_SUCCESS | 获取看门狗状态成功 | 118| 负数 | 获取看门狗状态失败 | 119 120```c 121int32_t ret; 122int32_t status; 123 124ret = WatchdogGetStatus(handle, &status); // 获取Watchdog状态 125if (ret != HDF_SUCCESS) { 126 HDF_LOGE("WatchdogGetStatus: watchdog get status failed, ret:%d\n", ret); 127 return ret; 128} 129``` 130 131#### 设置超时时间 132 133 134```c 135int32_t WatchdogSetTimeout(DevHandle *handle, uint32_t seconds); 136``` 137 138**表4** WatchdogSetTimeout参数和返回值描述 139 140| **参数** | **参数描述** | 141| -------- | -------- | 142| handle | 看门狗设备句柄 | 143| seconds | 超时时间,单位为秒 | 144| **返回值** | **返回值描述** | 145| HDF_SUCCESS | 设置成功 | 146| 负数 | 设置失败 | 147 148```c 149int32_t ret; 150 151ret = WatchdogSetTimeout(handle, 2); // 设置超时时间2秒 152if (ret != HDF_SUCCESS) { 153 HDF_LOGE("WatchdogSetTimeout: watchdog set timeOut failed, ret:%d\n", ret); 154 return ret; 155} 156``` 157 158#### 获取超时时间 159 160```c 161int32_t WatchdogGetTimeout(DevHandle *handle, uint32_t *seconds); 162``` 163 164**表5** WatchdogGetTimeout参数和返回值描述 165 166| **参数** | **参数描述** | 167| -------- | -------- | 168| handle | 看门狗设备句柄 | 169| seconds | 获取的看门狗超时时间 | 170| **返回值** | **返回值描述** | 171| HDF_SUCCESS | 获取看门狗超时时间成功 | 172| 负数 | 获取看门狗超时时间失败 | 173 174```c 175 int32_t ret; 176 uint32_t timeOut; 177 178 ret = WatchdogGetTimeout(handle, &timeOut); // 获取超时时间 179 if (ret != HDF_SUCCESS) { 180 HDF_LOGE("WatchdogGetTimeout: watchdog get timeOut failed, ret:%d\n", ret); 181 return ret; 182 } 183``` 184 185#### 启动看门狗 186 187```c 188int32_t WatchdogStart(DevHandle handle); 189``` 190 191**表6** WatchdogStart参数和返回值描述 192 193| **参数** | **参数描述** | 194| -------- | -------- | 195| handle | 看门狗设备句柄 | 196| **返回值** | **返回值描述** | 197| HDF_SUCCESS | 启动看门狗成功 | 198| 负数 | 启动看门狗失败 | 199 200```c 201int32_t ret; 202 203ret = WatchdogStart(handle); // 启动看门狗 204if (ret != HDF_SUCCESS) { 205 HDF_LOGE("WatchdogStart: start watchdog failed, ret:%d\n", ret); 206 return ret; 207} 208``` 209 210#### 喂狗 211 212```c 213int32_t WatchdogFeed(DevHandle handle); 214``` 215 216**表7** WatchdogFeed参数和返回值描述 217 218| **参数** | **参数描述** | 219| -------- | -------- | 220| handle | 看门狗设备句柄 | 221| **返回值** | **返回值描述** | 222| HDF_SUCCESS | 喂狗成功 | 223| 负数 | 喂狗失败 | 224 225```c 226int32_t ret; 227 228ret = WatchdogFeed(handle); // 喂狗 229if (ret != HDF_SUCCESS) { 230 HDF_LOGE("WatchdogFeed: feed watchdog failed, ret:%d\n", ret); 231 return ret; 232} 233``` 234 235#### 停止看门狗 236 237```c 238int32_t WatchdogStop(DevHandle handle); 239``` 240 241**表8** WatchdogStop参数和返回值描述 242 243| **参数** | **参数描述** | 244| -------- | -------- | 245| handle | 看门狗设备句柄 | 246| **返回值** | **返回值描述** | 247| HDF_SUCCESS | 停止看门狗成功 | 248| 负数 | 停止看门狗失败 | 249 250```c 251int32_t ret; 252 253ret = WatchdogStop(handle); // 停止看门狗 254if (ret != HDF_SUCCESS) { 255 HDF_LOGE("WatchdogStop: stop watchdog failed, ret:%d\n", ret); 256 return ret; 257} 258``` 259 260#### 关闭看门狗设备 261 262当所有操作完毕后,调用WatchdogClose关闭打开的看门狗设备: 263 264```c 265void WatchdogClose(DevHandle handle); 266``` 267 268**表9** WatchdogClose参数和返回值描述 269 270| **参数** | **参数描述** | 271| -------- | -------- | 272| handle | 看门狗设备句柄 | 273 274```c 275WatchdogClose(handle); // 关闭看门狗 276``` 277 278## 使用实例 279 280下面将基于Hi3516DV300开发板展示使用Watchdog完整操作,步骤主要如下: 281 2821. 传入看门狗ID号,及空的描述句柄,打开看门狗设备并获得看门狗设备句柄。 2832. 通过看门狗设备句柄及超时时间,设置看门狗设备超时时间。 2843. 通过看门狗设备句柄及待获取超时时间,获取看门狗设备超时时间。 2854. 通过看门狗设备句柄启动看门狗设备。 2865. 通过看门狗设备句柄喂狗。 2876. 通过看门狗设备句柄停止看门狗设备。 2887. 通过看门狗设备句柄关闭看门狗设备。 289 290```c 291#include "watchdog_if.h" /* watchdog标准接口头文件 */ 292#include "hdf_log.h" /* 标准日志打印头文件 */ 293#include "osal_time.h" /* 标准延迟&睡眠接口头文件 */ 294 295#define WATCHDOG_TEST_TIMEOUT 2 296#define WATCHDOG_TEST_FEED_TIME 6 297 298static int32_t TestCaseWatchdog(void) 299{ 300 int32_t i; 301 int32_t ret; 302 int16_t wdtId = 0; 303 int32_t status; 304 uint32_t timeout; 305 DevHandle *handle = NULL; 306 307 /* 打开0号看门狗设备 */ 308 ret = WatchdogOpen(wdtId, handle); 309 if (ret != HDF_SUCCESS) { 310 HDF_LOGE("WatchdogOpen: open watchdog_%hd failed, ret:%d\n", wdtId, ret); 311 return ret; 312 } 313 314 /* 设置超时时间 */ 315 ret = WatchdogSetTimeout(handle, WATCHDOG_TEST_TIMEOUT); 316 if (ret != HDF_SUCCESS) { 317 HDF_LOGE("%s: set timeout fail! ret:%d\n", __func__, ret); 318 WatchdogClose(handle); 319 return ret; 320 } 321 322 /* 获取超时时间 */ 323 ret = WatchdogGetTimeout(handle, &timeout); 324 if (ret != HDF_SUCCESS) { 325 HDF_LOGE("%s: get timeout fail! ret:%d\n", __func__, ret); 326 WatchdogClose(handle); 327 return ret; 328 } 329 /* 比较设置与获取的超时时间是否一致*/ 330 if (timeout != WATCHDOG_TEST_TIMEOUT) { 331 HDF_LOGE("%s: set:%u, but get:%u", __func__, WATCHDOG_TEST_TIMEOUT, timeout); 332 WatchdogClose(handle); 333 return HDF_FAILURE; 334 } 335 HDF_LOGI("%s: read timeout back:%u\n", __func__, timeout); 336 337 /* 启动看门狗,开始计时 */ 338 ret = WatchdogStart(handle); 339 if (ret != HDF_SUCCESS) { 340 HDF_LOGE("%s: start fail! ret:%d\n", __func__, ret); 341 WatchdogClose(handle); 342 return ret; 343 } 344 /* 获取看门狗状态,是否启动*/ 345 status = WATCHDOG_STOP; 346 ret = WatchdogGetStatus(handle, &status); 347 if (ret != HDF_SUCCESS) { 348 HDF_LOGE("%s: get status fail! ret:%d", __func__, ret); 349 WatchdogClose(handle); 350 return ret; 351 } 352 if (status != WATCHDOG_START) { 353 HDF_LOGE("%s: status is:%d after start", __func__, status); 354 WatchdogClose(handle); 355 return HDF_FAILURE; 356 } 357 358 /* 每隔1S喂狗一次 */ 359 for (i = 0; i < WATCHDOG_TEST_FEED_TIME; i++) { 360 HDF_LOGI("%s: feeding watchdog %d times... \n", __func__, i); 361 ret = WatchdogFeed(handle); 362 if (ret != HDF_SUCCESS) { 363 HDF_LOGE("%s: feed dog fail! ret:%d\n", __func__, ret); 364 WatchdogClose(handle); 365 return ret; 366 } 367 OsalSleep(1); 368 } 369 /* 由于喂狗间隔小于超时时间,系统不会发生复位,此日志可以正常打印 */ 370 HDF_LOGI("%s: no reset ... feeding test OK!!!\n", __func__); 371 372 ret = WatchdogStop(handle); 373 if (ret != HDF_SUCCESS) { 374 HDF_LOGE("%s: stop fail! ret:%d", __func__, ret); 375 WatchdogClose(handle); 376 return ret; 377 } 378 /* 获取看门狗状态,是否停止*/ 379 status = WATCHDOG_START; 380 ret = WatchdogGetStatus(handle, &status); 381 if (ret != HDF_SUCCESS) { 382 HDF_LOGE("%s: get status fail! ret:%d", __func__, ret); 383 WatchdogClose(handle); 384 return ret; 385 } 386 if (status != WATCHDOG_STOP) { 387 HDF_LOGE("%s: status is:%d after stop", __func__, status); 388 WatchdogClose(handle); 389 return HDF_FAILURE; 390 } 391 WatchdogClose(handle); 392 return HDF_SUCCESS; 393} 394```