Merge branch 'next-seccomp' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[linux-2.6-microblaze.git] / drivers / staging / greybus / audio_manager.c
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 */
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 */
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
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
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
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
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
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
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  */
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
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>");