linux下usb转串口驱动分析

分类: linux driver 2012-06-08 15:11 456人阅读 评论(0) 收藏 举报
linux struct interface returning buffer 工作

最近要做一个关于LTE的项目,模块厂家提供的驱动里面有usb转串口驱动,usb网卡驱动,项目还没立项所以比较空,闲的蛋疼把usb转串口驱动研究了一遍,本文尽量用画图把事情说明白,献给各位纠结的童鞋。

首先说一下linux的风格,个人理解

1. linux大小结构体其实是面向对象的方法,(如果把struct 比作类,kmalloc就是类的实例化,结构体里面的函数指针就是方法,还有重构,多态)

2. 在linux里面,设备是对象,驱动也是对象,并且这两个是分开的

现在我们来看驱动的总体架构

并不用太在意这个图,对用户来说usb_serial设备就是普通的串口设备

我们可以看驱动里面几个主要的源代码文件

usb-serial.c  模块的主要实现

bus.c  usb_serial总线驱动,驱动和设备都要注册到这条总线上

generic.c 通用的用户驱动,用户如果写自己的驱动只需拿自己的实现代替generic.c的函数,一般这个驱动已经能适应大部分设备了

现在我们来看usb_serial模块的初始化过程

[cpp] view plain copy print ?
  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. }
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的模块初始化函数

[cpp] view plain copy print ?
  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. }
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,就是下面的函数

[cpp] view plain copy print ?
  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. }
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驱动里面的

[cpp] view plain copy print ?
  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. };
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

[cpp] view plain copy print ?
  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. }
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转串口驱动分析相关推荐

  1. USB转串口驱动分析(一)

    之前追踪代码用的grep命令效率太低了,所以这次下载C代码阅读跳转利器ctags.cscope用于分析代码 因为用的是Centos6.7所以需要用到yum install安装软件 [wuyujun@w ...

  2. 笔记本+win7下USB转串口驱动的安装

    为了出行方便,更换装备,换了台笔记本,纠结许久-- 用笔记本做底层开发的纠结不是一班人可以体会到的,毕竟咱们都是二班的.笔记本还好,可能你用的还是xp,不错,也好办.笔记本+win7就有点头疼了,US ...

  3. USB转串口驱动分析(二)

    在static int __init usb_serial_init(void)里 tty_set_operations(usb_serial_tty_driver, &serial_ops) ...

  4. ttyUSB 串口编程 Linux下 USB转串口

    转:http://www.360doc.com/content/12/0222/15/1317564_188649565.shtml 在Linux下对设备的操作方法与对文件的操作方法是一样的,因此对串 ...

  5. linux下矩阵键盘设备名,Linux下矩阵键盘驱动分析与移植

    Post Views: 1,598 首先要介绍一下Linux中input子系统的模型,一图胜千言,所以直接上图. 上图一目了然,我们的键盘驱动就是工作在input子系统的最低层.单纯地从驱动角度讲,我 ...

  6. Linux环境下使用 USB转串口驱动(二)

    minicom是linux下串口通信的软件,它的使用完全依靠键盘的操作,虽然没有"超级终端"那么易用,但是使用习惯之后读者将会体会到它的高效与便利,下面将讲解minicom的安装和 ...

  7. linux装pl2303驱动下载,Linux下安装USB转串口驱动(PL2303)

    主机:Gentoo Linux 11.2 内核版本:Linux 3.0.6 以前在Ubuntu下USB转串口驱动已经编译进内核,而编译的Gentoo内核没有编译进去,这里将内核中PL2303驱动 co ...

  8. Linux下安装USB转串口驱动(PL2303)

    分类: 嵌入式ARM+Linux 2012-01-11 22:26  2077人阅读  评论(0)  收藏  举报 主机:Gentoo Linux 11.2 内核版本:Linux 3.0.6 原创作品 ...

  9. linux 下串口转usb不能发送数据包,Linux ,USB转串口驱动,没法读到数据

    Linux ,USB转串口驱动,无法读到数据 usb 1-1.1: new full-speed USB device number 5 using ehci-pci usb 1-1.1: New U ...

最新文章

  1. VTK:Math之VectorNorm
  2. 如何使用APTonCD备份和还原已安装的Ubuntu软件包
  3. 禁止word另存为,禁止图片另存为excel禁止另存为
  4. SpringBoot无法找到加载类 ,应用Feign其他服务无法package两类问题
  5. 20165302 学习基础和C语言基础调查
  6. 基于JAVA+Swing+MYSQL的超市管理系统
  7. java paint的使用_java GUI编程之paint绘制操作示例
  8. 建立域用户时遇到的问题。
  9. c++_string与double/int互转
  10. 一文读懂电感器的原理、结构、作用及分类
  11. java 右下角_java右下角弹窗
  12. 7-4 新浪微博热门话题 (20 分) 新浪微博可以在发言中嵌入“话题”,即将发言中的话题文字写在一对“#”之间,就可以生成话题链接,点击链接可以看到有多少人在跟自己讨论相同或者相似的话题。新浪微博还
  13. linux测试上下行最大网速和实时网速
  14. 苍狼敏捷软件开发团队建设指南-1-团队模型
  15. 【面经】数据分析岗_面试题整理总结(持续更新中…)
  16. 零基础学习微信小程序(7):组件
  17. iframe vue 宽度_vue 如何自适应调整嵌入的 iframe 的大小,让用户完全感觉不出有 iframe 这个东西?...
  18. 期货ctp持仓查询与更新
  19. 当我去了不到 20 人的 IT 公司后。。。
  20. 开发运维-常用远程桌面开源软件

热门文章

  1. 【Vim】基本操作及命令集详解
  2. awk 处理反算日志流量
  3. 基于ILP的最优PMU放置优化研究(Matlab代码实现)
  4. 模型的偏差、方差分析
  5. 2610_lab2 命令行与快捷键
  6. .xin 是什么域名?个人能使用吗?
  7. vfp报表纸张设置_vfp9终结一直以来的打印纸张设置
  8. excel2010的使用笔记
  9. HTML+CSS实现基础课程表页面制作 (锚链接到页面下方课程详细信息)~小白入门版~
  10. 工控电脑触摸显示屏幕监控报警方案-基于WiFi摄像头定时拍照图像识别技术