Linux 串口、usb转串口驱动分析(2-2) 【转】
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852
Linux 串口、usb转串口驱动分析2
内核版本:2.6.35.6 荣鹏140319
声明:图和个别段落(我做了小的修改)是直接从网上截取
目标:主要是想对Linux 串口、usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动、平台驱动等也不进行详细说明原理。
二、具体细节分析
先分析tty框架
tty在linux下属于字符设备驱动 tty层提供了一些数据结构和函数接口方便其他驱动注册上来,其中包括虚拟终端、串口终端、伪终端等
Tty核心部分在tty_io.c里面
第一步、内核默认的tty初始化部分
static int __init tty_class_init(void)
{
tty_class = class_create(THIS_MODULE, "tty");
if (IS_ERR(tty_class))
return PTR_ERR(tty_class);
tty_class->devnode = tty_devnode;
return 0;
}
postcore_initcall(tty_class_init);
上面代码创建了tty类,方便以后创建设备节点
然后是tty_init
tty_init函数负责初始化tty层,它是由chr_dev_init调用的(fs_initcall(chr_dev_init))
也就是说它属于字符设备一部分。
int __init tty_init(void)
{
cdev_init(&tty_cdev, &tty_fops);
if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
panic("Couldn't register /dev/tty driver\n");
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,
"tty");
cdev_init(&console_cdev, &console_fops);
if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
panic("Couldn't register /dev/console driver\n");
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
"console");
#ifdef CONFIG_VT
vty_init(&console_fops);
#endif
return 0;
}
注:个人认为上面的if判断写法不是很好,虽然是正确的
这里和我们最终关心的串口驱动没关系,但由此可以看出tty字符设备(/dev/tty)使用的主设备号是TTYAUX_MAJOR(5),次设备号为0,/dev/console使用的主设备号也是5,但次设备号为1,控制台的初始化console_init在这个函数之前会被调用(start_kernel),内核注释如下:
/*
* HACK ALERT! This is early. We're enabling the console before
* we've done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
console_init();
这里不跟进去分析了。
虚拟终端、控制台部分暂时忽略不管。
第二步:使用tty层提供的功能(我们只关心串口驱动,所以是serial核心层或者usb-serial核心层使用它们)
1)tty_register_driver 注册tty驱动
相关数据结构: struct tty_driver *driver 可以通过alloc_tty_driver分配
它主要做的事情:
1、创建一个字符设备,但是这个字符设备的操作集是tty层定义的tty_fops,之所以由tty层提供,是因为它要实现线路规划部分,数据流会由它转向线路规划部分中。
static const struct file_operations tty_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = tty_write,
.poll = tty_poll,
.unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
.open = tty_open,
.release = tty_release,
.fasync = tty_fasync,
};
这其实是起到一个桥接作用。后面再分析这点
2、将该驱动对象加入到全局的链表 这一步就是为了上面说的 桥接
2)tty_register_device注册tty设备,只需要指定对应的驱动对象和索引号即可
它主要做的事情:
创建一个字符设备到/dev下 设备号由驱动对应的设备号base+索引
下面以几个情景分析(这里只分析tty框架的处理,还没有和具体的驱动挂钩):
情景1:打开设备
在应用层open上文第二步中tty_register_device创建的设备,会经过vfs 最终到tty_init中注册的tty_fops操作集里的open
也就是tty_open
它会根据你打开的是/dev/tty 还是 /dev/console 或者是你自己定义的一个设备(比如串口设备)(这个是由你tty_register_driver注册是参数struct tty_driver *driver里面的major决定的)
这里假设打开的是自己定义的设备/dev/ttyS0,那么会通过
driver = get_tty_driver(device, &index);
获取,它其实是扫描全局链表,这个链表的建立是在第二步中第2小步说明部分完成的。
如果是第一次打开,那么会创建一个新的对象用来代表这个open及以后操作的上下文,即tty_struct,通过alloc_tty_struct分配的,它里面有相应的线路规划策略tty_ldisc_init,默认初始化为tty_ldisc_get(N_TTY)。 然后调用线路规划的open。tty_struct对象同时继承了driver的操作集tty_fops,它内部同时会分配并初始化ktermios对象tty_init_termios(tty)及在driver上登记driver->ttys[idx] = tty; 最后会调用驱动本身注册的open。
tty_struct对象会放到file的private_data,为以后操作做好准备。
情景2:从设备读数据
在应用层read上文第二步中tty_register_device创建的设备,会经过vfs 最终到tty_init中注册的tty_fops操作集里的read
也就是tty_read
tty设备没有read函数,是因为大部分tty的输入设备和输出设备不一样。例如我们的虚拟终端设备,它的输入是键盘,输出是显示器。
由于这样的原因,tty的驱动层和tty的线路规程层都有一个缓冲区。
tty驱动层的缓冲区用来保存硬件发过来的数据。在驱动程序里使用 tty_insert_flip_string 函数可以实现将硬件的数据存入到驱动层的缓冲区。
其实一个缓冲区就够了,为什么线路规程层还是有一个缓冲区呢?
那是因为tty核心无法直接读取驱动层的缓冲区的数据。tty核心读不到数据,用户也就无法获取数据。用户的read函数只能从tty核心读取数据。而tty核心只能从tty线路规程层的缓冲区读取数据。
因为是层层读写的关系,所以tty线路规程也是需要一个缓冲区的。
在驱动程序里使用 tty_flip_buffer_push() 函数将tty驱动层缓冲区的数据推到tty线路规程层的缓冲区。这样就完成了数据的流通。
因为全是缓冲区操作,所以需要两个进程:写数据进程和读数据进程。
如果缓冲区内没有数据,运行读进程的话,tty核心就会把读进程加入到等待队列。
tty_read的主要流程:
从上文分析的open函数所存储的private里面取出分配并初始化过的tty_struct对象
tty = (struct tty_struct *)file->private_data;
然后它会调用属于tty的线路规划里面的read
线路规划是通过tty_register_ldisc注册到一个全局数组里的
对应默认的线性规划是在文件tty_ldisc.c里面的
tty_ldisc_begin完成的,它是在console_init里调用的,也就是内核调用tty_init之前。
void tty_ldisc_begin(void)
{
/* Setup the default TTY line discipline. */
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
}
struct tty_ldisc_ops tty_ldisc_N_TTY = {
.magic = TTY_LDISC_MAGIC,
.name = "n_tty",
.open = n_tty_open,
.close = n_tty_close,
.flush_buffer = n_tty_flush_buffer,
.chars_in_buffer = n_tty_chars_in_buffer,
.read = n_tty_read,
.write = n_tty_write,
.ioctl = n_tty_ioctl,
.set_termios = n_tty_set_termios,
.poll = n_tty_poll,
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup
};
因此最终调用n_tty_read
它会根据是否有数据做不同的处理,如果有数据,则直接处理后返回,如果没有数据,那么就在等待队列上睡眠等待。
情景2:从设备写数据
在应用层write上文第二步中tty_register_device创建的设备,会经过vfs 最终到tty_init中注册的tty_fops操作集里的write
也就是tty_write
Write调用要简单很多。
它调用do_tty_write
它内部实际调用的是线路规划的n_tty_write,它当然会调用tty_struct 的write,也就是继承自tty驱动的write
c = tty->ops->write(tty, b, nr); 由驱动完成最终的操作硬件发送数据。
注意:这里描述的读、写是以终端io为例,如果是蓝牙、或者ppp这些网络io,read、write会通过网络协议栈,而不是这里的tty_read tty_write。
第二步、具体驱动部分分析
1、 serial核心层(tty驱动层实现)分析
2、 串口驱动分析(8250为例)
1、 usb-serial核心层(tty驱动层实现)分析
2、 usb转串口驱动分析(pl2303为例)
Linux 串口、usb转串口驱动分析(2-2) 【转】的更多相关文章
- linux下usb转串口驱动分析【转】
转自:http://blog.csdn.net/txxm520/article/details/8934706 首先说一下linux的风格,个人理解 1. linux大小结构体其实是面向对象的方法,( ...
- Linux 串口、usb转串口驱动分析(2-1) 【转】
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...
- linux驱动基础系列--Linux 串口、usb转串口驱动分析
前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...
- Linux下 USB设备驱动分析(原创)
之前做过STM32的usb HID复合设备,闲来看看linux下USB设备驱动是怎么一回事, 参考资料基于韦东山JZ2440开发板,以下,有错误欢迎指出. 1.准备知识 1.1USB相关概念: USB ...
- linux下USB转串口驱动的安装
ubuntu10.04,usb串口用的是moxa 1110 搞了半天没有驱动... 去官方下了个:http://www.moxa.com/support/sarch_result.aspx?type= ...
- STM32 USB转串口驱动安装不成功出现黄色感叹号解决方法!
相信很多人在做USB转串口时出现过串口驱动安装不成功,出现黄色感叹号问题, 出现这种问题一般是驱动安装不成功造成的. 这里我就这个问题总结几个简单的方法. 方法1: 插上USB,利用驱动人生安装驱动. ...
- linux内核SPI总线驱动分析(一)(转)
linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析 (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...
- usb转串口驱动时会出现“文件的哈希值不在指定的目录”这样的提示
一般在安装一些usb转串口驱动时会出现“文件的哈希值不在指定的目录”这样的提示,那么怎么解决呢?知道的别喷我哦,我只是再普及一下,嘿嘿1.鼠标移到右下角,点击“设置”,再点击“更改电脑设置”2.点击最 ...
- TI 开发板安装USB转串口驱动
使用TI开发板的时候,USB转串口驱动没有,显示,无法识别设备.搜了好久才搜到相关驱动. 做个记录. 链接: https://pan.baidu.com/s/1ZT5zzVcU727jrYacKVoT ...
随机推荐
- 第二个Sprint ------第四、五、六、七天
27号.28号.29号有事回家,没能及时更新博客. 罗伟业:加减算术----乘除算式 返回一个完整正确(加减.乘除)的算式<考虑到低年级还没有完全掌握四则混合运算> 康取:四则混合运算 ...
- Linux 备份 文件夹的权限 然后在其他机器进行恢复
Study From https://www.cnblogs.com/chenshoubiao/p/4780987.html 用到的命令 getfacl 和 setfacl 备份 getfacl -R ...
- AtCoder Grand Contest 030 自闭记
A:阅读. #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> ...
- MT【190】绝对值的和
(2012浙江压轴题)已知$a>0,b\in R$,函数$f(x)=4ax^3-2bx-a+b$.1)证明:当$0\le x\le 1$时,i)函数$f(x)$的最大值为$|2a-b|+a;$i ...
- 【题解】 [HNOI2015]落忆枫音 (拓扑排序+dp+容斥原理)
原题戳我 Solution: (部分复制Navi_Aswon博客) 解释博客中的两个小地方: \[\sum_{\left(S是G中y→x的一条路径的点集\right))}\prod_{2≤j≤n,(j ...
- Luogu 2469 [SDOI2010]星际竞速 / HYSBZ 1927 [Sdoi2010]星际竞速 (网络流,最小费用流)
Luogu 2469 [SDOI2010]星际竞速 / HYSBZ 1927 [Sdoi2010]星际竞速 (网络流,最小费用流) Description 10年一度的银河系赛车大赛又要开始了.作为全 ...
- 洛谷P1445 樱花
题意:求 1/x + 1/y = 1/(n!)的正整数解个数. 解:神仙...... 设(n!) = t 打表发现 x ∈ [t+1 , 2t] 反正就是拿到式子以后乱搞一通然后发现得到了这个很美观的 ...
- .net网站iis应用池完美解决方案
一.防DDOS攻击11条: 1.确保所有服务器采用最新系统,并打上安全补丁.计算机紧急响应协调中心发现,几乎每个受到DDoS攻击的系统都没有及时打上补丁. 2.确保管理员对所有主机进行检查,而不仅针对 ...
- 关闭centos自动升级内核
八.关闭Centos自动更新chkconfig --list yum-updatesd #显示当前系统状态yum-updatesd 0:关闭 1:关闭 2:启用 3:启用 4:启用 ...
- SQL Server 事务与隔离级别实例讲解
上班途中,你在一处ATM机前停了下来.正当你在敲入密码的时候,你的一位家人也正在镇上的另一处TAM机上输入密码.你打算从某个还有500元余额的账户上转出400元,而你的家人想从同一账户取走300元.倘 ...