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
18static struct kset *manager_kset;
19
20static LIST_HEAD(modules_list);
21static DECLARE_RWSEM(modules_rwsem);
22static DEFINE_IDA(module_id);
23
24/* helpers */
25static 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 */
41int 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(ida: &module_id, GFP_KERNEL);
48 if (id < 0)
49 return id;
50
51 err = gb_audio_manager_module_create(module: &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(sem: &modules_rwsem);
60 list_add_tail(new: &module->list, head: &modules_list);
61 up_write(sem: &modules_rwsem);
62
63 return module->id;
64}
65EXPORT_SYMBOL_GPL(gb_audio_manager_add);
66
67int gb_audio_manager_remove(int id)
68{
69 struct gb_audio_manager_module *module;
70
71 down_write(sem: &modules_rwsem);
72
73 module = gb_audio_manager_get_locked(id);
74 if (!module) {
75 up_write(sem: &modules_rwsem);
76 return -EINVAL;
77 }
78 list_del(entry: &module->list);
79 kobject_put(kobj: &module->kobj);
80 up_write(sem: &modules_rwsem);
81 ida_free(&module_id, id);
82 return 0;
83}
84EXPORT_SYMBOL_GPL(gb_audio_manager_remove);
85
86void gb_audio_manager_remove_all(void)
87{
88 struct gb_audio_manager_module *module, *next;
89 int is_empty;
90
91 down_write(sem: &modules_rwsem);
92
93 list_for_each_entry_safe(module, next, &modules_list, list) {
94 list_del(entry: &module->list);
95 ida_free(&module_id, id: module->id);
96 kobject_put(kobj: &module->kobj);
97 }
98
99 is_empty = list_empty(head: &modules_list);
100
101 up_write(sem: &modules_rwsem);
102
103 if (!is_empty)
104 pr_warn("Not all nodes were deleted\n");
105}
106EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all);
107
108struct gb_audio_manager_module *gb_audio_manager_get_module(int id)
109{
110 struct gb_audio_manager_module *module;
111
112 down_read(sem: &modules_rwsem);
113 module = gb_audio_manager_get_locked(id);
114 kobject_get(kobj: &module->kobj);
115 up_read(sem: &modules_rwsem);
116 return module;
117}
118EXPORT_SYMBOL_GPL(gb_audio_manager_get_module);
119
120void gb_audio_manager_put_module(struct gb_audio_manager_module *module)
121{
122 kobject_put(kobj: &module->kobj);
123}
124EXPORT_SYMBOL_GPL(gb_audio_manager_put_module);
125
126int gb_audio_manager_dump_module(int id)
127{
128 struct gb_audio_manager_module *module;
129
130 down_read(sem: &modules_rwsem);
131 module = gb_audio_manager_get_locked(id);
132 up_read(sem: &modules_rwsem);
133
134 if (!module)
135 return -EINVAL;
136
137 gb_audio_manager_module_dump(module);
138 return 0;
139}
140EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module);
141
142void gb_audio_manager_dump_all(void)
143{
144 struct gb_audio_manager_module *module;
145 int count = 0;
146
147 down_read(sem: &modules_rwsem);
148 list_for_each_entry(module, &modules_list, list) {
149 gb_audio_manager_module_dump(module);
150 count++;
151 }
152 up_read(sem: &modules_rwsem);
153
154 pr_info("Number of connected modules: %d\n", count);
155}
156EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all);
157
158/*
159 * module init/deinit
160 */
161static int __init manager_init(void)
162{
163 manager_kset = kset_create_and_add(GB_AUDIO_MANAGER_NAME, NULL,
164 parent_kobj: kernel_kobj);
165 if (!manager_kset)
166 return -ENOMEM;
167
168#ifdef GB_AUDIO_MANAGER_SYSFS
169 gb_audio_manager_sysfs_init(&manager_kset->kobj);
170#endif
171
172 return 0;
173}
174
175static void __exit manager_exit(void)
176{
177 gb_audio_manager_remove_all();
178 kset_unregister(kset: manager_kset);
179 ida_destroy(ida: &module_id);
180}
181
182module_init(manager_init);
183module_exit(manager_exit);
184
185MODULE_LICENSE("GPL");
186MODULE_AUTHOR("Svetlin Ankov <ankov_svetlin@projectara.com>");
187

source code of linux/drivers/staging/greybus/audio_manager.c