Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,68 @@
#
# USB notify configuration
#
comment "USB Notify features"
config USB_HOST_NOTIFY
boolean "USB Host notify Driver"
depends on USB_NOTIFY_LAYER
help
Android framework needs uevents for usb host operation.
Host notify Driver serves uevent format
that is used by usb host or otg.
config USB_NOTIFY_LAYER
boolean "USB notify layer"
depends on USB
help
Added usb notify layer on gadget,host,otg drivers.
The usb notify layer controls otg mode and vbus draw
and vbus detect irq.
config USB_NOTIFIER
boolean "USB notifier"
depends on USB_NOTIFY_LAYER
help
Added platform driver to call usb notify.
The usb_notifier.c can be fixed for each models.
config USB_DEBUG_DETAILED_LOG
boolean "USB detailed log for debugging"
depends on USB
help
Add detailed log for debugging.
config USB_STORAGE_DETECT
boolean "USB storage detect function"
depends on USB && SCSI
depends on USB_STORAGE
help
This feature enables to detect inserting or removing
sd card in sd reader device.
This must to be defined in /usb/storage/Kconfig
directory originally. But this feature is defined
in this Kconfig to gather samsung feature about usb host.
config USB_HMT_SAMSUNG_INPUT
boolean "USB HMT inputs for samsung hmt devices"
depends on HID
help
Some samsung smart phones support gear vr.
The samsung gear vr need some special hid input codes.
This feature enables special hid input codes.
config USB_EXTERNAL_NOTIFY
boolean "USB external notify"
depends on USB_NOTIFY_LAYER
help
Added usb external notify.
The usb external notify broadcast special situation
to muic and charger.
config USB_NOTIFY_PROC_LOG
boolean "USB proc log"
depends on USB_NOTIFY_LAYER
help
Added proc usblog.
The USB STATE, The USB MODE, Cable event
are saved in usblog heap.

View file

@ -0,0 +1,9 @@
# usb notify driver
obj-$(CONFIG_USB_HOST_NOTIFY) += host_notify_class.o
obj-$(CONFIG_USB_EXTERNAL_NOTIFY) += external_notify.o
obj-$(CONFIG_USB_NOTIFY_PROC_LOG) += usblog_proc_notify.o
obj-$(CONFIG_USB_NOTIFY_LAYER) += usb_notify_layer.o
usb_notify_layer-y := usb_notify.o usb_notify_sysfs.o dock_notify.o
obj-$(CONFIG_USB_NOTIFIER) += usb_notifier.o

View file

@ -0,0 +1,297 @@
/*
* Copyright (C) 2014 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 v2.0 */
#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/notifier.h>
#include <linux/version.h>
#include <linux/usb_notify.h>
#include "../core/hub.h"
#define SMARTDOCK_INDEX 1
#define MMDOCK_INDEX 2
struct dev_table {
struct usb_device_id dev;
int index;
};
static struct dev_table enable_notify_hub_table[] = {
{ .dev = { USB_DEVICE(0x0424, 0x2514), },
.index = SMARTDOCK_INDEX,
}, /* SMART DOCK HUB 1 */
{ .dev = { USB_DEVICE(0x1a40, 0x0101), },
.index = SMARTDOCK_INDEX,
}, /* SMART DOCK HUB 2 */
{ .dev = { USB_DEVICE(0x0424, 0x9512), },
.index = MMDOCK_INDEX,
}, /* SMSC USB LAN HUB 9512 */
{}
};
static struct dev_table essential_device_table[] = {
{ .dev = { USB_DEVICE(0x08bb, 0x2704), },
.index = SMARTDOCK_INDEX,
}, /* TI USB Audio DAC 1 */
{ .dev = { USB_DEVICE(0x08bb, 0x27c4), },
.index = SMARTDOCK_INDEX,
}, /* TI USB Audio DAC 2 */
{ .dev = { USB_DEVICE(0x0424, 0xec00), },
.index = MMDOCK_INDEX,
}, /* SMSC LAN Driver */
{}
};
static struct dev_table update_autotimer_device_table[] = {
{ .dev = { USB_DEVICE(0x04e8, 0xa500), },
.index = 5, /* 5 sec timer */
}, /* GearVR1 */
{ .dev = { USB_DEVICE(0x04e8, 0xa501), },
.index = 5,
}, /* GearVR2 */
{ .dev = { USB_DEVICE(0x04e8, 0xa502), },
.index = 5,
}, /* GearVR3 */
{}
};
static int check_essential_device(struct usb_device *dev, int index)
{
struct dev_table *id;
int ret = 0;
/* check VID, PID */
for (id = essential_device_table; id->dev.match_flags; id++) {
if ((id->dev.match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
(id->dev.match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
id->dev.idVendor == le16_to_cpu(dev->descriptor.idVendor) &&
id->dev.idProduct == le16_to_cpu(dev->descriptor.idProduct) &&
id->index == index) {
ret = 1;
break;
}
}
return ret;
}
static int check_gamepad_device(struct usb_device *dev)
{
int ret = 0;
pr_info("%s : product=%s\n", __func__, dev->product);
if (!dev->product)
return ret;
if (!strnicmp(dev->product , "Gamepad for SAMSUNG", 19))
ret = 1;
return ret;
}
static int check_lanhub_device(struct usb_device *dev)
{
int ret = 0;
pr_info("%s : product=%s\n", __func__, dev->product);
if (!dev->product)
return ret;
if (!strnicmp(dev->product , "LAN9512", 8))
ret = 1;
return ret;
}
static int is_notify_hub(struct usb_device *dev)
{
struct dev_table *id;
struct usb_device *hdev;
int ret = 0;
hdev = dev->parent;
if (!hdev)
goto skip;
/* check VID, PID */
for (id = enable_notify_hub_table; id->dev.match_flags; id++) {
if ((id->dev.match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
(id->dev.match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
id->dev.idVendor == le16_to_cpu(hdev->descriptor.idVendor) &&
id->dev.idProduct == le16_to_cpu(hdev->descriptor.idProduct)) {
ret = (hdev->parent &&
(hdev->parent == dev->bus->root_hub)) ? id->index : 0;
break;
}
}
skip:
return ret;
}
static int get_autosuspend_time(struct usb_device *dev)
{
struct dev_table *id;
int ret = 0;
/* check VID, PID */
for (id = update_autotimer_device_table; id->dev.match_flags; id++) {
if ((id->dev.match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
(id->dev.match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
id->dev.idVendor == le16_to_cpu(dev->descriptor.idVendor) &&
id->dev.idProduct == le16_to_cpu(dev->descriptor.idProduct)) {
ret = id->index;
break;
}
}
return ret;
}
static int call_battery_notify(struct usb_device *dev, bool on)
{
struct usb_device *hdev;
struct usb_device *udev;
struct usb_hub *hub;
struct otg_notify *o_notify = get_otg_notify();
int index = 0;
int count = 0;
int port;
index = is_notify_hub(dev);
if (!index)
goto skip;
if (check_essential_device(dev, index))
goto skip;
hdev = dev->parent;
hub = usb_hub_to_struct_hub(hdev);
if (!hub)
goto skip;
for (port = 1; port <= hdev->maxchild; port++) {
udev = hub->ports[port-1]->child;
if (udev) {
if (!check_essential_device(udev, index)) {
if (!on && (udev == dev))
continue;
else
count++;
}
}
}
pr_info("%s : VID : 0x%x, PID : 0x%x, on=%d, count=%d\n", __func__,
dev->descriptor.idVendor, dev->descriptor.idProduct,
on, count);
if (on) {
if (count == 1) {
if (index == SMARTDOCK_INDEX)
send_otg_notify(o_notify,
NOTIFY_EVENT_SMTD_EXT_CURRENT, 1);
else if (index == MMDOCK_INDEX)
send_otg_notify(o_notify,
NOTIFY_EVENT_MMD_EXT_CURRENT, 1);
}
} else {
if (!count) {
if (index == SMARTDOCK_INDEX)
send_otg_notify(o_notify,
NOTIFY_EVENT_SMTD_EXT_CURRENT, 0);
else if (index == MMDOCK_INDEX)
send_otg_notify(o_notify,
NOTIFY_EVENT_MMD_EXT_CURRENT, 0);
}
}
skip:
return 0;
}
static int call_device_notify(struct usb_device *dev)
{
struct otg_notify *o_notify = get_otg_notify();
if (dev->bus->root_hub != dev) {
pr_info("%s device\n", __func__);
send_otg_notify(o_notify, NOTIFY_EVENT_DEVICE_CONNECT, 1);
if (check_gamepad_device(dev))
send_otg_notify(o_notify,
NOTIFY_EVENT_GAMEPAD_CONNECT, 1);
else if (check_lanhub_device(dev))
send_otg_notify(o_notify,
NOTIFY_EVENT_LANHUB_CONNECT, 1);
else
;
} else
pr_info("%s root hub\n", __func__);
return 0;
}
static int update_hub_autosuspend_timer(struct usb_device *dev)
{
struct usb_device *hdev;
int time = 0;
if (!dev)
goto skip;
hdev = dev->parent;
if (hdev == NULL || dev->bus->root_hub != hdev)
goto skip;
/* hdev is root hub */
time = get_autosuspend_time(dev);
if (time == hdev->dev.power.autosuspend_delay)
goto skip;
pm_runtime_set_autosuspend_delay(&hdev->dev, time*1000);
pr_info("set autosuspend delay time=%d sec\n", time);
skip:
return 0;
}
static int dev_notify(struct notifier_block *self,
unsigned long action, void *dev)
{
switch (action) {
case USB_DEVICE_ADD:
call_device_notify(dev);
call_battery_notify(dev, 1);
update_hub_autosuspend_timer(dev);
break;
case USB_DEVICE_REMOVE:
call_battery_notify(dev, 0);
break;
}
return NOTIFY_OK;
}
static struct notifier_block dev_nb = {
.notifier_call = dev_notify,
};
void register_usbdev_notify(void)
{
usb_register_notify(&dev_nb);
}
EXPORT_SYMBOL(register_usbdev_notify);
void unregister_usbdev_notify(void)
{
usb_unregister_notify(&dev_nb);
}
EXPORT_SYMBOL(unregister_usbdev_notify);

View file

@ -0,0 +1,18 @@
/*
* Copyright (C) 2014 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 v2.0 */
#ifndef __LINUX_DOCK_NOTIFY_H__
#define __LINUX_DOCK_NOTIFY_H__
extern void register_usbdev_notify(void);
extern void unregister_usbdev_notify(void);
#endif

View file

@ -0,0 +1,147 @@
/*
* 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.
*/
/* usb notify layer v2.0 */
#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 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);

View file

@ -0,0 +1,295 @@
/*
* drivers/usb/notify/host_notify_class.c
*
* Copyright (C) 2011 Samsung, Inc.
* Author: Dongrak Shin <dongrak.shin@samsung.com>
*
*/
/* usb notify layer v2.0 */
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/host_notify.h>
struct notify_data {
struct class *host_notify_class;
atomic_t device_count;
};
static struct notify_data host_notify;
static ssize_t mode_show(
struct device *dev, struct device_attribute *attr, char *buf)
{
struct host_notify_dev *ndev = (struct host_notify_dev *)
dev_get_drvdata(dev);
char *mode;
switch (ndev->mode) {
case NOTIFY_HOST_MODE:
mode = "HOST";
break;
case NOTIFY_PERIPHERAL_MODE:
mode = "PERIPHERAL";
break;
case NOTIFY_TEST_MODE:
mode = "TEST";
break;
case NOTIFY_NONE_MODE:
default:
mode = "NONE";
break;
}
return snprintf(buf, sizeof(mode)+1, "%s\n", mode);
}
static ssize_t mode_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct host_notify_dev *ndev = (struct host_notify_dev *)
dev_get_drvdata(dev);
char *mode;
size_t ret = -ENOMEM;
int sret = 0;
mode = kzalloc(size+1, GFP_KERNEL);
if (!mode)
goto error;
sret = sscanf(buf, "%s", mode);
if (sret != 1)
goto error1;
if (ndev->set_mode) {
pr_info("host_notify: set mode %s\n", mode);
if (!strncmp(mode, "HOST", 4))
ndev->set_mode(NOTIFY_SET_ON);
else if (!strncmp(mode, "NONE", 4))
ndev->set_mode(NOTIFY_SET_OFF);
}
ret = size;
error1:
kfree(mode);
error:
return ret;
}
static ssize_t booster_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct host_notify_dev *ndev = (struct host_notify_dev *)
dev_get_drvdata(dev);
char *booster;
switch (ndev->booster) {
case NOTIFY_POWER_ON:
booster = "ON";
break;
case NOTIFY_POWER_OFF:
default:
booster = "OFF";
break;
}
pr_info("host_notify: read booster %s\n", booster);
return snprintf(buf, sizeof(booster)+1, "%s\n", booster);
}
static ssize_t booster_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct host_notify_dev *ndev = (struct host_notify_dev *)
dev_get_drvdata(dev);
char *booster;
size_t ret = -ENOMEM;
int sret = 0;
booster = kzalloc(size+1, GFP_KERNEL);
if (!booster)
goto error;
sret = sscanf(buf, "%s", booster);
if (sret != 1)
goto error1;
if (ndev->set_booster) {
pr_info("host_notify: set booster %s\n", booster);
if (!strncmp(booster, "ON", 2)) {
ndev->set_booster(NOTIFY_SET_ON);
ndev->mode = NOTIFY_TEST_MODE;
} else if (!strncmp(booster, "OFF", 3)) {
ndev->set_booster(NOTIFY_SET_OFF);
ndev->mode = NOTIFY_NONE_MODE;
}
}
ret = size;
error1:
kfree(booster);
error:
return ret;
}
static DEVICE_ATTR(mode, 0664, mode_show, mode_store);
static DEVICE_ATTR(booster, 0664, booster_show, booster_store);
static struct attribute *host_notify_attrs[] = {
&dev_attr_mode.attr,
&dev_attr_booster.attr,
NULL,
};
static struct attribute_group host_notify_attr_grp = {
.attrs = host_notify_attrs,
};
char *host_state_string(int type)
{
switch (type) {
case NOTIFY_HOST_NONE: return "none";
case NOTIFY_HOST_ADD: return "add";
case NOTIFY_HOST_REMOVE: return "remove";
case NOTIFY_HOST_OVERCURRENT: return "overcurrent";
case NOTIFY_HOST_LOWBATT: return "lowbatt";
case NOTIFY_HOST_BLOCK: return "block";
case NOTIFY_HOST_UNKNOWN:
default: return "unknown";
}
}
int host_state_notify(struct host_notify_dev *ndev, int state)
{
pr_info("host_notify: ndev name=%s: (%s --> %s)\n",
ndev->name,
host_state_string(ndev->state),
host_state_string(state));
if (ndev->state != state) {
ndev->state = state;
if (state != NOTIFY_HOST_NONE)
kobject_uevent(&ndev->dev->kobj, KOBJ_CHANGE);
return 1;
}
return 0;
}
EXPORT_SYMBOL_GPL(host_state_notify);
static int
host_notify_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct host_notify_dev *ndev = (struct host_notify_dev *)
dev_get_drvdata(dev);
char *state;
if (!ndev) {
/* this happens when the device is first created */
return 0;
}
switch (ndev->state) {
case NOTIFY_HOST_ADD:
state = "ADD";
break;
case NOTIFY_HOST_REMOVE:
state = "REMOVE";
break;
case NOTIFY_HOST_OVERCURRENT:
state = "OVERCURRENT";
break;
case NOTIFY_HOST_LOWBATT:
state = "LOWBATT";
break;
case NOTIFY_HOST_BLOCK:
state = "BLOCK";
break;
case NOTIFY_HOST_UNKNOWN:
state = "UNKNOWN";
break;
case NOTIFY_HOST_NONE:
default:
return 0;
}
if (add_uevent_var(env, "DEVNAME=%s", ndev->dev->kobj.name))
return -ENOMEM;
if (add_uevent_var(env, "STATE=%s", state))
return -ENOMEM;
return 0;
}
static int create_notify_class(void)
{
if (!host_notify.host_notify_class) {
host_notify.host_notify_class
= class_create(THIS_MODULE, "host_notify");
if (IS_ERR(host_notify.host_notify_class))
return PTR_ERR(host_notify.host_notify_class);
atomic_set(&host_notify.device_count, 0);
host_notify.host_notify_class->dev_uevent = host_notify_uevent;
}
return 0;
}
int host_notify_dev_register(struct host_notify_dev *ndev)
{
int ret;
if (!host_notify.host_notify_class) {
ret = create_notify_class();
if (ret < 0)
return ret;
}
ndev->index = atomic_inc_return(&host_notify.device_count);
ndev->dev = device_create(host_notify.host_notify_class, NULL,
MKDEV(0, ndev->index), NULL, ndev->name);
if (IS_ERR(ndev->dev))
return PTR_ERR(ndev->dev);
ret = sysfs_create_group(&ndev->dev->kobj, &host_notify_attr_grp);
if (ret < 0) {
device_destroy(host_notify.host_notify_class,
MKDEV(0, ndev->index));
return ret;
}
dev_set_drvdata(ndev->dev, ndev);
ndev->state = 0;
return 0;
}
EXPORT_SYMBOL_GPL(host_notify_dev_register);
void host_notify_dev_unregister(struct host_notify_dev *ndev)
{
ndev->state = NOTIFY_HOST_NONE;
sysfs_remove_group(&ndev->dev->kobj, &host_notify_attr_grp);
device_destroy(host_notify.host_notify_class, MKDEV(0, ndev->index));
dev_set_drvdata(ndev->dev, NULL);
}
EXPORT_SYMBOL_GPL(host_notify_dev_unregister);
static int __init notify_class_init(void)
{
return create_notify_class();
}
static void __exit notify_class_exit(void)
{
class_destroy(host_notify.host_notify_class);
}
module_init(notify_class_init);
module_exit(notify_class_exit);
MODULE_AUTHOR("Dongrak Shin <dongrak.shin@samsung.com>");
MODULE_DESCRIPTION("Usb host notify driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,488 @@
/*
* Copyright (C) 2011 Samsung Electronics Co. Ltd.
* Inchul Im <inchul.im@samsung.com>
*
* 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_notifier: " fmt
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/usb_notify.h>
#ifdef CONFIG_OF
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#endif
#ifdef CONFIG_MUIC_NOTIFIER
#include <linux/muic/muic.h>
#include <linux/muic/muic_notifier.h>
#endif
#ifdef CONFIG_VBUS_NOTIFIER
#include <linux/vbus_notifier.h>
#endif
#include <linux/battery/sec_charging_common.h>
#include "usb_notifier.h"
struct usb_notifier_platform_data {
struct notifier_block usb_nb;
struct notifier_block vbus_nb;
int gpio_redriver_en;
int can_disable_usb;
};
#ifdef CONFIG_OF
static void of_get_usb_redriver_dt(struct device_node *np,
struct usb_notifier_platform_data *pdata)
{
int gpio = 0;
gpio = of_get_named_gpio(np, "gpios_redriver_en", 0);
if (!gpio_is_valid(gpio)) {
pdata->gpio_redriver_en = -1;
pr_err("%s: usb30_redriver_en: Invalied gpio pins\n", __func__);
} else
pdata->gpio_redriver_en = gpio;
pr_info("%s, gpios_redriver_en %d\n", __func__, gpio);
pdata->can_disable_usb =
!(of_property_read_bool(np, "samsung,unsupport-disable-usb"));
pr_info("%s, can_disable_usb %d\n", __func__, pdata->can_disable_usb);
}
static int of_usb_notifier_dt(struct device *dev,
struct usb_notifier_platform_data *pdata)
{
struct device_node *np = dev->of_node;
if (!np)
return -EINVAL;
of_get_usb_redriver_dt(np, pdata);
return 0;
}
#endif
static struct device_node *exynos_udc_parse_dt(void)
{
struct platform_device *pdev = NULL;
struct device *dev = NULL;
struct device_node *np = NULL;
/**
* For previous chips such as Exynos7420 and Exynos7890
*/
np = of_find_compatible_node(NULL, NULL, "samsung,exynos5-dwusb3");
if (np)
goto find;
np = of_find_compatible_node(NULL, NULL, "samsung,usb-notifier");
if (!np) {
pr_err("%s: failed to get the usb-notifier device node\n",
__func__);
goto err;
}
pdev = of_find_device_by_node(np);
if (!pdev) {
pr_err("%s: failed to get platform_device\n", __func__);
goto err;
}
dev = &pdev->dev;
np = of_parse_phandle(dev->of_node, "udc", 0);
if (!np) {
dev_info(dev, "udc device is not available\n");
goto err;
}
find:
return np;
err:
return NULL;
}
#if defined(CONFIG_MUIC_NOTIFIER)
static void check_usb_vbus_state(int state)
{
struct device_node *np = NULL;
struct platform_device *pdev = NULL;
pr_info("%s vbus state = %d\n", __func__, state);
np = exynos_udc_parse_dt();
if (np) {
pdev = of_find_device_by_node(np);
of_node_put(np);
if (pdev) {
pr_info("%s: get the %s platform_device\n",
__func__, pdev->name);
dwc3_exynos_vbus_event(&pdev->dev, state);
goto end;
}
}
np = of_find_compatible_node(NULL, NULL, "samsung,exynos_udc");
if (np) {
pdev = of_find_device_by_node(np);
of_node_put(np);
if (pdev) {
pr_info("%s: get the %s platform_device\n",
__func__, pdev->name);
exynos_otg_vbus_event(pdev, state);
goto end;
}
}
pr_err("%s: failed to get the platform_device\n", __func__);
end:
return;
}
static void check_usb_id_state(int state)
{
struct device_node *np = NULL;
struct platform_device *pdev = NULL;
pr_info("%s id state = %d\n", __func__, state);
np = exynos_udc_parse_dt();
if (np) {
pdev = of_find_device_by_node(np);
of_node_put(np);
if (pdev) {
pr_info("%s: get the %s platform_device\n",
__func__, pdev->name);
dwc3_exynos_id_event(&pdev->dev, state);
goto end;
}
}
pr_err("%s: failed to get the platform_device\n", __func__);
end:
return;
}
static int usb_handle_notification(struct notifier_block *nb,
unsigned long action, void *data)
{
muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
struct otg_notify *o_notify;
o_notify = get_otg_notify();
pr_info("%s action=%lu, attached_dev=%d\n",
__func__, action, attached_dev);
switch (attached_dev) {
case ATTACHED_DEV_USB_MUIC:
case ATTACHED_DEV_CDP_MUIC:
case ATTACHED_DEV_UNOFFICIAL_ID_USB_MUIC:
case ATTACHED_DEV_UNOFFICIAL_ID_CDP_MUIC:
case ATTACHED_DEV_JIG_USB_OFF_MUIC:
case ATTACHED_DEV_JIG_USB_ON_MUIC:
if (action == MUIC_NOTIFY_CMD_DETACH)
send_otg_notify(o_notify, NOTIFY_EVENT_VBUS, 0);
else if (action == MUIC_NOTIFY_CMD_ATTACH)
send_otg_notify(o_notify, NOTIFY_EVENT_VBUS, 1);
else
pr_err("%s - ACTION Error!\n", __func__);
break;
case ATTACHED_DEV_OTG_MUIC:
if (action == MUIC_NOTIFY_CMD_DETACH)
send_otg_notify(o_notify, NOTIFY_EVENT_HOST, 0);
else if (action == MUIC_NOTIFY_CMD_ATTACH)
send_otg_notify(o_notify, NOTIFY_EVENT_HOST, 1);
else
pr_err("%s - ACTION Error!\n", __func__);
break;
case ATTACHED_DEV_HMT_MUIC:
if (action == MUIC_NOTIFY_CMD_DETACH)
send_otg_notify(o_notify, NOTIFY_EVENT_HMT, 0);
else if (action == MUIC_NOTIFY_CMD_ATTACH)
send_otg_notify(o_notify, NOTIFY_EVENT_HMT, 1);
else
pr_err("%s - ACTION Error!\n", __func__);
break;
case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC:
if (action == MUIC_NOTIFY_CMD_DETACH)
pr_info("%s - USB_HOST_TEST_DETACHED\n", __func__);
else if (action == MUIC_NOTIFY_CMD_ATTACH)
pr_info("%s - USB_HOST_TEST_ATTACHED\n", __func__);
else
pr_err("%s - ACTION Error!\n", __func__);
break;
case ATTACHED_DEV_SMARTDOCK_TA_MUIC:
if (action == MUIC_NOTIFY_CMD_DETACH)
send_otg_notify(o_notify, NOTIFY_EVENT_SMARTDOCK_TA, 0);
else if (action == MUIC_NOTIFY_CMD_ATTACH)
send_otg_notify(o_notify, NOTIFY_EVENT_SMARTDOCK_TA, 1);
else
pr_err("%s - ACTION Error!\n", __func__);
break;
case ATTACHED_DEV_SMARTDOCK_USB_MUIC:
if (action == MUIC_NOTIFY_CMD_DETACH)
send_otg_notify
(o_notify, NOTIFY_EVENT_SMARTDOCK_USB, 0);
else if (action == MUIC_NOTIFY_CMD_ATTACH)
send_otg_notify
(o_notify, NOTIFY_EVENT_SMARTDOCK_USB, 1);
else
pr_err("%s - ACTION Error!\n", __func__);
break;
case ATTACHED_DEV_AUDIODOCK_MUIC:
if (action == MUIC_NOTIFY_CMD_DETACH)
send_otg_notify(o_notify, NOTIFY_EVENT_AUDIODOCK, 0);
else if (action == MUIC_NOTIFY_CMD_ATTACH)
send_otg_notify(o_notify, NOTIFY_EVENT_AUDIODOCK, 1);
else
pr_err("%s - ACTION Error!\n", __func__);
break;
case ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC:
if (action == MUIC_NOTIFY_CMD_DETACH)
send_otg_notify(o_notify, NOTIFY_EVENT_MMDOCK, 0);
else if (action == MUIC_NOTIFY_CMD_ATTACH)
send_otg_notify(o_notify, NOTIFY_EVENT_MMDOCK, 1);
else
pr_err("%s - ACTION Error!\n", __func__);
break;
case ATTACHED_DEV_USB_LANHUB_MUIC:
if (action == MUIC_NOTIFY_CMD_DETACH)
send_otg_notify(o_notify, NOTIFY_EVENT_LANHUB, 0);
else if (action == MUIC_NOTIFY_CMD_ATTACH)
send_otg_notify(o_notify, NOTIFY_EVENT_LANHUB, 1);
else
pr_err("%s - ACTION Error!\n", __func__);
break;
default:
break;
}
return 0;
}
#endif
#ifdef CONFIG_VBUS_NOTIFIER
static int vbus_handle_notification(struct notifier_block *nb,
unsigned long cmd, void *data)
{
vbus_status_t vbus_type = *(vbus_status_t *)data;
struct otg_notify *o_notify;
o_notify = get_otg_notify();
pr_info("%s cmd=%lu, vbus_type=%s\n",
__func__, cmd, vbus_type == STATUS_VBUS_HIGH ? "HIGH" : "LOW");
switch (vbus_type) {
case STATUS_VBUS_HIGH:
send_otg_notify(o_notify, NOTIFY_EVENT_VBUSPOWER, 1);
break;
case STATUS_VBUS_LOW:
send_otg_notify(o_notify, NOTIFY_EVENT_VBUSPOWER, 0);
break;
default:
break;
}
return 0;
}
#endif
static int otg_accessory_power(bool enable)
{
u8 on = (u8)!!enable;
union power_supply_propval val;
struct device_node *np_charger = NULL;
char *charger_name;
pr_info("otg accessory power = %d\n", on);
np_charger = of_find_node_by_name(NULL, "battery");
if (!np_charger) {
pr_err("%s: failed to get the battery device node\n", __func__);
return 0;
}
if (!of_property_read_string(np_charger, "battery,charger_name",
(char const **)&charger_name)) {
pr_info("%s: charger_name = %s\n", __func__,
charger_name);
} else {
pr_err("%s: failed to get the charger name\n", __func__);
return 0;
}
val.intval = enable;
psy_do_property(charger_name, set,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, val);
return 0;
}
static int set_online(int event, int state)
{
union power_supply_propval val;
struct device_node *np_charger = NULL;
char *charger_name;
if (event == NOTIFY_EVENT_SMTD_EXT_CURRENT)
pr_info("request smartdock charging current = %s\n",
state ? "1000mA" : "1700mA");
else if (event == NOTIFY_EVENT_MMD_EXT_CURRENT)
pr_info("request mmdock charging current = %s\n",
state ? "900mA" : "1400mA");
np_charger = of_find_node_by_name(NULL, "battery");
if (!np_charger) {
pr_err("%s: failed to get the battery device node\n", __func__);
return 0;
}
if (!of_property_read_string(np_charger, "battery,charger_name",
(char const **)&charger_name)) {
pr_info("%s: charger_name = %s\n", __func__,
charger_name);
} else {
pr_err("%s: failed to get the charger name\n", __func__);
return 0;
}
if (state)
val.intval = POWER_SUPPLY_TYPE_SMART_OTG;
else
val.intval = POWER_SUPPLY_TYPE_SMART_NOTG;
psy_do_property(charger_name, set,
POWER_SUPPLY_PROP_ONLINE, val);
return 0;
}
static int exynos_set_host(bool enable)
{
if (!enable) {
pr_info("%s USB_HOST_DETACHED\n", __func__);
#ifdef CONFIG_OF
check_usb_id_state(1);
#endif
} else {
pr_info("%s USB_HOST_ATTACHED\n", __func__);
#ifdef CONFIG_OF
check_usb_id_state(0);
#endif
}
return 0;
}
static int exynos_set_peripheral(bool enable)
{
if (enable) {
pr_info("%s usb attached\n", __func__);
check_usb_vbus_state(1);
} else {
pr_info("%s usb detached\n", __func__);
check_usb_vbus_state(0);
}
return 0;
}
static struct otg_notify dwc_lsi_notify = {
.vbus_drive = otg_accessory_power,
.set_host = exynos_set_host,
.set_peripheral = exynos_set_peripheral,
.vbus_detect_gpio = -1,
.is_wakelock = 1,
.booting_delay_sec = 10,
.auto_drive_vbus = 1,
.set_battcall = set_online,
};
static int usb_notifier_probe(struct platform_device *pdev)
{
struct usb_notifier_platform_data *pdata = NULL;
int ret = 0;
if (pdev->dev.of_node) {
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct usb_notifier_platform_data), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
ret = of_usb_notifier_dt(&pdev->dev, pdata);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to get device of_node\n");
return ret;
}
pdev->dev.platform_data = pdata;
} else
pdata = pdev->dev.platform_data;
dwc_lsi_notify.redriver_en_gpio = pdata->gpio_redriver_en;
dwc_lsi_notify.disable_control = pdata->can_disable_usb;
set_otg_notify(&dwc_lsi_notify);
set_notify_data(&dwc_lsi_notify, pdata);
#ifdef CONFIG_MUIC_NOTIFIER
muic_notifier_register(&pdata->usb_nb, usb_handle_notification,
MUIC_NOTIFY_DEV_USB);
#endif
#ifdef CONFIG_VBUS_NOTIFIER
vbus_notifier_register(&pdata->vbus_nb, vbus_handle_notification,
MUIC_NOTIFY_DEV_USB);
#endif
dev_info(&pdev->dev, "usb notifier probe\n");
return 0;
}
static int usb_notifier_remove(struct platform_device *pdev)
{
struct usb_notifier_platform_data *pdata = dev_get_platdata(&pdev->dev);
#if defined(CONFIG_MUIC_NOTIFIER)
muic_notifier_unregister(&pdata->usb_nb);
#endif
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id usb_notifier_dt_ids[] = {
{ .compatible = "samsung,usb-notifier",
},
{ },
};
MODULE_DEVICE_TABLE(of, usb_notifier_dt_ids);
#endif
static struct platform_driver usb_notifier_driver = {
.probe = usb_notifier_probe,
.remove = usb_notifier_remove,
.driver = {
.name = "usb_notifier",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(usb_notifier_dt_ids),
#endif
},
};
static int __init usb_notifier_init(void)
{
return platform_driver_register(&usb_notifier_driver);
}
static void __init usb_notifier_exit(void)
{
platform_driver_unregister(&usb_notifier_driver);
}
late_initcall(usb_notifier_init);
module_exit(usb_notifier_exit);
MODULE_AUTHOR("inchul.im <inchul.im@samsung.com>");
MODULE_DESCRIPTION("USB notifier");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,28 @@
/*
* Copyright (C) 2014 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.
*/
#ifndef __LINUX_USB_NOTIFIER_H__
#define __LINUX_USB_NOTIFIER_H__
#ifdef CONFIG_USB_DWC3
extern int dwc3_exynos_id_event(struct device *dev, int state);
extern int dwc3_exynos_vbus_event(struct device *dev, int state);
#else
static inline int dwc3_exynos_id_event
(struct device *dev, int state) {return 0; }
static inline int dwc3_exynos_vbus_event
(struct device *dev, int state) {return 0; }
#endif
#ifdef CONFIG_USB_S3C_OTGD
extern int exynos_otg_vbus_event(struct platform_device *pdev, int state);
#else
static inline int exynos_otg_vbus_event(
struct platform_device *pdev, int state) {return 0; }
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,257 @@
/*
* drivers/usb/notify/usb_notify_sysfs.c
*
* Copyright (C) 2015 Samsung, Inc.
* Author: Dongrak Shin <dongrak.shin@samsung.com>
*
*/
/* usb notify layer v2.0 */
#define pr_fmt(fmt) "usb_notify: " fmt
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/usb_notify.h>
#include "usb_notify_sysfs.h"
struct notify_data {
struct class *usb_notify_class;
atomic_t device_count;
};
static struct notify_data usb_notify_data;
static int is_valid_cmd(char *cur_cmd, char *prev_cmd)
{
pr_info("%s : current state=%s, previous state=%s\n",
__func__, cur_cmd, prev_cmd);
if (!strcmp(cur_cmd, "ON") ||
!strncmp(cur_cmd, "ON_ALL_", 7)) {
if (!strcmp(prev_cmd, "ON") ||
!strncmp(prev_cmd, "ON_ALL_", 7)) {
goto ignore;
} else if (!strncmp(prev_cmd, "ON_HOST_", 8)) {
goto all;
} else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) {
goto all;
} else if (!strcmp(prev_cmd, "OFF")) {
goto all;
} else {
goto invalid;
}
} else if (!strcmp(cur_cmd, "OFF")) {
if (!strcmp(prev_cmd, "ON") ||
!strncmp(prev_cmd, "ON_ALL_", 7)) {
goto off;
} else if (!strncmp(prev_cmd, "ON_HOST_", 8)) {
goto off;
} else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) {
goto off;
} else if (!strcmp(prev_cmd, "OFF")) {
goto ignore;
} else {
goto invalid;
}
} else if (!strncmp(cur_cmd, "ON_HOST_", 8)) {
if (!strcmp(prev_cmd, "ON") ||
!strncmp(prev_cmd, "ON_ALL_", 7)) {
goto host;
} else if (!strncmp(prev_cmd, "ON_HOST_", 8)) {
goto ignore;
} else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) {
goto host;
} else if (!strcmp(prev_cmd, "OFF")) {
goto host;
} else {
goto invalid;
}
} else if (!strncmp(cur_cmd, "ON_CLIENT_", 10)) {
if (!strcmp(prev_cmd, "ON") ||
!strncmp(prev_cmd, "ON_ALL_", 7)) {
goto client;
} else if (!strncmp(prev_cmd, "ON_HOST_", 8)) {
goto client;
} else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) {
goto ignore;
} else if (!strcmp(prev_cmd, "OFF")) {
goto client;
} else {
goto invalid;
}
} else {
goto invalid;
}
host:
pr_info("%s cmd=%s is accepted.\n", __func__, cur_cmd);
return NOTIFY_BLOCK_TYPE_HOST;
client:
pr_info("%s cmd=%s is accepted.\n", __func__, cur_cmd);
return NOTIFY_BLOCK_TYPE_CLIENT;
all:
pr_info("%s cmd=%s is accepted.\n", __func__, cur_cmd);
return NOTIFY_BLOCK_TYPE_ALL;
off:
pr_info("%s cmd=%s is accepted.\n", __func__, cur_cmd);
return NOTIFY_BLOCK_TYPE_NONE;
ignore:
pr_err("%s cmd=%s is ignored but saved.\n", __func__, cur_cmd);
return -EEXIST;
invalid:
pr_err("%s cmd=%s is invalid.\n", __func__, cur_cmd);
return -EINVAL;
}
static ssize_t disable_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct usb_notify_dev *udev = (struct usb_notify_dev *)
dev_get_drvdata(dev);
pr_info("read disable_state %s\n", udev->disable_state_cmd);
return sprintf(buf, "%s\n", udev->disable_state_cmd);
}
static ssize_t disable_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct usb_notify_dev *udev = (struct usb_notify_dev *)
dev_get_drvdata(dev);
char *disable;
int sret, param = -EINVAL;
size_t ret = -ENOMEM;
if (size > MAX_DISABLE_STR_LEN) {
pr_err("%s size(%zu) is too long.\n", __func__, size);
goto error;
}
disable = kzalloc(size+1, GFP_KERNEL);
if (!disable)
goto error;
sret = sscanf(buf, "%s", disable);
if (sret != 1)
goto error1;
if (udev->set_disable) {
param = is_valid_cmd(disable, udev->disable_state_cmd);
if (param == -EINVAL) {
ret = param;
} else {
if (param != -EEXIST)
udev->set_disable(udev, param);
strncpy(udev->disable_state_cmd,
disable, sizeof(udev->disable_state_cmd)-1);
ret = size;
}
} else
pr_err("set_disable func is NULL\n");
error1:
kfree(disable);
error:
return ret;
}
static ssize_t support_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct usb_notify_dev *udev = (struct usb_notify_dev *)
dev_get_drvdata(dev);
struct otg_notify *n = udev->o_notify;
char *support;
if (n->unsupport_host || !IS_ENABLED(CONFIG_USB_HOST_NOTIFY))
support = "CLIENT";
else
support = "ALL";
pr_info("read support %s\n", support);
return snprintf(buf, sizeof(support)+1, "%s\n", support);
}
static DEVICE_ATTR(disable, 0664, disable_show, disable_store);
static DEVICE_ATTR(support, 0664, support_show, NULL);
static struct attribute *usb_notify_attrs[] = {
&dev_attr_disable.attr,
&dev_attr_support.attr,
NULL,
};
static struct attribute_group usb_notify_attr_grp = {
.attrs = usb_notify_attrs,
};
static int create_usb_notify_class(void)
{
if (!usb_notify_data.usb_notify_class) {
usb_notify_data.usb_notify_class
= class_create(THIS_MODULE, "usb_notify");
if (IS_ERR(usb_notify_data.usb_notify_class))
return PTR_ERR(usb_notify_data.usb_notify_class);
atomic_set(&usb_notify_data.device_count, 0);
}
return 0;
}
int usb_notify_dev_register(struct usb_notify_dev *udev)
{
int ret;
if (!usb_notify_data.usb_notify_class) {
ret = create_usb_notify_class();
if (ret < 0)
return ret;
}
udev->index = atomic_inc_return(&usb_notify_data.device_count);
udev->dev = device_create(usb_notify_data.usb_notify_class, NULL,
MKDEV(0, udev->index), NULL, udev->name);
if (IS_ERR(udev->dev))
return PTR_ERR(udev->dev);
udev->disable_state = 0;
strncpy(udev->disable_state_cmd, "OFF",
sizeof(udev->disable_state_cmd)-1);
ret = sysfs_create_group(&udev->dev->kobj, &usb_notify_attr_grp);
if (ret < 0) {
device_destroy(usb_notify_data.usb_notify_class,
MKDEV(0, udev->index));
return ret;
}
dev_set_drvdata(udev->dev, udev);
return 0;
}
EXPORT_SYMBOL_GPL(usb_notify_dev_register);
void usb_notify_dev_unregister(struct usb_notify_dev *udev)
{
sysfs_remove_group(&udev->dev->kobj, &usb_notify_attr_grp);
device_destroy(usb_notify_data.usb_notify_class, MKDEV(0, udev->index));
dev_set_drvdata(udev->dev, NULL);
}
EXPORT_SYMBOL_GPL(usb_notify_dev_unregister);
int usb_notify_class_init(void)
{
return create_usb_notify_class();
}
EXPORT_SYMBOL_GPL(usb_notify_class_init);
void usb_notify_class_exit(void)
{
class_destroy(usb_notify_data.usb_notify_class);
}
EXPORT_SYMBOL_GPL(usb_notify_class_exit);

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2015 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 v2.0 */
#ifndef __LINUX_USB_NOTIFY_SYSFS_H__
#define __LINUX_USB_NOTIFY_SYSFS_H__
#define MAX_DISABLE_STR_LEN 32
struct usb_notify_dev {
const char *name;
struct device *dev;
struct otg_notify *o_notify;
int index;
unsigned long disable_state;
char disable_state_cmd[MAX_DISABLE_STR_LEN];
int (*set_disable)(struct usb_notify_dev *, int);
};
extern int usb_notify_dev_register(struct usb_notify_dev *ndev);
extern void usb_notify_dev_unregister(struct usb_notify_dev *ndev);
extern int usb_notify_class_init(void);
extern void usb_notify_class_exit(void);
#endif

View file

@ -0,0 +1,350 @@
/*
* drivers/usb/notify/usblog_proc_notify.c
*
* Copyright (C) 2016 Samsung, Inc.
* Author: Dongrak Shin <dongrak.shin@samsung.com>
*
*/
/* usb notify layer v2.0 */
#define pr_fmt(fmt) "usb_notify: " fmt
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/time.h>
#include <linux/kernel.h>
#include <linux/security.h>
#include <linux/syscalls.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/seq_file.h>
#include <linux/usb_notify.h>
#define USBLOG_MAX_BUF_SIZE (1 << 7) /* 128 */
#define USBLOG_MAX_STRING_SIZE (1 << 4) /* 16 */
#define USBLOG_CMP_INDEX 3
struct mode_buf {
unsigned long long ts_nsec;
char usbmode_str[USBLOG_MAX_STRING_SIZE];
};
struct state_buf {
unsigned long long ts_nsec;
int usbstate;
};
struct event_buf {
unsigned long long ts_nsec;
unsigned long event;
int enable;
};
struct usblog_buf {
unsigned long long mode_count;
unsigned long long state_count;
unsigned long long event_count;
unsigned long mode_index;
unsigned long state_index;
unsigned long event_index;
struct mode_buf mode_buffer[USBLOG_MAX_BUF_SIZE];
struct state_buf state_buffer[USBLOG_MAX_BUF_SIZE];
struct event_buf event_buffer[USBLOG_MAX_BUF_SIZE];
};
struct usblog_root_str {
struct usblog_buf *usblog_buffer;
spinlock_t usblog_lock;
};
static struct usblog_root_str usblog_root;
static const char *usbstate_string(enum usblog_state usbstate)
{
switch (usbstate) {
case NOTIFY_CONFIGURED:
return "CONFIGURED";
case NOTIFY_CONNECTED:
return "CONNECTED";
case NOTIFY_DISCONNECTED:
return "DISCONNECTED";
case NOTIFY_RESET:
return "RESET";
case NOTIFY_ACCSTART:
return "ACCSTART";
default:
return "UNDEFINED";
}
}
static int usblog_proc_show(struct seq_file *m, void *v)
{
struct usblog_buf *temp_usblog_buffer;
unsigned long long ts;
unsigned long rem_nsec;
int i;
temp_usblog_buffer = usblog_root.usblog_buffer;
if (!temp_usblog_buffer)
goto err;
seq_printf(m,
"usblog USB_MODE: count=%llu maxline=%d\n",
temp_usblog_buffer->mode_count, USBLOG_MAX_BUF_SIZE);
if (temp_usblog_buffer->mode_count >= USBLOG_MAX_BUF_SIZE) {
for (i = temp_usblog_buffer->mode_index;
i < USBLOG_MAX_BUF_SIZE; i++) {
ts = temp_usblog_buffer->mode_buffer[i].ts_nsec;
rem_nsec = do_div(ts, 1000000000);
seq_printf(m, "[%5lu.%06lu] %s\n", (unsigned long)ts,
rem_nsec / 1000,
temp_usblog_buffer->mode_buffer[i].usbmode_str);
}
}
for (i = 0; i < temp_usblog_buffer->mode_index; i++) {
ts = temp_usblog_buffer->mode_buffer[i].ts_nsec;
rem_nsec = do_div(ts, 1000000000);
seq_printf(m, "[%5lu.%06lu] %s\n", (unsigned long)ts,
rem_nsec / 1000,
temp_usblog_buffer->mode_buffer[i].usbmode_str);
}
seq_printf(m,
"\n\n");
seq_printf(m,
"usblog USB STATE: count=%llu maxline=%d\n",
temp_usblog_buffer->state_count, USBLOG_MAX_BUF_SIZE);
if (temp_usblog_buffer->state_count >= USBLOG_MAX_BUF_SIZE) {
for (i = temp_usblog_buffer->state_index;
i < USBLOG_MAX_BUF_SIZE; i++) {
ts = temp_usblog_buffer->state_buffer[i].ts_nsec;
rem_nsec = do_div(ts, 1000000000);
seq_printf(m, "[%5lu.%06lu] %s\n", (unsigned long)ts,
rem_nsec / 1000,
usbstate_string(temp_usblog_buffer->
state_buffer[i].usbstate));
}
}
for (i = 0; i < temp_usblog_buffer->state_index; i++) {
ts = temp_usblog_buffer->state_buffer[i].ts_nsec;
rem_nsec = do_div(ts, 1000000000);
seq_printf(m, "[%5lu.%06lu] %s\n", (unsigned long)ts,
rem_nsec / 1000,
usbstate_string(temp_usblog_buffer->state_buffer[i].usbstate));
}
seq_printf(m,
"\n\n");
seq_printf(m,
"usblog USB EVENT: count=%llu maxline=%d\n",
temp_usblog_buffer->event_count, USBLOG_MAX_BUF_SIZE);
if (temp_usblog_buffer->event_count >= USBLOG_MAX_BUF_SIZE) {
for (i = temp_usblog_buffer->event_index;
i < USBLOG_MAX_BUF_SIZE; i++) {
ts = temp_usblog_buffer->event_buffer[i].ts_nsec;
rem_nsec = do_div(ts, 1000000000);
seq_printf(m, "[%5lu.%06lu] %s %s\n", (unsigned long)ts,
rem_nsec / 1000,
event_string(temp_usblog_buffer->event_buffer[i].event),
status_string(temp_usblog_buffer->
event_buffer[i].enable));
}
}
for (i = 0; i < temp_usblog_buffer->event_index; i++) {
ts = temp_usblog_buffer->event_buffer[i].ts_nsec;
rem_nsec = do_div(ts, 1000000000);
seq_printf(m, "[%5lu.%06lu] %s %s\n", (unsigned long)ts,
rem_nsec / 1000,
event_string(temp_usblog_buffer->event_buffer[i].event),
status_string(temp_usblog_buffer->event_buffer[i].enable));
}
err:
return 0;
}
static int usblog_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, usblog_proc_show, NULL);
}
static const struct file_operations usblog_proc_fops = {
.open = usblog_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void mode_store_usblog_notify(int type, char *param1)
{
struct mode_buf *md_buffer;
unsigned long long *target_count;
unsigned long *target_index;
char buf[256], buf2[4];
char *b, *name;
target_count = &usblog_root.usblog_buffer->mode_count;
target_index = &usblog_root.usblog_buffer->mode_index;
md_buffer = &usblog_root.usblog_buffer->mode_buffer[*target_index];
if (md_buffer == NULL) {
pr_err("%s target_buffer error\n", __func__);
goto err;
}
md_buffer->ts_nsec = local_clock();
strlcpy(buf, param1, sizeof(buf));
b = strim(buf);
if (b) {
name = strsep(&b, ",");
strlcpy(buf2, name, sizeof(buf2));
strncpy(md_buffer->usbmode_str, buf2,
sizeof(md_buffer->usbmode_str)-1);
}
while (b) {
name = strsep(&b, ",");
if (!name)
continue;
if (USBLOG_MAX_STRING_SIZE
- strlen(md_buffer->usbmode_str) < 5) {
strncpy(md_buffer->usbmode_str, "overflow",
sizeof(md_buffer->usbmode_str)-1);
b = NULL;
} else {
strncat(md_buffer->usbmode_str, ",", 1);
strncat(md_buffer->usbmode_str, name, 3);
}
}
*target_index = (*target_index+1)%USBLOG_MAX_BUF_SIZE;
(*target_count)++;
err:
return;
}
void state_store_usblog_notify(int type, char *param1)
{
struct state_buf *st_buffer;
unsigned long long *target_count;
unsigned long *target_index;
char buf[256], index;
char *b, *name;
int usbstate;
target_count = &usblog_root.usblog_buffer->state_count;
target_index = &usblog_root.usblog_buffer->state_index;
st_buffer = &usblog_root.usblog_buffer->state_buffer[*target_index];
if (st_buffer == NULL) {
pr_err("%s target_buffer error\n", __func__);
goto err;
}
st_buffer->ts_nsec = local_clock();
strlcpy(buf, param1, sizeof(buf));
b = strim(buf);
name = strsep(&b, "=");
index = *(b+USBLOG_CMP_INDEX);
switch (index) {
case 'F': /* CONFIGURED */
usbstate = NOTIFY_CONFIGURED;
break;
case 'N': /* CONNECTED */
usbstate = NOTIFY_CONNECTED;
break;
case 'C': /* DISCONNECTED */
usbstate = NOTIFY_DISCONNECTED;
break;
case 'E': /* RESET */
usbstate = NOTIFY_RESET;
break;
case 'R': /* ACCESSORY START */
usbstate = NOTIFY_ACCSTART;
break;
default:
pr_err("%s state param error. state=%s\n", __func__, param1);
goto err;
}
st_buffer->usbstate = usbstate;
*target_index = (*target_index+1)%USBLOG_MAX_BUF_SIZE;
(*target_count)++;
err:
return;
}
void event_store_usblog_notify(int type, unsigned long *param1, int *param2)
{
struct event_buf *ev_buffer;
unsigned long long *target_count;
unsigned long *target_index;
target_count = &usblog_root.usblog_buffer->event_count;
target_index = &usblog_root.usblog_buffer->event_index;
ev_buffer = &usblog_root.usblog_buffer->event_buffer[*target_index];
if (ev_buffer == NULL) {
pr_err("%s target_buffer error\n", __func__);
goto err;
}
ev_buffer->ts_nsec = local_clock();
ev_buffer->event = *param1;
ev_buffer->enable = *param2;
*target_index = (*target_index+1)%USBLOG_MAX_BUF_SIZE;
(*target_count)++;
err:
return;
}
void store_usblog_notify(int type, void *param1, void *parma2)
{
unsigned long flags = 0;
spin_lock_irqsave(&usblog_root.usblog_lock, flags);
if (!usblog_root.usblog_buffer) {
pr_err("%s usblog_buffer is null\n", __func__);
spin_unlock_irqrestore(&usblog_root.usblog_lock,flags);
return;
}
if (type == NOTIFY_EVENT)
event_store_usblog_notify(type,
(unsigned long *)param1, (int *)parma2);
else if (type == NOTIFY_USBMODE)
mode_store_usblog_notify(type, (char *)param1);
else if (type == NOTIFY_USBSTATE)
state_store_usblog_notify(type, (char *)param1);
else
pr_err("%s type error %d\n", __func__, type);
spin_unlock_irqrestore(&usblog_root.usblog_lock, flags);
}
EXPORT_SYMBOL(store_usblog_notify);
int register_usblog_proc(void)
{
int ret = 0;
proc_create("usblog", 0, NULL, &usblog_proc_fops);
usblog_root.usblog_buffer
= kzalloc(sizeof(struct usblog_buf), GFP_KERNEL);
if (!usblog_root.usblog_buffer) {
ret = -ENOMEM;
goto err;
}
pr_info("%s size=%zu\n", __func__, sizeof(struct usblog_buf));
spin_lock_init(&usblog_root.usblog_lock);
err:
return ret;
}
EXPORT_SYMBOL(register_usblog_proc);