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




 

你可能感兴趣的:(linux设备驱动,linux设备驱动)