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