从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
/*** __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
器件本身回调函数的设置,这个器件与外界通讯的主要途径
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
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
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的访问写
上面的打开和写及其它的一些操作,调用流程比较简单。这里不具体介绍了,可根据序列图自行跟踪代码