打通linux的tty驱动的数据链路
据是怎么来的呢?猜想,那肯定是tty_driver干的事了。
tty_ioctl:
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
……
switch(cmd) {
case… …… :
………
}
}
就是根据cmd的值进行相关操作,有对线路规程操作的,有直接通过tty_driver操作的。
三、TTY驱动层分析
接下来看,TTY驱动层是怎样的:
TTY驱动层是根据不同的硬件操作来完成相应的操作,这里我们以串口为例。
串口作为一个标准的设备,把共性的分离出来,就成了uart层,特性成了serial层。
主要是serial层作为一个驱动模块加载。以8250.c为例:
static int __init serial8250_init(void)
{
………
serial8250_reg.nr= UART_NR;
ret= uart_register_driver(serial8250_reg);
………
serial8250_register_ports(serial8250_reg,serial8250_isa_devs->dev);
………
}
#define UART_NR CONFIG_SERIAL_8250_NR_UARTS
CONFIG_SERIAL_8250_NR_UARTS是在配置内核的时候定义的,表示支持串口的个数。
static struct uart_driver serial8250_reg = {
。owner =THIS_MODULE,
。driver_name =serial,
。dev_name =ttyS,
。major =TTY_MAJOR,
。minor =64,
。cons =SERIAL8250_CONSOLE,
};
在驱动层里有几个重要的数据结构:
structuart_driver;
structuart_state ;
structuart_port;
structtty_driver;
structtty_port;
实际上,理清了这几个结构体的关系,也就理清了TTY驱动层。
uart_register_driver:
这个函数主要是向TTY核心层注册一个TTY驱动:
retval= tty_register_driver(normal);
其中normal是tty_driver.
另外,还会对tty_driver和uart_driver之间进行某些赋值和指针连接。我们最关心的是,给tty_driver初始化了操作函数uart_ops,这样,在tty核心层就可以通过uart_ops来对UART层进行操作。
serial8250_register_ports:
最重要的两个函数:serial8250_isa_init_ports和uart_add_one_port
serial8250_isa_init_ports主要的工作是初始化uart_8250_port:开启定时器和初始化uart_port.
uart_add_one_port顾名思议,就是为uart_driver增加一个端口,在uart_driver里的state指向NR个slot, 然后,这个函数的主要工作就是为slot增加一个port.这样,uart_driver就可以通过port对ops操作函数集进行最底层的操作。
现在来分析下连接部分,也就是tty_driver如何工作,如何连接tty核心层(或者ldisc层)和串口层uart_port.关于操作部分主要是uart_ops.
uart_open:
staticint uart_open(struct tty_struct *tty, struct file *filp)
{
………
retval= uart_startup(tty, state, 0);
……
}
staticint uart_startup(struct tty_struct *tty, struct uart_state *state,int init_hw)
{
……
retval= uport->ops->startup(uport);
………
}
调用了uart_port的操作函数ops的startup,在这个函数里作了一些串口初始化的工作,其中有申请接收数据中断或建立超时轮询处理。
在startup里面申请了接收数据中断,那么这个中断服务程序就跟读操作密切相关了,从tty核心层的读操作可知,接收到的数据一定是传送到read_buf中的。现在来看是中断服务程序。
调用receive_chars来接收数据,在receive_chars中,出现了两个传输数据的函数:
tty_insert_flip_char和tty_flip_buffer_push.
static inline int tty_insert_flip_char(struct tty_struct *tty,
unsigned char ch, char flag)
{
struct tty_buffer *tb = tty->buf.tail;
if(tb tb->used tb->size) {
tb->flag_buf_ptr[tb->used]= flag;
tb->char_buf_ptr[tb->used++]= ch;
return1;
}
return tty_insert_flip_string_flags(tty, ch, flag, 1);
}
当当前的tty_buffer空间不够时调用tty_insert_flip_string_flags,在这个函数里会去查找下一个tty_buffer,并将数据放到下一个tty_buffer的char_buf_ptr里。
那么char_buf_ptr的数据怎样与线路规程中的read_buf关联的呢,我们看,在初始化tty_buffer的时候,也就是在tty_buffer_init函数中:
void tty_buffer_init(struct tty_struct *tty)
{
spin_lock_init(tty->buf.lock);
tty->buf.head= NULL;
tty->buf.tail= NULL;
tty->buf.free= NULL;
tty->buf.memory_used= 0;
INIT_DELAYED_WORK(tty->buf.work,flush_to_ldis
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)