从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的访问写

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

相关推荐
Amazing_Cacao2 小时前
CFCA精品可可产区风土体系(亚洲):彻底拒绝应试背诵,将感官复核作为检验真实物理差异的唯一铁律
学习
FoldWinCard3 小时前
D5 Linux 网络及端口命令
linux·运维·服务器
零涂毕业设计4 小时前
毕业设计模块开发-OLED显示屏(IIC协议0.96寸)STM32 ESP32 FPGA Linux驱动免费分享
linux·stm32·单片机·嵌入式·esp32·fpga·oled
tedcloud1234 小时前
OmniRoute怎么部署?开源AI模型路由平台Linux部署教程
linux·服务器·人工智能·开源·音视频
霸道流氓气质4 小时前
KMS 密钥管理服务(Key Management Service)原理与实践
linux·服务器·数据库
SakitamaX5 小时前
LVS(Linux Virtual Server)概念详解
linux·运维·lvs
小张同学a.5 小时前
Linux系统管理
linux·运维
阿龙的工作记录5 小时前
深入理解Linux设备驱动模型
linux·嵌入式硬件·工作记录
代码村新手6 小时前
Linux的基本指令
linux·运维·服务器
hkj88086 小时前
Ubuntu 切换java版本
java·linux·ubuntu