linux-串口驱动-console-tty解析

熟悉的for_each_console。遍历console_drivers链表。对于存在device成员的console,调用device方法。获取tty_driver,退出遍历。

之后对于该console设备的读写操作都是基于该tty_driver。

全部的输入输出设备都会注冊tty_driver。

所以,对于一个新实现的输入输出设备,假设想让其即作为kernel的printk输出设备。也作为user空间的控制台。则须要在上面u_console基础上再实现device方法成员,来返回该设备的tty_driver。

那么另一个问题:

假设cmdline指定2个I/O设备。“console=ttyS0,115200 console=ttyS1,115200”,user空间选择哪个作为console?

用户空间console open时,console_device遍历console_drivers,找到有device成员的console。获取tty_driver,就会退出遍历。

所以哪个console放在console_drivers前面。就会被选择为user空间的console。

在分析register_console时,假设要注冊的newcon是cmdline指定的最新的console(i = selected_console),则置位CON_CONSDEV。

而在后面newcon加入console_drivers时,推断该置位。置位CON_CONSDEV,则将newcon加入到console_drivers的链表头,否则插入到后面。

所以这里user空间会选择ttyS1作为用户控件的console!

总结下,kernel和user空间下都有一个console,关系到kernel下printk的方向和user下printf的方向,实现区别还是非常大的。

kernel下的console是输入输出设备driver中实现的简单的输出console,仅仅实现write函数,而且是直接输出到设备。

user空间下的console,实际就是tty的一个样例。全部操作函数都继承与tty。全功能,能够打开 读写 关闭。所以对于console的读写。都是由kernel的tty层来终于发送到设备。

kernel的tty层之下还有ldisc线路规程层,线路规程层之下才是详细设备的driver。

ldisc层处理一些对于控制台来说有意义的输入输出字符,比方输入的crtl+C。输出的‘\n‘进过线路规程会变为’\n\r‘。

所以对于kernel下console的write方法。不要忘记,对于log buf中'\n'的处理。实现一个简单的线路规程!

kernel下printk的输出接管,可以通过for_each_console找到 原始的console。然后调用register_console,注册自己的console

可以参考https://www.cnblogs.com/brucemengbm/p/6707111.html

user下printf的输入输出接管,tty_register_driver和tty_register_device注册自己的tty驱动

参考:https://blog.csdn.net/lushengchu_luis/article/details/16823639

static struct tty_operations tty_lan_ops = {
    .open = tty_lan_open,
    .close = tty_lan_close,
    .write = tty_lan_write,
    .put_char = tty_lan_put_char,
    .write_room = tty_lan_write_room,
    .set_termios = tty_lan_set_termios,
    .wait_until_sent = uart_wait_until_sent,

    .flag = CON_PRINTBUFFER | CON_ENABLED,
};

下面分析的code都在register_console 这个函数中
在register_console中,如果console_cmdline 中的console driver和selected_console相等,也就是下面的条件成立
        if (i == selected_console) {
            newcon->flags |= CON_CONSDEV;
            preferred_console = selected_console;
        }
这样当注册真正的console的时候下面的条件就会成立
    if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV))
        newcon->flags &= ~CON_PRINTBUFFER;
这样下面这个if条件就不成了,因此console_seq 就不会被修改,还保持上一次earlycon 打印的值,也就是earlycon打印的log,真正的console不会重复打印
    if (newcon->flags & CON_PRINTBUFFER) {
        /*
         * console_unlock(); will print out the buffered messages
         * for us.
         */
        raw_spin_lock_irqsave(&logbuf_lock, flags);
        console_seq = syslog_seq;
        console_idx = syslog_idx;
        console_prev = syslog_prev;
        raw_spin_unlock_irqrestore(&logbuf_lock, flags);
        /*
         * We're about to replay the log buffer.  Only do this to the
         * just-registered console to avoid excessive message spam to
         * the already-registered consoles.
         */
        exclusive_console = newcon;
    }

总结一下CON_PRINTBUFFER这个flag,表示从buffer中的第一行log开始打印,而CON_CONSDEV表示从earlycon没有打印的log开始打印,通过设置CON_CONSDEV来清零CON_PRINTBUFFER,这样就避免earlycon和real console的log的重复打印
 

tty_lan_driver = alloc_tty_driver(TTY_LAN_MINORS_NUM);
    if(!tty_lan_driver)
        return -ENOMEM;
 
    tty_lan_driver->owner = THIS_MODULE;
    tty_lan_driver->driver_name = "tty_sclu";
    tty_lan_driver->name = "ttty_sclu";
    tty_lan_driver->major = TTY_LAN_MAJOR,
        tty_lan_driver->minor_start = 0;    
    tty_lan_driver->type = TTY_DRIVER_TYPE_SERIAL;
    tty_lan_driver->subtype = SERIAL_TYPE_NORMAL;
    //TTY_DRIVER_DYNAMIC_DEV标志表示手动注册device设备,在tty_io.c中会判断,如果没有该标志,会调用tty_register_device,
    //后续就不用再次注册device设备了
    tty_lan_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
    tty_lan_driver->init_termios = tty_std_termios;
    tty_lan_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
    tty_set_operations(tty_lan_driver, &tty_lan_ops);
 
    retval = tty_register_driver(tty_lan_driver);
    if(retval){
        printk(KERN_ERR"Failed to register tty_lan_driver!\n");
        put_tty_driver(tty_lan_driver);
        return retval;
    }
 
        tty_register_device(tty_lan_driver, 0, NULL);
    return 0;

 

参考:http://www.uml.org.cn/embeded/201209071.asp

https://blog.csdn.net/lushengchu_luis/article/details/9368031

https://blog.csdn.net/lushengchu_luis/article/details/16823639

https://www.cnblogs.com/brucemengbm/p/6707111.html

http://blog.chinaunix.net/uid-27717694-id-3495612.html

https://blog.csdn.net/tiantao2012/article/details/72364917

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值