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

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

 #include <linux/module.h>        // module_init  module_exit
#include <linux/init.h> // __init __exit
#include <linux/fs.h>
#include <linux/leds.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <mach/gpio.h>
#include <linux/platform_device.h> // 定义一个结构体用于放置本设备的私有数据
struct x210_led_platdata {
unsigned int gpio; // led设备用到的GPIO
char *device_name; // led设备在/sys/class/leds/目录下的名字
char *gpio_name; // 使用gpiolob申请gpio资源时分配的名字
}; // 定义x210_led_platdata类型的变量,分别对应板子上的4颗led小灯
static struct x210_led_platdata x210_led1_pdata = {
.gpio = S5PV210_GPJ0(),
.device_name = "led1",
.gpio_name = "led1-gpj0_3",
}; static struct x210_led_platdata x210_led2_pdata = {
.gpio = S5PV210_GPJ0(),
.device_name = "led2",
.gpio_name = "led2-gpj0_4",
}; static struct x210_led_platdata x210_led3_pdata = {
.gpio = S5PV210_GPJ0(),
.device_name = "led3",
.gpio_name = "led3-gpj0_5",
}; static struct x210_led_platdata x210_led4_pdata = {
.gpio = S5PV210_GPD0(),
.device_name = "led4",
.gpio_name = "led4-gpd0_1",
}; // 定义4个release函数,当我们卸载设备时会调用platform_device结构体中的device结构体下的release函数
void x210_led1_release(struct device *dev)
{
printk(KERN_INFO "x210_led1_release\n");
} void x210_led2_release(struct device *dev)
{
printk(KERN_INFO "x210_led1_release\n");
} void x210_led3_release(struct device *dev)
{
printk(KERN_INFO "x210_led1_release\n");
} void x210_led4_release(struct device *dev)
{
printk(KERN_INFO "x210_led1_release\n");
} // 定义4个platform_device结构体
static struct platform_device x210_led1 = {
.name = "x210_led",
.id = ,
.dev = {
.platform_data = &x210_led1_pdata,
.release = x210_led1_release,
},
}; static struct platform_device x210_led2 = {
.name = "x210_led",
.id = ,
.dev = {
.platform_data = &x210_led2_pdata,
.release = x210_led2_release,
},
}; static struct platform_device x210_led3 = {
.name = "x210_led",
.id = ,
.dev = {
.platform_data = &x210_led3_pdata,
.release = x210_led3_release,
},
}; static struct platform_device x210_led4 = {
.name = "x210_led",
.id = ,
.dev = {
.platform_data = &x210_led4_pdata,
.release = x210_led4_release,
},
}; // 将4个platform_device结构体地址放入一个数组中
static struct platform_device *x210_devices[] = {
&x210_led1,
&x210_led2,
&x210_led3,
&x210_led4,
}; // 入口函数
static int __init leds_x210_init(void)
{
if (platform_add_devices(x210_devices, ARRAY_SIZE(x210_devices))) // 循环注册platform平台设备
{
printk(KERN_ERR "platform_add_devices failed.\n");
return -;
} return ;
} // 出口函数(卸载)
static void __exit leds_x210_exit(void)
{
int i = ;
for (i = ; i < ; i++)
platform_device_unregister(x210_devices[i]); // 当执行到这个函数的时候就会去执行platform_device结构体中的device结构体下的release函数
} /*函数入口和出口修饰*/
module_init(leds_x210_init);
module_exit(leds_x210_exit); /*描述模块信息*/
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("Tao Deng <> 773904075@qq.com"); // 描述模块的作者
MODULE_DESCRIPTION("led device for x210."); // 描述模块的介绍信息
MODULE_ALIAS("alias DT"); // 描述模块的别名信息

device

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

 #include <linux/module.h>        // module_init  module_exit
#include <linux/init.h> // __init __exit
#include <linux/fs.h>
#include <linux/leds.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <mach/gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h> enum LED{
LED_ON_ = ,
LED_OFF_ = ,
}; struct x210_led_platdata {
unsigned int gpio;
char *device_name;
char *gpio_name;
}; struct x210_platform {
struct led_classdev led_class;
unsigned int gpio;
}; static inline struct x210_platform *pdev_to_gpio(struct platform_device *dev)
{
return platform_get_drvdata(dev);
} static void x210_led_set(struct led_classdev *led_cdev, enum led_brightness brightness)
{
struct x210_platform *pdata = container_of(led_cdev, struct x210_platform, led_class); // 使用led_classdev结构体反推得到x210_platform结构体 if ( < brightness)
gpio_set_value(pdata->gpio, LED_ON_); // 使led小灯发亮
else
gpio_set_value(pdata->gpio, LED_OFF_); // 使led小灯熄灭
} static int x210_led_probe(struct platform_device *dev)
{
// 定义变量
int ret = ;
struct x210_led_platdata *platdata = dev->dev.platform_data;
struct x210_platform *pform = NULL; // 分配空间
pform = kzalloc(sizeof(struct x210_platform), GFP_KERNEL); // 将pform指针指向系统分配的地址空间
if (NULL == pform) {
printk(KERN_ERR "kzalloc failed.\n");
return -ENOMEM;
} platform_set_drvdata(dev, pform); // 将pform值写入到传进来的platform_device结构体中的device结构体下的私有数据区中去,以便后面释放内存时用 //驱动框架接口填充
pform->led_class.name = platdata->device_name;
pform->led_class.brightness = ;
pform->led_class.brightness_set = x210_led_set; // 保存gpio管脚信息
pform->gpio = platdata->gpio; //注册led驱动
ret = led_classdev_register(NULL, &pform->led_class);
if (ret < ) {
printk(KERN_ERR "led_classdev_register failed.\n");
goto err_led_classdev_register;
} // 向gpiolib管理器申请gpio资源
if (gpio_request(platdata->gpio, platdata->gpio_name))
{
printk(KERN_ERR "gpio_request failed.\n");
goto err_gpio_request;
} // 设置GPIO输出高电平,关闭LED
gpio_direction_output(platdata->gpio, LED_OFF_); printk(KERN_INFO "x210_led_probe succeseful.\n"); return ; err_gpio_request:
led_classdev_unregister(&pform->led_class); err_led_classdev_register: return -;
} static int x210_led_remove(struct platform_device *dev)
{
struct x210_led_platdata *platdata = dev->dev.platform_data;
struct x210_platform *pform = pdev_to_gpio(dev); // 取出platform_device结构体中的device结构体中的私有数据区的x210_platform指针 // 卸载led驱动
led_classdev_unregister(&pform->led_class); // 关闭所有led
gpio_set_value(platdata->gpio, LED_OFF_); // 释放申请的GPIO资源
gpio_free(platdata->gpio); // 释放内存
kfree(pform); // 这个一定要放在最后 printk(KERN_INFO "x210_led_remove succeseful.\n"); return ;
} static struct platform_driver x210_led_driver = {
.probe = x210_led_probe,
.remove = x210_led_remove,
.driver = {
.name = "x210_led",
.owner = THIS_MODULE,
},
}; static int __init leds_x210_init(void)
{
int ret = ; ret = platform_driver_register(&x210_led_driver);
if (ret)
printk(KERN_ERR "platform_driver_register failed.\n"); return ret;
} static void __exit leds_x210_exit(void)
{
platform_driver_unregister(&x210_led_driver);
} /*函数入口和出口*/
module_init(leds_x210_init);
module_exit(leds_x210_exit); /*描述模块信息*/
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("Tao Deng <> 773904075@qq.com"); // 描述模块的作者
MODULE_DESCRIPTION("led driver for x210."); // 描述模块的介绍信息
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设备驱动模型》

platform总线驱动代码分析的更多相关文章

  1. Linux时间子系统之(十七):ARM generic timer驱动代码分析

    专题文档汇总目录 Notes:ARM平台Clock/Timer架构:System counter.Timer以及两者之间关系:Per cpu timer通过CP15访问,System counter通 ...

  2. 三星framebuffer驱动代码分析

    一.驱动总体概述 本次的驱动代码是Samsung公司为s5pv210这款SoC编写的framebuffer驱动,对应于s5pv210中的内部外设Display Controller (FIMD)模块. ...

  3. [置顶] 自娱自乐7之Linux UDC驱动2(自编udc驱动,现完成枚举过程,从驱动代码分析枚举过程)

    花了半个月,才搞定驱动中的枚举部分,现在说linux的枚举,windows可能有差别. 代码我会贴在后面,现在只是实现枚举,你可能对代码不感兴趣,我就不分析代码了,你可以看看 在<自娱自乐1&g ...

  4. Linux时间子系统(十七) ARM generic timer驱动代码分析

    一.前言 关注ARM平台上timer driver(clocksource chip driver和clockevent chip driver)的驱动工程师应该会注意到timer硬件的演化过程.在单 ...

  5. 基于等待队列及poll机制的按键驱动代码分析和测试代码

    按键驱动分析: #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> ...

  6. USB转串口驱动代码分析

    1.USB插入时,创建设备 [plain] view plaincopy DriverObject->DriverExtension->AddDevice = USB2COM_PnPAdd ...

  7. 支持阻塞操作和轮询操作的globalfifo设备驱动代码分析以及测试代码

    #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include ...

  8. linux内核SPI总线驱动分析(一)(转)

    linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...

  9. Linux内核中SPI总线驱动分析

    本文主要有两个大的模块:一个是SPI总线驱动的分析 (研究了具体实现的过程): 另一个是SPI总线驱动的编写(不用研究具体的实现过程). 1 SPI概述 SPI是英语Serial Peripheral ...

随机推荐

  1. phpstorm搜索匹配正则表达式

    data-position=".................................................................." 点是匹配任意一 ...

  2. HTTP请求中的form data,request payload,query string parameters以及在node服务器中如何接收这些参数

    http://www.cnblogs.com/hsp-blog/p/5919877.html 今天,在工作(倒腾微信小程序)的时候,发现发送post请求到node后台服务器接收不到前端传来的参数.其实 ...

  3. bzoj2004公交线路

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2004 好美妙的矩阵乘. 思考: 0.在一个序列上.所以考虑dp. 1.p<=10,k& ...

  4. load/domContentLoaded事件、异步/延迟Js 与DOM解析

    一.DOMContentLoaded 与 load事件 关于load和DOMContentLoaded事件,mdn对于它们是这样描述的: DOMContentLoaded mdn文档地址:https: ...

  5. Remi 安装源

    Remi repository 是包含最新版本 PHP 和 MySQL 包的 Linux 源,由 Remi 提供维护.有个这个源之后,使用 YUM 安装或更新 PHP.MySQL.phpMyAdmin ...

  6. PDB文件:每个开发人员都必须知道的 PDB Files

    PDB文件:每个开发人员都必须知道的   PDB Files: What Every Developer Must Knowhttp://www.wintellect.com/CS/blogs/jro ...

  7. Java 迭代器 Iterator

    迭代器模式 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式.这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示. 迭代器模式属于行 ...

  8. CSS布局模型学习

    转自:http://www.cnblogs.com/erliang/p/4092192.html CSS布局模型学习   参考链接慕课网:HTML+CSS基础课程 知识基础 1. 样式 内联 嵌入 外 ...

  9. MS-TEST 批处理执行测试时的资源文件目录问题

    What: 使用MS-TEST的 批处理执行它的测试 DLL  时,如: MSTest /testcontainer:C:\David\ADAccountGIT\AdAccountMSTest\ADA ...

  10. 初尝微信小程序开发与实践

    这可能是一个java程序员最不务正业的一次分享了. 小程序的火热相信不用我多说了,年初的时候老婆去浦东某达面试,甚至都被问有没有小程序测试经验.俨然小程序成为了互联网公司自PC,WAP,安卓,IOS之 ...