122 lines
2.9 KiB
C
122 lines
2.9 KiB
C
|
#include <linux/device.h>
|
||
|
#include <linux/module.h>
|
||
|
|
||
|
#include <linux/notifier.h>
|
||
|
#include <linux/sec_class.h>
|
||
|
#include <linux/vbus_notifier.h>
|
||
|
|
||
|
#define SET_VBUS_NOTIFIER_BLOCK(nb, fn, dev) do { \
|
||
|
(nb)->notifier_call = (fn); \
|
||
|
(nb)->priority = (dev); \
|
||
|
} while (0)
|
||
|
|
||
|
#define DESTROY_VBUS_NOTIFIER_BLOCK(nb) \
|
||
|
SET_VBUS_NOTIFIER_BLOCK(nb, NULL, -1)
|
||
|
|
||
|
static struct vbus_notifier_struct vbus_notifier;
|
||
|
struct blocking_notifier_head vbus_notifier_head =
|
||
|
BLOCKING_NOTIFIER_INIT(vbus_notifier_head);
|
||
|
static DEFINE_MUTEX(vbus_mutex);
|
||
|
|
||
|
int vbus_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
|
||
|
vbus_notifier_device_t listener)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_info("%s: listener=%d register\n", __func__, listener);
|
||
|
|
||
|
SET_VBUS_NOTIFIER_BLOCK(nb, notifier, listener);
|
||
|
ret = blocking_notifier_chain_register(&vbus_notifier_head, nb);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s: blocking_notifier_chain_register error(%d)\n",
|
||
|
__func__, ret);
|
||
|
|
||
|
mutex_lock(&vbus_mutex);
|
||
|
if (vbus_notifier.vbus_type != STATUS_VBUS_UNKNOWN)
|
||
|
nb->notifier_call(nb, vbus_notifier.cmd, &(vbus_notifier.vbus_type));
|
||
|
mutex_unlock(&vbus_mutex);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(vbus_notifier_register);
|
||
|
|
||
|
int vbus_notifier_unregister(struct notifier_block *nb)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_info("%s: listener=%d unregister\n", __func__, nb->priority);
|
||
|
|
||
|
ret = blocking_notifier_chain_unregister(&vbus_notifier_head, nb);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
|
||
|
__func__, ret);
|
||
|
DESTROY_VBUS_NOTIFIER_BLOCK(nb);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(vbus_notifier_unregister);
|
||
|
|
||
|
static int vbus_notifier_notify(void)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_info("%s: CMD=%d, DATA=%d\n", __func__, vbus_notifier.cmd,
|
||
|
vbus_notifier.vbus_type);
|
||
|
|
||
|
ret = blocking_notifier_call_chain(&vbus_notifier_head,
|
||
|
vbus_notifier.cmd, &(vbus_notifier.vbus_type));
|
||
|
|
||
|
switch (ret) {
|
||
|
case NOTIFY_STOP_MASK:
|
||
|
case NOTIFY_BAD:
|
||
|
pr_err("%s: notify error occur(0x%x)\n", __func__, ret);
|
||
|
break;
|
||
|
case NOTIFY_DONE:
|
||
|
case NOTIFY_OK:
|
||
|
pr_info("%s: notify done(0x%x)\n", __func__, ret);
|
||
|
break;
|
||
|
default:
|
||
|
pr_info("%s: notify status unknown(0x%x)\n", __func__, ret);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void vbus_notifier_handle(vbus_status_t new_dev)
|
||
|
{
|
||
|
pr_info("%s: (%d)->(%d)\n", __func__, vbus_notifier.vbus_type, new_dev);
|
||
|
|
||
|
if (vbus_notifier.vbus_type == new_dev)
|
||
|
return;
|
||
|
|
||
|
mutex_lock(&vbus_mutex);
|
||
|
if (new_dev == STATUS_VBUS_HIGH)
|
||
|
vbus_notifier.cmd = VBUS_NOTIFY_CMD_RISING;
|
||
|
else if (new_dev == STATUS_VBUS_LOW)
|
||
|
vbus_notifier.cmd = VBUS_NOTIFY_CMD_FALLING;
|
||
|
|
||
|
vbus_notifier.vbus_type = new_dev;
|
||
|
mutex_unlock(&vbus_mutex);
|
||
|
|
||
|
/* vbus attach broadcast */
|
||
|
vbus_notifier_notify();
|
||
|
}
|
||
|
EXPORT_SYMBOL(vbus_notifier_handle);
|
||
|
|
||
|
static int __init vbus_notifier_init(void)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void __exit vbus_notifier_exit(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
module_init(vbus_notifier_init);
|
||
|
module_exit(vbus_notifier_exit);
|
||
|
|
||
|
MODULE_AUTHOR("Samsung USB Team");
|
||
|
MODULE_DESCRIPTION("Vbus Notifier");
|
||
|
MODULE_LICENSE("GPL");
|