diff options
author | Svetlin Ankov <ankov_svetlin@projectara.com> | 2016-01-14 00:07:48 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@google.com> | 2016-01-14 03:12:56 +0300 |
commit | 8db00736d365b75d6af5dfd4a2673a1453fff4b7 (patch) | |
tree | dfa330dbcce83e88b87ecfc56c5a58799c1d43cf /drivers/staging/greybus/audio_manager.c | |
parent | 4dbf5056405ad3c0ead370f0f3254c17b81b1e04 (diff) | |
download | linux-8db00736d365b75d6af5dfd4a2673a1453fff4b7.tar.xz |
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.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>"); |