4 linux tty驱动
一. 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 串口驱动