tty设备驱动数据流动分析

在看过很多大神的文章,加上自己研究了一些tty驱动代码之后,算是对tty驱动有了一定的认识。就像研究tcp/ip协议栈一样,我喜欢从数据流动的角度来分析,下面就介绍下我对tty设备中数据流动的薄见。

先应用一张别人的数据流通图,如下,个人觉得这张图将tty设备驱动的大致轮廓描述的比较清晰,数据的流动也比较清晰的展现了。

http://blog.chinaunix.net/photo/94212_100730152614.png


用户在用户空间使用open系统调用打开了一个tty设备之后,得到设备文件名,然后可以通过write和read系统调用来进行读写数据。我们注意到在tty线路规程ldisc->ops中油两个read函数分别为read和receive_read函数,两个函数分别是从上和从下调用的。什么意思呢,就是说read是提供给上层代码调用,receive_read是提供给底层代码调用的,下面会有介绍,我们首先来介绍read的数据流动过程:

如果对tty驱动有所了解的应该知道,在tty设备驱动里只有write函数,并没有read函数。在tty驱动中,当硬件产生一个数据中断之后,也就是硬件收到数据后会发出一个中断信号,系统捕捉到这个中断信号之后调用tty驱动中的中断处理函数,将硬件fifo队列的数据保存到tty_struct临时缓存当中(实际为tty_struct中的struct tty_headbuf buf成员,其head指针指向临时缓存的链表头,tail指向待操作临时链表),此处用到的函数是tty_insert_filp_char。下面是涉及到的几个数据结构:

struct tty_struct {
int magic;
struct kref kref;
struct device *dev;
struct tty_driver *driver;
const struct tty_operations *ops;
int index;
....................

struct tty_struct *link;
struct fasync_struct *fasync;
struct tty_bufhead buf;/* Locked internally */
int alt_speed;/* For magic substitution of 38400 bps */
wait_queue_head_t write_wait;
wait_queue_head_t read_wait;
struct work_struct hangup_work;
void *disc_data;
void *driver_data;
struct list_head tty_files;


#define N_TTY_BUF_SIZE 4096


/*
* The following is data for the N_TTY line discipline.  For
* historical reasons, this is included in the tty structure.
* Mostly locked by the BKL.
*/
unsigned int column;
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
<span style="color:#FF0000;">        raw:在n_tty_set_termios函数中根据tty_driver->termios.c_iflag等值初始化
        real_raw:当tty_driver->flags中设置为TTY_DRIVER_REAL_RAW时置1
        icanon:在tty_open函数中会调用到n_tty_set_termios,然后根据tty_driver->tty_drv->init_termios.c_lflag的值初始化icanon
</span> unsigned char echo_overrun:1;
unsigned short minimum_to_wake;
unsigned long overrun_time;
int num_overrun;
unsigned long process_char_map[256/(8*sizeof(unsigned long))];
char *read_buf;
int read_head;
int read_tail;
int read_cnt;
..........
/* If the tty has a pending do_SAK, queue it here - akpm */
struct work_struct SAK_work;
struct tty_port *port;
};

struct tty_bufhead{

struct delayed_work work;

spinlock_t lock;

struct tty_buffer *head;

struct tty_buffer *tail;

struct tty_buffer *free;

int memory_used;

};

struct tty_buffer{

struct tty_buffer *next;

char *char_buf_ptr;

unsigned char *flag_buf_ptr;

int used;

int size;

int commit;

int read;

/*Data points here*/

unsigned long data[0];

};


将数据拷贝到临时缓存之后,紧接着调用tty_filp_buffer_push函数,将临时缓存中的数据拷贝到tty_struct的成员变量tty->read_buf,用户便可以读取数据了。注意在tty_filp_buffer_push函数中会调用flush_to_ldisc函数完成数据到read_buf的拷贝,在flush_to_ldisc中会调用到上面提到的ldisc->ops->receive_buf函数完成实际的工作。

正如这位老兄在这里所提问的:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3761128

他分析所列出的这一串函数调用:serial_in()-->serial8250_handle_port()--->receive chars()--->uart_insert_char()---->tty_insert_flip_char()-->tty_flip_buffer_push()-->flush_to_ldisc()--->n_tty_receive_buf()--->n_tty_receive_char()

这一串函数的调用正是上面我们分析的结果,是将数据从底层硬件收上来传递到线路规程这一层的过程。在这一过程中当然不会调用到tty_read函数,这是数据从下往上的过程。当数据送达到线路规程的缓存中之后,用户程序调用的read函数,会调用到tty_read,在tty_read函数中调用ldisc->ops->read函数,将数据从线路规程这一层的缓存中取走。

http://www.amobbs.com/thread-5521150-1-1.html

http://www.linuxidc.com/Linux/2011-02/32060p4.htm

http://blog.chinaunix.net/uid-21273878-id-1828727.html

http://blog.csdn.net/tommy_wxie/article/details/17003997

http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3761128

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值