15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
28443f2d2SAndrzej Pietrasiewicz /*
38443f2d2SAndrzej Pietrasiewicz * serial.c -- USB gadget serial driver
48443f2d2SAndrzej Pietrasiewicz *
58443f2d2SAndrzej Pietrasiewicz * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
68443f2d2SAndrzej Pietrasiewicz * Copyright (C) 2008 by David Brownell
78443f2d2SAndrzej Pietrasiewicz * Copyright (C) 2008 by Nokia Corporation
88443f2d2SAndrzej Pietrasiewicz */
98443f2d2SAndrzej Pietrasiewicz
108443f2d2SAndrzej Pietrasiewicz #include <linux/kernel.h>
118443f2d2SAndrzej Pietrasiewicz #include <linux/device.h>
12*a8bc8cc1SChristophe JAILLET #include <linux/kstrtox.h>
138443f2d2SAndrzej Pietrasiewicz #include <linux/module.h>
148443f2d2SAndrzej Pietrasiewicz #include <linux/tty.h>
158443f2d2SAndrzej Pietrasiewicz #include <linux/tty_flip.h>
168443f2d2SAndrzej Pietrasiewicz
178443f2d2SAndrzej Pietrasiewicz #include "u_serial.h"
188443f2d2SAndrzej Pietrasiewicz
198443f2d2SAndrzej Pietrasiewicz
208443f2d2SAndrzej Pietrasiewicz /* Defines */
218443f2d2SAndrzej Pietrasiewicz
228443f2d2SAndrzej Pietrasiewicz #define GS_VERSION_STR "v2.4"
238443f2d2SAndrzej Pietrasiewicz #define GS_VERSION_NUM 0x2400
248443f2d2SAndrzej Pietrasiewicz
258443f2d2SAndrzej Pietrasiewicz #define GS_LONG_NAME "Gadget Serial"
268443f2d2SAndrzej Pietrasiewicz #define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR
278443f2d2SAndrzej Pietrasiewicz
288443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
298443f2d2SAndrzej Pietrasiewicz USB_GADGET_COMPOSITE_OPTIONS();
308443f2d2SAndrzej Pietrasiewicz
318443f2d2SAndrzej Pietrasiewicz /* Thanks to NetChip Technologies for donating this product ID.
328443f2d2SAndrzej Pietrasiewicz *
338443f2d2SAndrzej Pietrasiewicz * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
348443f2d2SAndrzej Pietrasiewicz * Instead: allocate your own, using normal USB-IF procedures.
358443f2d2SAndrzej Pietrasiewicz */
368443f2d2SAndrzej Pietrasiewicz #define GS_VENDOR_ID 0x0525 /* NetChip */
378443f2d2SAndrzej Pietrasiewicz #define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */
388443f2d2SAndrzej Pietrasiewicz #define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */
398443f2d2SAndrzej Pietrasiewicz #define GS_CDC_OBEX_PRODUCT_ID 0xa4a9 /* ... as CDC-OBEX */
408443f2d2SAndrzej Pietrasiewicz
418443f2d2SAndrzej Pietrasiewicz /* string IDs are assigned dynamically */
428443f2d2SAndrzej Pietrasiewicz
438443f2d2SAndrzej Pietrasiewicz #define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
448443f2d2SAndrzej Pietrasiewicz
458443f2d2SAndrzej Pietrasiewicz static struct usb_string strings_dev[] = {
468443f2d2SAndrzej Pietrasiewicz [USB_GADGET_MANUFACTURER_IDX].s = "",
478443f2d2SAndrzej Pietrasiewicz [USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME,
488443f2d2SAndrzej Pietrasiewicz [USB_GADGET_SERIAL_IDX].s = "",
498443f2d2SAndrzej Pietrasiewicz [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */,
508443f2d2SAndrzej Pietrasiewicz { } /* end of list */
518443f2d2SAndrzej Pietrasiewicz };
528443f2d2SAndrzej Pietrasiewicz
538443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings stringtab_dev = {
548443f2d2SAndrzej Pietrasiewicz .language = 0x0409, /* en-us */
558443f2d2SAndrzej Pietrasiewicz .strings = strings_dev,
568443f2d2SAndrzej Pietrasiewicz };
578443f2d2SAndrzej Pietrasiewicz
588443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings *dev_strings[] = {
598443f2d2SAndrzej Pietrasiewicz &stringtab_dev,
608443f2d2SAndrzej Pietrasiewicz NULL,
618443f2d2SAndrzej Pietrasiewicz };
628443f2d2SAndrzej Pietrasiewicz
638443f2d2SAndrzej Pietrasiewicz static struct usb_device_descriptor device_desc = {
648443f2d2SAndrzej Pietrasiewicz .bLength = USB_DT_DEVICE_SIZE,
658443f2d2SAndrzej Pietrasiewicz .bDescriptorType = USB_DT_DEVICE,
660aecfc1bSIgor Kotrasinski /* .bcdUSB = DYNAMIC */
678443f2d2SAndrzej Pietrasiewicz /* .bDeviceClass = f(use_acm) */
688443f2d2SAndrzej Pietrasiewicz .bDeviceSubClass = 0,
698443f2d2SAndrzej Pietrasiewicz .bDeviceProtocol = 0,
708443f2d2SAndrzej Pietrasiewicz /* .bMaxPacketSize0 = f(hardware) */
718443f2d2SAndrzej Pietrasiewicz .idVendor = cpu_to_le16(GS_VENDOR_ID),
728443f2d2SAndrzej Pietrasiewicz /* .idProduct = f(use_acm) */
738443f2d2SAndrzej Pietrasiewicz .bcdDevice = cpu_to_le16(GS_VERSION_NUM),
748443f2d2SAndrzej Pietrasiewicz /* .iManufacturer = DYNAMIC */
758443f2d2SAndrzej Pietrasiewicz /* .iProduct = DYNAMIC */
768443f2d2SAndrzej Pietrasiewicz .bNumConfigurations = 1,
778443f2d2SAndrzej Pietrasiewicz };
788443f2d2SAndrzej Pietrasiewicz
793dcc7053SLi Jun static const struct usb_descriptor_header *otg_desc[2];
808443f2d2SAndrzej Pietrasiewicz
818443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
828443f2d2SAndrzej Pietrasiewicz
838443f2d2SAndrzej Pietrasiewicz /* Module */
848443f2d2SAndrzej Pietrasiewicz MODULE_DESCRIPTION(GS_VERSION_NAME);
858443f2d2SAndrzej Pietrasiewicz MODULE_AUTHOR("Al Borchers");
868443f2d2SAndrzej Pietrasiewicz MODULE_AUTHOR("David Brownell");
878443f2d2SAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
888443f2d2SAndrzej Pietrasiewicz
898443f2d2SAndrzej Pietrasiewicz static bool use_acm = true;
908443f2d2SAndrzej Pietrasiewicz module_param(use_acm, bool, 0);
918443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes");
928443f2d2SAndrzej Pietrasiewicz
938443f2d2SAndrzej Pietrasiewicz static bool use_obex = false;
948443f2d2SAndrzej Pietrasiewicz module_param(use_obex, bool, 0);
958443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(use_obex, "Use CDC OBEX, default=no");
968443f2d2SAndrzej Pietrasiewicz
978443f2d2SAndrzej Pietrasiewicz static unsigned n_ports = 1;
988443f2d2SAndrzej Pietrasiewicz module_param(n_ports, uint, 0);
998443f2d2SAndrzej Pietrasiewicz MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
1008443f2d2SAndrzej Pietrasiewicz
101bd25a14eSMichał Mirosław static bool enable = true;
102bd25a14eSMichał Mirosław
103bd25a14eSMichał Mirosław static int switch_gserial_enable(bool do_enable);
104bd25a14eSMichał Mirosław
enable_set(const char * s,const struct kernel_param * kp)105bd25a14eSMichał Mirosław static int enable_set(const char *s, const struct kernel_param *kp)
106bd25a14eSMichał Mirosław {
107bd25a14eSMichał Mirosław bool do_enable;
108bd25a14eSMichał Mirosław int ret;
109bd25a14eSMichał Mirosław
110bd25a14eSMichał Mirosław if (!s) /* called for no-arg enable == default */
111bd25a14eSMichał Mirosław return 0;
112bd25a14eSMichał Mirosław
113*a8bc8cc1SChristophe JAILLET ret = kstrtobool(s, &do_enable);
114bd25a14eSMichał Mirosław if (ret || enable == do_enable)
115bd25a14eSMichał Mirosław return ret;
116bd25a14eSMichał Mirosław
117bd25a14eSMichał Mirosław ret = switch_gserial_enable(do_enable);
118bd25a14eSMichał Mirosław if (!ret)
119bd25a14eSMichał Mirosław enable = do_enable;
120bd25a14eSMichał Mirosław
121bd25a14eSMichał Mirosław return ret;
122bd25a14eSMichał Mirosław }
123bd25a14eSMichał Mirosław
124bd25a14eSMichał Mirosław static const struct kernel_param_ops enable_ops = {
125bd25a14eSMichał Mirosław .set = enable_set,
126bd25a14eSMichał Mirosław .get = param_get_bool,
127bd25a14eSMichał Mirosław };
128bd25a14eSMichał Mirosław
129bd25a14eSMichał Mirosław module_param_cb(enable, &enable_ops, &enable, 0644);
130bd25a14eSMichał Mirosław
1318443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
1328443f2d2SAndrzej Pietrasiewicz
1338443f2d2SAndrzej Pietrasiewicz static struct usb_configuration serial_config_driver = {
1348443f2d2SAndrzej Pietrasiewicz /* .label = f(use_acm) */
1358443f2d2SAndrzej Pietrasiewicz /* .bConfigurationValue = f(use_acm) */
1368443f2d2SAndrzej Pietrasiewicz /* .iConfiguration = DYNAMIC */
1378443f2d2SAndrzej Pietrasiewicz .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
1388443f2d2SAndrzej Pietrasiewicz };
1398443f2d2SAndrzej Pietrasiewicz
1408443f2d2SAndrzej Pietrasiewicz static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS];
1418443f2d2SAndrzej Pietrasiewicz static struct usb_function *f_serial[MAX_U_SERIAL_PORTS];
1428443f2d2SAndrzej Pietrasiewicz
serial_register_ports(struct usb_composite_dev * cdev,struct usb_configuration * c,const char * f_name)1438443f2d2SAndrzej Pietrasiewicz static int serial_register_ports(struct usb_composite_dev *cdev,
1448443f2d2SAndrzej Pietrasiewicz struct usb_configuration *c, const char *f_name)
1458443f2d2SAndrzej Pietrasiewicz {
1468443f2d2SAndrzej Pietrasiewicz int i;
1478443f2d2SAndrzej Pietrasiewicz int ret;
1488443f2d2SAndrzej Pietrasiewicz
1498443f2d2SAndrzej Pietrasiewicz ret = usb_add_config_only(cdev, c);
1508443f2d2SAndrzej Pietrasiewicz if (ret)
1518443f2d2SAndrzej Pietrasiewicz goto out;
1528443f2d2SAndrzej Pietrasiewicz
1538443f2d2SAndrzej Pietrasiewicz for (i = 0; i < n_ports; i++) {
1548443f2d2SAndrzej Pietrasiewicz
1558443f2d2SAndrzej Pietrasiewicz fi_serial[i] = usb_get_function_instance(f_name);
1568443f2d2SAndrzej Pietrasiewicz if (IS_ERR(fi_serial[i])) {
1578443f2d2SAndrzej Pietrasiewicz ret = PTR_ERR(fi_serial[i]);
1588443f2d2SAndrzej Pietrasiewicz goto fail;
1598443f2d2SAndrzej Pietrasiewicz }
1608443f2d2SAndrzej Pietrasiewicz
1618443f2d2SAndrzej Pietrasiewicz f_serial[i] = usb_get_function(fi_serial[i]);
1628443f2d2SAndrzej Pietrasiewicz if (IS_ERR(f_serial[i])) {
1638443f2d2SAndrzej Pietrasiewicz ret = PTR_ERR(f_serial[i]);
1648443f2d2SAndrzej Pietrasiewicz goto err_get_func;
1658443f2d2SAndrzej Pietrasiewicz }
1668443f2d2SAndrzej Pietrasiewicz
1678443f2d2SAndrzej Pietrasiewicz ret = usb_add_function(c, f_serial[i]);
1688443f2d2SAndrzej Pietrasiewicz if (ret)
1698443f2d2SAndrzej Pietrasiewicz goto err_add_func;
1708443f2d2SAndrzej Pietrasiewicz }
1718443f2d2SAndrzej Pietrasiewicz
1728443f2d2SAndrzej Pietrasiewicz return 0;
1738443f2d2SAndrzej Pietrasiewicz
1748443f2d2SAndrzej Pietrasiewicz err_add_func:
1758443f2d2SAndrzej Pietrasiewicz usb_put_function(f_serial[i]);
1768443f2d2SAndrzej Pietrasiewicz err_get_func:
1778443f2d2SAndrzej Pietrasiewicz usb_put_function_instance(fi_serial[i]);
1788443f2d2SAndrzej Pietrasiewicz
1798443f2d2SAndrzej Pietrasiewicz fail:
1808443f2d2SAndrzej Pietrasiewicz i--;
1818443f2d2SAndrzej Pietrasiewicz while (i >= 0) {
1828443f2d2SAndrzej Pietrasiewicz usb_remove_function(c, f_serial[i]);
1838443f2d2SAndrzej Pietrasiewicz usb_put_function(f_serial[i]);
1848443f2d2SAndrzej Pietrasiewicz usb_put_function_instance(fi_serial[i]);
1858443f2d2SAndrzej Pietrasiewicz i--;
1868443f2d2SAndrzej Pietrasiewicz }
1878443f2d2SAndrzej Pietrasiewicz out:
1888443f2d2SAndrzej Pietrasiewicz return ret;
1898443f2d2SAndrzej Pietrasiewicz }
1908443f2d2SAndrzej Pietrasiewicz
gs_bind(struct usb_composite_dev * cdev)191c94e289fSArnd Bergmann static int gs_bind(struct usb_composite_dev *cdev)
1928443f2d2SAndrzej Pietrasiewicz {
1938443f2d2SAndrzej Pietrasiewicz int status;
1948443f2d2SAndrzej Pietrasiewicz
1958443f2d2SAndrzej Pietrasiewicz /* Allocate string descriptor numbers ... note that string
1968443f2d2SAndrzej Pietrasiewicz * contents can be overridden by the composite_dev glue.
1978443f2d2SAndrzej Pietrasiewicz */
1988443f2d2SAndrzej Pietrasiewicz
1998443f2d2SAndrzej Pietrasiewicz status = usb_string_ids_tab(cdev, strings_dev);
2008443f2d2SAndrzej Pietrasiewicz if (status < 0)
2018443f2d2SAndrzej Pietrasiewicz goto fail;
2028443f2d2SAndrzej Pietrasiewicz device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
2038443f2d2SAndrzej Pietrasiewicz device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
2048443f2d2SAndrzej Pietrasiewicz status = strings_dev[STRING_DESCRIPTION_IDX].id;
2058443f2d2SAndrzej Pietrasiewicz serial_config_driver.iConfiguration = status;
2068443f2d2SAndrzej Pietrasiewicz
2078443f2d2SAndrzej Pietrasiewicz if (gadget_is_otg(cdev->gadget)) {
2083dcc7053SLi Jun if (!otg_desc[0]) {
2093dcc7053SLi Jun struct usb_descriptor_header *usb_desc;
2103dcc7053SLi Jun
2113dcc7053SLi Jun usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
2123dcc7053SLi Jun if (!usb_desc) {
2133dcc7053SLi Jun status = -ENOMEM;
2143dcc7053SLi Jun goto fail;
2153dcc7053SLi Jun }
2163dcc7053SLi Jun usb_otg_descriptor_init(cdev->gadget, usb_desc);
2173dcc7053SLi Jun otg_desc[0] = usb_desc;
2183dcc7053SLi Jun otg_desc[1] = NULL;
2193dcc7053SLi Jun }
2208443f2d2SAndrzej Pietrasiewicz serial_config_driver.descriptors = otg_desc;
2218443f2d2SAndrzej Pietrasiewicz serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
2228443f2d2SAndrzej Pietrasiewicz }
2238443f2d2SAndrzej Pietrasiewicz
2248443f2d2SAndrzej Pietrasiewicz /* register our configuration */
2258443f2d2SAndrzej Pietrasiewicz if (use_acm) {
2268443f2d2SAndrzej Pietrasiewicz status = serial_register_ports(cdev, &serial_config_driver,
2278443f2d2SAndrzej Pietrasiewicz "acm");
2288443f2d2SAndrzej Pietrasiewicz usb_ep_autoconfig_reset(cdev->gadget);
2298443f2d2SAndrzej Pietrasiewicz } else if (use_obex)
2308443f2d2SAndrzej Pietrasiewicz status = serial_register_ports(cdev, &serial_config_driver,
2318443f2d2SAndrzej Pietrasiewicz "obex");
2328443f2d2SAndrzej Pietrasiewicz else {
2338443f2d2SAndrzej Pietrasiewicz status = serial_register_ports(cdev, &serial_config_driver,
2348443f2d2SAndrzej Pietrasiewicz "gser");
2358443f2d2SAndrzej Pietrasiewicz }
2368443f2d2SAndrzej Pietrasiewicz if (status < 0)
2373dcc7053SLi Jun goto fail1;
2388443f2d2SAndrzej Pietrasiewicz
2398443f2d2SAndrzej Pietrasiewicz usb_composite_overwrite_options(cdev, &coverwrite);
2408443f2d2SAndrzej Pietrasiewicz INFO(cdev, "%s\n", GS_VERSION_NAME);
2418443f2d2SAndrzej Pietrasiewicz
2428443f2d2SAndrzej Pietrasiewicz return 0;
2433dcc7053SLi Jun fail1:
2443dcc7053SLi Jun kfree(otg_desc[0]);
2453dcc7053SLi Jun otg_desc[0] = NULL;
2468443f2d2SAndrzej Pietrasiewicz fail:
2478443f2d2SAndrzej Pietrasiewicz return status;
2488443f2d2SAndrzej Pietrasiewicz }
2498443f2d2SAndrzej Pietrasiewicz
gs_unbind(struct usb_composite_dev * cdev)2508443f2d2SAndrzej Pietrasiewicz static int gs_unbind(struct usb_composite_dev *cdev)
2518443f2d2SAndrzej Pietrasiewicz {
2528443f2d2SAndrzej Pietrasiewicz int i;
2538443f2d2SAndrzej Pietrasiewicz
2548443f2d2SAndrzej Pietrasiewicz for (i = 0; i < n_ports; i++) {
2558443f2d2SAndrzej Pietrasiewicz usb_put_function(f_serial[i]);
2568443f2d2SAndrzej Pietrasiewicz usb_put_function_instance(fi_serial[i]);
2578443f2d2SAndrzej Pietrasiewicz }
2583dcc7053SLi Jun
2593dcc7053SLi Jun kfree(otg_desc[0]);
2603dcc7053SLi Jun otg_desc[0] = NULL;
2613dcc7053SLi Jun
2628443f2d2SAndrzej Pietrasiewicz return 0;
2638443f2d2SAndrzej Pietrasiewicz }
2648443f2d2SAndrzej Pietrasiewicz
265c94e289fSArnd Bergmann static struct usb_composite_driver gserial_driver = {
2668443f2d2SAndrzej Pietrasiewicz .name = "g_serial",
2678443f2d2SAndrzej Pietrasiewicz .dev = &device_desc,
2688443f2d2SAndrzej Pietrasiewicz .strings = dev_strings,
2698443f2d2SAndrzej Pietrasiewicz .max_speed = USB_SPEED_SUPER,
2708443f2d2SAndrzej Pietrasiewicz .bind = gs_bind,
2718443f2d2SAndrzej Pietrasiewicz .unbind = gs_unbind,
2728443f2d2SAndrzej Pietrasiewicz };
2738443f2d2SAndrzej Pietrasiewicz
switch_gserial_enable(bool do_enable)274bd25a14eSMichał Mirosław static int switch_gserial_enable(bool do_enable)
275bd25a14eSMichał Mirosław {
276bd25a14eSMichał Mirosław if (!serial_config_driver.label)
2776653b827SRandy Dunlap /* gserial_init() was not called, yet */
278bd25a14eSMichał Mirosław return 0;
279bd25a14eSMichał Mirosław
280bd25a14eSMichał Mirosław if (do_enable)
281bd25a14eSMichał Mirosław return usb_composite_probe(&gserial_driver);
282bd25a14eSMichał Mirosław
283bd25a14eSMichał Mirosław usb_composite_unregister(&gserial_driver);
284bd25a14eSMichał Mirosław return 0;
285bd25a14eSMichał Mirosław }
286bd25a14eSMichał Mirosław
gserial_init(void)2876653b827SRandy Dunlap static int __init gserial_init(void)
2888443f2d2SAndrzej Pietrasiewicz {
2898443f2d2SAndrzej Pietrasiewicz /* We *could* export two configs; that'd be much cleaner...
2908443f2d2SAndrzej Pietrasiewicz * but neither of these product IDs was defined that way.
2918443f2d2SAndrzej Pietrasiewicz */
2928443f2d2SAndrzej Pietrasiewicz if (use_acm) {
2938443f2d2SAndrzej Pietrasiewicz serial_config_driver.label = "CDC ACM config";
2948443f2d2SAndrzej Pietrasiewicz serial_config_driver.bConfigurationValue = 2;
2958443f2d2SAndrzej Pietrasiewicz device_desc.bDeviceClass = USB_CLASS_COMM;
2968443f2d2SAndrzej Pietrasiewicz device_desc.idProduct =
2978443f2d2SAndrzej Pietrasiewicz cpu_to_le16(GS_CDC_PRODUCT_ID);
2988443f2d2SAndrzej Pietrasiewicz } else if (use_obex) {
2998443f2d2SAndrzej Pietrasiewicz serial_config_driver.label = "CDC OBEX config";
3008443f2d2SAndrzej Pietrasiewicz serial_config_driver.bConfigurationValue = 3;
3018443f2d2SAndrzej Pietrasiewicz device_desc.bDeviceClass = USB_CLASS_COMM;
3028443f2d2SAndrzej Pietrasiewicz device_desc.idProduct =
3038443f2d2SAndrzej Pietrasiewicz cpu_to_le16(GS_CDC_OBEX_PRODUCT_ID);
3048443f2d2SAndrzej Pietrasiewicz } else {
3058443f2d2SAndrzej Pietrasiewicz serial_config_driver.label = "Generic Serial config";
3068443f2d2SAndrzej Pietrasiewicz serial_config_driver.bConfigurationValue = 1;
3078443f2d2SAndrzej Pietrasiewicz device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
3088443f2d2SAndrzej Pietrasiewicz device_desc.idProduct =
3098443f2d2SAndrzej Pietrasiewicz cpu_to_le16(GS_PRODUCT_ID);
3108443f2d2SAndrzej Pietrasiewicz }
3118443f2d2SAndrzej Pietrasiewicz strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label;
3128443f2d2SAndrzej Pietrasiewicz
313bd25a14eSMichał Mirosław if (!enable)
314bd25a14eSMichał Mirosław return 0;
315bd25a14eSMichał Mirosław
3168443f2d2SAndrzej Pietrasiewicz return usb_composite_probe(&gserial_driver);
3178443f2d2SAndrzej Pietrasiewicz }
3186653b827SRandy Dunlap module_init(gserial_init);
3198443f2d2SAndrzej Pietrasiewicz
gserial_cleanup(void)3206653b827SRandy Dunlap static void __exit gserial_cleanup(void)
3218443f2d2SAndrzej Pietrasiewicz {
322bd25a14eSMichał Mirosław if (enable)
3238443f2d2SAndrzej Pietrasiewicz usb_composite_unregister(&gserial_driver);
3248443f2d2SAndrzej Pietrasiewicz }
3256653b827SRandy Dunlap module_exit(gserial_cleanup);
326