diff options
Diffstat (limited to 'drivers/staging/greybus/audio_manager.c')
-rw-r--r-- | drivers/staging/greybus/audio_manager.c | 184 |
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>"); |