在linux内核驱动中使用__setup宏提取uboot的bootargs参数

本文介绍如何使用U-Boot传递给Linux内核的Bootargs参数,以实现驱动程序的自动适配,特别是针对LCD屏的型号选择。通过设置和解析Bootargs参数,如lcdtype,可以在生产调试中提供便利。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有时候linux内核中的驱动程序需要根据一些参数作出对应的调整,例如生产中可能需要适配多种型号LCD屏,如果只需要传入一些参数,就能让驱动程序自动适配驱动参数,这样会给生产调试带来很多方便。

这里介绍的方法是利用uboot传递给内核的bootargs参数。

为了能让内核使用uboot的bootargs参数,在配置内核选项中需要选择:

Boot opthions--->Kernel command line type :
(X) Use bootloader kernel arguments if available
( ) Extend bootloader kernel arguments
( ) Always use the default kernel command string

linux内核代码中已经实现了提取bootargs参数的方法,就是利用__setup宏。
__setup宏在<linux/init.h>定义如下:

/*
 * Only for really core code.  See moduleparam.h for the normal way.
 *
 * Force the alignment so the compiler doesn't space elements of the
 * obs_kernel_param "array" too far apart in .init.setup.
 */
#define __setup_param(str, unique_id, fn, early)			\
	static const char __setup_str_##unique_id[] __initconst	\
		__aligned(1) = str; \
	static struct obs_kernel_param __setup_##unique_id	\
		__used __section(.init.setup)			\
		__attribute__((aligned((sizeof(long)))))	\
		= { __setup_str_##unique_id, fn, early }

#define __setup(str, fn)					\
	__setup_param(str, fn, fn, 0)

/* NOTE: fn is as per module_param, not __setup!  Emits warning if fn
 * returns non-zero. */
#define early_param(str, fn)					\
	__setup_param(str, fn, fn, 1)

该宏的作用是在编译时产生一个.init.data初始化段,该初始化段以__setup_start标号为起始,以__setup_end标号为结束。而以__setup宏定义的data则插入在__setup_start与__setup_end之间。这样就形成一张线性表,只需要查找该表就可提取bootargs参数了。

early_param宏的作用与__setup宏的作用类似,区别在于early_param定义的选项会比普通内核选项优先处理。在源码init/main.c中看到首先使用parse_early_param()处理early_param定义的选项,然后使用parse_args()处理__setup定义的选项。这样做的目的是保证处理普通内核参数之前能够预先做一些工作,例如初始化存储环境,设置输入输出环境等。

我们这里以前面的适配LCD的例子讲解一下使用方法。
首先需要在uboot中设置需要的参数,例如我们设置一个lcdtype的参数
uboot#setenv lcdtype 1
然后将该参数插入bootargs参数中,lcdtype = ${lcdtype}
有的linux构建环境buildroot会提供修改bootargs参数的脚本,则需要从脚本中更改bootargs,这样就不用每次重新编译都重新设置该参数了。例如全志A33平台,有个配置文件brandy\pack\chips\sun8iw5p1\configs\linux\default\env.cfg
其内容就是配置uboot引导参数和linux运行环境的:

bootdelay=3
#default bootcmd, will change at runtime according to key press
bootcmd=run setargs_nand boot_normal#default nand boot
#kernel command arguments
console=ttyS0,115200
nand_root=/dev/nandd
mmc_root=/dev/mmcblk0p7
init=/init
loglevel=8
lcdtype=1
#set kernel cmdline if boot.img or recovery.img has no cmdline we will use this
setargs_nand=setenv bootargs console=${console} root=${nand_root} init=${init} loglevel=${loglevel} partitions=${partitions} lcdtype=${lcdtype}
setargs_mmc=setenv bootargs console=${console} root=${mmc_root} init=${init} loglevel=${loglevel} partitions=${partitions}  lcdtype=${lcdtype}
#nand command syntax: sunxi_flash read address partition_name read_bytes
#0x40007800 = 0x40008000(kernel entry) - 0x800(boot.img header 2k)
boot_normal=sunxi_flash read 40007800 boot;boota 40007800
boot_recovery=sunxi_flash read 40007800 recovery;boota 40007800
boot_fastboot=fastboot
#recovery key
recovery_key_value_max=0x13
recovery_key_value_min=0x10
#fastboot key
fastboot_key_value_max=0x8
fastboot_key_value_min=0x2

如上所示,已经将lcdtype参数插入到bootargs中了,我们可以使用如下方法提取参数lcdtype,在需要使用参数lcdtype的LCD驱动代码中加入以下代码:

static int lcdtype = 0;

static int __init lcdtype_get(char *str)
{
	if (!str)
		return 0;
	if(!strcmp("1",str)) {
		lcdtype = 1;
	} 
	return 1;
}

__setup("lcdtype=", lcdtype_get);

之后就可在LCD驱动代码中使用lcdtype这个变量了。是不是很简单。
这是最简单的使用bootargs的方法,当然如果想手动编代码解析bootargs参数也是可以的,在内核源码init/main.c文件中的数组boot_command_line保存了bootargs字符串,在main.c中写一个接口函数获取它,然后送给解析函数去解析。

char* kernel_get_cmdline_string(void)
{
	return boot_command_line;
}

上面是内核中获取bootargs字符串的方法。
在应用程序中需要通过读取

/proc/cmdline

的值获取bootargs字符串。

需要注意,在<linux/init.h>中__setup宏是在MODULE宏无效时起作用的,所以使用__setup宏的源文件在编译时需要使用[*]built-in方式,如果使用[M]module方式编译会报defined but not used错误。

假如需要在MODULE中使用bootargs的env参数,可以改造成如下方式:

static int lcdtype = 0;

int lcdtype_check(void)
{
	return lcdtype;
}
EXPORT_SYMBOL(lcdtype_check);

static int __init lcdtype_get(char *str)
{
	if (!str)
		return 0;
	if(!strcmp("1",str)) {
		lcdtype = 1;
	} 
	return 1;
}

__setup("lcdtype=", lcdtype_get);

并将以上代码放入非MODULE源码中,如/init/main.c中,因为已经使用EXPORT_SYMBOL宏将lcdtype_check扇出为全局符号呢,所以在应用中只需要声明一下即可:

extern int lcdtype_check();
int type = lcdtype_check();
//......

如果有帮到你,请帮我点个赞:P

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值