15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 20062f6e5SSebastian Andrzej Siewior #include <linux/kernel.h> 30062f6e5SSebastian Andrzej Siewior #include <linux/slab.h> 40062f6e5SSebastian Andrzej Siewior #include <linux/module.h> 50062f6e5SSebastian Andrzej Siewior #include <linux/err.h> 60062f6e5SSebastian Andrzej Siewior 70062f6e5SSebastian Andrzej Siewior #include <linux/usb/composite.h> 80062f6e5SSebastian Andrzej Siewior 90062f6e5SSebastian Andrzej Siewior static LIST_HEAD(func_list); 100062f6e5SSebastian Andrzej Siewior static DEFINE_MUTEX(func_lock); 110062f6e5SSebastian Andrzej Siewior try_get_usb_function_instance(const char * name)120062f6e5SSebastian Andrzej Siewiorstatic struct usb_function_instance *try_get_usb_function_instance(const char *name) 130062f6e5SSebastian Andrzej Siewior { 140062f6e5SSebastian Andrzej Siewior struct usb_function_driver *fd; 150062f6e5SSebastian Andrzej Siewior struct usb_function_instance *fi; 160062f6e5SSebastian Andrzej Siewior 170062f6e5SSebastian Andrzej Siewior fi = ERR_PTR(-ENOENT); 180062f6e5SSebastian Andrzej Siewior mutex_lock(&func_lock); 190062f6e5SSebastian Andrzej Siewior list_for_each_entry(fd, &func_list, list) { 200062f6e5SSebastian Andrzej Siewior 210062f6e5SSebastian Andrzej Siewior if (strcmp(name, fd->name)) 220062f6e5SSebastian Andrzej Siewior continue; 230062f6e5SSebastian Andrzej Siewior 240062f6e5SSebastian Andrzej Siewior if (!try_module_get(fd->mod)) { 250062f6e5SSebastian Andrzej Siewior fi = ERR_PTR(-EBUSY); 260062f6e5SSebastian Andrzej Siewior break; 270062f6e5SSebastian Andrzej Siewior } 280062f6e5SSebastian Andrzej Siewior fi = fd->alloc_inst(); 290062f6e5SSebastian Andrzej Siewior if (IS_ERR(fi)) 300062f6e5SSebastian Andrzej Siewior module_put(fd->mod); 310062f6e5SSebastian Andrzej Siewior else 320062f6e5SSebastian Andrzej Siewior fi->fd = fd; 330062f6e5SSebastian Andrzej Siewior break; 340062f6e5SSebastian Andrzej Siewior } 350062f6e5SSebastian Andrzej Siewior mutex_unlock(&func_lock); 360062f6e5SSebastian Andrzej Siewior return fi; 370062f6e5SSebastian Andrzej Siewior } 380062f6e5SSebastian Andrzej Siewior usb_get_function_instance(const char * name)390062f6e5SSebastian Andrzej Siewiorstruct usb_function_instance *usb_get_function_instance(const char *name) 400062f6e5SSebastian Andrzej Siewior { 410062f6e5SSebastian Andrzej Siewior struct usb_function_instance *fi; 420062f6e5SSebastian Andrzej Siewior int ret; 430062f6e5SSebastian Andrzej Siewior 440062f6e5SSebastian Andrzej Siewior fi = try_get_usb_function_instance(name); 450062f6e5SSebastian Andrzej Siewior if (!IS_ERR(fi)) 460062f6e5SSebastian Andrzej Siewior return fi; 470062f6e5SSebastian Andrzej Siewior ret = PTR_ERR(fi); 480062f6e5SSebastian Andrzej Siewior if (ret != -ENOENT) 490062f6e5SSebastian Andrzej Siewior return fi; 500062f6e5SSebastian Andrzej Siewior ret = request_module("usbfunc:%s", name); 510062f6e5SSebastian Andrzej Siewior if (ret < 0) 520062f6e5SSebastian Andrzej Siewior return ERR_PTR(ret); 530062f6e5SSebastian Andrzej Siewior return try_get_usb_function_instance(name); 540062f6e5SSebastian Andrzej Siewior } 550062f6e5SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_get_function_instance); 560062f6e5SSebastian Andrzej Siewior usb_get_function(struct usb_function_instance * fi)570062f6e5SSebastian Andrzej Siewiorstruct usb_function *usb_get_function(struct usb_function_instance *fi) 580062f6e5SSebastian Andrzej Siewior { 590062f6e5SSebastian Andrzej Siewior struct usb_function *f; 600062f6e5SSebastian Andrzej Siewior 610062f6e5SSebastian Andrzej Siewior f = fi->fd->alloc_func(fi); 620062f6e5SSebastian Andrzej Siewior if (IS_ERR(f)) 630062f6e5SSebastian Andrzej Siewior return f; 640062f6e5SSebastian Andrzej Siewior f->fi = fi; 650062f6e5SSebastian Andrzej Siewior return f; 660062f6e5SSebastian Andrzej Siewior } 670062f6e5SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_get_function); 680062f6e5SSebastian Andrzej Siewior usb_put_function_instance(struct usb_function_instance * fi)690062f6e5SSebastian Andrzej Siewiorvoid usb_put_function_instance(struct usb_function_instance *fi) 700062f6e5SSebastian Andrzej Siewior { 710062f6e5SSebastian Andrzej Siewior struct module *mod; 720062f6e5SSebastian Andrzej Siewior 730062f6e5SSebastian Andrzej Siewior if (!fi) 740062f6e5SSebastian Andrzej Siewior return; 750062f6e5SSebastian Andrzej Siewior 760062f6e5SSebastian Andrzej Siewior mod = fi->fd->mod; 770062f6e5SSebastian Andrzej Siewior fi->free_func_inst(fi); 780062f6e5SSebastian Andrzej Siewior module_put(mod); 790062f6e5SSebastian Andrzej Siewior } 800062f6e5SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_put_function_instance); 810062f6e5SSebastian Andrzej Siewior usb_put_function(struct usb_function * f)820062f6e5SSebastian Andrzej Siewiorvoid usb_put_function(struct usb_function *f) 830062f6e5SSebastian Andrzej Siewior { 840062f6e5SSebastian Andrzej Siewior if (!f) 850062f6e5SSebastian Andrzej Siewior return; 860062f6e5SSebastian Andrzej Siewior 870062f6e5SSebastian Andrzej Siewior f->free_func(f); 880062f6e5SSebastian Andrzej Siewior } 890062f6e5SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_put_function); 900062f6e5SSebastian Andrzej Siewior usb_function_register(struct usb_function_driver * newf)910062f6e5SSebastian Andrzej Siewiorint usb_function_register(struct usb_function_driver *newf) 920062f6e5SSebastian Andrzej Siewior { 930062f6e5SSebastian Andrzej Siewior struct usb_function_driver *fd; 940062f6e5SSebastian Andrzej Siewior int ret; 950062f6e5SSebastian Andrzej Siewior 960062f6e5SSebastian Andrzej Siewior ret = -EEXIST; 970062f6e5SSebastian Andrzej Siewior 980062f6e5SSebastian Andrzej Siewior mutex_lock(&func_lock); 990062f6e5SSebastian Andrzej Siewior list_for_each_entry(fd, &func_list, list) { 1000062f6e5SSebastian Andrzej Siewior if (!strcmp(fd->name, newf->name)) 1010062f6e5SSebastian Andrzej Siewior goto out; 1020062f6e5SSebastian Andrzej Siewior } 1030062f6e5SSebastian Andrzej Siewior ret = 0; 1040062f6e5SSebastian Andrzej Siewior list_add_tail(&newf->list, &func_list); 1050062f6e5SSebastian Andrzej Siewior out: 1060062f6e5SSebastian Andrzej Siewior mutex_unlock(&func_lock); 1070062f6e5SSebastian Andrzej Siewior return ret; 1080062f6e5SSebastian Andrzej Siewior } 1090062f6e5SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_function_register); 1100062f6e5SSebastian Andrzej Siewior usb_function_unregister(struct usb_function_driver * fd)1110062f6e5SSebastian Andrzej Siewiorvoid usb_function_unregister(struct usb_function_driver *fd) 1120062f6e5SSebastian Andrzej Siewior { 1130062f6e5SSebastian Andrzej Siewior mutex_lock(&func_lock); 1140062f6e5SSebastian Andrzej Siewior list_del(&fd->list); 1150062f6e5SSebastian Andrzej Siewior mutex_unlock(&func_lock); 1160062f6e5SSebastian Andrzej Siewior } 1170062f6e5SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_function_unregister); 118