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模块的初始化过程

[cpp] view plaincopy

 

  1. static int __init usb_serial_init(void)  
  2. {  
  3.     int i;  
  4.     int result;  
  5.   
  6.     usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);  
  7.     if (!usb_serial_tty_driver)  
  8.         return -ENOMEM;  
  9.   
  10.     /* Initialize our global data */  
  11.     for (i = 0; i < SERIAL_TTY_MINORS; ++i)  
  12.         serial_table[i] = NULL;  
  13.   
  14.     result = bus_register(&usb_serial_bus_type);  
  15.     if (result) {  
  16.         printk(KERN_ERR "usb-serial: %s - registering bus driver "  
  17.                "failed\n", __func__);  
  18.         goto exit_bus;  
  19.     }  
  20.   
  21.     usb_serial_tty_driver->owner = THIS_MODULE;  
  22.     usb_serial_tty_driver->driver_name = "usbserial";  
  23.     usb_serial_tty_driver->name =    "ttyUSB";  
  24.     usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;  
  25.     usb_serial_tty_driver->minor_start = 0;  
  26.     usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;  
  27.     usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;  
  28.     usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW |  
  29.                         TTY_DRIVER_DYNAMIC_DEV;  
  30.     usb_serial_tty_driver->init_termios = tty_std_termios;  
  31.     usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD  
  32.                             | HUPCL | CLOCAL;  
  33.     usb_serial_tty_driver->init_termios.c_ispeed = 9600;  
  34.     usb_serial_tty_driver->init_termios.c_ospeed = 9600;  
  35.     tty_set_operations(usb_serial_tty_driver, &serial_ops);  
  36.     result = tty_register_driver(usb_serial_tty_driver);  
  37.     if (result) {  
  38.         printk(KERN_ERR "usb-serial: %s - tty_register_driver failed\n",  
  39.                __func__);  
  40.         goto exit_reg_driver;  
  41.     }  
  42.   
  43.     /* register the USB driver */  
  44.     result = usb_register(&usb_serial_driver);  
  45.     if (result < 0) {  
  46.         printk(KERN_ERR "usb-serial: %s - usb_register failed\n",  
  47.                __func__);  
  48.         goto exit_tty;  
  49.     }  
  50.   
  51.     /* register the generic driver, if we should */  
  52.     result = usb_serial_generic_register(debug);  
  53.     if (result < 0) {  
  54.         printk(KERN_ERR "usb-serial: %s - registering generic "  
  55.                "driver failed\n", __func__);  
  56.         goto exit_generic;  
  57.     }  
  58.   
  59.     printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");  
  60.   
  61.     return result;  
  62.   
  63. exit_generic:  
  64.     usb_deregister(&usb_serial_driver);  
  65.   
  66. exit_tty:  
  67.     tty_unregister_driver(usb_serial_tty_driver);  
  68.   
  69. exit_reg_driver:  
  70.     bus_unregister(&usb_serial_bus_type);  
  71.   
  72. exit_bus:  
  73.     printk(KERN_ERR "usb-serial: %s - returning with error %d\n",  
  74.            __func__, result);  
  75.     put_tty_driver(usb_serial_tty_driver);  
  76.     return result;  
  77. }  

[cpp] view plaincopy

 

  1. static int __init usb_serial_init(void)  
  2. {  
  3.     int i;  
  4.     int result;  
  5.   
  6.     usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);  
  7.     if (!usb_serial_tty_driver)  
  8.         return -ENOMEM;  
  9.   
  10.     /* Initialize our global data */  
  11.     for (i = 0; i < SERIAL_TTY_MINORS; ++i)  
  12.         serial_table[i] = NULL;  
  13.   
  14.     result = bus_register(&usb_serial_bus_type);  
  15.     if (result) {  
  16.         printk(KERN_ERR "usb-serial: %s - registering bus driver "  
  17.                "failed\n", __func__);  
  18.         goto exit_bus;  
  19.     }  
  20.   
  21.     usb_serial_tty_driver->owner = THIS_MODULE;  
  22.     usb_serial_tty_driver->driver_name = "usbserial";  
  23.     usb_serial_tty_driver->name =    "ttyUSB";  
  24.     usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;  
  25.     usb_serial_tty_driver->minor_start = 0;  
  26.     usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;  
  27.     usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;  
  28.     usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW |  
  29.                         TTY_DRIVER_DYNAMIC_DEV;  
  30.     usb_serial_tty_driver->init_termios = tty_std_termios;  
  31.     usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD  
  32.                             | HUPCL | CLOCAL;  
  33.     usb_serial_tty_driver->init_termios.c_ispeed = 9600;  
  34.     usb_serial_tty_driver->init_termios.c_ospeed = 9600;  
  35.     tty_set_operations(usb_serial_tty_driver, &serial_ops);  
  36.     result = tty_register_driver(usb_serial_tty_driver);  
  37.     if (result) {  
  38.         printk(KERN_ERR "usb-serial: %s - tty_register_driver failed\n",  
  39.                __func__);  
  40.         goto exit_reg_driver;  
  41.     }  
  42.   
  43.     /* register the USB driver */  
  44.     result = usb_register(&usb_serial_driver);  
  45.     if (result < 0) {  
  46.         printk(KERN_ERR "usb-serial: %s - usb_register failed\n",  
  47.                __func__);  
  48.         goto exit_tty;  
  49.     }  
  50.   
  51.     /* register the generic driver, if we should */  
  52.     result = usb_serial_generic_register(debug);  
  53.     if (result < 0) {  
  54.         printk(KERN_ERR "usb-serial: %s - registering generic "  
  55.                "driver failed\n", __func__);  
  56.         goto exit_generic;  
  57.     }  
  58.   
  59.     printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");  
  60.   
  61.     return result;  
  62.   
  63. exit_generic:  
  64.     usb_deregister(&usb_serial_driver);  
  65.   
  66. exit_tty:  
  67.     tty_unregister_driver(usb_serial_tty_driver);  
  68.   
  69. exit_reg_driver:  
  70.     bus_unregister(&usb_serial_bus_type);  
  71.   
  72. exit_bus:  
  73.     printk(KERN_ERR "usb-serial: %s - returning with error %d\n",  
  74.            __func__, result);  
  75.     put_tty_driver(usb_serial_tty_driver);  
  76.     return result;  
  77. }  

很简单

第一步 将usb_seria的TTY驱动注册进TTY驱动列表里面,以后调用open,write,read首先会调用tty驱动里面的函数,然后函数指针会指到用户自己定义的驱动里面,这应该是多态的一种应用吧,个人理解求指正

第二步 将usb_seria驱动注册进usb_core里面的驱动列表

 

只有usb_serial模块驱动,设备还不能正常工作,linux很好的把它分成了

分层一: usb_serial驱动,设备的大部分实现都在此

分层二: 用户驱动,不需要知道太多的细节,实现几个回调函数就能实现整个驱动功能。

generic.c 就是个通用的用户驱动模型,并且大部分设备都能兼容。

下面generic.c的模块初始化函数

[cpp] view plaincopy

 

  1. int usb_serial_generic_register(int _debug)  
  2. {  
  3.     int retval = 0;  
  4.   
  5.     debug = _debug;  
  6. #ifdef CONFIG_USB_SERIAL_GENERIC   
  7.     generic_device_ids[0].idVendor = vendor;  
  8.     generic_device_ids[0].idProduct = product;  
  9.     generic_device_ids[0].match_flags =  
  10.         USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;  
  11.   
  12.     /* register our generic driver with ourselves */  
  13.     retval = usb_serial_register(&usb_serial_generic_device);  
  14.     if (retval)  
  15.         goto exit;  
  16.     retval = usb_register(&generic_driver);  
  17.     if (retval)  
  18.         usb_serial_deregister(&usb_serial_generic_device);  
  19. exit:  
  20. #endif   
  21.     return retval;  
  22. }  

[cpp] view plaincopy

 

  1. int usb_serial_generic_register(int _debug)  
  2. {  
  3.     int retval = 0;  
  4.   
  5.     debug = _debug;  
  6. #ifdef CONFIG_USB_SERIAL_GENERIC  
  7.     generic_device_ids[0].idVendor = vendor;  
  8.     generic_device_ids[0].idProduct = product;  
  9.     generic_device_ids[0].match_flags =  
  10.         USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;  
  11.   
  12.     /* register our generic driver with ourselves */  
  13.     retval = usb_serial_register(&usb_serial_generic_device);  
  14.     if (retval)  
  15.         goto exit;  
  16.     retval = usb_register(&generic_driver);  
  17.     if (retval)  
  18.         usb_serial_deregister(&usb_serial_generic_device);  
  19. exit:  
  20. #endif  
  21.     return retval;  
  22. }  

第一步 首先确定自己的特征码,这里通过vendor 和 product表明自己是哪种设备的驱动

第二步 将用户驱动注册进usb_serial_bus总线,linux很喜欢这么搞……,通过总线来管理一类设备

第三步 将usb驱动注册进usb_core的驱动列表

有人会问,为什么usb会自动发现对应的驱动,这其实是个匹配的过程,linux里面叫probe

前面第一步是确定了驱动的匹配值,第二步和第三步都把驱动的匹配值注册进去

事实上前面第三步代码retval = usb_register(&generic_driver);就是为了把generic驱动注册进usb的驱动列表,当有设备插入时,会轮询到它,然后会调用此驱动的probe,就是下面的函数

[cpp] view plaincopy

 

  1. static int generic_probe(struct usb_interface *interface,  
  2.                    const struct usb_device_id *id)  
  3. {  
  4.     const struct usb_device_id *id_pattern;  
  5.   
  6.     id_pattern = usb_match_id(interface, generic_device_ids);  
  7.     if (id_pattern != NULL)  
  8.         return usb_serial_probe(interface, id);  
  9.     return -ENODEV;  
  10. }  

[cpp] view plaincopy

 

  1. static int generic_probe(struct usb_interface *interface,  
  2.                    const struct usb_device_id *id)  
  3. {  
  4.     const struct usb_device_id *id_pattern;  
  5.   
  6.     id_pattern = usb_match_id(interface, generic_device_ids);  
  7.     if (id_pattern != NULL)  
  8.         return usb_serial_probe(interface, id);  
  9.     return -ENODEV;  
  10. }  

最主要的还是调用usb_serial_probe(interface, id)函数

现在我们来看usb_serial_probe()的匹配过程
 

 

通过probe我们最终将/dev/ttySn设备和usb_serial_port对象绑定起来,我们对/dev/ttySn设备进行操作,对应tty驱动里面的

[cpp] view plaincopy

 

  1. static const struct tty_operations serial_ops = {  
  2.     .open =         serial_open,  
  3.     .close =        serial_close,  
  4.     .write =        serial_write,  
  5.     .hangup =       serial_hangup,  
  6.     .write_room =       serial_write_room,  
  7.     .ioctl =        serial_ioctl,  
  8.     .set_termios =      serial_set_termios,  
  9.     .throttle =     serial_throttle,  
  10.     .unthrottle =       serial_unthrottle,  
  11.     .break_ctl =        serial_break,  
  12.     .chars_in_buffer =  serial_chars_in_buffer,  
  13.     .tiocmget =     serial_tiocmget,  
  14.     .tiocmset =     serial_tiocmset,  
  15.     .cleanup =      serial_cleanup,  
  16.     .install =      serial_install,  
  17.     .proc_fops =        &serial_proc_fops,  
  18. };  

[cpp] view plaincopy

 

  1. static const struct tty_operations serial_ops = {  
  2.     .open =         serial_open,  
  3.     .close =        serial_close,  
  4.     .write =        serial_write,  
  5.     .hangup =       serial_hangup,  
  6.     .write_room =       serial_write_room,  
  7.     .ioctl =        serial_ioctl,  
  8.     .set_termios =      serial_set_termios,  
  9.     .throttle =     serial_throttle,  
  10.     .unthrottle =       serial_unthrottle,  
  11.     .break_ctl =        serial_break,  
  12.     .chars_in_buffer =  serial_chars_in_buffer,  
  13.     .tiocmget =     serial_tiocmget,  
  14.     .tiocmset =     serial_tiocmset,  
  15.     .cleanup =      serial_cleanup,  
  16.     .install =      serial_install,  
  17.     .proc_fops =        &serial_proc_fops,  
  18. };  

比如我们对/dev/ttySn进行写操作,write->serial_write

[cpp] view plaincopy

 

  1. static int serial_write(struct tty_struct *tty, const unsigned char *buf,  
  2.                                 int count)  
  3. {  
  4.     struct usb_serial_port *port = tty->driver_data;  
  5.     int retval = -ENODEV;  
  6.   
  7.     if (port->serial->dev->state == USB_STATE_NOTATTACHED)  
  8.         goto exit;  
  9.   
  10.     dbg("%s - port %d, %d byte(s)", __func__, port->number, count);  
  11.   
  12.     /* pass on to the driver specific version of this function */  
  13.     retval = port->serial->type->write(tty, port, buf, count);  
  14.   
  15. exit:  
  16.     return retval;  
  17. }  

[cpp] view plaincopy

 

  1. static int serial_write(struct tty_struct *tty, const unsigned char *buf,  
  2.                                 int count)  
  3. {  
  4.     struct usb_serial_port *port = tty->driver_data;  
  5.     int retval = -ENODEV;  
  6.   
  7.     if (port->serial->dev->state == USB_STATE_NOTATTACHED)  
  8.         goto exit;  
  9.   
  10.     dbg("%s - port %d, %d byte(s)", __func__, port->number, count);  
  11.   
  12.     /* pass on to the driver specific version of this function */  
  13.     retval = port->serial->type->write(tty, port, buf, count);  
  14.   
  15. exit:  
  16.     return retval;  
  17. }  

 

struct usb_serial_port *port = tty->driver_data;找到了这个tty对象所对应的port对象,

port对象里面有驱动信息,urb的缓冲区信息,最终调用的是我们写的用户驱动里面的write方法。

用户驱动通过信使URB将想要发送的数据发送出去。

 

总结:

通过分析usb 串口驱动可以推测出其他usb设备大致的工作方式,下一步将分析usb网卡的驱动。

linux复杂的代码结构其实是有面向对象的思想

时间: 2024-10-18 14:22:34

linux下usb转串口驱动分析【转】的相关文章

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转串

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

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

Linux下USB suspend/resume源码分析【转】

转自:http://blog.csdn.net/aaronychen/article/details/3928479 Linux下USB suspend/resume源码分析 Author:aaron   本文主要从自己开发的一个USB驱动的例子来深入讲解linux内核是如何支持USB设备的休眠和唤醒的, 最近我在为我们公司的一个模块写linux下的驱动, 其中之一就是要支持USB的休眠唤醒问题, 实际上linux内核对USB的这个功能的支持还是比较新的, 也就是最近几年的事.   一  打开/

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

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

Linux下DM9000网卡驱动实验

Linux下DM9000网卡驱动实验 1.1        硬件系统介绍 1.1.1          网络驱动程序的特点     网络驱动程序是介于硬件和内核之间传送数据包,不是面向流的设备,不能象/dev/tty1那样简单的映射到文件系统的节点上.Linux调用这些接口的方式是给他们分配一个独立的名字(如eth0).这样的名字在文件系统中并没有对应项.内核和网络设备驱动程序之间的通信与字符设备驱动程序和快设备驱动程序与内核间的通信是完全不同的.内核不再调用read/write,它调用与数据包

Linux下Android ADB驱动安装详解

Linux下Android ADB驱动安装详解 概述 最近由于内置的合作商比较多,本人使用的Ubuntu系统好多厂商的Android手机都无法正确的识别,经过一番折腾,和查阅SDK,现把Linux下ADB驱动配置的方法和当中会遇到的相关问题的解决方法整理出来贡献给大家. Linux下使用手机USB调试模式连接ADB进行Android程序的调试,配置驱动没有Windows来的直观. 具体步骤 首先确认手机连接上电脑,lsusb查看下设备记录. matthew@matthew-1230-laptop