本文源自:http://blog.chinaunix.net/uid-21273878-id-1828727.html
tty设备的数据流通图:
tty设备有三层:tty核心,tty线路规程,tty驱动。
我们写驱动还是只负责最底层的tty驱动。 线路规程的设置也是在底层的tty驱动。
tty核心是封装好的。
来看一下tty设备的操作函数:
struct tty_operations { int ( * open ) ( struct tty_struct * tty, struct file * filp) ; void ( * close ) ( struct tty_struct * tty, struct file * filp) ; int ( * write ) ( struct tty_struct * tty, const unsigned char * buf, int count ) ; void ( * put_char) ( struct tty_struct * tty, unsigned char ch) ; void ( * flush_chars) ( struct tty_struct * tty) ; 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) ; void ( * unthrottle) ( struct tty_struct * tty) ; void ( * stop) ( struct tty_struct * tty) ; void ( * start) ( struct tty_struct * tty) ; void ( * hangup) ( struct tty_struct * tty) ; void ( * break_ctl) ( struct tty_struct * tty, int state) ; void ( * flush_buffer) ( struct tty_struct * tty) ; void ( * set_ldisc) ( struct tty_struct * tty) ; void ( * wait_until_sent) ( struct tty_struct * tty, int timeout) ; void ( * send_xchar) ( struct tty_struct * tty, char ch) ; int ( * read_proc) ( char * page, char * * start, off_t off, int count , int * eof , void * data) ; int ( * write_proc) ( struct file * file , const char __user * buffer, unsigned long count , void * data) ; int ( * tiocmget) ( struct tty_struct * tty, struct file * file ) ; int ( * tiocmset) ( struct tty_struct * tty, struct file * file , unsigned int set , unsigned int clear) ; } ;
tty设备没有read函数, 是因为大部分tty的输入设备和输出设备不一样。 例如我们的虚拟终端设备,它的输入是键盘,输出是显示器。
由于这样的原因, tty的驱动层和tty的线路规程层都有一个缓冲区。
tty驱动层的缓冲区用来保存硬件发过来的数据。 在驱动程序里使用 tty_insert_flip_string 函数可以实现将硬件的数据存入到驱动层的缓冲区。
其实一个缓冲区就够了,为什么线路规程层还是有一个缓冲区呢?
那是因为tty核心无法直接读取驱动层的缓冲区的数据。 tty核心读不到数据,用户也就无法获取数据。 用户的read函数只能从tty核心读取数据。 而tty核心只能从tty线路规程层的缓冲区读取数据。
因为是层层读写的关系, 所以tty线路规程也是需要一个缓冲区的。
在驱动程序里使用 tty_flip_buffer_push() 函数将tty驱动层缓冲区的数据推到tty线路规程层的缓冲区。 这样就完成了数据的流通。
因为全是缓冲区操作,所以需要两个进程: 写数据进程和读数据进程。
如果缓冲区内没有数据,运行读进程的话,tty核心就会把读进程加入到等待队列。
完整的代码如下:
/* * Copyright (c) 2009-~ Lan Peng * * This source code is released for free distribution under the terms of the * GNU General Public License * * Author: Lan Peng<lanpeng722@gmail.com> * Created Time: 2010年07月26日 星期一 10时12分32秒 * File Name: tty_lan.c * * Description: */ # include < linux/ module. h> # include < linux/ kernel. h> # include < linux/ init. h> # include < linux/ cdev. h> # include < linux/ tty. h> # include < linux/ fs. h> # include < linux/ tty_driver. h> # include < linux/ tty_flip. h> # include < linux/ ioport. h> # include < linux/ serial_reg. h> MODULE_LICENSE( "GPL" ) ; MODULE_AUTHOR( "lan" ) ; # define TTY_LAN_MINORS_NUM 5 # define TTY_LAN_MAJOR 202 static int open_count = 0; static unsigned char * to; static struct tty_driver * tty_lan_driver; static struct tty_struct * tty_lan_struct; static int tty_lan_open( struct tty_struct * tty, struct file * filp) ; static void tty_lan_close( struct tty_struct * tty, struct file * filp) ; static int tty_lan_write( struct tty_struct * tty, const unsigned char * buffer, int count ) ; static int tty_lan_write_room( struct tty_struct * tty) ; static void tty_lan_set_termios( struct tty_struct * tty, struct ktermios * old) ; static int tty_lan_put_char( struct tty_struct * tty, unsigned char ch) ; static struct tty_operations tty_lan_ops = { . open = tty_lan_open, . close = tty_lan_close, . write = tty_lan_write, . put_char = tty_lan_put_char, . write_room = tty_lan_write_room, . set_termios = tty_lan_set_termios, } ; static int __init tty_lan_init( void ) { int i; int retval; tty_lan_driver = alloc_tty_driver( TTY_LAN_MINORS_NUM) ; if ( ! tty_lan_driver) return - ENOMEM; tty_lan_driver- > owner = THIS_MODULE; tty_lan_driver- > driver_name = "tty_lan" ; tty_lan_driver- > name = "ttty_lan" ; tty_lan_driver- > major = TTY_LAN_MAJOR, tty_lan_driver- > minor_start = 0; tty_lan_driver- > type = TTY_DRIVER_TYPE_SERIAL; tty_lan_driver- > subtype = SERIAL_TYPE_NORMAL; tty_lan_driver- > flags = TTY_DRIVER_REAL_RAW; tty_lan_driver- > init_termios = tty_std_termios; tty_lan_driver- > init_termios. c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations( tty_lan_driver, & tty_lan_ops) ; retval = tty_register_driver( tty_lan_driver) ; if ( retval) { printk( KERN_ERR"Failed to register tty_lan_driver!\n" ) ; put_tty_driver( tty_lan_driver) ; return retval; } for ( i = 0; i < TTY_LAN_MINORS_NUM; i+ + ) tty_register_device( tty_lan_driver, i, NULL ) ; return 0; } static int tty_lan_open( struct tty_struct * tty, struct file * filp) { if ( open_count = = 0) { printk( "Open OK!\n" ) ; } tty_lan_struct = kmalloc( sizeof ( struct tty_struct) , GFP_KERNEL) ; tty- > low_latency = 1; tty_lan_struct = tty; return 0; } static void tty_lan_close( struct tty_struct * tty, struct file * filp) { printk( "ClOSE OK!\n" ) ; kfree( tty_lan_struct) ; return ; } static int tty_lan_write( struct tty_struct * tty, const unsigned char * buffer, int count ) { int i; int retval = count ; tty = tty_lan_struct; printk( KERN_DEBUG "%s - \n" , __FUNCTION__ ) ; printk( "count :%d\n" , count ) ; printk( "user write: %s " , buffer) ; printk( "\n" ) ; tty_insert_flip_string( tty, buffer, count ) ; tty_flip_buffer_push( tty) ; return retval; } static int tty_lan_put_char( struct tty_struct * tty, unsigned char ch) { printk( "put_char :%c\n" , ch) ; return 0; } static int tty_lan_write_room( struct tty_struct * tty) { int room; room = 255; return room; } static void tty_lan_set_termios( struct tty_struct * tty, struct ktermios * old) { tty = tty_lan_struct; if ( tty- > termios- > c_cflag = = old- > c_cflag) { printk( "Nothing to change!\n" ) ; return ; } printk( "There is something to Change............\n" ) ; return ; } static void __exit tty_lan_exit( void ) { int i; for ( i = 0; i < TTY_LAN_MINORS_NUM; i+ + ) tty_unregister_device( tty_lan_driver, i) ; tty_unregister_driver( tty_lan_driver) ; } module_init( tty_lan_init) ; module_exit( tty_lan_exit) ;
将此模块编译后加入到内核,再运行以下两个程序:
receive.c : 读取tty设备缓冲区的数据。
send.c : 将数据写入到tty设备驱动。
首先运行 receive
$ sudo ./receive
open /dev/ttty_lan0: Success
ready for receiving data...
The data received is:
等待数据状态。
再打开另一个终端,运行 send
$ sudo ./send
open /dev/ttty_lan0: Success
ready for sending data...
the number of char sent is 35
$
这个程序运行结束后,我们来看一下receive是否接受到了数据:
$sudo ./receive
open /dev/ttty_lan0: Success
ready for receiving data...
The data received is:
Hello,this is a Serial_Port test!
$
说明接受到了缓冲区的数据。
具体测试程序的代码如下:
send.c:
/******************************************************* * File Name: send.c * Description: send data to serial_Port * Date: *******************************************************/ /******************头文件定义******************/ # include < stdio. h> # include < string . h> # include < malloc . h> # include < sys/ types. h> # include < sys/ stat. h> # include < fcntl. h> # include < unistd. h> # include < termios. h> # define max_buffer_size 100 /*定义缓冲区最大宽度*/ /*******************************************/ int fd; /*定义设备文件描述符*/ int flag_close; int open_serial( int k) { if ( k= = 0) /*tty设备选择*/ { fd = open ( "/dev/ttty_lan0" , O_RDWR| O_NOCTTY) ; /*读写方式打开设备*/ perror ( "open /dev/ttty_lan0" ) ; } else { fd = open ( "/dev/ttty_lan1" , O_RDWR| O_NOCTTY) ; perror ( "open /dev/ttty_lan1" ) ; } if ( fd = = - 1) /*打开失败*/ return - 1; else return 0; } /********************************************************************/ int main( int argc, char * argv[ ] ) { char sbuf[ ] = { "Hello,this is a Serial_Port test!\n" } ; /*待发送的内容,以\n为结 束标志*/ int retv; struct termios opt; int length= sizeof ( sbuf) ; /*发送缓冲区数据宽度*/ /*******************************************************************/ open_serial( 0) ; /*打开设备1*/ /*******************************************************************/ printf ( "ready for sending data...\n" ) ; /*准备开始发送数据*/ tcgetattr( fd, & opt) ; cfmakeraw( & opt) ; /*****************************************************************/ //cfsetispeed(&opt,B9600); /*波特率设置为9600bps*/ //cfsetospeed(&opt,B9600); /*******************************************************************/ tcsetattr( fd, TCSANOW, & opt) ; retv= write ( fd, sbuf, length) ; /*接收数据*/ if ( retv= = - 1) { perror ( "write" ) ; } printf ( "the number of char sent is %d\n" , retv) ; flag_close = close ( fd) ; if ( flag_close = = - 1) /*判断是否成功关闭文件*/ printf ( "Close the Device failur!\n" ) ; return 0; } /****************************结束***********************************/
receive.c:
/******************************************************* *ilename:receive.c * Description:Receive data from Serial_Port * Date: *******************************************************/ /*********************头文件定义***********************/ # include < stdio. h> # include < string . h> # include < malloc . h> # include < sys/ types. h> # include < sys/ stat. h> # include < fcntl. h> # include < unistd. h> # include < termios. h> # include "math.h" # define max_buffer_size 100 /*定义缓冲区最大宽度*/ /*********************************************************/ int fd, s; int open_serial( int k) { if ( k= = 0) /*tty设备选择*/ { fd = open ( "/dev/ttty_lan0" , O_RDWR| O_NOCTTY) ; /*读写方式打开设备*/ perror ( "open /dev/ttty_lan0" ) ; } else { fd = open ( "/dev/ttty_lan1" , O_RDWR| O_NOCTTY) ; perror ( "open /dev/ttty_lan1" ) ; } if ( fd = = - 1) /*打开失败*/ return - 1; else return 0; } /********************************************************************/ int main( ) { char hd[ max_buffer_size] , * rbuf; /*定义接收缓冲区*/ int flag_close, retv, i, ncount= 0; struct termios opt; /*******************************************************************/ open_serial( 0) ; /*打开设备1*/ /*******************************************************************/ tcgetattr( fd, & opt) ; cfmakeraw( & opt) ; /*****************************************************************/ //cfsetispeed( & opt, B9600) ; /*波特率设置为9600bps*/ //cfsetospeed( & opt, B9600) ; /*******************************************************************/ tcsetattr( fd, TCSANOW, & opt) ; rbuf= hd; /*数据保存*/ printf ( "ready for receiving data...\n" ) ; retv= read ( fd, rbuf, 1) ; /*接收数据*/ if ( retv= = - 1) { perror ( "read" ) ; /*读状态标志判断*/ } /*************************开始接收数据******************************/ while ( * rbuf!='\n' ) /*判断数据是否接收完毕*/ { ncount+ = 1; rbuf+ + ; retv= read ( fd, rbuf, 1) ; if ( retv= = - 1) { perror ( "read" ) ; } } /*******************************************************************/ printf ( "The data received is:\n" ) ; /*输出接收到的数据*/ for ( i= 0; i< ncount; i+ + ) { printf ( "%c" , hd[ i] ) ; } printf ( "\n" ) ; flag_close = close ( fd) ; if ( flag_close = = - 1) /*判断是否成功关闭文件*/ printf ( "Close the Device failur!\n" ) ; return 0; } /****************************结束***********************************/