Linux看门狗驱动程序设计(一) 概念和框架

1.    实验目的:设计s3c2410的linux看门狗驱动程序,内核版本linux-2.6.22.6

2.    s3c2410看门狗定时器的相关寄存器

        WTCON用于设置看门狗定时器时钟,使能或禁止看门狗定时器,使能或禁止看门狗中断,以及设置是否输出复位信号:

      Linux看门狗驱动程序设计(一) 概念和框架_第1张图片

        WTDAT用于设置看门狗的超时时间

     、Linux看门狗驱动程序设计(一) 概念和框架_第2张图片

        WTCNT是看门狗计数器,看门狗启动后WTCNT值开始递减,为0后进行超时操作(产生看门狗中断或复位信号)

        Linux看门狗驱动程序设计(一) 概念和框架_第3张图片

          在程序正常运行时,我们需要在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其中就包括看门狗,如图所示:

Linux看门狗驱动程序设计(一) 概念和框架_第4张图片

Linux看门狗驱动程序设计(一) 概念和框架_第5张图片

        我们的驱动程序要编写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()等函数。

你可能感兴趣的:(嵌入式Linux驱动)