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_simple_get(&module_id, 0, 0, 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_simple_remove(&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_simple_remove(&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_simple_remove(&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_get_module(int id)108 struct gb_audio_manager_module *gb_audio_manager_get_module(int id)
109 {
110 	struct gb_audio_manager_module *module;
111 
112 	down_read(&modules_rwsem);
113 	module = gb_audio_manager_get_locked(id);
114 	kobject_get(&module->kobj);
115 	up_read(&modules_rwsem);
116 	return module;
117 }
118 EXPORT_SYMBOL_GPL(gb_audio_manager_get_module);
119 
gb_audio_manager_put_module(struct gb_audio_manager_module * module)120 void gb_audio_manager_put_module(struct gb_audio_manager_module *module)
121 {
122 	kobject_put(&module->kobj);
123 }
124 EXPORT_SYMBOL_GPL(gb_audio_manager_put_module);
125 
gb_audio_manager_dump_module(int id)126 int gb_audio_manager_dump_module(int id)
127 {
128 	struct gb_audio_manager_module *module;
129 
130 	down_read(&modules_rwsem);
131 	module = gb_audio_manager_get_locked(id);
132 	up_read(&modules_rwsem);
133 
134 	if (!module)
135 		return -EINVAL;
136 
137 	gb_audio_manager_module_dump(module);
138 	return 0;
139 }
140 EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module);
141 
gb_audio_manager_dump_all(void)142 void gb_audio_manager_dump_all(void)
143 {
144 	struct gb_audio_manager_module *module;
145 	int count = 0;
146 
147 	down_read(&modules_rwsem);
148 	list_for_each_entry(module, &modules_list, list) {
149 		gb_audio_manager_module_dump(module);
150 		count++;
151 	}
152 	up_read(&modules_rwsem);
153 
154 	pr_info("Number of connected modules: %d\n", count);
155 }
156 EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all);
157 
158 /*
159  * module init/deinit
160  */
manager_init(void)161 static int __init manager_init(void)
162 {
163 	manager_kset = kset_create_and_add(GB_AUDIO_MANAGER_NAME, NULL,
164 					   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 
manager_exit(void)175 static void __exit manager_exit(void)
176 {
177 	gb_audio_manager_remove_all();
178 	kset_unregister(manager_kset);
179 	ida_destroy(&module_id);
180 }
181 
182 module_init(manager_init);
183 module_exit(manager_exit);
184 
185 MODULE_LICENSE("GPL");
186 MODULE_AUTHOR("Svetlin Ankov <ankov_svetlin@projectara.com>");
187