greybus: audio: Add Audio Manager
authorSvetlin Ankov <ankov_svetlin@projectara.com>
Wed, 13 Jan 2016 21:07:48 +0000 (14:07 -0700)
committerGreg Kroah-Hartman <gregkh@google.com>
Thu, 14 Jan 2016 00:12:56 +0000 (16:12 -0800)
This is a simple module that keeps a list of connected GB audio
modules.

Whenever a device is attached, an appropriate uevent is sent to
userspace:

    UDEV  [4941.803215] add      /kernel/gb_audio_manager/0 (gb_audio_manager)
    ACTION=add
    CPORT=99
    DEVICES=0x10
    DEVPATH=/kernel/gb_audio_manager/0
    NAME=naim
    PID=64
    SEQNUM=1828
    SLOT=2
    SUBSYSTEM=gb_audio_manager
    USEC_INITIALIZED=802416
    VID=128

And whenever removed:

    UDEV  [4941.836588] remove   /kernel/gb_audio_manager/0 (gb_audio_manager)
    ACTION=remove
    DEVPATH=/kernel/gb_audio_manager/0
    SEQNUM=1833
    SUBSYSTEM=gb_audio_manager
    USEC_INITIALIZED=835681

The API consists of functions for adding, removing and inspecting
added device module descriptions (struct gb_audio_module):

    int                         gb_audio_manager_add(struct gb_audio_module_descriptor *desc);
    int                         gb_audio_manager_remove(int id);
    int                         gb_audio_manager_remove_all(void);
    struct  gb_audio_module*    gb_audio_manager_get_module(int id);
    void                        gb_audio_manager_put_module(struct gb_audio_module *module);
    int                         gb_audio_manager_dump_module(int id);
    void                        gb_audio_manager_dump_all(void);

Devices can be inspected through sysfs in /sys/kernel/gb_audio_manager/{id}/*

If GB_AUDIO_MANAGER_SYSFS is exported as 'true', managing devices can be done
via the SYSFS as well. For instance:

    echo name=naim slot=2 vid=128 pid=64 cport=99 devices=0x10 > /sys/kernel/gb_audio_manager/add
    echo all > /sys/kernel/gb_audio_manager/dump
    echo 2 > /sys/kernel/gb_audio_manager/dump
    echo 2 > /sys/kernel/gb_audio_manager/remove

Signed-off-by: Svetlin Ankov <ankov_svetlin@projectara.com>
Signed-off-by: Mark Greer <mgreer@animalcreek.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/Makefile
drivers/staging/greybus/audio_manager.c [new file with mode: 0644]
drivers/staging/greybus/audio_manager.h [new file with mode: 0644]
drivers/staging/greybus/audio_manager_module.c [new file with mode: 0644]
drivers/staging/greybus/audio_manager_private.h [new file with mode: 0644]
drivers/staging/greybus/audio_manager_sysfs.c [new file with mode: 0644]

index e2ae24b32ab7cf4be753b2d3fe3b78fe6480f84e..4ebdc6b9c89f307e25f4182bd8a278be475db926 100644 (file)
@@ -32,6 +32,8 @@ gb-arche-y := arche-platform.o arche-apb-ctrl.o
 gb-audio-codec-y := audio_codec.o
 gb-audio-gb-y := audio_gb.o
 gb-audio-apbridgea-y := audio_apbridgea.o
+gb-audio-manager-y += audio_manager.o
+gb-audio-manager-y += audio_manager_module.o
 gb-camera-y := camera.o
 
 obj-m += greybus.o
@@ -48,6 +50,7 @@ obj-m += gb-audio-codec.o
 obj-m += gb-camera.o
 obj-m += gb-audio-gb.o
 obj-m += gb-audio-apbridgea.o
+obj-m += gb-audio-manager.o
 
 KERNELVER              ?= $(shell uname -r)
 KERNELDIR              ?= /lib/modules/$(KERNELVER)/build
@@ -89,6 +92,12 @@ ccflags-y := -Wall
 # needed for trace events
 ccflags-y += -I$(src)
 
+GB_AUDIO_MANAGER_SYSFS ?= true
+ifeq ($(GB_AUDIO_MANAGER_SYSFS),true)
+gb-audio-manager-y += audio_manager_sysfs.o
+ccflags-y += -DGB_AUDIO_MANAGER_SYSFS
+endif
+
 all: module
 
 tools::
diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c
new file mode 100644 (file)
index 0000000..1176763
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Greybus operations
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rwlock.h>
+
+#include "audio_manager.h"
+#include "audio_manager_private.h"
+
+static struct kset *manager_kset;
+
+static LIST_HEAD(modules_list);
+static DEFINE_RWLOCK(modules_lock);
+
+static int current_module_id;
+
+/* helpers */
+static struct gb_audio_manager_module *gb_audio_manager_get_locked(int id)
+{
+       struct gb_audio_manager_module *module;
+
+       if (id < 0)
+               return NULL;
+
+       list_for_each_entry(module, &modules_list, list) {
+               if (module->id == id)
+                       return module;
+       }
+
+       return NULL;
+}
+
+/* public API */
+int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc)
+{
+       struct gb_audio_manager_module *module;
+       unsigned long flags;
+       int err;
+
+       err = gb_audio_manager_module_create(&module, manager_kset,
+                                            current_module_id++, desc);
+       if (err)
+               return err;
+
+       /* Add it to the list */
+       write_lock_irqsave(&modules_lock, flags);
+       list_add_tail(&module->list, &modules_list);
+       write_unlock_irqrestore(&modules_lock, flags);
+
+       return module->id;
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_add);
+
+int gb_audio_manager_remove(int id)
+{
+       struct gb_audio_manager_module *module;
+       unsigned long flags;
+
+       write_lock_irqsave(&modules_lock, flags);
+
+       module = gb_audio_manager_get_locked(id);
+       if (!module) {
+               write_unlock_irqrestore(&modules_lock, flags);
+               return -EINVAL;
+       }
+
+       list_del(&module->list);
+       kobject_put(&module->kobj);
+       write_unlock_irqrestore(&modules_lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_remove);
+
+void gb_audio_manager_remove_all(void)
+{
+       struct gb_audio_manager_module *module, *next;
+       int is_empty = 1;
+       unsigned long flags;
+
+       write_lock_irqsave(&modules_lock, flags);
+
+       list_for_each_entry_safe(module, next, &modules_list, list) {
+               list_del(&module->list);
+               kobject_put(&module->kobj);
+       }
+
+       is_empty = list_empty(&modules_list);
+
+       write_unlock_irqrestore(&modules_lock, flags);
+
+       if (!is_empty)
+               pr_warn("Not all nodes were deleted\n");
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all);
+
+struct gb_audio_manager_module *gb_audio_manager_get_module(int id)
+{
+       struct gb_audio_manager_module *module;
+       unsigned long flags;
+
+       read_lock_irqsave(&modules_lock, flags);
+       module = gb_audio_manager_get_locked(id);
+       kobject_get(&module->kobj);
+       read_unlock_irqrestore(&modules_lock, flags);
+       return module;
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_get_module);
+
+void gb_audio_manager_put_module(struct gb_audio_manager_module *module)
+{
+       kobject_put(&module->kobj);
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_put_module);
+
+int gb_audio_manager_dump_module(int id)
+{
+       struct gb_audio_manager_module *module;
+       unsigned long flags;
+
+       read_lock_irqsave(&modules_lock, flags);
+       module = gb_audio_manager_get_locked(id);
+       read_unlock_irqrestore(&modules_lock, flags);
+
+       if (!module)
+               return -EINVAL;
+
+       gb_audio_manager_module_dump(module);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module);
+
+void gb_audio_manager_dump_all(void)
+{
+       struct gb_audio_manager_module *module;
+       int count = 0;
+       unsigned long flags;
+
+       read_lock_irqsave(&modules_lock, flags);
+       list_for_each_entry(module, &modules_list, list) {
+               gb_audio_manager_module_dump(module);
+               count++;
+       }
+       read_unlock_irqrestore(&modules_lock, flags);
+
+       pr_info("Number of connected modules: %d\n", count);
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all);
+
+/*
+ * module init/deinit
+ */
+static int __init manager_init(void)
+{
+       manager_kset = kset_create_and_add(GB_AUDIO_MANAGER_NAME, NULL,
+                                          kernel_kobj);
+       if (!manager_kset)
+               return -ENOMEM;
+
+#ifdef GB_AUDIO_MANAGER_SYSFS
+       gb_audio_manager_sysfs_init(&manager_kset->kobj);
+#endif
+
+       return 0;
+}
+
+static void __exit manager_exit(void)
+{
+       gb_audio_manager_remove_all();
+       kset_unregister(manager_kset);
+}
+
+module_init(manager_init);
+module_exit(manager_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Svetlin Ankov <ankov_svetlin@projectara.com>");
diff --git a/drivers/staging/greybus/audio_manager.h b/drivers/staging/greybus/audio_manager.h
new file mode 100644 (file)
index 0000000..9ca7ac0
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Greybus operations
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#ifndef _GB_AUDIO_MANAGER_H_
+#define _GB_AUDIO_MANAGER_H_
+
+#include <linux/kobject.h>
+#include <linux/list.h>
+
+#define GB_AUDIO_MANAGER_NAME "gb_audio_manager"
+#define GB_AUDIO_MANAGER_MODULE_NAME_LEN 64
+#define GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "63"
+
+struct gb_audio_manager_module_descriptor {
+       char name[GB_AUDIO_MANAGER_MODULE_NAME_LEN];
+       int slot;
+       int vid;
+       int pid;
+       int cport;
+       unsigned int devices;
+};
+
+struct gb_audio_manager_module {
+       struct kobject kobj;
+       struct list_head list;
+       int id;
+       struct gb_audio_manager_module_descriptor desc;
+};
+
+/*
+ * Creates a new gb_audio_manager_module_descriptor, using the specified
+ * descriptor.
+ *
+ * Returns a negative result on error, or the id of the newly created module.
+ *
+ */
+int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc);
+
+/*
+ * Removes a connected gb_audio_manager_module_descriptor for the specified ID.
+ *
+ * Returns zero on success, or a negative value on error.
+ */
+int gb_audio_manager_remove(int id);
+
+/*
+ * Removes all connected gb_audio_modules
+ *
+ * Returns zero on success, or a negative value on error.
+ */
+void gb_audio_manager_remove_all(void);
+
+/*
+ * Retrieves a gb_audio_manager_module_descriptor for the specified id.
+ * Returns the gb_audio_manager_module_descriptor structure,
+ * or NULL if there is no module with the specified ID.
+ */
+struct gb_audio_manager_module *gb_audio_manager_get_module(int id);
+
+/*
+ * Decreases the refcount of the module, obtained by the get function.
+ * Modules are removed via gb_audio_manager_remove
+ */
+void gb_audio_manager_put_module(struct gb_audio_manager_module *module);
+
+/*
+ * Dumps the module for the specified id
+ * Return 0 on success
+ */
+int gb_audio_manager_dump_module(int id);
+
+/*
+ * Dumps all connected modules
+ */
+void gb_audio_manager_dump_all(void);
+
+#endif /* _GB_AUDIO_MANAGER_H_ */
diff --git a/drivers/staging/greybus/audio_manager_module.c b/drivers/staging/greybus/audio_manager_module.c
new file mode 100644 (file)
index 0000000..e5cffa3
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Greybus operations
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include <linux/slab.h>
+
+#include "audio_manager.h"
+#include "audio_manager_private.h"
+
+#define to_gb_audio_module_attr(x)     \
+               container_of(x, struct gb_audio_manager_module_attribute, attr)
+#define to_gb_audio_module(x)          \
+               container_of(x, struct gb_audio_manager_module, kobj)
+
+struct gb_audio_manager_module_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct gb_audio_manager_module *module,
+                       struct gb_audio_manager_module_attribute *attr,
+                       char *buf);
+       ssize_t (*store)(struct gb_audio_manager_module *module,
+                        struct gb_audio_manager_module_attribute *attr,
+                        const char *buf, size_t count);
+};
+
+static ssize_t gb_audio_module_attr_show(
+       struct kobject *kobj, struct attribute *attr, char *buf)
+{
+       struct gb_audio_manager_module_attribute *attribute;
+       struct gb_audio_manager_module *module;
+
+       attribute = to_gb_audio_module_attr(attr);
+       module = to_gb_audio_module(kobj);
+
+       if (!attribute->show)
+               return -EIO;
+
+       return attribute->show(module, attribute, buf);
+}
+
+static ssize_t gb_audio_module_attr_store(struct kobject *kobj,
+                                         struct attribute *attr,
+                                         const char *buf, size_t len)
+{
+       struct gb_audio_manager_module_attribute *attribute;
+       struct gb_audio_manager_module *module;
+
+       attribute = to_gb_audio_module_attr(attr);
+       module = to_gb_audio_module(kobj);
+
+       if (!attribute->store)
+               return -EIO;
+
+       return attribute->store(module, attribute, buf, len);
+}
+
+static const struct sysfs_ops gb_audio_module_sysfs_ops = {
+       .show = gb_audio_module_attr_show,
+       .store = gb_audio_module_attr_store,
+};
+
+static void gb_audio_module_release(struct kobject *kobj)
+{
+       struct gb_audio_manager_module *module = to_gb_audio_module(kobj);
+
+       pr_info("Destroying audio module #%d\n", module->id);
+       /* TODO -> delete from list */
+       kfree(module);
+}
+
+static ssize_t gb_audio_module_name_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s", module->desc.name);
+}
+
+static struct gb_audio_manager_module_attribute gb_audio_module_name_attribute =
+       __ATTR(name, 0664, gb_audio_module_name_show, NULL);
+
+static ssize_t gb_audio_module_slot_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d", module->desc.slot);
+}
+
+static struct gb_audio_manager_module_attribute gb_audio_module_slot_attribute =
+       __ATTR(slot, 0664, gb_audio_module_slot_show, NULL);
+
+static ssize_t gb_audio_module_vid_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d", module->desc.vid);
+}
+
+static struct gb_audio_manager_module_attribute gb_audio_module_vid_attribute =
+       __ATTR(vid, 0664, gb_audio_module_vid_show, NULL);
+
+static ssize_t gb_audio_module_pid_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d", module->desc.pid);
+}
+
+static struct gb_audio_manager_module_attribute gb_audio_module_pid_attribute =
+       __ATTR(pid, 0664, gb_audio_module_pid_show, NULL);
+
+static ssize_t gb_audio_module_cport_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d", module->desc.cport);
+}
+
+static struct gb_audio_manager_module_attribute
+                                       gb_audio_module_cport_attribute =
+       __ATTR(cport, 0664, gb_audio_module_cport_show, NULL);
+
+static ssize_t gb_audio_module_devices_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "0x%X", module->desc.devices);
+}
+
+static struct gb_audio_manager_module_attribute
+                                       gb_audio_module_devices_attribute =
+       __ATTR(devices, 0664, gb_audio_module_devices_show, NULL);
+
+static struct attribute *gb_audio_module_default_attrs[] = {
+       &gb_audio_module_name_attribute.attr,
+       &gb_audio_module_slot_attribute.attr,
+       &gb_audio_module_vid_attribute.attr,
+       &gb_audio_module_pid_attribute.attr,
+       &gb_audio_module_cport_attribute.attr,
+       &gb_audio_module_devices_attribute.attr,
+       NULL,   /* need to NULL terminate the list of attributes */
+};
+
+static struct kobj_type gb_audio_module_type = {
+       .sysfs_ops = &gb_audio_module_sysfs_ops,
+       .release = gb_audio_module_release,
+       .default_attrs = gb_audio_module_default_attrs,
+};
+
+static void send_add_uevent(struct gb_audio_manager_module *module)
+{
+       char name_string[128];
+       char slot_string[64];
+       char vid_string[64];
+       char pid_string[64];
+       char cport_string[64];
+       char devices_string[64];
+
+       char *envp[] = {
+               name_string,
+               slot_string,
+               vid_string,
+               pid_string,
+               cport_string,
+               devices_string,
+               NULL
+       };
+
+       snprintf(name_string, 128, "NAME=%s", module->desc.name);
+       snprintf(slot_string, 64, "SLOT=%d", module->desc.slot);
+       snprintf(vid_string, 64, "VID=%d", module->desc.vid);
+       snprintf(pid_string, 64, "PID=%d", module->desc.pid);
+       snprintf(cport_string, 64, "CPORT=%d", module->desc.cport);
+       snprintf(devices_string, 64, "DEVICES=0x%X", module->desc.devices);
+
+       kobject_uevent_env(&module->kobj, KOBJ_ADD, envp);
+}
+
+int gb_audio_manager_module_create(
+       struct gb_audio_manager_module **module,
+       struct kset *manager_kset,
+       int id, struct gb_audio_manager_module_descriptor *desc)
+{
+       int err;
+       struct gb_audio_manager_module *m;
+
+       m = kzalloc(sizeof(*m), GFP_ATOMIC);
+       if (!m)
+               return -ENOMEM;
+
+       /* Initialize the node */
+       INIT_LIST_HEAD(&m->list);
+
+       /* Set the module id */
+       m->id = id;
+
+       /* Copy the provided descriptor */
+       memcpy(&m->desc, desc, sizeof(*desc));
+
+       /* set the kset */
+       m->kobj.kset = manager_kset;
+
+       /*
+        * Initialize and add the kobject to the kernel.  All the default files
+        * will be created here.  As we have already specified a kset for this
+        * kobject, we don't have to set a parent for the kobject, the kobject
+        * will be placed beneath that kset automatically.
+        */
+       err = kobject_init_and_add(&m->kobj, &gb_audio_module_type, NULL, "%d",
+                                  id);
+       if (err) {
+               pr_err("failed initializing kobject for audio module #%d\n",
+                      id);
+               kobject_put(&m->kobj);
+               return err;
+       }
+
+       /*
+        * Notify the object was created
+        */
+       send_add_uevent(m);
+
+       *module = m;
+       pr_info("Created audio module #%d\n", id);
+       return 0;
+}
+
+void gb_audio_manager_module_dump(struct gb_audio_manager_module *module)
+{
+       pr_info("audio module #%d name=%s slot=%d vid=%d pid=%d cport=%d devices=0x%X\n",
+               module->id,
+               module->desc.name,
+               module->desc.slot,
+               module->desc.vid,
+               module->desc.pid,
+               module->desc.cport,
+               module->desc.devices);
+}
diff --git a/drivers/staging/greybus/audio_manager_private.h b/drivers/staging/greybus/audio_manager_private.h
new file mode 100644 (file)
index 0000000..079ce95
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Greybus operations
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#ifndef _GB_AUDIO_MANAGER_PRIVATE_H_
+#define _GB_AUDIO_MANAGER_PRIVATE_H_
+
+#include <linux/kobject.h>
+
+#include "audio_manager.h"
+
+int gb_audio_manager_module_create(
+       struct gb_audio_manager_module **module,
+       struct kset *manager_kset,
+       int id, struct gb_audio_manager_module_descriptor *desc);
+
+/* module destroyed via kobject_put */
+
+void gb_audio_manager_module_dump(struct gb_audio_manager_module *module);
+
+/* sysfs control */
+void gb_audio_manager_sysfs_init(struct kobject *kobj);
+
+#endif /* _GB_AUDIO_MANAGER_PRIVATE_H_ */
diff --git a/drivers/staging/greybus/audio_manager_sysfs.c b/drivers/staging/greybus/audio_manager_sysfs.c
new file mode 100644 (file)
index 0000000..c713f5f
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Greybus operations
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include <linux/string.h>
+#include <linux/sysfs.h>
+
+#include "audio_manager.h"
+#include "audio_manager_private.h"
+
+static ssize_t manager_sysfs_add_store(
+       struct kobject *kobj, struct kobj_attribute *attr,
+       const char *buf, size_t count)
+{
+       struct gb_audio_manager_module_descriptor desc = { {0} };
+
+       int num = sscanf(buf,
+                       "name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "s "
+                       "slot=%d vid=%d pid=%d cport=%d devices=0x%X",
+                       desc.name, &desc.slot, &desc.vid, &desc.pid,
+                       &desc.cport, &desc.devices);
+
+       if (num != 6)
+               return -EINVAL;
+
+       num = gb_audio_manager_add(&desc);
+       if (num < 0)
+               return -EINVAL;
+
+       return count;
+}
+
+static struct kobj_attribute manager_add_attribute =
+       __ATTR(add, 0664, NULL, manager_sysfs_add_store);
+
+static ssize_t manager_sysfs_remove_store(
+       struct kobject *kobj, struct kobj_attribute *attr,
+       const char *buf, size_t count)
+{
+       int id;
+
+       int num = sscanf(buf, "%d", &id);
+
+       if (num != 1)
+               return -EINVAL;
+
+       num = gb_audio_manager_remove(id);
+       if (num)
+               return num;
+
+       return count;
+}
+
+static struct kobj_attribute manager_remove_attribute =
+       __ATTR(remove, 0664, NULL, manager_sysfs_remove_store);
+
+static ssize_t manager_sysfs_dump_store(
+       struct kobject *kobj, struct kobj_attribute *attr,
+       const char *buf, size_t count)
+{
+       int id;
+
+       int num = sscanf(buf, "%d", &id);
+
+       if (num == 1) {
+               num = gb_audio_manager_dump_module(id);
+               if (num)
+                       return num;
+       } else if (!strncmp("all", buf, 3))
+               gb_audio_manager_dump_all();
+       else
+               return -EINVAL;
+
+       return count;
+}
+
+static struct kobj_attribute manager_dump_attribute =
+       __ATTR(dump, 0664, NULL, manager_sysfs_dump_store);
+
+static void manager_sysfs_init_attribute(
+               struct kobject *kobj, struct kobj_attribute *kattr)
+{
+       int err;
+
+       err = sysfs_create_file(kobj, &kattr->attr);
+       if (err) {
+               pr_warn("creating the sysfs entry for %s failed: %d\n",
+                       kattr->attr.name, err);
+       }
+}
+
+void gb_audio_manager_sysfs_init(struct kobject *kobj)
+{
+       manager_sysfs_init_attribute(kobj, &manager_add_attribute);
+       manager_sysfs_init_attribute(kobj, &manager_remove_attribute);
+       manager_sysfs_init_attribute(kobj, &manager_dump_attribute);
+}