zoukankan      html  css  js  c++  java
  • 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 ", __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 ",  
    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 ",  
    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 ", __func__);  
    56.         goto exit_generic;  
    57.     }  
    58.   
    59.     printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC " ");  
    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 ",  
    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 ", __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 ",  
    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 ",  
    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 ", __func__);  
    56.         goto exit_generic;  
    57.     }  
    58.   
    59.     printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC " ");  
    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 ",  
    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复杂的代码结构其实是有面向对象的思想

  • 相关阅读:
    C#成员设计建议
    基于任务的异步编程模式(TAP)的错误处理
    基于任务的异步编程模式(TAP)
    C#克隆
    C#操作excel打印
    父元素如何围住浮动子元素
    intellij idea创建第一个动态web项目
    Idea快捷键
    Python中列表的copy方法
    C++读取数量不定的数据
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/4942385.html
Copyright © 2011-2022 走看看