zoukankan      html  css  js  c++  java
  • platform总线驱动代码分析

    /************************************************************************/

    Linux内核版本:2.6.35.7

    运行平台:三星s5pv210

    /************************************************************************/

    1、本例中通过使用Linux驱动模型中的platform总线和led驱动框架编写出来的led驱动代码来分析platform总线的工作原理,本代码是我自己将近快一天的时间编写出来的,

    已经通过运行验证代码是能够正常运行的。对此对代码做如下分析:

    在platform总线(任意总线)下的驱动代码都是要分为两部分:设备和驱动,在platform总线下是platform_device和platform_driver。

    关于这个问题,我在我的上一篇博客中已经做了很好的说明。对于设备部分的注册:

    (1)一般是内核的移植工程师在做移植的时候添加的,在我的这个移植好的内核中是放在arch/arm/mach-s5pv210/mach-x210.c文件中,

    所以如果移植工程师没有添加你需要编写的驱动对应的设备,那么就需要你自己添加,你可以直接在这个文件中添加,系统启动的时候就会

    直接加载这个文件的代码,所以设备就会被注册到我们的platform总线下,那么就会添加到platform总线管理下的device设备管理相关的数

    据结构中去(链表),此时platform总线下的match函数就会自动进行匹配(每注册一个设备或者驱动match函数都会被调用),因为此时还

    相应的驱动被注册,所以匹配肯定是失败的;当我们把驱动也注册之后,也会把驱动添加到platform总线管理下的drive驱动管理相关的数据

    结构中去(也是一个链表),platform总线将会再次执行match函数,此时match函数就会匹配成功,platform总线下的设备和驱动就建立了对应

    关系了,那么设备就能够工作了。

    (2)设备注册部分也可以单独编写, 我们下驱动的时候提供 xxxxx_device.c(用来编写设备部分)和xxx_driver(用来编写驱动部分),将他们

    编译成模块,系统启动之后分别使用insmod装载设备和驱动(顺序无所谓)。这种情况一般使用在调试阶段,如果确定我们的驱动是没有bug的情况下

    ,最好还是把驱动编译进内核,把他们放在他们应该在的位置。

    2、led驱动代码

    本例子采用的是单独编写编译的方式,代码分析如下:

    (1)设备部分:leds-x210-device.c

      1 #include <linux/module.h>        // module_init  module_exit
      2 #include <linux/init.h>            // __init   __exit
      3 #include <linux/fs.h>
      4 #include <linux/leds.h>
      5 #include <mach/regs-gpio.h>
      6 #include <mach/gpio-bank.h>
      7 #include <linux/io.h>
      8 #include <linux/ioport.h>
      9 #include <mach/gpio.h>
     10 #include <linux/platform_device.h>
     11 
     12 // 定义一个结构体用于放置本设备的私有数据
     13 struct x210_led_platdata {
     14     unsigned int gpio;    // led设备用到的GPIO 
     15     char *device_name;    // led设备在/sys/class/leds/目录下的名字
     16     char *gpio_name;      // 使用gpiolob申请gpio资源时分配的名字             
     17 };
     18 
     19 
     20 // 定义x210_led_platdata类型的变量,分别对应板子上的4颗led小灯
     21 static struct x210_led_platdata x210_led1_pdata = {
     22     .gpio = S5PV210_GPJ0(3),
     23     .device_name = "led1",
     24     .gpio_name = "led1-gpj0_3",
     25 };
     26 
     27 static struct x210_led_platdata x210_led2_pdata = {
     28     .gpio = S5PV210_GPJ0(4),
     29     .device_name = "led2",
     30     .gpio_name = "led2-gpj0_4",
     31 };
     32 
     33 static struct x210_led_platdata x210_led3_pdata = {
     34     .gpio = S5PV210_GPJ0(5),
     35     .device_name = "led3",
     36     .gpio_name = "led3-gpj0_5",
     37 };
     38 
     39 static struct x210_led_platdata x210_led4_pdata = {
     40     .gpio = S5PV210_GPD0(1),
     41     .device_name = "led4",
     42     .gpio_name = "led4-gpd0_1",
     43 };
     44 
     45 
     46 // 定义4个release函数,当我们卸载设备时会调用platform_device结构体中的device结构体下的release函数
     47 void x210_led1_release(struct device *dev)
     48 {
     49     printk(KERN_INFO "x210_led1_release
    ");
     50 }
     51 
     52 void x210_led2_release(struct device *dev)
     53 {
     54     printk(KERN_INFO "x210_led1_release
    ");
     55 }
     56 
     57 void x210_led3_release(struct device *dev)
     58 {
     59     printk(KERN_INFO "x210_led1_release
    ");
     60 }
     61 
     62 void x210_led4_release(struct device *dev)
     63 {
     64     printk(KERN_INFO "x210_led1_release
    ");
     65 }
     66 
     67 
     68 // 定义4个platform_device结构体
     69 static struct platform_device x210_led1 = {
     70     .name        = "x210_led",
     71     .id        = 0,
     72     .dev        = {
     73         .platform_data    = &x210_led1_pdata,
     74         .release = x210_led1_release,
     75     },
     76 };
     77 
     78 static struct platform_device x210_led2 = {
     79     .name        = "x210_led",
     80     .id        = 1,
     81     .dev        = {
     82         .platform_data    = &x210_led2_pdata,
     83         .release = x210_led2_release,
     84     },
     85 };
     86 
     87 static struct platform_device x210_led3 = {
     88     .name        = "x210_led",
     89     .id        = 2,
     90     .dev        = {
     91         .platform_data    = &x210_led3_pdata,
     92         .release = x210_led3_release,
     93     },
     94 };
     95 
     96 static struct platform_device x210_led4 = {
     97     .name        = "x210_led",
     98     .id        = 3,
     99     .dev        = {
    100         .platform_data    = &x210_led4_pdata,
    101         .release = x210_led4_release,
    102     },
    103 };
    104 
    105 
    106 // 将4个platform_device结构体地址放入一个数组中
    107 static struct platform_device *x210_devices[] = {
    108     &x210_led1,
    109     &x210_led2,
    110     &x210_led3,
    111     &x210_led4,
    112 };
    113 
    114 // 入口函数
    115 static int __init leds_x210_init(void)
    116 {
    117     if (platform_add_devices(x210_devices, ARRAY_SIZE(x210_devices)))  // 循环注册platform平台设备
    118     {
    119         printk(KERN_ERR "platform_add_devices failed.
    ");
    120         return -1;
    121     }
    122     
    123     return 0;
    124 }
    125 
    126 // 出口函数(卸载)
    127 static void __exit leds_x210_exit(void)
    128 {
    129     int i = 0;
    130     for (i = 0; i < 4; i++)
    131        platform_device_unregister(x210_devices[i]);    // 当执行到这个函数的时候就会去执行platform_device结构体中的device结构体下的release函数 
    132 }
    133 
    134 /*函数入口和出口修饰*/
    135 module_init(leds_x210_init);
    136 module_exit(leds_x210_exit);
    137 
    138 /*描述模块信息*/
    139 MODULE_LICENSE("GPL");                // 描述模块的许可证
    140 MODULE_AUTHOR("Tao Deng <> 773904075@qq.com");            // 描述模块的作者
    141 MODULE_DESCRIPTION("led device for x210.");    // 描述模块的介绍信息
    142 MODULE_ALIAS("alias DT");            // 描述模块的别名信息
    device

    (2)驱动部分:leds-x210-driver.c

      1 #include <linux/module.h>        // module_init  module_exit
      2 #include <linux/init.h>            // __init   __exit
      3 #include <linux/fs.h>
      4 #include <linux/leds.h>
      5 #include <mach/regs-gpio.h>
      6 #include <mach/gpio-bank.h>
      7 #include <linux/io.h>
      8 #include <linux/ioport.h>
      9 #include <mach/gpio.h>
     10 #include <linux/platform_device.h>
     11 #include <linux/slab.h>
     12 
     13 enum LED{
     14     LED_ON_ = 0,
     15     LED_OFF_ = 1,
     16 };
     17 
     18 struct x210_led_platdata {
     19     unsigned int gpio;
     20     char *device_name;
     21     char *gpio_name;
     22 };
     23 
     24 struct x210_platform {
     25     struct led_classdev led_class;
     26     unsigned int gpio;
     27 };
     28 
     29 static inline struct x210_platform *pdev_to_gpio(struct platform_device *dev)
     30 {
     31     return platform_get_drvdata(dev);
     32 }
     33 
     34 static void x210_led_set(struct led_classdev *led_cdev, enum led_brightness brightness)
     35 {
     36     struct x210_platform *pdata = container_of(led_cdev, struct x210_platform, led_class);  // 使用led_classdev结构体反推得到x210_platform结构体
     37     
     38     if (0 < brightness)
     39         gpio_set_value(pdata->gpio, LED_ON_);       // 使led小灯发亮
     40     else
     41         gpio_set_value(pdata->gpio, LED_OFF_);      // 使led小灯熄灭
     42 }
     43 
     44 static int x210_led_probe(struct platform_device *dev)
     45 {
     46     // 定义变量
     47     int ret = 0;
     48     struct x210_led_platdata *platdata = dev->dev.platform_data;
     49     struct x210_platform *pform = NULL;
     50     
     51     // 分配空间
     52     pform = kzalloc(sizeof(struct x210_platform), GFP_KERNEL);    // 将pform指针指向系统分配的地址空间
     53     if (NULL == pform) {
     54         printk(KERN_ERR "kzalloc failed.
    ");
     55         return -ENOMEM;
     56     }
     57     
     58     platform_set_drvdata(dev, pform);    // 将pform值写入到传进来的platform_device结构体中的device结构体下的私有数据区中去,以便后面释放内存时用
     59     
     60     //驱动框架接口填充
     61     pform->led_class.name = platdata->device_name;             
     62     pform->led_class.brightness = 0;    
     63     pform->led_class.brightness_set = x210_led_set;  
     64     
     65     // 保存gpio管脚信息
     66     pform->gpio = platdata->gpio;
     67     
     68     //注册led驱动
     69     ret =  led_classdev_register(NULL, &pform->led_class);
     70     if (ret < 0) {
     71         printk(KERN_ERR "led_classdev_register failed.
    ");
     72         goto err_led_classdev_register;
     73     }
     74     
     75     // 向gpiolib管理器申请gpio资源 
     76     if (gpio_request(platdata->gpio, platdata->gpio_name))
     77     {
     78         printk(KERN_ERR "gpio_request failed.
    ");
     79         goto err_gpio_request;
     80     }
     81     
     82     // 设置GPIO输出高电平,关闭LED
     83     gpio_direction_output(platdata->gpio, LED_OFF_);
     84     
     85     printk(KERN_INFO "x210_led_probe succeseful.
    ");
     86 
     87     return 0;
     88 
     89 err_gpio_request:
     90     led_classdev_unregister(&pform->led_class);
     91     
     92 err_led_classdev_register:
     93 
     94     return -1;
     95 }
     96 
     97 static int x210_led_remove(struct platform_device *dev)
     98 {
     99     struct x210_led_platdata *platdata = dev->dev.platform_data;  
    100     struct x210_platform *pform = pdev_to_gpio(dev);     // 取出platform_device结构体中的device结构体中的私有数据区的x210_platform指针
    101     
    102     // 卸载led驱动
    103     led_classdev_unregister(&pform->led_class);
    104 
    105     // 关闭所有led
    106     gpio_set_value(platdata->gpio, LED_OFF_);
    107     
    108     // 释放申请的GPIO资源
    109     gpio_free(platdata->gpio);
    110     
    111     // 释放内存
    112     kfree(pform);         // 这个一定要放在最后
    113     
    114     printk(KERN_INFO "x210_led_remove succeseful.
    ");
    115     
    116     return 0;
    117 }
    118 
    119 static struct platform_driver x210_led_driver = {
    120     .probe        = x210_led_probe,
    121     .remove        = x210_led_remove,
    122     .driver        = {
    123         .name        = "x210_led",
    124         .owner        = THIS_MODULE,
    125     },
    126 };
    127 
    128 static int __init leds_x210_init(void)
    129 {
    130     int ret = 0;
    131     
    132     ret = platform_driver_register(&x210_led_driver);
    133     if (ret)
    134        printk(KERN_ERR "platform_driver_register failed.
    ");
    135     
    136     return ret; 
    137 }
    138 
    139 static void __exit leds_x210_exit(void)
    140 {
    141     platform_driver_unregister(&x210_led_driver);    
    142 }
    143 
    144 /*函数入口和出口*/
    145 module_init(leds_x210_init);
    146 module_exit(leds_x210_exit);
    147 
    148 /*描述模块信息*/
    149 MODULE_LICENSE("GPL");                // 描述模块的许可证
    150 MODULE_AUTHOR("Tao Deng <> 773904075@qq.com");            // 描述模块的作者
    151 MODULE_DESCRIPTION("led driver for x210.");    // 描述模块的介绍信息
    152 MODULE_ALIAS("alias DT");            // 描述模块的别名信息
    driver

    3、platform总线工作原理

    (1)insmod之后的现象

    当我们执行第一个insmod的时候,并没有出现后面的4条打印信息,只是执行了leds_x210_init函数(一次insmod只能对应执行一次xxx_init函数,

    一次rmmod只能对应执行一次xxx_exit函数),这里并没有放置打印语句的信息,因为此时匹配并不会成功,所以不会执行驱动层probe函数;

    而当执行第二个insmod的时候,此时platform总线下的match函数匹配就会成功,因为对应有4个led设备,所以就会匹配4次,执行了4次probe

    函数打印出相应的信息。

    (2)rmmod时的现象

    当我们卸载调驱动或者是设备中的任何一个,都会执行驱动层的remove函数;如果是先rmmod设备,那么先执行驱动层中的remove函数,之后就

    会执行设备层中的platform_device结构体中的device结构体下的release函数,每一个设备都会执行一次,因为这里卸载了4个设备,所以就会执行

    4次;如果是先rmmod驱动,那么直接就会执行驱动层的remove函数。

    (3)总结:

    insmod注册执行入口函数leds_x210_init -> platform总线下match函数进行匹配 -> 匹配成功则执行驱动层platform_driver结构体下的probe函数(失败就没什么可说的了)->

    初始化驱动。

    rmmod卸载执行出口函数leds_x210_exit -> 执行驱动层platform_driver结构体下的remove函数 -> 如果是设备则还需要执行设备层platform_device结构体中的device结构体下的

    release函数。

    (4)当我们注册了设备之后,在platform总线的device中会出现我们注册的设备;当我们注册了驱动之后,在platform总线的driver中会出现驱动名;

    但是只要match函数没有匹配成功就不会执行probe函数,那么属于操作led驱动框架下的接口/sys/class/leds下的设备接口就不会出现,因为

    led_classdev_register函数在probe中执行。

     参考:《朱友鹏嵌入式Linux开发5.Linux驱动开发5.5.linux设备驱动模型》

     

  • 相关阅读:
    解决SharePoint 文档库itemadded eventhandler导致的上传完成后,编辑页面保持报错的问题,错误信息为“该文档已经被编辑过 the file has been modified by...”
    解决SharePoint 2013 designer workflow 在发布的报错“负载平衡没有设置”The workflow files were saved but cannot be run.
    随机实例,随机值
    Spring4笔记
    struts2笔记(3)
    struts2笔记(2)
    获取文本的编码类型(from logparse)
    FileUtil(from logparser)
    DateUtil(SimpleDateFormat)
    struts2笔记
  • 原文地址:https://www.cnblogs.com/deng-tao/p/6033571.html
Copyright © 2011-2022 走看看