linux驱动学习笔记(3)——设备驱动模型

 (一)Kobject & Kset

Sysfs文件系统:

"sysfs is a ram-based filesystem initially based on ramfs. It provides a means to export kernel data structures, their attributes, and the linkages between them to userspace.”
--- documentation/filesystems/sysfs.txt
    Linux2.6内核引入了 sysfs 文件系统。sysfs 被看成是与 proc同类别的文件系统。sysfs 把连接在系统上的设备和总线组织成分级的文件,使其从用户空间可以访问到。
Sysfs 被加载在 /sys/ 目录下,它的子目录包括:

    • Block:在系统中发现的每个块设备在该目录下对应一个子目录。每个子目录中又包含一些属性文件,它们描述了这个块设备的各方面属性,如:设备大小。(loop块设备是使用文件来模拟的)
    • Bus:在内核中注册的每条总线在该目录下对应一个子目录, 如:ide pci scsi usb pcmcia  其中每个总线目录内又包含两个子目录:devices 和 drivers , devices目录包含了在整个系统中发现的属于该总线类型的设备,drivers目录包含了注册到该总线的所有驱动。
    • Class:将设备按照功能进行的分类,如/sys/class/net目录下包含了所有网络接口
    • Devices:包含系统所有的设备。
    • Kernel:内核中的配置参数
    • Module:系统中所有模块的信息
    • Firmware:系统中的固件
    • Fs: 描述系统中的文件系统
    • Power:系统中电源选项

Kobject

Kobject 实现了基本的面向对象管理机制,是构成Linux2.6设备模型的核心结构。它与sysfs文件系统紧密相连,在内核中注册的每个kobject对象对应sysfs文件系统中的一个目录。

类似于C++中的基类,Kobject常被嵌入于其他类型(即:容器)中。如bus,devices, drivers 都是典型的容器。这些容器通过kobject连接起来,形成了一个树状结构。
struct kobject {
  const char *name;
  struct list_head entry;
  struct kobject *parent; //指向父对象
  struct kset *kset;
  struct kobj_type *ktype;
  struct sysfs_dirent *sd;
  struct kref kref; //对象引用计数
  unsigned int state_initialized:1;
  unsigned int state_in_sysfs:1;
  unsigned int state_add_uevent_sent:1;
  unsigned int state_remove_uevent_sent:1;

};

Kobject操作:

void kobject_init(struct kobject * kobj) 
初始化kobject结构

int kobject_add(struct kobject * kobj)
将kobject对象注册到Linux系统

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,struct kobject *parent, const char *fmt, ...)
初始化kobject,并将其注册到linux系统

void kobject_del(struct kobject * kobj)
从Linux系统中删除kobject对象

struct kobject *kobject_get(struct kobject *kobj)
将kobject对象的引用计数加1,同时返回该对象指针。

void kobject_put(struct kobject * kobj)
将kobject对象的引用计数减1,如果引用计数降为0,则调用release方法释放该kobject对象。

Struct kobj_type:

Kobject的ktype成员是一个指向kobj_type结构的指针,该结构中记录了kobject对象的一些属性。

struct kobj_type {
  void (*release)(struct kobject *kobj);
  struct sysfs_ops *sysfs_ops;
  struct attribute **default_attrs;
};

release

  用于释放kobject占用的资源,当kobject的引用计数为0时被调用。

Struct attribute:

struct attribute {
  char * name; /*属性文件名*/
  struct module * owner;
  mode_t mode; /*属性的保护位*/
};
struct attribute (属性):

  对应于kobject的目录下的一个文件,Name成员就是文件名

Struct sysfs_ops:

struct sysfs_ops
{
  ssize_t (*show)(struct kobject *, struct attribute *,char *);
  ssize_t (*store)(struct kobject *,struct attribute *,const char *,size_t);
};

    • Show:当用户读属性文件时,该函数被调用,该函数将属性值存入buffer中返回给用户态;
    • Store:当用户写属性文件时,该函数被调用,用于存储用户传入的属性值。

实例分析:

Kobject.c

Kset

kset是具有相同类型的kobject的集合,在sysfs中体现成一个目录,在内核中用kset数据结构表示,定义为:
struct kset {
  struct list_head list; //连接该kset中所有kobject的链表头
  spinlock_t list_lock;
  struct kobject kobj; //内嵌的kobject
  struct kset_uevent_ops *uevent_ops; //处理热插拔事件的操作集合
}

Kset操作:

int kset_register(struct kset *kset) 
在内核中注册一个kset
void kset_unregister(struct kset *kset)
从内核中注销一个kset

热插拔事件:

在Linux系统中,当系统配置发生变化时,如:添加kset到系统;移动kobject, 一个通知会从内核空间发送到用户空间,这就是热插拔事件。热插拔事件会导致用户空间中相应的处理程序(如udev,mdev)被调用, 这些处理程序会通过加载驱动程序, 创建设备节点等来响应热插拔事件。

操作集合:

Struct kset_uevent_ops {
  int (*filter)(struct kset *kset, struct kobject *kobj);
  const char *(*name)(struct kset *kset, struct kobject *kobj);
  int (*uevent)(struct kset *kset, struct kobject *kobj,
  struct kobj_uevent_env *env);
}
kset_uevent_ops:

这三个函数什么时候调用?

    • 当该kset所管理的kobject和kset状态发生变化时(如被加入,移动),这三个函数将被调用。(例:kobject_uevent调用)

这三个函数的功能是什么?

    • filter:决定是否将事件传递到用户空间。如果 filter 返回 0,将不传递事件。(例: uevent_filter)
    • name:用于将字符串传递给用户空间的热插拔处理程序。
    • uevent:将用户空间需要的参数添加到环境变量中。(例:dev_uevent)

Kset.c
 

(二)设备模型

随着技术的不断进步,系统的拓扑结构也越来越复杂,对智能电源管理、热插拔的支持要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,
Linux 2.6内核提供了全新的内核设备模型。
设备模型元素

  • 总线
  • 驱动
  • 设备

总线

总线是处理器和设备之间的通道,在设备模型中, 所有的设备都通过总线相连, 甚至是内部的虚拟“platform”总线。 在 Linux 设备模型中, 总线由 bus_type 结构表示, 定义在 <linux/device.h>

总线描述

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;

}

总线注册/删除

总线的注册使用:
bus_register(struct bus_type * bus)
若成功,新的总线将被添加进系统,并可在sysfs 的 /sys/bus 下看到。
总线的删除使用:
void bus_unregister(struct bus_type *bus)

总线方法

int (*match)(struct device * dev, struct device_driver * drv)
当一个新设备或者驱动被添加到这个总线时,该方法被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零值

int (*uevent)(struct device *dev, char **envp, int num_envp,char *buffer, int buffer_size)
在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量。

总线属性

总线属性由结构bus_attribute 描述,定义如下:
struct bus_attribute {

struct attribute attr;
ssize_t (*show)(struct bus_type *, char * buf);
ssize_t (*store)(struct bus_type *, const char *
buf, size_t count);

}

创建属性
int bus_create_file(struct bus_type *bus,struct bus_attribute *attr)
删除属性
void bus_remove_file(struct bus_type*bus, struct bus_attribute *attr)

设备

设备描述

Linux 系统中的每个设备由一个 struct device 描述:
struct device {

…… …… …… …… …… ……
struct kobject kobj;
char bus_id[BUS_ID_SIZE]; /*在总线上唯一标识该设备的字符串 */
struct bus_type *bus; /* 设备所在总线 */
struct device_driver *driver; /*管理该设备的驱动*/
void *driver_data; /*该设备驱动使用的私有数据成员 *
struct klist_node knode_class;
struct class *class;
struct attribute_group **groups;
void (*release)(struct device *dev);

}

设备注册

int device_register(struct device *dev)
注册设备
void device_unregister(struct device *dev)
注销设备

**一条总线也是个设备,也必须按设备注册**

设备属性

设备属性由struct device_attribute 描述:
struct device_attribute
{

struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute
*attr,char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);

}
int device_create_file(struct device*device, struct device_attribute * entry)
创建属性
void device_remove_file(struct device *dev, struct device_attribute * attr)
删除属性

驱动

驱动描述

驱动程序由struct device_driver 描述 :
struct device_driver {

const char *name; /*驱动程序的名字( 体现在 sysfs 中 )*/
struct bus_type *bus; /*驱动程序所在的总线*/
struct module *owner;
const char *mod_name;
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 (*resume) (struct device *dev);
struct attribute_group **groups;
struct dev_pm_ops *pm;
struct driver_private *p;

}

驱动注册/注销

int driver_register(struct device_driver *drv)
注册驱动
void driver_unregister(struct device_driver *drv)
注销驱动

驱动属性

驱动的属性使用struct driver_attribute 来描述:
struct driver_attribute {

struct attribute attr;
ssize_t (*show)(struct device_driver *drv,
char *buf);
ssize_t (*store)(struct device_driver *drv,
const char *buf, size_t count);

}

int driver_create_file(struct device_driver * drv,struct driver_attribute * attr)
创建属性
void driver_remove_file(struct device_driver * drv,struct driver_attribute * attr)
删除属性

Platform总线

Platform总线是linux2.6内核加入的一种虚拟总线。platform机制的本身使用并不复杂,由两部分组成:
platform_deviceplatform_driver

Platform 驱动与传统的设备驱动模型相比,优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性。

platform_device

平台设备描述

平台设备使用Struct Platform_device来描述:
struct platform_device {

const char *name; /*设备名*/
int id; /*设备编号,配合设备名使用*/
struct device dev;
u32 num_resources;
struct resource *resource; /*设备资源*/

}
Struct Platform_device的分配使用:
struct platform_device *platform_device_alloc(const char *name, int id)
参数:

      • name: 设备名
      • id: 设备id,一般为-1

平台设备注册

注册平台设备,使用函数:
int platform_device_add(struct platform_device *pdev)

设备资源

平台设备资源使用struct resource来描述:
struct resource {

resource_size_t start; //资源的起始物理地址
resource_size_t end; //资源的结束物理地址
const char *name; //资源的名称
unsigned long flags; //资源的类型,比如MEM,IO,IRQ类型
struct resource *parent, *sibling, *child; //资源链表指针

}
设备资源-例
static struct resource s3c_wdt_resource1 = {

.start = 0x44100000,
.end = 0x44200000,
.flags = IORESOURCE_MEM,

}
static struct resource s3c_wdt_resource2 = {

.start = 20,
.end = 20,
.flags = IORESOURCE_IRQ,

}

获取资源

struct resource *platform_get_resource(struct platform_device*dev, unsigned int type, unsigned int num)
参数:

    • dev: 资源所属的设备
    • type: 获取的资源类型
    • num: 获取的资源数

例:
platform_get_resource(pdev, IORESOURCE_IRQ, 0)获取中断号

platform_driver

平台驱动描述

平台驱动使用struct platform_driver 描述:
struct platform_driver {

int (*probe)(struct platform_device *);
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;

}

平台驱动注册

平台驱动注册使用函数:
int platform_driver_register(struct platform_driver *)

版权说明:

      内容由网上找到的一套国嵌的培训视频整理而来

发布了164 篇原创文章 · 获赞 229 · 访问量 62万+

猜你喜欢

转载自blog.csdn.net/li_wen01/article/details/103899354
今日推荐