| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2008-06-19 17:52:07 -07:00
										 |  |  |  * serial.c -- USB gadget serial driver | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2008-06-19 17:52:07 -07:00
										 |  |  |  * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) | 
					
						
							|  |  |  |  * Copyright (C) 2008 by David Brownell | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  |  * Copyright (C) 2008 by Nokia Corporation | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This software is distributed under the terms of the GNU General | 
					
						
							|  |  |  |  * Public License ("GPL") as published by the Free Software Foundation, | 
					
						
							|  |  |  |  * either version 2 of that License or (at your option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/device.h>
 | 
					
						
							|  |  |  | #include <linux/tty.h>
 | 
					
						
							|  |  |  | #include <linux/tty_flip.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 17:52:07 -07:00
										 |  |  | #include "u_serial.h"
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include "gadget_chips.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Defines */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | #define GS_VERSION_STR			"v2.4"
 | 
					
						
							|  |  |  | #define GS_VERSION_NUM			0x2400
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define GS_LONG_NAME			"Gadget Serial"
 | 
					
						
							| 
									
										
										
										
											2008-06-19 17:52:07 -07:00
										 |  |  | #define GS_VERSION_NAME			GS_LONG_NAME " " GS_VERSION_STR
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | /*-------------------------------------------------------------------------*/ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-18 17:41:02 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Kbuild is not very cooperative with respect to linking separately | 
					
						
							|  |  |  |  * compiled library objects into one module.  So for now we won't use | 
					
						
							|  |  |  |  * separate compilation ... ensuring init/exit sections work to shrink | 
					
						
							|  |  |  |  * the runtime footprint, and giving us at least some parts of what | 
					
						
							|  |  |  |  * a "gcc --combine ... part1.c part2.c part3.c ... " build would. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-08-18 17:39:30 -07:00
										 |  |  | #include "f_obex.c"
 | 
					
						
							| 
									
										
										
										
											2008-08-18 17:41:02 -07:00
										 |  |  | #include "f_serial.c"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*-------------------------------------------------------------------------*/ | 
					
						
							| 
									
										
										
										
											2012-09-10 15:01:53 +02:00
										 |  |  | USB_GADGET_COMPOSITE_OPTIONS(); | 
					
						
							| 
									
										
										
										
											2008-08-18 17:41:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /* Thanks to NetChip Technologies for donating this product ID.
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | * | 
					
						
							|  |  |  | * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!! | 
					
						
							|  |  |  | * Instead:  allocate your own, using normal USB-IF procedures. | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #define GS_VENDOR_ID			0x0525	/* NetChip */
 | 
					
						
							|  |  |  | #define GS_PRODUCT_ID			0xa4a6	/* Linux-USB Serial Gadget */
 | 
					
						
							|  |  |  | #define GS_CDC_PRODUCT_ID		0xa4a7	/* ... as CDC-ACM */
 | 
					
						
							| 
									
										
										
										
											2008-08-18 17:39:30 -07:00
										 |  |  | #define GS_CDC_OBEX_PRODUCT_ID		0xa4a9	/* ... as CDC-OBEX */
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | /* string IDs are assigned dynamically */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:21 +02:00
										 |  |  | #define STRING_DESCRIPTION_IDX		USB_GADGET_FIRST_AVAIL_IDX
 | 
					
						
							| 
									
										
										
										
											2008-06-19 17:52:07 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | static struct usb_string strings_dev[] = { | 
					
						
							| 
									
										
										
										
											2012-09-10 15:01:58 +02:00
										 |  |  | 	[USB_GADGET_MANUFACTURER_IDX].s = "", | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:21 +02:00
										 |  |  | 	[USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME, | 
					
						
							|  |  |  | 	[USB_GADGET_SERIAL_IDX].s = "", | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	[STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	{  } /* end of list */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | static struct usb_gadget_strings stringtab_dev = { | 
					
						
							|  |  |  | 	.language	= 0x0409,	/* en-us */ | 
					
						
							|  |  |  | 	.strings	= strings_dev, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | static struct usb_gadget_strings *dev_strings[] = { | 
					
						
							|  |  |  | 	&stringtab_dev, | 
					
						
							|  |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct usb_device_descriptor device_desc = { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	.bLength =		USB_DT_DEVICE_SIZE, | 
					
						
							|  |  |  | 	.bDescriptorType =	USB_DT_DEVICE, | 
					
						
							| 
									
										
										
										
											2009-02-11 14:11:36 -08:00
										 |  |  | 	.bcdUSB =		cpu_to_le16(0x0200), | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	/* .bDeviceClass = f(use_acm) */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	.bDeviceSubClass =	0, | 
					
						
							|  |  |  | 	.bDeviceProtocol =	0, | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	/* .bMaxPacketSize0 = f(hardware) */ | 
					
						
							| 
									
										
										
										
											2009-02-11 14:11:36 -08:00
										 |  |  | 	.idVendor =		cpu_to_le16(GS_VENDOR_ID), | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	/* .idProduct =	f(use_acm) */ | 
					
						
							| 
									
										
										
										
											2012-09-10 19:06:44 +02:00
										 |  |  | 	.bcdDevice = cpu_to_le16(GS_VERSION_NUM), | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	/* .iManufacturer = DYNAMIC */ | 
					
						
							|  |  |  | 	/* .iProduct = DYNAMIC */ | 
					
						
							|  |  |  | 	.bNumConfigurations =	1, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | static struct usb_otg_descriptor otg_descriptor = { | 
					
						
							|  |  |  | 	.bLength =		sizeof otg_descriptor, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	.bDescriptorType =	USB_DT_OTG, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	/* REVISIT SRP-only hardware is possible, although
 | 
					
						
							|  |  |  | 	 * it would not be called "OTG" ... | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	.bmAttributes =		USB_OTG_SRP | USB_OTG_HNP, | 
					
						
							| 
									
										
										
										
											2008-06-19 17:52:07 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | static const struct usb_descriptor_header *otg_desc[] = { | 
					
						
							|  |  |  | 	(struct usb_descriptor_header *) &otg_descriptor, | 
					
						
							| 
									
										
										
										
											2008-06-19 17:52:07 -07:00
										 |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 17:52:07 -07:00
										 |  |  | /*-------------------------------------------------------------------------*/ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 17:52:07 -07:00
										 |  |  | /* Module */ | 
					
						
							|  |  |  | MODULE_DESCRIPTION(GS_VERSION_NAME); | 
					
						
							|  |  |  | MODULE_AUTHOR("Al Borchers"); | 
					
						
							|  |  |  | MODULE_AUTHOR("David Brownell"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-13 09:32:20 +10:30
										 |  |  | static bool use_acm = true; | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | module_param(use_acm, bool, 0); | 
					
						
							|  |  |  | MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes"); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-13 09:32:20 +10:30
										 |  |  | static bool use_obex = false; | 
					
						
							| 
									
										
										
										
											2008-08-18 17:39:30 -07:00
										 |  |  | module_param(use_obex, bool, 0); | 
					
						
							|  |  |  | MODULE_PARM_DESC(use_obex, "Use CDC OBEX, default=no"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | static unsigned n_ports = 1; | 
					
						
							|  |  |  | module_param(n_ports, uint, 0); | 
					
						
							|  |  |  | MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); | 
					
						
							| 
									
										
										
										
											2008-05-07 16:00:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | /*-------------------------------------------------------------------------*/ | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:06 +01:00
										 |  |  | static unsigned char tty_lines[MAX_U_SERIAL_PORTS]; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:03 +01:00
										 |  |  | static int __init serial_bind_obex_config(struct usb_configuration *c) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned i; | 
					
						
							|  |  |  | 	int status = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < n_ports && status == 0; i++) | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:06 +01:00
										 |  |  | 		status = obex_bind_config(c, tty_lines[i]); | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:03 +01:00
										 |  |  | 	return status; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init serial_bind_gser_config(struct usb_configuration *c) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned i; | 
					
						
							|  |  |  | 	int status = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < n_ports && status == 0; i++) | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:06 +01:00
										 |  |  | 		status = gser_bind_config(c, tty_lines[i]); | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	return status; | 
					
						
							| 
									
										
										
										
											2008-05-07 16:00:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | static struct usb_configuration serial_config_driver = { | 
					
						
							|  |  |  | 	/* .label = f(use_acm) */ | 
					
						
							|  |  |  | 	/* .bConfigurationValue = f(use_acm) */ | 
					
						
							|  |  |  | 	/* .iConfiguration = DYNAMIC */ | 
					
						
							|  |  |  | 	.bmAttributes	= USB_CONFIG_ATT_SELFPOWER, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:07 +01:00
										 |  |  | static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS]; | 
					
						
							|  |  |  | static struct usb_function *f_serial[MAX_U_SERIAL_PORTS]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int serial_register_ports(struct usb_composite_dev *cdev, | 
					
						
							|  |  |  | 		struct usb_configuration *c, const char *f_name) | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:06 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:07 +01:00
										 |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = usb_add_config_only(cdev, c); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < n_ports; i++) { | 
					
						
							|  |  |  | 		struct f_serial_opts *opts; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		fi_serial[i] = usb_get_function_instance(f_name); | 
					
						
							|  |  |  | 		if (IS_ERR(fi_serial[i])) { | 
					
						
							|  |  |  | 			ret = PTR_ERR(fi_serial[i]); | 
					
						
							|  |  |  | 			goto fail; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		opts = container_of(fi_serial[i], struct f_serial_opts, func_inst); | 
					
						
							|  |  |  | 		opts->port_num = tty_lines[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		f_serial[i] = usb_get_function(fi_serial[i]); | 
					
						
							|  |  |  | 		if (IS_ERR(f_serial[i])) { | 
					
						
							|  |  |  | 			ret = PTR_ERR(f_serial[i]); | 
					
						
							|  |  |  | 			goto err_get_func; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ret = usb_add_function(c, f_serial[i]); | 
					
						
							|  |  |  | 		if (ret) | 
					
						
							|  |  |  | 			goto err_add_func; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:06 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | err_add_func: | 
					
						
							|  |  |  | 	usb_put_function(f_serial[i]); | 
					
						
							|  |  |  | err_get_func: | 
					
						
							|  |  |  | 	usb_put_function_instance(fi_serial[i]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | fail: | 
					
						
							|  |  |  | 	i--; | 
					
						
							|  |  |  | 	while (i >= 0) { | 
					
						
							|  |  |  | 		usb_remove_function(c, f_serial[i]); | 
					
						
							|  |  |  | 		usb_put_function(f_serial[i]); | 
					
						
							|  |  |  | 		usb_put_function_instance(fi_serial[i]); | 
					
						
							|  |  |  | 		i--; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-12 17:43:52 +02:00
										 |  |  | static int __init gs_bind(struct usb_composite_dev *cdev) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	int			status; | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:06 +01:00
										 |  |  | 	int			cur_line; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:06 +01:00
										 |  |  | 	for (cur_line = 0; cur_line < n_ports; cur_line++) { | 
					
						
							|  |  |  | 		status = gserial_alloc_line(&tty_lines[cur_line]); | 
					
						
							|  |  |  | 		if (status) | 
					
						
							|  |  |  | 			goto fail; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-06-19 17:52:07 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	/* Allocate string descriptor numbers ... note that string
 | 
					
						
							|  |  |  | 	 * contents can be overridden by the composite_dev glue. | 
					
						
							| 
									
										
										
										
											2005-07-13 15:18:30 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:16 +02:00
										 |  |  | 	status = usb_string_ids_tab(cdev, strings_dev); | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	if (status < 0) | 
					
						
							|  |  |  | 		goto fail; | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:21 +02:00
										 |  |  | 	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; | 
					
						
							|  |  |  | 	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:16 +02:00
										 |  |  | 	status = strings_dev[STRING_DESCRIPTION_IDX].id; | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	serial_config_driver.iConfiguration = status; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	if (gadget_is_otg(cdev->gadget)) { | 
					
						
							|  |  |  | 		serial_config_driver.descriptors = otg_desc; | 
					
						
							|  |  |  | 		serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-05-07 16:00:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	/* register our configuration */ | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:07 +01:00
										 |  |  | 	if (use_acm) { | 
					
						
							|  |  |  | 		status  = serial_register_ports(cdev, &serial_config_driver, | 
					
						
							|  |  |  | 				"acm"); | 
					
						
							|  |  |  | 		usb_ep_autoconfig_reset(cdev->gadget); | 
					
						
							|  |  |  | 	} else if (use_obex) | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:03 +01:00
										 |  |  | 		status = usb_add_config(cdev, &serial_config_driver, | 
					
						
							|  |  |  | 				serial_bind_obex_config); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		status = usb_add_config(cdev, &serial_config_driver, | 
					
						
							|  |  |  | 				serial_bind_gser_config); | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	if (status < 0) | 
					
						
							|  |  |  | 		goto fail; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-10 15:01:53 +02:00
										 |  |  | 	usb_composite_overwrite_options(cdev, &coverwrite); | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	INFO(cdev, "%s\n", GS_VERSION_NAME); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | fail: | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:06 +01:00
										 |  |  | 	cur_line--; | 
					
						
							|  |  |  | 	while (cur_line >= 0) | 
					
						
							|  |  |  | 		gserial_free_line(tty_lines[cur_line--]); | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	return status; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:07 +01:00
										 |  |  | static int gs_unbind(struct usb_composite_dev *cdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < n_ports; i++) { | 
					
						
							|  |  |  | 		usb_put_function(f_serial[i]); | 
					
						
							|  |  |  | 		usb_put_function_instance(fi_serial[i]); | 
					
						
							|  |  |  | 		gserial_free_line(tty_lines[i]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:03 +02:00
										 |  |  | static __refdata struct usb_composite_driver gserial_driver = { | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	.name		= "g_serial", | 
					
						
							|  |  |  | 	.dev		= &device_desc, | 
					
						
							|  |  |  | 	.strings	= dev_strings, | 
					
						
							| 
									
										
										
										
											2012-02-06 18:46:36 +01:00
										 |  |  | 	.max_speed	= USB_SPEED_SUPER, | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:04 +02:00
										 |  |  | 	.bind		= gs_bind, | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:06 +01:00
										 |  |  | 	.unbind		= gs_unbind, | 
					
						
							| 
									
										
										
										
											2008-05-07 16:00:36 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | static int __init init(void) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	/* We *could* export two configs; that'd be much cleaner...
 | 
					
						
							|  |  |  | 	 * but neither of these product IDs was defined that way. | 
					
						
							| 
									
										
										
										
											2008-04-18 17:37:49 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (use_acm) { | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 		serial_config_driver.label = "CDC ACM config"; | 
					
						
							|  |  |  | 		serial_config_driver.bConfigurationValue = 2; | 
					
						
							|  |  |  | 		device_desc.bDeviceClass = USB_CLASS_COMM; | 
					
						
							|  |  |  | 		device_desc.idProduct = | 
					
						
							| 
									
										
										
										
											2009-02-11 14:11:36 -08:00
										 |  |  | 				cpu_to_le16(GS_CDC_PRODUCT_ID); | 
					
						
							| 
									
										
										
										
											2008-08-18 17:39:30 -07:00
										 |  |  | 	} else if (use_obex) { | 
					
						
							|  |  |  | 		serial_config_driver.label = "CDC OBEX config"; | 
					
						
							|  |  |  | 		serial_config_driver.bConfigurationValue = 3; | 
					
						
							|  |  |  | 		device_desc.bDeviceClass = USB_CLASS_COMM; | 
					
						
							|  |  |  | 		device_desc.idProduct = | 
					
						
							| 
									
										
										
										
											2009-02-11 14:11:36 -08:00
										 |  |  | 			cpu_to_le16(GS_CDC_OBEX_PRODUCT_ID); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 		serial_config_driver.label = "Generic Serial config"; | 
					
						
							|  |  |  | 		serial_config_driver.bConfigurationValue = 1; | 
					
						
							|  |  |  | 		device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; | 
					
						
							|  |  |  | 		device_desc.idProduct = | 
					
						
							| 
									
										
										
										
											2009-02-11 14:11:36 -08:00
										 |  |  | 				cpu_to_le16(GS_PRODUCT_ID); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; | 
					
						
							| 
									
										
										
										
											2008-05-07 16:00:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:04 +02:00
										 |  |  | 	return usb_composite_probe(&gserial_driver); | 
					
						
							| 
									
										
										
										
											2008-05-07 16:00:36 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | module_init(init); | 
					
						
							| 
									
										
										
										
											2008-05-07 16:00:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | static void __exit cleanup(void) | 
					
						
							| 
									
										
										
										
											2008-05-07 16:00:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | 	usb_composite_unregister(&gserial_driver); | 
					
						
							| 
									
										
										
										
											2008-05-07 16:00:36 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2008-06-19 18:19:03 -07:00
										 |  |  | module_exit(cleanup); |