Android中的otg检测
先来看下usb otg线和micro usb线的区别。
对应的usb连线如下
检测原理
当手机连接到电脑时,vbus为高电平,触发了vbus中断,在中断中确认为高电平后,手机的usb设置从设备,摆出usb线时,vbus变低。
当otg线(id脚和gnd脚是短路的)连接手机后,触发了id中断,检测到id脚为低电平后,手机的usb设置主设备,摆出usb线时,id脚变高。
这里以展讯的代码示例如下,
kernel/drivers/usb/musb/musb_sprd.c
static void musb_sprd_set_otg_en(struct sprd_glue *glue, int enable)
{
if (gpio_is_valid(glue->gpio_otg)) {
dev_dbg(glue->dev, "musb gpio_set_value %d\n", enable);
if (enable) {
/*
* Two mode for ext IC:
* mode 1. set gpio_otg high for 0.25A
* mode 2. make a gpio_otg pulse high-low-high for 0.55A,
* mode 2 is the typical mode suggest by provider.
*/
gpio_set_value(glue->gpio_otg, 1);
gpio_set_value(glue->gpio_otg, 0);
gpio_set_value(glue->gpio_otg, 1);
} else {
gpio_set_value(glue->gpio_otg, 0);
}
} else {
sprd_extic_otg_power(!!enable);
}
}
static irqreturn_t musb_sprd_vbus_handler(int irq, void *dev_id)
{
struct sprd_glue *glue = (struct sprd_glue *)dev_id;
struct musb *musb = platform_get_drvdata(glue->musb);
int value;
value = !!gpio_get_value(glue->gpio_device);
irq_set_irq_type(irq,value ? IRQ_TYPE_LEVEL_LOW : IRQ_TYPE_LEVEL_HIGH);
if (glue->vbus_active == value)//vbus_active在probe里初始化为0,如果与上一次电平一致,不需要出来
return IRQ_HANDLED;
else if (value)// usb线插入
glue->wq_mode = USB_DR_MODE_PERIPHERAL;
else if (glue->dr_mode != USB_DR_MODE_PERIPHERAL) {//不是从设备拔出没意义
dev_info(glue->dev, "%s ignore the vbus event\n", __func__);
return IRQ_HANDLED;
}
glue->vbus_active = value;//记录vbus的电平
MUSB_DEV_MODE(musb);
schedule_work(&glue->work);
return IRQ_HANDLED;
}
static irqreturn_t musb_sprd_usbid_handler(int irq, void *dev_id)
{
struct sprd_glue *glue = (struct sprd_glue *)dev_id;
int value;
value = !!gpio_get_value(glue->gpio_host);
value = !value;
irq_set_irq_type(irq,value ? IRQ_TYPE_LEVEL_HIGH : IRQ_TYPE_LEVEL_LOW);
glue->vbus_active = value;
glue->wq_mode = USB_DR_MODE_HOST;
schedule_work(&glue->work);
return IRQ_HANDLED;
}
static void sprd_musb_work(struct work_struct *work)
{
struct sprd_glue *glue = container_of(work, struct sprd_glue, work);
struct musb *musb = platform_get_drvdata(glue->musb);
struct usb_charger *uchger = musb->g.charger;
unsigned long flags;
bool charging_only = false;
int ret;
int cnt = 100;
glue->dr_mode = glue->wq_mode;
dev_dbg(glue->dev, "%s enter: vbus = %d mode = %d\n",
__func__, glue->vbus_active, glue->dr_mode);
disable_irq_nosync(glue->vbus_irq);
if (glue->vbus_active) {
if (glue->dr_mode == USB_DR_MODE_PERIPHERAL)
usb_gadget_set_state(&musb->g, USB_STATE_ATTACHED);
if (glue->dr_mode == USB_DR_MODE_HOST)
musb_sprd_set_otg_en(glue, 1);
goto end;
} else {
usb_gadget_set_state(&musb->g, USB_STATE_REMOVED);
if (glue->dr_mode == USB_DR_MODE_PERIPHERAL) {
u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
musb_writeb(musb->mregs, MUSB_DEVCTL,
devctl & ~MUSB_DEVCTL_SESSION);
musb->shutdowning = 1;
usb_phy_post_init(glue->xceiv);
cnt = 10;
while (musb->shutdowning && cnt-- > 0)
msleep(50);
}
if (glue->dr_mode == USB_DR_MODE_HOST)
musb_sprd_set_otg_en(glue, 0);
goto end;
}
end:
enable_irq(glue->vbus_irq);
}
当usb作为主设备时,需要提供5v的电压给从设备供电(cpu和pmic这边的电压一般不超过3.3),一般需要增加升压芯片或者由支持otg的充电ic提供。