blob: 90d1427ad4410538cd961f7626be569d66943867 [file] [log] [blame]
/*
* Copyright (C) 2016-2017 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.
*/
/* usb notify layer v3.2 */
#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";
case EXTERNAL_NOTIFY_HOSTBLOCK_PRE:
return "host_block_pre";
case EXTERNAL_NOTIFY_HOSTBLOCK_POST:
return "host_block_post";
case EXTERNAL_NOTIFY_MDMBLOCK_PRE:
return "mdm_block_pre";
case EXTERNAL_NOTIFY_MDMBLOCK_POST:
return "mdm_block_post";
case EXTERNAL_NOTIFY_POWERROLE:
return "power_role_notify";
case EXTERNAL_NOTIFY_DEVICEADD:
return "host_mode_device_added";
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";
case EXTERNAL_NOTIFY_DEV_PDIC:
return "pdic";
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 data)
{
int ret = 0;
pr_info("%s: cmd=%s(%lu), data=%d\n", __func__, cmd_string(cmd),
cmd, data);
create_external_notify();
ret = blocking_notifier_call_chain
(&(external_notifier.notifier_call_chain),
cmd, (void *)&(data));
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);