1 /* 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 #include <linux/idr.h> 15 16 #include "audio_manager.h" 17 #include "audio_manager_private.h" 18 19 static struct kset *manager_kset; 20 21 static LIST_HEAD(modules_list); 22 static DECLARE_RWSEM(modules_rwsem); 23 static DEFINE_IDA(module_id); 24 25 /* helpers */ 26 static 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 */ 42 int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc) 43 { 44 struct gb_audio_manager_module *module; 45 int id; 46 int err; 47 48 id = ida_simple_get(&module_id, 0, 0, GFP_KERNEL); 49 err = gb_audio_manager_module_create(&module, manager_kset, 50 id, desc); 51 if (err) { 52 ida_simple_remove(&module_id, id); 53 return err; 54 } 55 56 /* Add it to the list */ 57 down_write(&modules_rwsem); 58 list_add_tail(&module->list, &modules_list); 59 up_write(&modules_rwsem); 60 61 return module->id; 62 } 63 EXPORT_SYMBOL_GPL(gb_audio_manager_add); 64 65 int gb_audio_manager_remove(int id) 66 { 67 struct gb_audio_manager_module *module; 68 69 down_write(&modules_rwsem); 70 71 module = gb_audio_manager_get_locked(id); 72 if (!module) { 73 up_write(&modules_rwsem); 74 return -EINVAL; 75 } 76 list_del(&module->list); 77 kobject_put(&module->kobj); 78 up_write(&modules_rwsem); 79 ida_simple_remove(&module_id, id); 80 return 0; 81 } 82 EXPORT_SYMBOL_GPL(gb_audio_manager_remove); 83 84 void gb_audio_manager_remove_all(void) 85 { 86 struct gb_audio_manager_module *module, *next; 87 int is_empty = 1; 88 89 down_write(&modules_rwsem); 90 91 list_for_each_entry_safe(module, next, &modules_list, list) { 92 list_del(&module->list); 93 kobject_put(&module->kobj); 94 ida_simple_remove(&module_id, module->id); 95 } 96 97 is_empty = list_empty(&modules_list); 98 99 up_write(&modules_rwsem); 100 101 if (!is_empty) 102 pr_warn("Not all nodes were deleted\n"); 103 } 104 EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all); 105 106 struct gb_audio_manager_module *gb_audio_manager_get_module(int id) 107 { 108 struct gb_audio_manager_module *module; 109 110 down_read(&modules_rwsem); 111 module = gb_audio_manager_get_locked(id); 112 kobject_get(&module->kobj); 113 up_read(&modules_rwsem); 114 return module; 115 } 116 EXPORT_SYMBOL_GPL(gb_audio_manager_get_module); 117 118 void gb_audio_manager_put_module(struct gb_audio_manager_module *module) 119 { 120 kobject_put(&module->kobj); 121 } 122 EXPORT_SYMBOL_GPL(gb_audio_manager_put_module); 123 124 int gb_audio_manager_dump_module(int id) 125 { 126 struct gb_audio_manager_module *module; 127 128 down_read(&modules_rwsem); 129 module = gb_audio_manager_get_locked(id); 130 up_read(&modules_rwsem); 131 132 if (!module) 133 return -EINVAL; 134 135 gb_audio_manager_module_dump(module); 136 return 0; 137 } 138 EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module); 139 140 void gb_audio_manager_dump_all(void) 141 { 142 struct gb_audio_manager_module *module; 143 int count = 0; 144 145 down_read(&modules_rwsem); 146 list_for_each_entry(module, &modules_list, list) { 147 gb_audio_manager_module_dump(module); 148 count++; 149 } 150 up_read(&modules_rwsem); 151 152 pr_info("Number of connected modules: %d\n", count); 153 } 154 EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all); 155 156 /* 157 * module init/deinit 158 */ 159 static int __init manager_init(void) 160 { 161 manager_kset = kset_create_and_add(GB_AUDIO_MANAGER_NAME, NULL, 162 kernel_kobj); 163 if (!manager_kset) 164 return -ENOMEM; 165 166 #ifdef GB_AUDIO_MANAGER_SYSFS 167 gb_audio_manager_sysfs_init(&manager_kset->kobj); 168 #endif 169 170 return 0; 171 } 172 173 static void __exit manager_exit(void) 174 { 175 gb_audio_manager_remove_all(); 176 kset_unregister(manager_kset); 177 ida_destroy(&module_id); 178 } 179 180 module_init(manager_init); 181 module_exit(manager_exit); 182 183 MODULE_LICENSE("GPL"); 184 MODULE_AUTHOR("Svetlin Ankov <ankov_svetlin@projectara.com>"); 185