一、主要问题
本项目采用的是高通的SDM450平台,单USB口出来后接了USB HUB 同时可以连接电脑等,这就有个问题,当我同时连接电脑并将USB HUB上电后,在主从切换之间就有问题,也就是当处于主模式的时候,通过拉高USB_ID管脚进入从模式,这时候就会切换失败;或者处于从模式的时候,将USB_ID管脚拉低进入主模式的时候,也会切换失败。
二、原理图
主控部分原理图
usb_ID 控制部分原理图
Vbus电路原理图
通过原理图可以很清晰的看出,通过HOST_EN管脚可以控制USB_ID管脚,然后进行主从的状态切换
三、原理
高通Android9.0 kernel4.9 主从切换问题主要涉及到两个方面,一是通过OTG_ID 管脚的高低电平进行中断进行主模式的进入和退出;一是通过VBUS电压检测进行从模式的进入和退出。(注意这里只是相应模式的进入和退出,例如退出主模式后不一定是进入从模式,退出从模式不一定是进入主模式)
四、软件分析过程
用于VBUS和OTG_ID检测相关驱动
kernel/msm-4.9/drivers/platform/msm/gpio-usbdetect.c
用于设置主从状态相关驱动
kernel/msm-4.9/drivers/extcon/extcon.c
kernel/msm-4.9/drivers/extcon/driver.c
驱动简单分析
USB驱动器状态控制模块(extcon)主要做两件事
A、 注册extcon设备类
B、 提供相关的状态注册,状态设置等接口
状态检测驱动(gpio-usbdetect.c) 主要做三件事
A、 获取相关管脚并注册对应的中断处理函数
B、 向USB驱动器状态控制模块(extcon) 申请状态监听
C、 应对相关中断产生并向USB驱动器状态控制模块(extcon) 设置相应的状态用于上报
相关代码gpio-usbdetect.c
gpio_usbdetect_probe()
A、 获取id管脚并注册对应的中断处理函数
B、 向USB驱动器状态控制模块(extcon) 申请状态监听
C、应对相关中断产生并向USB驱动器状态控制模块(extcon) 设置相应的状态用于上报
Extcon.c驱动
注册 extcon类
还有一些其他相关的接口,不再一一贴出来
int extcon_dev_register(struct extcon_dev *edev)
int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id,
unsigned int prop,
union extcon_property_value prop_val)
int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id,
unsigned int prop)
int extcon_set_property(struct extcon_dev *edev, unsigned int id,
unsigned int prop,
union extcon_property_value prop_val)
int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id,
bool cable_state)
说明: Extcon 主要是实现usb驱动器的管理,提供包括USB状态器的注册,同步等,具体可以查看文件
kernel/msm-4.9/drivers/extcon/extcon.c
在我们的项目中对于USB驱动器状态管理模块来说,USB的状态有三种
EXTCON_USB
EXTCON_USB_HOST
EXTCON_NONE
当然实际不止这三种状态
根据调试过程log分析,得到以下一个问题
任何的一个切换都必须先USB退出当前状态,此时USB处于 EXTCON_NONE状态,然后再切换到HOST或者USB模式,否则就会有切换不过去的情况,而这些动作只是在两个中断处理函数里面做:
static irqreturn_t gpio_usbdetect_id_irq_thread(int irq, void *data)
static irqreturn_t gpio_usbdetect_vbus_irq(int irq, void *data)
这两个函数主要是处理VBUS产生中断和USB_ID产生中断,我们要做的就是修改里面的逻辑,使得当退出主模式时,不仅仅是将EXTCON_USB_HOST状态设置为0;当退出从模式的时候,不仅仅是将EXTCON_USB设置为0,而是完整的退出相应的模式。通过对比修改前后大概可以知道
gpio_usbdetect_id_irq_thread处理函数修改前
gpio_usbdetect_id_irq_thread处理函数修改后
由于我们的硬件设计时候,没有考虑到VBUS中断管脚的连接,使得VBUS的中断无法产生,我们只能在usb_id的中断处理函数里面通过一个 work来处理,若果插拔USB线可以产生VBUS中断的话,就可以把这个work的内容换到gpio_usbdetect_vbus_irq 里面,以下是work的处理
这两部分主要是进入EXTCON_USB_HOST 或者EXTCON_USB模式,只是在中断处理函数里面已经通过 extcon_set_property函数来完全退出上一个模式,然后再调用相应的work来进入一个新的模式
例如完全退出 EXTCON_USB 模式
val.intval = false;
extcon_set_property(usb->extcon_dev, EXTCON_USB,
EXTCON_PROP_USB_SS, val);
extcon_set_cable_state_(usb->extcon_dev, EXTCON_USB, 0);
完全退出EXTCON_USB_HOST模式
val.intval = false;
extcon_set_property(usb->extcon_dev, EXTCON_USB_HOST,
EXTCON_PROP_USB_SS, val);
extcon_set_cable_state_(usb->extcon_dev, EXTCON_USB_HOST, 0);