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