最近工作在调试usb虚拟串口,让其作为kernel启动的调试串口,以及user空间的输入输出控制台。
利用这个机会,学习下printk如何选择往哪个console输出以及user空间下控制台如何选择,记录与此,与大家共享,也方便自己以后翻阅。
Kernel版本号:3.4.55
依照我的思路(还是时间顺序)分了4部分,指定kernel调试console , kernel下printk console的选择 ,kernel下console的注册,user空间console的选择。
一 指定kernel调试console
首先看kernel启动时如何获取和处理指定的console参数。
kernel的启动参数cmdline可以指定调试console,如指定‘console=ttyS0,115200’,
kernel如何解析cmdline,我之前写了一篇博文如下:
http://blog.csdn.net/skyflying2012/article/details/41142801
根据之前的分析,cmdline中有console=xxx,start_kernel中parse_args遍历.init.setup段所有obs_kernel_param。
kernel/printk.c中注册了‘console=’的解析函数console_setup(注册了obs_kernel_param),所以匹配成功,会调用console_setup来解析,如下:
static int __init console_setup(char *str)
{
char buf[sizeof(console_cmdline[0].name) + 4]; /* 4 for index */
char *s, *options, *brl_options = NULL;
int idx;
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
if (!memcmp(str, "brl,", 4)) {
brl_options = "";
str += 4;
} else if (!memcmp(str, "brl=", 4)) {
brl_options = str + 4;
str = strchr(brl_options, ',');
if (!str) {
printk(KERN_ERR "need port name after brl=\n");
return 1;
}
*(str++) = 0;
}
#endif
/*
* Decode str into name, index, options.
*/
if (str[0] >= '0' && str[0] <= '9') {
strcpy(buf, "ttyS");
strncpy(buf + 4, str, sizeof(buf) - 5);
} else {
strncpy(buf, str, sizeof(buf) - 1);
}
buf[sizeof(buf) - 1] = 0;
if ((options = strchr(str, ',')) != NULL)
*(options++) = 0;
#ifdef __sparc__
if (!strcmp(str, "ttya"))
strcpy(buf, "ttyS0");
if (!strcmp(str, "ttyb"))
strcpy(buf, "ttyS1");
#endif
for (s = buf; *s; s++)
if ((*s >= '0' && *s <= '9') || *s == ',')
break;
idx = simple_strtoul(s, NULL, 10);
*s = 0;
__add_preferred_console(buf, idx, options, brl_options);
console_set_on_cmdline = 1;
return 1;
}
__setup("console=", console_setup);
参数是console=的值字符串,如“ttyS0,115200”,console_setup对console=参数值做解析,以ttyS0,115200为例,最后buf=“ttyS”,idx=0,options="115200",brl_options=NULL。调用__add_preferred_console如下:
/*
* If exclusive_console is non-NULL then only this console is to be printed to.
*/
static struct console *exclusive_console;
/*
* Array of consoles built from command line options (console=)
*/
struct console_cmdline
{
char name[8]; /* Name of the driver */
int index; /* Minor dev. to use */
char *options; /* Options for the driver */
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
char *brl_options; /* Options for braille driver */
#endif
};
#define MAX_CMDLINECONSOLES 8
static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
static int selected_console = -1;
static int preferred_console = -1;
int console_set_on_cmdline;
EXPORT_SYMBOL(console_set_on_cmdline);
static int __add_preferred_console(char *name, int idx, char *options,
char *brl_options)
{
struct console_cmdline *c;
int i;
/*
* See if this tty is not yet registered, and
* if we have a slot free.
*/
for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
if (strcmp(console_cmdline[i].name, name) == 0 &&
console_cmdline[i].index == idx) {
if (!brl_options)
selected_console = i;
return 0;
}
if (i == MAX_CMDLINECONSOLES)
return -E2BIG;
if (!brl_options)
selected_console = i;
c = &console_cmdline[i];
strlcpy(c->name, name, sizeof(c->name));
c->options = options;
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
c->brl_options = brl_options;
#endif
c->index = idx;
return 0;
}
kernel利用结构体数组console_cmdline[8],最多可支持8个cmdline传入的console参数。
__add_preferred_console将name idx options保存到数组下一个成员console_cmdline结构体中,如果数组中已有重名,则不添加,并置selected_console为最新添加的console_cmdline的下标号。
比如cmdline中有“console=ttyS0,115200 console=ttyS1,9600”