GNU Linux-libre 4.9-gnu1
[releases.git] / drivers / usb / usbip / vudc_main.c
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");