在Linux环境上使用SDX55模块时出现无法识别adb端口,但可以识别手机adb端口。
内核:Linux 4.19.26 系统:CentOS Linux release 7.8.2003 Modem:高通SDX55 连接方式:USB3.0(M.2)
通过分析dmesg log发现,手机与Linux设备连接使用的端口是USB2.0,而模块与Linux设备连接的端口是USB3.0。初步分析可能由于USB2.0和USB3.0的差异或者客户USB3.0的硬件有问题导致。再次对比测试,将SDX55模块通过USB2.0方式连接到Linux设备,发现可以正常识别adb端口。进一步确认我们的分析。由于adb涉及到的问题主要从主机侧驱动、主机侧adb、模块adbd状态来排查。接下来我们将对这些情况进行一一排查来确定问题:
由于对adb流程不熟悉,因此先排查一下USB内核驱动相关的初始化是否正常。
USB内核框架的代码在linux-4.19.26/drivers/usb目录下,入口函数为usb_init,在内核启动过程中加载执行,其代码如下图: kernel\drivers\usb\core\usb.c:
usb_init用于初始化usb内核框架,主要进行以下初始化:
usb驱动的匹配过程主要在hub初始化里面实现,接下里看一下hub初始化的流程。
kernel\drivers\usb\core\hub.c:
usb_hub_init主要做了以下事情:
usb_hub_init调用usb_register将hub_driver注册到usb总线驱动的列表里。然后调用hub_driver.probe加载hub驱动,hub_driver如下图:
hub_probe调用hub_configure注册了中断,一旦接入新的usb设备就会调用hub_irq
然后hub_irq中断处理完成后,开启hub_wq线程(kick_hub_wq(hub);)调用hub_event处理usb插入事件。下面详细介绍一下。
当usb设备插入之后usb总线会识别出adb端口,并且找到匹配的usbfs驱动加载,从而你完成adb设备的驱动初始化。当设备插入中断触发后,hub_irq处理中断后开启workqueue hub_wq调用hub_event处理插入事件。下面梳理一下流程。 hub_event调用遍历所有的port处理port event:
port_event调用hub_port_status进而调用hub_ext_port_status来处理端口事件:
如hub_ext_port_status获取到的hub port状态为0x203,即表示当前port有设备连接:
从port_event调用hub_port_connect_change最后调用到hub_port_connect,hub_port_connect主要作用是创建设备、给新设备选择新的编号,获取设备的各种描述符,然后调用usb_new_device注册新设备。 hub_port_connect ->choose_devnum 给新设备选择新的编号(地址) ->hub_port_init 把编号(地址)告诉USB设备,以后就使用这个地址了 ->usb_new_device usb_new_device调用usb_enumerate_device获取usb描述符
注:设备描述符在hub_port_init里面获取
如下图获取配置描述符,usb_get_configuration首先从设备描述符里面获取配置描述符的数量,然后遍历所有配置描述符并获取,将获取到描述符格式化到dev->rawdescriptors[cfgno]里面。
其他描述符这里不多赘述,只讲一下usb3.0和usb2.0的差异。usb3.0有超高速伙伴描述符usb_ss_ep_comp_descriptor,所以只有usb3.0会获取它,usb2.0是不会获取这个描述符的。如下图:
由于usb超高速伙伴描述符sizeof()=USB_DT_SS_EP_COMP_SIZE=6,保存该描述符后将buffer+6:
此时,所有的usb描述符都获取完毕,并解析保存完毕。
接下来回到usb_new_device,接下来调用add_device,此时usb总线匹配设备驱动,此时新插入的usb将于usb_init注册的驱动usb_generic_driver匹配,总线接下来加载该驱动识别并加载usb interface和驱动。
generic_probe做了两件事1、获取设备配置,2、应用配置。对usb设备的interface、endpoint进行设备注册与配置。
usb_set_configuration设置interface属性:
然后将interface设备加入到系统,然后初始化每个 interface对应的endpoint。add_device将match对应的interface设备驱动,总线将自动加载对应的驱动。
接下来编译interface的每个endpoint进行配置,将设备添加进系统中
至此内核驱动的初始化就完成了,当上层adb应用请求连接建立时将动态将usbfs设备驱动与设备进行关联,并加载驱动。也可以看一下usbfs驱动在usb_init时已经注册,他的probe函数是一个空函数,只有当上层adb设备请求的时候才会进行设备匹配。
当adb向usbfs驱动发起USBDEVFS_CLAIMINTERFACE请求后,此时将进行USB设备与usbfs驱动设备的绑定。 如下图为usb devio注册的函数,adb server发起的请求由usb devio进行处理:
adb的请求由usbdev_ioctl进行处理:
最终调用到claimintf,claimintf函数将进行ad端口与usbfs驱动进行绑定:
至此所有的驱动侧就完成了。
adb源码:https://android.googlesource.com/platform/system/core/ 可在以上链接获取adb源码集成到项目中。本次问题出现在adb server端,本文后续内容只对host侧adb server的相关代码进行解读,不涉及adbd和adb clinet。
adb由两个物理文件组成adb和adbd。adb client和adb server运行在host端,adbd守护进程运行在device侧,host和device通过tcp连接。
adb main函数调用adb_commandline对adb参数进行拼接组成adb command line。
adb_commandline调用adb_query_command然后调用adb_query查询adb server状态,如果adb server未运行则启动adb server:
launch_server在5037端口运行adb server,此时adb进程fork一个子进程,此时adb devices返回。子进程执行“adb –P 5037 fork-server server”启动adb server:
adb判断子进程需要启动server,调用adb_main启动adb server:
然后启动线程find usb devices:
如上图,find_usb_device读取字符设备/dev/bus/usb/
{device_id}获取设备各种描述符:
然后跳过设备描述符和配置描述符:
接下来在interface描述符里面查找adb端口,adb对应的interface描述符为endpoint为2,bInterfaceClass为255,bInterfaceSubClass为66,bInterfaceProtocol为1。当查找到adb接口的时候,遍历endpoint描述符,由于usb3.0 endpoint描述符后面会增加usb超高速伙伴描述符,且sizeof()=6,所以usb3.0需要在找到endpoint描述符后将指针偏移+6,USB2.0则不需要。具体的描述符解释请参考usb规范。
查找到adb interface后再调用驱动接口注册驱动。
自此adb server与usb驱动的绑定就完成了。
当执行adb devices的时候,adb log打印endpoints not found,对应代码如下图:
如下图的interface descriptor配置为对应的interface为adb设备的interface:
adb端口对应的usb interface有两个endpoint,ep5和ep89。对应的bInterfaceClass为255,bInterfaceSubClass为66,bInterfaceProtocol为1。adb设备在插入到主机后通过读取主机侧USB设备的descriptor找到对应的interface,其查找的条件满足以上4个条件即认为找到正确的adb设备,否则失败。
adb代码里面find_usb_device,当adb server在读取到的usb设备描述符里面查找adb对应的interface和endpoint描述符来注册adb,由于usb3.0在设备描述符里面会加入usb高速端点伙伴描述符,而在usb2.0的设备描述符里面没有。在usb3.0的情况下adb需要在查找到每一个endpoint描述服务后,将缓冲区偏移一个高速端点伙伴描述符的长度。由于客户的adb代码没有进行这个补丁的修改,导致usb2.0在查找endpoint描述符的时候出现问题,从而导致问题出现。
修改adb代码在Linux环境上,编译成adb二进制文件,进一步验证,问题不再复现。
确认ep个数为2,bInterfaceClass为255,bInterfaceSubClass为66,bInterfaceProtocol为1,即可确定usb驱动和模块adb端口是没有问题的,问题一定出现在主机侧。具体的主机侧问题或者adb工具问题根据实际情况分析。