1 /*
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>
14 #include <linux/idr.h>
15 
16 #include "audio_manager.h"
17 #include "audio_manager_private.h"
18 
19 static struct kset *manager_kset;
20 
21 static LIST_HEAD(modules_list);
22 static DECLARE_RWSEM(modules_rwsem);
23 static DEFINE_IDA(module_id);
24 
25 /* helpers */
26 static 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 */
42 int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc)
43 {
44 	struct gb_audio_manager_module *module;
45 	int id;
46 	int err;
47 
48 	id = ida_simple_get(&module_id, 0, 0, GFP_KERNEL);
49 	err = gb_audio_manager_module_create(&module, manager_kset,
50 					     id, desc);
51 	if (err) {
52 		ida_simple_remove(&module_id, id);
53 		return err;
54 	}
55 
56 	/* Add it to the list */
57 	down_write(&modules_rwsem);
58 	list_add_tail(&module->list, &modules_list);
59 	up_write(&modules_rwsem);
60 
61 	return module->id;
62 }
63 EXPORT_SYMBOL_GPL(gb_audio_manager_add);
64 
65 int gb_audio_manager_remove(int id)
66 {
67 	struct gb_audio_manager_module *module;
68 
69 	down_write(&modules_rwsem);
70 
71 	module = gb_audio_manager_get_locked(id);
72 	if (!module) {
73 		up_write(&modules_rwsem);
74 		return -EINVAL;
75 	}
76 	list_del(&module->list);
77 	kobject_put(&module->kobj);
78 	up_write(&modules_rwsem);
79 	ida_simple_remove(&module_id, id);
80 	return 0;
81 }
82 EXPORT_SYMBOL_GPL(gb_audio_manager_remove);
83 
84 void gb_audio_manager_remove_all(void)
85 {
86 	struct gb_audio_manager_module *module, *next;
87 	int is_empty = 1;
88 
89 	down_write(&modules_rwsem);
90 
91 	list_for_each_entry_safe(module, next, &modules_list, list) {
92 		list_del(&module->list);
93 		kobject_put(&module->kobj);
94 		ida_simple_remove(&module_id, module->id);
95 	}
96 
97 	is_empty = list_empty(&modules_list);
98 
99 	up_write(&modules_rwsem);
100 
101 	if (!is_empty)
102 		pr_warn("Not all nodes were deleted\n");
103 }
104 EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all);
105 
106 struct gb_audio_manager_module *gb_audio_manager_get_module(int id)
107 {
108 	struct gb_audio_manager_module *module;
109 
110 	down_read(&modules_rwsem);
111 	module = gb_audio_manager_get_locked(id);
112 	kobject_get(&module->kobj);
113 	up_read(&modules_rwsem);
114 	return module;
115 }
116 EXPORT_SYMBOL_GPL(gb_audio_manager_get_module);
117 
118 void gb_audio_manager_put_module(struct gb_audio_manager_module *module)
119 {
120 	kobject_put(&module->kobj);
121 }
122 EXPORT_SYMBOL_GPL(gb_audio_manager_put_module);
123 
124 int gb_audio_manager_dump_module(int id)
125 {
126 	struct gb_audio_manager_module *module;
127 
128 	down_read(&modules_rwsem);
129 	module = gb_audio_manager_get_locked(id);
130 	up_read(&modules_rwsem);
131 
132 	if (!module)
133 		return -EINVAL;
134 
135 	gb_audio_manager_module_dump(module);
136 	return 0;
137 }
138 EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module);
139 
140 void gb_audio_manager_dump_all(void)
141 {
142 	struct gb_audio_manager_module *module;
143 	int count = 0;
144 
145 	down_read(&modules_rwsem);
146 	list_for_each_entry(module, &modules_list, list) {
147 		gb_audio_manager_module_dump(module);
148 		count++;
149 	}
150 	up_read(&modules_rwsem);
151 
152 	pr_info("Number of connected modules: %d\n", count);
153 }
154 EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all);
155 
156 /*
157  * module init/deinit
158  */
159 static 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 
173 static void __exit manager_exit(void)
174 {
175 	gb_audio_manager_remove_all();
176 	kset_unregister(manager_kset);
177 	ida_destroy(&module_id);
178 }
179 
180 module_init(manager_init);
181 module_exit(manager_exit);
182 
183 MODULE_LICENSE("GPL");
184 MODULE_AUTHOR("Svetlin Ankov <ankov_svetlin@projectara.com>");
185