161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/*
261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * f_serial.c - generic USB serial function driver
361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell *
461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Copyright (C) 2008 by David Brownell
661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Copyright (C) 2008 by Nokia Corporation
761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell *
861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * This software is distributed under the terms of the GNU General
961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Public License ("GPL") as published by the Free Software Foundation,
1061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * either version 2 of that License or (at your option) any later version.
1161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell */
1261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
135a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
1461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell#include <linux/kernel.h>
1561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell#include <linux/device.h>
1661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
1761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell#include "u_serial.h"
1861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell#include "gadget_chips.h"
1961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
2061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
2161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/*
2261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * This function packages a simple "generic serial" port with no real
2361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * control mechanisms, just raw data transfer over two bulk endpoints.
2461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell *
2561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Because it's not standardized, this isn't as interoperable as the
2661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * CDC ACM driver.  However, for many purposes it's just as functional
2761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * if you can arrange appropriate host side drivers.
2861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell */
2961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
3061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstruct f_gser {
3161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	struct gserial			port;
3261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	u8				data_id;
3361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	u8				port_num;
3461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell};
3561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
3661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic inline struct f_gser *func_to_gser(struct usb_function *f)
3761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{
3861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	return container_of(f, struct f_gser, port.func);
3961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}
4061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
4161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/*-------------------------------------------------------------------------*/
4261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
4361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* interface descriptor: */
4461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
4561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_interface_descriptor gser_interface_desc __initdata = {
4661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bLength =		USB_DT_INTERFACE_SIZE,
4761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bDescriptorType =	USB_DT_INTERFACE,
4861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	/* .bInterfaceNumber = DYNAMIC */
4961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bNumEndpoints =	2,
5061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
5161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bInterfaceSubClass =	0,
5261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bInterfaceProtocol =	0,
5361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	/* .iInterface = DYNAMIC */
5461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell};
5561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
5661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* full speed support: */
5761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
5861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_endpoint_descriptor gser_fs_in_desc __initdata = {
5961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bLength =		USB_DT_ENDPOINT_SIZE,
6061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bDescriptorType =	USB_DT_ENDPOINT,
6161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bEndpointAddress =	USB_DIR_IN,
6261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
6361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell};
6461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
6561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_endpoint_descriptor gser_fs_out_desc __initdata = {
6661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bLength =		USB_DT_ENDPOINT_SIZE,
6761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bDescriptorType =	USB_DT_ENDPOINT,
6861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bEndpointAddress =	USB_DIR_OUT,
6961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
7061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell};
7161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
7261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_descriptor_header *gser_fs_function[] __initdata = {
7361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	(struct usb_descriptor_header *) &gser_interface_desc,
7461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	(struct usb_descriptor_header *) &gser_fs_in_desc,
7561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	(struct usb_descriptor_header *) &gser_fs_out_desc,
7661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	NULL,
7761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell};
7861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
7961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* high speed support: */
8061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
8161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_endpoint_descriptor gser_hs_in_desc __initdata = {
8261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bLength =		USB_DT_ENDPOINT_SIZE,
8361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bDescriptorType =	USB_DT_ENDPOINT,
8461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
85551509d267905705f6d723e51ec706916f06b859Harvey Harrison	.wMaxPacketSize =	cpu_to_le16(512),
8661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell};
8761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
8861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_endpoint_descriptor gser_hs_out_desc __initdata = {
8961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bLength =		USB_DT_ENDPOINT_SIZE,
9061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bDescriptorType =	USB_DT_ENDPOINT,
9161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
92551509d267905705f6d723e51ec706916f06b859Harvey Harrison	.wMaxPacketSize =	cpu_to_le16(512),
9361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell};
9461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
9561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_descriptor_header *gser_hs_function[] __initdata = {
9661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	(struct usb_descriptor_header *) &gser_interface_desc,
9761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	(struct usb_descriptor_header *) &gser_hs_in_desc,
9861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	(struct usb_descriptor_header *) &gser_hs_out_desc,
9961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	NULL,
10061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell};
10161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
1026fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewiorstatic struct usb_endpoint_descriptor gser_ss_in_desc __initdata = {
1036fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	.bLength =		USB_DT_ENDPOINT_SIZE,
1046fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	.bDescriptorType =	USB_DT_ENDPOINT,
1056fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
1066fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	.wMaxPacketSize =	cpu_to_le16(1024),
1076fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior};
1086fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior
1096fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewiorstatic struct usb_endpoint_descriptor gser_ss_out_desc __initdata = {
1106fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	.bLength =		USB_DT_ENDPOINT_SIZE,
1116fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	.bDescriptorType =	USB_DT_ENDPOINT,
1126fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
1136fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	.wMaxPacketSize =	cpu_to_le16(1024),
1146fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior};
1156fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior
1166fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewiorstatic struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc __initdata = {
1176fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	.bLength =              sizeof gser_ss_bulk_comp_desc,
1186fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
1196fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior};
1206fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior
1216fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewiorstatic struct usb_descriptor_header *gser_ss_function[] __initdata = {
1226fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	(struct usb_descriptor_header *) &gser_interface_desc,
1236fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	(struct usb_descriptor_header *) &gser_ss_in_desc,
1246fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
1256fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	(struct usb_descriptor_header *) &gser_ss_out_desc,
1266fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
1276fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	NULL,
1286fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior};
1296fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior
13061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* string descriptors: */
13161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
13261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_string gser_string_defs[] = {
13361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	[0].s = "Generic Serial",
13461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	{  } /* end of list */
13561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell};
13661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
13761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_gadget_strings gser_string_table = {
13861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.language =		0x0409,	/* en-us */
13961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	.strings =		gser_string_defs,
14061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell};
14161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
14261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_gadget_strings *gser_strings[] = {
14361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	&gser_string_table,
14461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	NULL,
14561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell};
14661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
14761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/*-------------------------------------------------------------------------*/
14861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
14961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
15061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{
15161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	struct f_gser		*gser = func_to_gser(f);
15261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	struct usb_composite_dev *cdev = f->config->cdev;
15361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
15461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	/* we know alt == 0, so this is an activation or a reset */
15561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
15661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	if (gser->port.in->driver_data) {
15761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
15861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		gserial_disconnect(&gser->port);
159ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman	}
160ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman	if (!gser->port.in->desc || !gser->port.out->desc) {
16161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
162fef6964452abae55c82e1615ac505b8377df00e9Robert Jarzmik		if (config_ep_by_speed(cdev->gadget, f, gser->port.in) ||
163fef6964452abae55c82e1615ac505b8377df00e9Robert Jarzmik		    config_ep_by_speed(cdev->gadget, f, gser->port.out)) {
164ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman			gser->port.in->desc = NULL;
165ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman			gser->port.out->desc = NULL;
166ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman			return -EINVAL;
167ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman		}
16861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	}
16961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gserial_connect(&gser->port, gser->port_num);
17061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	return 0;
17161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}
17261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
17361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic void gser_disable(struct usb_function *f)
17461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{
17561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	struct f_gser	*gser = func_to_gser(f);
17661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	struct usb_composite_dev *cdev = f->config->cdev;
17761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
17861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
17961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gserial_disconnect(&gser->port);
18061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}
18161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
18261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/*-------------------------------------------------------------------------*/
18361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
18461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* serial function driver setup/binding */
18561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
18661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic int __init
18761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellgser_bind(struct usb_configuration *c, struct usb_function *f)
18861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{
18961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	struct usb_composite_dev *cdev = c->cdev;
19061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	struct f_gser		*gser = func_to_gser(f);
19161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	int			status;
19261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	struct usb_ep		*ep;
19361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
19461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	/* allocate instance-specific interface IDs */
19561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	status = usb_interface_id(c, f);
19661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	if (status < 0)
19761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		goto fail;
19861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser->data_id = status;
19961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser_interface_desc.bInterfaceNumber = status;
20061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
20161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	status = -ENODEV;
20261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
20361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	/* allocate instance-specific endpoints */
20461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc);
20561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	if (!ep)
20661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		goto fail;
20761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser->port.in = ep;
20861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	ep->driver_data = cdev;	/* claim */
20961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
21061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc);
21161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	if (!ep)
21261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		goto fail;
21361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser->port.out = ep;
21461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	ep->driver_data = cdev;	/* claim */
21561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
21661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	/* copy descriptors, and track endpoint copies */
21761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	f->descriptors = usb_copy_descriptors(gser_fs_function);
21861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
21961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	/* support all relevant hardware speeds... we expect that when
22061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	 * hardware is dual speed, all bulk-capable endpoints work at
22161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	 * both speeds
22261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	 */
22361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	if (gadget_is_dualspeed(c->cdev->gadget)) {
22461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		gser_hs_in_desc.bEndpointAddress =
22561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell				gser_fs_in_desc.bEndpointAddress;
22661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		gser_hs_out_desc.bEndpointAddress =
22761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell				gser_fs_out_desc.bEndpointAddress;
22861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
22961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		/* copy descriptors, and track endpoint copies */
23061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		f->hs_descriptors = usb_copy_descriptors(gser_hs_function);
23161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	}
2326fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	if (gadget_is_superspeed(c->cdev->gadget)) {
2336fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior		gser_ss_in_desc.bEndpointAddress =
2346fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior			gser_fs_in_desc.bEndpointAddress;
2356fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior		gser_ss_out_desc.bEndpointAddress =
2366fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior			gser_fs_out_desc.bEndpointAddress;
2376fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior
2386fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior		/* copy descriptors, and track endpoint copies */
2396fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior		f->ss_descriptors = usb_copy_descriptors(gser_ss_function);
2406fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior		if (!f->ss_descriptors)
2416fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior			goto fail;
2426fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	}
24361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
24461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
24561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell			gser->port_num,
2466fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior			gadget_is_superspeed(c->cdev->gadget) ? "super" :
24761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
24861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell			gser->port.in->name, gser->port.out->name);
24961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	return 0;
25061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
25161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellfail:
25261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	/* we might as well release our claims on endpoints */
25361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	if (gser->port.out)
25461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		gser->port.out->driver_data = NULL;
25561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	if (gser->port.in)
25661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		gser->port.in->driver_data = NULL;
25761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
25861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
25961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
26061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	return status;
26161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}
26261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
26361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic void
26461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellgser_unbind(struct usb_configuration *c, struct usb_function *f)
26561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{
26661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	if (gadget_is_dualspeed(c->cdev->gadget))
26761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		usb_free_descriptors(f->hs_descriptors);
2686fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior	if (gadget_is_superspeed(c->cdev->gadget))
2696fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior		usb_free_descriptors(f->ss_descriptors);
27061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	usb_free_descriptors(f->descriptors);
27161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	kfree(func_to_gser(f));
27261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}
27361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
27461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/**
27561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * gser_bind_config - add a generic serial function to a configuration
27661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * @c: the configuration to support the serial instance
27761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * @port_num: /dev/ttyGS* port this interface will use
27861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Context: single threaded during gadget setup
27961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell *
28061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Returns zero on success, else negative errno.
28161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell *
28261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Caller must have called @gserial_setup() with enough ports to
28361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * handle all the ones it binds.  Caller is also responsible
28461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * for calling @gserial_cleanup() before module unload.
28561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell */
28661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellint __init gser_bind_config(struct usb_configuration *c, u8 port_num)
28761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{
28861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	struct f_gser	*gser;
28961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	int		status;
29061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
29161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	/* REVISIT might want instance-specific strings to help
29261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	 * distinguish instances ...
29361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	 */
29461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
29561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	/* maybe allocate device-global string ID */
29661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	if (gser_string_defs[0].id == 0) {
29761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		status = usb_string_id(c->cdev);
29861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		if (status < 0)
29961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell			return status;
30061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		gser_string_defs[0].id = status;
30161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	}
30261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
30361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	/* allocate and initialize one new instance */
30461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser = kzalloc(sizeof *gser, GFP_KERNEL);
30561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	if (!gser)
30661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		return -ENOMEM;
30761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
30861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser->port_num = port_num;
30961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
31061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser->port.func.name = "gser";
31161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser->port.func.strings = gser_strings;
31261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser->port.func.bind = gser_bind;
31361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser->port.func.unbind = gser_unbind;
31461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser->port.func.set_alt = gser_set_alt;
31561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	gser->port.func.disable = gser_disable;
31661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell
31761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	status = usb_add_function(c, &gser->port.func);
31861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	if (status)
31961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell		kfree(gser);
32061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell	return status;
32161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}
322