linux驱动——cmdline原理及利用

2019-07-13 06:05发布

最近安卓项目中想要获取内核cmdline特定的启动参数,因为我们在他的U-BOOT中定制了启动参数,需要在驱动中处理,这个手段其实很常见,今天mark个脚印。 内核中如果你用cat /proc/cmdline,你会看见大致如下的打印: console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0...。当然如果我也可以在我们的项目比如扫描头的型号加个字段scanner=se955,等号赋值,name和value跟前后字段以空格分割。 那么如何从中获取呢?! 方法一:直接获取原始的cmdline的,就是获取/proc/cmdline属性值,在代码中就是读取全局变量saved_command_line这个字符串,然后自行处理,缺点是这个处理的时机比较晚,在设备驱动中处理,优点开发者自由度比较大。 方法二:利用内核的__setup或者early_param。这个两个函数宏实质上是一样的,就是early_param比__setup先处理,优点他们在内核启动阶段运行,都在设备驱动前预先运行。上定义代码, #define __setup_param(str, unique_id, fn, early) / static char __setup_str_##unique_id[] __initdata = str; / static struct obs_kernel_param __setup_##unique_id / __attribute_used__ / __attribute__((__section__(".init.setup"))) / __attribute__((aligned((sizeof(long))))) / = { __setup_str_##unique_id, fn, early } #define __setup(str, fn) / __setup_param(str, fn, fn, 0) #define early_param(str, fn) / __setup_param(str, fn, fn, 1) 其中结构体定义如下: struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};
链接时以这个结构体保存在.init.setup段,实际在存储上就是个结构体数组。仔细看链接文件vmlinux.lds时,你会发现.init.setup实际上就是大致有这样的描述 __setup_start = .; 
*(.init.setup) 
__setup_end = .; 它的意思就是你所有定义的struct obs_kernel_param结构体变量连续序排在__setup_start 和__setup_end 存储之间,到时候就可以在这2个标记之间搜索。而__setup_start这个标记只被do_early_param和obsolete_checksetup。而这2个函数都是在kernel/init/main.c下的函数start_kernel中运行,并且do_early_param先运行,接着obsolete_checksetup后运行。调用大致流程如下start_kernel->parse_early_param->parse_early_options->parse_args->parse_one->do_early_param,而start_kernel->parse_args->parse_one->unknown_bootoption->obsolete_checksetup。翻代码代码中可见先调用__early_param定义的解析参数函数及__setup定义的(console及earlycon)的参数解析函数 接着再调用__setup定义的其他解析参数函数。   使用例子: static int __init scanner_setup(char *str) { if (!str) return 0; if(!strcmp("se955",str)){ scanner_id = SCANNER_SE955; }else if(!strcmp("ue966",str)){ scanner_id = SCANNER_UE966; }else if(!strcmp("n4313",str)){ scanner_id = SCANNER_N4313; }else if(!strcmp("n5600",str)){ scanner_id = SCANNER_N5600; }else if(!strcmp("se655",str)){ scanner_id = SCANNER_SE655; }else if(!strcmp("se4710",str)){ scanner_id = SCANNER_SE4710; }else{ scanner_id = SCANNER_SE4500; } printk("%s %d ",__func__,scanner_id); return 1; } __setup("scanner=", scanner_setup); 该段代码对应于cmdline中的... scanner=se955 ...,这样的话,kernel已启动会先搜索scanner=字符串,如果找到的话就把=号后面的字符串值传递给给回调函数scanner_setup,这样的话str参数就是se955,并且这些代码是在设备驱动运行之前。   NOTE:我碰到的问题,如果同一个字段被比如scanner字段,__setup使用两次,__setup(“scanner=”,fun_1)和__setup(“scanner=”,fun_2)在2个文件中,那么只会有1个被使用,谁先被链接,谁运行,另一个失效,因为运行不到他,代码决定,只匹配第一个。 --------------------- 本文来自 sgmenghuo 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/sgmenghuo/article/details/41251739?utm_source=copy