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