The Linux device model

/sys和 /dev的疑问

1、/dev 下放的是设备文件,是由应用层mknod创建的文件。如果底层驱动对mknod的设备号有对应的驱动,如open等函数,那么应用层open “/dev/**”时,就会调用到底层的驱动。说白了,/dev下放的是内核和应用层交互的文件,让应用层去open,write,poll等。

2、/sys 是个文件系统,你写内核代码时,如果有调用kobj_init等函数,就会在/sys下的相应目录生成相应文件。 它的作用是将内核注册的设备、驱动、BUS连成一个树形结构。 另外,应用层也可以通过读写/sys下的文件和内核进行交互(ktype)。 说白了/sys就是一个树形结构,让你明白内核都有哪些驱动和设备已经bus,方便电源管理。

sysfs底层操作

1、kobject结构

一提到kobject很多人就不想看了,千篇一律。但是使用这个结构,我们可以建立设备驱动模型,所以必须明白。开发驱动程序对我来说,也就是建几个目录,创几个属性文件。内核的设备驱动架构已经打好了,调几个函数来用就可以了。在sysfs文件系统里,kobject对应目录,属性(attribute)对应文件。

struct kobject {
    const char      * k_name;
    char            name[KOBJ_NAME_LEN];
    struct kref     kref;
    struct list_head    entry;
    struct kobject      * parent;
    struct kset     * kset;
    struct kobj_type    * ktype;
    struct sysfs_dirent * sd;
    wait_queue_head_t   poll;
};

kobject内容包括目录的名字,parent上层目录,如果parent=NULL就在/sys下面创建目录。ktype可理解为这个目录的属性。

struct kobj_type {
    void (*release)(struct kobject *);
    struct sysfs_ops    * sysfs_ops;
    struct attribute    ** default_attrs;
};
struct sysfs_ops {
    ssize_t (*show)(struct kobject *, struct attribute *,char *);//读方法
    ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);//写方法
};
struct attribute {
    const char      * name;
    struct module       * owner;
    mode_t          mode;
};//属性文件的名字,模式,只读设为S_IRUGO,可写设为S_IWUSR

2、方法(operations)

想一想在文件系统上能干什么呢?无非是创建目录(kobject),创建文件(属性)!

    // kobject初始化函数
    void kobject_init(struct kobject * kobj);
    // 设置指定kobject的名称
    int kobject_set_name(struct kobject *kobj, const char *format, ...);
    // 将kobj 对象的引用计数加,同时返回该对象的指针
    struct kobject *kobject_get(struct kobject *kobj);
    // 将kobj对象的引用计数减,如果引用计数降为,则调用kobject release()释放该kobject对象
    void kobject_put(struct kobject * kobj);
    // 将kobj对象加入Linux设备层次。挂接该kobject对象到kset的list链中,增加父目录各级kobject的引// 用计数,在其parent指向的目录下创建文件节点,并启动该类型内核对象的hotplug函数
    int kobject_add(struct kobject * kobj);
    // kobject注册函数,调用kobject init()初始化kobj,再调用kobject_add()完成该内核对象的注册
    int kobject_register(struct kobject * kobj);
    // 从Linux设备层次(hierarchy)中删除kobj对象
    void kobject_del(struct kobject * kobj);
    // kobject注销函数. 与kobject register()相反,它首先调用kobject del从设备层次中删除该对象,再调// 用kobject put()减少该对象的引用计数,如果引用计数降为,则释放kobject对象
    void kobject_unregister(struct kobject * kobj);

设备模型上层容器

这里说的上层容器指的是总线类型(bus_type)、设备(device)和驱动(device_driver)。LDD3里面举过一个比方,把kobject当成一个基类,总线类型、设备、驱动和kobject都是继承关系,这些上层的容器自然具备了sysfs的操作方法!

总线类型一般不需要我们创建了,内核支持大多数总线类型(pci,usb,iic…),还包含了虚拟总线类型(platform_bus),所以这里就不涉及如何创建总线,如何建立总线的属性文件云云。

1. 设备 device

设备的操作可简单理解为1、注册设备;2、创建设备属性文件。

通常的注册和注销函数:

int device_register(struct device *dev);

void device_unregister(struct device *dev);

设备的注册和注销包含了对底层的sysfs的操作,诸如kobject_init,kobject_add…

创建属性

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

这些属性结构可在编译时建立, 使用这些宏:

DEVICE_ATTR(name, mode, show, store);

结果结构通过前缀 dev_attr_ 到给定名子上来命名. 属性文件的实际管理使用通常的函数对来处理:

int device_create_file(struct device *device, struct device_attribute *entry);

void device_remove_file(struct device *dev, struct device_attribute *attr);

2. 驱动 driver

驱动的操作可简单理解为1、注册驱动;2、创建驱动属性文件。

通常的注册和注销函数:

int driver_register(struct device_driver *drv);

void driver_unregister(struct device_driver *drv);

设备的注册和注销包含了对底层的sysfs的操作,诸如kobject_init,kobject_add…

创建属性

DRIVER_ATTR(name, mode, show, store);

int driver_create_file(struct device_driver *drv, struct driver_attribute *attr);

void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr);

摘一段驱动代码来讲讲

#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
#include <linux/input.h>
#include <linux/platform_device.h>

struct input_dev *vms_input_dev;        /* Representation of an input device */
static struct platform_device *vms_dev; /* Device structure */

                                        /* Sysfs method to input simulated
                                           coordinates to the virtual
                                           mouse driver */
static ssize_t
write_vms(struct device *dev,
          struct device_attribute *attr,
          const char *buffer, size_t count)
{
  int x,y;
  sscanf(buffer, "%d%d", &x, &y);
                                        /* Report relative coordinates via the
                                           event interface */
  input_report_rel(vms_input_dev, REL_X, x);
  input_report_rel(vms_input_dev, REL_Y, y);
  input_sync(vms_input_dev);
  return count;
}
/* Attach the sysfs write method */
DEVICE_ATTR(coordinates, 0644, NULL, write_vms);
/* Attribute Descriptor */
static struct attribute *vms_attrs[] = {
  &dev_attr_coordinates.attr,
  NULL
};
/* Attribute group */
static struct attribute_group vms_attr_group = {
  .attrs = vms_attrs,
};
/* Driver Initialization */
int __init
vms_init(void)
{
  /* Register a platform device */
  vms_dev = platform_device_register_simple("vms", -1, NULL, 0);
  if (IS_ERR(vms_dev)) {
    PTR_ERR(vms_dev);
    printk("vms_init: error\n");
  }
  /* Create a sysfs node to read simulated coordinates */
  sysfs_create_group(&vms_dev->dev.kobj, &vms_attr_group);
  /* Allocate an input device data structure */
  vms_input_dev = input_allocate_device();
  if (!vms_input_dev) {
    printk("Bad input_alloc_device()\n");
  }
  /* Announce that the virtual mouse will generate
     relative coordinates */
  set_bit(EV_REL, vms_input_dev->evbit);
  set_bit(REL_X, vms_input_dev->relbit);
  set_bit(REL_Y, vms_input_dev->relbit);

  /* Register with the input subsystem */
  input_register_device(vms_input_dev);
  printk("Virtual Mouse Driver Initialized.\n");
  return 0;
}
/* Driver Exit */
void
vms_cleanup(void)
{
  /* Unregister from the input subsystem */
  input_unregister_device(vms_input_dev);
  /* Cleanup sysfs node */
  sysfs_remove_group(&vms_dev->dev.kobj, &vms_attr_group);
  /* Unregister driver */
  platform_device_unregister(vms_dev);
  return;
}

module_init(vms_init);
module_exit(vms_cleanup);

这是从《Essential Linux Device Drivers》第七章输入设备摘的代码,实现从虚拟鼠标读取数据坐标上报的功能。

这里使用了总线platform_bus。

vms_dev = platform_device_register_simple(“vms”, -1, NULL, 0);注册设备到总线;

sysfs_create_group(&vms_dev->dev.kobj, &vms_attr_group);创建属性文件,在linux/sysfs.h中引用,类似于sysfs_create_file;

input_register_device(vms_input_dev);注册设备文件到input子系统,具体细节参考源码;

总的来说,就是干了两件事,创建目录对象和创建属性,然后加到设备模型中,建立一个资源管理树!

文章只是片面的,宏观的略谈驱动模型,经验不足,仅作参考!

参考资料

http://bbs.csdn.net/topics/380166008

http://blog.csdn.net/xiahouzuoxin/article/details/8943863

时间: 2024-10-13 15:38:26

The Linux device model的相关文章

Linux驱动知识:Linux Device Model

Kobjects #include <linux/kobject.h> //The include file containing definitions for kobjects, related structures, and functions. void kobject_init(struct kobject *kobj); int kobject_set_name(struct kobject *kobj, const char *format, ...); //Functions

linux device tree源代码解析--转

//Based on Linux v3.14 source code Linux设备树机制(Device Tree) 一.描述 ARM Device Tree起源于OpenFirmware (OF),在过去的Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx 中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备. resource.i2c_board_info.spi_board_info

Linux device tree 简要笔记

第一.DTS简介     在嵌入式设备上,可能有不同的主板---它们之间差异表现在主板资源不尽相同,比如I2C.SPI.GPIO等接口定义有差别,或者是Timer不同,等等.于是这就产生了BSP的一个说法.所谓BSP,即是是板级支持包,英文全名为:Board Support Package.是介于主板硬件和操纵系统之间的一层.每一个主板,都有自己对应的BSP文件.在kernel/arch/arm/mach-* 目录下,放置着不同主板的BSP文件,比如展讯的某一个项目的BSP文件为: 1 kern

linux device drivers - debugging之proc

在书籍"linux device drivers"的第四章,专门介绍驱动开发中的debugging技术. printk只是其中一种技术,这种技术要求printk打印消息,并且会写入到磁盘里的文件中,这会拖慢整个代码的执行速度. 还有其中的debugging技术,并且对代码执行速度的影响比prink小. 其中讲到了,我们可以让驱动程序在proc文件系统里面创建文件,并且将需要的debugging信息写入到这个文件里面. linux内核提供了相应的API,来和proc文件系统交互,例如创建

linux device driver3 读书笔记(一)

一:机制与策略(转) http://www.51hei.com/bbs/dpj-29441-1.html 机制mechanism,策略policy.如果你看过<linux device drivers>,里面给出了大概的介绍.机制提供了干什么(do what),策略提供如何做(how to do).驱动程序完成机制的功能,把策略的实现留给用户的应用程序. 通常在机制中,驱动程序要完成打开,关闭,读写,控制等功能.这些都是设备使用时最基本的操作.而策略中就要实现一些高级的数据处理或界面功能.通过

《Linux Device Drivers》第十四章 Linux 设备模型

简介 2.6内核的设备模型提供一个对系统结构的一般性抽象描述,用以支持多种不同的任务 电源管理和系统关机 与用户空间通信 热插拔设备 设备类型 对象生命周期 kobject.kset和子系统 kobject是组成设备模型的基本结构 对象的引用计数 sysfs表述 数据结构关联 热插拔事件处理 kobject基础知识 <linux/kobject.h> 嵌入的kobject 内核代码很少去创建一个单独的kobject对象,kobject用于控制对大型域相关对象的访问 kobject的初始化 首先

The Linux usage model for device tree data

Linux and the Device Tree Author: Grant Likely [email protected] 这篇文章介绍了Linux中使用Device Tree的方法.可以在http://devicetree.org/Device_Tree_Usage获取到Device Tree数据格式. Device Tree是一种描述硬件的语言,它可以让操作系统不硬编码硬件的信息. 结构上讲,Device Tree是树形结构,或者非循环的有名节点组成的图.每个节点包含一定数目的属性和键

《Linux Device Drivers》 第十七章 网络驱动程序——note

简介 网络接口是第三类标准Linux设备,本章将描述网络接口是如何与内核其余的部分交互的 网络接口必须使用特定的内核数据结构注册自身,以备与外界进行数据线包交换时调用 对网络接口的常用文件操作是没有意义的,因此在它们身上无法体现Unix的"一切都是文件"的思想 网络驱动程序异步自外部世界的数据包 网络设备向内核请求把外部获得的数据包发送给内核 Linux内核中的网络子系统被设计成完全与协议无关 在网络世界中使用术语"octet"指一组8个的数据位,它是能为网络设备和

《Linux Device Drivers》第十二章 PCI驱动程序——note

简介 本章给出一个高层总线架构的综述 讨论重点是用于访问Peripheral Component Interconnect(PCI,外围设备互联)外设的内核函数 PCI总线是内核中得到最好支持的总线 本章主要介绍PCI驱动程序如果寻找其硬件和获得对它的访问 本章也会介绍ISA总线 PCI接口 PCI是一组完整的规范,定义了计算机的各个不同部分之间应该如何交互 PCI规范涵盖了与计算机接口相关的大部分问题 PCI架构被设计为ISA标准的替代品,有三个主要目标 获得在计算机和外设之间传输数据时更好的