arm linux 电源管理 platform 挂起函数,设备驱动模型:字符、BUS、platform、sysfs的关系...

以前做了很多设备驱动,对于字符设备,平台设备,I2C设备,块设备,SPI设备,USB设备等等,开始的时候一直没太明白是怎么回事。

做驱动的时候,对这些模型也没有搞清楚关系,主要都是挖掘一些硬件特性。

后来看了《设备驱动开发详解》以后,对这些设备驱动模型终于有了一个比较清晰的认识。

一、设备类型的关系

从时间上来看,

最早的设备类型是:字符设备,块设备,网络设备;这些设备模型不知道是什么时候就有的,自我学习2.4内核就已经有这些基本的类型了。

扩展的设备类型:2.6内核引入的bus_type、device_driver、device分别描述总线,驱动和设备,这就是所谓的“总线设备驱动模型”。

总线是三者联系起来的基础,通过一种总线类型,将设备和驱动联系起来。

总线类型中的match函数用来匹配设备和驱动,当match操作完成之后就会调用device_driver中的probe函数。

struct bus_type {

/*总线名*/

const char *name;

/*总线、设备、驱动属性*/

struct bus_attribute *bus_attrs;

struct device_attribute *dev_attrs;

struct driver_attribute *drv_attrs;

/*总线支持的函数操作*/

/*匹配函数,主要用来识别相应的设备和驱动,是两者直接形成关联

用来判断指定的驱动程序能否处理指定的设备

*/

int (*match)(struct device *dev, struct device_driver *drv);

/*在进行热插拔事件之前,为设备配置环境变量操作函数*/

int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

int (*probe)(struct device *dev);

int (*remove)(struct device *dev);

void (*shutdown)(struct device *dev);

int (*suspend)(struct device *dev, pm_message_t state);

int (*suspend_late)(struct device *dev, pm_message_t state);

int (*resume_early)(struct device *dev);

int (*resume)(struct device *dev);

struct dev_pm_ops *pm;

struct bus_type_private *p;

};这段话和结构体是不是和platform的概念很相似?

实际上,platform,I2C,SPI等,都是BUS的一种。

引用《设备驱动开发详解》的一段话,大意:

对于依附于I2C SPI总线的设备而言,设备和驱动挂在这些总线上。

对于SOC内部外设控制器和外设,需要依附于linux的虚拟总线,称之为platform。

所以,这些概念可以分为两类,一类是访问方式:字符 or 块,一类是总线。

对于这两种分类方式,是不相互隔离的,也就是说,一个设备既可以使字符设备,也可以是某种总线设备。

比较经典的例子是在已有的字符设备驱动模块中,再添加总线设备驱动(后面会说明作用)。

引用http://blog.csdn.net/yicao821/article/details/6783261代码:

led_dev.c

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

/* 参考arch/arm/plat-s3c24xx/devs.c */

/*1. 根据芯片手册来获取资源*/

static struct resource led_resource[] = {

[0] = {

.start = 0x56000050,

.end = 0x56000057,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = 5,

.end = 5,

.flags = IORESOURCE_IRQ,

},

};

void led_release(struct device *dev)

{

}

/*1.构建平台设备结构体,将平台资源加入进来*/

struct platform_device led_device = {

.name = "myled", /* 使用名为"myled"的平台驱动 */

.id = -1,

.dev = {

.release = led_release,

},

.num_resources = ARRAY_SIZE(led_resource),

.resource = led_resource,

};

/*2。把我们的设备资源挂在到虚拟总线的设备连表中去*/

int led_dev_init(void)

{

platform_device_register(&led_device);

return 0;

}

void led_dev_exit(void)

{

platform_device_unregister(&led_device);

}

module_init(led_dev_init);

module_exit(led_dev_exit);

MODULE_LICENSE("GPL");

led_drv.c

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

static int major = 0;

static volatile unsigned long *gpio_con;

static volatile unsigned long *gpio_dat;

static int pin;

static struct class *cls;

int led_open(struct inode *inode, struct file *file)

{

/* 设置对应的引脚为输出引脚 */

*gpio_con &= ~(0x3 << (pin * 2));

*gpio_con |= (0x1 << (pin * 2));

return 0;

}

ssize_t led_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)

{

char ker_buf[1];

/* 根据buf传入的值点/灭灯 */

/* buf[0] - 亮/灭 , 0 - 亮, 1 - 灭 */

if (size != 1)

{

return -EINVAL;

}

copy_from_user(ker_buf, buf, 1);

if ((ker_buf[0] != 0) && (ker_buf[0] != 1))

return -EINVAL;

if (ker_buf[0])

{

// 某个LED

*gpio_dat |= (0x1<< pin);

}

else

{

// 某个LED

*gpio_dat &= ~(0x1<

}

return 1;

}

/*4实现操作硬件的方法*/

static struct file_operations led_fops = {

.owner = THIS_MODULE,

.open = led_open,

.write = led_write,

};

/*3。实现probe函数*/

static int led_probe(struct platform_device *dev)

{

struct resource *r;

/* 根据平台设备确定点哪一个LED */

r = platform_get_resource(dev, IORESOURCE_MEM, 0);

/*获取到资源以后再进行映射*/

gpio_con = ioremap(r->start, r->end - r->start + 1);

gpio_dat = gpio_con + 1;

r = platform_get_resource(dev, IORESOURCE_IRQ, 0);

pin = r->start;

/* 注册驱动向内核里面注册我们的字符设备驱动 */

major = register_chrdev(0, "led", &led_fops);

/* sysfs ==> 挂接到/sys */

cls = class_create(THIS_MODULE, "led_class");

class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led");

// mdev会根据/sys下的这些内容创建/dev/led

return 0;

}

int led_remove(struct platform_device *dev)

{

unregister_chrdev(major, "led");

class_device_destroy(cls, MKDEV(major, 0));

class_destroy(cls);

iounmap(gpio_con);

return 0;

}

/*1。构建平台驱动结构体,不知道的时候可以看别人的*/

static struct platform_driver led_driver = {

.probe = led_probe, /* 平台总线下增加一个平台设备时,调用枚举函数 */

.remove = led_remove, /* 平台总线下去掉一个平台设备时,调用remove函数 */

.driver = {

.name = "myled", /* 能支持名为"myled"的平台设备 */

.owner = THIS_MODULE,

},

};

/*2。注册,把我们的驱动加入到平台设备驱动连表中去*/

static int led_drv_init(void)

{

platform_driver_register(&led_driver);

return 0;

}

static int led_drv_exit(void)

{

platform_driver_unregister(&led_driver);

return 0;

}

module_init(led_drv_init);

module_exit(led_drv_exit);

MODULE_LICENSE("GPL");

二、设备类型的作用 这个典型的例子说明,字符设备相关驱动函数是基本功能性的:read write ioctl

bus设备相关驱动是扩展功能的,比如系统挂起,恢复,以及后面会说到的可维护性。

总线设备驱动模型和sysfs是一起配合使用的

类型

所包含的内容

对应内核数据结构

对应/sys项

设备(Devices)

设备是此模型中最基本的类型,以设备本身的连接按层次组织

struct device

/sys/devices/*/*/.../

设备驱动(Device Drivers)

在一个系统中安装多个相同设备,只需要一份驱动程序的支持

struct device_driver

/sys/bus/pci/drivers/*/

总线类型(Bus Types)

在整个总线级别对此总线上连接的所有设备进行管理

struct bus_type

/sys/bus/*/

设备类别(Device Classes)

这是按照功能进行分类组织的设备层次树;如 USB 接口和 PS/2 接口的鼠标都是输入设备,都会出现在 /sys/class/input/ 下

struct class

/sys/class/*/

0818b9ca8b590ca3270a3433284dd417.png

引用IBM网站的一段话:

在 Linux 2.5 内核的开发过程中,人们设计了一套新的设备模型,目的是为了对计算机上的所有设备进行统一地表示和操作,包括设备本身和设备之间的连接关系。这个模型是在分析了 PCI 和 USB 的总线驱动过程中得到的,这两个总线类型能代表当前系统中的大多数设备类型,它们都有完善的热挺拔机制和电源管理的支持,也都有级连机制的支持,以桥接的 PCI/USB 总线控制器的方式可以支持更多的 PCI/USB 设备。为了给所有设备添加统一的电源管理的支持,而不是让每个设备中去独立实现电源管理的支持,人们考虑的是如何尽可能地重用代码;而且在有层次模型的 PCI/USB 总线中,必须以合理形式展示出这个层次关系,这也是电源管理等所要求的必须有层次结构。

如在一个典型的 PC 系统中,中央处理器(CPU)能直接控制的是 PCI 总线设备,而 USB 总线设备是以一个 PCI 设备(PCI-USB桥)的形式接入在 PCI 总线设备上,外部 USB 设备再接入在 USB 总线设备上;当计算机执行挂起(suspend)操作时, Linux 内核应该以 “外部USB设备->USB总线设备->PCI总线设备” 的顺序通知每一个设备将电源挂起;执行恢复(resume)时则以相反的顺序通知;反之如果不按此顺序则将有设备得不到正确的电源状态变迁的通知,将无法正常工作。电源管理,正是我写这篇文章的目的之一。在系统进入待机状态时,只有实现了BUS驱动的设备驱动,才能被POWER模块识别,并调用PM的SUSPEND函数和RESUME函数。当然,sysfs也是一个复杂的系统,后面再分析它的好处。如果不联系sysfs,bus驱动能够提供的也就只有电源管理和热拔插相关机制了(对照bus_type的定义)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值