​某设备提供了USB串口功能,上位机(Host端)可以通过USB串口与之通信。对于Linux上位机,比如Ubuntu,自带usbserial驱动,当安装usbserial驱动后,上位机就会生成ttyUSBx(x=0~n)设备,通过ttyUSBx就能与设备端进行USB串口通信。

该设备不仅提供了USB串口,也同时提供了adb口的功能。实际应用中发现,将设备通过USB线连接到Ubuntu PC后,有生成adb口,adb能正常使用;但是串口驱动默认没有加载,手动insmod usbserial后,串口可以正常使用,但是adb口却不通了。

usbserial的使用方法可参考kernel document:Documentation/usb/usb-serial.txt

  1. insmod usbserial vendor=0x#### product=0x####
  2. ####是vendor id和product id。

调查发现,Ubuntu加载usbserial驱动后,原来的adb口会被识别为USB串口,生成新的ttyUSBx设备,导致Ubuntu端的adb无法正常与设备端通信。从这个现象看,是Ubuntu端usbserial驱动错误地匹配了本来不应该属于它匹配的设备。具体情况如下。

成功执行adb shell后,加载usbserial驱动之前,在Ubuntu上执行 lsusb -t 指令得到的结果截取如下:

Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/10p, 480M

|__ Port 6: Dev 4, If 5, Class=Vendor Specific Class, Driver=, 480M

|__ Port 6: Dev 4, If 6, Class=Vendor Specific Class, Driver=usbfs, 480M

加载usbserial驱动之后,再次插拔USB,在Ubuntu上执行 lsusb -t 指令得到的结果截取如下:

Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/10p, 480M

|__ Port 6: Dev 4, If 5, Class=Vendor Specific Class, Driver=usbserial_generic, 480M

|__ Port 6: Dev 4, If 6, Class=Vendor Specific Class, Driver=usbserial_generic, 480M

If 5对应的是串口,If 6对应的是adb口;正常情况下,adb口匹配的驱动是usbfs ,异常情况下,adb口匹配的驱动是 usbserial_generic 。

为什么usbserial驱动会错误地匹配了本来不应该属于它匹配的设备呢?这个需要从源码码中去寻找答案(Linus名言:Read the fu*k source code  ^_^)。

代码路径在 drivers/usb/serial/*,关键代码如下,从match_flags可以看出,usb generic serial驱动是简单地通过vendor id和product id来匹配设备。

  1. drivers/usb/serial/generic.c
  2.  
  3. int usb_serial_generic_register(void)
  4. {
  5. int retval = 0;
  6.  
  7. #ifdef CONFIG_USB_SERIAL_GENERIC
  8. generic_device_ids[0].idVendor = vendor;
  9. generic_device_ids[0].idProduct = product;
  10. generic_device_ids[0].match_flags =
  11. USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
  12.  
  13. retval = usb_serial_register_drivers(serial_drivers,
  14. "usbserial_generic", generic_device_ids);
  15. #endif
  16. return retval;
  17. }
  18.  
  19. drivers/usb/serial/usb-serial.c
  20.  
  21. static int __init usb_serial_init(void)
  22. {
  23. int result;
  24.  
  25. usb_serial_tty_driver = alloc_tty_driver(USB_SERIAL_TTY_MINORS);
  26. ...
  27. /* register the generic driver, if we should */
  28. result = usb_serial_generic_register();

由于该设备的adb口和串口都是基于同样的USB设备,具有同样的vendor id和product id,那adb口被识别成串口,也就能理解了。

原因找到了,那有没有办法解决这个问题呢?

1)上位机先执行adb shell把adb口占住,再加载usbserial驱动,则adb口不会被重新匹配serial驱动;但是重新插拔USB后,又需要卸载usbserial驱动,再重复此过程。临时用用倒是可以。

2)修改usbserial驱动。在开发嵌入式产品时,一般上位机也是需要源码级开发的,在这种场景下修改usbserial驱动比较适用。

看看adb和generic串口的接口描述符信息。在Ubuntu上执行lsusb -v指令后得到的adb接口描述符如下:

Interface Descriptor:

bLength                 9

bDescriptorType         4

bInterfaceNumber        6

bAlternateSetting       0

bNumEndpoints           2

bInterfaceClass       255 Vendor Specific Class

bInterfaceSubClass     66

bInterfaceProtocol      1

iInterface             10 ADB Interface

usb generic串口的接口描述符如下:

Interface Descriptor:

bLength                 9

bDescriptorType         4

bInterfaceNumber        5

bAlternateSetting       0

bNumEndpoints           2

bInterfaceClass       255 Vendor Specific Class

bInterfaceSubClass     0

bInterfaceProtocol      0

可以看到两者的 bInterfaceClass 是一样的,都是 Vendor Specific Class ,但是 bInterfaceSubClass 和 bInterfaceProtocol 不一样。所以可以考虑修改usbserial驱动的匹配规则,除了vendor id和product id之外,再加入按interface class和interface protocol联合匹配。

除了USB generic serial驱动外,还有一些厂商自定义的USB serial驱动,比如 drivers/usb/serial/option.c,该驱动就在probe函数里面设置了一些判断条件,避免匹配上不该匹配的设备,可用来参考。如下,代码注释得相当详细,不再赘述。

  1. static int option_probe(struct usb_serial *serial,
  2. const struct usb_device_id *id)
  3. {
  4. struct usb_interface_descriptor *iface_desc =
  5. &serial->interface->cur_altsetting->desc;
  6. unsigned long device_flags = id->driver_info;
  7.  
  8. /* Never bind to the CD-Rom emulation interface */
  9. if (iface_desc->bInterfaceClass == USB_CLASS_MASS_STORAGE)
  10. return -ENODEV;
  11.  
  12. /*
  13. * Don't bind reserved interfaces (like network ones) which often have
  14. * the same class/subclass/protocol as the serial interfaces. Look at
  15. * the Windows driver .INF files for reserved interface numbers.
  16. */
  17. if (device_flags & RSVD(iface_desc->bInterfaceNumber))
  18. return -ENODEV;
  19.  
  20. /*
  21. * Allow matching on bNumEndpoints for devices whose interface numbers
  22. * can change (e.g. Quectel EP06).
  23. */
  24. if (device_flags & NUMEP2 && iface_desc->bNumEndpoints != 2)
  25. return -ENODEV;
  26.  
  27. /* Store the device flags so we can use them during attach. */
  28. usb_set_serial_data(serial, (void *)device_flags);
  29.  
  30. return 0;
  31. }

欢迎关注我的公众号,一起交流。微信搜索“大鱼嵌入式”或者扫描下列二维码。

加载usbserial驱动后,为什么adb不可用了?的更多相关文章

  1. Intel Nehalem微架构Calpella平台机型Windows XP系统下如何开启AHCI硬盘工作模式(XP系统下如何加载AHCI驱动)

    问题描述用户来电表示使用IDE模式安装XP系统后开启AHCI模式会出现开机蓝屏重启的问题,咨询如何在XP下加载AHCI驱动,以便开启BIOS中AHCI选项来发挥硬盘的最佳性能   问题分析 Windo ...

  2. Linux中mod相关的命令 内核模块化 mod相关命令都是用来动态加载内核模块/驱动程序模块

    Linux中mod相关的命令 内核模块化   mod相关命令都是用来动态加载内核模块/驱动程序模块 http://baike.baidu.com/link?url=lxiKxFvYm-UfJIxMjz ...

  3. 无软驱加载raid驱动安装windows2003及其他微软操作系统

    [转载]http://blog.zol.com.cn/2650/article_2649199.html [另一篇]http://www.blue1000.com/bkhtml/c159/2013-0 ...

  4. win10系统加载ahci驱动的操作方案(Win10之家)

    win10系统使用的过程中很多用户会想要加载ahci驱动,但是大部分用户根本不知道怎么操作加载ahci驱动,这样的话很多用户会遇到一些问题,那如果使用的过程中想要加载ahci驱动的话我们应该怎么操作呢 ...

  5. JDBC:加载数据库驱动、连接数据库(详细讲解)

    加载数据库驱动: 1)由于Java是一个纯面向对象语言,任何事物在其中都必须抽象成类或者类对象,数据库也不例外,JDBC同样也把数据库抽象成面向对象的结构: 2)JDBC将整个数据库驱动器在底层抽象成 ...

  6. ThinkCMF项目部署出现无法加载数据库驱动解决方案

    最近有个TP项目刚从从本地部署到阿里云服务器上,出现了无法加载数据库驱动的错误,提示 :( 无法加载数据库驱动: Think\Db\Driver 这里分享一下出现该错误的解决步骤: 首先记得项目部署到 ...

  7. java 加载数据库驱动

    JDBC编程步骤见 JDBC编程步骤 JDBC编程的第一步是加载数据库驱动,使用Class类的forName()方法,Class.forName("com.mysql.jdbc.Driver ...

  8. java加载jdbc驱动三种方式的比较

    一.引言 平时连接数据库的时候首先要加载jdbc驱动,这一步骤其实有三种方式,他们的区别?优劣? 二.快速了解三种加载方式 Class.forName(“com.mysql.jdbc.Driver”) ...

  9. JDBC 学习笔记(四)—— JDBC 加载数据库驱动,获取数据库连接

    1. 加载数据库驱动 通常来说,JDBC 使用 Class 类的 forName() 静态方法来加载驱动,需要输入数据库驱动代表的字符串. 例如: 加载 MySQL 驱动: Class.forName ...

随机推荐

  1. Typora常用编辑方法-一个能将写博客变作享受的工具

    1,标题 ctrl+数字(1~5) 2,序号 数字序号 数字 + . +空格,之后回车换行会自动产生数字序号 非数字序号 有三种 实心圆 ,非实心圆与实心方框 都是 +空格 ,之后按tab键向内缩进, ...

  2. 「HTML+CSS」--自定义按钮样式【002】

    前言 Hello!小伙伴! 首先非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 哈哈 自我介绍一下 昵称:海轰 标签:程序猿一只|C++选手|学生 简介:因C语言结识编程,随后转入计算机 ...

  3. Java 并发编程 Executor 框架

    本文部分摘自<Java 并发编程的艺术> Excutor 框架 1. 两级调度模型 在 HotSpot VM 的线程模型中,Java 线程被一对一映射为本地操作系统线程.在上层,Java ...

  4. MongoDB教程--配置与入门

    MongoDB简介 阿里云配置MongoDB 数据库的增删查改 MongoDB 数据最重要的操作是Key-Value的映射.有了这样的映射,可以直接通过关键字去寻找想要的值.例如,通过用户的ID寻找与 ...

  5. JavaCV 采集摄像头和麦克风数据推送到流媒体服务器

    越来越觉得放弃JavaCV FFmpeg native API,直接使用JavaCV二次封装的API开发是很明智的选择,使用JavaCV二次封装的API开发避免了各种内存操作不当引起的crash. 上 ...

  6. 「HTML+CSS」--自定义加载动画【015】

    前言 Hello!小伙伴! 首先非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 哈哈 自我介绍一下 昵称:海轰 标签:程序猿一只|C++选手|学生 简介:因C语言结识编程,随后转入计算机 ...

  7. 剑指offer刷题

    1.面试题43. 1-n整数中1出现的次数 输入一个整数 n ,求1-n这n个整数的十进制表示中1出现的次数. 例如,输入12,1-12这些整数中包含1 的数字有1.10.11和12,1一共出现了5次 ...

  8. 告别DNS劫持,一文读懂DoH

    如果评选一个差评服务器榜单,除去育碧高居榜首外,一定也少不了 Nintendo Switch 让人头秃的联网服务.尽管任天堂已经架设了香港 CDN 服务器用于加速,但是更新安装的速度也没有什么大幅改变 ...

  9. yolov2算法浅见

    因为最近在复习yolo系列的算法,就借着这个机会总结一下自己对这个算法的理解,由于是第一次写算法类的博客,文中有什么错误和行文不通的地方还希望大家指正. yolov2与yolov1有很多改变. 最重要 ...

  10. python对BP神经网络实现

    python对BP神经网络实现 一.概念理解 开始之前首先了解一下BP神经网络,BP的英文是back propagationd的意思,它是一种按误差反向传播(简称误差反传)训练的多层前馈网络,其算法称 ...