blob: aa6508b44fab27f805bcd629c8815a9d0b8bf413 [file] [log] [blame]
Svetlin Ankov8db00732016-01-13 14:07:48 -07001/*
2 * Greybus operations
3 *
4 * Copyright 2015-2016 Google Inc.
5 *
6 * Released under the GPLv2 only.
7 */
8
9#include <linux/string.h>
10#include <linux/sysfs.h>
11#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/rwlock.h>
Bartosz Golaszewskiec413562016-02-03 12:53:40 +010014#include <linux/idr.h>
Svetlin Ankov8db00732016-01-13 14:07:48 -070015
16#include "audio_manager.h"
17#include "audio_manager_private.h"
18
19static struct kset *manager_kset;
20
21static LIST_HEAD(modules_list);
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +053022static DECLARE_RWSEM(modules_rwsem);
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -080023static DEFINE_IDA(module_id);
Svetlin Ankov8db00732016-01-13 14:07:48 -070024
25/* helpers */
26static struct gb_audio_manager_module *gb_audio_manager_get_locked(int id)
27{
28 struct gb_audio_manager_module *module;
29
30 if (id < 0)
31 return NULL;
32
33 list_for_each_entry(module, &modules_list, list) {
34 if (module->id == id)
35 return module;
36 }
37
38 return NULL;
39}
40
41/* public API */
42int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc)
43{
44 struct gb_audio_manager_module *module;
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -080045 int id;
Svetlin Ankov8db00732016-01-13 14:07:48 -070046 int err;
47
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -080048 id = ida_simple_get(&module_id, 0, 0, GFP_KERNEL);
Svetlin Ankov8db00732016-01-13 14:07:48 -070049 err = gb_audio_manager_module_create(&module, manager_kset,
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -080050 id, desc);
51 if (err) {
52 ida_simple_remove(&module_id, id);
Svetlin Ankov8db00732016-01-13 14:07:48 -070053 return err;
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -080054 }
Svetlin Ankov8db00732016-01-13 14:07:48 -070055
56 /* Add it to the list */
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +053057 down_write(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -070058 list_add_tail(&module->list, &modules_list);
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +053059 up_write(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -070060
61 return module->id;
62}
63EXPORT_SYMBOL_GPL(gb_audio_manager_add);
64
65int gb_audio_manager_remove(int id)
66{
67 struct gb_audio_manager_module *module;
Svetlin Ankov8db00732016-01-13 14:07:48 -070068
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +053069 down_write(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -070070
71 module = gb_audio_manager_get_locked(id);
72 if (!module) {
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +053073 up_write(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -070074 return -EINVAL;
75 }
Svetlin Ankov8db00732016-01-13 14:07:48 -070076 list_del(&module->list);
77 kobject_put(&module->kobj);
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +053078 up_write(&modules_rwsem);
Vaibhav Agarwalac001542016-05-17 22:29:08 +053079 ida_simple_remove(&module_id, id);
Svetlin Ankov8db00732016-01-13 14:07:48 -070080 return 0;
81}
82EXPORT_SYMBOL_GPL(gb_audio_manager_remove);
83
84void gb_audio_manager_remove_all(void)
85{
86 struct gb_audio_manager_module *module, *next;
87 int is_empty = 1;
Svetlin Ankov8db00732016-01-13 14:07:48 -070088
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +053089 down_write(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -070090
91 list_for_each_entry_safe(module, next, &modules_list, list) {
92 list_del(&module->list);
93 kobject_put(&module->kobj);
Dinko Mironovc77f85b2016-05-05 19:58:22 +030094 ida_simple_remove(&module_id, module->id);
Svetlin Ankov8db00732016-01-13 14:07:48 -070095 }
96
97 is_empty = list_empty(&modules_list);
98
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +053099 up_write(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -0700100
101 if (!is_empty)
102 pr_warn("Not all nodes were deleted\n");
103}
104EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all);
105
106struct gb_audio_manager_module *gb_audio_manager_get_module(int id)
107{
108 struct gb_audio_manager_module *module;
Svetlin Ankov8db00732016-01-13 14:07:48 -0700109
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +0530110 down_read(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -0700111 module = gb_audio_manager_get_locked(id);
112 kobject_get(&module->kobj);
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +0530113 up_read(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -0700114 return module;
115}
116EXPORT_SYMBOL_GPL(gb_audio_manager_get_module);
117
118void gb_audio_manager_put_module(struct gb_audio_manager_module *module)
119{
120 kobject_put(&module->kobj);
121}
122EXPORT_SYMBOL_GPL(gb_audio_manager_put_module);
123
124int gb_audio_manager_dump_module(int id)
125{
126 struct gb_audio_manager_module *module;
Svetlin Ankov8db00732016-01-13 14:07:48 -0700127
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +0530128 down_read(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -0700129 module = gb_audio_manager_get_locked(id);
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +0530130 up_read(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -0700131
132 if (!module)
133 return -EINVAL;
134
135 gb_audio_manager_module_dump(module);
136 return 0;
137}
138EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module);
139
140void gb_audio_manager_dump_all(void)
141{
142 struct gb_audio_manager_module *module;
143 int count = 0;
Svetlin Ankov8db00732016-01-13 14:07:48 -0700144
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +0530145 down_read(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -0700146 list_for_each_entry(module, &modules_list, list) {
147 gb_audio_manager_module_dump(module);
148 count++;
149 }
Vaibhav Agarwal84e0e382016-05-04 16:29:19 +0530150 up_read(&modules_rwsem);
Svetlin Ankov8db00732016-01-13 14:07:48 -0700151
152 pr_info("Number of connected modules: %d\n", count);
153}
154EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all);
155
156/*
157 * module init/deinit
158 */
159static int __init manager_init(void)
160{
161 manager_kset = kset_create_and_add(GB_AUDIO_MANAGER_NAME, NULL,
162 kernel_kobj);
163 if (!manager_kset)
164 return -ENOMEM;
165
166#ifdef GB_AUDIO_MANAGER_SYSFS
167 gb_audio_manager_sysfs_init(&manager_kset->kobj);
168#endif
169
170 return 0;
171}
172
173static void __exit manager_exit(void)
174{
175 gb_audio_manager_remove_all();
176 kset_unregister(manager_kset);
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -0800177 ida_destroy(&module_id);
Svetlin Ankov8db00732016-01-13 14:07:48 -0700178}
179
180module_init(manager_init);
181module_exit(manager_exit);
182
183MODULE_LICENSE("GPL");
184MODULE_AUTHOR("Svetlin Ankov <ankov_svetlin@projectara.com>");