xref: /openbmc/linux/drivers/usb/usbip/vudc_main.c (revision 8730046c)
1 /*
2  * Copyright (C) 2015 Karol Kosik <karo9@interia.eu>
3  * Copyright (C) 2015-2016 Samsung Electronics
4  *               Igor Kotrasinski <i.kotrasinsk@samsung.com>
5  *               Krzysztof Opasiak <k.opasiak@samsung.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <linux/device.h>
22 #include <linux/list.h>
23 #include <linux/module.h>
24 
25 #include "vudc.h"
26 
27 static unsigned int vudc_number = 1;
28 
29 module_param_named(num, vudc_number, uint, S_IRUGO);
30 MODULE_PARM_DESC(num, "number of emulated controllers");
31 
32 static struct platform_driver vudc_driver = {
33 	.probe		= vudc_probe,
34 	.remove		= vudc_remove,
35 	.driver		= {
36 		.name	= GADGET_NAME,
37 	},
38 };
39 
40 static struct list_head vudc_devices = LIST_HEAD_INIT(vudc_devices);
41 
42 static int __init init(void)
43 {
44 	int retval = -ENOMEM;
45 	int i;
46 	struct vudc_device *udc_dev = NULL, *udc_dev2 = NULL;
47 
48 	if (usb_disabled())
49 		return -ENODEV;
50 
51 	if (vudc_number < 1) {
52 		pr_err("Number of emulated UDC must be no less than 1");
53 		return -EINVAL;
54 	}
55 
56 	retval = platform_driver_register(&vudc_driver);
57 	if (retval < 0)
58 		goto out;
59 
60 	for (i = 0; i < vudc_number; i++) {
61 		udc_dev = alloc_vudc_device(i);
62 		if (!udc_dev) {
63 			retval = -ENOMEM;
64 			goto cleanup;
65 		}
66 
67 		retval = platform_device_add(udc_dev->pdev);
68 		if (retval < 0) {
69 			put_vudc_device(udc_dev);
70 			goto cleanup;
71 		}
72 
73 		list_add_tail(&udc_dev->dev_entry, &vudc_devices);
74 		if (!platform_get_drvdata(udc_dev->pdev)) {
75 			/*
76 			 * The udc was added successfully but its probe
77 			 * function failed for some reason.
78 			 */
79 			retval = -EINVAL;
80 			goto cleanup;
81 		}
82 	}
83 	goto out;
84 
85 cleanup:
86 	list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) {
87 		list_del(&udc_dev->dev_entry);
88 		platform_device_del(udc_dev->pdev);
89 		put_vudc_device(udc_dev);
90 	}
91 
92 	platform_driver_unregister(&vudc_driver);
93 out:
94 	return retval;
95 }
96 module_init(init);
97 
98 static void __exit cleanup(void)
99 {
100 	struct vudc_device *udc_dev = NULL, *udc_dev2 = NULL;
101 
102 	list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) {
103 		list_del(&udc_dev->dev_entry);
104 		platform_device_unregister(udc_dev->pdev);
105 		put_vudc_device(udc_dev);
106 	}
107 	platform_driver_unregister(&vudc_driver);
108 }
109 module_exit(cleanup);
110 
111 MODULE_DESCRIPTION("USB over IP Device Controller");
112 MODULE_AUTHOR("Krzysztof Opasiak, Karol Kosik, Igor Kotrasinski");
113 MODULE_LICENSE("GPL");
114