blob: 05af441be1624ff34098b5e097be2b2382e52f70 [file] [log] [blame]
Svetlin Ankov8db00732016-01-13 14:07:48 -07001/*
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
15#include "audio_manager.h"
16#include "audio_manager_private.h"
17
18static struct kset *manager_kset;
19
20static LIST_HEAD(modules_list);
21static DEFINE_RWLOCK(modules_lock);
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -080022static DEFINE_IDA(module_id);
Svetlin Ankov8db00732016-01-13 14:07:48 -070023
24/* helpers */
25static 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 */
41int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc)
42{
43 struct gb_audio_manager_module *module;
44 unsigned long flags;
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -080045 int id;
Svetlin Ankov8db00732016-01-13 14:07:48 -070046 int err;
47
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -080048 id = ida_simple_get(&module_id, 0, 0, GFP_KERNEL);
Svetlin Ankov8db00732016-01-13 14:07:48 -070049 err = gb_audio_manager_module_create(&module, manager_kset,
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -080050 id, desc);
51 if (err) {
52 ida_simple_remove(&module_id, id);
Svetlin Ankov8db00732016-01-13 14:07:48 -070053 return err;
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -080054 }
Svetlin Ankov8db00732016-01-13 14:07:48 -070055
56 /* Add it to the list */
57 write_lock_irqsave(&modules_lock, flags);
58 list_add_tail(&module->list, &modules_list);
59 write_unlock_irqrestore(&modules_lock, flags);
60
61 return module->id;
62}
63EXPORT_SYMBOL_GPL(gb_audio_manager_add);
64
65int gb_audio_manager_remove(int id)
66{
67 struct gb_audio_manager_module *module;
68 unsigned long flags;
69
70 write_lock_irqsave(&modules_lock, flags);
71
72 module = gb_audio_manager_get_locked(id);
73 if (!module) {
74 write_unlock_irqrestore(&modules_lock, flags);
75 return -EINVAL;
76 }
77
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -080078 ida_simple_remove(&module_id, module->id);
Svetlin Ankov8db00732016-01-13 14:07:48 -070079 list_del(&module->list);
80 kobject_put(&module->kobj);
81 write_unlock_irqrestore(&modules_lock, flags);
82 return 0;
83}
84EXPORT_SYMBOL_GPL(gb_audio_manager_remove);
85
86void gb_audio_manager_remove_all(void)
87{
88 struct gb_audio_manager_module *module, *next;
89 int is_empty = 1;
90 unsigned long flags;
91
92 write_lock_irqsave(&modules_lock, flags);
93
94 list_for_each_entry_safe(module, next, &modules_list, list) {
95 list_del(&module->list);
96 kobject_put(&module->kobj);
97 }
98
99 is_empty = list_empty(&modules_list);
100
101 write_unlock_irqrestore(&modules_lock, flags);
102
103 if (!is_empty)
104 pr_warn("Not all nodes were deleted\n");
105}
106EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all);
107
108struct gb_audio_manager_module *gb_audio_manager_get_module(int id)
109{
110 struct gb_audio_manager_module *module;
111 unsigned long flags;
112
113 read_lock_irqsave(&modules_lock, flags);
114 module = gb_audio_manager_get_locked(id);
115 kobject_get(&module->kobj);
116 read_unlock_irqrestore(&modules_lock, flags);
117 return module;
118}
119EXPORT_SYMBOL_GPL(gb_audio_manager_get_module);
120
121void gb_audio_manager_put_module(struct gb_audio_manager_module *module)
122{
123 kobject_put(&module->kobj);
124}
125EXPORT_SYMBOL_GPL(gb_audio_manager_put_module);
126
127int gb_audio_manager_dump_module(int id)
128{
129 struct gb_audio_manager_module *module;
130 unsigned long flags;
131
132 read_lock_irqsave(&modules_lock, flags);
133 module = gb_audio_manager_get_locked(id);
134 read_unlock_irqrestore(&modules_lock, flags);
135
136 if (!module)
137 return -EINVAL;
138
139 gb_audio_manager_module_dump(module);
140 return 0;
141}
142EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module);
143
144void gb_audio_manager_dump_all(void)
145{
146 struct gb_audio_manager_module *module;
147 int count = 0;
148 unsigned long flags;
149
150 read_lock_irqsave(&modules_lock, flags);
151 list_for_each_entry(module, &modules_list, list) {
152 gb_audio_manager_module_dump(module);
153 count++;
154 }
155 read_unlock_irqrestore(&modules_lock, flags);
156
157 pr_info("Number of connected modules: %d\n", count);
158}
159EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all);
160
161/*
162 * module init/deinit
163 */
164static int __init manager_init(void)
165{
166 manager_kset = kset_create_and_add(GB_AUDIO_MANAGER_NAME, NULL,
167 kernel_kobj);
168 if (!manager_kset)
169 return -ENOMEM;
170
171#ifdef GB_AUDIO_MANAGER_SYSFS
172 gb_audio_manager_sysfs_init(&manager_kset->kobj);
173#endif
174
175 return 0;
176}
177
178static void __exit manager_exit(void)
179{
180 gb_audio_manager_remove_all();
181 kset_unregister(manager_kset);
Greg Kroah-Hartman3c5de592016-01-25 16:52:17 -0800182 ida_destroy(&module_id);
Svetlin Ankov8db00732016-01-13 14:07:48 -0700183}
184
185module_init(manager_init);
186module_exit(manager_exit);
187
188MODULE_LICENSE("GPL");
189MODULE_AUTHOR("Svetlin Ankov <ankov_svetlin@projectara.com>");