转自:https://blog.csdn.net/qingzhuyuxian/article/details/82895416

最近安卓项目中想要获取内核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先处理,优点他们在内核启动阶段运行,都在设备驱动前预先运行。上定义代码,

  1.  
    #define __setup_param(str, unique_id, fn, early) /
  2.  
    static char __setup_str_##unique_id[] __initdata = str; /
  3.  
    static struct obs_kernel_param __setup_##unique_id /
  4.  
    __attribute_used__ /
  5.  
    __attribute__((__section__(".init.setup"))) /
  6.  
    __attribute__((aligned((sizeof(long))))) /
  7.  
    = { __setup_str_##unique_id, fn, early }
  8.  
     
  9.  
    #define __setup(str, fn) /
  10.  
    __setup_param(str, fn, fn, 0)
  11.  
     
  12.  
    #define early_param(str, fn) /
  13.  
    __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定义的其他解析参数函数。

使用例子:

  1.  
    static int __init scanner_setup(char *str)
  2.  
    {
  3.  
    if (!str)
  4.  
    return 0;
  5.  
    if(!strcmp("se955",str)){
  6.  
    scanner_id = SCANNER_SE955;
  7.  
    }else if(!strcmp("ue966",str)){
  8.  
    scanner_id = SCANNER_UE966;
  9.  
    }else if(!strcmp("n4313",str)){
  10.  
    scanner_id = SCANNER_N4313;
  11.  
    }else if(!strcmp("n5600",str)){
  12.  
    scanner_id = SCANNER_N5600;
  13.  
    }else if(!strcmp("se655",str)){
  14.  
    scanner_id = SCANNER_SE655;
  15.  
    }else if(!strcmp("se4710",str)){
  16.  
    scanner_id = SCANNER_SE4710;
  17.  
    }else{
  18.  
    scanner_id = SCANNER_SE4500;
  19.  
    }
  20.  
    printk("%s %d\n",__func__,scanner_id);
  21.  
    return 1;
  22.  
    }
  23.  
    __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

05-21 21:32