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设备驱动模型》

     

  • 相关阅读:
    求最大公约数
    1013
    实现页面的3D效果
    实现购物车的加减算法
    随机验证码
    深入理解jQuery中的ajax
    canvas粒子时钟
    node、express框架
    vue的增删改查
    实现点击a标签页面跳转后颜色高亮
  • 原文地址:https://www.cnblogs.com/deng-tao/p/6033571.html
Copyright © 2011-2022 走看看