• Linux 设备驱动开发 —— platform设备驱动应用实例解析


    前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 —— platform 设备驱动 ,下面将通过一个实例来深入我们的学习。

           

    一、platform 驱动的工作过程

            platform模型驱动编程,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳。

         在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中。那么我们编写platform模型驱动时,需要完成两个工作:

    a -- 实现platform驱动 

    架构就很简单,如下所示:


    platform驱动模型三个对象:platform总线platform设备platform驱动

    platform总线对应的内核结构:struct bus_type-->它包含的最关键的函数:match() (要注意的是,这块由内核完成,我们不参与)

    platform设备对应的内核结构:struct platform_device-->注册:platform_device_register(unregister)

    platform驱动对应的内核结构:struct platform_driver-->注册:platform_driver_register(unregister)

           

    那具体platform驱动的工作过程是什么呢:

         如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时的同名的设备与驱动,再调用驱动中的probe函数等;

    如果是驱动先注册,同设备驱动一样先会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用。



    二、实现platform 驱动与设备的详细过程

    1、思考问题?

          在分析platform 之前,可以先思考一下下面的问题:

    a -- 为什么要用 platform 驱动?不用platform驱动可以吗?

    b -- 设备驱动中引入platform 概念有什么好处?

            现在先不回答,看完下面的分析就明白了,后面会附上总结。


    2、platform_device 结构体 VS platform_driver 结构体

          这两个结构体分别描述了设备和驱动,二者有什么关系呢?先看一下具体结构体对比

    设备(硬件部分):中断号,寄存器,DMA等
                       platform_device 结构体
     驱动(软件部分)
                             platform_driver 结构体       
    struct platform_device {
        const char    *name;       名字
        int        id;
        bool        id_auto;
        struct device    dev;   硬件模块必须包含该结构体
        u32        num_resources;        资源个数
        struct resource    *resource;         资源  人脉
        const struct platform_device_id    *id_entry;
        /* arch specific additions */
        struct pdev_archdata    archdata;
    };
    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;内核里所有的驱动程序必须包含该结构体
        const struct platform_device_id *id_table;  八字
    };
    设备实例:
    static struct platform_device hello_device=
    {
        .name = "bigbang",
        .id = -1,
        .dev.release = hello_release,
    };
    驱动实例:
    static struct platform_driver hello_driver=
    {
        .driver.name = "bigbang",
        .probe = hello_probe,
        .remove = hello_remove,
    };

           前面提到,实现platform模型的过程就是总线对设备和驱动的匹配过程 。打个比方,就好比相亲,总线是红娘,设备是男方,驱动是女方:

    a -- 红娘(总线)负责男方(设备)和女方(驱动)的撮合;     

    b -- 男方(女方)找到红娘,说我来登记一下,看有没有合适的姑娘(汉子)—— int (*probe)(struct platform_device *) 匹配成功后驱动执行的第一个函数),当然如果男的跟小三跑了(设备卸载),女方也不会继续待下去的(  int (*remove)(struct platform_device *))。


    3、设备资源结构体

          在struct platform_device 结构体中有一重要成员 struct resource *resource

    1. struct resource {  
    2.     resource_size_t start;  资源起始地址     
    3.     resource_size_t end;   资源结束地址  
    4.     const char *name;        
    5.     unsigned long flags;   区分是资源什么类型的  
    6.     struct resource *parent, *sibling, *child;  
    7. };  
    8.   
    9. #define IORESOURCE_MEM        0x00000200  
    10. #define IORESOURCE_IRQ        0x00000400     

           flags 指资源类型,我们常用的是 IORESOURCE_MEM、IORESOURCE_IRQ  这两种。start 和 end 的含义会随着 flags而变更,如

    a -- flags为IORESOURCE_MEM 时,start 、end 分别表示该platform_device占据的内存的开始地址和结束值;  

    b -- flags为 IORESOURCE_IRQ   时,start 、end 分别表示该platform_device使用的中断号的开始地址和结束值; 

    下面看一个实例:

    1. static struct  resource beep_resource[] =  
    2. {  
    3.     [0] = {  
    4.             .start = 0x114000a0,  
    5.         .end = 0x114000a0+0x4,  
    6.             .flags = IORESOURCE_MEM,  
    7.     },  
    8.   
    9.     [1] = {  
    10.             .start = 0x139D0000,  
    11.             .end = 0x139D0000+0x14,  
    12.             .flags = IORESOURCE_MEM,  
    13.     },  
    14. };  
    4、将字符设备添加到 platform的driver中
          前面我们提到platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳,下面我们看一下添加的过程:
    1. static struct file_operations hello_ops=  
    2. {  
    3.     .open = hello_open,  
    4.     .release = hello_release,  
    5.     .unlocked_ioctl = hello_ioctl,  
    6. };  
    7.   
    8. static int hello_remove(struct platform_device *pdev)  
    9. {  
    10.     注销分配的各种资源  
    11. }  
    12.   
    13. static int hello_probe(struct platform_device *pdev)  
    14. {  
    15.     1.申请设备号  
    16.     2.cdev初始化注册,&hello_ops  
    17.     3.从pdev读出硬件资源  
    18.     4.对硬件资源初始化,ioremap,request_irq( )  
    19. }  
    20.   
    21. static int hello_init(void)  
    22. {  
    23.     只注册 platform_driver  
    24. }  
    25.   
    26. static void hello_exit(void)  
    27. {  
    28.     只注销 platform_driver  
    29. }  
          可以看到,
    5、platform是如何匹配device和driver
          这时就该总线出场了,系统为platform总线定义了一个bus_type 的实例platform_bus_type,其定义如下:
    1. struct bus_type platform_bus_type = {  
    2.     .name        = "platform",  
    3.     .dev_groups    = platform_dev_groups,  
    4.     .match        = platform_match,  
    5.     .uevent        = platform_uevent,  
    6.     .pm        = &platform_dev_pm_ops,  
    7. };  
          其又是怎样工作的呢?在platform.c (e:linux-3.14-fs4412driversase)    31577    2014/3/31 中可以看到
    1. __platform_driver_register()  
    2. {  
    3.     drv->driver.bus = &platform_bus_type;     536行  
    4. }  
        在 platform_bus_type 中调用 了platform_match:
    1. static int platform_match(struct device *dev, struct device_driver *drv)  
    2. {  
    3.     struct platform_device *pdev = to_platform_device(dev);  
    4.     struct platform_driver *pdrv = to_platform_driver(drv);  
    5.   
    6.     匹配设备树信息,如果有设备树,就调用 of_driver_match_device() 函数进行匹配  
    7.     if (of_driver_match_device(dev, drv))  
    8.         return 1;  
    9.   
    10.   
    11.     匹配id_table  
    12.     if (pdrv->id_table)  
    13.         return platform_match_id(pdrv->id_table, pdev) != NULL;  
    14.   
    15.     最基本匹配规则  
    16.     return (strcmp(pdev->name, drv->name) == 0);  
    17. }  


    6、解决问题

          现在可以回答这两个问题了

    a -- 为什么要用 platform 驱动?不用platform驱动可以吗?

    b -- 设备驱动中引入platform 概念有什么好处?

          引入platform模型符合Linux 设备模型 —— 总线、设备、驱动,设备模型中配套的sysfs节点都可以用,方便我们的开发;当然你也可以选择不用,不过就失去了一些platform带来的便利;

          Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动, 下面来看一下,套上platform 外壳后的程序:

    1、device.c

    1. #include <linux/module.h>  
    2. #include <linux/device.h>  
    3. #include <linux/platform_device.h>  
    4. #include <linux/ioport.h>  
    5.   
    6. static struct resource beep_resource[] =  
    7. {  
    8.     [0] ={  
    9.         .start = 0x114000a0,  
    10.         .end =  0x114000a0 + 0x4,  
    11.         .flags = IORESOURCE_MEM,  
    12.     },  
    13.   
    14.     [1] ={  
    15.         .start = 0x139D0000,  
    16.         .end =  0x139D0000 + 0x14,  
    17.         .flags = IORESOURCE_MEM,  
    18.     }  
    19. };  
    20.   
    21. static void hello_release(struct device *dev)  
    22. {  
    23.     printk("hello_release ");  
    24.     return ;  
    25. }  
    26.   
    27.   
    28.   
    29. static struct platform_device hello_device=  
    30. {  
    31.     .name = "bigbang",  
    32.     .id = -1,  
    33.     .dev.release = hello_release,  
    34.     .num_resources = ARRAY_SIZE(beep_resource),  
    35.     .resource = beep_resource,  
    36. };  
    37.   
    38. static int hello_init(void)  
    39. {  
    40.     printk("hello_init");  
    41.     return platform_device_register(&hello_device);  
    42. }  
    43.   
    44. static void hello_exit(void)  
    45. {  
    46.     printk("hello_exit");  
    47.     platform_device_unregister(&hello_device);  
    48.     return;  
    49. }  
    50.   
    51. MODULE_LICENSE("GPL");  
    52. module_init(hello_init);  
    53. module_exit(hello_exit);  

    2、driver.c

    1. #include <linux/module.h>  
    2. #include <linux/fs.h>  
    3. #include <linux/cdev.h>  
    4. #include <linux/device.h>  
    5. #include <linux/platform_device.h>  
    6. #include <asm/io.h>  
    7.   
    8. static int major = 250;  
    9. static int minor=0;  
    10. static dev_t devno;  
    11. static struct class *cls;  
    12. static struct device *test_device;  
    13.            
    14. #define TCFG0         0x0000                 
    15. #define TCFG1         0x0004                              
    16. #define TCON          0x0008               
    17. #define TCNTB0        0x000C            
    18. #define TCMPB0        0x0010             
    19.   
    20. static unsigned int *gpd0con;  
    21. static void *timer_base;  
    22.   
    23. #define  MAGIC_NUMBER    'k'  
    24. #define  BEEP_ON    _IO(MAGIC_NUMBER    ,0)  
    25. #define  BEEP_OFF   _IO(MAGIC_NUMBER    ,1)  
    26. #define  BEEP_FREQ   _IO(MAGIC_NUMBER   ,2)  
    27.   
    28. static void fs4412_beep_init(void)  
    29. {     
    30.     writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);  
    31.     writel ((readl(timer_base +TCFG0  )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);   
    32.     writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );   
    33.   
    34.     writel (500, timer_base +TCNTB0  );  
    35.     writel (250, timer_base +TCMPB0 );  
    36.     writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );   
    37. }  
    38.   
    39. void fs4412_beep_on(void)  
    40. {  
    41.     writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );  
    42. }  
    43.   
    44. void fs4412_beep_off(void)  
    45. {  
    46.     writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );  
    47. }  
    48.   
    49. static void beep_unmap(void)  
    50. {  
    51.         iounmap(gpd0con);  
    52.         iounmap(timer_base);  
    53. }  
    54.   
    55. static int beep_open (struct inode *inode, struct file *filep)  
    56. {  
    57.     fs4412_beep_on();  
    58.     return 0;  
    59. }  
    60.   
    61. static int beep_release(struct inode *inode, struct file *filep)  
    62. {  
    63.      fs4412_beep_off();  
    64.      return 0;  
    65. }  
    66.   
    67. #define BEPP_IN_FREQ 100000  
    68. static void beep_freq(unsigned long arg)  
    69. {  
    70.     writel(BEPP_IN_FREQ/arg, timer_base +TCNTB0  );  
    71.     writel(BEPP_IN_FREQ/(2*arg), timer_base +TCMPB0 );  
    72.   
    73. }  
    74.   
    75. static long beep_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)  
    76. {  
    77.     switch(cmd)  
    78.     {  
    79.         case BEEP_ON:  
    80.             fs4412_beep_on();  
    81.             break;  
    82.         case BEEP_OFF:  
    83.             fs4412_beep_off();  
    84.             break;  
    85.         case BEEP_FREQ:  
    86.             beep_freq( arg );  
    87.             break;  
    88.         default :  
    89.             return -EINVAL;  
    90.     }  
    91.     return 0;  
    92. }  
    93.   
    94. static struct file_operations beep_ops=  
    95. {  
    96.     .open     = beep_open,  
    97.     .release = beep_release,  
    98.     .unlocked_ioctl      = beep_ioctl,  
    99. };  
    100.   
    101. static int beep_probe(struct platform_device *pdev)  
    102. {  
    103.     int ret;      
    104.     printk("match ok!");  
    105.       
    106.     gpd0con = ioremap(pdev->resource[0].start,pdev->resource[0].end - pdev->resource[0].start);  
    107.     timer_base = ioremap(pdev->resource[1].start, pdev->resource[1].end - pdev->resource[1].start);  
    108.   
    109.     devno = MKDEV(major,minor);  
    110.     ret = register_chrdev(major,"beep",&beep_ops);  
    111.   
    112.     cls = class_create(THIS_MODULE, "myclass");  
    113.     if(IS_ERR(cls))  
    114.     {  
    115.         unregister_chrdev(major,"beep");  
    116.         return -EBUSY;  
    117.     }  
    118.   
    119.     test_device = device_create(cls,NULL,devno,NULL,"beep");//mknod /dev/hello  
    120.     if(IS_ERR(test_device))  
    121.     {  
    122.         class_destroy(cls);  
    123.         unregister_chrdev(major,"beep");  
    124.         return -EBUSY;  
    125.     }  
    126.       
    127.     fs4412_beep_init();  
    128.       
    129.     return 0;  
    130. }  
    131.   
    132. static int beep_remove(struct platform_device *pdev)  
    133. {  
    134.     beep_unmap();  
    135.     device_destroy(cls,devno);  
    136.     class_destroy(cls);   
    137.     unregister_chrdev(major,"beep");  
    138.   
    139.     return 0;  
    140. }  
    141.   
    142.   
    143. static struct platform_driver beep_driver=  
    144. {  
    145.     .driver.name = "bigbang",  
    146.     .probe = beep_probe,  
    147.     .remove = beep_remove,  
    148. };  
    149.    
    150.   
    151. static int beep_init(void)  
    152. {  
    153.     printk("beep_init");  
    154.       
    155.     return platform_driver_register(&beep_driver);  
    156. }  
    157.   
    158. static void beep_exit(void)  
    159. {  
    160.     printk("beep_exit");  
    161.     platform_driver_unregister(&beep_driver);  
    162.       
    163.     return;  
    164. }  
    165.   
    166.   
    167. MODULE_LICENSE("GPL");  
    168. module_init(beep_init);  
    169. module_exit(beep_exit);  

    3、makefile   

    1. ifneq  ($(KERNELRELEASE),)  
    2. obj-m:=device.o driver.o  
    3. $(info "2nd")  
    4. else  
    5. #KDIR := /lib/modules/$(shell uname -r)/build  
    6. KDIR := /home/fs/linux/linux-3.14-fs4412  
    7. PWD:=$(shell pwd)  
    8. all:  
    9.     $(info "1st")  
    10.     make -C $(KDIR) M=$(PWD) modules  
    11. clean:  
    12.     rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order  
    13. endif  

    4、test.c

      1. #include <sys/types.h>  
      2. #include <sys/stat.h>  
      3. #include <fcntl.h>  
      4. #include <stdio.h>  
      5.   
      6. main()  
      7. {  
      8.     int fd,i,lednum;  
      9.   
      10.     fd = open("/dev/beep",O_RDWR);  
      11.     if(fd<0)  
      12.     {  
      13.         perror("open fail  ");  
      14.         return ;  
      15.     }  
      16.       
      17.     sleep(10);  
      18.     close(fd);  
  • 相关阅读:
    查看SQL Server被锁的表以及如何解锁【转】
    JQUERY的$(function(){})和window.onload=function(){}的区别【转】
    安装和使用Redis【转】
    RabbitMQ的简单应用【转】
    Redis集群的搭建【转】
    Spring--如何解决循环依赖
    分布式事务--2PC(两阶段提交)
    CAP理论
    JVM垃圾回收机制
    Redis面试题
  • 原文地址:https://www.cnblogs.com/wanghuaijun/p/6985634.html
Copyright © 2020-2023  润新知