首页 > 系统相关 >linux TTY子系统 之 串口

linux TTY子系统 之 串口

时间:2023-04-17 20:57:31浏览次数:38  
标签:TTY register struct uart 串口 driver tty linux

TTY设备

TTY 驱动类型如下,串口也属于TTY

/* tty driver types */
#define TTY_DRIVER_TYPE_SYSTEM        0x0001
#define TTY_DRIVER_TYPE_CONSOLE        0x0002
#define TTY_DRIVER_TYPE_SERIAL        0x0003
#define TTY_DRIVER_TYPE_PTY        0x0004
#define TTY_DRIVER_TYPE_SCC        0x0005    /* scc driver */
#define TTY_DRIVER_TYPE_SYSCONS        0x0006

 

串口驱动程序分析

串口驱动程序层次结构如下图所示。简单来说,串口驱动程序层次结构可以分为两层,下层为串口驱动层,它直接与硬件相接触,需要填充一个 struct uart_ops 的结构体。上层为tty层,包括tty核心层及线路规程,它们各自都有一个 ops 结构体,用户空间可以通过tty注册的字符设备节点来访问串口设备。

 



 

芯片厂商定义一个 struct uart_driver 类型全局变量,只是填充了一些名字、设备号等信息,这些都是不涉及底层硬件访问的。

static struct uart_driver imx_uart_uart_driver = {
    .owner          = THIS_MODULE,
    .driver_name    = DRIVER_NAME,
    .dev_name       = DEV_NAME,
    .major          = SERIAL_IMX_MAJOR,
    .minor          = MINOR_START,
    .nr             = ARRAY_SIZE(imx_uart_ports),
    .cons           = IMX_CONSOLE,
};

 

在struct uart_driver imx_uart_uart_driver 结构体中,有两个成员未被赋值,分别是tty_driver和uart_state。对于tty_driver,代表的是上层,它会在 uart_ register_driver 的过程中赋值。而uart_state ,则代表下层,uart_state也会在uart_ register_driver 的过程中分配空间,但是它里面真正设置硬件相关的东西是 uart_state->uart_port ,这个uart_port 是需要从其它地方调用 uart_add_one_port 来添加的。

 

调用 uart_register_driver 把 struct uart_driver 类型变量注册到系统中,由下面代码可知,无论芯片有几个串口,uart_register_driver 函数只会执行一遍,共用一套驱动代码,在系统启动时执行

static int __init imx_uart_init(void)
{
    int ret = uart_register_driver(&imx_uart_uart_driver);

    if (ret)
        return ret;

    ret = platform_driver_register(&imx_uart_platform_driver);
    if (ret != 0)
        uart_unregister_driver(&imx_uart_uart_driver);

    return ret;
}
 module_init(imx_uart_init);

 

uart_register_driver 函数

  1、根据 uart_driver->nr 来申请 nr 个 uart_state 空间,用来存放驱动所支持的串口(端口)物理信息

  2、创建一个 struct tty_driver 类型变量 normal,并且调用 tty_register_driver 把 normal 注册到系统中。因为串口属于TTY,注册串口驱动就是注册TTY驱动。芯片上所有的串口设备共用 normal

int uart_register_driver(struct uart_driver *drv)
{
    struct tty_driver *normal;
...

    drv->tty_driver = normal;

    normal->driver_name    = drv->driver_name;
    normal->name        = drv->dev_name;
    normal->major        = drv->major;
    normal->minor_start    = drv->minor;
    normal->type        = TTY_DRIVER_TYPE_SERIAL;
    normal->subtype        = SERIAL_TYPE_NORMAL;
    normal->init_termios    = tty_std_termios;
    normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
    normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
...

    retval = tty_register_driver(normal);
...
}

 

在 tty_register_driver 内,由于已经指定了 driver->major,driver->flags == TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV,所以功能是

  1、按芯片串口总数申请设备号

  2、把 driver 添加到链表 tty_drivers

int tty_register_driver(struct tty_driver *driver)
{
    int error;
    int i;
    dev_t dev;
    struct device *d;

    if (!driver->major) {
...
    } else {
        dev = MKDEV(driver->major, driver->minor_start);
        error = register_chrdev_region(dev, driver->num, driver->name);
    }
...

    mutex_lock(&tty_mutex);
    list_add(&driver->tty_drivers, &tty_drivers);
    mutex_unlock(&tty_mutex);

...
    driver->flags |= TTY_DRIVER_INSTALLED;
    return 0;
...
}

 

串口设备和驱动通过platform总线进行匹配,当设备树的串口设备和驱动匹配,执行 imx_uart_probe

如下设备树有两个串口设备

                uart7: serial@2018000 {
                    compatible = "fsl,imx6ul-uart",
                             "fsl,imx6q-uart";
                    reg = <0x02018000 0x4000>;
                    interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
                    clocks = <&clks IMX6UL_CLK_UART7_IPG>,
                         <&clks IMX6UL_CLK_UART7_SERIAL>;
                    clock-names = "ipg", "per";
                    dmas = <&sdma 43 4 0>, <&sdma 44 4 0>;
                    dma-names = "rx", "tx";
                    status = "disabled";
                };

                uart1: serial@2020000 {
                    compatible = "fsl,imx6ul-uart",
                             "fsl,imx6q-uart";
                    reg = <0x02020000 0x4000>;
                    interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
                    clocks = <&clks IMX6UL_CLK_UART1_IPG>,
                         <&clks IMX6UL_CLK_UART1_SERIAL>;
                    clock-names = "ipg", "per";
                    status = "disabled";
                };

static const struct of_device_id imx_uart_dt_ids[] = {
    { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], },
    { .compatible = "fsl,imx53-uart", .data = &imx_uart_devdata[IMX53_UART], },
    { .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },
    { .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },
    { /* sentinel */ }
};

 

imx_uart_probe() 功能:

  1、创建一个 struct imx_port 类型变量 sport,并添加到数组 imx_uart_ports[] 内,数组容量大小等于芯片串口总数。

  2、调用 uart_add_one_port() ,令imx_uart_uart_driver->state[串口号]->uart_port 指向 sport->port,一个 sport->port 对应一个串口设备

 

uart_add_one_port ---> tty_port_register_device_attr_serdev

分析可知,serdev_tty_port_register 返回值为 -ENODEV,所以会执行  tty_register_device_attr()

struct device *tty_port_register_device_attr_serdev(struct tty_port *port,
        struct tty_driver *driver, unsigned index,
        struct device *device, void *drvdata,
        const struct attribute_group **attr_grp)
{
    struct device *dev;

    tty_port_link_device(port, driver, index);

    dev = serdev_tty_port_register(port, device, driver, index);
    if (PTR_ERR(dev) != -ENODEV) {
        /* Skip creating cdev if we registered a serdev device */
        return dev;
    }

    return tty_register_device_attr(driver, index, device, drvdata,
            attr_grp);
}

 

tty_register_device_attr

  1、创建设备文件,其类是 tty_class

  2、创建一个字符设备

调用关系

用户空间的任何open、write、read等操作,直接对应到了tty 层的注册到字符设备的file_operation,也就是tty_fops,tty_fops成员函数执行过程中就会调用tty_operations中相应的函数。

 

标签:TTY,register,struct,uart,串口,driver,tty,linux
From: https://www.cnblogs.com/god-of-death/p/17327445.html

相关文章

  • linux文件管理
    1.文件权限管理每个文件或目录重要权限控制:所有者、所属组、其它人,每个类型都有读(r)、写(w)(不包括删除)、执行(x)三种权限。目录对于读保证可以查看目录的内容,写保证可以新建或删除文件,修改和移动等操作,执行保证当前目录可以成为工作目录(程序执行时的目录)。通过ls-al命令查看当......
  • linux中less查看文件显示行数
    当通过less命令来查看文件时,默认情况下不会显示行数。但是你可以使用下面的方法,在less命令的底部显示文件的行数:1.打开文件,使用less命令,并在文件名后添加加上选项`-N`:```less-Nmyfile.txt```2.或者,你也可以首先进入less命令,然后在less命令底部输入`-N`或`--LIN......
  • zynq7010,petalinux, USB-wifi测试
    zynq7010,基于linux验证USB-wifi功能1.相关电路图,这里貌似复位键默认上电开启的,引脚并没有印出来需要注意的地方注意芯片型号"USB3320",这个在linux内核中如果USB配置正确的话是会被打印出来的usbcore:registerednewinterfacedriverusb-storagechipidea-us......
  • linux操作系统之fork
    fork流程图参考Linux内核进程创建fork源码解析关于Linux下fork()函数机制?......
  • 在linux系统下搭建STM32单片机开发环境
    在linux系统下搭建STM32单片机开发环境的记录目录在linux系统下搭建STM32单片机开发环境的记录前言准备安装交叉编译工具链vscode的配置编码问题include问题关于其他报错处理makefile前言懒得说了,过后再补准备ubuntu系统,vscode,交叉编译工具链gcc-arm-none-eabi,还需要一个下载......
  • linux开发之ls -l命令实现
    效果大致思路首先,程序需要通过参数接受一个文件名。接着通过stat函数获取文件的信息,通过处理,进行合适的输出。转换时间戳可以用localtime函数来进行。判断文件类型时候要按位与S_IFMT。判断权限时候,也要按位与对应的宏。具体流程判断程序用法是否正确通过stas函数获取文......
  • Linux常用命令大全
    Linux常用命令大全最近都在和Linux打交道,这方面基础比较薄弱的我只好买了本鸟哥的书看看,感觉还不错。我觉得Linux相比windows比较麻烦的就是很多东西都要用命令来控制,当然,这也是很多人喜欢linux的原因,比较短小但却功能强大。为了方便大家查找linux的相关命令,我就将我了解到的命令......
  • 安装完SFTP后,用PUTTY连接CENTOS,软件闪退问题
    我出现这个问题是因为SFTP和SSH的配置文件是一个,都是etc/ssh/sshd_config所以是配置文件的问题,我将配置SFTP时设置的删除下面这些就好了MatchGroupsftpX11ForwardingnoAllowTcpForwardingnoChrootDirectory%h           ForceCommandinternal-sftp......
  • linux系统升级
    3.8内核升级所有节点升级系统并重启(线上环境必须升级)yumupdate-y--exclude=kernel*wgethttp://193.49.22.109/elrepo/kernel/el7/x86_64/RPMS/kernel-ml-devel-4.19.12-1.el7.elrepo.x86_64.rpmwgethttp://193.49.22.109/elrepo/kernel/el7/x86_64/RPMS/kernel-ml-4.19.1......
  • Linux-文本编辑vim
    vim使用流程vim默认机器是不安装的,需要手动安装这个工具命令yuminstallvim-y #通过yum软件管理工具进行安装当vim打开不存在的文件的时候,默认会创建文件语法1.vim文件名2.输入字母i,进入编辑模式,代表insert输入字母o,在光标下一行开始编辑3.写完代码......