| 前面两篇一篇分析了看门狗定时器的驱动架构,另一篇分析了平台设备对应的probe函数,虽然对应的remove函数没分析,其实和别的平台设备驱动一样,做和probe函数相反的工作。这一篇要说点啥呢? S3c2410_wdt.c (linux2.6.28\drivers\watchdog)在这个文件中还有很多函数,我们都没有分析过,这一篇就找些重要的函数分析下。 1、那就先从s3c2410wdt_start函数开始,此函数开启看门狗,源码如下: static void s3c2410wdt_start(void){
 unsigned long wtcon;
 
 spin_lock(&wdt_lock);
 
 __s3c2410wdt_stop();先停止看门狗便于设置
 
 
 wtcon = readl(wdt_base + S3C2410_WTCON);读控制寄存器WTCON
 wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
 这里的定义: #define S3C2410_WTCON_ENABLE  (1<<5) #define S3C2410_WTCON_DIV128  (3<<3)对照下面的寄存器图,更直观:
   
 
 if (soft_noboot) {   这个标志第一篇中说明了它的作用,不明的可以回过去查看。等于1,作为定时器使用。 wtcon |= S3C2410_WTCON_INTEN;使能中断
 wtcon &= ~S3C2410_WTCON_RSTEN;不允许发出复位信号
 } else {等于0,看门狗做复位电路使用
 wtcon &= ~S3C2410_WTCON_INTEN;禁止中断
 wtcon |= S3C2410_WTCON_RSTEN;允许复位
 }
 
 
 DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",
 __func__, wdt_count, wtcon);
 
 
 writel(wdt_count, wdt_base + S3C2410_WTDAT);写数据寄存器
 writel(wdt_count, wdt_base + S3C2410_WTCNT);写计数器寄存器
 writel(wtcon, wdt_base + S3C2410_WTCON);写控制寄存器
 spin_unlock(&wdt_lock);
 }
 
 
 2、接着说s3c2410wdt_stop函数,停止看门狗工作。 static void s3c2410wdt_stop(void){
 spin_lock(&wdt_lock);
 __s3c2410wdt_stop();
 spin_unlock(&wdt_lock);
 }
 static void __s3c2410wdt_stop(void){
 unsigned long wtcon;
 
 wtcon = readl(wdt_base + S3C2410_WTCON);
 wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);使看门狗不工作,不发出复位信号
 writel(wtcon, wdt_base + S3C2410_WTCON);
 }
 
 3、s3c2410wdt_shutdown此函数用于停止看门狗 static void s3c2410wdt_shutdown(struct platform_device *dev){
 s3c2410wdt_stop();
 }
 4、s3c2410wdt_suspend该函数用于电源管理,挂起设备 static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state){
 /* Save watchdog state, and turn it off. */保存看门狗当前状态
 wtcon_save = readl(wdt_base + S3C2410_WTCON);
 wtdat_save = readl(wdt_base + S3C2410_WTDAT);
 
 
 /* Note that WTCNT doesn't need to be saved. */
 s3c2410wdt_stop();关闭看门狗
 
 
 return 0;
 }
 5、s3c2410wdt_resume也与电源管理有关,挂起后的恢复 static int s3c2410wdt_resume(struct platform_device *dev){
 /* Restore watchdog state. */恢复各寄存器的设置
 writel(wtdat_save, wdt_base + S3C2410_WTDAT);
 writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */
 writel(wtcon_save, wdt_base + S3C2410_WTCON);
 
 printk(KERN_INFO PFX "watchdog %sabled\n",
 (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
 
 
 return 0;
 }
 6、再来看s3c2410wdt_fops结构体中的函数: static const struct file_operations s3c2410wdt_fops = {.owner  = THIS_MODULE,
 .llseek  = no_llseek,
 .write  = s3c2410wdt_write,
 .unlocked_ioctl= s3c2410wdt_ioctl,
 .open = s3c2410wdt_open,
 .release  = s3c2410wdt_release,
 };
 
 6.1、先看s3c2410wdt_open函数:在用户调用open函数时被调用。源码如下: /** /dev/watchdog handling
 */
 static int s3c2410wdt_open(struct inode *inode, struct file *file)
 {
 
 printk("s3c2410wdt_open.\n");
 if (test_and_set_bit(0, &open_lock))
 return -EBUSY;
 调用此函数测试open_lock的第0位。为0则表示此函数返回0,表示设备没有被另外的程序打开。如果第0位为1,则表示设备已经被打开,返回EBUSY
 
 if (nowayout)不为0,表示看门狗不允许关闭,则增加看门狗模块的引用计数。
 __module_get(THIS_MODULE);
 
 allow_close = CLOSE_STATE_NOT;设为不允许关闭
 
 /* start the timer */
 s3c2410wdt_start();打开设备
 return nonseekable_open(inode, file);不允许调用seek函数,即不允许对设备进行定位。
 }
 
 6.2、接着看s3c2410wdt_release函数,在close函数中被调用。函数源码如下: static int s3c2410wdt_release(struct inode *inode, struct file *file){
 /*
 * Shut off the timer.
 * Lock it in if it's a module and we set nowayout
 */
 if (allow_close == CLOSE_STATE_ALLOW)看门狗为允许关闭
 s3c2410wdt_stop();关闭看门狗
 else {
 dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");
 s3c2410wdt_keepalive();使看门狗为活动状态
 }
 其中s3c2410wdt_keepalive函数源码如下: static void s3c2410wdt_keepalive(void){
 spin_lock(&wdt_lock);
 writel(wdt_count, wdt_base + S3C2410_WTCNT);重新写计数寄存器
 spin_unlock(&wdt_lock);
 }
 allow_close = CLOSE_STATE_NOT;设为不允许关闭
 clear_bit(0, &open_lock);将open_lock的第0位设为0,允许打开
 return 0;
 }
 
 6.3、再来看s3c2410wdt_write函数,该函数主要用来设置allow_close变量为允许关闭状态。如果向设备中写入V,那么就允许关闭设备。源码如下: static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,size_t len, loff_t *ppos)
 {
 /*
 * Refresh the timer.
 */
 printk("s3c2410wdt_write.\n");
 if (len) {
 if (!nowayout) {  允许关闭
 size_t i;
 
 
 /* In case it was set long ago */
 allow_close = CLOSE_STATE_NOT 不允许关闭
 
 for (i = 0; i != len; i++) {
 char c;
 
 if (get_user(c, data + i))
 return -EFAULT;
 if (c == 'V')
 allow_close = CLOSE_STATE_ALLOW;
 }如果往看门狗设备中写入V,允许关闭
 }
 s3c2410wdt_keepalive();
 }
 return len;
 }
 
 6.4、现在来看一个比较重要的函数,s3c2410wdt_ioctl函数用来接受系统的命令,设置看门狗的状态。 static long s3c2410wdt_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
 {
 void __user *argp = (void __user *)arg;
 int __user *p = argp;
 int new_margin;
 printk("s3c2410wdt_ioctl.\n");
 
 switch (cmd) {
 case WDIOC_GETSUPPORT:获取看门狗的信息
 return copy_to_user(argp, &s3c2410_wdt_ident,
 sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;
 其中有定义: #define OPTIONS WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE
 static const struct watchdog_info s3c2410_wdt_ident = {
 .options          =     OPTIONS,
 .firmware_version =0,
 .identity         ="S3C2410 Watchdog",
 };
 case WDIOC_GETSTATUS:
 case WDIOC_GETBOOTSTATUS: 获得看门狗状态
 return put_user(0, p);
 case WDIOC_KEEPALIVE: 对看门狗进行喂狗
 s3c2410wdt_keepalive();
 return 0;
 case WDIOC_SETTIMEOUT:设置新的超时时间
 if (get_user(new_margin, p))
 return -EFAULT;
 if (s3c2410wdt_set_heartbeat(new_margin))
 return -EINVAL;
 s3c2410wdt_keepalive();
 return put_user(tmr_margin, p);
 case WDIOC_GETTIMEOUT:获得看门狗的当前超时时间
 return put_user(tmr_margin, p);
 default:
 return -ENOTTY;
 }
 }
 
 6.5、不知道你还记得吗?我们在probe函数中,申请的中断,并设置中断处理函数是s3c2410wdt_irq,现在来说这个函数,源码如下: /* interrupt handler code */static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
 {
 dev_info(wdt_dev, "watchdog timer expired (irq)\n");
 
 
 s3c2410wdt_keepalive();
 return IRQ_HANDLED;
 }
 
 其实就是喂狗。     这里我就有个疑问了,在看门狗用作定时器时,WTDAT寄存器用于指定超时时间。当看门狗作为定时器使用时,当计数器WTCNT的值到0时,WTDAT寄存器的值会被自动装入WTCNT,并不会发出复位信号。这里WTDAT寄存器的值会被自动装入WTCNT,那我们还喂狗干什么?这不是自动喂狗吗? 当用作看门狗时,到时间都复位了,中断还有用吗? 那位帮我前辈帮我解惑,在这里先谢了。 
 |