1. 程式人生 > >linux驅動——cmdline原理及利用

linux驅動——cmdline原理及利用

最近安卓專案中想要獲取核心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\n",__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