linux驱动编程--设备模型

   在前面学习了 kobject和 kset之后,就迫不及待的想开始“研究”设备模型了。经过这几天的学习,感觉受益匪浅。所以就将自己的理解整理了下来

  想要完成一个设备的驱动,就要涉及三部分: Bus, device, driver。当然这些“新”节点都是最终继承于kobject。

  一.Bus

  这里先整理一下BUS,总线负责在设备与驱动间建立连接,包括 I2C, PCI, 串口,platform等。其中platform是虚拟总线。

  1.1 结构体

  信息结构体是 bus_type.

  struct bus_type {

  const char *name; //the name of bus

  struct bus_attribute *bus_attrs;

  //attribute for bus, contain attribute file and some operate function.

  // this is a interface between kernel space and user space.

  struct device_attribute *dev_attrs; //attribute for device,

  struct driver_attribute *drv_attrs; //attribute for deriver

  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; //power manage

  struct bus_type_private *p;

  //private data for bus. In fact, it is core of this structure

  };

  在其中 bus_attrs, dev_attrs 和 drv_attrs记录了该总线的一些属性信息,而最重要的被用来构建该总线的逻辑结构的信息都记录在了bus_type_private中。对应这个总线私有数据结构体的解析如下。

  struct bus_type_private {

  struct kset subsys;

  //there are two points:

  //1).this is a set. It is contain some devices and derivers about this bus.

  //2). it's parent is @bus_kset, which is the root of all other bus.@bus_kset have many subset, this is just one of them.

  //

  struct kset *drivers_kset;

  //all drivers about this bus will belong to this set.

  struct kset *devices_kset;

  //all devices of this bus will belong to this set.

  struct klist klist_devices;

  struct klist klist_drivers;

  //they are two lists , for mount all corresponding nodes.

  struct blocking_notifier_head bus_notifier;

  unsigned int drivers_autoprobe:1;

  //is this bus automaticly run when a new devices arrvied.

  //sometime, we can see some attribute files in user space.(for example:@drivers_autoprobe).

  //it is interface that kernel leave user to modify this argument.

  struct bus_type *bus;

  //just a port for return this bus.

  };

  其中的klist_devices, klist_drivers 链表会用来挂载该总线的设备与驱动。当需要找东西的时候就会去俩面翻。而上面的两个kset 分别是它们所属的集合。不同的集合对应于不同的操作特性。这是一种很给力的组织结构。就拿这里来说,我们用kobject来组织了一个二维链表(或其他什么数据结构),每个kobject在这个链表中充当了一个节点。但又想让其中指定的一些kobject节点具有一些属性。kset相当于kobject的属性。它包含了进行事件通知需要的一些数据信息。每当kobject有需要时,就会去找到自己所属的kset,或者上级kobject的kset来用。

  1.2 重要函数分析

  对于总线的注册需要使用到如下函数,通过分析它的行为对于理解bus_type的逻辑结构是很有帮助。

  /**

  * bus_register - register a bus with the system.

  * @bus: bus.

  *

  * Once we have that, we registered the bus with the kobject

  * infrastructure, then register the children subsystems it has:

  * the devices and drivers that belong to the bus.

  */

  int bus_register(struct bus_type *bus)

  {

  int retval;

  struct bus_type_private *priv;

  //alloc a private data package for @bus. It is the core of this structure,

  //include device list, deriver list and so on.

  priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);

  if (!priv)

  return -ENOMEM;

  priv->bus = bus;

  bus->p = priv;

  BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

  // before we keep on, what we should to know is that this bus is one of members of the great building,

  //so it must be inherit form @kobject.

  //and @(priv->subsys.kobj) is it's kobject.

  retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);

  if (retval)

  goto out;

  priv->subsys.kobj.kset = bus_kset; //1).@bus_kset is the root of all buses. 2). this structure is the type of bus.

  priv->subsys.kobj.ktype = &bus_ktype; //corresponding operation function for bus

  priv->drivers_autoprobe = 1; //automaticly probe when new device arrived.

  retval = kset_register(&priv->subsys);

  if (retval)

  goto out;

  retval = bus_create_file(bus, &bus_attr_uevent); //create attribute file for bus,it is a interface between user space and kernel space.

  if (retval)

  goto bus_uevent_fail;

  //给该总线创建一个设备子集,it is the set of all devices about this bus.

  //在文件系统中的表现就是在该总线的目录下多了一个名字叫"devices"的子目录

  //还需要提醒的一点就是:设备模型中的层次结构关系都是由kobject对象来指定,所以凡是属于这个设备模型的节点必须要继承kobject.

  priv->devices_kset = kset_create_and_add("devices", NULL,

  &priv->subsys.kobj);

  if (!priv->devices_kset) {

  retval = -ENOMEM;

  goto bus_devices_fail;

  }

  //create a deriver set for this bus

  priv->drivers_kset = kset_create_and_add("drivers", NULL,

  &priv->subsys.kobj);

  if (!priv->drivers_kset) {

  retval = -ENOMEM;

  goto bus_drivers_fail;

  }

  //thoes two list is used to mount some nodes. device-node or deriver-node.

  klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);

  klist_init(&priv->klist_drivers, NULL, NULL);

  //create attribute file for this structure

  retval = add_probe_files(bus);

  if (retval)

  goto bus_probe_files_fail;

  retval = bus_add_attrs(bus); //create attribute file for @bus_attr

  if (retval)

  goto bus_attrs_fail;

  pr_debug("bus: '%s': registeredn", bus->name);

  return 0;

  bus_attrs_fail:

  remove_probe_files(bus);

  bus_probe_files_fail:

  kset_unregister(bus->p->drivers_kset);

  bus_drivers_fail:

  kset_unregister(bus->p->devices_kset);

  bus_devices_fail:

  bus_remove_file(bus, &bus_attr_uevent);

  bus_uevent_fail:

  kset_unregister(&bus->p->subsys);

  kfree(bus->p);

  out:

  bus->p = NULL;

  return retval;

  }

  在函数体中已经对行为进行了较详细的分析。

  1.3 device_bind_driver

  那么device到底又是怎么和一个driver进行绑定的呢?让它们在需要时能找到彼此

  //bind a driver to one device.

  int device_bind_driver(struct device *dev)

  {

  int ret;

  ret = driver_sysfs_add(dev);

  if (!ret)

  driver_bound(dev);

  return ret;

  }

  //这个函数是在设备已经绑定驱动之后使用,

  static void driver_bound(struct device *dev)

  {

  if (klist_node_attached(&dev->p->knode_driver)) {

  printk(KERN_WARNING "%s: device %s already boundn",

  __func__, kobject_name(&dev->kobj));

  return;

  }

  pr_debug("driver: '%s': %s: bound to device '%s'n", dev_name(dev),

  __func__, dev->driver->name);

  if (dev->bus)

  blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

  BUS_NOTIFY_BOUND_DRIVER, dev);

  //把设备节点加入驱动的节点链表中

  //?既然设备已经能找到自己的驱动了,为什么还需要加入驱动的链表

  //?一个驱动可能支持一组设备,对于那些逻辑操作相同的设备,所有驱动有一个设备链表

  klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

  }

  二. 设备信息

  2.1 信息结构体

  在总线的其中一端挂载的是设备。其用于记录数据和操作的结构体是device.

  struct device {

  struct device *parent; //parent device

  struct device_private *p; //private data for device, 包含了其拓扑结构信息

  struct kobject kobj;

  //@device is inherit from @kobject. so it is belong to @devices_kset, which is created by kernel for devices and contain of some operation functions for devices.

  //devices_kset

  const char *init_name; /* initial name of the device */

  struct device_type *type;

  struct semaphore sem; /* semaphore to synchronize calls to

  * its driver.

  */

  struct bus_type *bus; /* type of bus device is on */

  struct device_driver *driver; /* which driver has allocated this

  device */

  void *driver_data; /* data private to the driver */

  void *platform_data; /* Platform specific data, device

  core doesn't touch it */

  struct dev_pm_info power;

  #ifdef CONFIG_NUMA

  int numa_node; /* NUMA node this device is close to */

  #endif

  u64 *dma_mask; /* dma mask (if dma'able device) */

  u64 coherent_dma_mask;/* Like dma_mask, but for

  alloc_coherent mappings as

  not all hardware supports

  64 bit addresses for consistent

  allocations such descriptors. */

  struct device_dma_parameters *dma_parms;

  struct list_head dma_pools; /* dma pools (if dma'ble) */

  struct dma_coherent_mem *dma_mem; /* internal for coherent mem

  override */

  /* arch specific additions */

  struct dev_archdata archdata;

  dev_t devt; /* dev_t, creates the sysfs "dev" */

  spinlock_t devres_lock;

  struct list_head devres_head;

  struct klist_node knode_class;

  struct class *class;

  struct attribute_group **groups; /* optional groups */

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

  };

  而这个节点的拓扑结构信息则保存在 device_private 中

  struct device_private {

  struct klist klist_children;

  //klist containing all children of this device

  struct klist_node knode_parent;

  //node in sibling list

  struct klist_node knode_driver;

  //node in driver list, @device is mount on the list of driver by this member.

  //In linux system, one device have only one driver.

  //But it is not same as to driver, one driver may have a lot of devices, if only those device have a same logical operation.

  struct klist_node knode_bus;

  //apparently, this device is need to mount on the list of bus, too.

  struct device *device;

  //point to this device

  };

  2.2 重要函数

  设备的注册函数为 device_register( ), 其会建立设备的拓扑关系,并将设备添加到对应总线的拓扑列表中。

  int device_register(struct device *dev)

  {

  device_initialize(dev);

  return device_add(dev);

  }

  其中的device_initialize( )在对设备进行初始化。初始化中使用的一个非常重要的变量是devices_kset ,是内核在启动时就建立的专门用来装载设备的集合。与之一起初始化的还有另外两个

  void device_initialize(struct device *dev)

  {

  dev->kobj.kset = devices_kset;

  //@devices_kset is created by kernel as the parent of all devices.

  kobject_init(&dev->kobj, &device_ktype);

  //@device_ktype 为kobj定义了两个操作函数

  INIT_LIST_HEAD(&dev->dma_pools);

  init_MUTEX(&dev->sem);

  spin_lock_init(&dev->devres_lock);

  INIT_LIST_HEAD(&dev->devres_head);

  device_init_wakeup(dev, 0);

  device_pm_init(dev);

  set_dev_node(dev, -1);

  }

  那三个重要设备集合的故事是从这开工的

  int __init devices_init(void)

  {

  ......

  devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);

  dev_kobj = kobject_create_and_add("dev", NULL);

  sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);

  sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);

  ......

  }

  对于设备注册来说核心的操作在device_add()中完成,其简化版如下

  int device_add(struct device *dev)

  {

  ......

  kobject_uevent(&dev->kobj, KOBJ_ADD);

  //开始调用事件通知函数

  bus_attach_device(dev);

  //attach @device to bus

  if (parent)

  klist_add_tail(&dev->p->knode_parent,

  &parent->p->klist_children);

  ......

  }

  在这个函数中是bus_attach_device()完成了最后的操作,将设备挂进总线的链表中,在加粗的部分可以很清楚的看到。

  void bus_attach_device(struct device *dev)

  {

  struct bus_type *bus = dev->bus;

  int ret = 0;

  if (bus) {

  if (bus->p->drivers_autoprobe)

  ret = device_attach(dev);

  // if autoprobe = 1,it will bind to driver

  WARN_ON(ret < 0);

  //add @device to the devices-list of @bus

  if (ret >= 0)

  klist_add_tail(&dev->p->knode_bus,

  &bus->p->klist_devices);

  //@device will be add to the list of the bus.

  //@knode_bus is a mount point. think about @containerof()

  }

  }

  三. 驱动信息

  3.1 结构体

  记录驱动信息的结构体是device_driver,它就是device, bus, driver这个三角模型中的最后一个了。

  struct device_driver {

  const char *name;

  //it is used to match with the device.

  struct bus_type *bus;

  //corresponding bus for this driver

  struct module *owner;

  const char *mod_name; /* used for built-in modules */

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

  //this is a probe function, for ensure whether this driver is match with the device.

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

  //when this device is removed.

  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;

  //as the counterpart of @device and @bus, it is also contain of the hierarchy information.

  };

  当然,和两外两个一样,结构体中也制作了一个专门用来装该驱动节点拓扑信息的结构体,就是driver_private

  struct driver_private {

  struct kobject kobj;

  //of course, @driver is also a @kobject.

  //我们使用kobj为了将该节点增加到这个虚拟的层次结构中。

  struct klist klist_devices;

  //the list of devices. Thanks for device-model, we could use a driver serices for a serice of devices,

  // only if they have a similar operation.

  struct klist_node knode_bus;

  //@driver will be mounted on the list of driver of the bus.

  // Usually, there are two list for the bus.

  struct module_kobject *mkobj;

  struct device_driver *driver;

  //point to container.

  };

  3.2 重要函数

  下面来整理一下驱动注册函数 driver_register,因为它包含了这个结构体的最终解释权

  int driver_register(struct device_driver *drv)

  {

  ......

  //add @driver to the list of drivers of bus.

  ret = bus_add_driver(drv);

  ......

  }

  沿着核心部分继续向下找

  int bus_add_driver(struct device_driver *drv)

  {

  ......

  priv = kzalloc(sizeof(*priv), GFP_KERNEL);

  ......

  //该驱动对象属于对应总线的驱动集

  priv->kobj.kset = bus->p->drivers_kset;

  error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,

  "%s", drv->name);

  //假如开启了自动探测,则尝试加载设备

  if (drv->bus->p->drivers_autoprobe) {

  error = driver_attach(drv);

  if (error)

  goto out_unregister;

  }

  //add @driver to the list of drivers of the bus

  klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

  module_add_driver(drv->owner, drv);

  //create attribute file for driver

  error = driver_create_file(drv, &driver_attr_uevent);

  error = driver_add_attrs(bus, drv);

  error = add_bind_files(drv);

  //notify a event, this will notify the user space.

  kobject_uevent(&priv->kobj, KOBJ_ADD);

  return 0;

  ......

  }

  看到了两件事,一是函数将 driver挂载到了对应总线的链表中,就是这句话 "klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers) ”,现在我又一次看到了containerof的恶行。自从containerof之后内核经常做一些让我这种新手看着不习惯的链表出来。二是该函数尝试着自动探测对应设备,调用了driver_attach 。这里又出现了一个有趣的东西

  /**

  * driver_attach - try to bind driver to devices.

  * @drv: driver.

  *

  * Walk the list of devices that the bus has on it and try to

  * match the driver with each one. If driver_probe_device()

  * returns 0 and the @dev->driver is set, we've found a

  * compatible pair.

  */

  int driver_attach(struct device_driver *drv)

  {

  return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

  }

  driver_attach函数将会去遍历该总线的设备列表,并对每个遍历到的设备调用__driver_attach ,检查一下是否是自己要找的设备。

  static int __driver_attach(struct device *dev, void *data)

  {

  /*

  * Lock device and try to bind to it. We drop the error

  * here and always return 0, because we need to keep trying

  * to bind to devices and some drivers will return an error

  * simply if it didn't support the device.

  *

  * driver_probe_device() will spit a warning if there

  * is an error.

  */

  //假如这个设备不是自己要找的,则马上退出

  if (!driver_match_device(drv, dev))

  return 0;

  ......

  //并且这个设备还没有驱动,如果已经有了也是不行的

  if (!dev->driver)

  driver_probe_device(drv, dev);

  ......

  return 0;

  }

  接下来就是 driver_probe_device了,这里将会调用探针函数(总线的,或我们的)去检查这个设备是否确实是对应这个驱动的。

  int driver_probe_device(struct device_driver *drv, struct device *dev)

  {

  ......

  ret = really_probe(dev, drv);

  ......

  }

  static int really_probe(struct device *dev, struct device_driver *drv)

  {

  ......

  //假如总线没有定义探针函数,则调用用户填写的探测函数

  //只要探针函数返回0值,则表示选中。

  if (dev->bus->probe) {

  ret = dev->bus->probe(dev);

  if (ret)

  goto probe_failed;

  } else if (drv->probe) {

  ret = drv->probe(dev);

  if (ret)

  goto probe_failed;

  }

  ......

  //驱动绑定

  driver_bound(dev);

  }

  从上面已经看到了探针函数的使用过程,再来看最后一个部分。

  static void driver_bound(struct device *dev)

  {

  ......

  //把设备节点加入驱动的节点链表中

  //?既然设备已经能找到自己的驱动了,为什么还需要加入驱动的链表

  //?一个驱动可能支持一组设备,对于那些逻辑操作相同的设备,所有驱动有一个设备链表

  klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

  ......

  }

  这就是这个驱动的实际运作了。

  四. 总结

  这就是设备模型的三个基础部分。那么下一个问题就是,通过对这三个信息结构体填不同的参数就形成了多种多样的模型了吗?

时间: 2024-12-01 07:49:11

linux驱动编程--设备模型的相关文章

Linux设备模型(3)_Uevent【转】

转自:http://www.wowotech.net/device_model/uevent.html 1. Uevent的功能 Uevent是Kobject的一部分,用于在Kobject状态发生改变时,例如增加.移除等,通知用户空间程序.用户空间程序收到这样的事件后,会做相应的处理. 该机制通常是用来支持热拔插设备的,例如U盘插入后,USB相关的驱动软件会动态创建用于表示该U盘的device结构(相应的也包括其中的kobject),并告知用户空间程序,为该U盘动态的创建/dev/目录下的设备节

iic-linux驱动中设备驱动XXXdev.c和具体的设备驱动 有什么区别

问题描述 linux驱动中设备驱动XXXdev.c和具体的设备驱动 有什么区别 刚学习驱动一个月,了解了IIC驱动和SPI驱动的架构,发现他们大概构架都差不多,发现他们核心层上面的设备驱动都有一个xxxdev.c.比如说IIC驱动有一个I2C-dev.c.我想问一下这个I2c-dev.c和具体设备驱动 比如AT24.C(也是采用IIC) 有什么区别.i2c-dev.c到底是干嘛用的(适配器设备驱动?还是模拟驱动设备)

【Linux驱动】字符设备驱动

一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 1.字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等. 2.块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备

LDD3学习笔记(17):linux设备模型

  1.Kobjects结构 #include <linux/kobject.h> 包含文件, 包含 kobject 的定义, 相关结构, 和函数. void kobject_init(struct kobject *kobj); int kobject_set_name(struct kobject *kobj, const char *format, ...); 用作 kobject 初始化的函数 struct kobject *kobject_get(struct kobject *ko

Linux设备模型(9)_device resource management ---devm申请空间【转】

转自:http://www.wowotech.net/linux_kenrel/device_resource_management.html   1. 前言 蜗蜗建议,每一个Linux驱动工程师,都能瞄一眼本文. 之所以用"瞄",因此它很简单,几乎不需要花费心思就能理解.之所有这建议,是因为它非常实用,可以解答一些困惑,可以使我们的代码变得简单.简洁.先看一个例子: 1: /* drivers/media/platform/soc_camera/mx1_camera.c, line

Linux设备模型(热插拔、mdev 与 firmware)【转】

转自:http://www.cnblogs.com/hnrainll/archive/2011/06/10/2077469.html 转自:http://blog.chinaunix.net/space.php?uid=20543672&do=blog&cuid=460882 热插拔有 2 个不同角度来看待热插拔:   从内核角度看,热插拔是在硬件.内核和内核驱动之间的交互.   从用户角度看,热插拔是内核和用户空间之间,通过调用用户空间程序(如hotplug.udev 和 mdev)的交

linux驱动加载后能找到设备,但是/dev下不能找到设备文件

问题描述 linux驱动加载后能找到设备,但是/dev下不能找到设备文件 linux驱动加载后能找到设备,但是/dev下不能找到设备文件,我用的是动态分配设备号,insmod也能通过,但是/dev下就是找不到设备文件,加载后也不能通过测试程序我基本上直接用的板子例程,静态动态我都试了,就是不行,日志文件里面也什么都没有,板子是2410的,主机是红帽的,希望大神能够指点迷津/*****************************************Copyright (c)********

linux i2c设备驱动字符设备问题

问题描述 linux i2c设备驱动字符设备问题 在设备驱动中ioctl.read和write中使用struct i2c_client *client = file->private_data;都会出现问题,在板子上走程序就会出 现Unable to handle kernel NULL pointer dereference at virtual address 00000000,求解答为什么,还有file->private_data是什么数据,有什么用处 解决方案 http://blog.

linux驱动-Linux系统读取PCI设备基地址

问题描述 Linux系统读取PCI设备基地址 向大家请教一个问题,毕业急用!谢谢啦! 基于PMP-10D-X型PC/104定制了Linux系统(内核版本:2.6.37.6). 现在通过PCI接口连接一个FPGA. FPGA地址映射到内存上,Linux系统只需要对内存操作就相当于对FPGA操作. 那现在如何在Linux系统下获得PCI设备基地址? 我使用了lspci ?-v命令查出FPGA的信息为:Memory ?at ?e8000000 (32-bits, non-prefetchable)[s