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复杂的代码结构其实是有面向对象的思想

  • 相关阅读:
    HTTP断点续传 规格严格
    Java Shutdown 规格严格
    linux 命令源码 规格严格
    JTable调整列宽 规格严格
    linux 多CPU 规格严格
    Hello can not find git path 规格严格
    Kill 规格严格
    拜拜牛人 规格严格
    Swing 规格严格
    Debugging hangs in JVM (on AIX but methodology applicable to other platforms) 规格严格
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/4942385.html
Copyright © 2011-2022 走看看