我们知道在调用prink输出的时候最终都会调用到call_console_drivers
static void call_console_drivers(int level,
const char *ext_text, size_t ext_len,
const char *text, size_t len)
{
struct console *con;
trace_console_rcuidle(text, len);
if (!console_drivers)
return;
for_each_console(con) {
if (exclusive_console && con != exclusive_console)
continue;
if (!(con->flags & CON_ENABLED))
continue;
if (!con->write)
continue;
if (!cpu_online(smp_processor_id()) &&
!(con->flags & CON_ANYTIME))
continue;
if (con->flags & CON_EXTENDED)
con->write(con, ext_text, ext_len);
else
con->write(con, text, len);
}
}
而call_console_drivers 又会调用个个注册driver的write函数。
con_init 函数中下面的这段code中的visual_init会调用tty0往下模块的 vc->vc_sw->con_init(vc, init);函数
for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
tty_port_init(&vc->port);
visual_init(vc, currcons, 1);
vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
vc_init(vc, vc->vc_rows, vc->vc_cols,
currcons || !vc->vc_sw->con_save_screen);
}
从visual_init 中我们知道vc->vc_sw = conswitchp;所以也就是调用conswitchp的con_init
而在setup_arch 中
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
我们的系统定义了CONFIG_VT和CONFIG_DUMMY_CONSOLE,因此conswitchp = &dummy_con;
而在drivers/video/console/dummycon.c 中
const struct consw dummy_con = {
.owner = THIS_MODULE,
.con_startup = dummycon_startup,
.con_init = dummycon_init,
.con_deinit = DUMMY,
.con_clear = DUMMY,
.con_putc = DUMMY,
.con_putcs = DUMMY,
.con_cursor = DUMMY,
.con_scroll = DUMMY,
.con_switch = DUMMY,
.con_blank = DUMMY,
.con_font_set = DUMMY,
.con_font_get = DUMMY,
.con_font_default = DUMMY,
.con_font_copy = DUMMY,
};
可以看到con_init和 con_putcs 都是空函数。而显示的话,那这部分是怎么真正的driver对接的呢?这里以fb为例,出列fb还有drm。
不管哪个硬件实现fb,都会调用register_framebuffer。
int
register_framebuffer(struct fb_info *fb_info)
{
int ret;
mutex_lock(®istration_lock);
ret = do_register_framebuffer(fb_info);
mutex_unlock(®istration_lock);
return ret;
}
继续调用do_register_framebuffer,其中最重要的code如下:
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
static int fbcon_event_notify(struct notifier_block *self,
unsigned long action, void *data)
{
struct fb_event *event = data;
struct fb_info *info = event->info;
struct fb_videomode *mode;
struct fb_con2fbmap *con2fb;
struct fb_blit_caps *caps;
int idx, ret = 0;
/*
* ignore all events except driver registration and deregistration
* if fbcon is not active
*/
if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED ||
action == FB_EVENT_FB_UNREGISTERED))
goto done;
switch(action) {
case FB_EVENT_SUSPEND:
fbcon_suspended(info);
break;
case FB_EVENT_FB_REGISTERED:
ret = fbcon_fb_registered(info);
}
可见对应的FB_EVENT_FB_REGISTERED的处理函数为fbcon_fb_registered
/* called with console_lock held */
static int fbcon_fb_registered(struct fb_info *info)
{
int ret = 0, i, idx;
idx = info->node;
fbcon_select_primary(info);
if (info_idx == -1) {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx) {
info_idx = idx;
break;
}
}
if (info_idx != -1)
ret = do_fbcon_takeover(1);
} else {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx)
set_con2fb_map(i, idx, 0);
}
}
return ret;
}
在fbcon_fb_registered 中会调用do_fbcon_takeover 来显示
static int do_fbcon_takeover(int show_logo)
{
int err, i;
if (!num_registered_fb)
return -ENODEV;
if (!show_logo)
logo_shown = FBCON_LOGO_DONTSHOW;
for (i = first_fb_vc; i <= last_fb_vc; i++)
con2fb_map[i] = info_idx;
err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,
fbcon_is_default);
if (err) {
for (i = first_fb_vc; i <= last_fb_vc; i++)
con2fb_map[i] = -1;
info_idx = -1;
} else {
fbcon_has_console_bind = 1;
}
return err;
}
do_fbcon_takeover 有又调用do_take_over_console 来接过console的显示
这样当在call_console_drivers 中调用tty0的write函数vt_console_print会调用
vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x);
这样当调用do_take_over_console 之后,这里的con_putcs就对应是具体driver的con_putcs函数了
static void call_console_drivers(int level,
const char *ext_text, size_t ext_len,
const char *text, size_t len)
{
struct console *con;
trace_console_rcuidle(text, len);
if (!console_drivers)
return;
for_each_console(con) {
if (exclusive_console && con != exclusive_console)
continue;
if (!(con->flags & CON_ENABLED))
continue;
if (!con->write)
continue;
if (!cpu_online(smp_processor_id()) &&
!(con->flags & CON_ANYTIME))
continue;
if (con->flags & CON_EXTENDED)
con->write(con, ext_text, ext_len);
else
con->write(con, text, len);
}
}
而call_console_drivers 又会调用个个注册driver的write函数。
con_init 函数中下面的这段code中的visual_init会调用tty0往下模块的 vc->vc_sw->con_init(vc, init);函数
for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
tty_port_init(&vc->port);
visual_init(vc, currcons, 1);
vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
vc_init(vc, vc->vc_rows, vc->vc_cols,
currcons || !vc->vc_sw->con_save_screen);
}
从visual_init 中我们知道vc->vc_sw = conswitchp;所以也就是调用conswitchp的con_init
而在setup_arch 中
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
我们的系统定义了CONFIG_VT和CONFIG_DUMMY_CONSOLE,因此conswitchp = &dummy_con;
而在drivers/video/console/dummycon.c 中
const struct consw dummy_con = {
.owner = THIS_MODULE,
.con_startup = dummycon_startup,
.con_init = dummycon_init,
.con_deinit = DUMMY,
.con_clear = DUMMY,
.con_putc = DUMMY,
.con_putcs = DUMMY,
.con_cursor = DUMMY,
.con_scroll = DUMMY,
.con_switch = DUMMY,
.con_blank = DUMMY,
.con_font_set = DUMMY,
.con_font_get = DUMMY,
.con_font_default = DUMMY,
.con_font_copy = DUMMY,
};
可以看到con_init和 con_putcs 都是空函数。而显示的话,那这部分是怎么真正的driver对接的呢?这里以fb为例,出列fb还有drm。
不管哪个硬件实现fb,都会调用register_framebuffer。
int
register_framebuffer(struct fb_info *fb_info)
{
int ret;
mutex_lock(®istration_lock);
ret = do_register_framebuffer(fb_info);
mutex_unlock(®istration_lock);
return ret;
}
继续调用do_register_framebuffer,其中最重要的code如下:
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
static int fbcon_event_notify(struct notifier_block *self,
unsigned long action, void *data)
{
struct fb_event *event = data;
struct fb_info *info = event->info;
struct fb_videomode *mode;
struct fb_con2fbmap *con2fb;
struct fb_blit_caps *caps;
int idx, ret = 0;
/*
* ignore all events except driver registration and deregistration
* if fbcon is not active
*/
if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED ||
action == FB_EVENT_FB_UNREGISTERED))
goto done;
switch(action) {
case FB_EVENT_SUSPEND:
fbcon_suspended(info);
break;
case FB_EVENT_FB_REGISTERED:
ret = fbcon_fb_registered(info);
}
可见对应的FB_EVENT_FB_REGISTERED的处理函数为fbcon_fb_registered
/* called with console_lock held */
static int fbcon_fb_registered(struct fb_info *info)
{
int ret = 0, i, idx;
idx = info->node;
fbcon_select_primary(info);
if (info_idx == -1) {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx) {
info_idx = idx;
break;
}
}
if (info_idx != -1)
ret = do_fbcon_takeover(1);
} else {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx)
set_con2fb_map(i, idx, 0);
}
}
return ret;
}
在fbcon_fb_registered 中会调用do_fbcon_takeover 来显示
static int do_fbcon_takeover(int show_logo)
{
int err, i;
if (!num_registered_fb)
return -ENODEV;
if (!show_logo)
logo_shown = FBCON_LOGO_DONTSHOW;
for (i = first_fb_vc; i <= last_fb_vc; i++)
con2fb_map[i] = info_idx;
err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,
fbcon_is_default);
if (err) {
for (i = first_fb_vc; i <= last_fb_vc; i++)
con2fb_map[i] = -1;
info_idx = -1;
} else {
fbcon_has_console_bind = 1;
}
return err;
}
do_fbcon_takeover 有又调用do_take_over_console 来接过console的显示
这样当在call_console_drivers 中调用tty0的write函数vt_console_print会调用
vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x);
这样当调用do_take_over_console 之后,这里的con_putcs就对应是具体driver的con_putcs函数了