Linux version 5.4.31
usr_wtd {
compatible = "user-watchdog";
status = "okay";
gpio = <&gpiof 3 GPIO_ACTIVE_HIGH>;
pluse-width = <100>; /* 1ms */
pluse-inverse = <1>;
pluse-count = <3>;
timeout-sec = <5>;
};
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
#include
第一类延时函数原型是:(忙等)
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
说明:内核函数 ndelay, udelay, 以及 mdelay 对于短延时好用, 分别延后执行指定的纳秒数, 微秒数或者毫秒数. 它们涉及到的延时常常是最多几个毫秒。
第二类延时函数原型是:(使进程进入休眠)
void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds)
*/
#define WATCHDOG_TAG "[wtd_tag]"
#define WATCHDOG_MAJOR 0
#define WATCHDOG_NAME "usr_wdt" //设备节点
struct wtd_device_t {
int gpio;
int pluse_width;
int pluse_count;
int timeout_sec;
int pluse_inverse;
int status;
struct timer_list timer;
struct class *wtd_class;
int major;
};
static struct wtd_device_t watchdog_dev;
static void feed_dog(struct wtd_device_t *dev)
{
int i = 0;
//printk(WATCHDOG_TAG"feed dog,count:%d,gpio:%d,width:%d\n",dev->pluse_count,dev->gpio,dev->pluse_width);
for(i=0;i<dev->pluse_count;i++){
gpio_direction_output(dev->gpio,dev->pluse_inverse);
mdelay(dev->pluse_width);
gpio_direction_output(dev->gpio,!dev->pluse_inverse);
mdelay(dev->pluse_width);
}
gpio_direction_output(dev->gpio,dev->pluse_inverse);
}
static void timer_function(struct timer_list *t)
{
struct wtd_device_t *wtd = from_timer(wtd, t, timer);
feed_dog(wtd);
mod_timer(&wtd->timer, jiffies + wtd->timeout_sec * HZ);
}
static int watchdog_pin_config(int gpio)
{
int ret = 0;
gpio_free(gpio);
ret = gpio_request(gpio,"watchdog_pin");
if(ret){
printk(WATCHDOG_TAG"%s::request gpio[%d] failed\n",__FUNCTION__,gpio);
return -1;
}else{
printk(WATCHDOG_TAG"%s::request gpio[%d] successfully\n",__FUNCTION__,gpio);
}
return 0;
}
static int init_wtd_timer(struct wtd_device_t *dev)
{
timer_setup(&dev->timer,timer_function,0);
mod_timer(&dev->timer, jiffies + dev->timeout_sec * HZ);
return 0 ;
}
static int watchdog_open(struct inode* inode,struct file* file)
{
return 0;
}
static int watchdog_release(struct inode* inode ,struct file* file)
{
return 0;
}
#if 0
static int watchdog_ioctl(struct inode* inode, struct file* file,unsigned int cmd, unsigned long arg)
{
return 0;
}
#endif
static int watchdog_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
return 0;
}
static int watchdog_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
return 0;
}
static struct file_operations watchdog_fops =
{
.owner = THIS_MODULE,
.open = watchdog_open,
.release = watchdog_release,
.write = watchdog_write,
.read = watchdog_read,
};
static int user_wtd_probe(struct platform_device * pdev)
{
struct device_node * node = pdev->dev.of_node;
int ret;
int num = 0;
int gpio = 0;
enum of_gpio_flags flags;
struct wtd_device_t *dev = &watchdog_dev;
if(!node)
return -ENODEV;
dev->major = register_chrdev(WATCHDOG_MAJOR,WATCHDOG_NAME,&watchdog_fops);
if(dev->major <0){
printk(WATCHDOG_TAG" register failed \n");
return dev->major;
}
dev->wtd_class = class_create(THIS_MODULE,WATCHDOG_NAME);
if(IS_ERR(dev->wtd_class)){
printk(WATCHDOG_TAG " register class failed \n");
return -1;
}
device_create(dev->wtd_class,NULL,MKDEV(dev->major,0),NULL,WATCHDOG_NAME);
/*
usr_wtd {
compatible = "user-watchdog";
status = "okay";
gpio = <&gpioh 15 GPIO_ACTIVE_HIGH>;
pluse-inverse = <1>;
pluse-width = <1>;
pluse-count = <2>;
timeout-sec = <32>;
};
*/
ret = of_property_read_u32(node, "pluse-inverse", &num);
if (ret) {
pr_err(WATCHDOG_TAG"pluse-inverse no found !!!\n");
goto INIT_ERR_FREE;
}
printk("pluse-inverse:%d\n",num);
dev->pluse_inverse = num;
ret = of_property_read_u32(node, "pluse-width", &num);
if (ret || !num) {
pr_err(WATCHDOG_TAG"pluse-width no found !!!\n");
goto INIT_ERR_FREE;
}
printk("pluse-width:%d\n",num);
dev->pluse_width = num;
ret = of_property_read_u32(node, "pluse-count", &num);
if (ret || !num) {
pr_err(WATCHDOG_TAG"pluse-count no found !!!\n");
goto INIT_ERR_FREE;
}
printk("pluse-count:%d\n",num);
dev->pluse_count = num;
ret = of_property_read_u32(node, "timeout-sec", &num);
if (ret || !num) {
pr_err(WATCHDOG_TAG"timeout-sec no found !!!\n");
goto INIT_ERR_FREE;
}
printk("timeout-sec:%d\n",num);
dev->timeout_sec = num;
gpio = of_get_named_gpio_flags(node, "gpio", 0,&flags);
dev->gpio = gpio;
ret = watchdog_pin_config(gpio);
if (ret) {
goto INIT_ERR_FREE;
}
init_wtd_timer(dev);
dev_set_drvdata(&pdev->dev, dev);
pr_info("init watchdog dev finish\n");
return 0;
INIT_ERR_FREE:
pr_err("init watchdog dev err\n");
return -1;
}
static int user_wtd_remove(struct platform_device *pdev)
{
struct wtd_device_t * dev = dev_get_drvdata(&pdev->dev);
if(dev != NULL){
del_timer(&dev->timer);
unregister_chrdev(dev->major,WATCHDOG_NAME);
device_destroy(dev->wtd_class,MKDEV(dev->major ,0));
class_destroy(dev->wtd_class);
}
pr_info("exit user watchdog \n");
return 0;
}
#ifdef CONFIG_PM
static int user_wtd_suspend(struct device *dev)
{
return 0;
}
static int user_wtd_resume(struct device *dev)
{
return 0;
}
#else
#define user_wtd_suspend NULL
#define user_wtd_resume NULL
#endif
static const struct dev_pm_ops user_wtd_pm_ops = {
.suspend = user_wtd_suspend,
.resume = user_wtd_resume,
};
static struct of_device_id user_wtd_of_match[] = {
{ .compatible = "user-watchdog" },
{},
};
MODULE_DEVICE_TABLE(of, user_wtd_of_match);
static struct platform_driver user_wtd_driver = {
.driver = {
.name = "user-wtd",
.owner = THIS_MODULE,
.pm = &user_wtd_pm_ops,
.of_match_table = of_match_ptr(user_wtd_of_match),
},
.probe = user_wtd_probe,
.remove = user_wtd_remove,
};
module_platform_driver(user_wtd_driver);
MODULE_DESCRIPTION("user watchdog driver");
MODULE_AUTHOR("zhengweiqing, 1548889230@qq.com");
MODULE_LICENSE("GPL");
末尾增加:
obj-m += usr_wtd.o
insmod usr_wtd.ko
usr_wtd {
compatible = "user-watchdog";
status = "okay";
gpio = <&gpiof 3 GPIO_ACTIVE_HIGH>;
pluse-width = <100>;
pluse-inverse = <1>;
pluse-count = <3>;
timeout-sec = <5>;
};
timeout-sec:周期,单位秒
pluse-inverse:脉冲是否反向
pluse-count:脉冲个数
pluse-width:脉冲宽度,单位毫秒
这里举例的是:每隔5秒有3个宽度为100毫秒的脉冲。