1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright (C) 2015 Karol Kosik <karo9@interia.eu> |
4 | * Copyright (C) 2015-2016 Samsung Electronics |
5 | * Igor Kotrasinski <i.kotrasinsk@samsung.com> |
6 | * Krzysztof Opasiak <k.opasiak@samsung.com> |
7 | */ |
8 | |
9 | #include <linux/device.h> |
10 | #include <linux/list.h> |
11 | #include <linux/module.h> |
12 | |
13 | #include "vudc.h" |
14 | |
15 | static unsigned int vudc_number = 1; |
16 | |
17 | module_param_named(num, vudc_number, uint, S_IRUGO); |
18 | MODULE_PARM_DESC(num, "number of emulated controllers" ); |
19 | |
20 | static struct platform_driver vudc_driver = { |
21 | .probe = vudc_probe, |
22 | .remove = vudc_remove, |
23 | .driver = { |
24 | .name = GADGET_NAME, |
25 | .dev_groups = vudc_groups, |
26 | }, |
27 | }; |
28 | |
29 | static LIST_HEAD(vudc_devices); |
30 | |
31 | static int __init vudc_init(void) |
32 | { |
33 | int retval = -ENOMEM; |
34 | int i; |
35 | struct vudc_device *udc_dev = NULL, *udc_dev2 = NULL; |
36 | |
37 | if (usb_disabled()) |
38 | return -ENODEV; |
39 | |
40 | if (vudc_number < 1) { |
41 | pr_err("Number of emulated UDC must be no less than 1" ); |
42 | return -EINVAL; |
43 | } |
44 | |
45 | retval = platform_driver_register(&vudc_driver); |
46 | if (retval < 0) |
47 | goto out; |
48 | |
49 | for (i = 0; i < vudc_number; i++) { |
50 | udc_dev = alloc_vudc_device(devid: i); |
51 | if (!udc_dev) { |
52 | retval = -ENOMEM; |
53 | goto cleanup; |
54 | } |
55 | |
56 | retval = platform_device_add(pdev: udc_dev->pdev); |
57 | if (retval < 0) { |
58 | put_vudc_device(udc_dev); |
59 | goto cleanup; |
60 | } |
61 | |
62 | list_add_tail(new: &udc_dev->dev_entry, head: &vudc_devices); |
63 | if (!platform_get_drvdata(pdev: udc_dev->pdev)) { |
64 | /* |
65 | * The udc was added successfully but its probe |
66 | * function failed for some reason. |
67 | */ |
68 | retval = -EINVAL; |
69 | goto cleanup; |
70 | } |
71 | } |
72 | goto out; |
73 | |
74 | cleanup: |
75 | list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) { |
76 | list_del(entry: &udc_dev->dev_entry); |
77 | /* |
78 | * Just do platform_device_del() here, put_vudc_device() |
79 | * calls the platform_device_put() |
80 | */ |
81 | platform_device_del(pdev: udc_dev->pdev); |
82 | put_vudc_device(udc_dev); |
83 | } |
84 | |
85 | platform_driver_unregister(&vudc_driver); |
86 | out: |
87 | return retval; |
88 | } |
89 | module_init(vudc_init); |
90 | |
91 | static void __exit vudc_cleanup(void) |
92 | { |
93 | struct vudc_device *udc_dev = NULL, *udc_dev2 = NULL; |
94 | |
95 | list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) { |
96 | list_del(entry: &udc_dev->dev_entry); |
97 | /* |
98 | * Just do platform_device_del() here, put_vudc_device() |
99 | * calls the platform_device_put() |
100 | */ |
101 | platform_device_del(pdev: udc_dev->pdev); |
102 | put_vudc_device(udc_dev); |
103 | } |
104 | platform_driver_unregister(&vudc_driver); |
105 | } |
106 | module_exit(vudc_cleanup); |
107 | |
108 | MODULE_DESCRIPTION("USB over IP Device Controller" ); |
109 | MODULE_AUTHOR("Krzysztof Opasiak, Karol Kosik, Igor Kotrasinski" ); |
110 | MODULE_LICENSE("GPL" ); |
111 | |