diff options
Diffstat (limited to 'drivers/usb/usbip/vudc_main.c')
-rw-r--r-- | drivers/usb/usbip/vudc_main.c | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/drivers/usb/usbip/vudc_main.c b/drivers/usb/usbip/vudc_main.c new file mode 100644 index 0000000000..993e721cb8 --- /dev/null +++ b/drivers/usb/usbip/vudc_main.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Karol Kosik <karo9@interia.eu> + * Copyright (C) 2015-2016 Samsung Electronics + * Igor Kotrasinski <i.kotrasinsk@samsung.com> + * Krzysztof Opasiak <k.opasiak@samsung.com> + */ + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/module.h> + +#include "vudc.h" + +static unsigned int vudc_number = 1; + +module_param_named(num, vudc_number, uint, S_IRUGO); +MODULE_PARM_DESC(num, "number of emulated controllers"); + +static struct platform_driver vudc_driver = { + .probe = vudc_probe, + .remove = vudc_remove, + .driver = { + .name = GADGET_NAME, + .dev_groups = vudc_groups, + }, +}; + +static LIST_HEAD(vudc_devices); + +static int __init vudc_init(void) +{ + int retval = -ENOMEM; + int i; + struct vudc_device *udc_dev = NULL, *udc_dev2 = NULL; + + if (usb_disabled()) + return -ENODEV; + + if (vudc_number < 1) { + pr_err("Number of emulated UDC must be no less than 1"); + return -EINVAL; + } + + retval = platform_driver_register(&vudc_driver); + if (retval < 0) + goto out; + + for (i = 0; i < vudc_number; i++) { + udc_dev = alloc_vudc_device(i); + if (!udc_dev) { + retval = -ENOMEM; + goto cleanup; + } + + retval = platform_device_add(udc_dev->pdev); + if (retval < 0) { + put_vudc_device(udc_dev); + goto cleanup; + } + + list_add_tail(&udc_dev->dev_entry, &vudc_devices); + if (!platform_get_drvdata(udc_dev->pdev)) { + /* + * The udc was added successfully but its probe + * function failed for some reason. + */ + retval = -EINVAL; + goto cleanup; + } + } + goto out; + +cleanup: + list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) { + list_del(&udc_dev->dev_entry); + /* + * Just do platform_device_del() here, put_vudc_device() + * calls the platform_device_put() + */ + platform_device_del(udc_dev->pdev); + put_vudc_device(udc_dev); + } + + platform_driver_unregister(&vudc_driver); +out: + return retval; +} +module_init(vudc_init); + +static void __exit vudc_cleanup(void) +{ + struct vudc_device *udc_dev = NULL, *udc_dev2 = NULL; + + list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) { + list_del(&udc_dev->dev_entry); + /* + * Just do platform_device_del() here, put_vudc_device() + * calls the platform_device_put() + */ + platform_device_del(udc_dev->pdev); + put_vudc_device(udc_dev); + } + platform_driver_unregister(&vudc_driver); +} +module_exit(vudc_cleanup); + +MODULE_DESCRIPTION("USB over IP Device Controller"); +MODULE_AUTHOR("Krzysztof Opasiak, Karol Kosik, Igor Kotrasinski"); +MODULE_LICENSE("GPL"); |