Linux Device Model: Architecture Of Driver World

Hello, I am Lao Wu.

Does every ambitious person feel that they can work a little harder?

In the end, if the goal is not achieved, the reason for failure can always be attributed to “not trying a little harder”.

However, the biggest misconception about effort is: the longer the time and the more painful the process, the more effort I have put in.

Think about it, is there a more reasonable way to put in effort?

Below is the main content:

1. What is the device model?  
2. The 3 core concepts of the device model  
3. How are bus, device, and driver related?  
4. The simplest example of bus, device, and driver  
5. Summary  
6. Related references  

1. What is the device model?

The Linux device model is a model designed to unify the management of all device drivers.

It is like a grand building:

Using kobject, kset, attribute, etc. as basic building materials,

It constructs the three main components of the driver world: bus, device, and driver,

Finally, through sysfs, it establishes interconnection relationships among various basic building materials and provides an interface for interaction with the facilities inside the building.

Linux Device Model: Architecture Of Driver World

Click to view the larger image

What is the role of the device model?

It can separate the hardware description of the device from the driver, improving the code reusability of the driver;

It can classify devices;

It can traverse devices and drivers;

It can better present the topology of devices;

It can access devices through sysfs;

It can allow devices to support hot plugging;

To control the length, this article will focus on the three components most closely related to driver engineers: bus, device, and driver.

2. The 3 Core Concepts of the Device Model

There are 3 core concepts in the device model:

  • bus

  • device

  • driver

What is a bus?

A bus represents a type of bus, such as I2C, SPI, USB, etc.

The bus is the core framework of the Linux device driver model, and the devices and drivers in the system depend on it.

After starting the system, you can check which buses are currently in the system through /sys/bus.

A 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;  
};  

You do not need to understand the role of each member all at once; explanations will be given when needed.

Focus on the members:

  • int (*match)(struct device *dev, struct device_driver *drv), the callback function used to determine whether the device and driver attached to the bus match;

  • int (*probe)(struct device *dev), if the bus has the ability to detect devices, this callback function will be provided;

  • struct subsys_private *p, used to manage the data structures of devices and drivers on the bus;

API for registering a bus:

int bus_register(struct bus_type *bus);  

What is a device?

A device represents a certain device.

It is 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;  
 ...  
}  

Focus on the members:

  • struct kobject kobj, kernel object;

  • struct bus_type *bus, the bus where the device is located;

  • struct device_driver *driver, the driver bound to the device; if not bound yet, it is NULL;

API for registering a device:

int device_register(struct device *dev)  

What is a driver?

A driver represents a device driver.

It is 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;  
};  

Focus on the members:

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

It is worth mentioning that the bus controller is also a type of device.

For example, the I2C bus controller corresponds to the I2C controller driver.

And the devices on the I2C bus correspond to the I2C device driver.

API for registering a driver:

int driver_register(struct device_driver *drv);  

3. How Are Bus, Device, and Driver Related?

The core work of the device model is to maintain instances of these three abstractions and establish their relationships.

How does the bus manage devices and drivers?

In struct bus_type, there is a struct subsys_private *p pointer, which is responsible for managing all devices and drivers attached to the bus. Its definition is 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;  
};  

Linux Device Model: Architecture Of Driver World

Click to view the larger image

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

struct kset *drivers_kset and struct kset *devices_kset are dynamically generated ksets that accommodate all drivers and devices on the current bus when registering the new bus to the system.

In the kernel, kobject is used to represent an object, and kset is the abbreviation for kobject set, which is a collection of kernel objects.

The kernel uses data structures such as kobject and kset as raw materials to construct the framework of the device model in an object-oriented way.

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

Linux Device Model: Architecture Of Driver World

Binding of Device and Driver

Whether registering a device to the bus via device_register() or registering a device_driver to the bus via driver_register(), both will trigger the binding behavior between the 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);  

__device_attach(dev, true) will traverse all drivers on the bus for the 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);  

driver_match_device() uses the match function in the bus to determine whether the device and driver match. The matching criteria are generally measured by of_match_table or id_table.

Taking the match function of the i2c bus 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;  
}  

Once a match is successful, driver_probe_device() will be called to trigger the device detection behavior:

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

If the bus has the ability to detect devices, such as the PCI bus, it will use bus->probe() to detect the device; otherwise, it will use driver->probe() to detect the device, and the probe operation of the driver is linked to the specific hardware device.

2. Binding Triggered by driver_register()

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

driver_attach(drv) will traverse 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);  

Like device_register(), it ultimately calls driver_match_device(drv, dev), which in turn uses the match function in the bus to determine whether the device and driver match.

Similarly, once a match is successful, driver_probe_device() will be called to trigger the device detection behavior, and the subsequent operations are identical to those when registering the device.

3. The Binding Relationship Between Device and Driver

Having discussed how the binding is triggered, let’s clarify the specific operations of the binding.

For successfully matched devices and drivers, the relationship between them is N to 1, meaning multiple devices can be bound to one driver.

Linux Device Model: Architecture Of Driver World

Click to view the larger image

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

For the driver:

In the device_driver, the klist_devices linked list saves all devices that have been bound to this 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);  

In /driver/base/driver.c, some APIs are provided for traversing and processing all devices bound to the driver:

  • int driver_for_each_device()
  • struct device *driver_find_device()

4. The Simplest Example of Bus, Device, and Driver

In the following example,

We construct a bus instance named “simple_bus”.

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(&sbdrv->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);  

xxx_chip.c: Registers four 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);  

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

Running Effect:

root@buildroot:~# insmod simple_bus.ko  
root@buildroot:~# tree /sys/bus/sb  
/sys/bus/sb  
├── devices  
├── drivers  
├── drivers_autoprobe  
├── drivers_probe  
├── 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/chip2  
│   └── chip3 -> ../../../devices/chip3  
├── drivers  
├── drivers_autoprobe  
├── drivers_probe  
├── uevent  
└── version  

root@buildroot:~# insmod 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/chip2  
│   └── chip3 -> ../../../devices/chip3  
├── drivers  
│   └── chip  
│       ├── bind  
│       ├── chip0 -> ../../../../devices/chip0  
│       ├── chip1 -> ../../../../devices/chip1  
│       ├── chip2 -> ../../../../devices/chip2  
│       └── chip3 -> ../../../../devices/chip3  
│       ├── uevent  
│       └── unbind  
├── drivers_autoprobe  
├── drivers_probe  
├── uevent  
└── version  

From the printed information, it can be seen that the device and driver are matched through the bus, and then the driver’s probe() function is executed, which aligns with our previous analysis.

5. Summary

The Linux device model is a very complex system.

From a higher level, it mainly consists of buses, devices, and drivers.

To establish the relationships among these components, the kernel defines basic underlying data structures such as kobject and kset, and then presents the interconnection relationships among the components occurring in kernel space to user space through the sysfs file system, providing a simple method for user space programs to access kernel object attribute information through file system interfaces.

To control the length, this article does not cover kojbect and sysfs.

If you are interested, explore the following topics:

  • How do the underlying data structures kobject and kset work in the device model?

  • How does the kernel use the device model to construct driver frameworks for I2C, SPI, USB, etc.?

  • How do device model and sysfs work together?

  • How to create attribute files in sysfs to access device drivers?

  • What is the role of classes in sysfs?

6. Related References

“Linux Device Drivers”

  • Chapter 14: Linux Device Model

“Deep Dive into Linux Device Driver Kernel Mechanisms”

  • Chapter 9: Linux Device Driver Model

“Detailed Explanation of Linux Device Driver Development”

  • Chapter 5: Linux File System and Device Files
  • Chapter 12: Software Architecture Ideas of Linux Device Drivers

Linux/Documentation/driver-model

  • bus.txt
  • class.txt
  • device.txt
  • driver.txt
  • overview.txt

Thinking About Technology, Also Thinking About Life

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

Recently Reading:

“Index Fund Investment Guide”

Author: Bank Screw, focuses on undervalued index fund investments, systematically explaining various index funds and effective strategies for investing in index funds.

Linux Device Model: Architecture Of Driver World

Click to view the larger image

What Have I Gained?

  • Reviewed some basic knowledge about index fund investment;

If you and I each have an apple, if we exchange apples, we still have one apple each. But when you and I each have an idea, if we exchange ideas, we both end up with two ideas.

If you find this article valuable, feel free to like + share.

Recommended Reading:

Album | Linux Device Development

Album | A Little C Each Day

Album | Linux System Programming

Recommended Reading:
Album | Summary of Linux Articles
Album | Programming Life
Album | C Language
My Knowledge Circle
Follow the public account, reply “1024” in the background to get the link to learning materials cloud disk.
Welcome to like, follow, forward, and view. I will remember every encouragement you give me~

Linux Device Model: Architecture Of Driver World

Leave a Comment