/* * drivers/usb/notify/usblog_proc_notify.c * * Copyright (C) 2016 Samsung, Inc. * Author: Dongrak Shin * */ /* usb notify layer v2.0 */ #define pr_fmt(fmt) "usb_notify: " fmt #include #include #include #include #include #include #include #include #include #include #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);