S3C2440驱动简析——看门狗驱动

来源:互联网 发布:男生穿衣软件 编辑:程序博客网 时间:2024/06/07 05:45

     本文假设各位看官已经了解看门狗的工作原理,而且手上有2440的datasheet,可以随时查看看门狗相关的寄存器。我在这里仅仅专注于对驱动程序的分析,望见谅~

 

 

借用网上的朋友☆&寒 烟☆的一幅描述看门狗驱动程序的结构框图,总结得还是相当到位的,这里妄自copy如下(如有冒犯原作者请立刻通知,立删并道歉),各位可以按照结构图来按图索骥,更好地掌握驱动程序里面稍稍复杂的关系。

 

看过结构框图,想必对程序的构成有一个大致的认识了。下面开始贴上代码并分析之

 

watchdog_init & watchdog_exit

[c-sharp] view plaincopy
  1. static int __init watchdog_init(void)  
  2. {  
  3.     printk(banner);  
  4.     return platform_driver_register(&s3c2410wdt_driver);  
  5. }  
  6.   
  7. static void __exit watchdog_exit(void)  
  8. {  
  9.     platform_driver_unregister(&s3c2410wdt_driver);  
  10. }  
  11.   
  12. module_init(watchdog_init);  
  13. module_exit(watchdog_exit);  

上面两个函数分别是整个驱动的入口和出口,分别完成注册platform设备和注销platform设备的动作,其操作的platform设备由结构体s3c2410wdt_driver描述,代码如下

 

 结构体s3c2410wdt_driver

[c-sharp] view plaincopy
  1. static struct platform_driver s3c2410wdt_driver = {  
  2.     .probe      = s3c2410wdt_probe,  
  3.     .remove     = __devexit_p(s3c2410wdt_remove),  
  4.     .shutdown   = s3c2410wdt_shutdown,  
  5.     .suspend    = s3c2410wdt_suspend,  
  6.     .resume     = s3c2410wdt_resume,  
  7.     .driver     = {  
  8.         .owner  = THIS_MODULE,  
  9.         .name   = "s3c2410-wdt",  
  10.     },  
  11. };  

 对应文章一开始的框图,上述结构体绑定了5个函数和一些自身的信息。5个函数分别实现的功能如下。

 

s3c2410wdt_remove

[c-sharp] view plaincopy
  1. static int __devexit s3c2410wdt_remove(struct platform_device *dev)  
  2. {  
  3.     release_resource(wdt_mem);  //释放获取的watchdog平台设备的IO资源  
  4.     kfree(wdt_mem);  
  5.     wdt_mem = NULL;  
  6.   
  7.     free_irq(wdt_irq->start, dev);   //释放中断(重要)  
  8.     wdt_irq = NULL;  
  9.   
  10.     clk_disable(wdt_clock);   //释放看门狗时钟  
  11.     clk_put(wdt_clock);  
  12.     wdt_clock = NULL;  
  13.   
  14.     iounmap(wdt_base);   //释放内存映射地址  
  15.     misc_deregister(&s3c2410wdt_miscdev);   //注销混杂设备  
  16.     return 0;  
  17. }  

 

s3c2410wdt_shutdown

[c-sharp] view plaincopy
  1. static void s3c2410wdt_shutdown(struct platform_device *dev)  
  2. {  
  3.     s3c2410wdt_stop();  
  4. }  
  5.   
  6. static void s3c2410wdt_stop(void)  
  7. {  
  8.     spin_lock(&wdt_lock);    //自旋锁  
  9.     __s3c2410wdt_stop();  
  10.     spin_unlock(&wdt_lock);  
  11. }  
  12.   
  13. static void __s3c2410wdt_stop(void)  
  14. {  
  15.     unsigned long wtcon;  
  16.   
  17.         //这里就是直接对看门狗寄存器进行操作,至于如何赋值、赋什么值  
  18.          //大家自己查阅一下数据手册收获更多!  
  19.     wtcon = readl(wdt_base + S3C2410_WTCON);  
  20.     wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);  
  21.     writel(wtcon, wdt_base + S3C2410_WTCON);  
  22. }  

 

s3c2410wdt_suspend

[c-sharp] view plaincopy
  1. static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)  
  2. {  
  3.     /* 保存看门狗状态,之后关闭,即暂停功能 */  
  4.     wtcon_save = readl(wdt_base + S3C2410_WTCON);  
  5.     wtdat_save = readl(wdt_base + S3C2410_WTDAT);  
  6.   
  7.     /* Note that WTCNT doesn't need to be saved. */  
  8.     s3c2410wdt_stop();  
  9.   
  10.     return 0;  
  11. }  

 

s3c2410wdt_resume

[c-sharp] view plaincopy
  1. static int s3c2410wdt_resume(struct platform_device *dev)  
  2. {  
  3.     /* 重装看门狗状态 */  
  4.   
  5.     writel(wtdat_save, wdt_base + S3C2410_WTDAT);  
  6.     writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */  
  7.     writel(wtcon_save, wdt_base + S3C2410_WTCON);  
  8.   
  9.     printk(KERN_INFO PFX "watchdog %sabled/n",  
  10.            (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");  
  11.   
  12.     return 0;  
  13. }  

 

s3c2410wdt_probe

[c-sharp] view plaincopy
  1. static int __devinit s3c2410wdt_probe(struct platform_device *pdev)  
  2. {  
  3.     struct resource *res;   //定义一个资源,用来保存获取的watchdog的IO资源  
  4.     struct device *dev;  
  5.     unsigned int wtcon;  
  6.     int started = 0;  
  7.     int ret;  
  8.     int size;  
  9.   
  10.     DBG("%s: probe=%p/n", __func__, pdev);  
  11.   
  12.     dev = &pdev->dev;  
  13.     wdt_dev = &pdev->dev;  
  14.   
  15.     /* get the memory region for the watchdog timer */  
  16.   
  17.         //获取watchdog平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志  
  18.          //和watchdog平台设备定义中的一致  
  19.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  20.     if (res == NULL) {  
  21.         dev_err(dev, "no memory resource specified/n");  
  22.         return -ENOENT;  
  23.     }  
  24.   
  25.     size = (res->end - res->start) + 1;  
  26.   
  27.         //request_mem_region标记这段物理地址自己使用了  
  28.     wdt_mem = request_mem_region(res->start, size, pdev->name);  
  29.     if (wdt_mem == NULL) {  
  30.         dev_err(dev, "failed to get memory region/n");  
  31.         ret = -ENOENT;  
  32.         goto err_req;  
  33.     }  
  34.   
  35.         //把watchdog的io端口映射到内存虚拟地址,size为映射地址的长度  
  36.     wdt_base = ioremap(res->start, size);  
  37.     if (wdt_base == NULL) {  
  38.         dev_err(dev, "failed to ioremap() region/n");  
  39.         ret = -EINVAL;  
  40.         goto err_req;  
  41.     }  
  42.   
  43.     DBG("probe: mapped wdt_base=%p/n", wdt_base);  
  44.   
  45.         //获取中断号  
  46.     wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
  47.     if (wdt_irq == NULL) {  
  48.         dev_err(dev, "no irq resource specified/n");  
  49.         ret = -ENOENT;  
  50.         goto err_map;  
  51.     }  
  52.   
  53.         //申请中断  
  54.     ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);  
  55.     if (ret != 0) {  
  56.         dev_err(dev, "failed to install irq (%d)/n", ret);  
  57.         goto err_map;  
  58.     }  
  59.   
  60.         //申请时钟  
  61.     wdt_clock = clk_get(&pdev->dev, "watchdog");  
  62.     if (IS_ERR(wdt_clock)) {  
  63.         dev_err(dev, "failed to find watchdog clock source/n");  
  64.         ret = PTR_ERR(wdt_clock);  
  65.         goto err_irq;  
  66.     }  
  67.   
  68.         //时钟获取后要使能后才可以使用,clk_enable定义在arch/arm/plat-s3c/clock.c中  
  69.     clk_enable(wdt_clock);  
  70.   
  71.     /* see if we can actually set the requested timer margin, and if 
  72.      * not, try the default value */  
  73.   
  74.     if (s3c2410wdt_set_heartbeat(tmr_margin)) {  
  75.         started = s3c2410wdt_set_heartbeat(  
  76.                     CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);  
  77.   
  78.         if (started == 0)  
  79.             dev_info(dev,  
  80.                "tmr_margin value out of range, default %d used/n",  
  81.                    CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);  
  82.         else  
  83.             dev_info(dev, "default timer value is out of range, "  
  84.                             "cannot start/n");  
  85.     }  
  86.   
  87.         //把wdt又注册为一个杂项设备,注意结构体s3c2410wdt_miscdev  
  88.     ret = misc_register(&s3c2410wdt_miscdev);  
  89.     if (ret) {  
  90.         dev_err(dev, "cannot register miscdev on minor=%d (%d)/n",  
  91.             WATCHDOG_MINOR, ret);  
  92.         goto err_clk;  
  93.     }  
  94.   
  95.     if (tmr_atboot && started == 0) {  
  96.         dev_info(dev, "starting watchdog timer/n");  
  97.         s3c2410wdt_start();  
  98.     } else if (!tmr_atboot) {  
  99.         /* if we're not enabling the watchdog, then ensure it is 
  100.          * disabled if it has been left running from the bootloader 
  101.          * or other source */  
  102.   
  103.         s3c2410wdt_stop();  
  104.     }  
  105.   
  106.     /* print out a statement of readiness */  
  107.   
  108.     wtcon = readl(wdt_base + S3C2410_WTCON);  
  109.   
  110.     dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled/n",  
  111.          (wtcon & S3C2410_WTCON_ENABLE) ?  "" : "in",  
  112.          (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",  
  113.          (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");  
  114.   
  115.     return 0;  
  116.   
  117.  err_clk:  
  118.     clk_disable(wdt_clock);  
  119.     clk_put(wdt_clock);  
  120.   
  121.  err_irq:  
  122.     free_irq(wdt_irq->start, pdev);  
  123.   
  124.  err_map:  
  125.     iounmap(wdt_base);  
  126.   
  127.  err_req:  
  128.     release_resource(wdt_mem);  
  129.     kfree(wdt_mem);  
  130.   
  131.     return ret;  
  132. }  

 

留意到,当注册wdt为杂项设备时,参数里的结构体s3c2410wdt_miscdev,代码如

[c-sharp] view plaincopy
  1. static struct miscdevice s3c2410wdt_miscdev = {  
  2.     .minor      = WATCHDOG_MINOR,  
  3.     .name       = "watchdog",  
  4.     .fops       = &s3c2410wdt_fops,  
  5. };  

哈哈,看到这个东东,应该很熟悉了吧,在之前的一些列驱动里都有接触过的结构体。其中我们关注.fops,它为我们提供了若干工具函数。

[c-sharp] view plaincopy
  1. static const struct file_operations s3c2410wdt_fops = {  
  2.     .owner      = THIS_MODULE,  
  3.     .llseek     = no_llseek,  
  4.     .write      = s3c2410wdt_write,  
  5.     .unlocked_ioctl = s3c2410wdt_ioctl,  
  6.     .open       = s3c2410wdt_open,  
  7.     .release    = s3c2410wdt_release,  
  8. };  

 

s3c2410wdt_open

[c-sharp] view plaincopy
  1. static int s3c2410wdt_open(struct inode *inode, struct file *file)  
  2. {  
  3.     //试着获取信号量(即:加锁),如果获取不成功,说明其他进程此时占用了,就返回忙  
  4.         if (test_and_set_bit(0, &open_lock))  
  5.         return -EBUSY;  
  6.   
  7.     //如果内核配置了CONFIG_WATCHDOG_NOWAYOUT项,则使模块使用计数加1  
  8.         if (nowayout)  
  9.         __module_get(THIS_MODULE);  
  10.   
  11.     expect_close = 0;  
  12.   
  13.     /* start the timer */  
  14.     s3c2410wdt_start();  
  15.   
  16.         //表示返回的这个设备文件是不可以被seek操作的,nonseekable_open定义在fs.h中  
  17.     return nonseekable_open(inode, file);  
  18. }  
  19.   
  20. static void s3c2410wdt_start(void)  
  21. {  
  22.     //整个函数直接操作寄存器,嘿嘿,还是不解释  
  23.         unsigned long wtcon;  
  24.   
  25.     spin_lock(&wdt_lock);   //尝试取得自旋锁  
  26.   
  27.     __s3c2410wdt_stop();  
  28.   
  29.     wtcon = readl(wdt_base + S3C2410_WTCON);  
  30.     wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;  
  31.   
  32.     if (soft_noboot) {  
  33.         wtcon |= S3C2410_WTCON_INTEN;  
  34.         wtcon &= ~S3C2410_WTCON_RSTEN;  
  35.     } else {  
  36.         wtcon &= ~S3C2410_WTCON_INTEN;  
  37.         wtcon |= S3C2410_WTCON_RSTEN;  
  38.     }  
  39.   
  40.     DBG("%s: wdt_count=0x%08x, wtcon=%08lx/n",  
  41.         __func__, wdt_count, wtcon);  
  42.   
  43.     writel(wdt_count, wdt_base + S3C2410_WTDAT);  
  44.     writel(wdt_count, wdt_base + S3C2410_WTCNT);  
  45.     writel(wtcon, wdt_base + S3C2410_WTCON);  
  46.     spin_unlock(&wdt_lock);  
  47. }  

 

s3c2410wdt_release

[c-sharp] view plaincopy
  1. static int s3c2410wdt_release(struct inode *inode, struct file *file)  
  2. {  
  3.     /* 
  4.      *  Shut off the timer. 
  5.      *  Lock it in if it's a module and we set nowayout 
  6.      */  
  7.   
  8.         // 如果判断到当前操作状态是可以关闭看门狗定时器时就关闭,否则就是“喂狗”状态  
  9.     if (expect_close == 42)  
  10.         s3c2410wdt_stop();  
  11.     else {  
  12.         dev_err(wdt_dev, "Unexpected close, not stopping watchdog/n");  
  13.         s3c2410wdt_keepalive();   //喂狗  
  14.     }  
  15.     expect_close = 0;  
  16.     clear_bit(0, &open_lock);  
  17.     return 0;  
  18. }  

 

s3c2410wdt_write

[c-sharp] view plaincopy
  1. static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,  
  2.                 size_t len, loff_t *ppos)  
  3. {  
  4.     /* 
  5.      *  Refresh the timer. 
  6.      */  
  7.     if (len) {  
  8.         if (!nowayout) {  
  9.             size_t i;  
  10.   
  11.             /* In case it was set long ago */  
  12.             expect_close = 0;  
  13.   
  14.             for (i = 0; i != len; i++) {  
  15.                 char c;  
  16.   
  17.                 if (get_user(c, data + i))  
  18.                     return -EFAULT;  
  19.                 if (c == 'V')   //写入字符 V容许关闭看门狗,但前提还是  
  20.                                                        //不配置CONFIG_WATCHDOG_NOWAYOUT  
  21.                     expect_close = 42;  
  22.             }  
  23.         }  
  24.         s3c2410wdt_keepalive();   //喂狗  
  25.     }  
  26.     return len;  
  27. }  

 

 s3c2410wdt_ioctl

 

[c-sharp] view plaincopy
  1. static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd,  
  2.                             unsigned long arg)  
  3. {  
  4.     void __user *argp = (void __user *)arg;  
  5.     int __user *p = argp;  
  6.     int new_margin;  
  7.   
  8.     switch (cmd) {  
  9.     case WDIOC_GETSUPPORT:  //获取看门狗的支持信息,wdt_ident定义在上面  
  10.         return copy_to_user(argp, &s3c2410_wdt_ident,  
  11.             sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;  
  12.     case WDIOC_GETSTATUS:   //获取看门狗状态  
  13.     case WDIOC_GETBOOTSTATUS:  
  14.         return put_user(0, p);  
  15.     case WDIOC_KEEPALIVE:   //喂狗命令  
  16.         s3c2410wdt_keepalive();  
  17.         return 0;  
  18.     case WDIOC_SETTIMEOUT:  //设置定时器溢出时间值命令(以秒为单位)  
  19.         if (get_user(new_margin, p))  
  20.             return -EFAULT;  
  21.         if (s3c2410wdt_set_heartbeat(new_margin))  
  22.             return -EINVAL;  
  23.         s3c2410wdt_keepalive();  
  24.         return put_user(tmr_margin, p);  
  25.     case WDIOC_GETTIMEOUT:  //读取定时器默认溢出时间值命令  
  26.         return put_user(tmr_margin, p);  
  27.     default:  
  28.         return -ENOTTY;  
  29.     }  
  30. }  

 

到这里为止,看门狗驱动函数已经全部介绍完毕,可能有朋友跟我一样,一开始会迷惑,到底看门狗算是什么设备吖。它究竟是平台设备(platform)、混杂设备(misc)、还是字符设备(char)呢。这里我摘录一段网友的解释,视点比较独特,深入浅出,摘录如下:

 

曾看到这样一段话,说一粒花生,如果我们把它看作字符设备,当我们煮着炖着吃了,它就成了菜中的一个混杂设备,但是了它总是从挂在花生秧的根部上的一员长来了,因此它是可以作为一个花生秧苗中一个独立的设备而存在,所以就也可以定义为一个平台设备。这说明什么呢?字符设备是对其本质的描述,说明是串行,顺序访问的(而不是缓冲随机的),混杂设备是存放这个字符设备的容器,即这个设备是被丢在使用了同一设备号的混杂设备里面,平台设备则是描述了设备的一种特性或者说属性。换句话说就是这个设备属于平台的独立模块,完全是一种附加的属性。


原创粉丝点击