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 = 1; 90 91 down_write(&modules_rwsem); 92 93 list_for_each_entry_safe(module, next, &modules_list, list) { 94 list_del(&module->list); 95 kobject_put(&module->kobj); 96 ida_simple_remove(&module_id, module->id); 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>"); 187