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

并不用太在意这个图,对用户来说usb_serial设备就是普通的串口设备
我们可以看驱动里面几个主要的源代码文件
usb-serial.c 模块的主要实现
bus.c usb_serial总线驱动,驱动和设备都要注册到这条总线上
generic.c 通用的用户驱动,用户如果写自己的驱动只需拿自己的实现代替generic.c的函数,一般这个驱动已经能适应大部分设备了
现在我们来看usb_serial模块的初始化过程
- static int __init usb_serial_init(void)
 - {
 - int i;
 - int result;
 - usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);
 - if (!usb_serial_tty_driver)
 - return -ENOMEM;
 - /* Initialize our global data */
 - for (i = 0; i < SERIAL_TTY_MINORS; ++i)
 - serial_table[i] = NULL;
 - result = bus_register(&usb_serial_bus_type);
 - if (result) {
 - printk(KERN_ERR "usb-serial: %s - registering bus driver "
 - "failed\n", __func__);
 - goto exit_bus;
 - }
 - usb_serial_tty_driver->owner = THIS_MODULE;
 - usb_serial_tty_driver->driver_name = "usbserial";
 - usb_serial_tty_driver->name = "ttyUSB";
 - usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;
 - usb_serial_tty_driver->minor_start = 0;
 - usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
 - usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
 - usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW |
 - TTY_DRIVER_DYNAMIC_DEV;
 - usb_serial_tty_driver->init_termios = tty_std_termios;
 - usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD
 - | HUPCL | CLOCAL;
 - usb_serial_tty_driver->init_termios.c_ispeed = 9600;
 - usb_serial_tty_driver->init_termios.c_ospeed = 9600;
 - tty_set_operations(usb_serial_tty_driver, &serial_ops);
 - result = tty_register_driver(usb_serial_tty_driver);
 - if (result) {
 - printk(KERN_ERR "usb-serial: %s - tty_register_driver failed\n",
 - __func__);
 - goto exit_reg_driver;
 - }
 - /* register the USB driver */
 - result = usb_register(&usb_serial_driver);
 - if (result < 0) {
 - printk(KERN_ERR "usb-serial: %s - usb_register failed\n",
 - __func__);
 - goto exit_tty;
 - }
 - /* register the generic driver, if we should */
 - result = usb_serial_generic_register(debug);
 - if (result < 0) {
 - printk(KERN_ERR "usb-serial: %s - registering generic "
 - "driver failed\n", __func__);
 - goto exit_generic;
 - }
 - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
 - return result;
 - exit_generic:
 - usb_deregister(&usb_serial_driver);
 - exit_tty:
 - tty_unregister_driver(usb_serial_tty_driver);
 - exit_reg_driver:
 - bus_unregister(&usb_serial_bus_type);
 - exit_bus:
 - printk(KERN_ERR "usb-serial: %s - returning with error %d\n",
 - __func__, result);
 - put_tty_driver(usb_serial_tty_driver);
 - return result;
 - }
 
- static int __init usb_serial_init(void)
 - {
 - int i;
 - int result;
 - usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);
 - if (!usb_serial_tty_driver)
 - return -ENOMEM;
 - /* Initialize our global data */
 - for (i = 0; i < SERIAL_TTY_MINORS; ++i)
 - serial_table[i] = NULL;
 - result = bus_register(&usb_serial_bus_type);
 - if (result) {
 - printk(KERN_ERR "usb-serial: %s - registering bus driver "
 - "failed\n", __func__);
 - goto exit_bus;
 - }
 - usb_serial_tty_driver->owner = THIS_MODULE;
 - usb_serial_tty_driver->driver_name = "usbserial";
 - usb_serial_tty_driver->name = "ttyUSB";
 - usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;
 - usb_serial_tty_driver->minor_start = 0;
 - usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
 - usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
 - usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW |
 - TTY_DRIVER_DYNAMIC_DEV;
 - usb_serial_tty_driver->init_termios = tty_std_termios;
 - usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD
 - | HUPCL | CLOCAL;
 - usb_serial_tty_driver->init_termios.c_ispeed = 9600;
 - usb_serial_tty_driver->init_termios.c_ospeed = 9600;
 - tty_set_operations(usb_serial_tty_driver, &serial_ops);
 - result = tty_register_driver(usb_serial_tty_driver);
 - if (result) {
 - printk(KERN_ERR "usb-serial: %s - tty_register_driver failed\n",
 - __func__);
 - goto exit_reg_driver;
 - }
 - /* register the USB driver */
 - result = usb_register(&usb_serial_driver);
 - if (result < 0) {
 - printk(KERN_ERR "usb-serial: %s - usb_register failed\n",
 - __func__);
 - goto exit_tty;
 - }
 - /* register the generic driver, if we should */
 - result = usb_serial_generic_register(debug);
 - if (result < 0) {
 - printk(KERN_ERR "usb-serial: %s - registering generic "
 - "driver failed\n", __func__);
 - goto exit_generic;
 - }
 - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
 - return result;
 - exit_generic:
 - usb_deregister(&usb_serial_driver);
 - exit_tty:
 - tty_unregister_driver(usb_serial_tty_driver);
 - exit_reg_driver:
 - bus_unregister(&usb_serial_bus_type);
 - exit_bus:
 - printk(KERN_ERR "usb-serial: %s - returning with error %d\n",
 - __func__, result);
 - put_tty_driver(usb_serial_tty_driver);
 - return result;
 - }
 
很简单
第一步 将usb_seria的TTY驱动注册进TTY驱动列表里面,以后调用open,write,read首先会调用tty驱动里面的函数,然后函数指针会指到用户自己定义的驱动里面,这应该是多态的一种应用吧,个人理解求指正
第二步 将usb_seria驱动注册进usb_core里面的驱动列表
只有usb_serial模块驱动,设备还不能正常工作,linux很好的把它分成了
分层一: usb_serial驱动,设备的大部分实现都在此
分层二: 用户驱动,不需要知道太多的细节,实现几个回调函数就能实现整个驱动功能。
generic.c 就是个通用的用户驱动模型,并且大部分设备都能兼容。
下面generic.c的模块初始化函数
- int usb_serial_generic_register(int _debug)
 - {
 - int retval = 0;
 - debug = _debug;
 - #ifdef CONFIG_USB_SERIAL_GENERIC
 - generic_device_ids[0].idVendor = vendor;
 - generic_device_ids[0].idProduct = product;
 - generic_device_ids[0].match_flags =
 - USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
 - /* register our generic driver with ourselves */
 - retval = usb_serial_register(&usb_serial_generic_device);
 - if (retval)
 - goto exit;
 - retval = usb_register(&generic_driver);
 - if (retval)
 - usb_serial_deregister(&usb_serial_generic_device);
 - exit:
 - #endif
 - return retval;
 - }
 
- int usb_serial_generic_register(int _debug)
 - {
 - int retval = 0;
 - debug = _debug;
 - #ifdef CONFIG_USB_SERIAL_GENERIC
 - generic_device_ids[0].idVendor = vendor;
 - generic_device_ids[0].idProduct = product;
 - generic_device_ids[0].match_flags =
 - USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
 - /* register our generic driver with ourselves */
 - retval = usb_serial_register(&usb_serial_generic_device);
 - if (retval)
 - goto exit;
 - retval = usb_register(&generic_driver);
 - if (retval)
 - usb_serial_deregister(&usb_serial_generic_device);
 - exit:
 - #endif
 - return retval;
 - }
 
第一步 首先确定自己的特征码,这里通过vendor 和 product表明自己是哪种设备的驱动
第二步 将用户驱动注册进usb_serial_bus总线,linux很喜欢这么搞……,通过总线来管理一类设备
第三步 将usb驱动注册进usb_core的驱动列表
有人会问,为什么usb会自动发现对应的驱动,这其实是个匹配的过程,linux里面叫probe
前面第一步是确定了驱动的匹配值,第二步和第三步都把驱动的匹配值注册进去

事实上前面第三步代码retval = usb_register(&generic_driver);就是为了把generic驱动注册进usb的驱动列表,当有设备插入时,会轮询到它,然后会调用此驱动的probe,就是下面的函数
- static int generic_probe(struct usb_interface *interface,
 - const struct usb_device_id *id)
 - {
 - const struct usb_device_id *id_pattern;
 - id_pattern = usb_match_id(interface, generic_device_ids);
 - if (id_pattern != NULL)
 - return usb_serial_probe(interface, id);
 - return -ENODEV;
 - }
 
- static int generic_probe(struct usb_interface *interface,
 - const struct usb_device_id *id)
 - {
 - const struct usb_device_id *id_pattern;
 - id_pattern = usb_match_id(interface, generic_device_ids);
 - if (id_pattern != NULL)
 - return usb_serial_probe(interface, id);
 - return -ENODEV;
 - }
 
最主要的还是调用usb_serial_probe(interface, id)函数
现在我们来看usb_serial_probe()的匹配过程
 
通过probe我们最终将/dev/ttySn设备和usb_serial_port对象绑定起来,我们对/dev/ttySn设备进行操作,对应tty驱动里面的
- static const struct tty_operations serial_ops = {
 - .open = serial_open,
 - .close = serial_close,
 - .write = serial_write,
 - .hangup = serial_hangup,
 - .write_room = serial_write_room,
 - .ioctl = serial_ioctl,
 - .set_termios = serial_set_termios,
 - .throttle = serial_throttle,
 - .unthrottle = serial_unthrottle,
 - .break_ctl = serial_break,
 - .chars_in_buffer = serial_chars_in_buffer,
 - .tiocmget = serial_tiocmget,
 - .tiocmset = serial_tiocmset,
 - .cleanup = serial_cleanup,
 - .install = serial_install,
 - .proc_fops = &serial_proc_fops,
 - };
 
- static const struct tty_operations serial_ops = {
 - .open = serial_open,
 - .close = serial_close,
 - .write = serial_write,
 - .hangup = serial_hangup,
 - .write_room = serial_write_room,
 - .ioctl = serial_ioctl,
 - .set_termios = serial_set_termios,
 - .throttle = serial_throttle,
 - .unthrottle = serial_unthrottle,
 - .break_ctl = serial_break,
 - .chars_in_buffer = serial_chars_in_buffer,
 - .tiocmget = serial_tiocmget,
 - .tiocmset = serial_tiocmset,
 - .cleanup = serial_cleanup,
 - .install = serial_install,
 - .proc_fops = &serial_proc_fops,
 - };
 
比如我们对/dev/ttySn进行写操作,write->serial_write
- static int serial_write(struct tty_struct *tty, const unsigned char *buf,
 - int count)
 - {
 - struct usb_serial_port *port = tty->driver_data;
 - int retval = -ENODEV;
 - if (port->serial->dev->state == USB_STATE_NOTATTACHED)
 - goto exit;
 - dbg("%s - port %d, %d byte(s)", __func__, port->number, count);
 - /* pass on to the driver specific version of this function */
 - retval = port->serial->type->write(tty, port, buf, count);
 - exit:
 - return retval;
 - }
 
- static int serial_write(struct tty_struct *tty, const unsigned char *buf,
 - int count)
 - {
 - struct usb_serial_port *port = tty->driver_data;
 - int retval = -ENODEV;
 - if (port->serial->dev->state == USB_STATE_NOTATTACHED)
 - goto exit;
 - dbg("%s - port %d, %d byte(s)", __func__, port->number, count);
 - /* pass on to the driver specific version of this function */
 - retval = port->serial->type->write(tty, port, buf, count);
 - exit:
 - return retval;
 - }
 
struct usb_serial_port *port = tty->driver_data;找到了这个tty对象所对应的port对象,
port对象里面有驱动信息,urb的缓冲区信息,最终调用的是我们写的用户驱动里面的write方法。
用户驱动通过信使URB将想要发送的数据发送出去。
总结:
通过分析usb 串口驱动可以推测出其他usb设备大致的工作方式,下一步将分析usb网卡的驱动。
linux复杂的代码结构其实是有面向对象的思想
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= ...
 - Linux 串口、usb转串口驱动分析(2-2) 【转】
		
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...
 - 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转串口  【转】
		
转自:http://blog.163.com/smilexiao_11015461/blog/static/2122052182012102410399459/ 1.将设备u口插入pc2.输入#lsm ...
 - Linux下usb设备驱动详解
		
USB驱动分为两块,一块是USB的bus驱动,这个东西,Linux内核已经做好了,我们可以不管,我们只需要了解它的功能.形象的说,USB的bus驱动相当于铺出一条路来,让所有的信息都可以通过这条USB ...
 - Linux下USB转串口的驱动【转】
		
转自:http://www.linuxidc.com/Linux/2011-02/32218.htm Linux发行版自带usb to serial驱动,以模块方式编译驱动,在内核源代码目录下运行Ma ...
 - minicom-linux下USB转串口配置
		
现在的笔记本越做越薄,好些电脑已经没有串口了,做硬件开发会非常头疼,不过有了USB转串口设备,PC机只需要有USB接口就可以了.在linux下我们使用minicom与目标设备通信,在此记录一下linu ...
 
随机推荐
- NetFPGA Demo ——reference_nic_nf1_cml
			
NetFPGA Demo --reference_nic_nf1_cml 实验平台 OS:deepin 15.4 开发板:NetFPGA_1G_CML 实验过程 从NetFPGA-1G-CML从零开始 ...
 - ElasticSearch 2 (22) - 语言处理系列之标记规范化
			
ElasticSearch 2 (22) - 语言处理系列之标记规范化 摘要 将文本拆解成标记只是工作的一半.为了使这些标记更容易被搜索到,它们需要经过一个规范化的处理过程,以移除相同单词间不重要的差 ...
 - T检验在项目上的具体实施
			
我觉得 T 检验,应该用在 判断某种仿真条件因素 对碳纳米管的随机性 是否有显著影响 上.所以不是针对<相同仿真条件对不同源的影响>这个表中的数据做 T 检验 如:判断 金属/半导体比率 ...
 - [2017BUAA软工]结对项目-数独程序扩展
			
零.github地址 GitHub地址:https://github.com/Liu-SD/SudoCmd (这个地址是命令行模式数独的仓库,包含了用作测试的BIN.DLL核心计算模块地址是:http ...
 - PAT 1044 火星数字
			
https://pintia.cn/problem-sets/994805260223102976/problems/994805279328157696 火星人是以13进制计数的: 地球人的0被火星 ...
 - Git查看与修改用户名、邮箱
			
用户名和邮箱的作用: 用户名和邮箱地址相当于你的身份标识,是本地Git客户端的一个变量,不会随着Git库而改变. 每次commit都会用用户名和邮箱纪录. github的contributions跟你 ...
 - catch/finally中不应使用 writer.flush()
			
在开发中遇到了一个问题,关闭流的时候会出现某种莫名其妙的错误.后来一个巧合看到了这个解决方法. 先看问题(知道答案以后,才知道是这里出错了) FileWriter writer = null; Str ...
 - CF438D The Child and Sequence(线段树)
			
题目链接:CF原网 洛谷 题目大意:维护一个长度为 $n$ 的正整数序列 $a$,支持单点修改,区间取模,区间求和.共 $m$ 个操作. $1\le n,m\le 10^5$.其它数均为非负整数且 ...
 - 解题:WC 2018 州区划分
			
题面 WC之前写的,补一补,但是基本就是学新知识了 首先可以枚举子集$3^n$转移,优化是额外记录每个集合选取的个数,然后按照选取个数从小到大转移.转移的时候先FWT成“点值”转移完了IFWT回去乘逆 ...
 - Java中如何遍历Map对象
			
方法一:使用map.entrySet()来遍历.这是最常见的并且在大多数情况下也是最可取的遍历方式.在键值都需要的时候使用. Map<String,String> map = new Ha ...