144 lines
3.2 KiB
C
144 lines
3.2 KiB
C
/*
|
|
* Copyright (C) 2015-2016 Samsung Electronics Co. Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "usb_notify: " fmt
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/err.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/usb_notify.h>
|
|
|
|
struct external_notify_struct {
|
|
struct blocking_notifier_head notifier_call_chain;
|
|
int call_chain_init;
|
|
};
|
|
|
|
#define SET_EXTERNAL_NOTIFY_BLOCK(nb, fn, dev) do { \
|
|
(nb)->notifier_call = (fn); \
|
|
(nb)->priority = (dev); \
|
|
} while (0)
|
|
|
|
#define DESTROY_EXTERNAL_NOTIFY_BLOCK(nb) \
|
|
SET_EXTERNAL_NOTIFY_BLOCK(nb, NULL, -1)
|
|
|
|
static struct external_notify_struct external_notifier;
|
|
|
|
static const char *cmd_string(unsigned long cmd)
|
|
{
|
|
switch (cmd) {
|
|
case EXTERNAL_NOTIFY_3S_NODEVICE:
|
|
return "3s_no_device";
|
|
case EXTERNAL_NOTIFY_DEVICE_CONNECT:
|
|
return "device_connect";
|
|
default:
|
|
return "undefined";
|
|
}
|
|
}
|
|
|
|
static const char *listener_string(int listener)
|
|
{
|
|
switch (listener) {
|
|
case EXTERNAL_NOTIFY_DEV_MUIC:
|
|
return "muic";
|
|
case EXTERNAL_NOTIFY_DEV_CHARGER:
|
|
return "charger";
|
|
default:
|
|
return "undefined";
|
|
}
|
|
}
|
|
|
|
static int create_external_notify(void)
|
|
{
|
|
if (!external_notifier.call_chain_init) {
|
|
pr_info("%s\n", __func__);
|
|
BLOCKING_INIT_NOTIFIER_HEAD
|
|
(&(external_notifier.notifier_call_chain));
|
|
external_notifier.call_chain_init = 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int usb_external_notify_register(struct notifier_block *nb, notifier_fn_t notifier,
|
|
int listener)
|
|
{
|
|
int ret = 0;
|
|
|
|
pr_info("%s: listener=(%s)%d register\n", __func__,
|
|
listener_string(listener), listener);
|
|
|
|
create_external_notify();
|
|
|
|
SET_EXTERNAL_NOTIFY_BLOCK(nb, notifier, listener);
|
|
ret = blocking_notifier_chain_register
|
|
(&(external_notifier.notifier_call_chain), nb);
|
|
if (ret < 0)
|
|
pr_err("%s: blocking_notifier_chain_register error(%d)\n",
|
|
__func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int usb_external_notify_unregister(struct notifier_block *nb)
|
|
{
|
|
int ret = 0;
|
|
|
|
pr_info("%s: listener=(%s)%d unregister\n", __func__,
|
|
listener_string(nb->priority),
|
|
nb->priority);
|
|
|
|
ret = blocking_notifier_chain_unregister
|
|
(&(external_notifier.notifier_call_chain), nb);
|
|
if (ret < 0)
|
|
pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
|
|
__func__, ret);
|
|
DESTROY_EXTERNAL_NOTIFY_BLOCK(nb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int send_external_notify(unsigned long cmd, int enable)
|
|
{
|
|
int ret = 0;
|
|
|
|
pr_info("%s: cmd=%s(%lu), enable=%d\n", __func__, cmd_string(cmd),
|
|
cmd, enable);
|
|
|
|
create_external_notify();
|
|
|
|
ret = blocking_notifier_call_chain
|
|
(&(external_notifier.notifier_call_chain), cmd, &(enable));
|
|
|
|
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;
|
|
}
|
|
|
|
static int __init external_notifier_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
pr_info("%s\n", __func__);
|
|
create_external_notify();
|
|
|
|
return ret;
|
|
}
|
|
module_init(external_notifier_init);
|