Linux TTY子系统框架

1. TTY介绍

TTY(TeleType)指Linux中的一类终端(Terminal)设备, 是一种字符设备

在Linux中, tty可分为如下几类
- 串行端口终端(serial port terminal): 指使用计算机串行端口连接的终端设备, /dev/ttySn
- 伪终端(pseudo terminal): 通常是通过ssh登陆的终端, /dev/pts/*
- 控制终端(controlling terminal): 代表当前tty设备 /dev/tty
- 控制台终端(console):  指计算机的输出设备, 通常是printk信息输出的设备, /dev/ttyn、/dev/console

详细定义如下

复制代码

/* 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

复制代码

tty可以分为如下几层
- 核心层(tty core): 是tty设备的抽象
- 线路规程(tty line discipline): 是对上层和底层之间数据传输的协议转换, 不同类型的终端设备数据转换协议不同
- 驱动层(tty driver): 面向底层硬件的设备驱动

tty代码位于drivers/tty目录下

tty软件框架如图所示:

tty_structure

回到顶部

2. TTY初始化

在系统启动过程中, 注册了/dev/tty和/dev/console设备

复制代码

chr_dev_init()
  tty_init()
    /* 注册/dev/tty设备 */
    cdev_init(&tty_cdev, &tty_fops);
    cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1)
    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty")
    device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty")
    /* 注册/dev/console设备 */ 

    cdev_init(&console_cdev, &console_fops);
    cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1)
    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console")
    consdev = device_create_with_groups(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, cons_dev_groups, "console");

start_kernel()
  console_init()
    /* 注册N_TTY线路规程 */
    n_tty_init()
      tty_register_ldisc(N_TTY, &n_tty_ops);

复制代码

其中, 值得一说的是tty_fops, 定义了tty设备文件操作集

复制代码

static const struct file_operations tty_fops = {
    .llseek            = no_llseek,
    .read              = tty_read,
    .write             = tty_write,
    .poll              = tty_poll,
    .unlocked_ioctl    = tty_ioctl,
    .compat_ioctl      = tty_compat_ioctl,
    .open              = tty_open,
    .release           = tty_release,
    .fasync            = tty_fasync,
};

复制代码

回到顶部

3. TTY接口

驱动相关API主要如下:

复制代码

/* 分配tty驱动数据结构 */
struct tty_driver *alloc_tty_driver(unsigned int lines);
struct tty_driver *tty_alloc_driver(unsigned int lines, unsigned long flags)
/* 注册/释放tty驱动 */
int tty_register_driver(struct tty_driver *driver);
int tty_unregister_driver(struct tty_driver *driver);
/* 释放tty驱动 */
void put_tty_driver(struct tty_driver *d);
/* tty端口销毁 */
void tty_port_destroy(struct tty_port *port);
/* 设置tty文件操作集 */
void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op);

复制代码

alloc_tty_driver/tty_alloc_driver完成了如下事宜
1. 分配tty_driver数据结构
2. 初始化tty_driver的magic、num、owner、flags成员
3. 如果flags不包含TTY_DRIVER_DEVPTS_MEM, 分配num个tty_struct和ktermios指针变量(!!!注意只是分配了指针!!!
4. 如果flags不包含TTY_DRIVER_DYNAMIC_ALLOC, 分配num个tty_port指针变量(!!!注意只是分配了指针!!!
5. 分配cdev数据结构

tty_register_driver完成了如下事宜
1. 如果指定了主设备号, 通过register_chrdev_region静态申请设备号; 否则通过alloc_chrdev_region动态分配设备号
2. 如果flags包含TTY_DRIVER_DYNAMIC_ALLOC, 调用tty_cdev_add分配并注册字符设备对象
2.1 tty_cdev_add通过cdev_alloc动态分配字符设备对象并赋值给tty_driver的cdevs, 并将tty_fops赋值给cdev的ops, 最后通过cdev_add注册字符设备对象
3. 通过list_add将tty_driver变量添加到tty_drivers链表
4. 如果flags不包含TTY_DRIVER_DYNAMIC_DEV, 通过tty_register_device注册num个tty设备
5. 调用proc_tty_register_driver注册/proc/tty/driver/*

设备相关API主要如下:

复制代码

/* tty设备注册 */
struct device *tty_register_device(struct tty_driver *driver, unsigned index, struct device *dev);
struct device *tty_port_register_device(struct tty_port *port, struct tty_driver *driver, unsigned index, struct device *device);
struct device *tty_port_register_device_attr(struct tty_port *port, struct tty_driver *driver, unsigned index, struct device *device, void *drvdata, const struct attribute_group **attr_grp);
/* tty设备注销 */
void tty_unregister_device(struct tty_driver *driver, unsigned index);

复制代码

tty_port_register_device完成了如下事宜
1. 调用tty_port_link_device将tty_port赋值到tty_driver的ports变量
2. 调用tty_register_device_attr注册tty设备
2.1 根据tty_driver的type调用pty_line_name或者tty_line_name生成tty名称
2.2 如果tty_driver的flags包含TTY_DRIVER_DYNAMIC_ALLOC, 调用tty_cdev_add分配并注册字符设备对象
2.2.1 tty_cdev_add通过cdev_alloc动态分配字符设备对象并赋值给tty_driver的cdevs, 并将tty_fops赋值给cdev的ops, 最后通过cdev_add注册字符设备对象
2.3 分配device数据结构, 并赋值devt、class、parent、release、groups成员; 通过dev_set_name设置device设备名称; 通过dev_set_drvdata设置device的成员变量driver_data, 对于uart驱动来说为tty_port变量
2.4 通过device_register向系统注册设备

回到顶部

4. TTY数据结构

tty核心层包含如下几个重要数据结构

tty_struct是tty设备的"动态抽象", 和文件句柄的功能类似, 还保存了tty设备生命周期中的临时信息, 其生命周期为从打开tty设备开始, 到关闭tty设备结束; 主要供核心层使用

复制代码

struct tty_struct {
    int    magic;
    struct kref kref;
    struct device *dev;                  /* 指向tty设备 */
    struct tty_driver *driver;           /* 指向tty驱动 */
    const struct tty_operations *ops;    /* 指向tty_driver::tty_operations */
    int index;                           /* 指向tty设备的编号(如tty0、tty1中的0、1) */

    struct ld_semaphore ldisc_sem;
    struct tty_ldisc *ldisc;             /* 该tty对应的线路规程 */ 

    struct mutex atomic_write_lock;
    struct mutex legacy_mutex;
    struct mutex throttle_mutex;
    struct rw_semaphore termios_rwsem;
    struct mutex winsize_mutex;
    spinlock_t ctrl_lock;
    spinlock_t flow_lock;
    /* Termios values are protected by the termios rwsem */
    struct ktermios termios, termios_locked;
    struct termiox *termiox;    /* May be NULL for unsupported */
    char name[64];
    struct pid *pgrp;        /* Protected by ctrl lock */
    struct pid *session;
    unsigned long flags;
    int count;
    struct winsize winsize;        /* winsize_mutex */
    unsigned long stopped:1,    /* flow_lock */
              flow_stopped:1,
              unused:BITS_PER_LONG - 2;
    int hw_stopped;
    unsigned long ctrl_status:8,    /* ctrl_lock */
              packet:1,
              unused_ctrl:BITS_PER_LONG - 9;
    unsigned int receive_room;    /* Bytes free for queue */
    int flow_change;

    struct tty_struct *link;
    struct fasync_struct *fasync;
    int alt_speed;        /* For magic substitution of 38400 bps */
    wait_queue_head_t write_wait;
    wait_queue_head_t read_wait;
    struct work_struct hangup_work;
    void *disc_data;              /* 指向n_tty_data, 用于保存线路规程缓冲区信息 */ 
    void *driver_data;            /* 保存tty驱动数据, 对于uart为uart_state*/ 
    spinlock_t files_lock;        /* protects tty_files list */
    struct list_head tty_files;

    int closing;
    unsigned char *write_buf;
    int write_cnt;
    /* If the tty has a pending do_SAK, queue it here - akpm */
    struct work_struct SAK_work;
    struct tty_port *port;               /* 指向tty端口 */
};

复制代码

tty_driver是tty设备的主要数据结构

复制代码

struct tty_driver {
    int              magic;           /* magic number for this structure */
    struct kref      kref;            /* Reference management */
    struct cdev      **cdevs;
    struct module    *owner;
    const char       *driver_name;    /* 该tty驱动的名称, 在tty内部使用 */
    const char       *name;           /* 该tty驱动的设备名称, 体现到sysfs以及/dev/等文件系统下 */
    int              name_base;       /* offset of printed name */
    int              major;           /* 主设备号, 如TTY_MAJOR */
    int              minor_start;     /* 起始次设备号, 通常从64开始, why? */
    unsigned int     num;             /* tty驱动对应设备数 */
    short            type;            /* tty驱动类型, 如SERIAL、PTY等 */
    short            subtype;         /* tty驱动子类型*/
    struct ktermios  init_termios;    /* 初始termios, 如tty_std_termios */
    unsigned long    flags;           /* tty驱动flags */
    struct proc_dir_entry   *proc_entry;      /* /proc文件系统入口 */
    struct tty_driver       *other;           /* 只用于PTY驱动 */

    /*
     * 指向tty相关数据结构
     */
    struct tty_struct **ttys;
    struct tty_port **ports;
    struct ktermios **termios;
    void *driver_state;                    /* tty驱动私有数据 */ 

    const struct tty_operations *ops;      /* tty文件操作集 */ 
    struct list_head tty_drivers;
};

复制代码

tty_port是tty设备固有属性的"静态抽象", 保存了该tty设备的一些固定的属性, 供具体的tty驱动使用

复制代码

struct tty_port {
    struct tty_bufhead   buf;                 /* Locked internally */
    struct tty_struct    *tty;                /* Back pointer */
    struct tty_struct    *itty;               /* internal back ptr */
    const struct tty_port_operations *ops;    /* Port operations */
    spinlock_t           lock;                /* Lock protecting tty field */
    int                  blocked_open;        /* Waiting to open */
    int                  count;               /* Usage count */
    wait_queue_head_t    open_wait;           /* Open waiters */
    wait_queue_head_t    delta_msr_wait;      /* Modem status change */
    unsigned long        flags;               /* User TTY flags ASYNC_ */
    unsigned long        iflags;              /* Internal flags TTY_PORT_ */
    unsigned char        console:1,           /* port is a console */
                         low_latency:1;       /* optional: tune for latency */
    struct mutex         mutex;               /* Locking */
    struct mutex         buf_mutex;           /* Buffer alloc lock */
    unsigned char        *xmit_buf;           /* Optional buffer */
    unsigned int         close_delay;         /* Close port delay */
    unsigned int         closing_wait;        /* Delay for output */
    int                  drain_delay;         /* Set to zero if no pure time
                                                 based drain is needed else
                                                 set to size of fifo */
    struct kref          kref;                /* Ref counter */
};

复制代码

termios, 定义了POSIX终端接口操作对象; 在用户空间为struct termios, 内核空间为struct ktermios

复制代码

struct ktermios {
    tcflag_t c_iflag;        /* input mode flags */
    tcflag_t c_oflag;        /* output mode flags */
    tcflag_t c_cflag;        /* control mode flags */
    tcflag_t c_lflag;        /* local mode flags */
    cc_t c_line;             /* line discipline */
    cc_t c_cc[NCCS];         /* control characters */
    speed_t c_ispeed;        /* tty设备输入速率 */
    speed_t c_ospeed;        /* tty设备输出速率 */
};

复制代码

tty_operations定义了硬件有关的操作, 由tty驱动实现, 被tty核心层调用

复制代码

struct tty_operations {
    struct tty_struct *(*lookup)(struct tty_driver *, struct file *, int);
    int  (*install)(struct tty_driver *, struct tty_struct *);
    void (*remove)(struct tty_driver *, struct tty_struct *);
    int  (*open)(struct tty_struct *, struct file *);                /* 打开*/ 
    void (*close)(struct tty_struct *, struct file *);               /* 关闭 */ 
    void (*shutdown)(struct tty_struct *);
    void (*cleanup)(struct tty_struct *);
    int  (*write)(struct tty_struct *, const unsigned char *, int);  /* 写入缓冲区并刷新到硬件 */
    int  (*put_char)(struct tty_struct *, unsigned char); /* 单字节写入缓冲区 */
    void (*flush_chars)(struct tty_struct *);             /* 刷新写缓冲区到硬件 */
    int  (*write_room)(struct tty_struct *);              /* 写缓冲区空闲大小 */
    int  (*chars_in_buffer)(struct tty_struct *);         /* 写缓冲区数据大小 */
    int  (*ioctl)(struct tty_struct *, unsigned int, unsigned long);
    long (*compat_ioctl)(struct tty_struct *, unsigned int, unsigned long);
    void (*set_termios)(struct tty_struct *, struct ktermios *);    /* termios设置函数 */
    void (*throttle)(struct tty_struct *);  /* 流控,当读缓冲区满时被调用,告知tty驱动不再接收数据 */
    void (*unthrottle)(struct tty_struct *);/* 流控,当读缓冲区空时被调用,告知tty驱动可以接收数据 */
    void (*stop)(struct tty_struct *);      /* 流控,告知tty驱动开始发送数据 */
    void (*start)(struct tty_struct *);     /* 流控,告知tty驱动停止发送数据 */
    void (*hangup)(struct tty_struct *);                    /* 挂起tty设备 */
    int  (*break_ctl)(struct tty_struct *, int state);      /* 线路规程BREAK控制函数 */
    void (*flush_buffer)(struct tty_struct *);              /* 清空写缓冲区并丢弃数据 */
    void (*set_ldisc)(struct tty_struct *);                 /* 线路规程设置函数 */
    void (*wait_until_sent)(struct tty_struct *, int);      /* 同flush_chars */ 
    void (*send_xchar)(struct tty_struct *, char);          /* 高优先级字符XON/XOFF发送函数 */
    int (*tiocmget)(struct tty_struct *);                   /* 获取当前tty设备线路规程设置 */
    int (*tiocmset)(struct tty_struct *, unsigned int set, unsigned int clear);
    int (*resize)(struct tty_struct *, struct winsize *ws);
    int (*set_termiox)(struct tty_struct *, struct termiox *tnew);
    int (*get_icount)(struct tty_struct *, struct serial_icounter_struct *icount);
#ifdef CONFIG_CONSOLE_POLL
    int  (*poll_init)(struct tty_driver *, int line, char *options);
    int  (*poll_get_char)(struct tty_driver *, int line);
    void (*poll_put_char)(struct tty_driver *, int line, char ch);
#endif
    const struct file_operations *proc_fops;               /* /proc文件系统操作集 */ 
};

复制代码

tty_port_operations是tty端口相关操作,  主要是在打开和关闭tty设备(端口)时使用

复制代码

struct tty_port_operations {
    int  (*carrier_raised)(struct tty_port *);                 /* 端口载体检测 */ 
    void (*dtr_rts)(struct tty_port *port, int raise);         /* 控制DTR/RTS*/  
    void (*shutdown)(struct tty_port *);                       /* 端口关闭 */ 
    int  (*activate)(struct tty_port *, struct tty_struct *);  /* 端口激活 */ 
    void (*destruct)(struct tty_port *);                       /* 端口释放 */ 
};

复制代码

回到顶部

5. TTY驱动编写

tty驱动的编写主要步骤如下:

1. 实现tty设备有关的文件操作集(tty_operations)
2. 调用tty_alloc_driver分配一个tty驱动, 并设置driver中相关字段(包括tty_operations变量)
3. 调用tty_register_driver注册tty驱动
4. 如果需要动态注册tty设备, 则需调用tty_register_device或者tty_register_device_attr注册tty设备
5. 接收到数据时, 调用tty_insert_flip_string或者tty_insert_flip_char将数据交给TTY core; TTY core需要发送数据时, 会调用driver提供的回调函数, 在那里面访问硬件送出数据即可

一文彻底讲清Linux tty子系统架构及编程实例

Leon_George

于 2022-10-23 00:16:11 发布

阅读量3.1k
 收藏 43

点赞数 6
分类专栏: Linux3.4.2驱动开发 Linux内核原理及其接口函数分析 文章标签: linux Linux驱动开发 tty子系统 串口设备驱动
版权

Linux3.4.2驱动开发
同时被 2 个专栏收录
64 篇文章65 订阅
订阅专栏

Linux内核原理及其接口函数分析
21 篇文章12 订阅
订阅专栏
【摘要】本文详细解读了linux系统下的tty子系统的深层次原理和架构,并参考了LDD3中的代码实例讲述了无硬件下实现一个简单的tty设备驱动模块的编写。对了解tty子系统及下部串口驱动模块的学习有较好的参考价值。

1、tty设备简介
tty一词源于Teletypes,或Teletypewriters,它是最早出现的一种终端设备,类似电传打字机,由Teletype公司生产。最初tty是指连接到Unix系统上的物理或者虚拟终端。终端是一种字符型设备,通常使用tty来统称各种类型的终端设备。随着时间的推移,当通过串行口能够建立起终端连接后,这个名字也用来指任何的串口设备。

它还有多种类,例如串口(ttySn、ttySACn、ttyOn)、USB到串口的转换器(ttyUSBn),还有需要特殊处理才能正常工作的调制解调器(比如传统的WinModem类设备)等。tty虚拟设备支持虚拟控制台,它能通过键盘及网络连接或者通过xterm会话登录到计算机上。

其实起初终端和控制台都不是个人电脑的概念,而是多人共用的小型中型大型计算机上的概念。终端为主机提供了人机接口,每个人都通过终端使用主机的资源。终端有字符终端和图形终端两种。一台主机可以连很多终端。控制台是一种特殊的人机接口, 是人控制主机的第一人机接口。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/liangzc1124/article/details/127469767

二、TTY子系统框架

1、TTY子系统框架分析

该篇主要用来分析TTY子系统的框架,TTY框架图如下:

image-20221231220853840

TTY子系统位于标准字符驱动之下,其中包括:TTY核心层,TTY线路规程,TTY驱动层。

TTY Core:该核心层主要负责控制跨越一个tty设备的数据流和数据格式,使得TTY Driver能够以一致的方式处理 到硬件出自硬件 的数据。

TTY Line Discipline:线路规程,以特殊的方式对数据进行二次加工,通常表现为协议的转换,如:PPPBluetooth,以便可以虚拟的“插入”到任何tty设备。

TTY Driver:该层主要用于实现各类终端的驱动,用以控制实际硬件设备,用于收发数据。其有3种不通类型的驱动:**控制台,串口,pty**。其中,控制台和 pty 驱动已经被编写进内核。

下面是在网上的另一个TTY框架图,结合上图更加方便理解

image-20221021212536798

从上往下看,最上面是应用层,其次是字符设备驱动,然后是TTY核心层,TTY驱动层。

其中,

用户层:在用户空间,我们操作TTY的方法就是通过openreadwrite读写设备文件,如:/dev/tty/dev/console

通用字符设备驱动:对上,以字符设备驱动的形式,向应用程序提供统一接口open, read, write,以便输入输出数据

System Console Core:其主要有两个功能:

1)向系统提供控制台终端(Console Terminal) ,以便让用户登录进行交互操作。

2)提供printk功能,以便kernel代码进行日志输出。

System console core模块使用struct console结构抽象system console功能,具体的driver不需要关心console的内部逻辑,填充该接口并注册给kernel即可。

2、TTY数据处理流程

  • tty 核心从一个用户获取将要发送给一个 tty 设备的数据. 它接着传递它到一个 tty 线路规程驱动, 接着传递它到一个 tty 驱动. 这个 tty 驱动转换数据为可以发送给硬件的格式.
  • 从 tty 硬件收到的数据向上回流通过 tty 驱动, 进入 tty 线路规程驱动, 再进入 tty 核心, 在这里它被一个用户获取.
  • 有时 tty 驱动直接和 tty 核心通讯, 并且 tty 核心直接发送数据到tty 驱动, 但是大多数情况下 tty 线路规程有机会修改在 2 者之间发送的数据.

3、驱动的目录结构及核心文件

ketnel
│   └── driver
│   │   └── tty
│   │   │   └── serial  # 串口终端驱动  
│   │   │   │   └── 8250
│   │   │   │ │   ├── 8250_core.c  # 8250 串口核心层   
│   │   │   │ │   ├── 8250_port.c  # 8250 串口port抽象
│   │   │   │ │   ├── 8250_dma.c  # 8250 dma驱动
│   │   │   │ │   ├── 8250_dw.c  # 8250 device driver抽象 
│   │   │ └── vt   # 虚拟终端驱动
│   │   │ └── ipwireless # 无线终端驱动
│   │   │ └── hvc   # 虚拟控制台
│   │   │ ├── tty_xxx  #tty driver port 抽象
│   │   │ ├── n_xxx.c  # tty line discipline 线路规程相关文件

复制

serial:该目录下为串口终端的驱动程序

hvc:hypervisor虚拟控制台

vt:目录下为虚拟终端的驱动程序

n_xxx.c:为线路规程的相关文件,也就是串口数据处理

tty_xxx:包括字符设备驱动的实现,ioctl

8250_xxx:为serial Driver

4、TTY在Linux下的分布

TTY是所有终端的统称,对于不同的终端,我们有不同的驱动程序,那么我们怎么知道当前系统中,哪个驱动被加载,哪个终端设备存在呢?

  • 查看所有终端

可以查看/proc/tty/drivers文件,这个文件可以包含当前存在的,不同tty驱动的列表,驱动的名字,驱动的主编号,次编号范围,tty驱动的类型。

/dev/tty             /dev/tty        5       0 system:/dev/tty
/dev/console         /dev/console    5       1 system:console
/dev/ptmx            /dev/ptmx       5       2 system
/dev/vc/0            /dev/vc/0       4       0 system:vtmaster
usbserial            /dev/ttyUSB   188   0-254 serial
serial               /dev/ttyS       4   64-67 serial
pty_slave            /dev/pts      136   0-255 pty:slave
pty_master           /dev/ptm      128   0-255 pty:master
pty_slave            /dev/ttyp       3   0-255 pty:slave
pty_master           /dev/pty        2   0-255 pty:master
unknown              /dev/tty        4    1-63 console

复制

  • 设备模型的角度

所有当前注册的以及在内核中出现的tty设备,有他们自己的子目录 /sys/class/tty下面。

在他们tty设备的子目录下面的文件夹中,有一个dev文件,包含了分配给tty设备的主次编号。 如果驱动程序告诉内核与tty设备相关联的物理设备和驱动程序的位置,它就会创建回这些位置的符号链接。

/sys/class/tty/
|-- console
|   `-- dev
|-- ptmx
|   `-- dev
|-- tty
|   `-- dev
|-- tty0
|   `-- dev
   ... 
|-- ttyS1
|   `-- dev
|-- ttyS2
|   `-- dev
|-- ttyS3
|   `-- dev
   ...
|-- ttyUSB0
|   |-- dev
|   |-- device -> ../../../devices/pci0000:00/0000:00:09.0/usb3/3-1/3-1:1.0/ttyUSB0
|   `-- driver -> ../../../bus/usb-serial/drivers/keyspan_4
|-- ttyUSB1
|   |-- dev
|   |-- device -> ../../../devices/pci0000:00/0000:00:09.0/usb3/3-1/3-1:1.0/ttyUSB1
|   `-- driver -> ../../../bus/usb-serial/drivers/keyspan_4
|-- ttyUSB2
|   |-- dev
|   |-- device -> ../../../devices/pci0000:00/0000:00:09.0/usb3/3-1/3-1:1.0/ttyUSB2
|   `-- driver -> ../../../bus/usb-serial/drivers/keyspan_4
`-- ttyUSB3
    |-- dev
    |-- device -> ../../../devices/pci0000:00/0000:00:09.0/usb3/3-1/3-1:1.0/ttyUSB3
    `-- driver -> ../../../bus/usb-serial/drivers/keyspan_4

复制

  • 字符设备的角度

每个tty设备都有一个struct cdev,以便用户空间可以访问。

/dev/tty

/dev/console

/dev/ttyS0

复制

OK,TTY子系统框架以及目录结构分析就先了解到此,后续分析其主要的数据结构!

参考

一文了解TTY子系统框架-腾讯云开发者社区-腾讯云

https://blog.51cto.com/u_14592069/5824831

一文彻底讲清Linux tty子系统架构及编程实例_master pty-CSDN博客

https://www.cnblogs.com/hzl6255/p/9533327.html

TTY驱动读写流程随笔_tty_operations-CSDN博客

嵌入式学习-驱动开发-lesson6.3-UART驱动send和receive流程分析_uart_write("\r\n",2)-CSDN博客

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值