blob: 4997bfeb2b5218407fd9da6d715873c4a977cb36 [file] [log] [blame]
/*
* drivers/usb/notify/usblog_proc_notify.c
*
* Copyright (C) 2016-2017 Samsung, Inc.
* Author: Dongrak Shin <dongrak.shin@samsung.com>
*
*/
/* usb notify layer v3.1 */
#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_BUF2_SIZE (1 << 8) /* 256 */
#define USBLOG_MAX_BUF3_SIZE (1 << 9) /* 512 */
#define USBLOG_MAX_STRING_SIZE (1 << 4) /* 16 */
#define USBLOG_CMP_INDEX 3
#define USBLOG_CCIC_BUFFER_SIZE USBLOG_MAX_BUF3_SIZE
#define USBLOG_MODE_BUFFER_SIZE USBLOG_MAX_BUF_SIZE
#define USBLOG_STATE_BUFFER_SIZE USBLOG_MAX_BUF2_SIZE
#define USBLOG_EVENT_BUFFER_SIZE USBLOG_MAX_BUF_SIZE
struct ccic_buf {
unsigned long long ts_nsec;
int cc_type;
uint64_t noti;
};
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 ccic_count;
unsigned long long mode_count;
unsigned long long state_count;
unsigned long long event_count;
unsigned long ccic_index;
unsigned long mode_index;
unsigned long state_index;
unsigned long event_index;
struct ccic_buf ccic_buffer[USBLOG_CCIC_BUFFER_SIZE];
struct mode_buf mode_buffer[USBLOG_MODE_BUFFER_SIZE];
struct state_buf state_buffer[USBLOG_STATE_BUFFER_SIZE];
struct event_buf event_buffer[USBLOG_EVENT_BUFFER_SIZE];
};
struct ccic_version {
unsigned char hw_version[4];
unsigned char sw_main[3];
unsigned char sw_boot;
};
struct usblog_root_str {
struct usblog_buf *usblog_buffer;
struct ccic_version ccic_ver;
spinlock_t usblog_lock;
int init;
};
struct ccic_type {
uint64_t src:4;
uint64_t dest:4;
uint64_t id:8;
uint64_t sub1:16;
uint64_t sub2:16;
uint64_t sub3:16;
};
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_RESET_FULL:
return "RESET : FULL";
case NOTIFY_RESET_HIGH:
return "RESET: HIGH";
case NOTIFY_RESET_SUPER:
return "RESET : SUPER";
case NOTIFY_PULLUP:
return "VBUS_PULLUP (EN OR DIS)";
case NOTIFY_PULLUP_ENABLE:
return "VBUS_PULLUP_EN";
case NOTIFY_PULLUP_EN_SUCCESS:
return "VBUS_PULLUP_EN : S";
case NOTIFY_PULLUP_EN_FAIL:
return "VBUS_PULLUP_EN : F";
case NOTIFY_PULLUP_DISABLE:
return "VBUS_PULLUP_DIS";
case NOTIFY_PULLUP_DIS_SUCCESS:
return "VBUS_PULLUP_DIS : S";
case NOTIFY_PULLUP_DIS_FAIL:
return "VBUS_PULLUP_DIS : F";
case NOTIFY_VBUS_SESSION:
return "VBUS_SESSION (EN OR DIS)";
case NOTIFY_VBUS_SESSION_ENABLE:
return "VBUS_SESSION_EN";
case NOTIFY_VBUS_EN_SUCCESS:
return "VBUS_SESSION_EN : S";
case NOTIFY_VBUS_EN_FAIL:
return "VBUS_SESSION_EN : F";
case NOTIFY_VBUS_SESSION_DISABLE:
return "VBUS_SESSIOIN_DIS";
case NOTIFY_VBUS_DIS_SUCCESS:
return "VBUS_SESSION_DIS : S";
case NOTIFY_VBUS_DIS_FAIL:
return "VBUS_SESSION_DIS : F";
case NOTIFY_ACCSTART:
return "ACCSTART";
case NOTIFY_HIGH:
return "HIGH SPEED";
case NOTIFY_SUPER:
return "SUPER SPEED";
default:
return "UNDEFINED";
}
}
static const char *usbstatus_string(enum usblog_status usbstatus)
{
switch (usbstatus) {
case NOTIFY_DETACH:
return "DETACH";
case NOTIFY_ATTACH_DFP:
return "ATTACH_DFP";
case NOTIFY_ATTACH_UFP:
return "ATTACH_UFP";
case NOTIFY_ATTACH_DRP:
return "ATTACH_DRP";
default:
return "UNDEFINED";
}
}
static const char *ccic_dev_string(enum ccic_device dev)
{
switch (dev) {
case NOTIFY_DEV_INITIAL:
return "INITIAL";
case NOTIFY_DEV_USB:
return "USB";
case NOTIFY_DEV_BATTERY:
return "BATTERY";
case NOTIFY_DEV_PDIC:
return "PDIC";
case NOTIFY_DEV_MUIC:
return "MUIC";
case NOTIFY_DEV_CCIC:
return "CCIC";
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
case NOTIFY_DEV_MANAGER:
return "MANAGER";
#endif
case NOTIFY_DEV_DP:
return "DP";
case NOTIFY_DEV_USB_DP:
return "USB_DP";
default:
return "UNDEFINED";
}
}
static const char *ccic_id_string(enum ccic_id id)
{
switch (id) {
case NOTIFY_ID_INITIAL:
return "ID_INITIAL";
case NOTIFY_ID_ATTACH:
return "ID_CONNECT";
case NOTIFY_ID_RID:
return "ID_RID";
case NOTIFY_ID_USB:
return "ID_USB";
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
case NOTIFY_ID_POWER_STATUS:
return "ID_POWER_STATUS";
#endif
case NOTIFY_ID_WATER:
return "ID_WATER";
case NOTIFY_ID_VCONN:
return "ID_VCONN";
case NOTIFY_ID_OTG:
return "ID_OTG";
case NOTIFY_ID_TA:
return "ID_TA";
case NOTIFY_ID_DP_CONNECT:
return "ID_DP_CONNECT";
case NOTIFY_ID_DP_HPD:
return "ID_DP_HPD";
case NOTIFY_ID_DP_LINK_CONF:
return "ID_DP_LINK_CONF";
case NOTIFY_ID_USB_DP:
return "ID_USB_DP";
case NOTIFY_ID_ROLE_SWAP:
return "ID_ROLE_SWAP";
default:
return "UNDEFINED";
}
}
static const char *ccic_rid_string(enum ccic_rid rid)
{
switch (rid) {
case NOTIFY_RID_UNDEFINED:
return "RID_UNDEFINED";
case NOTIFY_RID_000K:
return "RID_000K";
case NOTIFY_RID_001K:
return "RID_001K";
case NOTIFY_RID_255K:
return "RID_255K";
case NOTIFY_RID_301K:
return "RID_301K";
case NOTIFY_RID_523K:
return "RID_523K";
case NOTIFY_RID_619K:
return "RID_619K";
case NOTIFY_RID_OPEN:
return "RID_OPEN";
default:
return "UNDEFINED";
}
}
static const char *ccic_con_string(enum ccic_con con)
{
switch (con) {
case NOTIFY_CON_DETACH:
return "DETACHED";
case NOTIFY_CON_ATTACH:
return "ATTACHED";
default:
return "UNDEFINED";
}
}
static const char *ccic_rprd_string(enum ccic_rprd rprd)
{
switch (rprd) {
case NOTIFY_RD:
return "RD";
case NOTIFY_RP:
return "RP";
default:
return "UNDEFINED";
}
}
static const char *ccic_rpstatus_string(enum ccic_rpstatus rprd)
{
switch (rprd) {
case NOTIFY_RP_NONE:
return "NONE";
case NOTIFY_RP_56K:
return "RP_56K";
case NOTIFY_RP_22K:
return "RP_22K";
case NOTIFY_RP_10K:
return "RP_10K";
case NOTIFY_RP_ABNORMAL:
return "RP_ABNORMAL";
default:
return "UNDEFINED";
}
}
static const char *ccic_hpd_string(enum ccic_hpd hpd)
{
switch (hpd) {
case NOTIFY_HPD_LOW:
return "LOW";
case NOTIFY_HPD_HIGH:
return "HIGH";
case NOTIFY_HPD_IRQ:
return "IRQ";
default:
return "UNDEFINED";
}
}
static const char *ccic_pinA_string(enum ccic_pin_assignment pin)
{
switch (pin) {
case NOTIFY_DP_PIN_UNKNOWN:
return "UNKNOWN";
case NOTIFY_DP_PIN_A:
return "DP_PIN_A";
case NOTIFY_DP_PIN_B:
return "DP_PIN_B";
case NOTIFY_DP_PIN_C:
return "DP_PIN_C";
case NOTIFY_DP_PIN_D:
return "DP_PIN_D";
case NOTIFY_DP_PIN_E:
return "DP_PIN_E";
case NOTIFY_DP_PIN_F:
return "DP_PIN_F";
default:
return "UNKNOWN";
}
}
static const char *ccic_alternatemode_string(uint64_t id)
{
if ((id & ALTERNATE_MODE_READY) && (id & ALTERNATE_MODE_START))
return "READY & START";
else if ((id & ALTERNATE_MODE_READY) && (id & ALTERNATE_MODE_STOP))
return "READY & STOP";
else if (id & ALTERNATE_MODE_READY)
return "MODE READY";
else if (id & ALTERNATE_MODE_START)
return "START";
else if (id & ALTERNATE_MODE_STOP)
return "STOP";
else if (id & ALTERNATE_MODE_RESET)
return "RESET";
else
return "UNDEFINED";
}
static void print_ccic_event(struct seq_file *m, unsigned long long ts,
unsigned long rem_nsec, int cc_type, uint64_t *noti)
{
struct ccic_type type = *(struct ccic_type *)noti;
int cable = type.sub3;
switch (cc_type) {
case NOTIFY_FUNCSTATE:
seq_printf(m, "[%5lu.%06lu] function state = %llu\n",
(unsigned long)ts, rem_nsec / 1000, *noti);
break;
case NOTIFY_ALTERNATEMODE:
seq_printf(m, "[%5lu.%06lu] ccic alternate mode is %s 0x%04llx\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_alternatemode_string(*noti), *noti);
break;
case NOTIFY_CCIC_EVENT:
if (type.id == NOTIFY_ID_ATTACH)
seq_printf(m,
"[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s rprd=%s rpstatus=%s cable=%d %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_rprd_string(type.sub2),
ccic_rpstatus_string(type.sub3),
cable, ccic_con_string(type.sub1));
else if (type.id == NOTIFY_ID_RID)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s rid=%s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_rid_string(type.sub1));
else if (type.id == NOTIFY_ID_USB)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s status=%s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
usbstatus_string(type.sub2));
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
else if (type.id == NOTIFY_ID_POWER_STATUS)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_con_string(type.sub1));
#endif
else if (type.id == NOTIFY_ID_WATER)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s %s detected\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
type.sub1 ? "WATER":"DRY");
else if (type.id == NOTIFY_ID_VCONN)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest));
else if (type.id == NOTIFY_ID_OTG)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
type.sub2? "Enable":"Disable");
else if (type.id == NOTIFY_ID_TA)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_con_string(type.sub1));
else if (type.id == NOTIFY_ID_DP_CONNECT)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s 0x%04x/0x%04x %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
type.sub2,
type.sub3,
ccic_con_string(type.sub1));
else if (type.id == NOTIFY_ID_DP_HPD)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s hpd=%s irq=%s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_hpd_string(type.sub1),
type.sub2 ? "VALID":"NONE");
else if (type.id == NOTIFY_ID_DP_LINK_CONF)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s PIN-assign=%s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_pinA_string(type.sub1));
else if (type.id == NOTIFY_ID_USB_DP)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s CON=%d HS=%d\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
type.sub1,
type.sub2);
else if (type.id == NOTIFY_ID_ROLE_SWAP)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s sub1=%d sub2=%d\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
type.sub1,
type.sub2);
else
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s rprd=%s %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_rprd_string(type.sub2),
ccic_con_string(type.sub1));
break;
case NOTIFY_MANAGER:
if (type.id == NOTIFY_ID_ATTACH)
seq_printf(m,
"[%5lu.%06lu] manager notify: id=%s src=%s dest=%s rprd=%s rpstatus=%s cable=%d %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_rprd_string(type.sub2),
ccic_rpstatus_string(type.sub3),
cable, ccic_con_string(type.sub1));
else if (type.id == NOTIFY_ID_RID)
seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s rid=%s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_rid_string(type.sub1));
else if (type.id == NOTIFY_ID_USB)
seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s status=%s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
usbstatus_string(type.sub2));
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
else if (type.id == NOTIFY_ID_POWER_STATUS)
seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_con_string(type.sub1));
#endif
else if (type.id == NOTIFY_ID_WATER)
seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s %s detected\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
type.sub1 ? "WATER":"DRY");
else if (type.id == NOTIFY_ID_VCONN)
seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest));
else if (type.id == NOTIFY_ID_OTG)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
type.sub2? "Enable":"Disable");
else if (type.id == NOTIFY_ID_TA)
seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_con_string(type.sub1));
else if (type.id == NOTIFY_ID_DP_CONNECT)
seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s 0x%04x/0x%04x %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
type.sub2,
type.sub3,
ccic_con_string(type.sub1));
else if (type.id == NOTIFY_ID_DP_HPD)
seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s hpd=%s irq=%s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_hpd_string(type.sub1),
type.sub2 ? "VALID":"NONE");
else if (type.id == NOTIFY_ID_DP_LINK_CONF)
seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s PIN-assign=%s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_pinA_string(type.sub1));
else if (type.id == NOTIFY_ID_USB_DP)
seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s CON=%d HS=%d\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
type.sub1,
type.sub2);
else if (type.id == NOTIFY_ID_ROLE_SWAP)
seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s syb1=%d sub2=%d\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
type.sub1,
type.sub2);
else
seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s rprd=%s %s\n",
(unsigned long)ts, rem_nsec / 1000,
ccic_id_string(type.id),
ccic_dev_string(type.src),
ccic_dev_string(type.dest),
ccic_rprd_string(type.sub2),
ccic_con_string(type.sub1));
break;
default:
pr_info("%s undefined event\n", __func__);
break;
}
}
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;
unsigned long i;
temp_usblog_buffer = usblog_root.usblog_buffer;
if (!temp_usblog_buffer)
goto err;
seq_printf(m,
"usblog CC IC version:\n");
seq_printf(m,
"hw version =%2x %2x %2x %2x\n",
usblog_root.ccic_ver.hw_version[3],
usblog_root.ccic_ver.hw_version[2],
usblog_root.ccic_ver.hw_version[1],
usblog_root.ccic_ver.hw_version[0]);
seq_printf(m,
"sw version =%2x %2x %2x %2x\n",
usblog_root.ccic_ver.sw_main[2],
usblog_root.ccic_ver.sw_main[1],
usblog_root.ccic_ver.sw_main[0],
usblog_root.ccic_ver.sw_boot);
seq_printf(m,
"\n\n");
seq_printf(m,
"usblog CCIC EVENT: count=%llu maxline=%d\n",
temp_usblog_buffer->ccic_count,
USBLOG_CCIC_BUFFER_SIZE);
if (temp_usblog_buffer->ccic_count >= USBLOG_CCIC_BUFFER_SIZE) {
for (i = temp_usblog_buffer->ccic_index;
i < USBLOG_CCIC_BUFFER_SIZE; i++) {
ts = temp_usblog_buffer->ccic_buffer[i].ts_nsec;
rem_nsec = do_div(ts, 1000000000);
print_ccic_event(m, ts, rem_nsec,
temp_usblog_buffer->ccic_buffer[i].cc_type,
&temp_usblog_buffer->ccic_buffer[i].noti);
}
}
for (i = 0; i < temp_usblog_buffer->ccic_index; i++) {
ts = temp_usblog_buffer->ccic_buffer[i].ts_nsec;
rem_nsec = do_div(ts, 1000000000);
print_ccic_event(m, ts, rem_nsec,
temp_usblog_buffer->ccic_buffer[i].cc_type,
&temp_usblog_buffer->ccic_buffer[i].noti);
}
seq_printf(m,
"\n\n");
seq_printf(m,
"usblog USB_MODE: count=%llu maxline=%d\n",
temp_usblog_buffer->mode_count,
USBLOG_MODE_BUFFER_SIZE);
if (temp_usblog_buffer->mode_count >= USBLOG_MODE_BUFFER_SIZE) {
for (i = temp_usblog_buffer->mode_index;
i < USBLOG_MODE_BUFFER_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_STATE_BUFFER_SIZE);
if (temp_usblog_buffer->state_count >= USBLOG_STATE_BUFFER_SIZE) {
for (i = temp_usblog_buffer->state_index;
i < USBLOG_STATE_BUFFER_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_EVENT_BUFFER_SIZE);
if (temp_usblog_buffer->event_count >= USBLOG_EVENT_BUFFER_SIZE) {
for (i = temp_usblog_buffer->event_index;
i < USBLOG_EVENT_BUFFER_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 ccic_store_usblog_notify(int type, uint64_t *param1)
{
struct ccic_buf *ccic_buffer;
unsigned long long *target_count;
unsigned long *target_index;
target_count = &usblog_root.usblog_buffer->ccic_count;
target_index = &usblog_root.usblog_buffer->ccic_index;
ccic_buffer = &usblog_root.usblog_buffer->ccic_buffer[*target_index];
if (ccic_buffer == NULL) {
pr_err("%s target_buffer error\n", __func__);
goto err;
}
ccic_buffer->ts_nsec = local_clock();
ccic_buffer->cc_type = type;
ccic_buffer->noti = *param1;
*target_index = (*target_index+1)%USBLOG_CCIC_BUFFER_SIZE;
(*target_count)++;
err:
return;
}
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;
int param_len;
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 (type == NOTIFY_USBMODE_EXTRA) {
param_len = strlen(b);
if (param_len >= USBLOG_MAX_STRING_SIZE)
param_len = USBLOG_MAX_STRING_SIZE-1;
strncpy(md_buffer->usbmode_str, b, param_len);
} else if (type == NOTIFY_USBMODE) {
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_MODE_BUFFER_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, index2, index3;
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 */
name = strsep(&b, ":");
if (b) {
index2 = *b;
switch (index2) {
case 'L': /* FULL SPEED */
usbstate = NOTIFY_RESET_FULL;
break;
case 'H': /* HIGH SPEED */
usbstate = NOTIFY_RESET_HIGH;
break;
case 'S': /* SUPER SPEED */
usbstate = NOTIFY_RESET_SUPER;
break;
default:
usbstate = NOTIFY_RESET;
break;
}
} else
usbstate = NOTIFY_RESET;
break;
case 'L': /* GADGET PULL UP/DN */
name = strsep(&b, ":");
if (b) {
index2 = *b;
name = strsep(&b, ":");
if (b)
index3 = *b;
else /* X means none */
index3 = 'X';
} else /* X means none */
index2 = 'X';
switch (index2) {
case 'E': /* VBUS SESSION ENABLE */
if (index3 == 'S')
usbstate = NOTIFY_PULLUP_EN_SUCCESS;
else if (index3 == 'F')
usbstate = NOTIFY_PULLUP_EN_FAIL;
else
usbstate = NOTIFY_PULLUP_ENABLE;
break;
case 'D': /* VBUS SESSION DISABLE */
if (index3 == 'S')
usbstate = NOTIFY_PULLUP_DIS_SUCCESS;
else if (index3 == 'F')
usbstate = NOTIFY_PULLUP_DIS_FAIL;
else
usbstate = NOTIFY_PULLUP_DISABLE;
break;
default:
usbstate = NOTIFY_PULLUP;
break;
}
break;
case 'R': /* ACCESSORY START */
usbstate = NOTIFY_ACCSTART;
break;
case 'S': /* GADGET_VBUS EN/DN*/
name = strsep(&b, ":");
if (b) {
index2 = *b;
name = strsep(&b, ":");
if (b)
index3 = *b;
else /* X means none */
index3 = 'X';
} else /* X means none */
index2 = 'X';
switch (index2) {
case 'E': /* VBUS SESSION ENABLE */
if (index3 == 'S')
usbstate = NOTIFY_VBUS_EN_SUCCESS;
else if (index3 == 'F')
usbstate = NOTIFY_VBUS_EN_FAIL;
else
usbstate = NOTIFY_VBUS_SESSION_ENABLE;
break;
case 'D': /* VBUS SESSION DISABLE */
if (index3 == 'S')
usbstate = NOTIFY_VBUS_DIS_SUCCESS;
else if (index3 == 'F')
usbstate = NOTIFY_VBUS_DIS_FAIL;
else
usbstate = NOTIFY_VBUS_SESSION_DISABLE;
break;
default:
usbstate = NOTIFY_VBUS_SESSION;
break;
}
break;
case 'D': /* SUPER SPEED */
usbstate = NOTIFY_SUPER;
break;
case 'H': /*HIGH SPEED */
usbstate = NOTIFY_HIGH;
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_STATE_BUFFER_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_EVENT_BUFFER_SIZE;
(*target_count)++;
err:
return;
}
void store_usblog_notify(int type, void *param1, void *param2)
{
unsigned long flags = 0;
uint64_t temp = 0;
if (!usblog_root.init)
register_usblog_proc();
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_FUNCSTATE || type == NOTIFY_ALTERNATEMODE) {
temp = *(int *)param1;
ccic_store_usblog_notify(type, &temp);
} else if (type == NOTIFY_CCIC_EVENT
|| type == NOTIFY_MANAGER)
ccic_store_usblog_notify(type, (uint64_t *)param1);
else if (type == NOTIFY_EVENT)
event_store_usblog_notify(type,
(unsigned long *)param1, (int *)param2);
else if (type == NOTIFY_USBMODE
|| type == NOTIFY_USBMODE_EXTRA)
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);
void store_ccic_version(unsigned char *hw, unsigned char *sw_main,
unsigned char *sw_boot)
{
if (!hw || !sw_main || !sw_boot) {
pr_err("%s null buffer\n", __func__);
return;
}
memcpy(&usblog_root.ccic_ver.hw_version, hw, 4);
memcpy(&usblog_root.ccic_ver.sw_main, sw_main, 3);
memcpy(&usblog_root.ccic_ver.sw_boot, sw_boot, 1);
}
EXPORT_SYMBOL(store_ccic_version);
#if defined(CONFIG_USB_HW_PARAM)
unsigned long long show_ccic_version(void)
{
unsigned long long ret = 0;
memcpy(&ret, &usblog_root.ccic_ver, sizeof(unsigned long long));
return ret;
}
EXPORT_SYMBOL(show_ccic_version);
#endif
int register_usblog_proc(void)
{
int ret = 0;
struct otg_notify *o_notify = get_otg_notify();
if (usblog_root.init) {
pr_err("%s already registered\n", __func__);
if (o_notify != NULL)
goto err;
}
spin_lock_init(&usblog_root.usblog_lock);
usblog_root.init = 1;
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));
err:
return ret;
}
EXPORT_SYMBOL(register_usblog_proc);
void unregister_usblog_proc(void)
{
kfree(usblog_root.usblog_buffer);
usblog_root.usblog_buffer = NULL;
remove_proc_entry("usblog", NULL);
usblog_root.init = 0;
}
EXPORT_SYMBOL(unregister_usblog_proc);