[kernel新手培训实战系列1 ] watchdog driver编写

自己写了一个的简单的 for smdk2410 的watchdog driver (完全依照datasheet ,并没有抄袭
s3c2410-wdt.c ) , 可是运行的时候 ,有问题:


# ./wdt_tt
in wdt_disable
readw ok ,tmp=0x0000
COUNTER_VALUE=18300
S3C2410_WTCNT address = c5000008
the value written S3C2410_WTCON=0xff00
Unable to handle kernel NULL pointer dereference at virtual address 00000032
pgd = c3d60000
[00000032] *pgd=338e9031, *pte=00000000, *ppte=00000000
Internal error: Oops: 13 [#1]
Modules linked in: wdt_s3c
CPU: 0
PC is at do_sys_open+0x70/0xe0
LR is at mntput_no_expire+0x24/0x84
pc : [<c007d730>] lr : [<c009ace4>] Not tainted
sp : c38e7f70 ip : c38e7ea8 fp : c38e7f94
r10: 4014f60c r9 : c38e6000 r8 : c38e0000
r7 : 00000003 r6 : 0000002a r5 : beb4feec r4 : 00000000
r3 : 00000000 r2 : a0000013 r1 : a0000093 r0 : 0000002a
Flags: nzcv IRQs on FIQs on Mode SVC_32 Segment user
Control: 717F
Table: 33D60000 DAC: 00000015
Process wdt_tt (pid: 263, stack limit = 0xc38e6250)
Stack: (0xc38e7f70 to 0xc38e8000)
7f60: 00008550 beb4fee4 000085e4 00000001
7f80: 00000005 c0026f44 c38e7fa4 c38e7f98 c007d7d8 c007d6d0 00000000 c38e7fa8
7fa0: c0026da0 c007d7c4 beb4fee4 000085e4 000086f8 00000000 beb4feec 00000000
7fc0: beb4fee4 000085e4 00000001 00008648 00008550 00000000 4014f60c beb4feb8
7fe0: 00000000 beb4fe94 00002680 400ee830 60000010 000086f8 fbffff7f ffffffff
Backtrace:
[<c007d6c0>] (do_sys_open+0x0/0xe0) from [<c007d7d8>]
(sys_open+0x24/0x28)
r8 = C0026F44 r7 = 00000005 r6 = 00000001 r5 = 000085E4
r4 = BEB4FEE4
[<c007d7b4>] (sys_open+0x0/0x28) from [<c0026da0>]
(ret_fast_syscall+0x0/0x2c)
Code: e1a00007 ebfffe9e e1a07006 ea000014 (e5900008)
Segmentation fault
#



----
上面的wdt_tt的源码是:




#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>



int main(int argc,char **argv)

{



int fd = 0;

int n =0;



fd = open("/dev/wdt",O_RDONLY );

printf("fd=%d/n",fd);

if(fd < 0) {

perror("/dev/wdt");

return -1;

}



sleep(10);



close(fd);



return 0;

}




dirver的源码是


/* S3C2410-wdt.c

* CopyRight reserved by BobZhang
href="mailto:[email protected]">[email protected]
href="mailto:[email protected]">[email protected]

*/





#include <linux/module.h>

#include <linux/fs.h>

#include <linux/string.h>

#include <linux/init.h>

#include <linux/platform_device.h>

#include <linux/interrupt.h>

#include <linux/rtc.h>

#include <linux/bcd.h>

#include <linux/clk.h>



#include <asm/hardware.h>

#include <asm/uaccess.h>

#include <asm/io.h>

#include <asm/irq.h>

#include <asm/rtc.h>



#include <linux/miscdevice.h> //一定要有这个



#include <asm/mach/time.h>



#include <asm/arch/regs-watchdog.h> //专门定义smdk2410 watchdog 寄存器的header
file





#undef S3C24XX_VA_WATCHDOG

#define S3C24XX_VA_WATCHDOG s3c_wdt_base





static u32 heartbeat = 12; //自己定义的心跳值,这个value应该会有个上限

#define PCLK 50000000

#define PRESCALER 255

#define DIVISION_FACTOR 128 //最最大的



//#define COUNTER_VALUE heartbeat * ( 50000000 / (PRESCALER + 1) / DIVISION_FACTOR )







static unsigned long wdt_status;



#define WDT_IN_USE 0







static void __iomem *s3c_wdt_base;

static struct resource *s3c_wdt_mem;



/* 这个是一定要的,因为在regs-watchdog.h 里面都定义 ,把S3C24XX_VA_WATCHDOG 定义成
这里得到的实际的 s3c_wdt_base ,就可以实际控制硬件了。

#define S3C2410_WDOGREG(x) ((x) + S3C24XX_VA_WATCHDOG)



#define S3C2410_WTCON S3C2410_WDOGREG(0x00)

#define S3C2410_WTDAT S3C2410_WDOGREG(0x04)

#define S3C2410_WTCNT S3C2410_WDOGREG(0x08)

*/





//#define S3C2410_WTCON_RSTEN (0x01)

//#define S3C2410_WTCON_INTEN (1<<2)

//#define S3C2410_WTCON_ENABLE (1<<5)





struct resource *platform_get_resource(struct platform_device *dev, unsigned int
type,unsigned int num);



static void wdt_disable(void)

{



u32 tmp;



printk("in wdt_disable /n");

tmp = readl(S3C2410_WTCON); //only 低6bit是起作用的,其他的无用,所以用readl

printk("readl ok ,tmp=0x%04x/n",tmp);

writel(tmp & ~S3C2410_WTCON_ENABLE, S3C2410_WTCON);



}





static void wdt_enable(void)

{

u32 tmp; //255 //128

u16 COUNTER_VALUE = heartbeat * ( 50000000 / (PRESCALER + 1) / DIVISION_FACTOR );



wdt_disable();



printk("COUNTER_VALUE=%hu/n",COUNTER_VALUE);

printk("S3C2410_WTCNT address = %p/n",S3C2410_WTCNT);

//写入 S3C2410_WTDAT

writel(COUNTER_VALUE, S3C2410_WTDAT);

//写入S3C2410_WTCNT

writel(COUNTER_VALUE, S3C2410_WTCNT);



tmp = readl(S3C2410_WTCON);

tmp &= ~0xff00; //bit8~bit15 清零,然后写入PRESCALER

tmp |= (PRESCALER << 8);

printk("the value written S3C2410_WTCON=0x%04x/n",tmp);

writel(tmp, S3C2410_WTCON);



//重新eanble reset

tmp = readl(S3C2410_WTCON);

writel(tmp |S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN , S3C2410_WTCON);

}





static int s3c_wdt_open(struct inode *inode, struct file *file)

{

if (test_and_set_bit(WDT_IN_USE, &wdt_status))

return -EBUSY;



wdt_enable();

}







static int s3c_wdt_release(struct inode *inode, struct file *file)

{

clear_bit(WDT_IN_USE, &wdt_status);

wdt_disable();



return 0;

}







//

//static struct watchdog_info ident = {

// .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |

// WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,

// .identity = "s3c Watchdog",

//};





static const struct file_operations s3c_wdt_fops =

{

.owner = THIS_MODULE,

// .write = s3c_wdt_write,

// .ioctl = s3c_wdt_ioctl,

.open = s3c_wdt_open,

.release = s3c_wdt_release,

};



static struct miscdevice s3c_wdt_miscdev =

{

.minor = WATCHDOG_MINOR, //130

.name = "watchdog_s3c_bob",

.fops = &s3c_wdt_fops,

};







//probe函数很简单, 就是

// 1>取得resource_wdt

// 2> request_mem_region

// 3> ioremap()

// 失败不要紧,关键要作好错误处理

static int s3c_wdt_probe(struct platform_device *pdev)

{

struct resource *res;

int ret = 0;



//#define IORESOURCE_MEM 0x00000200





/* get the memory region */

res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //根据watchdog的情况作特殊处理

if (res == NULL) {

printk("failed to get memory region resource/n");

return -ENOENT;

}



s3c_wdt_mem = request_mem_region(res->start, res->end-res->start+1,
pdev->name);



if (s3c_wdt_mem == NULL) {

printk("failed to reserve memory region/n");

ret = -ENOENT;

goto err_nores;

}



s3c_wdt_base = ioremap(res->start, res->end - res->start + 1);

if (s3c_wdt_base == NULL) {

printk("WDT s3c2410 failed ioremap()/n");

ret = -EINVAL;

goto err_nomap;

}



/* 可能要作一些s3c2410 WDT 的初始化的一些动作 */



/* 开始注册watchdog 相应的函数, 是作为misc设备进行注册 */

ret = misc_register(&s3c_wdt_miscdev);

if (ret == 0)

printk("S3C2410_bob Watchdog Timer: heartbeat %d sec/n", heartbeat);

else

printk("S3C2410_bob Watchdog Timer misc_register failure ,error=%d/n",ret);



return ret;



err_nomap:

release_resource(s3c_wdt_mem);



err_nores:

return ret;

}









static struct platform_driver s3c_wdtdrv = {

.probe = s3c_wdt_probe,

// .remove = s3c_wdt_remove,

// .suspend = s3c_wdt_suspend,

// .resume = s3c_wdt_resume,

.driver = {

.name = "s3c2410-wdt",

.owner = THIS_MODULE,

},

};



static char __initdata banner[] = "S3C2410a Watchdog, (c) 2007 BobZhang/n";



static int __init s3c_wdt_init(void)

{

printk(banner);

return platform_driver_register(&s3c_wdtdrv);

}



static void __exit s3c_wdt_exit(void)

{

platform_driver_unregister(&s3c_wdtdrv);

}



module_init(s3c_wdt_init);

module_exit(s3c_wdt_exit);



MODULE_DESCRIPTION("Samsung S3C Watchdog Driver , BobZhang write , only for
testing");

MODULE_AUTHOR("BobZhang
href="mailto:[email protected]>");">[email protected]>");
MODULE_LICENSE("GPL");

 

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

我是这样计算watchdog到底支持几秒的: 


t_watchdog = 1/( PCLK / (Prescaler value + 1) / Division_factor )

其实 ,最后的 要写入寄存器的counter = ( PCLK / (Prescaler value + 1) / Division_factor ) * 用户要求的秒数Tmax

设用户想要设置的秒数为Tmax

则满足关系式:

Tmax * counter < 2^16

即: Tmax * ( PCLK / (Prescaler value + 1) / Division_factor ) <2^16

Tmax < 2^16 / ( PCLK / (Prescaler value + 1) / Division_factor )

即 Tmax < (2^16 * (Prescaler value + 1) * Division_factor) / PCLK

要想求出 Tmax的最大的值 , 即让 Prescaler value 和 Division_factor 取最大值即可。

而PCLK式常数 = 50000000

Tmax < (2^16 * (255 + 1) * 128) / 50000000

Tmax < 42.9

所以最终用户能设置的最大的值是42 秒, 而且 Prescaler 寄存器要设置成 255


 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++

找到原因了,

原来


static int s3c_wdt_open(struct inode *inode, struct file *file)

{

if (test_and_set_bit(WDT_IN_USE, &wdt_status))

return -EBUSY;



wdt_enable();



return 0; //之前忘记 return 了。 ^_^。

}






static void wdt_enable(void)

{

u32 tmp; //255 //128

u16 COUNTER_VALUE = heartbeat * ( 50000000 / (PRESCALER + 1) / DIVISION_FACTOR );

//u16 COUNTER_VALUE = (1 << 16) -10;



printk("COUNTER_VALUE=%hu/n",COUNTER_VALUE);

printk("S3C2410_WTCNT address = %p/n",S3C2410_WTCNT);

writel(COUNTER_VALUE, S3C2410_WTDAT);

writel(COUNTER_VALUE, S3C2410_WTCNT);



tmp = readl(S3C2410_WTCON);

tmp &= ~0xff00; //bit8~bit15

tmp |= (PRESCALER << 8);



//这里是后加的,



tmp &= ~(0x03 << 3);

tmp |= 0x03 << 3; //11b is 128 division



printk("the value written S3C2410_WTCON=0x%04x/n",tmp);

writel(tmp, S3C2410_WTCON);



//&Ouml;&Oslash;&ETH;&Acirc;eanble reset

tmp = readl(S3C2410_WTCON);

writel(tmp |S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN , S3C2410_WTCON);





//test,

tmp = readl(S3C2410_WTCNT);

printk("S3C2410_WTCNT value = %hu/n",tmp);

}







--------------------
watchdog的datasheet还是最简单的, 所以就安排新人 完全靠自己的理解, 不要看原有的c code 自己写, 只有这样才能真正自己写出一个driver来 。 但是这样安排还是要有个前期铺垫 , 之前仔细的领 新人 分析了 RTC的代码和datasheet ,其实RTC的datasheet 还是比Watchdog 复杂的, 是driver新手入门的好突破口。

正好自己也从来没有看过现有的watchdog的实现c 代码, 也自己写写看 , 其实也是挺锻炼人的 。 都说写driver 是天下文章一大抄 , 不过 终归还是 要培养逼着眼睛自己写的能力啊。


写driver ,看懂datasheet确实是很高的要求, 哪怕是最简单的datasheet 也要有第一次的仔细看 , 看明白了, 以后看别的datasheet 就彻底容易了。

 
 

你可能感兴趣的:(c,timer,struct,Module,File,null)