从pty驱动学习tty设备驱动加载

本文从三个方向研究tty设备

1、tty驱动的加载

2、tty设备的打开

3、tty设备的访问

tty驱动的加载

首先看下tty驱动加载的大体流程

1、legacy_pty_init

上述代码是tty_driver结构体比较通用的初始化代码,这里需要注意几点:

一、lines的设置,有可能会生成设置数量的device

二、flag的标置位赋值数据

三、主设备号设置

四、ops回调函数的设置

2、__tty_alloc_driver

c 复制代码
/**
 * __tty_alloc_driver -- allocate tty driver
 * @lines: count of lines this driver can handle at most
 * @owner: module which is responsible for this driver
 * @flags: some of %TTY_DRIVER_ flags, will be set in driver->flags
 *
 * This should not be called directly, some of the provided macros should be
 * used instead. Use IS_ERR() and friends on @retval.
 */
struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,
		unsigned long flags)
{
	struct tty_driver *driver;
	unsigned int cdevs = 1;
	int err;

	if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1))
		return ERR_PTR(-EINVAL);

	driver = kzalloc(sizeof(*driver), GFP_KERNEL);
	if (!driver)
		return ERR_PTR(-ENOMEM);

	kref_init(&driver->kref);
	driver->num = lines;
	driver->owner = owner;
	driver->flags = flags;

	if (!(flags & TTY_DRIVER_DEVPTS_MEM)) {
		driver->ttys = kcalloc(lines, sizeof(*driver->ttys),
				GFP_KERNEL);
		driver->termios = kcalloc(lines, sizeof(*driver->termios),
				GFP_KERNEL);
		if (!driver->ttys || !driver->termios) {
			err = -ENOMEM;
			goto err_free_all;
		}
	}

	if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
		driver->ports = kcalloc(lines, sizeof(*driver->ports),
				GFP_KERNEL);
		if (!driver->ports) {
			err = -ENOMEM;
			goto err_free_all;
		}
		cdevs = lines;
	}

	driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);
	if (!driver->cdevs) {
		err = -ENOMEM;
		goto err_free_all;
	}

	return driver;
err_free_all:
	kfree(driver->ports);
	kfree(driver->ttys);
	kfree(driver->termios);
	kfree(driver->cdevs);
	kfree(driver);
	return ERR_PTR(err);
}

比较简单明了的代码,主要留意下 tty_driver几个数据成员的赋值

3、tty_set_operations

器件本身回调函数的设置,这个器件与外界通讯的主要途径

c 复制代码
static inline void tty_set_operations(struct tty_driver *driver,
		const struct tty_operations *op)
{
	driver->ops = op;
}
static const struct tty_operations master_pty_ops_bsd = {
	.install = pty_install,
	.open = pty_open,
	.close = pty_close,
	.write = pty_write,
	.write_room = pty_write_room,
	.flush_buffer = pty_flush_buffer,
	.unthrottle = pty_unthrottle,
	.ioctl = pty_bsd_ioctl,
	.compat_ioctl = pty_bsd_compat_ioctl,
	.cleanup = pty_cleanup,
	.resize = pty_resize,
	.remove = pty_remove
};

4、tty_register_driver

c 复制代码
int tty_register_driver(struct tty_driver *driver)
{
	int error;
	int i;
	dev_t dev;
	struct device *d;

	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)
		goto err;

	if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {
		error = tty_cdev_add(driver, dev, 0, driver->num);
		if (error)
			goto err_unreg_char;
	}

	mutex_lock(&tty_mutex);
	list_add(&driver->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);
			if (IS_ERR(d)) {
				error = PTR_ERR(d);
				goto err_unreg_devs;
			}
		}
	}
	proc_tty_register_driver(driver);
	driver->flags |= TTY_DRIVER_INSTALLED;
	return 0;

这里的代码,主要是将设备信息保存到全局数组上去,数组大小为255,主设备号%255,获取下标,然后再根据major从小到大的方式,挂到对应下标的链表上去。为后续上层获取设备信息提供支持

tty_cdev_add

c 复制代码
static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
		unsigned int index, unsigned int count)
{
	int err;

	/* init here, since reused cdevs cause crashes */
	driver->cdevs[index] = cdev_alloc();
	if (!driver->cdevs[index])
		return -ENOMEM;
	driver->cdevs[index]->ops = &tty_fops;
	driver->cdevs[index]->owner = driver->owner;
	err = cdev_add(driver->cdevs[index], dev, count);
	if (err)
		kobject_put(&driver->cdevs[index]->kobj);
	return err;
}
static const struct file_operations tty_fops = {
	.llseek		= no_llseek,
	.read_iter	= tty_read,
	.write_iter	= tty_write,
	.splice_read	= generic_file_splice_read,
	.splice_write	= iter_file_splice_write,
	.poll		= tty_poll,
	.unlocked_ioctl	= tty_ioctl,
	.compat_ioctl	= tty_compat_ioctl,
	.open		= tty_open,
	.release	= tty_release,
	.fasync		= tty_fasync,
	.show_fdinfo	= tty_show_fdinfo,
};

注册字符设备到系统,这里的tty_fops是中间中转用的

上面这段代码两点:

1、生成具体的deivce,这里的device会与上面的cdev_add对应起来,对dev/xxx的访问提供支持.这里需要特别说明的是,tty设备没有自己的总线,所有不会有devic和driver匹配的过程。

2、注册proc节点,以供上层访问

到这里tty驱动注册大体完成,总体看起来比较简单

tty设备的打开

先看流程图

pty的访问写

上面的打开和写及其它的一些操作,调用流程比较简单。这里不具体介绍了,可根据序列图自行跟踪代码

相关推荐
---学无止境---2 小时前
Linux任务迁移函数和空闲负载均衡函数的实现
linux·负载均衡
楼田莉子2 小时前
vscode搭建C/C++配置开发环境
c语言·开发语言·c++·vscode·学习·编辑器
tt666qq3 小时前
linux文件系统学习
linux·运维·学习
我命由我123453 小时前
Photoshop - Photoshop 工具库
笔记·学习·ui·职场和发展·职场·photoshop·ps
七七七七073 小时前
【Linux系统】进程替换
linux·运维·服务器
skyutuzz4 小时前
vim删除文本文件内容
linux·编辑器·vim
拉姆哥的小屋4 小时前
基于提示学习的多模态情感分析系统:从MULT到PromptModel的华丽升级
python·深度学习·学习
---学无止境---5 小时前
Linux信号处理的相关数据结构和操作函数
linux
tiankongdeyige5 小时前
Unity学习之C#的反射机制
学习·unity·c#