Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[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         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
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
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
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
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
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
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  */
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
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>");