#define TTY_MAJOR 4
#define TTYAUX_MAJOR 5
Tty : 终端设备的统称。一般分为:控制台、串口和pty.
/dev/ttySn: 串行端口终端(串口),串行端口被看做字符设备。
4:66 (设备号)-> ../../devices/platform/serial8250/tty/ttyS2
4:67 -> ../../devices/platform/serial8250/tty/ttyS3
4:68 -> ../../devices/platform/serial8250/tty/ttyS4
echo test > /dev/ttyS2可接串口来测试ttyS2。
Pty: pseudo tty虚拟(伪)终端
pts(pseudo-terminal slave) 与ptmx(pseudo-terminal master) 配合使用实现pty。
打开一个伪terminal,那么将会在devpts文件系统/dev/pts下创建一个对应的pts字符文件。
通过tty命令(或者who am i)查看当前终端,可以看出此伪terminal对应着pts3。
再打开一个伪terminal:
此伪terminal对应着pts4.
两个终端间传输信息:
/dev/tty: 就是当前使用的终端。设备号:5:0
tty可以是pts3,也可以是tty1。
控制台终端:
tty0:代表当前使用的控制台终端。设备号4:0.
Ubuntu下:图像界面下通过Ctrl + Alt + F1—F6 可以切换到tty1到tty6。Ctrl + Alt + F7切会图像界面。
Pts3 发送到tty1。
/dev/console是指向当前console。设备号5:1
----------------------------------------------------------------------------------------------------------
Tty核心:
structtty_driver *alloc_tty_driver(int lines)
{
structtty_driver *driver;
driver = kzalloc(sizeof(structtty_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;
}
//分配一个结构体tty_driver,初始化其magic、num。
inttty_register_driver(structtty_driver *driver)
{
if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
}
if (!driver->major) {
error = alloc_chrdev_region(&dev, driver->minor_start,
driver->num, driver->name);
} else {
dev = MKDEV(driver->major, driver->minor_start);
error = register_chrdev_region(dev, driver->num, driver->name);
}
//如果没有指定主设备号,则分配并注册,否则直接注册设备号。
if (p) {
driver->ttys = (structtty_struct **)p;
driver->termios = (structktermios **)(p + driver->num);
} 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);
//注册字符设备。
mutex_lock(&tty_mutex);
list_add(&driver->tty_drivers, &tty_drivers);
//此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);
//如果没有指定TTY_DRIVER_DYNAMIC_DEV,则注册tty设备(在sysfs上建立相应的文件)
}
}
proc_tty_register_driver(driver);
driver->flags |= TTY_DRIVER_INSTALLED;
//标记为已安装
return 0;
}
structtty_struct {
structtty_driver *driver;
structtty_ldisc *ldisc;
}
下面
分析
定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析
一下tty_fops:
staticconststructfile_operationstty_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_write -->do_tty_write -->tty->ldisc->ops->write
tty_read -->tty->ldisc ->ops->read
tty ->ldisc在哪里赋值?在tty_open里。
tty_open -->tty_init_dev -->initialize_tty_struct -->tty_ldisc_init
在initialize_tty_struct里还有:tty->ops = driver->ops;
----------------------------------------------------------------------------------------------------------
Tty线路规程:
voidtty_ldisc_init(structtty_struct *tty)
{
structtty_ldisc *ld = tty_ldisc_get(N_TTY);
//ldops = tty_ldiscs[disc];ld->ops = ldops;
tty_ldisc_assign(tty, ld);
//tty->ldisc = ld;
}
全局变量tty_ldiscs在哪里赋值?
start_kernel -->console_init -->tty_ldisc_begin -->tty_register_ldisc -->
tty_ldiscs[disc] =tty_ldisc_N_TTY
structtty_ldisc_opstty_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
};
n_tty_write -->tty->ops->write即(driver->ops->write)
n_tty_read没有直接掉用下一层的read,使用了tty->read_buf进行交互。
----------------------------------------------------------------------------------------------------------
Tty驱动程序:
structuart_driver {
structuart_state *state;
structtty_driver *tty_driver;
};
uart_register_driver -->tty_register_driver
intuart_register_driver(structuart_driver *drv)
{
drv->state = kzalloc(sizeof(structuart_state) * drv->nr, GFP_KERNEL);
//分配uart_state,一个端口对应一个
normal = alloc_tty_driver(drv->nr);
//分配tty_driver
drv->tty_driver = normal;
normal->owner = drv->owner;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start = drv->minor;
normal->type = TTY_DRIVER_TYPE_SERIAL;
normal->subtype = SERIAL_TYPE_NORMAL;
normal->init_termios = tty_std_termios;
//安全设置值,参考ddr3 P542 termios结构。
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv;
tty_set_operations(normal, &uart_ops);
//里面执行了uart_driver ->tty_driver->ops =uart_ops
* initialise the UART state(s).
//初始化所有端口uart_state
for (i = 0; i
nr; i++) {
structuart_state *state = drv->state + i;
structtty_port *port = &state->port;
tty_port_init(port);
port->ops = &uart_port_ops;
port->close_delay = 500; /* .5 seconds */
port->closing_wait = 30000; /* 30 seconds */
tasklet_init(&state->tlet, uart_tasklet_action,
(unsigned long)state);
}
retval = tty_register_driver(normal);
}
uart_add_one_port注册一个port,核心作用可以看做赋值了uart_port,其中包含了最低层的操作函数集。
structuart_port {
conststructuart_ops *ops;
};
uart_write -->uart_start -->__uart_start -->port->ops->start_tx
----------------------------------------------------------------------------------------------------------
TI Omap分析:
dra6xx_init–>omap_serial_init -->omap_serial_init_port
omap_serial_init_port初始化了uart,并赋值到oh->dev_attr。注册了“omap_uart”设备。
在serial_omap_init注册了其驱动:
staticint __initserial_omap_init(void)
{
int ret;
ret =uart_register_driver(&serial_omap_reg);
ret =platform_driver_register(&serial_omap_driver);
}
匹配成功调用serial_omap_probe,serial_omap_probe调用uart_add_one_port注册了端口。端口的操作函数集:serial_omap_pops。
读:
serial_omap_irq -->receive_chars -->tty_flip_buffer_push -->flush_to_ldisc -->disc->ops->receive_buf
----------------------------------------------------------------------------------------------------------
补充知识:
线路规程是可以设置的,默认的就是tty_ldisc_N_TTY,下面看一个新的:
先注册一个线路规程:(就是加到全局变量tty_ldiscs里)
staticint __initserport_init(void)
{
intretval;
retval = tty_register_ldisc(N_MOUSE, &serport_ldisc);
}
//就是tty_ldiscs[N_MOUSE] = serport_ldisc;
应用层通过:
ldisc = N_MOUSE;
if (ioctl(fd, TIOCSETD, &ldisc))
对应内核会调用tty_ioctl -->tiocsetd -->tty_set_ldisc
这样有什么用呢?
例如串口触摸屏: UART -->serport_ldisc(修改线路规程) --> SERIO --> INPUT DEVICE --> EVENT
具体参考代码:serport.c (注册新的线路规程)、touchit213.c(注册serio驱动)
需要注意的:
Serio设备是在serport_ldisc_read里面注册的,也就是在使用之前,必须先调用read一下,才会注册设备,然后serio才会匹配成功,匹配成功之后会调用其connect函数。在connect里面注册input device,没有指定输入设备event,使用通用的eventX.
读操作:
serial_omap_irq -->receive_chars -->tty_flip_buffer_push -->flush_to_ldisc -->disc->ops->receive_buf(即serport_ldisc_receive)-->直接调用serio_interrupt -->serio->drv->interrupt(即touchit213_interrupt) -->input_sync
(touchit213_interrupt只是假设的)
写操作:
直接调用serport_serio_writ -->serport->tty->ops->write