summaryrefslogtreecommitdiff
path: root/drivers/staging/greybus/audio_manager.c
diff options
context:
space:
mode:
authorSvetlin Ankov <ankov_svetlin@projectara.com>2016-01-13 14:07:48 -0700
committerGreg Kroah-Hartman <gregkh@google.com>2016-01-13 16:12:56 -0800
commit8db00736d365b75d6af5dfd4a2673a1453fff4b7 (patch)
treedfa330dbcce83e88b87ecfc56c5a58799c1d43cf /drivers/staging/greybus/audio_manager.c
parent4dbf5056405ad3c0ead370f0f3254c17b81b1e04 (diff)
greybus: audio: Add Audio Manager
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>
Diffstat (limited to 'drivers/staging/greybus/audio_manager.c')
-rw-r--r--drivers/staging/greybus/audio_manager.c184
1 files changed, 184 insertions, 0 deletions
diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c
new file mode 100644
index 000000000000..117676314daf
--- /dev/null
+++ b/drivers/staging/greybus/audio_manager.c
@@ -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>");