简而言之,linux驱动非常重视软件的可重用和跨平台的能力,最好一行不改就能在任何一个平台上跑起来,基于这样的事实,驱动中如果集成所有平台的设备信息显然太过臃肿,驱动也会显得很奇怪,所以把设备信息分离出去,设备就负责表示设备的硬件信息和资源,驱动就负责用标准化的手法得到设备的资源,总线就负责匹配设备和驱动
用结构体struct platform_device来描述一个平台设备
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;
};
/*
* Resources are tree-like, allowing
* nesting etc..
*/
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
unsigned long desc;
struct resource *parent, *sibling, *child;
};
用struct platform_driver描述一个平台设备驱动
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 platform_device_id *id_table;
bool prevent_deferred_probe;
};
struct platform_device_id {
char name[PLATFORM_NAME_SIZE];
kernel_ulong_t driver_data;
};
/* struct device_driver - The basic device driver structure */
struct device_driver {
const char *name;/* Name of the device driver */
struct bus_type *bus;/* The bus which the device of this driver belongs to */
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;/* The open firmware table */
const struct acpi_device_id *acpi_match_table;/* The 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;
};
/*
* Struct used for matching a device
*/
struct of_device_id {
char name[32];
char type[32];
char compatible[128];
const void *data;
};
系统为platform bus定义了一个实例叫做platform_bus_type
/* @match: Called, perhaps multiple times, whenever a new device or driver
* is added for this bus. It should return a positive value if the
* given device can be handled by the given driver and zero
* otherwise. It may also return error code if determining that
* the driver supports the device is not possible. In case of
* -EPROBE_DEFER it will queue the device for deferred probing.
*/
struct bus_type platform_bus_type = {
.name = "platform", /* The name of the bus */
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
/**
* platform_match - bind platform device to platform driver.
* @dev: device.
* @drv: driver.
*
* Platform device IDs are assumed to be encoded like this:
* "", where is a short description of the type of
* device, like "pci" or "floppy", and is the enumerated
* instance of the device, like '0' or '42'. Driver IDs are simply
* "". So, extract the 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 */
if (acpi_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
注:书籍参考
注:代码来源:linux-4.14.99