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