linux下usb转串口驱动分析
来源:互联网 发布:广电网络投诉电话多少 编辑:程序博客网 时间:2024/05/16 10:56
http://blog.csdn.net/alading664/article/details/7638714
最近要做一个关于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模块的初始化过程
- 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;
- }
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的模块初始化函数
- 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;
- }
int usb_serial_generic_register(int _debug){int retval = 0;debug = _debug;#ifdef CONFIG_USB_SERIAL_GENERICgeneric_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:#endifreturn retval;}
第一步 首先确定自己的特征码,这里通过vendor 和 product表明自己是哪种设备的驱动
第二步 将用户驱动注册进usb_serial_bus总线,linux很喜欢这么搞……,通过总线来管理一类设备
第三步 将usb驱动注册进usb_core的驱动列表
有人会问,为什么usb会自动发现对应的驱动,这其实是个匹配的过程,linux里面叫probe
前面第一步是确定了驱动的匹配值,第二步和第三步都把驱动的匹配值注册进去
事实上前面第三步代码retval = usb_register(&generic_driver);就是为了把generic驱动注册进usb的驱动列表,当有设备插入时,会轮询到它,然后会调用此驱动的probe,就是下面的函数
- 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;
- }
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驱动里面的
- 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,
- };
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
- 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;
- }
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转串口驱动分析
- linux下usb转串口驱动分析
- linux下usb转串口驱动分析
- linux下usb转串口驱动分析
- Linux下USB转串口的驱动
- Linux下USB转串口的驱动
- Linux下USB转串口的驱动
- Linux下USB转串口驱动
- linux下查看USB转串口驱动
- Linux下USB转串口的驱动
- Linux下USB转串口的驱动
- USB-serial驱动分析(usb转串口)
- Windows XP下USB转串口驱动编码实现分析
- linux驱动基础系列--Linux 串口、usb转串口驱动分析
- Linux下USB驱动框架分析 转
- Linux下安装USB转串口驱动(PL2303)【转】
- Linux下安装USB转串口驱动(PL2303)
- Linux环境下使用 USB转串口驱动(一)
- Android APK反编译详解(附图)
- 使用ANT打包Android应用
- watir语法
- Gridview后台添加模板列
- 创业者一定要学的十句英语 (zz.is2120)
- linux下usb转串口驱动分析
- 记事本
- JAVA--第五周实验--任务2--封装一类梯形形对象Triangle,并实现一些求周长求面积的功能。
- 矩阵乘法求解
- Emacs和Vim:神的编辑器和编辑器之神
- 程序语言与程序员的快乐
- 使用debugger在dojo小部件中调试代码
- 12个有趣的C语言面试题
- 去除字符串中多余的空格 C语言实现