A bus is a communication channel between the processor and an input/output device. To ensure that the model is generic, all input/output devices are connected to the processor via such a bus (even if it can be a virtual one without a physical hardware correspondent).
When adding a system bus, it will appear in the sysfs file system in /sys/bus. As with kobjects, buses can be organized into hierarchies and will be represented in sysfs.
In the Linux Device Model, a bus is represented by the structure struct bus_type:
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
struct subsys_private *p;
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);
//...
};
It can be noticed that a bus has a name, lists of default attributes, a number of specific functions, and the driver’s private data. The uevent function (formerly hotplug) is used with hotplug devices.
Bus operations are the registration, the implementation of the operations described in the struct bus_type structure and the iteration and inspection of the devices connected to the bus.
A bus is registered using bus_register(), and unregistered using bus_unregister().
Implementation example:
#include <linux/device.h>
/* mybus.c */
//bus type
struct bus_type my_bus_type = {
.name = "mybus",
.match = my_match,
.uevent = my_uevent,
};
static int __init my_bus_init(void)
{
int err;
//...
err = bus_register(&my_bus_type);
if (err)
return err;
//...
}
static void __exit my_bus_exit(void)
{
//...
bus_unregister(&my_bus_type);
//...
}
The functions that will normally be initialized within a bus_type structure are match and uevent:
#include <linux/device.h>
#include <linux/string.h>
/* mybus.c */
// match devices to drivers; just do a simple name test
static int my_match(struct device *dev, struct device_driver *driver)
{
return !strncmp(dev_name(dev), driver->name, strlen(driver->name));
}
// respond to hotplug user events; add environment variable DEV_NAME
static int my_uevent(struct device *dev, struct kobj_uevent_env *env)
{
add_uevent_var(env, "DEV_NAME=%s", dev_name(dev));
return 0;
}
The match function is used when a new device or a new driver is added to the bus. Its role is to make a comparison between the device ID and the driver ID. The uevent function is called before generating a hotplug in user-space and has the role of adding environment variables.
Other possible operations on a bus are iterating over the drivers or devices attached to it. Although we can not directly access them (lists of drivers and devices being stored in the private data of the driver, the subsys_private *p field), these can be iterated using the bus_for_each_dev and bus_for_each_drv macros.
The Linux Device Model interface allows you to create attributes for the associated objects. These attributes will have a corresponding file in the bus subdirectory in sysfs. The attributes associated with a bus are described by the bus_attribute structure :
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *, char *buf);
ssize_t (*store)(struct bus_type *, const char *buf, size_t count);
};
Typically, an attribute is defined by the BUS_ATTR macro. The bus_create_file() and bus_remove_file() functions can be used to add/delete an attribute within the bus structure.
An example of defining an attribute for my_bus is shown below:
/* mybus.c */
#define MY_BUS_DESCR "SO2 rules forever"
// export a simple bus attribute
static ssize_t my_show_bus_descr(struct bus_type *bus, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", MY_BUS_DESCR);
}
/*
* define attribute - attribute name is descr;
* full name is bus_attr_descr;
* sysfs entry should be /sys/bus/mybus/descr
*/
BUS_ATTR(descr, 0444, my_show_bus_descr, NULL);
// specify attribute - in module init function
static int __init my_bus_init(void)
{
int err;
//...
err = bus_create_file(&my_bus_type, &bus_attr_descr);
if (err) {
/* handle error */
}
//...
}
static void __exit my_bus_exit(void)
{
//...
bus_remove_file(&my_bus_type, &bus_attr_descr);
//...
}
The bus is represented by both a bus_type object and a device object, as we will see later (the bus is also a device).
下回 Devices