table of Contents
1. Linux driver separation and layering
1.1. Drive separation and separation
Three, platform platform driver model
Four, platform platform driver experimental program writing
1. Linux driver separation and layering
In order to facilitate the writing of drivers, improve software reuse and cross-platform performance, Linux proposes the layering and separation of drivers
1.1. Drive separation and separation
It is divided into host driver and device driver, with a unified API in the middle for connection. Host controller drivers are generally written by semiconductor manufacturers, and device drivers are written under the linux driver framework. This is the bus, driver, and device models in Linux
1.2, driven layering
2. Driver-Bus-Device Model
According to the separation and layering of drivers, a bus-driver-device driver framework is derived. The bus code generally does not need to be written by us, provided by the linux kernel. We only need to write drivers and devices. When registering a driver to the bus, the bus will look up all existing devices to see which device matches this driver. Similarly, when registering a device to the bus, the bus will be in the existing Check whether it matches the driver in the driver.
Driver : specific device driver, read, modify, erase, write
Device : device properties, for example, IIC device has IIC device address, speed, etc.
2.1 Bus
Bus data type is the bus_type , using bus_register up to the kernel bus, the main role is to complete match with the drive apparatus. There are many types of buses in Linux, which can be viewed in the /sys/bus directory of the root file system
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
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);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
2.2 Drive
The bus data type is device_driver , use driver_register to register the driver with the kernel. After the driver matches the device, the probe function in the driver will be executed . Can be viewed in the /sys/bus/specific bus/drivers directory of the root file system
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 */
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;
};
2.3 Equipment
The bus data type is device , use device_register to register the device with the kernel. After the driver matches the device, the probe function in the driver will be executed . Can be viewed in the /sys/bus/specific bus/devices directory of the root file system
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
const struct device_type *type;
struct mutex mutex; /* mutex 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 */
...
/* arch specific additions */
struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */
struct fwnode_handle *fwnode; /* firmware device node */
dev_t devt; /* dev_t, creates the sysfs "dev" */
};
Three, platform platform driver model
According to the driver-bus-device driver model, real buses such as IIC, SPI, and USB are completely matched, but some peripherals cannot be attributed to specific buses, such as timers, RTC, and LCD etc. For this reason, the linux kernel has created a virtual platform driver model: platform driver - bus - platform device
3.1 platform bus
For the platform platform, the platform bus is also a bus_type type. platform_match is responsible for the matching of the platform driver and the device. Register the platform bus with the kernel in platform_bus_init ---> bus_register
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
error = device_register(&platform_bus);
if (error)
return error;
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
of_platform_register_reconfig_notifier();
return error;
}
3.2 platform driver
The platform driver is derived on the basis of struct device_driver, adding some platform own attributes, using platform_driver_register to register the platform driver with the kernel, if the platform driver matches the platform device successfully, the probe in platform_driver will be executed .
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
// ---> const struct of_device_id *of_match_table;
// ---> const char *name;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};
3.3 platform equipment
The platform driver is derived on the basis of struct device, adding some platform properties. When there is no device tree, you need to use platform_device_register to register platform devices with the kernel. When there is a device tree, you only need to modify the device tree. If the platform driver matches the platform device successfully, the probe in platform_driver will be executed.
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
3.4 platform matching process
Driver and device matching is done through the bus->match function, the match function under the platform bus is platform_match
/**
* platform_match - bind platform device to platform driver.
* @dev: device.
* @drv: driver.
*
* Platform device IDs are assumed to be encoded like this:
* "<name><instance>", where <name> is a short description of the type of
* device, like "pci" or "floppy", and <instance> is the enumerated
* instance of the device, like '0' or '42'. Driver IDs are simply
* "<name>". So, extract the <name> from the platform_device structure,
* and compare it against the name of the driver. Return whether they match
* or not.
*/
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* When driver_override is set, only bind to the matching driver */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
/* Attempt an OF style match first(首先尝试通过设备树来匹配) */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI style match(再尝试通过acpi来匹配) */
if (acpi_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table(根据driver下的id_table来匹配dev) */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
/*最终的就是比较字符串,也就是比较 platform_device->name 和 platform_driver->driver->name
*这是大多数无设备树情况下的匹配使用方式*/
return (strcmp(pdev->name, drv->name) == 0);
}
When there is a device tree, the matching process is:
of_driver_match_device(dev, drv)
---> of_match_device //of_match_table 为 platform驱动下的 device_driver ---> of_match_table
static inline int of_driver_match_device(struct device *dev,const struct device_driver *drv)
{
return of_match_device(drv->of_match_table, dev) != NULL;
}
/*
* Struct used for matching a device
*/
struct of_device_id {
char name[32];
char type[32];
char compatible[128];
const void *data;
};
Four, platform platform driver experimental program writing
4.1 No device tree
The experimental routine takes led lighting as an example. It needs to be written in two parts: led device and led driver. The led device contains all the resources of the led, that is, the address information of the IO used. These address information will be provided to the led driver. . That is, in the led driver, the function platform_get_resource will be used to obtain these resources (address information) from the led device
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, //资源类型
unsigned int num)//资源索引
Program source code:
https://github.com/denghengli/linux_driver/tree/master/18_led_platform
Usage example:
4.2 With device tree
When there is a device tree, the device is described by the device tree, so there is no need to register the device with the bus, but directly modify the device tree, and then write the driver. After the driver and the device are successfully matched, the device information will be converted from the device tree node to the platform_device structure. The experimental routine uses led lighting as an example:
Program source code:
https://github.com/denghengli/linux_driver/tree/master/19_led_dtsplatform
Usage example: