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

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

很简单

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  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. id_pattern = usb_match_id(interface, generic_device_ids);
  6. if (id_pattern != NULL)
  7. return usb_serial_probe(interface, id);
  8. return -ENODEV;
  9. }
  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. id_pattern = usb_match_id(interface, generic_device_ids);
  6. if (id_pattern != NULL)
  7. return usb_serial_probe(interface, id);
  8. return -ENODEV;
  9. }

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

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

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

  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. };
  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

  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. if (port->serial->dev->state == USB_STATE_NOTATTACHED)
  7. goto exit;
  8. dbg("%s - port %d, %d byte(s)", __func__, port->number, count);
  9. /* pass on to the driver specific version of this function */
  10. retval = port->serial->type->write(tty, port, buf, count);
  11. exit:
  12. return retval;
  13. }
  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. if (port->serial->dev->state == USB_STATE_NOTATTACHED)
  7. goto exit;
  8. dbg("%s - port %d, %d byte(s)", __func__, port->number, count);
  9. /* pass on to the driver specific version of this function */
  10. retval = port->serial->type->write(tty, port, buf, count);
  11. exit:
  12. return retval;
  13. }

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

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

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

总结:

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

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

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

  1. Linux下 USB设备驱动分析(原创)

    之前做过STM32的usb HID复合设备,闲来看看linux下USB设备驱动是怎么一回事, 参考资料基于韦东山JZ2440开发板,以下,有错误欢迎指出. 1.准备知识 1.1USB相关概念: USB ...

  2. linux下USB转串口驱动的安装

    ubuntu10.04,usb串口用的是moxa 1110 搞了半天没有驱动... 去官方下了个:http://www.moxa.com/support/sarch_result.aspx?type= ...

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

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...

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

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...

  5. linux驱动基础系列--Linux 串口、usb转串口驱动分析

    前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...

  6. LINUX下 USB转串口 【转】

    转自:http://blog.163.com/smilexiao_11015461/blog/static/2122052182012102410399459/ 1.将设备u口插入pc2.输入#lsm ...

  7. Linux下usb设备驱动详解

    USB驱动分为两块,一块是USB的bus驱动,这个东西,Linux内核已经做好了,我们可以不管,我们只需要了解它的功能.形象的说,USB的bus驱动相当于铺出一条路来,让所有的信息都可以通过这条USB ...

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

    转自:http://www.linuxidc.com/Linux/2011-02/32218.htm Linux发行版自带usb to serial驱动,以模块方式编译驱动,在内核源代码目录下运行Ma ...

  9. minicom-linux下USB转串口配置

    现在的笔记本越做越薄,好些电脑已经没有串口了,做硬件开发会非常头疼,不过有了USB转串口设备,PC机只需要有USB接口就可以了.在linux下我们使用minicom与目标设备通信,在此记录一下linu ...

随机推荐

  1. Alpha版本冲刺(十)

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...

  2. Docker(八)-Docker创建Nginx容器

    获取Nginx镜像 最简单的方法就是通过 docker pull nginx 命令来创建 Nginx容器. $ sudo docker pull nginx 或者: $ sudo docker pul ...

  3. ehcache、redis应用场景比较

    应用场景: ehcache是Hibernate中默认的CacheProvider,直接在jvm虚拟机中缓存,速度快,效率高:但是缓存共享麻烦,集群分布式应用不方便.  . 缓存数据有两级:内存和磁盘, ...

  4. 解决Delphi 2010启动时出现cannot create xxxx\EditorLineEnds.ttr问题

    由于在Windows安装了最近的更新(KB2982791, KB2970228)后,Delphi的IDE需要创建的一个文件%TEMP%\EditorLineEnds.ttr会被系统锁定,导致除非重新启 ...

  5. 去除百度搜索结果中的广告的 js 代码

    在百度页面下控制台里执行如下代码, 然后关掉控制台 setInterval(() => { try{ Array.from( document.querySelectorAll('#conten ...

  6. flask再学习-重构!启动!

    1.打造MVC框架: common/libs:放置一些功能公用的方法. common/models:放置ORM模型 config:配置文件属性 web/controllers:视图层,处理url和ap ...

  7. 【题解】 [ZJOI2012]灾难 (拓扑排序+LCA)

    懒得复制,戳我戳我 Solution: 这题思路很神奇,首先你要知道这个毁灭树是怎么保证实现的:一句话就是如果该节点要被破坏,他的所有父节点就要被破坏,也就只要所有父节点的LCA被破坏就可以,所以我们 ...

  8. Apache Storm从一端读取实时数据的原始流

    Apache Storm从一端读取实时数据的原始流,并将其传递通过一系列小处理单元,并在另一端输出处理/有用的信息. 下图描述了Apache Storm的核心概念. 640?wx_fmt=png&am ...

  9. 【BZOJ1082】[SCOI2005]栅栏(搜索)

    [BZOJ1082][SCOI2005]栅栏(搜索) 题面 BZOJ 洛谷 题解 随便写个爆搜,洛谷上就\(80\)分了.先放爆搜代码: #include<iostream> #inclu ...

  10. CentOS服务器配置SSH免密码登录

    由于工作需要,经常要登录到多台服务器远程操作,每次都是ssh user@host:port 再输入密码,时间长了,难免觉得乏味-- 故而从度娘那里扒来了一些让SSH免密码登录的办法,其实这也是使用Gi ...