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