1. linux内核中,tty层次结构包括了:
a. tty 核心
b. tty 线路规程(discipline): 以协议转换的方式,格式化从一个用户或硬件收到的数据, 如PPP协议或蓝牙协议。
c. tty驱动
三层之间数据流通关系如下图所示:
tty设备发送数据流程: tty核心从用户获取想要发送给一个tty设备的数据,tty核心将数据传递给tty线路规程驱动,然后数据被传递到tty驱动,tty驱动将数据转换为可以发送给硬件的格式。
tty设备接收数据流程: 从硬件接收到的数据向上交给tty驱动,然后进入tty线路规程,再进入tty核心,在这里被一个用户获取。
尽管大多数时候tty核心和tty之间的数据传输会经历tty线路规程的转换,但是tty驱动与tty核心之间也可以直接传输数据。
2. tty_driver 和 tty_operation 结构体:
tty设备驱动的主要工作是填充tty_driver结构体中的成员,实现tty_operations结构体中的成员函数。
struct tty_driver {
int magic; /* magic number for this structure */ //在alloc_tty_driver()函数中被初始化
struct kref kref; /* Reference management */
struct cdev cdev; //对应字符设备的cdev
struct module *owner; //这个驱动模块的拥有者
const char *driver_name; //驱动名,在/proc/tty 和 /sysfs 中使用
const char *name; //设备名,在 /dev 下使用,即驱动的设备节点名,如:ttySn
int name_base; /* offset of printed name */ //为创建设备名字而使用的开始编号,在内核创建一个tty设备名称时使用。
int major; /* major device number */ //驱动程序的主设备号
int minor_start; /* start of minor device number */ //最小次设备号,通常与 name_base 值相同,经常为0.
int minor_num; /* number of *possible* devices */ //此设备号的个数,也就是拥有相同驱动的设备的个数
int num; /* number of devices allocated */ //同minor_num.
short type; /* type of tty driver */
short subtype; /* subtype of tty driver */
struct ktermios init_termios; /* Initial termios */ //初始线路规程设置,用来提供一个线路(line)设置集合。
int flags; /* tty driver flags */
struct proc_dir_entry *proc_entry; /* /proc fs entry */ //proc入口结构体,如果驱动程序实现write_proc()或read_proc(),它将由tty 核心创建。
struct tty_driver *other; /* only used for the PTY driver */ //指向tty从属设备驱动程序的指针,只能被pty驱动程序使用。
/*
* Pointer to the tty data structures
*/
struct tty_struct **ttys;
struct ktermios **termios;
struct ktermios **termios_locked;
void *driver_state; //tty 驱动程序内部的状态,也只能被 pty 驱动程序使用。
/*
* Driver methods
*/
const struct tty_operations *ops;
struct list_head tty_drivers; //构成一个 tty 驱动程序链表。把所有不同的tty驱动程序连接起来。
};
--- type :
--- subtype :
描述 tty 驱动程序的类型,subtype 取决于 type. type 可以有以下几种类型:
TTY_DRIVER_TYPE_SYSTEM --- 在tty子系统内部使用,表明tty子系统正在处理一个内部的tty驱动程序。这种类型不能被常规的tty驱动程序使用
subtype 值可以是:SYSTEM_TYPE_TTY, SYSTEM_TYPE_CONSOLE, SYSTEM_TYPE_SYSCONS, SYSTEM_TYPE_SYSPTMX
TTY_DRIVER_TYPE_CONSOLE --- 只被控制台驱动程序使用,CONSOLE.
TTY_DRIVER_TYPE_SERIAL --- 可以被任何串行类驱动程序使用。这是type最常用的设置之一。
subtype 值可以为:SERIAL_TYPE_NORMAL, SERIAL_TYPE_CALLOUT,这取决于驱动程序的类型。
TTY_DRIVER_TYPE_PTY --- 伪终端接口(pty)使用。
subtype值可以为:PTY_TYPE_MASTER, PTY_TYPE_SLAVE.
--- flags : tty驱动和 tty 核心都使用flags 来表明当前驱动程序的状态及该tty驱动程序的类型,可以是:
TTY_DRIVER_RESET_TERMIOS --- 该标志表示当最后一个进程关闭该设备时, tty 核心对 termios 复位。
这对控制台和pty驱动程序非常有用。比如一个用户将终端设置为非常规状态,如果设置了该位,当用户退出时,或控制该会话进程关闭时,终端能自动恢复常规值。
TTY_DRIVER_REAL_RAW --- 该标志表示驱动程序使用奇偶校验或中断字符线路规程。
这使得线路规程能比较快的方式接收字符,所有的tty驱动程序通常都设该位。
TTY_DRIVER_NO_DEVFS --- 该位表示 当tty_register_driver()时,tty核心不需要为tty驱动程序创建任何的 devfs 入口。
这对于那些需要动态创建和删除次设备的驱动程序来说非常有用,比如:USB转串口驱动程序,USB调制解调器驱动程序,USB蓝牙tty驱动程序
TTY_DRIVER_INSTALLED --- 它由tty核心控制, 当tty驱动程序注册后,tty核心置该位,而不是tty驱动程序。
--- init_termios : 初始线路设置,也是termios结构体
--- termios :当前的线路设置,线路设置控制当前的波特率,数据大小,流控制等参数。
struct tty_operations {
struct tty_struct * (*lookup)(struct tty_driver *driver,
struct inode *inode, int idx);
int (*install)(struct tty_driver *driver, struct tty_struct *tty);
void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
void (*shutdown)(struct tty_struct *tty);
void (*cleanup)(struct tty_struct *tty);
int (*write)(struct tty_struct * tty,
const unsigned char *buf, int count);
int (*put_char)(struct tty_struct *tty, unsigned char ch);//单个字节写函数,把单个字节写入设备。
void (*flush_chars)(struct tty_struct *tty); //与wait_until_sent()一样,刷新数据到硬件。
int (*write_room)(struct tty_struct *tty); //表示有多少缓冲区空闲,
int (*chars_in_buffer)(struct tty_struct *tty); //缓冲区长度
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);
void (*throttle)(struct tty_struct * tty); //用于控制tty核心输入缓存,当tty核心输入缓存满时,throttle()函数将被调用,tty驱动试图通知设备不该发送字符给它了。
void (*unthrottle)(struct tty_struct * tty); //当tty核心输入缓冲被清空后,unthrottle()函数被调用,暗示设备可以接受数据了,
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
//stop()和start(),跟throttle()和unthrottle()类似,他们表示tty驱动应该停止发送数据给设备,和恢复发送数据给设备。
void (*hangup)(struct tty_struct *tty); //tty驱动挂起tty设备,在此函数中进行相关的硬件操作。
int (*break_ctl)(struct tty_struct *tty, int state); //线路中断控制函数被调用
void (*flush_buffer)(struct tty_struct *tty); //用于刷新缓冲区,并丢弃任何剩下的数据。
void (*set_ldisc)(struct tty_struct *tty); //设置线路规程,当tty核心改变tty驱动的线路规程时,这个函数被调用。
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch); //X-类型字符发送函数。
int (*tiocmget)(struct tty_struct *tty, struct file *file); //获取tty设备的线路设置
int (*tiocmset)(struct tty_struct *tty, struct file *file, //设置tty设备的线路设置, 参数set和clear包含了要设置或清除的线路设置。
unsigned int set, unsigned int clear);
int (*resize)(struct tty_struct *tty, struct winsize *ws);
int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew); //当设备的termios设置被改变时,set_termios()函数被tty核心调用。
int (*get_icount)(struct tty_struct *tty,
struct serial_icounter_struct *icount);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct tty_driver *driver, int line, char *options);
int (*poll_get_char)(struct tty_driver *driver, int line);
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
#endif
const struct file_operations *proc_fops;
};
3. termios 结构体:
用来描述当前线路设置,这些线路设置控制当前波特率,数据大小,数据流控制 等。
typedef unsigned char cc_t;
typedef unsigned int speed_t;
typedef unsigned int tcflag_t;
#define NCCS 19
struct termios {
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 */ //控制字符数组
};
struct termios2 {
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 */
};
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 */
};
驱动会用一个标准的数值集初始化这个成员,这个数值集源于tty_std_termios变量:
struct ktermios tty_std_termios = { /* for the benefit of tty drivers */
.c_iflag = ICRNL | IXON, //输入模式
.c_oflag = OPOST | ONLCR, //输出模式
.c_cflag = B38400 | CS8 | CREAD | HUPCL, //控制模式
.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
ECHOCTL | ECHOKE | IEXTEN, //本地模式
.c_cc = INIT_C_CC, //控制字符,用来修改终端的特殊字符映射。
.c_ispeed = 38400,
.c_ospeed = 38400
};
EXPORT_SYMBOL(tty_std_termios);
4. Linux内核提供了一组操作 tty_driver 和 tty设备的函数:
4.1 struct tty_driver alloc_tty_driver(int lines); ---分配tty驱动
该函数返回 tty_driver指针,
参数lines 是要分配的设备数量,lines会被赋值给tty_driver的num成员。
magic 成员会在这里赋值,如TTY_DRIVER_MAGIC。
定义如下: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);
driver->magic = TTY_DRIVER_MAGIC;
driver->num = lines;
/* later we'll move allocation of tables here */
}
return driver;
}
EXPORT_SYMBOL(alloc_tty_driver);
使用例子:
xxx_tty_driver = alloc_tty_driver(XXX_TTY_MINORS);
if(!xxx_tty_driver) //分配失败
return -ENOMEM;
4.2 int tty_register_driver(struct tty_driver *driver); --- 注册tty驱动
成功时返回0,参数为 由 alloc_tty_driver()分配的 tty_driver 指针。
定义如下:
-
例子:/* * Called by a tty driver to register itself. */ 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); // 设置线路规程 tty_driver->termios } 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); //添加到链表 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; } } } proc_tty_register_driver(driver); //procfs 注册 driver->flags |= TTY_DRIVER_INSTALLED; //完成后,flags 表示向tty core注册driver了。 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; } EXPORT_SYMBOL(tty_register_driver);
int tty_unregister_driver(struct tty_driver *driver); --- 注销tty驱动;retval = tty_register_driver(xxx_tty_driver); if(retval) { printk(KERN_ERR "failed to register tiny tty driver"); put_tty_driver(xxx_tty_driver); //减小tty_driver的引用计数 return retval; }
/* * Called by a tty driver to unregister itself. */ int tty_unregister_driver(struct tty_driver *driver) { #if 0 /* FIXME */ if (driver->refcount) return -EBUSY; #endif unregister_chrdev_region(MKDEV(driver->major, driver->minor_start), driver->num); mutex_lock(&tty_mutex); list_del(&driver->tty_drivers); mutex_unlock(&tty_mutex); return 0; } EXPORT_SYMBOL(tty_unregister_driver);
4.3 void tty_register_device(struct tty_driver *driver, unsigned index, struct device *device); --- 注册tty设备
仅有 tty_driver 是不够的,驱动必须依附于设备。index --- 设备的索引号
定义如下:
/** * tty_register_device - register a tty device * @driver: the tty driver that describes the tty device * @index: the index in the tty driver for this tty device * @device: a struct device that is associated with this tty device. * This field is optional, if there is no known struct device * for this tty device it can be set to NULL safely. * * Returns a pointer to the struct device for this tty device * (or ERR_PTR(-EFOO) on error). * * This call is required to be made to register an individual tty device * if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set. If * that bit is not set, this function should not be called by a tty * driver. * * Locking: ?? */ 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设备,伪终端 pty_line_name(driver, index, name); else tty_line_name(driver, index, name); return device_create(tty_class, device, dev, NULL, name); //在/sys/device下建立tty类 } EXPORT_SYMBOL(tty_register_device);
例子:
void tty_unregister_device(struct tty_driver *driver, unsigned index); --- 注销tty设备for (i = 0; i < XXX_TTY_MINORS; ++i) tty_register_device(xxx_tty_driver, i, NULL);
/** * tty_unregister_device - unregister a tty device * @driver: the tty driver that describes the tty device * @index: the index in the tty driver for this tty device * * If a tty device is registered with a call to tty_register_device() then * this function must be called when the tty device is gone. * * Locking: ?? */ void tty_unregister_device(struct tty_driver *driver, unsigned index) { device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index); } EXPORT_SYMBOL(tty_unregister_device);
例子:
4.4 void tty_set_operations(struct tty_driver *driver, struct tty_operations *op); --- 设置 tty 驱动操作函数:for (i = 0; i < XXX_TTY_MINORS; ++i) tty_unregister_device(xxx_tty_driver, i);
- 该函数会把 tty_operaions 结构体中的函数指针,赋值到 tty_driver 对应的函数指针。
在具体 tty 驱动中,通常会定义一个设备特定的 tty_operations。
void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op) { driver->ops = op; }; EXPORT_SYMBOL(tty_set_operations);
终端设备驱动都围绕tty_driver结构体展开,一般应包含:
1. 终端设备驱动模块加载和卸载函数:完成注册和注销tty_driver,初始化和释放终端设备对应的tty_driver结构体和硬件资源。
2. 实现tty_operations结构体中的一系列成员:主要有open(),close(),write(),tiocmget(),tiocmset().
- 该函数会把 tty_operaions 结构体中的函数指针,赋值到 tty_driver 对应的函数指针。