linux驱动程序中设备树、device、driver之间的关系
- 驱动程序的一般形式
- 1、platform_driver
- 2、platform_device
驱动程序的一般形式
下面是一个简单的驱动程序框架:
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/bcd.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/device.h>
#include <linux/platform_device.h>
static int my_test_probe(struct platform_device *pdev)
{
printk("my_test_probe\r\n");
printk("%s\r\n",pdev->name);
return 0;
}
static int my_test_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id my_test_id[] = {
{
.compatible = "test_device_id",
},
{}};
MODULE_DEVICE_TABLE(of, my_test_id);
static struct platform_driver my_test_driver = {
.driver = {
.name = "my_test",
.of_match_table = my_test_id,
},
.probe = my_test_probe,
.remove = my_test_remove,
};
static int my_test_init(void)
{
printk("my_test_init\r\n");
platform_driver_register(&my_test_driver);
return 0;
}
static void my_test_exit(void)
{
printk("my_test_exit\r\n");
platform_driver_unregister(&my_test_driver);
}
module_init(my_test_init);
module_exit(my_test_exit);
MODULE_DESCRIPTION("MY test");
MODULE_AUTHOR("yhl");
MODULE_LICENSE("GPL");
从代码中,我们找到以下几个关键的信息:
1、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;
};
其中,probe需要驱动程序实现,当driver和device匹配时,该函数就会执行。设备和驱动分别在总线的两侧,由总线进行匹配。
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.dma_configure = platform_dma_configure,
.pm = &platform_dev_pm_ops,
};
齐总.match成员实现了总线与设备之间的匹配:
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);
}
由于我们是用设备树实现的device,因此我们需要关注下of_driver_match_device();
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;
}
static
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
const struct device_node *node)
{
const struct of_device_id *best_match = NULL;
int score, best_score = 0;
if (!matches)
return NULL;
for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
score = __of_device_is_compatible(node, matches->compatible,
matches->type, matches->name);
if (score > best_score) {
best_match = matches;
best_score = score;
}
}
return best_match;
}
```c
static int __of_device_is_compatible(const struct device_node *device,
const char *compat, const char *type, const char *name)
{
struct property *prop;
const char *cp;
int index = 0, score = 0;
/* Compatible match has highest priority */
if (compat && compat[0]) {
prop = __of_find_property(device, "compatible", NULL);//找到compatible节点
for (cp = of_prop_next_string(prop, NULL); cp;
cp = of_prop_next_string(prop, cp), index++) {
if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
score = INT_MAX/2 - (index << 2);
break;
}
}
if (!score)
return 0;
}
/* Matching type is better than matching name */
if (type && type[0]) {
if (!__of_node_is_type(device, type))
return 0;
score += 2;
}
/* Matching name is a bit better than not */
if (name && name[0]) {
if (!of_node_name_eq(device, name))
return 0;
score++;
}
return score;
}
最终,会解析到设备树的compatible属性节点。
通过上面的分析可以知道,当.of_match_table = my_test_id和设备树中的compatible = “”;节点相匹配时,就是device和driver发生了匹配,probe函数就会被回调。
可以在/sys/bus/platform中看到系统中已经注册的设备:
也可以在/sys/bus/platform中查看已经注册的驱动:
当然在/sys/bus下还有很多总线,有些是实际的硬件总线,platfrom是linux操作系统中软件虚拟的总线。
2、platform_device
这个结构出现在my_test_probe(struct platform_device *pdev)这里,probe函数执行时,能够从该参数中获取/修改那些参数呢?跟踪进去看下:
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u64 dma_mask;
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;
};
又一个关键的成员struct device dev;
struct device {
struct kobject kobj; //相当于内核的基类
struct device *parent; //用于实现链表
struct device_private *p; //私有数据
const char *init_name; /* initial name of the device */
const struct device_type *type; //设备类型
struct bus_type *bus; /* type of bus device is on */ //总线类型
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
void *driver_data; /* Driver data, set and get with
dev_set_drvdata/dev_get_drvdata */
#ifdef CONFIG_PROVE_LOCKING
struct mutex lockdep_mutex;
#endif
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct dev_links_info links;
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
struct irq_domain *msi_domain;
#endif
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
struct list_head msi_list;
#endif
const struct dma_map_ops *dma_ops;
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
u64 bus_dma_limit; /* upstream dma constraint */
unsigned long dma_pfn_offset;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
#ifdef CONFIG_DMA_DECLARE_COHERENT
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
#endif
#ifdef CONFIG_DMA_CMA
struct cma *cma_area; /* contiguous memory area for dma
allocations */
#endif
/* arch specific additions */
struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */
struct fwnode_handle *fwnode; /* firmware device node */
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */
spinlock_t devres_lock;
struct list_head devres_head;
struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
struct iommu_group *iommu_group;
struct iommu_fwspec *iommu_fwspec;
struct iommu_param *iommu_param;
bool offline_disabled:1;
bool offline:1;
bool of_node_reused:1;
bool state_synced:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
bool dma_coherent:1;
#endif
};
这里又涉及到了kobject。
kobject与我们最直接的交互就是/sys目录。
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct kernfs_node *sd; /* sysfs directory entry */
struct kref kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
更多推荐
linux驱动程序中设备树、device、driver之间的关系
发布评论