@设备驱动匹配简述
linux驱动模型是分成三个部分的,设备(结构体device),驱动(结构体device_driver),总线(结构体bus_type),这个模型管理着linux的驱动,让他们在开机之后能正常驱动硬件有序的干活。这一切需要从一棵树讲起,我说的这棵树就是我们常说的dts(device tree source),搞懂搞透设备树是我们可以正确开发硬件驱动的前提,因为设备树是配置板级信息的地方。
我们所说的设备树起源于openfirmware,所以linux关于设备树的接口都是以of开头的,存放这些核心API的源代码路径是drivers/of/of.c
首先,我们需要搞清楚一个流程,那就是driver是如何找到硬件的,驱动模型告诉我们是device主动去找driver或者driver主动去找device的,就像一男一女想要通过相亲来解决个人问题,如果没有一个红娘,他们没有机会认识彼此的,bus_type就是我说的这个红娘,她来负责牵线,让两个人开始相识,后面会不会相知相守,她就不会管了。如果想让红娘给你们牵线的前提,就是红娘都要认识你们,那么device和driver是如何让红娘都认识的呢,就是两者需要通过红娘提供的注册接口来注册个人信息,不过bus_type比较勤劳,也比较敬业,只要你一注册,她就会立马触发匹配流程,也就是说,一旦有一方注册,她就会立马把她认识的所有的异性给你介绍一遍,让你挨个去相亲,如果你找到了你心仪的另外一半,就把彼此交给对方,堕入爱河吧。那如果你都不满意,不好意思了,只能等你心仪的另外一半来找你了。接下来我们从device/driver双方角度结合代码分别说明这个匹配流程。
@device端发起匹配:
在linux启动并初始化的地方,会有一个专门解析设备树的地方,他就在著名的start_kernel这个函数里面,让我们来看看这个梦开始的地方,是如何一步一步让万千男女堕入爱河的。
下面是流程图,结合这个流程图再去细究每一个函数模块
asmlinkage __visible void __init start_kernel(void)
{
char *command_line;
char *after_dashes;
。。。。此处省略大量无关代码
setup_arch(&command_line);//可以看到,这个地方开 始解析设备树
。。。。此处省略大量无关代码
/* Do the rest non-__init'ed, we're now alive */
rest_init();
}
再看看setup_arch(&command_line)是如何写的吧
void __init setup_arch(char **cmdline_p)
{
。。。。此处省去大量无关代码
setup_machine_fdt(__fdt_pointer);//第一步是把设备树三个重要的节点解析出来
。。。。此处省去大量无关代码
if (acpi_disabled) {
unflatten_device_tree();//第二步是把所有子节点解析出来
psci_dt_init();
} else {
psci_acpi_init();
}
。。。。此处省去大量无关代码
}
细究一下这两个函数
static void __init setup_machine_fdt(phys_addr_t dt_phys)
{
void *dt_virt = fixmap_remap_fdt(dt_phys);
if (!dt_virt || !early_init_dt_scan(dt_virt)) {
pr_crit("\n"
"Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n"
"The dtb must be 8-byte aligned and must not exceed 2 MB in size\n"
"\nPlease check your bootloader.",
&dt_phys, dt_virt);
while (true)
cpu_relax();
}
dump_stack_set_arch_desc("%s (DT)", of_flat_dt_get_machine_name());
}
bool __init early_init_dt_scan(void *params)
{
bool status;
status = early_init_dt_verify(params);
if (!status)
return false;
early_init_dt_scan_nodes();
return true;
}
void __init early_init_dt_scan_nodes(void)
{
/* Retrieve various information from the /chosen node */
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);//从chosen节点解析出bootargs并存放到boot_command_line这个全局变量
/* Initialize {size,address}-cells info */
of_scan_flat_dt(early_init_dt_scan_root, NULL);//从根节点中解析出address-cells和size-cells并存放在dt_root_addr_cells和dt_root_size_cells两个全局变量中
/* Setup memory, calling early_init_dt_add_memory_arch */
of_scan_flat_dt(early_init_dt_scan_memory, NULL);//解析memory节点并初始化内存
}
上面流程主要是第一次扫描节点并解析出三个对于系统来说非常重要的三个节点
接下来是重头戏,解析出root节点下所有的子设备节点,也是我们需要重点了解和掌握的。
void __init unflatten_device_tree(void)
{
__unflatten_device_tree(initial_boot_params, &of_root,
early_init_dt_alloc_memory_arch);
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
of_alias_scan(early_init_dt_alloc_memory_arch);
}
static void __unflatten_device_tree(const void *blob,
struct device_node **mynodes,
void * (*dt_alloc)(u64 size, u64 align))
{
unsigned long size;
int start;
void *mem;
pr_debug(" -> unflatten_device_tree()\n");
if (!blob) {
pr_debug("No device tree pointer\n");
return;
}
pr_debug("Unflattening device tree:\n");
pr_debug("magic: %08x\n", fdt_magic(blob));
pr_debug("size: %08x\n", fdt_totalsize(blob));
pr_debug("version: %08x\n", fdt_version(blob));
if (fdt_check_header(blob)) {
pr_err("Invalid device tree blob header\n");
return;
}
/* First pass, scan for size */
start = 0;
size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);
size = ALIGN(size, 4);
pr_debug(" size is %lx, allocating...\n", size);
/* Allocate memory for the expanded device tree */
mem = dt_alloc(size + 4, __alignof__(struct device_node));
memset(mem, 0, size);
*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
pr_debug(" unflattening %p...\n", mem);
/* Second pass, do actual unflattening */
start = 0;
unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
if (be32_to_cpup(mem + size) != 0xdeadbeef)
pr_warning("End of tree marker overwritten: %08x\n",
be32_to_cpup(mem + size));
pr_debug(" <- unflatten_device_tree()\n");
}
static void * unflatten_dt_node(const void *blob,
void *mem,
int *poffset,
struct device_node *dad,
struct device_node **nodepp,
unsigned long fpsize,
bool dryrun)
{
const __be32 *p;
struct device_node *np;
struct property *pp, **prev_pp = NULL;
const char *pathp;
unsigned int l, allocl;
static int depth;
int old_depth;
int offset;
int has_name = 0;
int new_format = 0;
/* 获取node节点的name指针到pathp中 */
pathp = fdt_get_name(blob, *poffset, &l);
if (!pathp)
return mem;
allocl = ++l;
/* version 0x10 has a more compact unit name here instead of the full
* path. we accumulate the full path size using "fpsize", we'll rebuild
* it later. We detect this because the first character of the name is
* not '/'.
*/
if ((*pathp) != '/') {
new_format = 1;
if (fpsize == 0) {
/* root node: special case. fpsize accounts for path
* plus terminating zero. root node only has '/', so
* fpsize should be 2, but we want to avoid the first
* level nodes to have two '/' so we use fpsize 1 here
*/
fpsize = 1;
allocl = 2;
l = 1;
pathp = "";
} else {
/* account for '/' and path size minus terminal 0
* already in 'l'
*/
fpsize += l;
allocl = fpsize;
}
}
/* 分配struct device_node内存,包括路径全称大小 */
np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
if (!dryrun) {
char *fn;
of_node_init(np);
/* 填充full_name,full_name指向该node节点的全路径名称字符串 */
np->full_name = fn = ((char *)np) + sizeof(*np);
if (new_format) {
/* rebuild full path for new format */
if (dad && dad->parent) {