设备驱动模型:字符、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 <linux/fs.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/mach/irq.h>
#include <asm/arch/regs-gpio.h>
#include <linux/poll.h>
#include <linux/platform_device.h>

/* 参考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 <linux/fs.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/mach/irq.h>
#include <asm/arch/regs-gpio.h>
#include <linux/poll.h>
#include <linux/platform_device.h>

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<<pin);
 }
 
 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/*/

Linux设备驱动开发详解-Note(11)--- Linux 文件系统与设备文件系

引用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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值