1. 实验目的:设计s3c2410的linux看门狗驱动程序,内核版本linux-2.6.22.6
2. s3c2410看门狗定时器的相关寄存器
WTCON用于设置看门狗定时器时钟,使能或禁止看门狗定时器,使能或禁止看门狗中断,以及设置是否输出复位信号:
WTDAT用于设置看门狗的超时时间
WTCNT是看门狗计数器,看门狗启动后WTCNT值开始递减,为0后进行超时操作(产生看门狗中断或复位信号)
在程序正常运行时,我们需要在WTCNT减为0之前不断的为其写入新值(喂狗),使其值不为0,则系统不会复位;如果程序跑飞,没有继续喂狗,WTCNT递减为0后,就会复位系统。
3. 驱动程序设计框架
看门狗Linux驱动基于platform机制来设计。Linux的platform机制用platform_device和platform_driver这两个数据结构来管理相关设备,其优点在于将本身的硬件资源由内核统一管理,在驱动程序中可以通过platform机制提供的标准接口申请使用硬件资源,提高了驱动和资源管理的独立性,有较好的安全性和可移植性。
平台设备用platform_device结构体描述:
struct platform_device {
const char * name; //设备名
u32 id;
struct device dev;
u32 num_resources; //设备所使用各类资源数量
struct resource* resource; //资源
};
通过platform_add_devices()函数可以将平台设备添加到系统中:
int platform_add_devices(struct platform_device **devs, int num);
看门狗platform_device的定义及注册在内核源码的/arch/arm/plat-s3c24xx/devs.c和/arch/arm/mach-s3c2410/mach-smdk2410.c中已经完成,因此我们无需再编写platform_device相关程序,只需在驱动程序中获取相关硬件资源使用即可。
内核源码的/arch/arm/plat-s3c24xx/devs.c中看门狗platform_device和硬件资源定义如下:
struct platform_device s3c_device_wdt = {
.name = "s3c2410-wdt", //设备名
.id = - 1, .
.num_resources = ARRAY_SIZE(s3c_wdt_resource), //资源数量
.resource = s3c_wdt_resource, //看门狗所使用资源
};
s3c_wdt_resource定义如下:
static struct resource s3c_wdt_resource[] = {
[0] = {
.start = S3C24XX_PA_WATCHDOG, //看门狗I/O内存开始位置
.end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1, //看门狗I/O内存结束位置
.flags = IORESOURCE_MEM, //I/O内存资源
} ,
[1] = {
.start = IRQ_WDT, //看门狗开始IRQ号
.end = IRQ_WDT, //看门狗结束IRQ号
.flags = IORESOURCE_IRQ, //IRQ资源
}
};
/arch/arm/mach-s3c2410/mach-smdk2410.c源码文件中添加的platform_device其中就包括看门狗,如图所示:
我们的驱动程序要编写platform_driver的相关代码,平台驱动platform_driver结构体描述:
struct platform_driver {
int (*probe)(struct platform_device *);//探测
int (*remove)(struct platform_device *);//移除
void (*shutdown)(struct platform_device *);//关闭
int (*suspend)(struct platform_device *, pm_message_t state);//挂起
int (*resume)(struct platform_device *);//恢复
struct device_driver driver;
};
在我们的驱动程序中可以这样定义看门狗的platform_driver:
static struct platform_driver s3c2410wdt_driver = {
.probe = s3c2410wdt_probe,//S3C2410看门狗探测
.remove = s3c2410wdt_remove,// S3C2410看门狗移除
.shutdown = s3c2410wdt_shutdown,//S3C2410看门狗关闭
.suspend = s3c2410wdt_suspend,//S3C2410看门狗挂起
.resume = s3c2410wdt_resume, //S3C2410看门狗恢复
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-wdt",//设备名
},
};
在驱动程序中,我们需要实现该结构体中的probe()、remove()等函数,这里需要重点关注probe()函数:当我们加载一个platform_driver后,系统探测到与与之匹配的platform_device,就会执行probe()函数。判断是否匹配在于platform_driver 和platform_device的设备名是否一致。例如我们的platform_device结构体name成员为"s3c2410-wdt",那么platform_driver结构体中driver成员中的name也应该初始化为"s3c2410-wdt",系统探测到二者匹配后就会执行probe()函数,我们在probe函数中就可以进行获取platform_device资源的操作。
了解了平台设备和平台驱动后,我们还需要解决一个问题,就是文件操作中的open(), read(), write()函数该如何实现,平台驱动包括的函数中并没有包含这些函数,因此我们还需要把看门狗注册为一个字符设备或混杂设备,来实现文件操作的相关函数,在这里我们使用混杂设备(miscdevice)。
miscdevice共享一个主设备号MISC_MAJOR(即10),但次设备号不同。所有的miscdevice设备形成一个链表,对设备访问时内核根据次设备号查找对应的miscdevice设备,然后调用其struct file_operations 中注册的文件操作接口进行操作。
miscdevice结构体描述:
struct miscdevice{
int minor;
const char *name;//设备名
struct file_operations *fops;//文件操作
struct list_head list;//链表头
struct device *dev;
struct class_device *class ;
char devfs_name[64];//devfs文件节点
};
注册和注销miscdevice结构体分别用misc_register()函数和用misc_deregister()函数来实现:
int misc_register(struct miscdevice * misc);
int misc_deregister(struct miscdevice *misc);
熟悉了相关概念后,我们可以明确驱动程序要做的工作:
1. 定义看门狗的platform_driver并初始化, 注意driver成员的name要和platform_device的name一致才能够匹配到设备;
2. 驱动入口函数中注册platform_driver,出口函数中注销platform_driver;
3. 实现platform_driver中probe(), remove()等函数;
4. 为了实现文件接口,我们还需要为看门狗注册为miscdevice, 这一步可以放在probe()中;
5. 实现文件接口(fops)的open(), read(), write()等函数。