xref: /openbmc/linux/drivers/usb/usbip/vudc_main.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
280fd9cd5SIgor Kotrasinski /*
380fd9cd5SIgor Kotrasinski  * Copyright (C) 2015 Karol Kosik <karo9@interia.eu>
480fd9cd5SIgor Kotrasinski  * Copyright (C) 2015-2016 Samsung Electronics
580fd9cd5SIgor Kotrasinski  *               Igor Kotrasinski <i.kotrasinsk@samsung.com>
680fd9cd5SIgor Kotrasinski  *               Krzysztof Opasiak <k.opasiak@samsung.com>
780fd9cd5SIgor Kotrasinski  */
880fd9cd5SIgor Kotrasinski 
980fd9cd5SIgor Kotrasinski #include <linux/device.h>
1080fd9cd5SIgor Kotrasinski #include <linux/list.h>
1180fd9cd5SIgor Kotrasinski #include <linux/module.h>
1280fd9cd5SIgor Kotrasinski 
1380fd9cd5SIgor Kotrasinski #include "vudc.h"
1480fd9cd5SIgor Kotrasinski 
1580fd9cd5SIgor Kotrasinski static unsigned int vudc_number = 1;
1680fd9cd5SIgor Kotrasinski 
1780fd9cd5SIgor Kotrasinski module_param_named(num, vudc_number, uint, S_IRUGO);
1880fd9cd5SIgor Kotrasinski MODULE_PARM_DESC(num, "number of emulated controllers");
1980fd9cd5SIgor Kotrasinski 
2080fd9cd5SIgor Kotrasinski static struct platform_driver vudc_driver = {
2180fd9cd5SIgor Kotrasinski 	.probe		= vudc_probe,
2280fd9cd5SIgor Kotrasinski 	.remove		= vudc_remove,
2380fd9cd5SIgor Kotrasinski 	.driver		= {
2480fd9cd5SIgor Kotrasinski 		.name	= GADGET_NAME,
2591148dbaSGreg Kroah-Hartman 		.dev_groups = vudc_groups,
2680fd9cd5SIgor Kotrasinski 	},
2780fd9cd5SIgor Kotrasinski };
2880fd9cd5SIgor Kotrasinski 
294378e427SCai Huoqing static LIST_HEAD(vudc_devices);
3080fd9cd5SIgor Kotrasinski 
vudc_init(void)31*393dcd1fSRandy Dunlap static int __init vudc_init(void)
3280fd9cd5SIgor Kotrasinski {
3380fd9cd5SIgor Kotrasinski 	int retval = -ENOMEM;
3480fd9cd5SIgor Kotrasinski 	int i;
3580fd9cd5SIgor Kotrasinski 	struct vudc_device *udc_dev = NULL, *udc_dev2 = NULL;
3680fd9cd5SIgor Kotrasinski 
3780fd9cd5SIgor Kotrasinski 	if (usb_disabled())
3880fd9cd5SIgor Kotrasinski 		return -ENODEV;
3980fd9cd5SIgor Kotrasinski 
4080fd9cd5SIgor Kotrasinski 	if (vudc_number < 1) {
4180fd9cd5SIgor Kotrasinski 		pr_err("Number of emulated UDC must be no less than 1");
4280fd9cd5SIgor Kotrasinski 		return -EINVAL;
4380fd9cd5SIgor Kotrasinski 	}
4480fd9cd5SIgor Kotrasinski 
4580fd9cd5SIgor Kotrasinski 	retval = platform_driver_register(&vudc_driver);
4680fd9cd5SIgor Kotrasinski 	if (retval < 0)
4780fd9cd5SIgor Kotrasinski 		goto out;
4880fd9cd5SIgor Kotrasinski 
4980fd9cd5SIgor Kotrasinski 	for (i = 0; i < vudc_number; i++) {
5080fd9cd5SIgor Kotrasinski 		udc_dev = alloc_vudc_device(i);
5180fd9cd5SIgor Kotrasinski 		if (!udc_dev) {
5280fd9cd5SIgor Kotrasinski 			retval = -ENOMEM;
5380fd9cd5SIgor Kotrasinski 			goto cleanup;
5480fd9cd5SIgor Kotrasinski 		}
5580fd9cd5SIgor Kotrasinski 
5680fd9cd5SIgor Kotrasinski 		retval = platform_device_add(udc_dev->pdev);
5780fd9cd5SIgor Kotrasinski 		if (retval < 0) {
5880fd9cd5SIgor Kotrasinski 			put_vudc_device(udc_dev);
5980fd9cd5SIgor Kotrasinski 			goto cleanup;
6080fd9cd5SIgor Kotrasinski 		}
6180fd9cd5SIgor Kotrasinski 
6280fd9cd5SIgor Kotrasinski 		list_add_tail(&udc_dev->dev_entry, &vudc_devices);
6380fd9cd5SIgor Kotrasinski 		if (!platform_get_drvdata(udc_dev->pdev)) {
6480fd9cd5SIgor Kotrasinski 			/*
6580fd9cd5SIgor Kotrasinski 			 * The udc was added successfully but its probe
6680fd9cd5SIgor Kotrasinski 			 * function failed for some reason.
6780fd9cd5SIgor Kotrasinski 			 */
6880fd9cd5SIgor Kotrasinski 			retval = -EINVAL;
6980fd9cd5SIgor Kotrasinski 			goto cleanup;
7080fd9cd5SIgor Kotrasinski 		}
7180fd9cd5SIgor Kotrasinski 	}
7280fd9cd5SIgor Kotrasinski 	goto out;
7380fd9cd5SIgor Kotrasinski 
7480fd9cd5SIgor Kotrasinski cleanup:
7580fd9cd5SIgor Kotrasinski 	list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) {
7680fd9cd5SIgor Kotrasinski 		list_del(&udc_dev->dev_entry);
77e28fd56aSShuah Khan (Samsung OSG) 		/*
78e28fd56aSShuah Khan (Samsung OSG) 		 * Just do platform_device_del() here, put_vudc_device()
79e28fd56aSShuah Khan (Samsung OSG) 		 * calls the platform_device_put()
80e28fd56aSShuah Khan (Samsung OSG) 		 */
8180fd9cd5SIgor Kotrasinski 		platform_device_del(udc_dev->pdev);
8280fd9cd5SIgor Kotrasinski 		put_vudc_device(udc_dev);
8380fd9cd5SIgor Kotrasinski 	}
8480fd9cd5SIgor Kotrasinski 
8580fd9cd5SIgor Kotrasinski 	platform_driver_unregister(&vudc_driver);
8680fd9cd5SIgor Kotrasinski out:
8780fd9cd5SIgor Kotrasinski 	return retval;
8880fd9cd5SIgor Kotrasinski }
89*393dcd1fSRandy Dunlap module_init(vudc_init);
9080fd9cd5SIgor Kotrasinski 
vudc_cleanup(void)91*393dcd1fSRandy Dunlap static void __exit vudc_cleanup(void)
9280fd9cd5SIgor Kotrasinski {
9380fd9cd5SIgor Kotrasinski 	struct vudc_device *udc_dev = NULL, *udc_dev2 = NULL;
9480fd9cd5SIgor Kotrasinski 
9580fd9cd5SIgor Kotrasinski 	list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) {
9680fd9cd5SIgor Kotrasinski 		list_del(&udc_dev->dev_entry);
97e28fd56aSShuah Khan (Samsung OSG) 		/*
98e28fd56aSShuah Khan (Samsung OSG) 		 * Just do platform_device_del() here, put_vudc_device()
99e28fd56aSShuah Khan (Samsung OSG) 		 * calls the platform_device_put()
100e28fd56aSShuah Khan (Samsung OSG) 		 */
101e28fd56aSShuah Khan (Samsung OSG) 		platform_device_del(udc_dev->pdev);
10280fd9cd5SIgor Kotrasinski 		put_vudc_device(udc_dev);
10380fd9cd5SIgor Kotrasinski 	}
10480fd9cd5SIgor Kotrasinski 	platform_driver_unregister(&vudc_driver);
10580fd9cd5SIgor Kotrasinski }
106*393dcd1fSRandy Dunlap module_exit(vudc_cleanup);
10780fd9cd5SIgor Kotrasinski 
10880fd9cd5SIgor Kotrasinski MODULE_DESCRIPTION("USB over IP Device Controller");
10980fd9cd5SIgor Kotrasinski MODULE_AUTHOR("Krzysztof Opasiak, Karol Kosik, Igor Kotrasinski");
11080fd9cd5SIgor Kotrasinski MODULE_LICENSE("GPL");
111