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 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;
void (*coredump) (struct device *dev);
struct driver_private *p;
};
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);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
int (*num_vf)(struct device *dev);
int (*dma_configure)(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;
bool need_parent_lock;
};
#define platform_driver_register(drv) \
__platform_driver_register(drv, THIS_MODULE)
int __platform_driver_register(struct platform_driver *drv,
struct module *owner)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
drv->driver.probe = platform_drv_probe;
drv->driver.remove = platform_drv_remove;
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
}
//一种是由设备树展开得到;一种是由arch/arm/mach-xxx/文件中定义;内核移植时完善之
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;//平台设备ID表,同系列微差别设备区分
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;//架构相关、预留
};
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}
/**
* 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 */
//首先按设备树去匹配见下文@1分析
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 */
//驱动可能支持多个设备,来的设备和其id_table中的name字段匹配,命中其中一个则匹配上
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);
}
i2c总线,PCI总线以及us
b总线,这些物理总线
platform总线是虚拟的,为了内核以总线方式统一管理
@1:
//驱动和设备分离 驱动是业务处理,算法之类,数据在设备包含。
//举例
static const struct of_device_id davinci_i2s_match[] = {
{ .compatible = "ti,da850-mcbsp" },
{},
};
MODULE_DEVICE_TABLE(of, davinci_i2s_match);
static struct platform_driver davinci_mcbsp_driver = {
.probe = davinci_i2s_probe,
.remove = davinci_i2s_remove,
.driver = {
.name = "davinci-mcbsp",//可用其在内核中找对应的platform_device代码
.of_match_table = of_match_ptr(davinci_i2s_match),
},
};
static inline int of_driver_match_device(struct device *dev,
const struct device_driver *drv)
{
//传入包含compatible 字段的of_device_id davinci_i2s_match
return of_match_device(drv->of_match_table, dev) != NULL;
}
const struct of_device_id *of_match_device(const struct of_device_id *matches,
const struct device *dev)
{
if ((!matches) || (!dev->of_node))
return NULL;
//设备树节点dev->of_node ,将来查找其compatible属性值以及name和type值用于和of_device_id 匹配
return of_match_node(matches, dev->of_node);
}
static struct platform_device dm355_asp1_device = {
.name = "davinci-mcbsp",
.id = 1,
.num_resources = ARRAY_SIZE(dm355_asp1_resources),
.resource = dm355_asp1_resources,
};
使用时,1)在module_init中调用platform_driver_register注册davinci_mcbsp_driver这个platform_driver,在module_exit中调用platform_driver_unregister注销platform_driver。
2)实现platform_driver的probe和remove等函数,如将原来放在module_init中的初始化操作放在probe中,反初始化操作放在remove中
框架示例:
1)设备树文件:
vexpress-v2p-ca9.dts中添加设备
hello {
compatible = "jxl,hello-test";
};
2)驱动文件
hello_platform.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
static int hello_probe(struct platform_device *pdev)
{
printk(KERN_ALERT "hello probe completed.\n");
return 0;
}
static int hello_remove(struct platform_device *pdev)
{
printk(KERN_ALERT "hello remove completed.\n");
return 0;
}
static const struct of_device_id hello_match[] = {
{ .compatible = "jxl,hello-test", },
{}
};
static struct platform_driver hello_driver = {
.driver = {
.name = "hello-test",
.of_match_table = hello_match,
},
.probe = hello_probe,
.remove = hello_remove,
};
static int __init hello_init(void) {
printk(KERN_ALERT "hello init completed.\n");
platform_driver_register(&hello_driver);
return 0;
}
static void __exit hello_exit(void) {
printk(KERN_ALERT "hello exit now.\n");
platform_driver_unregister(&hello_driver);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("JACK <shaoyoujing@163.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("A simple hello driver show.");
MODULE_ALIAS("Hello Module");
3)编译设备树
make CROSS_COMPILE=arm-none-linux-gnueabi- ARCH=arm dtbs
编译驱动
make CROSS_COMPILE=arm-none-linux-gnueabi- ARCH=arm
Makefile如下:
obj-m:=hello_platform.o
CURRENT_PATH:=$(shell pwd)
#the absolute path
LINUX_KERNEL_PATH:=/home/datou/qqq/linux-4.19.108/
#complie object
modules:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules clean
- 运行实验
出现hello设备
/ # ls /sys/devices/platform/
10000000.sysreg Fixed MDIO bus.0
10013000.virtio_mmio alarmtimer
10013200.virtio_mmio hello
10013400.virtio_mmio hsb@e0000000
10013600.virtio_mmio oprofile-perf.0
10020000.clcd pmu
1e000000.scu power
1e000600.timer reg-dummy
1e000620.watchdog smb@4000000
1e00a000.cache-controller uevent
安装驱动:注意输出
/ # insmod hello_platform.ko
hello_platform: loading out-of-tree module taints kernel.
hello init completed.
hello probe completed.
查看设备:出现hello-test驱动
/ # ls /sys/bus/platform/drivers
ARM-CCI of_fixed_clk syscon
alarmtimer of_fixed_factor_clk versatile-i2c
armv7-pmu oprofile-perf vexpress-hwmon
basic-mmio-gpio physmap-flash vexpress-osc
generic-bl pwrseq_emmc vexpress-regulator
gpio-clk pwrseq_simple vexpress-reset
hello-test reg-dummy vexpress-syscfg
isp1760 reg-fixed-voltage vexpress-sysreg
leds-gpio sbsa-uart virtio-mmio
mtd-ram smc91x
of-flash smsc911x