技術(shù)員聯(lián)盟提供win764位系統(tǒng)下載,win10,win7,xp,裝機(jī)純凈版,64位旗艦版,綠色軟件,免費(fèi)軟件下載基地!

當(dāng)前位置:主頁 > 教程 > 服務(wù)器類 >

linux驅(qū)動(dòng)編程--設(shè)備模型

來源:技術(shù)員聯(lián)盟┆發(fā)布時(shí)間:2018-08-09 00:13┆點(diǎn)擊:

  在前面學(xué)習(xí)了 kobject和 kset之后,就迫不及待的想開始“研究”設(shè)備模型了。經(jīng)過這幾天的學(xué)習(xí),感覺受益匪淺。所以就將自己的理解整理了下來

  想要完成一個(gè)設(shè)備的驅(qū)動(dòng),就要涉及三部分: Bus, device, driver。當(dāng)然這些“新”節(jié)點(diǎn)都是最終繼承于kobject。

  一.Bus

  這里先整理一下BUS,總線負(fù)責(zé)在設(shè)備與驅(qū)動(dòng)間建立連接,包括 I2C, PCI, 串口,platform等。其中platform是虛擬總線。

  1.1 結(jié)構(gòu)體

  信息結(jié)構(gòu)體是 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記錄了該總線的一些屬性信息,而最重要的被用來構(gòu)建該總線的邏輯結(jié)構(gòu)的信息都記錄在了bus_type_private中。對應(yīng)這個(gè)總線私有數(shù)據(jù)結(jié)構(gòu)體的解析如下。

  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 鏈表會(huì)用來掛載該總線的設(shè)備與驅(qū)動(dòng)。當(dāng)需要找東西的時(shí)候就會(huì)去倆面翻。而上面的兩個(gè)kset 分別是它們所屬的集合。不同的集合對應(yīng)于不同的操作特性。這是一種很給力的組織結(jié)構(gòu)。就拿這里來說,我們用kobject來組織了一個(gè)二維鏈表(或其他什么數(shù)據(jù)結(jié)構(gòu)),每個(gè)kobject在這個(gè)鏈表中充當(dāng)了一個(gè)節(jié)點(diǎn)。但又想讓其中指定的一些kobject節(jié)點(diǎn)具有一些屬性。kset相當(dāng)于kobject的屬性。它包含了進(jìn)行事件通知需要的一些數(shù)據(jù)信息。每當(dāng)kobject有需要時(shí),就會(huì)去找到自己所屬的kset,或者上級kobject的kset來用。

  1.2 重要函數(shù)分析

  對于總線的注冊需要使用到如下函數(shù),通過分析它的行為對于理解bus_type的邏輯結(jié)構(gòu)是很有幫助。

  /**

  * 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;

  //給該總線創(chuàng)建一個(gè)設(shè)備子集,it is the set of all devices about this bus.

  //在文件系統(tǒng)中的表現(xiàn)就是在該總線的目錄下多了一個(gè)名字叫"devices"的子目錄

  //還需要提醒的一點(diǎn)就是:設(shè)備模型中的層次結(jié)構(gòu)關(guān)系都是由kobject對象來指定,所以凡是屬于這個(gè)設(shè)備模型的節(jié)點(diǎn)必須要繼承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;

  }

  在函數(shù)體中已經(jīng)對行為進(jìn)行了較詳細(xì)的分析。

  1.3 device_bind_driver

  那么device到底又是怎么和一個(gè)driver進(jìn)行綁定的呢?讓它們在需要時(shí)能找到彼此

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

  }

  //這個(gè)函數(shù)是在設(shè)備已經(jīng)綁定驅(qū)動(dòng)之后使用,

  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);

  //把設(shè)備節(jié)點(diǎn)加入驅(qū)動(dòng)的節(jié)點(diǎn)鏈表中

  //?既然設(shè)備已經(jīng)能找到自己的驅(qū)動(dòng)了,為什么還需要加入驅(qū)動(dòng)的鏈表

  //?一個(gè)驅(qū)動(dòng)可能支持一組設(shè)備,對于那些邏輯操作相同的設(shè)備,所有驅(qū)動(dòng)有一個(gè)設(shè)備鏈表

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

  }

  二. 設(shè)備信息

  2.1 信息結(jié)構(gòu)體

  在總線的其中一端掛載的是設(shè)備。其用于記錄數(shù)據(jù)和操作的結(jié)構(gòu)體是device.

  struct device {

  struct device *parent; //parent device

  struct device_private *p; //private data for device, 包含了其拓?fù)浣Y(jié)構(gòu)信息

  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);

  };

  而這個(gè)節(jié)點(diǎn)的拓?fù)浣Y(jié)構(gòu)信息則保存在 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 重要函數(shù)

  設(shè)備的注冊函數(shù)為 device_register( ), 其會(huì)建立設(shè)備的拓?fù)潢P(guān)系,并將設(shè)備添加到對應(yīng)總線的拓?fù)淞斜碇小?/p>

  int device_register(struct device *dev)

  {

  device_initialize(dev);

  return device_add(dev);

  }