Hello, I’m Lao Wu.

Does every progressive person feel that they can work harder?

At the end of the day, as long as you don’t achieve your goal, you can always attribute the failure to “not trying a little harder”.

But the biggest misconception about effort is that the longer it takes, the more painful it is, the harder I try.

Think about it. Is there a more reasonable way to try?

Here is the text:

I. What is device Model? 3. How are bus, Device and driver related? Bus, Device, driver the simplest example five, summary six, related referenceCopy the code

I. What is device Model?

Linux’s Device Model is a model designed to centrally manage all device drivers.

It is like a building on a grand scale:

With KObject, Kset, Attribute and other basic building materials,

Three major components, bus, Device and driver, are constructed to support the driving world.

Finally, sysFS is used to establish a hierarchical relationship between the various basic building materials and provide a file interface for the outside world to interact with the facilities in the building.

Click for a larger version

What does the Device Model do?

The hardware description of device can be separated from the driver to improve the code reuse rate of the driver.

You can classify devices;

Device and driver can be traversed;

The topology of devices can be better displayed.

Devices can be accessed through SYSFS;

The device can support hot swap;

.

In order to control the length, this paper focuses on bus, device and driver, which are closely related to driver engineers.

2. Three core concepts of device Model

There are three core concepts in the Device Model:

  • bus

  • device

  • driver

What is a bus?

Bus stands for a bus, such as I2C, SPI, and USB.

Bus is the core framework of the Linux device driver model architecture, around which devices and drivers in the system are attached.

After the system is started, you can view the current buses in the system by running /sys/bus.

Bus is described by struct bus_type:

struct bus_type { const char *name; const char *dev_name; struct device *dev_root; const struct attribute_group **bus_groups; const struct attribute_group **dev_groups; const struct attribute_group **drv_groups; 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); . struct subsys_private *p; struct lock_class_key lock_key; };Copy the code

You don’t need to understand the role of each member all at once; you can explain it when you need it.

Focus on members:

  • Int (*match)(struct device *dev, struct device_driver * DRV);

  • Int (*probe)(struct device *dev), if the bus has the ability to probe the device, it will provide this callback function;

  • Struct subsys_private *p; struct subsys_private *p;

Register bus API:

int bus_register(struct bus_type *bus);

Copy the code

What is device?

Device represents a device.

Described by struct device:

struct device { struct device *parent; struct device_private *p; struct kobject kobj; const char *init_name; const struct device_type *type; struct mutex mutex; struct bus_type *bus; struct device_driver *driver; void *platform_data; void *driver_data; . }Copy the code

Focus on members:

  • Struct kObject kobj;

  • Struct bus_type *bus, bus of the device;

  • Struct device_driver *driver, NULL if not already bound;

Register device API:

int device_register(struct device *dev)

Copy the code

What is a driver?

Driver stands for device driver.

Described by struct device_driver:

struct device_driver {
 const char *name;
 struct bus_type *bus;

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

 bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
 enum probe_type probe_type;

 const struct of_device_id *of_match_table;
 const struct acpi_device_id *acpi_match_table;

 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);
 const struct attribute_group **groups;

 const struct dev_pm_ops *pm;

 struct driver_private *p;
};

Copy the code

Focus on members:

  • struct bus_type *bus;

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

It is worth mentioning that a bus controller is also a device.

For example, the I2C bus controller drives the I2C controller driver.

For devices connected to the I2C bus, the corresponding driver is the I2C device driver.

Register the driver API:

int driver_register(struct device_driver *drv);

Copy the code

3. How are bus, Device and driver related?

The core job of the Device Model is to maintain these three abstract instances and establish relationships between them.

How does bus manage device and driver?

In struct bus_type, there is a struct subsys_private *p pointer, which is responsible for managing all devices and drivers mounted on the bus. It is defined as follows:

struct subsys_private {
 struct kset subsys;
 struct kset *devices_kset;
 struct list_head interfaces;
 struct mutex mutex;

 struct kset *drivers_kset;
 struct klist klist_devices;
 struct klist klist_drivers;
 struct blocking_notifier_head bus_notifier;
 unsigned int drivers_autoprobe:1;
 struct bus_type *bus;

 struct kset glue_dirs;
 struct class *class;
};


Copy the code

Click for a larger version

The two KList members link together all the drivers and devices on the bus in a linked list.

Struct kset *drivers_kset and struct kset *devices_kset are dynamically generated ksets containing all drivers and devices on the bus when registering the current new bus with the system.

In the kernel, kObject is used to represent an object, and Kset is the abbreviation of KObject Set, which is the kernel object collection.

The kernel uses data structures such as KObject and Kset as raw materials to construct the framework of device Model in an object-oriented way.

Finally, the bus members of device and Device_driver also point to the bus:

Binding of device and driver


Whether registering a device on the bus via device_Register (),

Driver_register () registers a Device_driver to the bus.

Both cause the bus to attempt to bind device and driver.

1. Binding triggered by device_register()

When registering a device:

int device_register(struct device *dev);
 device_add(dev);
  bus_probe_device(dev);
   __device_attach(dev, true);

Copy the code

__device_attach(dev, true) will traverse all bus drivers for device:

bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
 driver_match_device(drv, dev);
  drv->bus->match ? drv->bus->match(dev, drv) : 1;
 driver_probe_device(drv, dev);

Copy the code

Driver_match_device () uses the bus match function to determine whether the device matches the driver.

Of_match_table or ID_table is generally used as the measuring standard to judge whether a match is made.

Take the i2C bus match function as an example:

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
 struct i2c_client *client = i2c_verify_client(dev);
 struct i2c_driver *driver;


 /* Attempt an OF style match */
 if (i2c_of_match_device(drv->of_match_table, client))
  return 1;

 /* Then ACPI style match */
 if (acpi_driver_match_device(dev, drv))
  return 1;

 driver = to_i2c_driver(drv);

 /* Finally an I2C match */
 if (i2c_match_id(driver->id_table, client))
  return 1;

 return 0;
}

Copy the code

Once match is successful, driver_probe_device() is called to trigger the behavior of the probe device:

int driver_probe_device(struct device_driver *drv, struct device *dev);
 really_probe(dev, drv);
  if (dev->bus->probe) {
   ret = dev->bus->probe(dev);
  } else if (drv->probe) {
   ret = drv->probe(dev);
  }

Copy the code

If bus has the ability to detect devices, such as PCI bus, bus->probe() will be used.

Otherwise, use driver->probe() to probe the device. The driver’s probe operation is linked to a specific hardware device.

2. Bindings triggered by Driver_register ()

int driver_register(struct device_driver *drv);
 bus_add_driver(drv);
  driver_attach(drv);

Copy the code

Driver_attach (DRV) loops through all devices on the bus for the driver:

int driver_attach(struct device_driver *drv);
 bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
  __driver_attach();
   driver_match_device(drv, dev);
   driver_probe_device(drv, dev);

Copy the code

As with device_register(), driver_match_device(DRV, dev) is eventually called,

Then the match function in bus is used to determine whether the device and driver match.

Similarly, once match is successful, driver_Probe_device () is called to trigger the behavior of the probe device, and the subsequent actions are exactly the same as when the device was registered.

3. Binding relationship between Device and Drvier

Now that we’ve seen how the binding is triggered, let’s see how the binding works.

For the device and driver that can be matched successfully, the relationship between the two is N to 1, that is, multiple devices can be bound to one driver.

Click for a larger version

The device:

Its driver member points to the bound Device_driver.

int driver_probe_device(struct device_driver *drv, struct device *dev)
 really_probe(dev, drv);
  dev->driver = drv;

Copy the code

The driver:

The linked list klist_Devices in Device_driver holds all devices bound to the driver.

int driver_probe_device(struct device_driver *drv, struct device *dev)
 really_probe(dev, drv);
  driver_bound(dev);
   klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

Copy the code

In /driver/base/driver.c, apis are provided for traversing all devices bound to the driver:

  • int driver_for_each_device()

  • struct device *driver_find_device()

The simplest examples of bus, device, and driver

So here’s an example,

A bus instance named “simple_bus” is constructed.

Simple_bus. c: Registers a bus named “sb” and provides apis for registering devices and drivers.

static int sb_match(struct device *dev, struct device_driver *driver) { return ! strncmp(dev_name(dev), driver->name, strlen(driver->name)); } struct bus_type sb_bus_type = { .name = "sb", .match = sb_match, }; static ssize_t version_show(struct bus_type *bus, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", Version); } static BUS_ATTR_RO(version); static void sb_dev_release(struct device *dev) { } int register_sb_device(struct sb_device *sbdev) { sbdev->dev.bus = &sb_bus_type; sbdev->dev.release = sb_dev_release; dev_set_name(&sbdev->dev, sbdev->name); return device_register(&sbdev->dev); } EXPORT_SYMBOL(register_sb_device); void unregister_sb_device(struct sb_device *sbdev) { device_unregister(&sbdev->dev); } EXPORT_SYMBOL(unregister_sb_device); static int sb_drv_probe(struct device *dev) { printk(KERN_INFO"sb_drv probe %s\n", dev_name(dev)); return 0; } int register_sb_driver(struct sb_driver *sdrv) { sdrv->driver.bus = &sb_bus_type; sdrv->driver.probe = &sb_drv_probe; return driver_register(&sdrv->driver); } EXPORT_SYMBOL(register_sb_driver); void unregister_sb_driver(struct sb_driver *driver) { driver_unregister(&driver->driver); } EXPORT_SYMBOL(unregister_sb_driver); static int __init sb_bus_init(void) { int ret; ret = bus_register(&sb_bus_type); if (ret) { printk(KERN_ERR "Unable to register sb bus, failure was %d\n",ret); return ret; } if (bus_create_file(&sb_bus_type, &bus_attr_version)) printk(KERN_ERR "Unable to create version attribute\n"); return 0; } static void sb_bus_exit(void) { bus_unregister(&sb_bus_type); } module_init(sb_bus_init); module_exit(sb_bus_exit);Copy the code

Xxx_chip. c: Register 4 devices named “chipX”

struct xxx_chip {
 char devname[20];
 struct sb_device sdev;
};

int chipdev_num = 4;
struct xxx_chip *chipdev;

static void chip_register_dev(struct xxx_chip *dev, int index)
{
 snprintf(dev->devname, sizeof(dev->devname), "chip%d", index);
 dev->sdev.name = dev->devname;
 dev_set_drvdata(&dev->sdev.dev, dev);
 register_sb_device(&dev->sdev);
}

int chip_init(void)
{
    int i;

    chipdev = kmalloc(chipdev_num*sizeof (struct xxx_chip), GFP_KERNEL);

    memset(chipdev, 0, chipdev_num*sizeof (struct xxx_chip));
    for (i = 0; i < chipdev_num; i++) {
  chip_register_dev(chipdev + i, i);
 }

    return 0;
}

void chip_cleanup(void)
{
    int i;
    for (i = 0; i < chipdev_num; i++) {
  unregister_sb_device(&chipdev[i].sdev);
 }
    kfree(chipdev);
}

module_init(chip_init);
module_exit(chip_cleanup);

Copy the code

Xxx_chip_drv. c: Registers a driver named “chip”

static struct sb_driver sculld_driver = {
 .driver = {
  .name = "chip",
 },
};

int xxx_chipdrv_init(void)
{
    return register_sb_driver(&sculld_driver);
}

void xxx_chipdrv_cleanup(void)
{
    unregister_sb_driver(&sculld_driver);
}

module_init(xxx_chipdrv_init);
module_exit(xxx_chipdrv_cleanup);

Copy the code

Operation effect:

root@buildroot:~# insmod Simple_bus. ko root@buildroot:~# Tree/SYS /bus/sb/SYS /bus/ SB ├── ─ Drivers ├─ Drivers_autoprobe ├── UEvent ├─ version root@buildroot:~# insmod xxx_chip-ko root@buildroot:~# tree / sys/bus/sb/sys/bus/sb ├ ─ ─ devices │ ├ ─ ─ chip0 - >.. /.. /.. / Devices/Chip0 │ ├─ Chip1 ->.. /.. /.. / Devices/Chip1 │ ├─ Chip2 ->.. /.. /.. / Devices / ├ ─ chimp ->.. /.. /.. / Devices/Chip Bass Exercises ── Heavy Bass Exercises ── Heavy Bass Exercises ─ version root@buildroot:~# xxx_chip_drv.ko sb_drv probe chip0 sb_drv probe chip1 sb_drv probe chip2 sb_drv probe chip3 root@buildroot:~# tree / sys/bus/sb/sys/bus/sb ├ ─ ─ devices │ ├ ─ ─ chip0 - >.. /.. /.. / Devices/Chip0 │ ├─ Chip1 ->.. /.. /.. / Devices/Chip1 │ ├─ Chip2 ->.. /.. /.. / Devices / ├ ─ chimp ->.. /.. /.. / devices/chip3 ├ ─ ─ drivers │ └ ─ ─ chip │ ├ ─ ─ the bind │ ├ ─ ─ chip0 - >.. /.. /.. /.. / Devices/Chip0 │ ├─ Chip1 ->.. /.. /.. /.. / Devices/Chip1 │ ├─ Chip2 ->.. /.. /.. /.. / Devices/Chip2 │ ├─ Chip3 ->.. /.. /.. /.. / devices/chip3 │ ├ ─ ─ uevent │ └ ─ ─ unbind ├ ─ ─ drivers_autoprobe ├ ─ ─ drivers_probe ├ ─ ─ uevent └ ─ ─ versionCopy the code

It can be seen from the printed information that after bus determines whether the device and driver match, driver probe() function is executed, which is consistent with our previous analysis.

Five, the summary

Linux’s Device Model is a very complex system.

At a high level, it consists of buses, devices, and drivers.

In order to realize the correlation between these components, the kernel defines the basic low-level data structures such as KObject and Kset, and then shows the interconnection hierarchy between the components occurring in the kernel space to the user space through the SYSFS file system. The file system interface provides a simple way for user – space program to access kernel object attribute information.

For the sake of length, kojbect and SYSFS are not covered in this article.

If you’re interested, dig into the following:

  • How do kojbect and Kset, the underlying data structures of the Device Model, work?

  • How does the kernel use device Model to build i2C, SPI, USB and other driver frameworks?

  • How do the Device Model and SYSFS work together?

  • How to create a properties file in SYSFS to access device drivers?

  • What does class do in SYSFS?

Vi. Relevant references

Linux Device Drivers

  • Chapter 14. Linux device Model

In Depth Linux Device Driver Kernel Mechanism

  • Chapter 9 Linux device driver model

Linux Device Driver Development details

  • Chapter 5 Linux File system and device Files

  • Chapter 12 software architecture ideas for Linux device drivers

Linux/Documentation/driver-model

  • bus.txt

  • class.txt

  • device.txt

  • driver.txt

  • overview.txt

Think about technology, but also think about life

To learn technology, but also to learn how to live.

Books I’ve been reading recently:

Index Fund Investment Guide

The author, a bank screw, focuses on low valuation index fund investment, systematically explains all kinds of index funds, as well as the effective strategy of index fund investment.

Click for a larger version

What did you get?

  • Reviewed some basic knowledge about fund investment;

You and I each have an apple. If we exchange apples, we still have only one apple. But when you and I each have an idea, and we exchange ideas, we both have two ideas.

Feel the article is valuable to you, might as well read + share.

Recommended reading:

Album | Linux driver development

Album | a little C every day

Album | Linux system programming

This article uses the article synchronization assistant to synchronize