Chinaunix首页 | 论坛 | 博客
  • 博客访问: 831050
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: 嵌入式

2015-08-24 13:59:11

目的

        这一节将点灯驱动程序通过总线设备驱动模型来实现,人为地进行拆分,这只是提供了一个参考的机制。



1. 介绍

上一节讲了输入子系统,在这个概念里讲到了分层分离思想。“一切都可以利用分层来实现”。
对于输入子系统,有一个核心层,下面两个层,一个是纯软件部分,内核已经做好了;还有一个是代表设备层,需要用户自己添加驱动代码。这是和硬件相关的部分,需要改变。这样分层的好处就是,对于软件层来说,不需要改变;而硬件层,需要提供不同的操作方法,用户可以根据自己使用硬件的不同而改变驱动程序,仅此而已。
输入子系统中:将核心层与设备相关层分层,每层专注自己的功能。
                      将硬件层和纯软件层分离出来,把稳定的代码保留,用户只需修改与硬件相关的部分。



分离分层思想
(1)分层:核心层和设备相关层分开
这种思想的优点就是能把很多文件共用的代码抽离集中起来成为一个或者多个核心文件供设备相关层调用,每一层专注于自己的功能。
(2)分离:
把硬件相关的代码(对于某个CPU来说是固定的,如板子的网卡、中断地址)和驱动(会根据程序作变动,如点哪一个灯)分离开来,即要编写两个文件:dev.c和drv.c。


2. 代码分析

bus_drv_dev模型: 参考gpio_keys.c文件。

(0)几个重要的数据结构

bus_type:总线的类型

点击(此处)折叠或打开

  1. struct bus_type {
  2.     const char        * name;
  3.     struct module        * owner;

  4.     struct kset        subsys;

  5.       // 含有driver 和 device 链表
  6.   struct kset        drivers;   // drv链表
  7.   struct kset        devices;   // dev链表
  8.     struct klist        klist_devices;
  9.     struct klist        klist_drivers;

  10.     struct blocking_notifier_head bus_notifier;

  11.     struct bus_attribute    * bus_attrs;
  12.     struct device_attribute    * dev_attrs;
  13.     struct driver_attribute    * drv_attrs;
  14.     struct bus_attribute drivers_autoprobe_attr;
  15.     struct bus_attribute drivers_probe_attr;

  16.    int        (*match)(struct device * dev, struct device_driver * drv);   // 用来比较drv和dev的ID是否一致,匹配。
  17.     int        (*uevent)(struct device *dev, char **envp,
  18.                  int num_envp, char *buffer, int buffer_size);
  19.     int        (*probe)(struct device * dev);
  20.     int        (*remove)(struct device * dev);
  21.     void        (*shutdown)(struct device * dev);

  22.     int (*suspend)(struct device * dev, pm_message_t state);
  23.     int (*suspend_late)(struct device * dev, pm_message_t state);
  24.     int (*resume_early)(struct device * dev);
  25.     int (*resume)(struct device * dev);

  26.     unsigned int drivers_autoprobe:1;
  27. };

struct device

点击(此处)折叠或打开

  1. struct device {
  2.     struct klist        klist_children;
  3.     struct klist_node    knode_parent;        /* node in sibling list */
  4.     struct klist_node    knode_driver;
  5.     struct klist_node    knode_bus;
  6.     struct device        *parent;

  7.     struct kobject kobj;
  8.     char    bus_id[BUS_ID_SIZE];    /* position on parent bus */
  9.     struct device_type    *type;
  10.     unsigned        is_registered:1;
  11.     unsigned        uevent_suppress:1;
  12.     struct device_attribute uevent_attr;
  13.     struct device_attribute *devt_attr;

  14.     struct semaphore    sem;    /* semaphore to synchronize calls to
  15.                      * its driver.
  16.                      */

  17.   struct bus_type    * bus;        /* type of bus device is on 定义在那一根bus上面 */
  18.    struct device_driver *driver;    /* which driver has allocated this device, 和该dev对应的driver */

  19.     void        *driver_data;    /* data private to the driver */
  20.     void        *platform_data;    /* Platform specific data, device
  21.                      core doesn't touch it */
  22.     struct dev_pm_info    power;

  23. #ifdef CONFIG_NUMA
  24.     int        numa_node;    /* NUMA node this device is close to */
  25. #endif
  26.     u64        *dma_mask;    /* dma mask (if dma'able device) */
  27.     u64        coherent_dma_mask;/* Like dma_mask, but for
  28.                      alloc_coherent mappings as
  29.                      not all hardware supports
  30.                      64 bit addresses for consistent
  31.                      allocations such descriptors. */

  32.     struct list_head    dma_pools;    /* dma pools (if dma'ble) */

  33.     struct dma_coherent_mem    *dma_mem; /* internal for coherent mem
  34.                      override */
  35.     /* arch specific additions */
  36.     struct dev_archdata    archdata;

  37.     spinlock_t        devres_lock;
  38.     struct list_head    devres_head;

  39.     /* class_device migration path */
  40.     struct list_head    node;
  41.     struct class        *class;
  42.     dev_t            devt;        /* dev_t, creates the sysfs "dev" */
  43.     struct attribute_group    **groups;    /* optional groups */

  44.     void    (*release)(struct device * dev);  // 需要显示提供
  45. };

struct device_driver

点击(此处)折叠或打开

  1. struct device_driver {
  2.     const char        * name;
  3.    struct bus_type        * bus;   // dev在哪一个bus上面

  4.     struct kobject        kobj;
  5.     struct klist        klist_devices;
  6.     struct klist_node    knode_bus;

  7.     struct module        * owner;
  8.     const char         * mod_name;    /* used for built-in modules */
  9.     struct module_kobject    * mkobj;

  10.    int    (*probe)    (struct device * dev);  // 如果dev和drv匹配,则调用该probe函数。
  11.     int    (*remove)    (struct device * dev);
  12.     void    (*shutdown)    (struct device * dev);
  13.     int    (*suspend)    (struct device * dev, pm_message_t state);
  14.     int    (*resume)    (struct device * dev);
  15. };

(1) 平台驱动注册

platform_driver_register(struct platform_driver *drv)
    driver_register(struct device_driver * drv)
        bus_add_driver(struct device_driver *drv)
           driver_attach(struct device_driver * drv) 
                bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, int (*fn)(struct device *, void *))  // 从dev链表取出每个dev,一一和drv进行比较。
                    __driver_attach(struct device * dev, void * data)
                        driver_probe_device(struct device_driver * drv, struct device * dev)
                            if (drv->bus->match && !drv->bus->match(dev, drv))   // 这里调用bus总线上的match函数来判断drv的ID 和 dev的ID 是否一致,如果一致,则match。
                                                                                                            // 对应platform_bus总线的match函数已经定义好了。

点击(此处)折叠或打开

  1. struct bus_type platform_bus_type = {
  2.     .name        = "platform",
  3.     .dev_attrs    = platform_dev_attrs,
  4.     .match        = platform_match,
  5.     .uevent        = platform_uevent,
  6.     .suspend    = platform_suspend,
  7.     .suspend_late    = platform_suspend_late,
  8.     .resume_early    = platform_resume_early,
  9.     .resume        = platform_resume,
  10. };

(2)平台设备注册

platform_device_register(struct platform_device * pdev)
    platform_device_add(struct platform_device *pdev)
        device_add(struct device *dev)
            bus_attach_device(struct device * dev)
                device_attach(struct device * dev)
                    bus_for_each_drv(struct bus_type * bus, struct device_driver * start,  void * data, int (*fn)(struct device_driver *, void *)) // 这里从drv链表,取出drv一一和dev比较
                        __device_attach(struct device_driver * drv, void * data)
                            driver_probe_device(struct device_driver * drv, struct device * dev)
                                if (drv->bus->match && !drv->bus->match(dev, drv))  //调用bus上定义的match函数进行比较,如果drv和dev的ID相同的话,则匹配


(3)总结





3. 驱动代码

dev这边放置的需要经常改变的代码(根据用户使用的外设不同而不同); drv这边放置的是稳定的代码。

led_dev.c

点击(此处)折叠或打开



  1. #include <linux/module.h>
  2. #include <linux/version.h>

  3. #include <linux/init.h>

  4. #include <linux/kernel.h>
  5. #include <linux/types.h>
  6. #include <linux/interrupt.h>
  7. #include <linux/list.h>
  8. #include <linux/timer.h>
  9. #include <linux/init.h>
  10. #include <linux/serial_core.h>
  11. #include <linux/platform_device.h>


  12. /* 分配/设置/注册一个platform_device */

  13. static struct resource led_resource[] = {
  14.     [0] = {
  15.         .start = 0x56000050,          // 起始 地址GPFCON
  16.         .end = 0x56000050 + 8 - 1,    // 结束地址,定义了两个32位的寄存器。 GPFCON GPFDAT
  17.         .flags = IORESOURCE_MEM,      // 资源类型: 内存类
  18.     },
  19.     [1] = {
  20.         .start = 5,                   // pin 脚值
  21.         .end = 5,                     // pin 脚值
  22.         .flags = IORESOURCE_IRQ,      // 中断资源
  23.     }

  24. };

  25. static void led_release(struct device * dev)  // 显示提供,可以不做任何事
  26. {
  27. }



  28. static struct platform_device led_dev = {
  29.     .name = "myled",
  30.     .id = -1,
  31.     .num_resources = ARRAY_SIZE(led_resource),
  32.     .resource = led_resource,
  33.     .dev = {
  34.         .release = led_release,  // 必须显示提供,不然卸载会报错
  35.     },
  36. };

  37. static int led_dev_init(void)
  38. {
  39.     platform_device_register(&led_dev);  // 注册平台设备
  40.     return 0;
  41. }

  42. static void led_dev_exit(void)
  43. {
  44.     platform_device_unregister(&led_dev);
  45. }

  46. module_init(led_dev_init);
  47. module_exit(led_dev_exit);

  48. MODULE_LICENSE("GPL");

led_drv.c

点击(此处)折叠或打开

  1. /* 分配/设置/注册一个platform_driver */

  2. #include <linux/module.h>
  3. #include <linux/version.h>

  4. #include <linux/init.h>
  5. #include <linux/fs.h>
  6. #include <linux/interrupt.h>
  7. #include <linux/irq.h>
  8. #include <linux/sched.h>
  9. #include <linux/pm.h>
  10. #include <linux/sysctl.h>
  11. #include <linux/proc_fs.h>
  12. #include <linux/delay.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/input.h>
  15. #include <linux/irq.h>
  16. #include <asm/uaccess.h>
  17. #include <asm/io.h>

  18. static int major;


  19. static struct class *cls;
  20. static volatile unsigned long *gpio_con;
  21. static volatile unsigned long *gpio_dat;
  22. static int pin;

  23. static int led_open(struct inode *inode, struct file *file)
  24. {
  25.     //printk("first_drv_open\n");
  26.     /* 配置为输出 */
  27.     *gpio_con &= ~(0x3<<(pin*2));
  28.     *gpio_con |= (0x1<<(pin*2));
  29.     return 0;    
  30. }

  31. static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
  32. {
  33.     int val;

  34.     //printk("first_drv_write\n");

  35.     copy_from_user(&val, buf, count); //    copy_to_user();

  36.     if (val == 1)
  37.     {
  38.         // 点灯
  39.         *gpio_dat &= ~(1<<pin);
  40.     }
  41.     else
  42.     {
  43.         // 灭灯
  44.         *gpio_dat |= (1<<pin);
  45.     }
  46.     
  47.     return 0;
  48. }


  49. static struct file_operations led_fops = {
  50.     .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
  51.     .open = led_open,
  52.     .write    =    led_write,    
  53. };

  54. static int led_probe(struct platform_device *pdev // probe函数里面你可以做你想做的事情,这里只是提供了这样一种机制。
  55. {
  56.     struct resource        *res;

  57.     /* 根据platform_device的资源进行ioremap */
  58.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  // 最后一个参数0:表示从资源的最开始地址获取。
  59.     gpio_con = ioremap(res->start, res->end - res->start + 1);
  60.     gpio_dat = gpio_con + 1;

  61.     res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  62.     pin = res->start;   //获取pin脚值


  63.     /* 注册字符设备驱动程序 */
  64.     printk("led_probe, found led\n");

  65.     major = register_chrdev(0, "myled", &led_fops);
  66.     cls = class_create(THIS_MODULE, "myled");
  67.     class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
  68.     
  69.     return 0;
  70. }

  71. static int led_remove(struct platform_device *pdev)
  72. {
  73.     /* 卸载字符设备驱动程序 */
  74.     /* iounmap */
  75.     printk("led_remove, remove led\n");

  76.     class_device_destroy(cls, MKDEV(major, 0));
  77.     class_destroy(cls);
  78.     unregister_chrdev(major, "myled");
  79.     iounmap(gpio_con);
  80.     
  81.     return 0;
  82. }


  83. struct platform_driver led_drv = {
  84.     .probe        = led_probe,
  85.     .remove        = led_remove,
  86.     .driver        = {
  87.         .name    = "myled",
  88.     }
  89. };


  90. static int led_drv_init(void)
  91. {
  92.     platform_driver_register(&led_drv);
  93.     return 0;
  94. }

  95. static void led_drv_exit(void)
  96. {
  97.     platform_driver_unregister(&led_drv);
  98. }

  99. module_init(led_drv_init);
  100. module_exit(led_drv_exit);

  101. MODULE_LICENSE("GPL");

4. 测试代码

点击(此处)折叠或打开

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>

  5. /* led_test on
  6.  * led_test off
  7.  */
  8. int main(int argc, char **argv)
  9. {
  10.     int fd;
  11.     int val = 1;
  12.     fd = open("/dev/led", O_RDWR);
  13.     if (fd < 0)
  14.     {
  15.         printf("can't open!\n");
  16.     }
  17.     if (argc != 2)
  18.     {
  19.         printf("Usage :\n");
  20.         printf("%s \n", argv[0]);
  21.         return 0;
  22.     }

  23.     if (strcmp(argv[1], "on") == 0)
  24.     {
  25.         val = 1;
  26.     }
  27.     else
  28.     {
  29.         val = 0;
  30.     }
  31.     
  32.     write(fd, &val, 4);
  33.     return 0;
  34. }

5. 测试





小结



阅读(560) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~