123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- /*
- * 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 int otg_id_suspended;
- static bool otg_id_pending;
- 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;
- bool proxy_wait = false;
- if (plist_head_empty(&otg_id_plist))
- return;
- plist_for_each_entry(otg_id_nb, &otg_id_plist, p) {
- if (proxy_wait) {
- if (otg_id_nb->proxy_wait)
- ret = otg_id_nb->proxy_wait(otg_id_nb);
- } else {
- ret = otg_id_nb->detect(otg_id_nb);
- }
- if (ret == OTG_ID_HANDLED) {
- otg_id_active = otg_id_nb;
- return;
- }
- if (ret == OTG_ID_PROXY_WAIT)
- proxy_wait = true;
- }
- 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;
- if (otg_id_suspended != 0) {
- otg_id_pending = true;
- goto out;
- }
- __otg_id_notify();
- out:
- mutex_unlock(&otg_id_lock);
- }
- /**
- * otg_id_suspend
- *
- * Mark the otg_id subsystem as going into suspend. From here on out,
- * any notifications will be deferred until the last otg_id client resumes.
- * If there is a pending notification when calling this function, it will
- * return a negative errno and expects that the caller will abort suspend.
- * Returs 0 on success.
- */
- int otg_id_suspend(void)
- {
- int ret = 0;
- mutex_lock(&otg_id_lock);
- /*
- * if there's a pending notification, tell the caller to abort suspend
- */
- if (otg_id_suspended != 0 && otg_id_pending) {
- pr_info("otg_id: pending notification, should abort suspend\n");
- ret = -EBUSY;
- goto out;
- }
- otg_id_suspended++;
- out:
- mutex_unlock(&otg_id_lock);
- return ret;
- }
- /**
- * otg_id_resume
- *
- * Inform the otg_id subsystem that a client is resuming. If this is the
- * last client to be resumed and there's a pending notification,
- * otg_id_notify() is called.
- */
- void otg_id_resume(void)
- {
- mutex_lock(&otg_id_lock);
- if (WARN(!otg_id_suspended, "unbalanced otg_id_resume\n"))
- goto out;
- if (--otg_id_suspended == 0) {
- if (otg_id_pending) {
- pr_info("otg_id: had pending notification\n");
- otg_id_pending = false;
- __otg_id_notify();
- }
- }
- out:
- mutex_unlock(&otg_id_lock);
- }
|