arm linux 电源管理 platform 挂起函数,[arm驱动]Platform设备驱动

Tip:红色字体为简要部分

《[arm驱动]Platform设备驱动》涉及内核驱动函数五个,内核结构体三个,分析了内核驱动函数四个;可参考的相关应用程序模板或内核驱动模板零个,可参考的相关应用程序或内核驱动二个

想了解platform总线管理方式的原理 参考[arm驱动]Platform总线原理

前言扩展

1、sysfs文件系统

设备模型sysfs是2.6内核新引入的特征。设备模型提供了一个独立的机制专门来表示设备,并描述其在系统中的拓扑结构。

在2.4内核中,设备的信息放在/proc中。

而在2.6内核,内核把设备相关的信息归类在新增加sysfs文件系统,并将它挂载到/sys目录中,把设备信息归类的同时,让用户可以通过用户空间访问。

2、sys中的目录:

block:用于管理块设备,系统中的每一个块设备会在该目录下对应一个子目录。

bus:用于管理总线,每注册一条总线,在该目录下有一个对应的子目录。(重要)

其中,每个总线子目录下会有两个子目录:devices和drivers。

devices包含里系统中所有属于该总线的的设备。(重要)

drivers包含里系统中所有属于该总线的的驱动。(这个好像在linux2.6以上版本剔除了)

class:将系统中的设备按功能分类。(重要)

devices:该目录提供了系统中设备拓扑结构图。

dev:该目录已注册的设备节点的视图。

kernel:内核中的相关参数。

module:内核中的模块信息。

fireware:内核中的固件信息。

Fs:描述内核中的文件系统。

3、总线,设备,驱动对应的定义struct bus_type,devices,drivers;

4、总线:总线不是字符设备、块设备和网络设备并列的概念,它是一种"设备"和"驱动"的管理者,总线通过分别遍历"设备(device)"和”驱动(driver)"链表如果发现name是一样的就将调device_bind_driver将name对应的设备(device)和驱动(driver)绑定好。

5、总的来说:对于总线型设备驱动,我们只要关心如何将设备(device)和驱动(driver)挂载到总线上就行了;剩下的工作就交给了内核了:设备(device)如果配备驱动(driver)由内核的总线来管理

6、总线的工作方式案例:以USB为例(u盘、键盘、鼠标设备都算usb)

a)古老的方式:

先有device,每一个要用的device在计算机启动之前就已经插好了,插放在它应该在的位置上,然后计算机启动,然后操作系统开始初始化,总线开始扫描设备,每找到一个设备,就为其申请一个struct device结构,并且挂入总线中的devices链表中来;然后每一个驱动程序开始初始化,开始注册其struct device_driver结构,然后它去总线的devices链表中去寻找(遍历),去寻找每一个还没有绑定driver的设备,即struct device中的struct device_driver指针仍为空的设备,然后它会去观察这种设备的特征,看是否是他所支持的设备,如果是,那么调用一个叫做device_bind_driver的函数,然后他们就结为了秦晋之好.

b)现在的方式:

device可以在任何时刻出现,而driver也可以在任何时刻被加载,所以,出现的情况就是,每当一个struct device诞生,它就会去bus的drivers链表中寻找自己的另一半,反之,每当一个一个struct device_driver诞生,它就去bus的devices链表中寻找它的那些设备.如果找到了合适的,那么ok,和之前那种情况一下,调用device_bind_driver绑定好.如果找不到,没有关系,等待

7)platform总线是bus总线的一条子总线,可以理解为platform继承了bus,platform是bus的子类

一、platform设备驱动

platform继承bus,文件位于/sys/bus/platform下;/sys//bus/platform/device文件夹和/sys/bus/platform/driver目录下的文件分别是对应platform总线的"设备(device)“驱动(driver)"。与传统的bus/device/driver机制相比,platform由内核进行统一管理,在驱动中使用资源,提高了代码的安全性和可移植性。

1.一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI等的设备而言,这自然不是问题,

但是在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等确不依附于此类总线。

基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为 platform_driver。

2.注意,所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念,而是Linux系统提供的一种附加手段,

例如,在 S3C6410处理器中,把内部集成的I2C、RTC、SPI、LCD、看门狗等控制器都归纳为platform_device,而它们本身就是字符设备。

二、设备(device)驱动(driver)相关结构体

结构体一)platform设备(device)结构体platform_devicestruct platform_device {     //设备结构体信息

const char      * name;     //该平台设备的名称

u32             id;      //设备id,用于给插入给该总线并且具有相同name的设备编号,如果只有一个设备的话填-1。

struct device   dev;    ///结构体中内嵌的device结构体

u32             num_resources;    //设备使用的资源的数目

struct resource * resource;         //定义平台设备的资源

};

结构体二)device结构体中的resourcesstruct resource {

resource_size_t start;      //定义资源的起始地址

resource_size_t end;       //定义资源的结束地址

const char *name;                   //定义资源的名称

unsigned long flags;           //定义资源的类型,例如MEM, IO ,IRQ, DMA类型

struct resource *parent, *sibling, *child;     //资源链表指针

};

结构体三)platform 驱动(driver)结构体struct platform_driver {

int (*probe)(struct platform_device *);

//driver和device注册之后,会触发平台设备和驱动的匹配函数platform_match,匹配成功,则会调用平台设备驱动的probe()函数

int (*remove)(struct platform_device *);        //删除该设备

void (*shutdown)(struct platform_device *);    //关闭该设备

int (*suspend)(struct platform_device *, pm_message_t state);//电源管理挂起

int (*suspend_late)(struct platform_device *, pm_message_t state);//电源管理挂起

int (*resume_early)(struct platform_device *);//电源管理唤醒

int (*resume)(struct platform_device *);//电源管理唤醒

struct device_driver driver;                         //老设备驱动,定义在include/linux/device.h中

};

三、设备(device)驱动(driver)相关定义、注册函数

1设备结构体定义和注册

1)、设备platform_device变量的定义

方式a)使用platform_device结构体数组定义

单个设备结构platform_device体定义static struct platform_device platformthird_dev_device = {  //单个设备结构体定义

.name = "platformthird_dev",

.id = -1,

};

多个设备结构体定义static struct platform_device platformthird_dev_device[] = {  //多个设备结构体定义

[0] = {.name = "platformthird_dev",

.id = 0,

},

[1] = {.name = "platformthird_dev",

.id = 1,

},

[2] = {.name = "platformthird_dev",

.id = 2,

},

//................

};

函数一)b)动态定义方式一个platform_devicestruct platform_device * platform_device_alloc(const char * name, unsigned int id)

如:struct platform_device *platformthird_dev_device = platform_device_alloc("platform_led", -1);

//-1表示只有一个设备

2)、设备(device)注册函数

函数二)单个设备(device)的注册:platform_device_register(struct platform_device * pdev);

内核源码一)int platform_device_register(struct platform_device *pdev)

{

device_initialize(&pdev->dev);//dev初始化

arch_setup_pdev_archdata(pdev);

return platform_device_add(pdev);//加入到dev链表

}

//int platform_device_add(struct platform_device *pdev)内核源码

int platform_device_add(struct platform_device *pdev)

{

int i, ret = 0;

if (!pdev)

return -EINVAL;

if (!pdev->dev.parent)

pdev->dev.parent = &platform_bus;

pdev->dev.bus = &platform_bus_type;

//id = -1时的操作

if (pdev->id != -1)

snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);

else

strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);

for (i = 0; i num_resources; i++) {

struct resource *p, *r = &pdev->resource[i];

if (r->name == NULL)

r->name = pdev->dev.bus_id;

p = r->parent;

if (!p) {

if (r->flags & IORESOURCE_MEM)

p = &iomem_resource;

else if (r->flags & IORESOURCE_IO)

p = &ioport_resource;

}

if (p && insert_resource(p, r)) {

printk(KERN_ERR

"%s: failed to claim resource %d\n",

pdev->dev.bus_id, i);

ret = -EBUSY;

goto failed;

}

}

pr_debug("Registering platform device '%s'. Parent at %s\n",

pdev->dev.bus_id, pdev->dev.parent->bus_id);

ret = device_add(&pdev->dev);

if (ret == 0)

return ret;

failed:

while (--i >= 0)

if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))

release_resource(&pdev->resource[i]);

return ret;

}

函数三)多个设备(device)的注册:platform_add_devices(struct platform_device **devs, int num)

内核源码三)//platform_add_devices调用了platform_device_register

int platform_add_devices(struct platform_device **devs, int num)

{

int i, ret = 0;

for (i = 0; i 

ret = platform_device_register(devs[i]);

if (ret) {

while (--i >= 0)

platform_device_unregister(devs[i]);

break;

}

}

return ret;

}

如:platform_add_devices(platformthird_dev_device,  ARRAY_SIZE(platformthird_dev_device));

2、驱动(driver)结构体定义和注册

a)platform_driver结构体的定义如:static struct platform_driver platformthird_dev_driver = {

.probe = platformthird_dev_probe,

.remove = platformthird_dev_remove,

.driver = {

.name = "platformthird_dev",//名字要和设备(device)的名字一样

.owner = THIS_MODULE,

}

函数四)platform_driver结构体的注册platform_driver_register(struct platform_driver *drv)

如:platform_driver_register(&platformthird_dev_driver)

内核源码四)

int platform_driver_register(struct platform_driver *drv)

{

drv->driver.bus =&platform_bus_type;

//总线platform_bus_type就是指platform总线,详细见(五、platform总线管理方式的原理)

/*设置成platform_bus_type这个很重要,因为driver和device是通过bus联系在一起的,

具体在本例中是通过 platform_bus_type中注册的回调例程和属性来是实现的,

driver与device的匹配就是通过 platform_bus_type注册的回调例程platform_match ()来完成的。

*/

if(drv->probe)

drv-> driver.probe = platform_drv_probe;

if(drv->remove)

drv->driver.remove = platform_drv_remove;

if(drv->shutdown)

drv->driver.shutdown = platform_drv_shutdown;//电源设备

return driver_register(&drv->driver);

//注册驱动,将drv的driver添加到总线的driver链表中

}

函数五)int (*probe)(struct platform_device *);//driver被insmod后,会触发平台设备和驱动的匹配函数platform_match匹配成功,则会调用平台设备驱动的probe()函数;详细看五、platform总线管理方式的原理。

driver结构体几个函数的运行顺序

插入driver==>触发平台设备和驱动的匹配函数platform_match匹配,成功则调用probe()==>当device被卸载时就会调用remove

四、实例

实例一)实验一、只添加一个名为name的驱动(driver)到总线上,此时只创建名称为name驱动文件夹/sys/bus/platform/driver/name/*

*本程序只涉及platform的driver(驱动);未涉及device。

*/

#include 

#include 

#include 

#include 

#include 

#include 

MODULE_LICENSE("GPL");

static int test_probe(struct platform_device *dev)//

{

printk("driver say the driver found the device\n");

return 0;

}

static int test_remove(struct platform_device *dev)

{

printk("driver say the device is polled out\n");

return 0;

}

//platform 总线相关文件挂载/sys/bus/platform/路径下

static struct platform_driver test_driver = {//driver是驱动的意思,相关文件在/sys/bus/platform/drivers

.probe = test_probe,//注册时要执行的函数

.remove = test_remove,//注销时会执行的函数

.driver = {

.owner = THIS_MODULE,

.name = "platform_dev",//会在"/sys/bus/platform/drivers"下创建platform_dev文件夹

},

};

static int __init test_driver_init(void)

{

/*注册平台驱动*/

return platform_driver_register(&test_driver);

}

static void test_driver_exit(void)

{

platform_driver_unregister(&test_driver);

}

module_init(test_driver_init);

module_exit(test_driver_exit);

实例二)实验二、只添加一个名为names(driver)到总线上,此时只创建名称为name驱动文件夹/sys/bus/platform/drivers/name

#include 

#include 

#include 

#include 

#include 

#include 

#include //文件系统相关的函数和头文件

#include 

#include  //cdev结构的头文件包含

static void platformthird_dev_release(struct device * dev){

printk("device say the device is release\n");

return;

}

static struct platform_device platformthird_dev_device = {  //添加设备结构体

.name = "platform_dev",

.id = -1,

.dev = {

.release = platformthird_dev_release,//解决"Device 'platform_dev' does not have a release() function“问题

}

};

static int  __init platformthird_dev_init(void){

platform_device_register(&platformthird_dev_device);  //注册设备到内核

return 0;

}

static void platformthird_dev_exit(void){

platform_device_unregister(&platformthird_dev_device);  //卸载设备

printk(KERN_ALERT "good bye\n");

}

module_init(platformthird_dev_init);

module_exit(platformthird_dev_exit);

MODULE_LICENSE("GPL");

实例一和实例二的Makefile如下

KERN_DIR = /workspacearm/linux-2.6.2.6

#platform_device_test.ko

all:

make -C $(KERN_DIR) M=`pwd` modules

cp platform_device_test.ko /opt/fsmini/

cp platform_driver_test.ko /opt/fsmini/

clean:

make -C $(KERN_DIR) M=`pwd` modules clean

rm -rf modules.order

rm -rf Module.symvers

obj-m   += platform_device_test.o

obj-m   += platform_driver_test.o

Make一下,在开发板上加载驱动,结果如下输出# insmod platform_driver_test.ko

# insmod platform_device_test.ko

driver say the driver found the device

# rmmod platform_device_test.ko

driver say the device is polled out

device say the device is release

good bye

# rmmod platform_driver_test.ko

#

实验结论:从前面三句可以看的出来,当driver没有device时,会一直等候device的接入,类似热插拔,当有对应name的device时执行probe;从第四句可以知道当设备被拔出时候,driver执行remove。验证了:插入driver==>触发平台设备和驱动的匹配函数platform_match匹配,成功则调用probe()

实例三)实验三、创建名称为name的/sys/bus/platform/drivers/name和/sys/bus/platform/devices/name文件夹/*

*本程序实现将字符设备挂载在总线上,只是在原来的的基础上加上一层platform_driver的外壳。

*将驱动设备device挂载在总线上,这样的好处是:总线将设备(device)和驱动(driver)绑定,当有事件发生时就会调用相应驱动函数来完成,如智能电源管理(挂起,休眠,关机唤醒等等),热插拔,即插即用的支持

*

*/

#include 

#include 

#include 

#include 

#include 

#include 

#include //文件系统相关的函数和头文件

#include 

#include  //cdev结构的头文件包含

#define VIRTUALDISK_MAJOR 250

int VirtualDisk_major = VIRTUALDISK_MAJOR;

static int  platformthird_dev_probe(struct platform_device *pdev){

return 0;

}

static int platformthird_dev_remove(struct platform_device *pdev){

return 0;

}

static struct platform_device platformthird_dev_device = {  //添加设备结构体

.name = "platformthird_dev",

.id = -1,

};

static struct platform_driver platformthird_dev_driver = {

.probe = platformthird_dev_probe,

.remove = platformthird_dev_remove,

.driver = {

.name = "platformthird_dev",

.owner = THIS_MODULE,

}

};

static int  __init platformthird_dev_init(void){

int ret = 0;

platform_device_register(&platformthird_dev_device);  //注册设备到内核总线

ret = platform_driver_register(&platformthird_dev_driver);  //注册驱动到内核总线

if (ret){

printk(KERN_ERR "failed to register\n");

}

return ret;

}

static void platformthird_dev_exit(void){

platform_device_unregister(&platformthird_dev_device);  //卸载设备

platform_driver_unregister(&platformthird_dev_driver);  //卸载驱动

printk(KERN_ALERT "good bye\n");

}

module_init(platformthird_dev_init);

module_exit(platformthird_dev_exit);

MODULE_LICENSE("GPL");

实例四)实验四,给以前写的一般驱动代码加platform壳,原来只是创建/dev/***,注册class,class_device(对应/sys/class文件夹下****文件夹)

/*

*本程序实现将字符设备挂载在总线上,只是在原来的的基础上加上一层platform_driver的外壳。

*将驱动设备device挂载在总线上,这样的好处是:总线将设备(device)和驱动(driver)绑定,当有事件发生时就会调用相应驱动函数来完成,如智能电源管理(挂起,休眠,关机唤醒等等),热插拔,即插即用的支持

*

*/

#include 

#include 

#include 

#include 

#include 

#include 

#include //文件系统相关的函数和头文件

#include 

#include  //cdev结构的头文件包含

#define VIRTUALDISK_MAJOR 250

int VirtualDisk_major = VIRTUALDISK_MAJOR;

struct cdev cdev;

static struct class *platform_class;

static struct class_device  *platform_class_dev;

static int platform_drv_open(struct inode *inode, struct file *file)

{

printk("platform_dev read\n");

return 0;

}

static int platform_drv_release(struct inode *inode, struct file *file)

{

printk("platform_dev release\n");

return 0;

}

static struct file_operations platform_drv_fops = {

.owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

.release = platform_drv_release,

.open = platform_drv_open,

};

static int  platform_dev_probe(struct platform_device *pdev){

int result;

int err;

dev_t devno = MKDEV(VirtualDisk_major, 0);

if(VirtualDisk_major){

result = register_chrdev_region(devno, 1, "platform_dev");

}else{

result = alloc_chrdev_region(&devno, 0, 1, "platform_dev");

VirtualDisk_major = MAJOR(devno);

}

if(result 

return result;

}

cdev_init(&cdev, &platform_drv_fops);

cdev.owner = THIS_MODULE;

err = cdev_add(&cdev, devno, 1);

if(err){

printk("error %d cdev file added\n", err);

}

platform_class = class_create(THIS_MODULE, "platform_dev");

if (IS_ERR(platform_class))

return PTR_ERR(platform_class);

platform_class_dev = class_device_create(platform_class, NULL, MKDEV(VirtualDisk_major, 0), NULL, "platform_dev"); /* /dev/xyz */

if (IS_ERR(platform_class_dev))

return PTR_ERR(platform_class_dev);

return 0;

}

static int platform_dev_remove(struct platform_device *pdev){

cdev_del(&cdev);

unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);

class_device_unregister(platform_class_dev);

class_destroy(platform_class);

return 0;

}

static void platform_device_release(struct device * dev){

return;

}

static struct platform_device platform_dev_device = {  //添加设备结构体

.name = "platform_dev",

.id = -1,

.dev = {

.release = platform_device_release,//解决"Device 'platform_dev' does not have a release() function“问题

}

};

static struct platform_driver platform_dev_driver = {

.probe = platform_dev_probe,

.remove = platform_dev_remove,

.driver = {

.name = "platform_dev",

.owner = THIS_MODULE,

}

};

static int  __init platform_dev_init(void){

int ret = 0;

platform_device_register(&platform_dev_device);  //注册设备到内核

ret = platform_driver_register(&platform_dev_driver);  //注册驱动到内核

if (ret){

printk(KERN_ERR "failed to register\n");

}

return ret;

}

static void platform_dev_exit(void){

platform_device_unregister(&platform_dev_device);  //卸载设备

platform_driver_unregister(&platform_dev_driver);  //卸载驱动

printk(KERN_ALERT "good bye\n");

}

module_init(platform_dev_init);

module_exit(platform_dev_exit);

MODULE_LICENSE("GPL");

五、platform总线管理方式的原理

参考[arm驱动]Platform总线原理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值