fbcon的注册和bind

之前讲过console ---> fbcon ---> fbdev drivers ---> hardware,这个flow的fbdev drivers的产生,
从video/console下面的makefile
obj-$(CONFIG_DUMMY_CONSOLE)       += dummycon.o
obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o
obj-$(CONFIG_STI_CONSOLE)         += sticon.o sticore.o
obj-$(CONFIG_VGA_CONSOLE)         += vgacon.o
obj-$(CONFIG_MDA_CONSOLE)         += mdacon.o
obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o bitblit.o softcursor.o
ifeq ($(CONFIG_FB_TILEBLITTING),y)
obj-$(CONFIG_FRAMEBUFFER_CONSOLE)     += tileblit.o
endif
ifeq ($(CONFIG_FRAMEBUFFER_CONSOLE_ROTATION),y)
obj-$(CONFIG_FRAMEBUFFER_CONSOLE)     += fbcon_rotate.o fbcon_cw.o fbcon_ud.o \
                                         fbcon_ccw.o
endif

obj-$(CONFIG_FB_STI)              += sticore.o
可以看到如果要使用用fbcon的话,必须打开CONFIG_FRAMEBUFFER_CONSOLE。从这样看fbcon的入口就在fbcon.c 中
static int __init fb_console_init(void)
{
    int i;

    console_lock();
    fb_register_client(&fbcon_event_notifier);
//device_create 会在/sys/class下面生成fbcon这个device
    fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
                     "fbcon");

    if (IS_ERR(fbcon_device)) {
        printk(KERN_WARNING "Unable to create device "
               "for fbcon; errno = %ld\n",
               PTR_ERR(fbcon_device));
        fbcon_device = NULL;
    } else
        fbcon_init_device();

    for (i = 0; i < MAX_NR_CONSOLES; i++)
        con2fb_map[i] = -1;

    console_unlock();
    fbcon_start();
    return 0;
}

fs_initcall(fb_console_init);
可见是在kernel 初始化阶段就调用fb_console_init。最后调用fbcon_start
static void fbcon_start(void)
{
    if (num_registered_fb) {
        int i;

        console_lock();

        for (i = 0; i < FB_MAX; i++) {
            if (registered_fb[i] != NULL) {
                info_idx = i;
                break;
            }
        }

        do_fbcon_takeover(0);
        console_unlock();

    }
}
在fbcon_start 调用do_fbcon_takeover 来将console从dummy切换到fbcon,需要注意的是在调用do_fbcon_takeover的前后用console_lock/console_unlock 包起来了,
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,注意这个函数的第一个参数是fb_con ,这个fb_con 也是在drivers/video/console/fbcon.c中定义
继续看do_take_over_console
int do_take_over_console(const struct consw *csw, int first, int last, int deflt)
{
    int err;

    err = do_register_con_driver(csw, first, last);
    /*
     * If we get an busy error we still want to bind the console driver
     * and return success, as we may have unbound the console driver
     * but not unregistered it.
     */
    if (err == -EBUSY)
        err = 0;
    if (!err)
        do_bind_con_driver(csw, first, last, deflt);

    return err;
}
在do_take_over_console 中可以看到分为两步,第一步是注册fbcon,第二部是将vtcon bind到fbcon
我们先看第一个
static int do_register_con_driver(const struct consw *csw, int first, int last)
{
    struct module *owner = csw->owner;
    struct con_driver *con_driver;
    const char *desc;
    int i, retval;

    WARN_CONSOLE_UNLOCKED();

    if (!try_module_get(owner))
        return -ENODEV;

    for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
        con_driver = ®istered_con_driver[i];

        /* already registered */
        if (con_driver->con == csw) {
            retval = -EBUSY;
            goto err;
        }
    }

    desc = csw->con_startup();
    if (!desc) {
        retval = -ENODEV;
        goto err;
    }

    retval = -EINVAL;
//对con_driver进行初始化。而con_driver= ®istered_con_driver[i];
    for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
        con_driver = ®istered_con_driver[i];

        if (con_driver->con == NULL &&
            !(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE)) {
            con_driver->con = csw;//这里的csw就是fbcon
            con_driver->desc = desc;
            con_driver->node = i;
            con_driver->flag = CON_DRIVER_FLAG_MODULE |
                               CON_DRIVER_FLAG_INIT;
            con_driver->first = first;
            con_driver->last = last;
            retval = 0;
            break;
        }
    }

    if (retval)
        goto err;
// 这样就可以在看到/sys/class/vtconsole/vtcon 这个目录
    con_driver->dev =
        device_create_with_groups(vtconsole_class, NULL,
                      MKDEV(0, con_driver->node),
                      con_driver, con_dev_groups,
                      "vtcon%i", con_driver->node);
    if (IS_ERR(con_driver->dev)) {
        printk(KERN_WARNING "Unable to create device for %s; "
               "errno = %ld\n", con_driver->desc,
               PTR_ERR(con_driver->dev));
        con_driver->dev = NULL;
    } else {
        vtconsole_init_device(con_driver);
    }

err:
    module_put(owner);
    return retval;
}
初始化完con_driver,并通过device_create_with_groups在/sys/class/vtconsole/vtcon建立目录后,fbcon就算注册完了。
回到do_take_over_console 中继续调用do_bind_con_driver来bind driver
static int do_bind_con_driver(const struct consw *csw, int first, int last,
               int deflt)
{
    struct module *owner = csw->owner;
    const char *desc = NULL;
    struct con_driver *con_driver;
    int i, j = -1, k = -1, retval = -ENODEV;

    if (!try_module_get(owner))
        return -ENODEV;

    WARN_CONSOLE_UNLOCKED();
//检测driver是否已经注册了
    /* check if driver is registered */
    for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
        con_driver = ®istered_con_driver[i];

        if (con_driver->con == csw) {
            desc = con_driver->desc;
            retval = 0;
            break;
        }
    }

    if (retval)
        goto err;
//如果没有初始化,就调用con_startup()
    if (!(con_driver->flag & CON_DRIVER_FLAG_INIT)) {
        csw->con_startup();
        con_driver->flag |= CON_DRIVER_FLAG_INIT;
    }

    if (deflt) {
        if (conswitchp)
            module_put(conswitchp->owner);

        __module_get(owner);
        conswitchp = csw;
    }

    first = max(first, con_driver->first);
    last = min(last, con_driver->last);

    for (i = first; i <= last; i++) {
        int old_was_color;
        struct vc_data *vc = vc_cons[i].d;

        if (con_driver_map[i])
            module_put(con_driver_map[i]->owner);
        __module_get(owner);
        con_driver_map[i] = csw;

        if (!vc || !vc->vc_sw)
            continue;

        j = i;

        if (con_is_visible(vc)) {
            k = i;
            save_screen(vc);
        }

        old_was_color = vc->vc_can_do_color;
        vc->vc_sw->con_deinit(vc);
        vc->vc_origin = (unsigned long)vc->vc_screenbuf;
        visual_init(vc, i, 0);
        set_origin(vc);
        update_attr(vc);

        /* If the console changed between mono <-> color, then
         * the attributes in the screenbuf will be wrong.  The
         * following resets all attributes to something sane.
         */
        if (old_was_color != vc->vc_can_do_color)
            clear_buffer_attributes(vc);
    }
// 从开机log中能看到vtcon已经切换过来了
    pr_info("Console: switching ");
    if (!deflt)
        printk(KERN_CONT "consoles %d-%d ", first+1, last+1);
    if (j >= 0) {
        struct vc_data *vc = vc_cons[j].d;

        printk(KERN_CONT "to %s %s %dx%d\n",
               vc->vc_can_do_color ? "colour" : "mono",
               desc, vc->vc_cols, vc->vc_rows);

        if (k >= 0) {
            vc = vc_cons[k].d;
            update_screen(vc);
        }
    } else
        printk(KERN_CONT "to %s\n", desc);

    retval = 0;
err:
    module_put(owner);
    return retval;
}

你可能感兴趣的:(Linux,源码分析)