1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Greybus operations
4 *
5 * Copyright 2015-2016 Google Inc.
6 */
7
8 #include <linux/string.h>
9 #include <linux/sysfs.h>
10 #include <linux/module.h>
11 #include <linux/init.h>
12 #include <linux/spinlock.h>
13 #include <linux/idr.h>
14
15 #include "audio_manager.h"
16 #include "audio_manager_private.h"
17
18 static struct kset *manager_kset;
19
20 static LIST_HEAD(modules_list);
21 static DECLARE_RWSEM(modules_rwsem);
22 static DEFINE_IDA(module_id);
23
24 /* helpers */
gb_audio_manager_get_locked(int id)25 static struct gb_audio_manager_module *gb_audio_manager_get_locked(int id)
26 {
27 struct gb_audio_manager_module *module;
28
29 if (id < 0)
30 return NULL;
31
32 list_for_each_entry(module, &modules_list, list) {
33 if (module->id == id)
34 return module;
35 }
36
37 return NULL;
38 }
39
40 /* public API */
gb_audio_manager_add(struct gb_audio_manager_module_descriptor * desc)41 int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc)
42 {
43 struct gb_audio_manager_module *module;
44 int id;
45 int err;
46
47 id = ida_alloc(&module_id, GFP_KERNEL);
48 if (id < 0)
49 return id;
50
51 err = gb_audio_manager_module_create(&module, manager_kset,
52 id, desc);
53 if (err) {
54 ida_free(&module_id, id);
55 return err;
56 }
57
58 /* Add it to the list */
59 down_write(&modules_rwsem);
60 list_add_tail(&module->list, &modules_list);
61 up_write(&modules_rwsem);
62
63 return module->id;
64 }
65 EXPORT_SYMBOL_GPL(gb_audio_manager_add);
66
gb_audio_manager_remove(int id)67 int gb_audio_manager_remove(int id)
68 {
69 struct gb_audio_manager_module *module;
70
71 down_write(&modules_rwsem);
72
73 module = gb_audio_manager_get_locked(id);
74 if (!module) {
75 up_write(&modules_rwsem);
76 return -EINVAL;
77 }
78 list_del(&module->list);
79 kobject_put(&module->kobj);
80 up_write(&modules_rwsem);
81 ida_free(&module_id, id);
82 return 0;
83 }
84 EXPORT_SYMBOL_GPL(gb_audio_manager_remove);
85
gb_audio_manager_remove_all(void)86 void gb_audio_manager_remove_all(void)
87 {
88 struct gb_audio_manager_module *module, *next;
89 int is_empty;
90
91 down_write(&modules_rwsem);
92
93 list_for_each_entry_safe(module, next, &modules_list, list) {
94 list_del(&module->list);
95 ida_free(&module_id, module->id);
96 kobject_put(&module->kobj);
97 }
98
99 is_empty = list_empty(&modules_list);
100
101 up_write(&modules_rwsem);
102
103 if (!is_empty)
104 pr_warn("Not all nodes were deleted\n");
105 }
106 EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all);
107
gb_audio_manager_put_module(struct gb_audio_manager_module * module)108 void gb_audio_manager_put_module(struct gb_audio_manager_module *module)
109 {
110 kobject_put(&module->kobj);
111 }
112 EXPORT_SYMBOL_GPL(gb_audio_manager_put_module);
113
gb_audio_manager_dump_module(int id)114 int gb_audio_manager_dump_module(int id)
115 {
116 struct gb_audio_manager_module *module;
117
118 down_read(&modules_rwsem);
119 module = gb_audio_manager_get_locked(id);
120 up_read(&modules_rwsem);
121
122 if (!module)
123 return -EINVAL;
124
125 gb_audio_manager_module_dump(module);
126 return 0;
127 }
128 EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module);
129
gb_audio_manager_dump_all(void)130 void gb_audio_manager_dump_all(void)
131 {
132 struct gb_audio_manager_module *module;
133 int count = 0;
134
135 down_read(&modules_rwsem);
136 list_for_each_entry(module, &modules_list, list) {
137 gb_audio_manager_module_dump(module);
138 count++;
139 }
140 up_read(&modules_rwsem);
141
142 pr_info("Number of connected modules: %d\n", count);
143 }
144 EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all);
145
146 /*
147 * module init/deinit
148 */
manager_init(void)149 static int __init manager_init(void)
150 {
151 manager_kset = kset_create_and_add(GB_AUDIO_MANAGER_NAME, NULL,
152 kernel_kobj);
153 if (!manager_kset)
154 return -ENOMEM;
155
156 #ifdef GB_AUDIO_MANAGER_SYSFS
157 gb_audio_manager_sysfs_init(&manager_kset->kobj);
158 #endif
159
160 return 0;
161 }
162
manager_exit(void)163 static void __exit manager_exit(void)
164 {
165 gb_audio_manager_remove_all();
166 kset_unregister(manager_kset);
167 ida_destroy(&module_id);
168 }
169
170 module_init(manager_init);
171 module_exit(manager_exit);
172
173 MODULE_DESCRIPTION("Greybus audio operations manager");
174 MODULE_LICENSE("GPL");
175 MODULE_AUTHOR("Svetlin Ankov <ankov_svetlin@projectara.com>");
176