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