[linux字符驱动]Watchdog如何躲过linux的启动时间

本文介绍了在Linux系统中,如何处理Watchdog在ARM系统启动时间较长的情况,提出了三种解决方案,并详细讲解了Linux字符驱动的编写,特别是如何利用epoll减少CPU开销。同时分享了其他Linux驱动和底层知识的相关资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Watchdog做过项目的同学应该不会陌生,主要是启动程序守护作用,程序跑飞后能够让MCU重新复位,让设备进入正常工作模式,通常所说的看门狗,普通的看门狗喂狗的周期是1.6S,只要在1.6S的时间内,让看门狗的喂狗电平进行反转一次,看门狗内部的计数器就会清0,如果超过1.6S周期没有反转喂狗信号,则会发出一个下降沿的电平给MCU的RST引脚,MCU从而复位。
普通单片机的操作是这样的,如果换成了ARM系统,1.6S周期对于Linux来说,启动时间是不够的,先从bootstrap->uboot->kernel->文件系统->应用程序;这个过程下来大概要10S时间,已经算是非常快了。
我接触的Linux系统方案有以下两种:

1、看门狗的Rst引脚和ARM的Rst脚之间加一个使能芯片,就像是一个开关一样,默认是关闭的,复位信号过不来,系统起来后,应用程序把开关打开,让复位信号导通;

缺点就是无法保证复位信号是不是导通的,也不能很清晰的知道硬件的开关是否正常。

在这里插入图片描述

2、ARM的外侧加一个单片机用来喂狗,ARM与单片机使用串口通讯,在系统启动之前喂狗权在单片机上面,系统启动后双机能够正常通讯喂狗权交给ARM。

缺点成本较高,通讯复杂。

3、基于以上两种呢,都不是很优的方案,后面找了一款超长时间的比较器,也能输出RST信号,用作看门狗比较好,并且还能输出提醒喂狗信号,ARM端可以做是否焊接看门狗芯片的逻辑;

在这里插入图片描述

下面主要介绍一下Linux下的字符驱动编写:

上期有小伙伴提了疑问,为什么IO的配置不放在设备树里面呢,你把IO配置还写在字符驱动的头文件当中是不是很Low,感觉不是很方便啊,工作和项目中难道是这么用的吗?

设备树里面放对应字符驱动IO配置当然没有问题,我现在目前的字符驱动都是模块驱动,就是不用更改内核的情况下可以随意更改驱动,就像是个应用程序一样,编译好的文件放入文件系统中,然后使用命令加载就可以了,如果IO的配置放在设备树里面,管脚有改动的话还要重新烧录设备树问题,根本做不到远程升级字符驱动,不利于应付工作中的突发需求。

关注微信公众号,回复“看门狗字符驱动”,下载源代码。

字符驱动的模型还是套用gpio系统;

1、模块的入口函数


module_init(at91_watdog_init);
module_exit(at91_watdog_cleanup);


MODULE_DESCRIPTION ("AT91 watdog detect DRIVER"); 
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR ("Jack"); 
MODULE_LICENSE ("GPL");
调用insmod和rmmod的时候会调用这两个函数;

2、创建init和cleanup函数
定义初始化信息,喂狗信号和捕捉提醒喂狗信号

struct watdog_pin
{
    ulong wdgfree_pin;  //
    ulong wdgdete_pin;  //
};

struct St_watdog
{
    dev_t dev_major;
    int sys_irq;
    int hard_irq;

    atomic_t open_count;
    int detect_flag;// 断电检查标志
    struct watdog_pin st_pin;
    struct mutex watdog_mudex;//互斥锁
    spinlock_t watdog_spinlock;
    wait_queue_head_t watdog_poll_head;
    struct cdev watdog_dev;//
    struct class *watdog_class;
    struct device *watdog_device;
};

3、注册及注销驱动流程

static int __init at91_watdog_init(void)
{ 
    int retval = -1;
    dev_t dev_id;

    // 分配一个结构
    pwdg = (struct St_watdog *)kzalloc(sizeof (struct St_watdog),GFP_KERNEL);
    if(!pwdg)
    {
        printk("%s:kzalloc failded from:%s\n", __func__,WATCHDOG_NAME);
        return -ENOMEM;
    }

    //需要减去偏移量

    pwdg->st_pin.wdgfree_pin = WATCHDOG_PIN;
    pwdg->st_pin.wdgdete_pin = WATDETECT_PIN;

    mutex_init(&(pwdg->watdog_mudex));
    spin_lock_init(&(pwdg->watdog_spinlock));
    //init_timer
    init_waitqueue_head(&pwdg->watdog_poll_head);
    pwdg->detect_flag =0;

    //分配主设备号
    if (watdog_major) {
        dev_id = MKDEV(watdog_major, 0);
        retval = register_chrdev_region(dev_id, MAX_WATDOG_NB,
                        WATCHDOG_NAME);
    } else {
        retval = alloc_chrdev_region(&dev_id, 0, MAX_WATDOG_NB,
                         WATCHDOG_NAME);
        watdog_major = MAJOR(dev_id);
    }
    if(retval<0)
    {
        printk("watdog: register error!\n");
        goto error_malloc;
    }
    else
        printk("watdog:watdog_major:%d\n",watdog_major);    

    cdev_init(&(pwdg->watdog_dev), &at91_watdog_fops);
    retval = cdev_add(&(pwdg->watdog_dev),dev_id,MAX_WATDOG_NB);
    if(retval<0)
    {
        printk("watdog:cdev_add failed!!!\n");
        goto error_reg;
    }


    //创建类
    pwdg->watdog_class = class_create(THIS_MODULE,WATCHDOG_NAME);
    if(!pwdg->watdog_class)
    {
        printk("watdog:class_create error!\n");
        goto error_cdev;
    }

    pwdg->watdog_device= device_create(pwdg->watdog_class, NULL,
                   MKDEV(watdog_major, 0),
                   NULL, WATCHDOG_NAME);
    if(!pwdg->watdog_device)
    {
        printk("watdog:device_create error!\n");
        goto error_class;
    }

    return 0;


error_class:
    class_destroy(pwdg->watdog_class);
error_cdev:
    cdev_del(&(pwdg->watdog_dev));
error_reg:
    unregister_chrdev_region(dev_id,MAX_WATDOG_NB);
error_malloc:
    kfree(pwdg);

    return retval;
} 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void __exit at91_watdog_cleanup(void)
{ 
    dev_t dev_id;
    dev_id = MKDEV(watdog_major, 0);

    if(pwdg->watdog_device)
    {
        device_destroy(pwdg->watdog_class,MKDEV(watdog_major, 0));
    }
    if(pwdg->watdog_class)
    {
        class_destroy(pwdg->watdog_class);
    }
    // del cdev
    cdev_del(&(pwdg->watdog_dev));
    if(watdog_major)
    {
        unregister_chrdev_region(dev_id,MAX_WATDOG_NB);
    }
    //mutex destory
    mutex_destroy(&pwdg->watdog_mudex);
    //释放内存
    kfree(pwdg);

    printk("watdog:mod is cleanup.\n"); 
} 

4、驱动的操作方法,这里面用到了epoll,用于捕捉信号,比如按键、YX信号等,应用层可以使用select这种非阻塞的方式读取信号,减少CPU的开销,让系统更加的丝滑。

static struct file_operations at91_watdog_fops={ 
    .owner      = THIS_MODULE, 
    .llseek     = NULL,
    .read       = NULL,
    .write      = NULL,
    .unlocked_ioctl      = at91_watdog_ioctl, 
    .open       = at91_watdog_open, 
    .poll       = at91_watdog_poll,
    .release    = at91_watdog_close,
};

static long at91_watdog_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{ 
        int ret = 0;
    struct St_watdog *pwdg;
    pwdg = (struct St_watdog *)filp->private_data;

    //检查命令的有效性
     if ((_IOC_TYPE(cmd) != WDG_MAGIC_NB) || (_IOC_NR(cmd) > WDG_IOC_MAXNR))
     {
        printk("%s:CMD error\n",__func__);
        return -EINVAL;
     }  

     //判断空间是否可访问
      if(_IOC_DIR(cmd) & _IOC_READ)
        ret = !access_ok(VERIFY_WRITE,(void *)args,_IOC_SIZE(cmd));
    else if(_IOC_DIR(cmd) & _IOC_WRITE)
        ret = !access_ok(VERIFY_READ,(void *)args,_IOC_SIZE(cmd));
    if(ret)
        return -EFAULT;

    //互斥锁
    if(mutex_lock_interruptible(&(pwdg->watdog_mudex)))
        {
        return -ERESTARTSYS;
        }

    switch(cmd) 
    {
        case WATCHDOG_OUTPUT_LOW:
            gpio_set_value(pwdg->st_pin.wdgfree_pin,0);
            break;

        case WATCHDOG_OUTPUT_HIGH:
            gpio_set_value(pwdg->st_pin.wdgfree_pin,1);
            break;

        default:
            printk("watdog:error cmd %d.\n", cmd);
            mutex_unlock(&(pwdg->watdog_mudex));
            return -EINVAL;
    }
    mutex_unlock(&(pwdg->watdog_mudex));
    return ret;
}

static irqreturn_t at91_watdog_isr(int irq, void *dev_id)
{
    struct St_watdog *pwdg = (struct St_watdog *)dev_id;
    spin_lock(&pwdg->watdog_spinlock);
    if(irq == pwdg->sys_irq)
    {
         pwdg->detect_flag = 1;//可读
         wake_up_interruptible(&pwdg->watdog_poll_head);
    }
    spin_unlock(&pwdg->watdog_spinlock);
    return IRQ_HANDLED;
}

static int at91_watdog_open(struct inode *inode, struct file *filp)
{
    struct St_watdog *pwdg;

    int ret=0;

    pwdg = container_of(inode->i_cdev, struct St_watdog,watdog_dev);  
    filp->private_data = (void *)pwdg;

    if(atomic_read(&(pwdg->open_count))>0)
    {
        printk("at91_watdog_open:busy!!!\n");
        return -EAGAIN;
    }
    else
    {
        atomic_add(1,&(pwdg->open_count));
    }

#if 0    
    at91_set_GPIO_periph(pwdg->st_pin.wdgfree_pin, 1);  /* wdg_free */
    at91_set_GPIO_periph(pwdg->st_pin.wdgdete_pin, 1);
    at91_set_deglitch(pwdg->st_pin.wdgdete_pin, 1);
#endif

    ret = gpio_request(pwdg->st_pin.wdgfree_pin, "wdgfree_pin");
    if (ret < 0) {
          printk("watchdog wdgfree_pin %ld\n", pwdg->st_pin.wdgfree_pin);

    }

    gpio_direction_output(pwdg->st_pin.wdgfree_pin, 1);
    gpio_set_value(pwdg->st_pin.wdgfree_pin, 1);        

    //获取gpio描述符号
/*    watdog_gpio_des = gpio_to_desc(pwdg->st_pin.wdgdete_pin);
    if(!watdog_gpio_des)
    {
        printk("watdog:get watdog_gpio_des failed!!\n");
        return -ENODEV;
    }
    else
    {
        pwdg->sys_irq = gpiod_to_irq(watdog_gpio_des);
        printk("watdog:gpio_irq:%d\n",pwdg->sys_irq);
    }
    */

    pwdg->sys_irq = gpio_to_irq(pwdg->st_pin.wdgdete_pin);
    irq_set_status_flags(pwdg->sys_irq, IRQ_NOAUTOEN);
    ret = request_irq(pwdg->sys_irq,at91_watdog_isr,IRQ_TYPE_EDGE_BOTH,WATCHDOG_NAME,pwdg);
    if(ret)
    {
        printk("watdog:request_irq error:%d\n",pwdg->sys_irq);
        return ret;
    }
    enable_irq(pwdg->sys_irq);

    return nonseekable_open(inode,filp);

}

static int at91_watdog_close(struct inode *inode, struct file *filp)
{
       struct St_watdog *pwdg = (struct St_watdog  *)filp->private_data;  

    atomic_sub(1,&(pwdg->open_count));
    disable_irq(pwdg->sys_irq);
    free_irq(pwdg->sys_irq,pwdg);
    gpio_free(pwdg->st_pin.wdgdete_pin);
    gpio_free(pwdg->st_pin.wdgfree_pin);
    return 0;
}

unsigned int at91_watdog_poll(struct file *filp,struct poll_table_struct *poll_table)
{
    unsigned int mask = 0;
    unsigned long flags=0;

    struct St_watdog *pwdg;  
    pwdg =  (struct St_watdog *)filp->private_data;

    poll_wait(filp, &pwdg->watdog_poll_head, poll_table);

    spin_lock_irqsave(&pwdg->watdog_spinlock,flags);
    if (pwdg->detect_flag)
    {
        pwdg->detect_flag = 0;
        mask |= (POLLIN | POLLRDNORM);
    }   
    spin_unlock_irqrestore(&pwdg->watdog_spinlock,flags);

    return mask;

}

应用程序的编写

int main()
{    
    int iCnt=0;
    int value =0;
    int inum = 0;
    int idetect = 0;

    fd_set client_fd_set;  
       struct timeval tv;
    int fd = 0;



    for(;;)
    {
            fd = open("/dev/"WATCHDOG_NAME, 0);

            if (fd < 0)
            {
                printf("watdg:failed to open watdg.\n");
                return 0;       
            }
            else
            {
                printf("watdg:open watdg sucess!!!\n");
            }

            tv.tv_sec = 2;  
            tv.tv_usec = 0;//10ms  
            FD_ZERO(&client_fd_set);   
            FD_SET(fd, &client_fd_set);
            int ret;
            ret = select(fd + 1, &client_fd_set, NULL, NULL, &tv); 

            if(ret<0)
           {
                perror("select error\n");
                exit(0);
           }
           else if(0==ret)
           {
                printf("watdg:select time out\n");
           }
           else
           {
             printf("watdg:catch watdg single\n");
            idetect = 1;
           }

        inum ++;
//        if ((inum > 10000000 ) && idetect)
        if ( idetect)
        {
            inum = 0;
            idetect = 0;
             usleep(500000);
             ioctl(fd,WATCHDOG_OUTPUT_LOW, 0);
             usleep(500000);
            ioctl(fd,WATCHDOG_OUTPUT_HIGH, 0);
        }

        close(fd);
    }
    close(fd);

}

更多linux知识点推荐:

[Linux字符驱动] LED点灯试验

[Linux 驱动]模块加载RTX8025驱动

[linux kernel] 内核下RX8025对接系统时钟

[linux kernel]内核启动阶段控制IO口时序输出

[职场吐槽]如何缓解焦虑

[linux kernel] 内核下ksz8081驱动调试

[linux kernel] 内核下ksz9031驱动调试

[linux kernel]内核图形化裁剪配置

[linux kernel]内核移植过程记录

[linux kernel] 内核启动流程梳理

[linux 底层]u-boot EMMC驱动

[linux 底层]u-boot图形化裁剪配置

[Linux 底层]U-boot ksz9031网络驱动调试

[Linux 底层]U-boot调试命令使用技巧

[Linux 底层]U-boot编译移植

[Linux 底层]U-boot烧录脚本介绍SecureCRT

[Linux 底层]bootstrap移植裁剪及编译

[Linux 底层] 平台软件分层介绍

[Linux 驱动] RS485测试程序编写

[Linux 驱动] CAN测试程序编写

推荐阅读:

芯片手册解读 | Linux底层 | 职场吐槽 | C语言视频

关注微信公众号,回复“看门狗字符驱动”,下载源代码。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值