USB: otg: add otg id notifier utiltiies

Add a otg_id notifier to allow multiple drivers to cooperate to
determine the type of cable connected to a USB connector without
requiring direct calls between the drivers.

Change-Id: Ic5675f1a89daf85b17336765de24e4bdb6df6348
Signed-off-by: Colin Cross <ccross@android.com>
This commit is contained in:
Colin Cross 2011-06-24 15:58:51 -07:00 committed by Dima Zavin
parent 2fc52d6458
commit ab246b81fb
3 changed files with 189 additions and 0 deletions

View File

@ -8,6 +8,7 @@ ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG
# infrastructure
obj-$(CONFIG_USB_OTG_UTILS) += otg.o
obj-$(CONFIG_USB_OTG_WAKELOCK) += otg-wakelock.o
obj-$(CONFIG_USB_OTG_UTILS) += otg_id.o
# transceiver drivers
obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o

138
drivers/usb/otg/otg_id.c Normal file
View File

@ -0,0 +1,138 @@
/*
* Copyright (C) 2011 Google, Inc.
*
* Author:
* Colin Cross <ccross@android.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/usb/otg_id.h>
static DEFINE_MUTEX(otg_id_lock);
static struct plist_head otg_id_plist =
PLIST_HEAD_INIT(otg_id_plist);
static struct otg_id_notifier_block *otg_id_active;
static bool otg_id_cancelling;
static bool otg_id_inited;
static void otg_id_cancel(void)
{
if (otg_id_active) {
otg_id_cancelling = true;
mutex_unlock(&otg_id_lock);
otg_id_active->cancel(otg_id_active);
mutex_lock(&otg_id_lock);
otg_id_cancelling = false;
}
}
static void __otg_id_notify(void)
{
int ret;
struct otg_id_notifier_block *otg_id_nb;
if (plist_head_empty(&otg_id_plist))
return;
plist_for_each_entry(otg_id_nb, &otg_id_plist, p) {
ret = otg_id_nb->detect(otg_id_nb);
if (ret == OTG_ID_HANDLED) {
otg_id_active = otg_id_nb;
return;
}
}
WARN(1, "otg id event not handled");
otg_id_active = NULL;
}
int otg_id_init(void)
{
mutex_lock(&otg_id_lock);
otg_id_inited = true;
__otg_id_notify();
mutex_unlock(&otg_id_lock);
return 0;
}
late_initcall(otg_id_init);
/**
* otg_id_register_notifier
* @otg_id_nb: notifier block containing priority and callback function
*
* Register a notifier that will be called on any USB cable state change.
* The priority determines the order the callback will be called in, a higher
* number will be called first. A callback function needs to determine the
* type of USB cable that is connected. If it can determine the type, it
* should notify the appropriate drivers (for example, call an otg notifier
* with USB_EVENT_VBUS), and return OTG_ID_HANDLED. Once a callback has
* returned OTG_ID_HANDLED, it is responsible for calling otg_id_notify() when
* the detected USB cable is disconnected.
*/
int otg_id_register_notifier(struct otg_id_notifier_block *otg_id_nb)
{
plist_node_init(&otg_id_nb->p, otg_id_nb->priority);
mutex_lock(&otg_id_lock);
plist_add(&otg_id_nb->p, &otg_id_plist);
if (otg_id_inited) {
otg_id_cancel();
__otg_id_notify();
}
mutex_unlock(&otg_id_lock);
return 0;
}
void otg_id_unregister_notifier(struct otg_id_notifier_block *otg_id_nb)
{
mutex_lock(&otg_id_lock);
plist_del(&otg_id_nb->p, &otg_id_plist);
if (otg_id_inited && (otg_id_active == otg_id_nb)) {
otg_id_cancel();
__otg_id_notify();
}
mutex_unlock(&otg_id_lock);
}
/**
* otg_id_notify
*
* Notify listeners on any USB cable state change.
*
* A driver may only call otg_id_notify if it returned OTG_ID_HANDLED the last
* time it's notifier was called, and it's cancel function has not been called.
*/
void otg_id_notify(void)
{
mutex_lock(&otg_id_lock);
if (otg_id_cancelling)
goto out;
__otg_id_notify();
out:
mutex_unlock(&otg_id_lock);
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2011 Google, Inc.
*
* Author:
* Colin Cross <ccross@android.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __LINUX_USB_OTG_ID_H
#define __LINUX_USB_OTG_ID_H
#include <linux/notifier.h>
#include <linux/plist.h>
/**
* otg_id_notifier_block
*
* @priority: Order the notifications will be called in. Higher numbers
* get called first.
* @detect: Called during otg_id_notify. Return OTG_ID_HANDLED if the USB cable
* has been identified
* @cancel: Called after detect has returned OTG_ID_HANDLED to ask it to
* release detection resources to allow a new identification to occur.
*/
struct otg_id_notifier_block {
int priority;
int (*detect)(struct otg_id_notifier_block *otg_id_nb);
void (*cancel)(struct otg_id_notifier_block *otg_id_nb);
struct plist_node p;
};
#define OTG_ID_HANDLED 1
#define OTG_ID_UNHANDLED 0
int otg_id_register_notifier(struct otg_id_notifier_block *otg_id_nb);
void otg_id_unregister_notifier(struct otg_id_notifier_block *otg_id_nb);
void otg_id_notify(void);
#endif /* __LINUX_USB_OTG_ID_H */