Linux设备模型之终端设备(tty)驱动架构分析*****************************************************本文系本站原创,欢迎转载!转载请注明出处:这里感谢:晓刚的分析和指点,及网上的所有串口资源*****************************************************一:前言终端设备在Linux系统中,终端是一种字符型设备,它有多种类型,通常使用tty来简称各种类型的终端设备。tty是Teletype的缩写,Teletype是最早出现的一种终端设备,很像电传打字机,是由Teletype公司生产的。Linux中包含如下几类终端设备:1.串行端口终端(/dev/ttySn)串行端口终端(SerialPortTerminal)是使用计算机串行端口连接的终端设备。2.伪终端(/dev/pty/)伪终端(PseudoTerminal)是成对的逻辑终端设备3.控制台终端(/dev/ttyn,/dev/console)二:终端设备驱动结构1.Tty架构如下所示:Tty_core:Tty核心层Linediscipline:是线路规程的意思(链路层)。正如它的名字一样,它表示的是这条终端”线程”的输入与输出规范设置.主要用来进行输入/输出数据的预处理Tty_driver:Tty_driver就是终端对应的驱动了。它将字符转换成终端可以理解的字串.将其传给终端设备。tty设备发送数据的流程为:tty_core从一个用户获得将要发送给一个tty设备的数据,tty_core将数据传递给Linediscipline处理,接着数据被传递到tty_driver,tty驱动将数据转换为可以发送给硬件的格式tty设备接受数据的流程为:从tty硬件接收到得数据向上交给tty_driver,进入Linediscipline处理,再进入tty_core,在这里它被一个用户获取尽管大多数时候tty_core和tty_driver之间的数据传输会经历Linediscipline的转换,但是tty_core和tty_driver之间也可以直接传输数据2.tty主要源文件关系及数据流向:图二:tty主要源文件关系及数据流向上图显示了与tty相关的主要源文件及数据流向。tty_io.c:tty_io.c定义了tty设备通用的的file_operation结构体,并实现了接口函数tty_register_driver()用于注册tty设备,它会利用fs/char_dev.c提供的接口函数注册字符设备。tty_io.c也提供了tty_register_ldisc()接口函数,用于注册线路规程。xxx_tty.c:与具体设备对应的tty驱动(xxx_tty.c)将实现tty_driver结构体中的成员函数。Ntty.c:ntty.c文件则实现了tty_disc结构体中的成员特定tty设备的主体工作是填充tty_driver结构体中的成员,实现其中的成员函数,tty_driver结构体的源码(linux/driver/char/tty_driver.h)如下:structtty_driver{intmagic;/*magicnumberforthisstructure*/structcdevcdev;structmodule*owner;constchar*driver_name;constchar*devfs_name;constchar*name;intname_base;/*offsetofprintedname*/intmajor;/*majordevicenumber*/intminor_start;/*startofminordevicenumber*/intminor_num;/*numberof*possible*devices*/intnum;/*numberofdevicesallocated*/shorttype;/*typeofttydriver*/shortsubtype;/*subtypeofttydriver*/structtermiosinit_termios;/*Initialtermios*/intflags;/*ttydriverflags*/intrefcount;/*forloadablettydrivers*/structproc_dir_entry*proc_entry;/*/procfsentry*/structtty_driver*other;/*onlyusedforthePTYdriver*//**Pointertothettydatastructures*/structtty_struct**ttys;structtermios**termios;structtermios**termios_locked;void*driver_state;/*onlyusedforthePTYdriver*//**Interfaceroutinesfromtheupperttylayertothetty*driver.Willbereplacedwithstructtty_operations.*/int(*open)(structtty_struct*tty,structfile*filp);void(*close)(structtty_struct*tty,structfile*filp);int(*write)(structtty_struct*tty,constunsignedchar*buf,intcount);void(*put_char)(structtty_struct*tty,unsignedcharch);void(*flush_chars)(structtty_struct*tty);int(*write_room)(structtty_struct*tty);int(*chars_in_buffer)(structtty_struct*tty);int(*ioctl)(structtty_struct*tty,structfile*file,unsignedintcmd,unsignedlongarg);void(*set_termios)(structtty_struct*tty,structtermios*old);void(*throttle)(structtty_struct*tty);void(*unthrottle)(structtty_struct*tty);void(*stop)(structtty_struct*tty);void(*start)(structtty_struct*tty);void(*hangup)(structtty_struct*tty);void(*break_ctl)(structtty_struct*tty,intstate);void(*flush_buffer)(structtty_struct*tty);void(*set_ldisc)(structtty_struct*tty);void(*wait_until_sent)(structtty_struct*tty,inttimeout);void(*send_xchar)(structtty_struct*tty,charch);int(*read_proc)(char*page,char**start,off_toff,intcount,int*eof,void*data);int(*write_proc)(structfile*file,constchar__user*buffer,unsignedlongcount,void*data);int(*tiocmget)(structtty_struct*tty,structfile*file);int(*tiocmset)(structtty_struct*tty,structfile*file,unsignedintset,unsignedintclear);structlist_headtty_drivers;};三:tty驱动接口分析1.分配tty驱动ttydriver的所有操作都包含在tty_driver中。内核即供了一个名叫alloc_tty_driver()来分配这个tty_driver。当然我们也可以在自己的驱动中将它定义成一个静态的结构。对tty_driver进行一些必要的初始化之后,调用tty_register_driver()将其注册.alloc_tty_driver()接口代码(Linux/driver/char/tty_io.c)如下所示:structtty_driver*alloc_tty_driver(intlines){structtty_driver*driver;driver=kmalloc(sizeof(structtty_driver),GFP_KERNEL);if(driver){memset(driver,0,sizeof(structtty_driver));driver-magic=TTY_DRIVER_MAGIC;driver-num=lines;/*laterwe'llmoveallocationoftableshere*/}returndriver;}这个函数只有一个参数。这个参数的含义为line的个数。也即次设备号的个数。注意每个设备文件都会对应一个line.在这个接口里为tty_driver分配内存,然后将driver-magic与driver-num初始化之后就返回了.其中driver-magic为tty_driver这个结构体的“幻数”,设为TTY_DRIVER_MAGIC,在这里被初始化。2.注册tty驱动在这里,tty_register_driver()用来注册一个tty_driver,源码如下(Linux/driver/char/tty_io.c):inttty_register_driver(structtty_driver*driver){interror;inti;dev_tdev;void**p=NULL;if(driver-flags&TTY_DRIVER_INSTALLED)return0;//TTY_DRIVER_DEVPTS_MEM:使用devpts进行动态内存映射if(!(driver-flags&TTY_DRIVER_DEVPTS_MEM)){p=kmalloc(driver-num*3*sizeof(void*),GFP_KERNEL);if(!p)return-ENOMEM;memset(p,0,driver-num*3*sizeof(void*));}///如果没有指定driver-major,注册字符设备号if(!driver-major){error=alloc_chrdev_region(&dev,driver-minor_start,driver-num,(char*)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,(char*)driver-name);}if(error0){kfree(p);returnerror;}if(p){driver-ttys=(structtty_struct**)p;driver-termios=(structtermios**)(p+driver-num);driver-termios_locked=(structtermios**)(p+