一. tty结构体
1.tty_driver
- struct tty_driver {
- int magic;
- struct kref kref; //参考计数
- struct cdev cdev; //字符设备
- struct module *owner; //模块所有者
- const char *driver_name; //驱动名
- const char *name; //设备名
- int name_base;
- int major; //主设备号
- int minor_start; //起始次设备号
- int minor_num; //设备个数
- int num; //分配了的tty设备个数
- short type; //tty设备的类型
- short subtype; //tty设备子类型
- struct ktermios init_termios; //初始化的ktermios
- int flags; //tty驱动标志
- struct proc_dir_entry *proc_entry; //procfs入口
- struct tty_driver *other;
- struct tty_struct **ttys;
- struct ktermios **termios;
- struct ktermios **termios_locked;
- void *driver_state;
- const struct tty_operations *ops; //操作函数集
- struct list_head tty_drivers; //驱动链表
- };
1.1 tty->flag
- #define TTY_DRIVER_INSTALLED 0x0001
- #define TTY_DRIVER_RESET_TERMIOS 0x0002
- #define TTY_DRIVER_REAL_RAW 0x0004
- #define TTY_DRIVER_DYNAMIC_DEV 0x0008
- #define TTY_DRIVER_DEVPTS_MEM 0x0010
- #define TTY_DRIVER_HARDWARE_BREAK 0x0020
1.2 tty->type tty设备类型
- #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
2.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; /* input speed */ //输入速度
- speed_t c_ospeed; /* output speed */ //输出速度
- };
3.tty_struct
- struct tty_struct {
- int magic; //魔数
- struct kref kref; //参考计数
- struct device *dev; //设备文件
- struct tty_driver *driver; //tty驱动
- const struct tty_operations *ops; //tty操作函数集
- int index;
- struct mutex ldisc_mutex;
- struct tty_ldisc *ldisc; //线路规程
- struct mutex termios_mutex;
- spinlock_t ctrl_lock;
- struct ktermios *termios, *termios_locked;
- struct termiox *termiox;
- char name[64]; //名字
- struct pid *pgrp;
- struct pid *session;
- unsigned long flags;
- int count;
- struct winsize winsize;
- unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
- unsigned char low_latency:1, warned:1;
- unsigned char ctrl_status;
- unsigned int receive_room;
- struct tty_struct *link;
- struct fasync_struct *fasync;
- struct tty_bufhead buf;
- int alt_speed;
- wait_queue_head_t write_wait;
- wait_queue_head_t read_wait;
- struct work_struct hangup_work;
- void *disc_data;
- void *driver_data;
- struct list_head tty_files;
- #define N_TTY_BUF_SIZE 4096
- unsigned int column;
- unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
- unsigned char closing:1;
- unsigned char echo_overrun:1;
- unsigned short minimum_to_wake;
- unsigned long overrun_time;
- int num_overrun;
- unsigned long process_char_map[256/(8*sizeof(unsigned long))];
- char *read_buf;
- int read_head;
- int read_tail;
- int read_cnt;
- unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
- unsigned char *echo_buf;
- unsigned int echo_pos;
- unsigned int echo_cnt;
- int canon_data;
- unsigned long canon_head;
- unsigned int canon_column;
- struct mutex atomic_read_lock;
- struct mutex atomic_write_lock;
- struct mutex output_lock;
- struct mutex echo_lock;
- unsigned char *write_buf;
- int write_cnt;
- spinlock_t read_lock;
- struct work_struct SAK_work;
- struct tty_port *port;
- };
4.tty_ldisc线路规程
- struct tty_ldisc {
- struct tty_ldisc_ops *ops;
- atomic_t users;
- };
4.1 线路规程操作函数集
- struct tty_ldisc_ops {
- int magic;
- char *name;
- int num;
- int flags;
- int (*open)(struct tty_struct *);
- void (*close)(struct tty_struct *);
- void (*flush_buffer)(struct tty_struct *tty);
- ssize_t (*chars_in_buffer)(struct tty_struct *tty);
- ssize_t (*read)(struct tty_struct * tty, struct file * file,unsigned char __user * buf, size_t nr);
- ssize_t (*write)(struct tty_struct * tty, struct file * file,const unsigned char * buf, size_t nr);
- int (*ioctl)(struct tty_struct * tty, struct file * file,unsigned int cmd, unsigned long arg);
- long (*compat_ioctl)(struct tty_struct * tty, struct file * file,unsigned int cmd, unsigned long arg);
- void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
- unsigned int (*poll)(struct tty_struct *, struct file *,struct poll_table_struct *);
- int (*hangup)(struct tty_struct *tty);
- void (*receive_buf)(struct tty_struct *, const unsigned char *cp,char *fp, int count);
- void (*write_wakeup)(struct tty_struct *);
- void (*dcd_change)(struct tty_struct *, unsigned int,struct timespec *);
- struct module *owner;
- int refcount;
- };
二.系统初始化
设备类初始化
- static int __init tty_class_init(void)
- {
- tty_class = class_create(THIS_MODULE, "tty"); //创建tty类
- if (IS_ERR(tty_class))
- return PTR_ERR(tty_class);
- tty_class->devnode = tty_devnode; //指定tty设备节点
- return 0;
- }
字符设备初始化
- int __init tty_init(void)
- {
- cdev_init(&tty_cdev, &tty_fops); //初始化tty字符设备
- if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) || //添加tty字符设备
- register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0) //注册/dev/tty字符设备
- panic("Couldn't register /dev/tty driver\n");
- device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,"tty"); //注册tty设备
- cdev_init(&console_cdev, &console_fops); //初始化console字符设备
- if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) || //添加console字符设备
- register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0) //注册/dev/console设备
- panic("Couldn't register /dev/console driver\n");
- device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,"console"); //创建console设备
- #ifdef CONFIG_VT
- vty_init(&console_fops);
- #endif
- return 0;
- }
虚拟终端/dev/tty0初始化vty_init
- int __init vty_init(const struct file_operations *console_fops)
- {
- cdev_init(&vc0_cdev, console_fops);
- if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
- panic("Couldn't register /dev/tty0 driver\n");
- device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0"); //创建/dev/tty0 (4,0)
- vcs_init();
- console_driver = alloc_tty_driver(MAX_NR_CONSOLES);
- if (!console_driver)
- panic("Couldn't allocate console driver\n");
- console_driver->owner = THIS_MODULE;
- console_driver->name = "tty";
- console_driver->name_base = 1;
- console_driver->major = TTY_MAJOR;
- console_driver->minor_start = 1;
- console_driver->type = TTY_DRIVER_TYPE_CONSOLE;
- console_driver->init_termios = tty_std_termios;
- if (default_utf8)
- console_driver->init_termios.c_iflag |= IUTF8;
- console_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
- tty_set_operations(console_driver, &con_ops);
- if (tty_register_driver(console_driver))
- panic("Couldn't register console driver\n");
- kbd_init();
- console_map_init();
- #ifdef CONFIG_MDA_CONSOLE
- mda_console_init();
- #endif
- return 0;
- }
三.tty初始化步骤
1.分配tty结构体
- struct tty_driver *alloc_tty_driver(int lines)
- {
- struct tty_driver *driver;
- driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL); //分配内存
- if (driver) {
- kref_init(&driver->kref); //引用计数+1
- driver->magic = TTY_DRIVER_MAGIC; //设置魔数0x5402
- driver->num = lines; //设置tty line个数
- }
- return driver;
- }
2.填充tty结构体成员
3.注册tty设备驱动
- int tty_register_driver(struct tty_driver *driver)
- {
- int error;
- int i;
- dev_t dev;
- void **p = NULL;
- struct device *d;
- if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
- p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
- if (!p)
- return -ENOMEM;
- }
- if (!driver->major) { //指定了主设备号
- error = alloc_chrdev_region(&dev, driver->minor_start,driver->num, driver->name); //分配多个设备号
- if (!error) {
- driver->major = MAJOR(dev); //主设备号
- driver->minor_start = MINOR(dev); //次设备号
- }
- } else {
- dev = MKDEV(driver->major, driver->minor_start); //获取第一个设备号
- error = register_chrdev_region(dev, driver->num, driver->name); //分配多个设备号
- }
- if (error < 0) {
- kfree(p);
- return error;
- }
- if (p) {
- driver->ttys = (struct tty_struct **)p; //tty_struct
- driver->termios = (struct ktermios **)(p + driver->num); //ktermios
- k } else {
- driver->ttys = NULL;
- driver->termios = NULL;
- }
- cdev_init(&driver->cdev, &tty_fops); //初始化字符设备
- driver->cdev.owner = driver->owner; //设置模块所有者
- error = cdev_add(&driver->cdev, dev, driver->num); //添加字符设备
- if (error) {
- unregister_chrdev_region(dev, driver->num);
- driver->ttys = NULL;
- driver->termios = NULL;
- kfree(p);
- return error;
- }
- mutex_lock(&tty_mutex);
- list_add(&driver->tty_drivers, &tty_drivers); //添加到全局tty_drivers链表
- mutex_unlock(&tty_mutex);
- if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) { //标志动态创建设备标志
- for (i = 0; i < driver->num; i++) {
- d = tty_register_device(driver, i, NULL); //添加tty设备文件/dev/ttyXX
- if (IS_ERR(d)) {
- error = PTR_ERR(d);
- goto err;
- }
- }
- }
- proc_tty_register_driver(driver); //设置tty在/proc下的接口
- driver->flags |= TTY_DRIVER_INSTALLED;
- return 0;
- err:
- for (i--; i >= 0; i--)
- tty_unregister_device(driver, i);
- mutex_lock(&tty_mutex);
- list_del(&driver->tty_drivers);
- mutex_unlock(&tty_mutex);
- unregister_chrdev_region(dev, driver->num);
- driver->ttys = NULL;
- driver->termios = NULL;
- kfree(p);
- return error;
- }
4.创建tty设备文件/dev/ttyXXX
- struct device *tty_register_device(struct tty_driver *driver, unsigned index,struct device *device)
- {
- char name[64];
- dev_t dev = MKDEV(driver->major, driver->minor_start) + index; //获取设备号
- if (index >= driver->num) {
- printk(KERN_ERR "Attempt to register invalid tty line number (%d).\n", index);
- return ERR_PTR(-EINVAL);
- }
- if (driver->type == TTY_DRIVER_TYPE_PTY) //伪终端
- pty_line_name(driver, index, name);
- else //"/dev/ttyXXX"
- tty_line_name(driver, index, name);
- return device_create(tty_class, device, dev, NULL, name); //创建设备文件
- }
5.tty设备文件捆绑的操作函数集tty_fops
- 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,
- };
四. tty的操作
当打开/dev/ttyXX的时候会调用tty_open函数
tty_open操作
- static int tty_open(struct inode *inode, struct file *filp)
- {
- struct tty_struct *tty = NULL;
- int noctty, retval;
- struct tty_driver *driver;
- int index;
- dev_t device = inode->i_rdev; //通过i节点获取设备号
- unsigned saved_flags = filp->f_flags;
- nonseekable_open(inode, filp); //设置打开关联文件的模式
- retry_open:
- noctty = filp->f_flags & O_NOCTTY; //设置O_NOCTTY标志
- index = -1;
- retval = 0;
- mutex_lock(&tty_mutex);
- tty_lock();
- if (device == MKDEV(TTYAUX_MAJOR, 0)) { //打开的设备是/dev/tty (5,0)
- tty = get_current_tty(); //取当前进程tty_struct
- if (!tty) {
- tty_unlock();
- mutex_unlock(&tty_mutex);
- return -ENXIO;
- }
- driver = tty_driver_kref_get(tty->driver); //获取tty_driver
- index = tty->index;
- filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
- tty_kref_put(tty); //引用计数
- goto got_driver;
- }
- #ifdef CONFIG_VT //虚拟终端设备 /dev/tty0
- if (device == MKDEV(TTY_MAJOR, 0)) { //(4,0)
- extern struct tty_driver *console_driver;
- driver = tty_driver_kref_get(console_driver); //获取tty_struct
- index = fg_console;
- noctty = 1;
- goto got_driver;
- }
- #endif
- if (device == MKDEV(TTYAUX_MAJOR, 1)) { //打开的是/dev/console (5,1)
- struct tty_driver *console_driver = console_device(&index); //获取console对应的tty_driver
- if (console_driver) {
- driver = tty_driver_kref_get(console_driver); //获取tty_struct
- if (driver) {
- filp->f_flags |= O_NONBLOCK;
- noctty = 1;
- goto got_driver;
- }
- }
- tty_unlock();
- mutex_unlock(&tty_mutex);
- return -ENODEV;
- }
- driver = get_tty_driver(device, &index); //获取tty_driver,设置次设备号(索引值) 非特殊的tty
- if (!driver) {
- tty_unlock();
- mutex_unlock(&tty_mutex);
- return -ENODEV;
- }
- got_driver:
- if (!tty) {
- tty = tty_driver_lookup_tty(driver, inode, index); //获取tty_drivver的tty_struct
- if (IS_ERR(tty)) {
- tty_unlock();
- mutex_unlock(&tty_mutex);
- return PTR_ERR(tty);
- }
- }
- if (tty) { //若tty_struct不为空
- retval = tty_reopen(tty);
- if (retval)
- tty = ERR_PTR(retval);
- } else //若tty_struct为空
- tty = tty_init_dev(driver, index, 0); //调用tty_init_dev函数(下面分析)
- mutex_unlock(&tty_mutex);
- tty_driver_kref_put(driver); //增加引用计数
- if (IS_ERR(tty)) {
- tty_unlock();
- return PTR_ERR(tty);
- }
- retval = tty_add_file(tty, filp);
- if (retval) {
- tty_unlock();
- return retval;
- }
- check_tty_count(tty, "tty_open");
- if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER)
- noctty = 1;
- #ifdef TTY_DEBUG_HANGUP
- printk(KERN_DEBUG "opening %s...", tty->name);
- #endif
- if (!retval) {
- if (tty->ops->open) //若tty_struct存在open方法
- retval = tty->ops->open(tty, filp); //则调用其open方法
- else
- retval = -ENODEV;
- }
- filp->f_flags = saved_flags;
- if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&!capable(CAP_SYS_ADMIN))
- retval = -EBUSY;
- if (retval) {
- #ifdef TTY_DEBUG_HANGUP
- printk(KERN_DEBUG "error %d in opening %s...", retval,tty->name);
- #endif
- tty_unlock(); /* need to call tty_release without BTM */
- tty_release(inode, filp);
- if (retval != -ERESTARTSYS)
- return retval
- if (signal_pending(current))
- return retval;
- schedule();
- tty_lock();
- if (filp->f_op == &hung_up_tty_fops)
- filp->f_op = &tty_fops;
- tty_unlock();
- goto retry_open;
- }
- tty_unlock();
- mutex_lock(&tty_mutex);
- tty_lock();
- spin_lock_irq(¤t->sighand->siglock);
- if (!noctty && current->signal->leader &&!current->signal->tty && tty->session == NULL)
- __proc_set_tty(current, tty);
- spin_unlock_irq(¤t->sighand->siglock);
- tty_unlock();
- mutex_unlock(&tty_mutex);
- return 0;
- }
tty_init_dev函数
- struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,int first_ok)
- {
- struct tty_struct *tty;
- int retval;
- if (driver->subtype == PTY_TYPE_MASTER &&(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
- return ERR_PTR(-EIO);
- }
- if (!try_module_get(driver->owner))
- return ERR_PTR(-ENODEV);
- tty = alloc_tty_struct(); //分配tty_struct
- if (!tty)
- goto fail_no_mem;
- initialize_tty_struct(tty, driver, idx); //初始化tty_struct
- retval = tty_driver_install_tty(driver, tty); //调用tty_driver的install方法,并初始化termios
- if (retval < 0) {
- free_tty_struct(tty);
- module_put(driver->owner);
- return ERR_PTR(retval);
- }
- retval = tty_ldisc_setup(tty, tty->link); //调用线路规程的open方法,及线路规程link的open方法
- if (retval)
- goto release_mem_out;
- return tty;
- fail_no_mem:
- module_put(driver->owner);
- return ERR_PTR(-ENOMEM);
- release_mem_out:
- if (printk_ratelimit())
- printk(KERN_INFO "tty_init_dev: ldisc open failed,clearing slot %d\n", idx);
- release_tty(tty, idx);
- return ERR_PTR(retval);
- }
tty_init_dev>>>initialize_tty_struct
- void initialize_tty_struct(struct tty_struct *tty,struct tty_driver *driver, int idx)
- {
- memset(tty, 0, sizeof(struct tty_struct)); //初始化tty_struct
- kref_init(&tty->kref);
- tty->magic = TTY_MAGIC; //魔数
- tty_ldisc_init(tty); //初始化线路规程
- tty->session = NULL;
- tty->pgrp = NULL;
- tty->overrun_time = jiffies;
- tty->buf.head = tty->buf.tail = NULL;
- tty_buffer_init(tty);
- mutex_init(&tty->termios_mutex);
- mutex_init(&tty->ldisc_mutex);
- init_waitqueue_head(&tty->write_wait); //初始化写等待队列头
- init_waitqueue_head(&tty->read_wait); //初始化读等待队列头
- INIT_WORK(&tty->hangup_work, do_tty_hangup);
- mutex_init(&tty->atomic_read_lock);
- mutex_init(&tty->atomic_write_lock);
- mutex_init(&tty->output_lock);
- mutex_init(&tty->echo_lock);
- spin_lock_init(&tty->read_lock);
- spin_lock_init(&tty->ctrl_lock);
- INIT_LIST_HEAD(&tty->tty_files);
- INIT_WORK(&tty->SAK_work, do_SAK_work);
- tty->driver = driver; //tty_struct->driver=tty_driver 捆绑tty_struct和tty_driver
- tty->ops = driver->ops; //tty_strcut的操作函数集=tty_driver的操作函数集
- tty->index = idx; //设置索引值
- tty_line_name(driver, idx, tty->name);
- tty->dev = tty_get_device(tty); //设置tty_struct的设备文件
- }
tty_init_dev>>>initialize_tty_struct>>>tty_ldisc_init
- void tty_ldisc_init(struct tty_struct *tty)
- {
- struct tty_ldisc *ld = tty_ldisc_get(N_TTY); //获取线路规程数组tty_ldiscs[N_TTY]项
- if (IS_ERR(ld))
- panic("n_tty: init_tty");
- tty_ldisc_assign(tty, ld); //设置线路规程
- }
在start_kernel-->console_init-->tty_ldisc_begin-->tty_register_ldisc将tty_ldiscs[N_TTY]=tty_ldisc_N_TTY
tty_driver_install_tty
- static int tty_driver_install_tty(struct tty_driver *driver,struct tty_struct *tty)
- {
- int idx = tty->index;
- int ret;
- if (driver->ops->install) { //tty驱动操作函数集存在install方法
- ret = driver->ops->install(driver, tty); //则调用install方法
- return ret;
- }
- if (tty_init_termios(tty) == 0) { //初始化termios
- tty_driver_kref_get(driver);
- tty->count++;
- driver->ttys[idx] = tty;
- return 0;
- }
- return -ENOMEM;
- }
tty_driver_install_tty>>>tty_init_termios
- int tty_init_termios(struct tty_struct *tty)
- {
- struct ktermios *tp;
- int idx = tty->index;
- tp = tty->driver->termios[idx]; //获取termios
- if (tp == NULL) {
- tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);
- if (tp == NULL)
- return -ENOMEM;
- memcpy(tp, &tty->driver->init_termios,sizeof(struct ktermios));
- tty->driver->termios[idx] = tp;
- }
- tty->termios = tp;
- tty->termios_locked = tp + 1;
- /* Compatibility until drivers always set this */
- tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); //设置接收波特率
- tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); //设置波特率
- return 0;
- }
- EXPORT_SYMBOL_GPL(tty_init_termios);
tty_ldisc_setup
- int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
- {
- struct tty_ldisc *ld = tty->ldisc;
- int retval;
- retval = tty_ldisc_open(tty, ld); //调用tty_struct的open方法
- if (retval)
- return retval;
- if (o_tty) {
- retval = tty_ldisc_open(o_tty, o_tty->ldisc); //调用tty_struct->link的open方法
- if (retval) {
- tty_ldisc_close(tty, ld);
- return retval;
- }
- tty_ldisc_enable(o_tty); //使能tty_struct->kink
- }
- tty_ldisc_enable(tty); //使能tty_struct
- return 0;
- }
tty_read操作
- static ssize_t tty_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
- {
- int i;
- struct inode *inode = file->f_path.dentry->d_inode;
- struct tty_struct *tty = file_tty(file); //获取tty_strcut
- struct tty_ldisc *ld;
- if (tty_paranoia_check(tty, inode, "tty_read"))
- return -EIO;
- if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
- return -EIO;
- ld = tty_ldisc_ref_wait(tty); //获取线路规程
- if (ld->ops->read) //线路规程存在读方法
- i = (ld->ops->read)(tty, file, buf, count); //调用其读方法
- else
- i = -EIO;
- tty_ldisc_deref(ld); //引用计数
- if (i > 0)
- inode->i_atime = current_fs_time(inode->i_sb);
- return i;
- }
tty_write操作
- static ssize_t tty_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos)
- {
- struct inode *inode = file->f_path.dentry->d_inode;
- struct tty_struct *tty = file_tty(file); //获取tty_struct
- struct tty_ldisc *ld;
- ssize_t ret;
- if (tty_paranoia_check(tty, inode, "tty_write"))
- return -EIO;
- if (!tty || !tty->ops->write ||(test_bit(TTY_IO_ERROR, &tty->flags)))
- return -EIO;
- /* Short term debug to catch buggy drivers */
- if (tty->ops->write_room == NULL)
- printk(KERN_ERR "tty driver %s lacks a write_room method.\n",tty->driver->name);
- ld = tty_ldisc_ref_wait(tty);
- if (!ld->ops->write) //线路规程存在写方法
- ret = -EIO;
- else
- ret = do_tty_write(ld->ops->write, tty, file, buf, count); //调用线路规程写方法
- tty_ldisc_deref(ld);
- return ret;
- }
do_tty_write函数
do_tty_write(ld->ops->write,
tty, file, buf, count);
这个函数第一个参数是调用线路规程的写方法,写方法函数的参数是后面四个参数
该写方法将数据写入tty_struct相关的file里
- static inline ssize_t do_tty_write(ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
- struct tty_struct *tty,struct file *file,const char __user *buf,size_t count)
- {
- ssize_t ret, written = 0;
- unsigned int chunk;
- ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
- if (ret < 0)
- return ret;
- chunk = 2048;
- if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
- chunk = 65536;
- if (count < chunk)
- chunk = count;
- /* write_buf/write_cnt is protected by the atomic_write_lock mutex */
- if (tty->write_cnt < chunk) {
- unsigned char *buf_chunk;
- if (chunk < 1024)
- chunk = 1024;
- buf_chunk = kmalloc(chunk, GFP_KERNEL);
- if (!buf_chunk) {
- ret = -ENOMEM;
- goto out;
- }
- kfree(tty->write_buf);
- tty->write_cnt = chunk;
- tty->write_buf = buf_chunk;
- }
- /* Do the write .. */
- for (;;) {
- size_t size = count;
- if (size > chunk)
- size = chunk;
- ret = -EFAULT;
- if (copy_from_user(tty->write_buf, buf, size))
- break;
- ret = write(tty, file, tty->write_buf, size);
- if (ret <= 0)
- break;
- written += ret;
- buf += ret;
- count -= ret;
- if (!count)
- break;
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- break;
- cond_resched();
- }
- if (written) {
- struct inode *inode = file->f_path.dentry->d_inode;
- inode->i_mtime = current_fs_time(inode->i_sb);
- ret = written;
- }
- out:
- tty_write_unlock(tty);
- return ret;
- }
五.线路规程操作
tty_driver的open,read,write最终都会调用线路规程的对应操作方法
线路规程默认函数集
- struct tty_ldisc_ops tty_ldisc_N_TTY = {
- .magic = TTY_LDISC_MAGIC,
- .name = "n_tty",
- .open = n_tty_open,
- .close = n_tty_close,
- .flush_buffer = n_tty_flush_buffer,
- .chars_in_buffer = n_tty_chars_in_buffer,
- .read = n_tty_read,
- .write = n_tty_write,
- .ioctl = n_tty_ioctl,
- .set_termios = n_tty_set_termios,
- .poll = n_tty_poll,
- .receive_buf = n_tty_receive_buf,
- .write_wakeup = n_tty_write_wakeup
- };
1.open方法
- static int n_tty_open(struct tty_struct *tty)
- {
- if (!tty)
- return -EINVAL;
- if (!tty->read_buf) {
- tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
- if (!tty->read_buf)
- return -ENOMEM;
- }
- if (!tty->echo_buf) {
- tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
- if (!tty->echo_buf)
- return -ENOMEM;
- }
- reset_buffer_flags(tty);
- tty->column = 0;
- n_tty_set_termios(tty, NULL);
- tty->minimum_to_wake = 1;
- tty->closing = 0;
- return 0;
- }
2.写方法
- static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,const unsigned char *buf, size_t nr)
- {
- const unsigned char *b = buf;
- DECLARE_WAITQUEUE(wait, current);
- int c;
- ssize_t retval = 0;
- /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
- if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- }
- /* Write out any echoed characters that are still pending */
- process_echoes(tty);
- add_wait_queue(&tty->write_wait, &wait);
- while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
- if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
- retval = -EIO;
- break;
- }
- if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
- while (nr > 0) {
- ssize_t num = process_output_block(tty, b, nr);
- if (num < 0) {
- if (num == -EAGAIN)
- break;
- retval = num;
- goto break_out;
- }
- b += num;
- nr -= num;
- if (nr == 0)
- break;
- c = *b;
- if (process_output(c, tty) < 0)
- break;
- b++; nr--;
- }
- if (tty->ops->flush_chars)
- tty->ops->flush_chars(tty); //调用tty_struct操作函数集合的flush_chars方法
- } else {
- while (nr > 0) {
- c = tty->ops->write(tty, b, nr); //调用tty_struct操作函数集合的write方法
- if (c < 0) {
- retval = c;
- goto break_out;
- }
- if (!c)
- break;
- b += c;
- nr -= c;
- }
- }
- if (!nr)
- break;
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- break;
- }
- schedule();
- }
- break_out:
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&tty->write_wait, &wait);
- if (b - buf != nr && tty->fasync)
- set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
- return (b - buf) ? b - buf : retval;
- }
3.读方法
- static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,unsigned char __user *buf, size_t nr)
- {
- unsigned char __user *b = buf;
- DECLARE_WAITQUEUE(wait, current);
- int c;
- int minimum, time;
- ssize_t retval = 0;
- ssize_t size;
- long timeout;
- unsigned long flags;
- int packet;
- do_it_again:
- BUG_ON(!tty->read_buf);
- c = job_control(tty, file);
- if (c < 0)
- return c;
- minimum = time = 0;
- timeout = MAX_SCHEDULE_TIMEOUT;
- if (!tty->icanon) {
- time = (HZ / 10) * TIME_CHAR(tty);
- minimum = MIN_CHAR(tty);
- if (minimum) {
- if (time)
- tty->minimum_to_wake = 1;
- else if (!waitqueue_active(&tty->read_wait) ||
- (tty->minimum_to_wake > minimum))
- tty->minimum_to_wake = minimum;
- } else {
- timeout = 0;
- if (time) {
- timeout = time;
- time = 0;
- }
- tty->minimum_to_wake = minimum = 1;
- }
- }
- if (file->f_flags & O_NONBLOCK) {
- if (!mutex_trylock(&tty->atomic_read_lock))
- return -EAGAIN;
- } else {
- if (mutex_lock_interruptible(&tty->atomic_read_lock))
- return -ERESTARTSYS;
- }
- packet = tty->packet;
- add_wait_queue(&tty->read_wait, &wait);
- while (nr) {
- /* First test for status change. */
- if (packet && tty->link->ctrl_status) {
- unsigned char cs;
- if (b != buf)
- break;
- spin_lock_irqsave(&tty->link->ctrl_lock, flags);
- cs = tty->link->ctrl_status;
- tty->link->ctrl_status = 0;
- spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
- if (tty_put_user(tty, cs, b++)) {
- retval = -EFAULT;
- b--;
- break;
- }
- nr--;
- break;
- }
- set_current_state(TASK_INTERRUPTIBLE);
- if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
- ((minimum - (b - buf)) >= 1))
- tty->minimum_to_wake = (minimum - (b - buf));
- if (!input_available_p(tty, 0)) {
- if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
- retval = -EIO;
- break;
- }
- if (tty_hung_up_p(file))
- break;
- if (!timeout)
- break;
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
- /* FIXME: does n_tty_set_room need locking ? */
- n_tty_set_room(tty);
- timeout = schedule_timeout(timeout);
- continue;
- }
- __set_current_state(TASK_RUNNING);
- /* Deal with packet mode. */
- if (packet && b == buf) {
- if (tty_put_user(tty, TIOCPKT_DATA, b++)) {
- retval = -EFAULT;
- b--;
- break;
- }
- nr--;
- }
- if (tty->icanon && !L_EXTPROC(tty)) {
- /* N.B. avoid overrun if nr == 0 */
- while (nr && tty->read_cnt) {
- int eol;
- eol = test_and_clear_bit(tty->read_tail,tty->read_flags);
- c = tty->read_buf[tty->read_tail];
- spin_lock_irqsave(&tty->read_lock, flags);
- tty->read_tail = ((tty->read_tail+1) &(N_TTY_BUF_SIZE-1));
- tty->read_cnt--;
- if (eol) {
- if (--tty->canon_data < 0)
- tty->canon_data = 0;
- }
- spin_unlock_irqrestore(&tty->read_lock, flags);
- if (!eol || (c != __DISABLED_CHAR)) {
- if (tty_put_user(tty, c, b++)) {
- retval = -EFAULT;
- b--;
- break;
- }
- nr--;
- }
- if (eol) {
- tty_audit_push(tty);
- break;
- }
- }
- if (retval)
- break;
- } else {
- int uncopied;
- uncopied = copy_from_read_buf(tty, &b, &nr);
- uncopied += copy_from_read_buf(tty, &b, &nr);
- if (uncopied) {
- retval = -EFAULT;
- break;
- }
- }
- if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) {
- n_tty_set_room(tty);
- check_unthrottle(tty);
- }
- if (b - buf >= minimum)
- break;
- if (time)
- timeout = time;
- }
- mutex_unlock(&tty->atomic_read_lock);
- remove_wait_queue(&tty->read_wait, &wait);
- if (!waitqueue_active(&tty->read_wait))
- tty->minimum_to_wake = minimum;
- __set_current_state(TASK_RUNNING);
- size = b - buf;
- if (size) {
- retval = size;
- if (nr)
- clear_bit(TTY_PUSH, &tty->flags);
- } else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
- goto do_it_again;
- n_tty_set_room(tty);
- return retval;
- }
六.线路规程的操作函数集会调用对应的tty_struct的操作函数集
在initialize_tty_struct中tty->ops = driver->ops也就是说tty_struct的操作函数集就是tty_driver的操作函数集合
tty_driver的操作函数集设置在其register之前,例如串口的注册函数uart_register_driver中调用tty_set_operations(normal, &uart_ops)
将tty_driver->ops设置为uart_ops。
这样我们对/dev/ttyXXX的操作的调用顺序:字符设备的操作函数集tty_fops-->线路规程的操作函数集tty_ldisc_N_TTY-->串口核心或其他类型设备的操作函数集uart_ops五 linux 串口驱动