Framing Analysis of Uart Driver In Linux

Author:Visteonding

项目:Eagle2

平台:Imx6_solo/Linux

废话不说直接进入主题

一、Linux 下的tty/uart 架构

 

二、Uart 设备中重要的数据结构及其关联。

这里有两个关键三个关键的数据结构。uart_driveruart_portuart_status

uart_driver 相当于设备驱动,uart_port 相当于串口设备。在Eagle2/imx 中我们定义了五个串口设备分别是01234。(参考代码arch/arm/mach-mx6/eagle_mx6solo.c imx6q_add_imx_uart 这个函数被调用5次分别添加了5个串口设备,设备的id 分别是01234

 

我们在/driver/tty/serial/imx.c 中定义了uart_driver, driver的名字为ttymxc。然后我们调用uart_driver_register函数来注册一个uart_driver,因为之前我们在uart_driver中定义的drv->nr8,所以在这里我们就分配了8uart_state的存储空间给drv->state。然后我们对uart_driver 中的tty_driver进行初始化,然后调用tty_register_driver注册该tty_driver,并分配好了drv->nr个次设备号,这样我们就完成了字符设备驱动的注册,同时还调用list_add将该tty_driver加入tty_driver_list

然后我们会调用uart_add_one_port来完成一个uart设备的添加,应为我们要添加五个uart设备所以这个函数被调用五次uart_portid分别为platform_device id01234uart_add_one_port 函数的另外一个作用就是完成uart_port  uart_driver 的绑定。绑定的方法就是通过uart_state。我们通过id作为uart_state数组的索引来分配uart_stateuart_port,在uart_add_one_port中我们还会调用另外一个函数,tty_register_device,这里我们会把uart_portid作为此设备好来创建设备结点,这样我们就可以在/dev目录下看到如下设备结点:ttymxc0ttymxc1ttymxc2ttymxc3ttymxc4

另外三个比较重要的结构体是tty_drivertty_structtty_ldisc. 

之前我们利用uart_portid作为此设备号来创建设备结点。当我们打开该设备结点时就可以利用该备号作为索引来寻找对应的uart_port

在我们要向对应的uart_port写数据之前要经过tty_ldisc来对相应的数据进行格式化。如果我们想要我们的数据按照BT的规范来发送,那么我们可以再这里添加对应的线路规程(tty_ldisc(目前这部分工作还没有人来做,不知道是因为BT协议不是free或其它什么原因,不多ldis号已经给BT分配好了,如果可以移植进去或许还可以在GUN中留下名字,呵呵!)。目前我们用的线路规程就是将数据直接写入uart_port, ldis 号为N_TTY

这里如何将tty_driver  tty_ldisc 联系起来, tty_struct 起了很关键的作用。

首先必须明确一点概念tty_driver有很多uart_driver只是tty_driver的一种,我们自己也可以创建我们自己的tty_driver(类似于伪终端)。所以问题的关键点在于如何根据设备结点来找到uar porttty_driver。这里住设备号就起到了很关键的作用,我们利用主设备号来探测该结点对应的那类tty设备然后利用次设备号来索引对应的设备,这里还用到了Linux下双向联表的相关知识(这里暂不作介绍)。

当我们用主设备号索引到我们注册的tty_driver后,我们为其初始化tty_struct结构体,在对tty_struct结构体初始化的过程中我们会为其分配对应的线路规程(这里为N_tty)。同时把索引号index付给tty_struct结构体中的indextty->index=index,做这个工作的目的就是为了索引uart_driver中的uart_status已找到对应的uart_port

 

 

 

 

 

 

 

三、系统调用流程:

用户的数据首先经过系统调用到达内核。然后数据到达分配好的线路规程,然后在tty线路规程中会把协议好的数据利用tty_struct中的接口函数最终将数据传送到我们自己定义的函数startup。在startup函数中我们会直接和寄存器打交道。

 

这里我重点介绍一下如何添加一个线路规程,如果有机会可以将蓝牙协议移植到地层(难度很大)。

在添加一个线路规程之前首先要注册一个线路规程。接口函数为:

tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)

第一个参数是线路规程号,第二个参数对应线路规程的协议。目前采用的串口线路规程为N_TTY,线路规程的索引号为0。系统已经给蓝牙分配好了线路规程号N_HCI,索引号为13。所以我们可以如下注册蓝牙的线路规程。

tty_register_ldisc(N_HCI, &tty_ldisc_N_HCI),前提是我们必须定义好我们的协议tty_ldisc_N_HCI

注册好我们的线路规程号后,我们把该线路规程的地址赋给指向该指针的数组tty_ldisc[]N_HCI为对应的数组号。

注册完tty_ldisc后,下一步的工作就是把该线路规程分配给对tty_struct。实例代码如下

struct tty_ldisc *ld = tty_ldisc_get(N_HCI)//以线路号为索引,从tty_ldisc[]中获取线路规程。

tty_ldisc_assign(tty,ld) //把获取的线路规程分配给tty_struct

至此,我们就完成了蓝牙线路规程的注册和分配。

Email:dingyujieky@163.com