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