From 6078a031f17bf0fcb2097cd665d9c0486c869b05 Mon Sep 17 00:00:00 2001 From: a17671 Date: Tue, 9 Oct 2018 14:38:38 +0800 Subject: [PATCH] Add Motorola dropbox driver Add Moto dropbox driver to Provide interface for other drivers So they could report issue to system dropbox Change-Id: Ic44ee90417f88965b44367b4d04957cc64bcf49d Signed-off-by: a17671 Reviewed-on: https://gerrit.mot.com/1252527 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- drivers/misc/Kconfig | 10 + drivers/misc/Makefile | 1 + drivers/misc/dropbox.c | 438 ++++++++++++++++++++++++++++++++++++++++ include/linux/dropbox.h | 47 +++++ 4 files changed, 496 insertions(+) create mode 100644 drivers/misc/dropbox.c create mode 100644 include/linux/dropbox.h diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 46aabe2c6227..3c4248002116 100755 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -527,6 +527,16 @@ config MEMORY_STATE_TIME help Memory time statistics exported to /sys/kernel/memory_state_time +config DROPBOX + tristate "Dropbox sysfs support" + default y + help + This option enables a sysfs interface for drivers to send text or + binary events to the Android dropbox. A lightweight userspace daemon + polls the sysfs files and copies the data into dropbox using the built + in dropbox manager service. This enables fast and simple logging of + various critical events from kernel drivers. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index af8593b5d961..c7baa400cc24 100755 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -84,3 +84,4 @@ targets += lkdtm_rodata.o lkdtm_rodata_objcopy.o $(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o FORCE $(call if_changed,objcopy) obj-$(CONFIG_AW8695_HAPTIC) += aw8695_haptic/ +obj-$(CONFIG_DROPBOX) += dropbox.o diff --git a/drivers/misc/dropbox.c b/drivers/misc/dropbox.c new file mode 100644 index 000000000000..3605f1c9415b --- /dev/null +++ b/drivers/misc/dropbox.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2013-2014, Motorola Mobility, LLC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DROPBOX_TEXT_FLAG 0x00000002 +#define DROPBOX_FILE_FLAG 0x40000000 + +#define EVENT_QUEUE_SIZE 5 +#define EVENT_NAME_LENGTH 64 + +static DEFINE_MUTEX(dropbox_queue_lock); +static struct kobject dropbox_kobj; + +struct dropbox_event { + char name[EVENT_NAME_LENGTH]; + void *data; + size_t size; + int flags; + struct list_head list; +}; + +static LIST_HEAD(dropbox_event_list); + +static int dropbox_event_queue_depth_locked(void) +{ + struct dropbox_event *event = NULL; + int queue_depth = 0; + + list_for_each_entry(event, &dropbox_event_list, list) + queue_depth++; + return queue_depth; +} + +static bool dropbox_event_queue_full_locked(void) +{ + return dropbox_event_queue_depth_locked() >= EVENT_QUEUE_SIZE; +} + +static bool dropbox_event_queue_empty_locked(void) +{ + return dropbox_event_queue_depth_locked() == 0; +} + +static void dropbox_notify_if_events(void) +{ + if (mutex_lock_interruptible(&dropbox_queue_lock)) + sysfs_notify(&dropbox_kobj, NULL, "event"); + else { + if (!dropbox_event_queue_empty_locked()) + sysfs_notify(&dropbox_kobj, NULL, "event"); + mutex_unlock(&dropbox_queue_lock); + } +} + +static void dropbox_free_event(struct dropbox_event *event) +{ + if (event) { + if (event->data) { + vfree(event->data); + event->data = NULL; + } + vfree(event); + } +} + +static struct dropbox_event *dropbox_dequeue_event_locked(void) +{ + struct dropbox_event *event = NULL; + + if (!list_empty(&dropbox_event_list)) { + event = list_first_entry(&dropbox_event_list, + struct dropbox_event, list); + list_del(&event->list); + } + + return event; +} + +static struct dropbox_event *dropbox_dequeue_event(void) +{ + struct dropbox_event *event = NULL; + + mutex_lock(&dropbox_queue_lock); + event = dropbox_dequeue_event_locked(); + mutex_unlock(&dropbox_queue_lock); + + return event; +} + +static void dropbox_queue_event(char *name, void *data, size_t size, int flags) +{ + struct dropbox_event *new_event = NULL; + + if (!name || strlen(name) <= 0 || strlen(name) > EVENT_NAME_LENGTH) { + pr_err("%s: invalid name\n", __func__); + return; + } + + if (!data || !size) { + pr_err("%s: invalid arguments [%s][%p][%zu]\n", __func__, name, + data, size); + return; + } + + mutex_lock(&dropbox_queue_lock); + + if (dropbox_event_queue_full_locked()) { + struct dropbox_event *cur = NULL; + /* drop oldest event */ + cur = dropbox_dequeue_event_locked(); + if (cur) { + pr_warn("%s: Dropping oldest event [%s]", + __func__, cur->name); + dropbox_free_event(cur); + } + } + + new_event = vzalloc(sizeof(struct dropbox_event)); + if (new_event) { + scnprintf(new_event->name, + sizeof(new_event->name), "%s", name); + new_event->data = vmalloc(size); + if (new_event->data) { + memcpy(new_event->data, data, size); + new_event->size = size; + new_event->flags = flags; + + list_add_tail(&new_event->list, &dropbox_event_list); + sysfs_notify(&dropbox_kobj, NULL, "event"); + } else { + pr_err("%s: Failed to allocate new event data", + __func__); + dropbox_free_event(new_event); + } + } else { + pr_err("%s: Failed to allocate new event", __func__); + } + + mutex_unlock(&dropbox_queue_lock); +} + +void dropbox_queue_event_binary(char *name, void *data, size_t size) +{ + dropbox_queue_event(name, data, size, 0); +} + +void dropbox_queue_event_text(char *name, void *data, size_t size) +{ + dropbox_queue_event(name, data, size, DROPBOX_TEXT_FLAG); +} + +void dropbox_queue_event_binaryfile(char *name, char *path) +{ + dropbox_queue_event(name, path, strlen(path), DROPBOX_FILE_FLAG); +} + +void dropbox_queue_event_textfile(char *name, char *path) +{ + dropbox_queue_event(name, path, strlen(path), + DROPBOX_TEXT_FLAG | DROPBOX_FILE_FLAG); +} + +void dropbox_queue_event_empty(char *name) +{ + dropbox_queue_event(name, name, strlen(name), DROPBOX_TEXT_FLAG); +} + +struct dropbox_trigger_callback { + char name[EVENT_NAME_LENGTH]; + void (*callback)(void *); + void *data; + struct list_head list; +}; + +static LIST_HEAD(dropbox_trigger_callback_list); + +void dropbox_register_trigger_callback(const char *name, + void (*callback)(void *), + void *data) +{ + struct dropbox_trigger_callback *cur = NULL; + + if (!name || strlen(name) <= 0 || strlen(name) > EVENT_NAME_LENGTH) { + pr_err("%s: invalid name\n", __func__); + return; + } + + list_for_each_entry(cur, &dropbox_trigger_callback_list, list) { + if (!strncmp(cur->name, name, sizeof(cur->name))) { + pr_err("%s: Event already registered with name: %s\n", + __func__, name); + return; + } + } + + cur = vzalloc(sizeof(struct dropbox_trigger_callback)); + if (cur) { + scnprintf(cur->name, sizeof(cur->name), "%s", name); + cur->callback = callback; + cur->data = data; + + list_add_tail(&cur->list, &dropbox_trigger_callback_list); + } else { + pr_err("%s: Failed to allocate new trigger callback", __func__); + } +} + +static void do_dropbox_trigger_callback(const char *name) +{ + struct dropbox_trigger_callback *cur = NULL; + + if (!name || strlen(name) <= 0 || strlen(name) > EVENT_NAME_LENGTH) { + pr_err("%s: invalid name\n", __func__); + return; + } + + list_for_each_entry(cur, &dropbox_trigger_callback_list, list) { + if (!strncmp(cur->name, name, sizeof(cur->name))) { + cur->callback(cur->data); + return; + } + } + + pr_err("%s: No callback found for event [%s]", __func__, name); +} + +void dropbox_dummy_test_trigger(void *data) +{ + dropbox_queue_event_empty("dropbox_dummy_test"); +} + +/* sysfs entry code */ + + +struct dropbox_attribute { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, char *buf); + ssize_t (*store)(struct kobject *kobj, const char *buf, + size_t count); +}; + +#define to_dropbox_attr(a) \ +container_of(a, struct dropbox_attribute, attr) + +static struct dropbox_event *dropbox_current_event; + +/* Dump the sysfs binary data to the user */ +static ssize_t dropbox_data_show(struct file *filep, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + int size = 0; + + if (!dropbox_current_event) + dropbox_current_event = dropbox_dequeue_event(); + + if (!dropbox_current_event) { + pr_err("%s: no dropbox events", __func__); + goto end; + } + + if (dropbox_current_event->data) { + void *src = dropbox_current_event->data + off; + + if (dropbox_current_event->size > off) { + size = dropbox_current_event->size - off; + size = size > count ? count : size; + memcpy(buf, src, size); + } + } else + pr_err("%s: dropbox_data is empty\n", __func__); + + if (!size) { + dropbox_free_event(dropbox_current_event); + dropbox_current_event = NULL; + dropbox_notify_if_events(); + } + +end: + return size; +} + +static ssize_t dropbox_event_show(struct kobject *kobj, char *buf) +{ + if (!list_empty(&dropbox_event_list)) { + struct dropbox_event *event = list_first_entry( + &dropbox_event_list, struct dropbox_event, list); + return scnprintf(buf, PAGE_SIZE, "%s:%d\n", event->name, + event->flags); + } else + return scnprintf(buf, PAGE_SIZE, "\n"); +} + +static ssize_t dropbox_trigger_store(struct kobject *kobj, const char *buf, + size_t count) +{ + unsigned int ret = -EINVAL; + char event_name[EVENT_NAME_LENGTH]; + + if (count <= 0 || count > EVENT_NAME_LENGTH) { + pr_err("%s: invalid string, max length is 64 characters\n", + __func__); + } else { + ret = sscanf(buf, "%63s", event_name); + if (ret != 1) + return -EINVAL; + pr_info("%s: forcing event [%s]\n", __func__, event_name); + do_dropbox_trigger_callback(event_name); + } + + return count; +} + +static ssize_t dropbox_available_triggers_show(struct kobject *kobj, char *buf) +{ + char *cur = buf; + struct dropbox_trigger_callback *cb_cur = NULL; + + list_for_each_entry(cb_cur, &dropbox_trigger_callback_list, list) { + int count = scnprintf(cur, PAGE_SIZE-(cur-buf), "%s\n", + cb_cur->name); + cur = cur + count; + } + + return cur-buf; +} + +static struct bin_attribute attr_data = { + .attr.name = "data", + .attr.mode = 0440, + .size = 0, + .read = dropbox_data_show +}; + +#define DROPBOX_ATTR(_name, _mode, _show, _store) \ +static struct dropbox_attribute attr_##_name = { \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +} + +DROPBOX_ATTR(event, 0440, dropbox_event_show, NULL); +DROPBOX_ATTR(trigger, 0200, NULL, dropbox_trigger_store); +DROPBOX_ATTR(available_triggers, 0400, dropbox_available_triggers_show, NULL); + +static void dropbox_sysfs_release(struct kobject *kobj) +{ +} + +static ssize_t dropbox_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct dropbox_attribute *pattr = to_dropbox_attr(attr); + ssize_t ret = -EIO; + + if (kobj && pattr->show) + ret = pattr->show(kobj, buf); + + return ret; +} + +static ssize_t dropbox_sysfs_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t count) +{ + struct dropbox_attribute *pattr = to_dropbox_attr(attr); + ssize_t ret = -EIO; + + if (kobj && pattr->store) + ret = pattr->store(kobj, buf, count); + + return ret; +} + +static const struct sysfs_ops dropbox_sysfs_ops = { + .show = dropbox_sysfs_show, + .store = dropbox_sysfs_store, +}; + +static struct kobj_type ktype_dropbox = { + .sysfs_ops = &dropbox_sysfs_ops, + .default_attrs = NULL, + .release = dropbox_sysfs_release, +}; + +int __init dropbox_init(void) +{ + int ret = 0; + + ret = kobject_init_and_add(&dropbox_kobj, &ktype_dropbox, + kernel_kobj, "dropbox"); + if (ret) + goto done; + + ret = sysfs_create_file(&dropbox_kobj, &attr_event.attr); + if (ret) + goto done; + + ret = sysfs_create_file(&dropbox_kobj, &attr_trigger.attr); + if (ret) + goto done; + + ret = sysfs_create_file(&dropbox_kobj, &attr_available_triggers.attr); + if (ret) + goto done; + + ret = sysfs_create_bin_file(&dropbox_kobj, &attr_data); + if (ret) + goto done; + + dropbox_register_trigger_callback("dummy_test_trigger", + &dropbox_dummy_test_trigger, NULL); + +done: + return ret; +} + +core_initcall(dropbox_init); diff --git a/include/linux/dropbox.h b/include/linux/dropbox.h new file mode 100644 index 000000000000..bc081cfcfb34 --- /dev/null +++ b/include/linux/dropbox.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013-2014, Motorola Mobility, LLC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef DROPBOX_H +#define DROPBOX_H + +#include +#include + +#ifdef CONFIG_DROPBOX + +extern void dropbox_register_trigger_callback(const char *name, + void (*callback)(void *), void *data); +extern void dropbox_queue_event_binary(char *name, + void *data, size_t size); +extern void dropbox_queue_event_text(char *name, + void *data, size_t size); +extern void dropbox_queue_event_binaryfile(char *name, char *path); +extern void dropbox_queue_event_textfile(char *name, char *path); +extern void dropbox_queue_event_empty(char *name); + +#else + +static inline void dropbox_register_trigger_callback(const char *name, + void (*callback)(void *), void *data) {} +static inline void dropbox_queue_event_binary(char *name, + void *data, size_t size) {} +static inline void dropbox_queue_event_text(char *name, + void *data, size_t size) {} +static inline void dropbox_queue_event_binaryfile(char *name, char *path) {} +static inline void dropbox_queue_event_textfile(char *name, char *path) {} +static inline void dropbox_queue_event_empty(char *name) {} + +#endif + +#endif /* DROPBOX_H */ -- 2.20.1