mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
68
drivers/usb/notify/Kconfig
Normal file
68
drivers/usb/notify/Kconfig
Normal 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.
|
9
drivers/usb/notify/Makefile
Normal file
9
drivers/usb/notify/Makefile
Normal 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
|
||||
|
297
drivers/usb/notify/dock_notify.c
Normal file
297
drivers/usb/notify/dock_notify.c
Normal 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);
|
18
drivers/usb/notify/dock_notify.h
Normal file
18
drivers/usb/notify/dock_notify.h
Normal 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
|
||||
|
147
drivers/usb/notify/external_notify.c
Executable file
147
drivers/usb/notify/external_notify.c
Executable 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);
|
295
drivers/usb/notify/host_notify_class.c
Normal file
295
drivers/usb/notify/host_notify_class.c
Normal 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");
|
488
drivers/usb/notify/usb_notifier.c
Normal file
488
drivers/usb/notify/usb_notifier.c
Normal 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");
|
||||
|
28
drivers/usb/notify/usb_notifier.h
Executable file
28
drivers/usb/notify/usb_notifier.h
Executable 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
|
||||
|
1591
drivers/usb/notify/usb_notify.c
Normal file
1591
drivers/usb/notify/usb_notify.c
Normal file
File diff suppressed because it is too large
Load diff
257
drivers/usb/notify/usb_notify_sysfs.c
Executable file
257
drivers/usb/notify/usb_notify_sysfs.c
Executable 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);
|
||||
|
32
drivers/usb/notify/usb_notify_sysfs.h
Executable file
32
drivers/usb/notify/usb_notify_sysfs.h
Executable 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
|
||||
|
350
drivers/usb/notify/usblog_proc_notify.c
Executable file
350
drivers/usb/notify/usblog_proc_notify.c
Executable 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);
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue