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为例)

时间: 2024-07-30 03:04:08

Linux 串口、usb转串口驱动分析(2-2) 【转】的相关文章

linux下usb转串口驱动分析【转】

转自:http://blog.csdn.net/txxm520/article/details/8934706 首先说一下linux的风格,个人理解 1. linux大小结构体其实是面向对象的方法,(如果把struct 比作类,kmalloc就是类的实例化,结构体里面的函数指针就是方法,还有重构,多态) 2. 在linux里面,设备是对象,驱动也是对象,并且这两个是分开的   现在我们来看驱动的总体架构 并不用太在意这个图,对用户来说usb_serial设备就是普通的串口设备 我们可以看驱动里面

Linux 串口、usb转串口驱动分析(2-1) 【转】

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851   Linux 串口.usb转串口驱动分析1 内核版本:2.6.35.6                                                                        荣鹏140319   声明:图和个别段落(我做了小的修改)是直接从网上截取   目标:主要是想对Linux 串口.usb

软件工程疑问-win10系统usb转串口驱动安装不上怎么回事?有没有朋友知道……

问题描述 win10系统usb转串口驱动安装不上怎么回事?有没有朋友知道-- win10系统usb转串口驱动安装不上怎么回事?有没有朋友知道-- 是驱动不对? 解决方案 可以尝试用驱动精灵,驱动人生安装驱动,,,你的问题可能是你安装上了,只是版本太老或太新,导致设备无法工作,可以下载其他版本试试 解决方案二: win8.1 64位系统下正确安装串口转usb驱动Ubuntu 下安装 USB转串口驱动 解决方案三: 可以尝试用驱动精灵,驱动人生安装驱动,,,你的问题可能是你安装上了,只是版本太老或太

《STM32库开发实战指南:基于STM32F103(第2版)》——3.1节安装USB转串口驱动

3.1 安装USB转串口驱动 秉火的STM32开发板用的USB转串口的驱动芯片是CH340,要使用串口,需要先在电脑中安装USB转串口驱动:CH340版本,见图3-1.驱动可在网上搜索下载,或者使用我们论坛里面提供的.Windows 7用户请用管理员身份安装.如果不能安装成功,上网查找原因自行解决. 如果USB转串口驱动安装成功,USB线与板子连接没有问题,依次选择"计算机→管理→设备管理器→端口",可识别到串口. 如果识别不了串口,请检查USB线是否完好,或换一根USB线试试.

linux设备驱动之USB主机控制器驱动分析 【转】

转自:http://blog.chinaunix.net/uid-20543183-id-1930831.html   ------------------------------------------ 本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ ------------------------------------------ 一:前言 Usb是一个很复杂的系统.在usb2.0规范中,将其定义成了一个分层模型.linux中的代码也是按照

USB转串口驱动代码分析

1.USB插入时,创建设备 [plain] view plaincopy DriverObject->DriverExtension->AddDevice = USB2COM_PnPAddDevice;   步一.调用USB2COM_CreateDeviceObject创建功能设备对象(FDO) (1) IoCreateDevice系统API的原理为: [plain] view plaincopy NTKERNELAPI   NTSTATUS   IoCreateDevice(       I

Windows XP下USB转串口驱动编码实现分析

       USB转COM驱动的编写实现有很多中方法,最近在网络上看到一个最常用的方式,即是虚拟一个COM口,在COM初试的时候进行打开一个USB设备.        当USB串COM口驱动处理Write和Read 等IRQ的时候,其实是去读写USB 设备驱动,读写方式是直接采用ZwReadFile和ZwWriteFile函数.但是真正的实现也不是这么简单,真实的实现是开辟一个线程和一段较大的缓冲区,线程用于适时读取USB设备的数据并保存在缓冲区当中.当上层应用程序向这个虚拟的COM口发送RE

LINUX下 USB转串口 【转】

转自:http://blog.163.com/smilexiao_11015461/blog/static/2122052182012102410399459/ 1.将设备u口插入pc2.输入#lsmod 先看看能否检测到这个设备,就看有没有pl2303字眼可以了.如果有,则不需要再装驱动.另外如果有的话最好再用dmesg | grep usb查找如果看到:"drivers/usb/serial/usb-serial.c: USB Serial support registered for Ge

Linux下USB转串口的驱动【转】

转自:http://www.linuxidc.com/Linux/2011-02/32218.htm Linux发行版自带usb to serial驱动,以模块方式编译驱动,在内核源代码目录下运行Make MenuConfig选择Devces drivers-->USB seupport--> <M>USB Serial Converter support --> <M> USB driver for GSM and CDMA modems & [*]US