打通Linux的TTY驱动的数据链路

2014-11-24 09:56:47 · 作者: · 浏览: 1

一、首先把tty驱动在linux中的分层结构理清楚:




自上而下分为TTY核心层、TTY线路规程、TTY驱动。


二、TTY核心层与线路规程层分析


用户空间的程序直接对tty核心层进行读写等相关操作,在tty_io.c中:


int__init tty_init(void)



cdev_init(&tty_cdev,&tty_fops);


if(cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||


register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty")< 0)


panic("Couldn'tregister /dev/tty driver\n");


device_create(tty_class,NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");


…...





以上的一段初始化代码可以获取以下信息:


注册了一个字符驱动,用户空间操作对应到tty_fops结构体里的函数:


staticconst struct file_operations tty_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,


};


对于字符设备驱动,我们知道,读写操作一一对应于fops




tty_open:


static int tty_open(struct inode *inode, struct file *filp)



int index;


dev_tdevice = inode->i_rdev;


structtty_driver *driver;


……


driver= get_tty_driver(device, &index);


……


tty= tty_init_dev(driver, index, 0);


……


retval= tty_add_file(tty, filp);


……


if(tty->ops->open)


retval= tty->ops->open(tty, filp);





get_tty_driver是根据设备号device,通过查找tty_drivers全局链表来查找tty_driver


tty_init_dev是初始化一个tty结构体:


tty->driver= driver;


tty->ops= driver->ops;


并建立线路规程:


ldops= tty_ldiscs[N_TTY];


ld->ops= ldops;


tty->ldisc= ld;




其实tty_ldiscs[N_TTY]console_init中确定,该函数在内核启动的时候调用。


tty_register_ldisc(N_TTY,&tty_ldisc_N_TTY);


则:tty_ldiscs[N_TTY]&tty_ldisc_N_TTY




struct tty_ldisc_ops tty_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


};


tty_add_file主要是将tty保存到file的私有变量private_data中。


tty->ops->open的调用,实则上就是应用driver->ops->open。这样,我们就从tty核心层到tty驱动层了。




tty_write:


static ssize_t tty_write(struct file *file, const char __user *buf,


size_t count, loff_t *ppos)


{


…...


ld= tty_ldisc_ref_wait(tty);


if(!ld->ops->write)


ret= -EIO;


else


ret= do_tty_write(ld->ops->write, tty, file, buf, count);


…...


}


从以上这个函数里,可以看到tty_write调用路线规程的write函数,所以,我们来看ldisc中的write函数是怎样的。经过一些操作后,最终调用:


tty->ops->flush_chars(tty);


tty->ops->write(tty,b, nr);