Add Motorola dropbox driver
authora17671 <a17671@motorola.com>
Tue, 9 Oct 2018 06:38:38 +0000 (14:38 +0800)
committerlingsen1 <lingsen1@lenovo.com>
Sun, 7 Feb 2021 09:36:48 +0000 (17:36 +0800)
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 <a17671@motorola.com>
Reviewed-on: https://gerrit.mot.com/1252527
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Reviewed-by: Xiangpo Zhao <zhaoxp3@motorola.com>
Submit-Approved: Jira Key

drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/dropbox.c [new file with mode: 0644]
include/linux/dropbox.h [new file with mode: 0644]

index 46aabe2c6227b45c01f9c89265bead55e8893b8e..3c42480021160837deef058e5dd309306054b754 100755 (executable)
@@ -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"
index 84ec1aaccb7bb6e4c1723027cd9787d9ac76f8e7..2d2363926abfb4d899d637be9476e97ee114a068 100755 (executable)
@@ -85,3 +85,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 (file)
index 0000000..3605f1c
--- /dev/null
@@ -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 <asm/page.h>
+#include <linux/export.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+
+#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 (file)
index 0000000..bc081cf
--- /dev/null
@@ -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 <linux/types.h>
+#include <linux/time.h>
+
+#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 */