/* * Try to request the watchdog dedicated timer clock source. It must * be supplied if asynchronous mode is enabled. Otherwise fallback * to the common timer/bus clocks configuration, in which the very * first found clock supply both timer and APB signals. */ /* * 尝试请求看门狗专用的定时器时钟源。如果启用了异步模式,必须提供该时钟源。 * 否则,回退到通用定时器/总线时钟配置,在这种配置中,第一个找到的时钟同时为定时器和APB 信号提供时钟。 */ dw_wdt->clk = devm_clk_get(dev, "tclk");// 获取名为 "tclk" 的时钟 if (IS_ERR(dw_wdt->clk)) {// 如果获取失败 dw_wdt->clk = devm_clk_get(dev, NULL);// 尝试获取默认时钟 if (IS_ERR(dw_wdt->clk))// 如果再次失败,返回 PTR_ERR(dw_wdt->clk) 错误码 return PTR_ERR(dw_wdt->clk); }
ret = clk_prepare_enable(dw_wdt->clk);// 准备并启用时钟 if (ret) return ret;
dw_wdt->rate = clk_get_rate(dw_wdt->clk);// 获取时钟的速率 if (dw_wdt->rate == 0) {// 如果时钟速率为 0,设置错误码并跳转到 out_disable_clk 标签处进行清理 ret = -EINVAL; goto out_disable_clk; }
/* * Request APB clock if device is configured with async clocks mode. * In this case both tclk and pclk clocks are supposed to be specified. * Alas we can't know for sure whether async mode was really activated, * so the pclk phandle reference is left optional. If it couldn't be * found we consider the device configured in synchronous clocks mode. */ /* * 如果设备配置为异步时钟模式,请求 APB 时钟。 * 在这种情况下,假设同时指定了 tclk 和 pclk 时钟。 * 遗憾的是,我们不能确定是否真的启用了异步模式, * 所以 pclk 的 phandle 引用是可选的。如果找不到它,我们认为设备配置为同步时钟模式。 */ // 尝试获取名为 "pclk" 的时钟,devm_clk_get_optional 函数用于获取可选的时钟 dw_wdt->pclk = devm_clk_get_optional(dev, "pclk"); if (IS_ERR(dw_wdt->pclk)) {// 如果获取失败 ret = PTR_ERR(dw_wdt->pclk);// 设置错误码并跳转到 out_disable_clk 标签处进行清理 goto out_disable_clk; }
// 准备并启用 APB 时钟 ret = clk_prepare_enable(dw_wdt->pclk); if (ret)// 如果准备或启用 APB 时钟失败,跳转到 out_disable_clk 标签处进行清理 goto out_disable_clk;
// 获取复位控制对象,devm_reset_control_get_optional_shared 函数用于获取可选的共享复位控制对象 dw_wdt->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); if (IS_ERR(dw_wdt->rst)) {// 如果获取失败 ret = PTR_ERR(dw_wdt->rst);// 设置错误码并跳转到 out_disable_pclk 标签处进行清理 goto out_disable_pclk; }
/* Enable normal reset without pre-timeout by default. */ dw_wdt_update_mode(dw_wdt, DW_WDT_RMOD_RESET);
/* * Pre-timeout IRQ is optional, since some hardware may lack support * of it. Note we must request rising-edge IRQ, since the lane is left * pending either until the next watchdog kick event or up to the * system reset. */ ret = platform_get_irq_optional(pdev, 0); if (ret > 0) { ret = devm_request_irq(dev, ret, dw_wdt_irq, IRQF_SHARED | IRQF_TRIGGER_RISING, pdev->name, dw_wdt); if (ret) goto out_disable_pclk;
/* * If the watchdog is already running, use its already configured * timeout. Otherwise use the default or the value provided through * devicetree. */ /* * 如果看门狗已经在运行,使用其已配置的超时时间。 * 否则,使用默认值或通过设备树提供的值。 */ if (dw_wdt_is_enabled(dw_wdt)) {// 检查看门狗是否已经启用 // 如果已经启用,获取当前的超时时间并设置到看门狗设备中 wdd->timeout = dw_wdt_get_timeout(dw_wdt); // 设置看门狗设备的状态位,表示硬件正在运行 set_bit(WDOG_HW_RUNNING, &wdd->status); } else { // 如果未启用,设置默认超时时间 wdd->timeout = DW_WDT_DEFAULT_SECONDS; // 使用默认超时时间重新初始化看门狗设备 watchdog_init_timeout(wdd, 0, dev); }
/** struct watchdog_device - The structure that defines a watchdog device * * @id: The watchdog's ID. (Allocated by watchdog_register_device) * @parent: The parent bus device * @groups: List of sysfs attribute groups to create when creating the * watchdog device. * @info: Pointer to a watchdog_info structure. * @ops: Pointer to the list of watchdog operations. * @gov: Pointer to watchdog pretimeout governor. * @bootstatus: Status of the watchdog device at boot. * @timeout: The watchdog devices timeout value (in seconds). * @pretimeout: The watchdog devices pre_timeout value. * @min_timeout:The watchdog devices minimum timeout value (in seconds). * @max_timeout:The watchdog devices maximum timeout value (in seconds) * as configurable from user space. Only relevant if * max_hw_heartbeat_ms is not provided. * @min_hw_heartbeat_ms: * Hardware limit for minimum time between heartbeats, * in milli-seconds. * @max_hw_heartbeat_ms: * Hardware limit for maximum timeout, in milli-seconds. * Replaces max_timeout if specified. * @reboot_nb: The notifier block to stop watchdog on reboot. * @restart_nb: The notifier block to register a restart function. * @driver_data:Pointer to the drivers private data. * @wd_data: Pointer to watchdog core internal data. * @status: Field that contains the devices internal status bits. * @deferred: Entry in wtd_deferred_reg_list which is used to * register early initialized watchdogs. * * The watchdog_device structure contains all information about a * watchdog timer device. * * The driver-data field may not be accessed directly. It must be accessed * via the watchdog_set_drvdata and watchdog_get_drvdata helpers. */ structwatchdog_device {// 定义表示看门狗设备的结构体 int id;// 设备唯一标识符 structdevice *parent;// 指向父设备的指针 conststructattribute_group **groups;// 指向属性组数组的指针,用于管理设备属性 conststructwatchdog_info *info;// 指向包含设备详细信息的结构体指针 conststructwatchdog_ops *ops;// 指向包含设备操作函数的结构体指针 conststructwatchdog_governor *gov;// 指向监管相关结构体指针,可能用于调控设备运行 // 启动状态标志,具体含义由其他部分定义 unsignedint bootstatus; unsignedint timeout; unsignedint pretimeout; unsignedint min_timeout; unsignedint max_timeout; unsignedint min_hw_heartbeat_ms; unsignedint max_hw_heartbeat_ms; structnotifier_blockreboot_nb; structnotifier_blockrestart_nb; void *driver_data; structwatchdog_core_data *wd_data; unsignedlong status; /* Bit numbers for status flags */ #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ #define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */ #define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ #define WDOG_HW_RUNNING 3 /* True if HW watchdog running */ #define WDOG_STOP_ON_UNREGISTER 4 /* Should be stopped on unregister */ structlist_headdeferred; };
struct watchdog_info
watchdog_info 成员指向一个包含看门狗设备详细信息的结构体,结构体
1 2 3 4 5 6 7 8 9 10 11 12
// 定义一个名为 watchdog_info 的结构体,用于存储看门狗设备的相关特定信息 structwatchdog_info { // 用于存储看门狗设备或其驱动所支持的选项标志位信息 // 通过不同的位设置来表示支持的各种功能、特性或工作模式等 __u32 options; /* Options the card/driver supports */ // 用于存储看门狗设备的固件版本号 // 随着设备固件的更新,该版本号会相应改变,以便区分不同版本的固件 __u32 firmware_version; /* Firmware version of the card */ // 用于存储能够标识该看门狗设备所在电路板或设备本身的字符串信息 // 最多可存储 32 个字节的字符内容,用于唯一识别该设备,比如设备名称、型号等 __u8 identity[32]; /* Identity of the board */ };
/** struct watchdog_ops - The watchdog-devices operations * * @owner: The module owner. * @start: The routine for starting the watchdog device. * @stop: The routine for stopping the watchdog device. * @ping: The routine that sends a keepalive ping to the watchdog device. * @status: The routine that shows the status of the watchdog device. * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). * @set_pretimeout:The routine for setting the watchdog devices pretimeout. * @get_timeleft:The routine that gets the time left before a reset (in seconds). * @restart: The routine for restarting the machine. * @ioctl: The routines that handles extra ioctl calls. * * The watchdog_ops structure contains a list of low-level operations * that control a watchdog device. It also contains the module that owns * these operations. The start function is mandatory, all other * functions are optional. */ structwatchdog_ops { // 指向拥有此操作函数集的模块指针,通常用于模块引用计数等管理 structmodule *owner; /* mandatory operations */ /* 以下是必须实现的操作函数指针 */ int (*start)(struct watchdog_device *); //启动看门狗设备的函数指针,传入指向看门狗设备结构体的指针,返回操作结果 /* optional operations */ /* 以下是可选实现的操作函数指针 */ int (*stop)(struct watchdog_device *);// 停止看门狗设备的函数指针,传入指向看门狗设备结构体的指针,返回操作结果 int (*ping)(struct watchdog_device *);// 执行喂狗操作的函数指针,传入指向看门狗设备结构体的指针,返回操作结果 unsignedint(*status)(struct watchdog_device *);// 获取看门狗设备当前状态的函数指针,传入指向看门狗设备结构体的指针,返回状态值 int (*set_timeout)(struct watchdog_device *, unsignedint);// 设置看门狗设备超时时间的函数指针,传入指向看门狗设备结构体的指针和新的超时时间值,返回操作结果 int (*set_pretimeout)(struct watchdog_device *, unsignedint);// 设置看门狗设备预超时时间的函数指针,传入指向看门狗设备结构体的指针和新的预超时时间值,返回操作结果 unsignedint(*get_timeleft)(struct watchdog_device *);// 获取看门狗设备剩余时间的函数指针,传入指向看门狗设备结构体的指针,返回剩余时间值 int (*restart)(struct watchdog_device *, unsignedlong, void *);// 重启看门狗设备的函数指针,传入指向看门狗设备结构体的指针、重启相关参数等,返回操作结果 long (*ioctl)(struct watchdog_device *, unsignedint, unsignedlong);// 处理 ioctl 系统调用的函数指针,传入指向看门狗设备结构体的指针、ioctl 命令码及参数等,返回处理结果 };
/** * watchdog_register_device() - register a watchdog device * @wdd: watchdog device * * Register a watchdog device with the kernel so that the * watchdog timer can be accessed from userspace. * * A zero is returned on success and a negative errno code for * failure. */
intwatchdog_register_device(struct watchdog_device *wdd) { constchar *dev_str; int ret = 0;
mutex_lock(&wtd_deferred_reg_mutex); if (wtd_deferred_reg_done)//如果 wtd_deferred_reg_done 为真,则执行__watchdog_register_device(wdd)函数 ret = __watchdog_register_device(wdd); else// 否则执行watchdog_deferred_registration_add(wdd); watchdog_deferred_registration_add(wdd); mutex_unlock(&wtd_deferred_reg_mutex);
/* * Note: now that all watchdog_device data has been verified, we * will not check this anymore in other functions. If data gets * corrupted in a later stage then we expect a kernel panic! */
/* Use alias for watchdog id if possible */ if (wdd->parent) {// 获取 watchdog ID(优先使用设备树别名) ret = of_alias_get_id(wdd->parent->of_node, "watchdog"); if (ret >= 0) id = ida_simple_get(&watchdog_ida, ret, ret + 1, GFP_KERNEL); }
if (id < 0)// 分配 ID 失败时的处理 id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL);
if (id < 0) return id; wdd->id = id;
ret = watchdog_dev_register(wdd);// 注册 watchdog 字符设备 if (ret) { ida_simple_remove(&watchdog_ida, id); if (!(id == 0 && ret == -EBUSY)) return ret;
/* Retry in case a legacy watchdog module exists */ // 处理旧版驱动冲突的特殊情况(id=0 时可能被占用) id = ida_simple_get(&watchdog_ida, 1, MAX_DOGS, GFP_KERNEL); if (id < 0) return id; wdd->id = id;
ret = watchdog_dev_register(wdd); if (ret) { ida_simple_remove(&watchdog_ida, id); return ret; } }
/* Module parameter to force watchdog policy on reboot. */ if (stop_on_reboot != -1) { if (stop_on_reboot) set_bit(WDOG_STOP_ON_REBOOT, &wdd->status); else clear_bit(WDOG_STOP_ON_REBOOT, &wdd->status); }
// 注册系统重启通知回调 if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { if (!wdd->ops->stop) pr_warn("watchdog%d: stop_on_reboot not supported\n", wdd->id); else { wdd->reboot_nb.notifier_call = watchdog_reboot_notifier;
/* * watchdog_dev_register: register a watchdog device * @wdd: watchdog device * * Register a watchdog device including handling the legacy * /dev/watchdog node. /dev/watchdog is actually a miscdevice and * thus we set it up like that. */
intwatchdog_dev_register(struct watchdog_device *wdd) { int ret; // 1. 注册字符设备(创建/dev/watchdogX 节点 ret = watchdog_cdev_register(wdd); if (ret) return ret; // 2. 注册预超时处理机制 ret = watchdog_register_pretimeout(wdd); if (ret) watchdog_cdev_unregister(wdd); // 失败时回滚字符设备注册
/* * watchdog_cdev_register: register watchdog character device * @wdd: watchdog device * * Register a watchdog character device including handling the legacy * /dev/watchdog node. /dev/watchdog is actually a miscdevice and * thus we set it up like that. */
staticintwatchdog_cdev_register(struct watchdog_device *wdd) { // 核心数据结构(包含设备状态、定时器等) structwatchdog_core_data *wd_data; int err;
// 传统设备注册(/dev/watchdog),注册为杂项设备 if (wdd->id == 0) { old_wd_data = wd_data; watchdog_miscdev.parent = wdd->parent; err = misc_register(&watchdog_miscdev);// 注册 misc 设备(兼容旧版接口) if (err != 0) { pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", wdd->info->identity, WATCHDOG_MINOR, err); if (err == -EBUSY)// 处理设备号冲突的特殊情况(旧驱动存在时返回 EBUSY) pr_err("%s: a legacy watchdog module is probably present.\n", wdd->info->identity); old_wd_data = NULL; put_device(&wd_data->dev); return err; } }
/* Fill in the data structures */ // 字符设备注册(创建/dev/watchdogX 节点) cdev_init(&wd_data->cdev, &watchdog_fops); // 绑定文件操作接口
/* Add the device */ err = cdev_device_add(&wd_data->cdev, &wd_data->dev);// 添加至系统 if (err) { pr_err("watchdog%d unable to add device %d:%d\n", wdd->id, MAJOR(watchdog_devt), wdd->id); if (wdd->id == 0) { misc_deregister(&watchdog_miscdev); old_wd_data = NULL; put_device(&wd_data->dev); } return err; }
wd_data->cdev.owner = wdd->ops->owner;
/* Record time of most recent heartbeat as 'just before now'. */ wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1); watchdog_set_open_deadline(wd_data);
/* * If the watchdog is running, prevent its driver from being unloaded, * and schedule an immediate ping. */ if (watchdog_hw_running(wdd)) {// 运行状态处理 __module_get(wdd->ops->owner); // 增加模块引用计数 get_device(&wd_data->dev); if (handle_boot_enabled)// 根据配置启动定时器(用户空间接管前维持心跳) hrtimer_start(&wd_data->timer, 0, HRTIMER_MODE_REL_HARD); else pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n", wdd->id); }
/* * watchdog_open: open the /dev/watchdog* devices. * @inode: inode of device * @file: file handle to device * * When the /dev/watchdog* device gets opened, we start the watchdog. * Watch out: the /dev/watchdog device is single open, so we make sure * it can only be opened once. */
/* Get the corresponding watchdog device */ if (imajor(inode) == MISC_MAJOR)// 传统设备通过 miscdevice 获取,新设备通过字符设备结构获取 wd_data = old_wd_data; else wd_data = container_of(inode->i_cdev, struct watchdog_core_data, cdev);
/* the watchdog is single open! */ /* 单例模式检测 - 保证设备只能被打开一次 */ if (test_and_set_bit(_WDOG_DEV_OPEN, &wd_data->status))/* 单例模式检测 - 保证设备只能被打开一次 */ return -EBUSY;
wdd = wd_data->wdd;
/* * If the /dev/watchdog device is open, we don't want the module * to be unloaded. */ hw_running = watchdog_hw_running(wdd);/* 模块引用管理 */ // 如果硬件未运行,增加模块引用计数防止卸载 if (!hw_running && !try_module_get(wdd->ops->owner)) { err = -EBUSY; goto out_clear; }
/* 更新设备引用计数 */ if (!hw_running) get_device(&wd_data->dev);// 增加设备引用计数
/* * open_timeout only applies for the first open from * userspace. Set open_deadline to infinity so that the kernel * will take care of an always-running hardware watchdog in * case the device gets magic-closed or WDIOS_DISABLECARD is * applied. */ wd_data->open_deadline = KTIME_MAX;
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */ return stream_open(inode, file);
/* * watchdog_write: writes to the watchdog. * @file: file from VFS * @data: user address of data * @len: length of data * @ppos: pointer to the file offset * * A write to a watchdog device is defined as a keepalive ping. * Writing the magic 'V' sequence allows the next close to turn * off the watchdog (if 'nowayout' is not set). */
/* * Note: just in case someone wrote the magic character * five months ago... */ /* 清除上次遗留的魔术字符标记 */ clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status);
/* scan to see whether or not we got the magic character */ for (i = 0; i != len; i++) {/* 扫描用户数据寻找魔术字符'V' */ if (get_user(c, data + i))// 从用户空间安全复制数据 return -EFAULT; if (c == 'V')// 检测到魔术字符 set_bit(_WDOG_ALLOW_RELEASE, &wd_data->status);// 允许安全关闭 }
/* someone wrote to us, so we send the watchdog a keepalive ping */
/* * watchdog_ioctl: handle the different ioctl's for the watchdog device. * @file: file handle to the device * @cmd: watchdog command * @arg: argument pointer * * The watchdog API defines a common set of functions for all watchdogs * according to their available features. */