162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * f_sourcesink.c - USB peripheral source/sink configuration driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2003-2008 David Brownell
662306a36Sopenharmony_ci * Copyright (C) 2008 by Nokia Corporation
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/* #define VERBOSE_DEBUG */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/device.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/usb/composite.h>
1662306a36Sopenharmony_ci#include <linux/err.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "g_zero.h"
1962306a36Sopenharmony_ci#include "u_f.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
2362306a36Sopenharmony_ci * controller drivers.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * This just sinks bulk packets OUT to the peripheral and sources them IN
2662306a36Sopenharmony_ci * to the host, optionally with specific data patterns for integrity tests.
2762306a36Sopenharmony_ci * As such it supports basic functionality and load tests.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * In terms of control messaging, this supports all the standard requests
3062306a36Sopenharmony_ci * plus two that support control-OUT tests.  If the optional "autoresume"
3162306a36Sopenharmony_ci * mode is enabled, it provides good functional coverage for the "USBCV"
3262306a36Sopenharmony_ci * test harness from USB-IF.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_cistruct f_sourcesink {
3562306a36Sopenharmony_ci	struct usb_function	function;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	struct usb_ep		*in_ep;
3862306a36Sopenharmony_ci	struct usb_ep		*out_ep;
3962306a36Sopenharmony_ci	struct usb_ep		*iso_in_ep;
4062306a36Sopenharmony_ci	struct usb_ep		*iso_out_ep;
4162306a36Sopenharmony_ci	int			cur_alt;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	unsigned pattern;
4462306a36Sopenharmony_ci	unsigned isoc_interval;
4562306a36Sopenharmony_ci	unsigned isoc_maxpacket;
4662306a36Sopenharmony_ci	unsigned isoc_mult;
4762306a36Sopenharmony_ci	unsigned isoc_maxburst;
4862306a36Sopenharmony_ci	unsigned buflen;
4962306a36Sopenharmony_ci	unsigned bulk_qlen;
5062306a36Sopenharmony_ci	unsigned iso_qlen;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic inline struct f_sourcesink *func_to_ss(struct usb_function *f)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	return container_of(f, struct f_sourcesink, function);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic struct usb_interface_descriptor source_sink_intf_alt0 = {
6162306a36Sopenharmony_ci	.bLength =		USB_DT_INTERFACE_SIZE,
6262306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	.bAlternateSetting =	0,
6562306a36Sopenharmony_ci	.bNumEndpoints =	2,
6662306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
6762306a36Sopenharmony_ci	/* .iInterface		= DYNAMIC */
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic struct usb_interface_descriptor source_sink_intf_alt1 = {
7162306a36Sopenharmony_ci	.bLength =		USB_DT_INTERFACE_SIZE,
7262306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	.bAlternateSetting =	1,
7562306a36Sopenharmony_ci	.bNumEndpoints =	4,
7662306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
7762306a36Sopenharmony_ci	/* .iInterface		= DYNAMIC */
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* full speed support: */
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_source_desc = {
8362306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
8462306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
8762306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_sink_desc = {
9162306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
9262306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_OUT,
9562306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_iso_source_desc = {
9962306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
10062306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
10362306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
10462306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1023),
10562306a36Sopenharmony_ci	.bInterval =		4,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic struct usb_endpoint_descriptor fs_iso_sink_desc = {
10962306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
11062306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_OUT,
11362306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
11462306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1023),
11562306a36Sopenharmony_ci	.bInterval =		4,
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic struct usb_descriptor_header *fs_source_sink_descs[] = {
11962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &source_sink_intf_alt0,
12062306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_sink_desc,
12162306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_source_desc,
12262306a36Sopenharmony_ci	(struct usb_descriptor_header *) &source_sink_intf_alt1,
12362306a36Sopenharmony_ci#define FS_ALT_IFC_1_OFFSET	3
12462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_sink_desc,
12562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_source_desc,
12662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_iso_sink_desc,
12762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &fs_iso_source_desc,
12862306a36Sopenharmony_ci	NULL,
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/* high speed support: */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_source_desc = {
13462306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
13562306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
13862306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(512),
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_sink_desc = {
14262306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
14362306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
14662306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(512),
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_iso_source_desc = {
15062306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
15162306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
15462306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
15562306a36Sopenharmony_ci	.bInterval =		4,
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hs_iso_sink_desc = {
15962306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
16062306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
16362306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
16462306a36Sopenharmony_ci	.bInterval =		4,
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic struct usb_descriptor_header *hs_source_sink_descs[] = {
16862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &source_sink_intf_alt0,
16962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_source_desc,
17062306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_sink_desc,
17162306a36Sopenharmony_ci	(struct usb_descriptor_header *) &source_sink_intf_alt1,
17262306a36Sopenharmony_ci#define HS_ALT_IFC_1_OFFSET	3
17362306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_source_desc,
17462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_sink_desc,
17562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_iso_source_desc,
17662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &hs_iso_sink_desc,
17762306a36Sopenharmony_ci	NULL,
17862306a36Sopenharmony_ci};
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci/* super speed support: */
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_source_desc = {
18362306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
18462306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
18762306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
19162306a36Sopenharmony_ci	.bLength =		USB_DT_SS_EP_COMP_SIZE,
19262306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	.bMaxBurst =		0,
19562306a36Sopenharmony_ci	.bmAttributes =		0,
19662306a36Sopenharmony_ci	.wBytesPerInterval =	0,
19762306a36Sopenharmony_ci};
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_sink_desc = {
20062306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
20162306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
20462306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
20862306a36Sopenharmony_ci	.bLength =		USB_DT_SS_EP_COMP_SIZE,
20962306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	.bMaxBurst =		0,
21262306a36Sopenharmony_ci	.bmAttributes =		0,
21362306a36Sopenharmony_ci	.wBytesPerInterval =	0,
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_iso_source_desc = {
21762306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
21862306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
22162306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
22262306a36Sopenharmony_ci	.bInterval =		4,
22362306a36Sopenharmony_ci};
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = {
22662306a36Sopenharmony_ci	.bLength =		USB_DT_SS_EP_COMP_SIZE,
22762306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	.bMaxBurst =		0,
23062306a36Sopenharmony_ci	.bmAttributes =		0,
23162306a36Sopenharmony_ci	.wBytesPerInterval =	cpu_to_le16(1024),
23262306a36Sopenharmony_ci};
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor ss_iso_sink_desc = {
23562306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
23662306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
23962306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(1024),
24062306a36Sopenharmony_ci	.bInterval =		4,
24162306a36Sopenharmony_ci};
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
24462306a36Sopenharmony_ci	.bLength =		USB_DT_SS_EP_COMP_SIZE,
24562306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	.bMaxBurst =		0,
24862306a36Sopenharmony_ci	.bmAttributes =		0,
24962306a36Sopenharmony_ci	.wBytesPerInterval =	cpu_to_le16(1024),
25062306a36Sopenharmony_ci};
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic struct usb_descriptor_header *ss_source_sink_descs[] = {
25362306a36Sopenharmony_ci	(struct usb_descriptor_header *) &source_sink_intf_alt0,
25462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_source_desc,
25562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_source_comp_desc,
25662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_sink_desc,
25762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_sink_comp_desc,
25862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &source_sink_intf_alt1,
25962306a36Sopenharmony_ci#define SS_ALT_IFC_1_OFFSET	5
26062306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_source_desc,
26162306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_source_comp_desc,
26262306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_sink_desc,
26362306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_sink_comp_desc,
26462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_iso_source_desc,
26562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_iso_source_comp_desc,
26662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_iso_sink_desc,
26762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &ss_iso_sink_comp_desc,
26862306a36Sopenharmony_ci	NULL,
26962306a36Sopenharmony_ci};
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/* function-specific strings: */
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic struct usb_string strings_sourcesink[] = {
27462306a36Sopenharmony_ci	[0].s = "source and sink data",
27562306a36Sopenharmony_ci	{  }			/* end of list */
27662306a36Sopenharmony_ci};
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic struct usb_gadget_strings stringtab_sourcesink = {
27962306a36Sopenharmony_ci	.language	= 0x0409,	/* en-us */
28062306a36Sopenharmony_ci	.strings	= strings_sourcesink,
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic struct usb_gadget_strings *sourcesink_strings[] = {
28462306a36Sopenharmony_ci	&stringtab_sourcesink,
28562306a36Sopenharmony_ci	NULL,
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	return alloc_ep_req(ep, len);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	int			value;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	value = usb_ep_disable(ep);
30062306a36Sopenharmony_ci	if (value < 0)
30162306a36Sopenharmony_ci		DBG(cdev, "disable %s --> %d\n", ep->name, value);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_civoid disable_endpoints(struct usb_composite_dev *cdev,
30562306a36Sopenharmony_ci		struct usb_ep *in, struct usb_ep *out,
30662306a36Sopenharmony_ci		struct usb_ep *iso_in, struct usb_ep *iso_out)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	disable_ep(cdev, in);
30962306a36Sopenharmony_ci	disable_ep(cdev, out);
31062306a36Sopenharmony_ci	if (iso_in)
31162306a36Sopenharmony_ci		disable_ep(cdev, iso_in);
31262306a36Sopenharmony_ci	if (iso_out)
31362306a36Sopenharmony_ci		disable_ep(cdev, iso_out);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic int
31762306a36Sopenharmony_cisourcesink_bind(struct usb_configuration *c, struct usb_function *f)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct usb_composite_dev *cdev = c->cdev;
32062306a36Sopenharmony_ci	struct f_sourcesink	*ss = func_to_ss(f);
32162306a36Sopenharmony_ci	int	id;
32262306a36Sopenharmony_ci	int ret;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/* allocate interface ID(s) */
32562306a36Sopenharmony_ci	id = usb_interface_id(c, f);
32662306a36Sopenharmony_ci	if (id < 0)
32762306a36Sopenharmony_ci		return id;
32862306a36Sopenharmony_ci	source_sink_intf_alt0.bInterfaceNumber = id;
32962306a36Sopenharmony_ci	source_sink_intf_alt1.bInterfaceNumber = id;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* allocate bulk endpoints */
33262306a36Sopenharmony_ci	ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
33362306a36Sopenharmony_ci	if (!ss->in_ep) {
33462306a36Sopenharmony_ciautoconf_fail:
33562306a36Sopenharmony_ci		ERROR(cdev, "%s: can't autoconfigure on %s\n",
33662306a36Sopenharmony_ci			f->name, cdev->gadget->name);
33762306a36Sopenharmony_ci		return -ENODEV;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
34162306a36Sopenharmony_ci	if (!ss->out_ep)
34262306a36Sopenharmony_ci		goto autoconf_fail;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* sanity check the isoc module parameters */
34562306a36Sopenharmony_ci	if (ss->isoc_interval < 1)
34662306a36Sopenharmony_ci		ss->isoc_interval = 1;
34762306a36Sopenharmony_ci	if (ss->isoc_interval > 16)
34862306a36Sopenharmony_ci		ss->isoc_interval = 16;
34962306a36Sopenharmony_ci	if (ss->isoc_mult > 2)
35062306a36Sopenharmony_ci		ss->isoc_mult = 2;
35162306a36Sopenharmony_ci	if (ss->isoc_maxburst > 15)
35262306a36Sopenharmony_ci		ss->isoc_maxburst = 15;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* fill in the FS isoc descriptors from the module parameters */
35562306a36Sopenharmony_ci	fs_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket > 1023 ?
35662306a36Sopenharmony_ci						1023 : ss->isoc_maxpacket;
35762306a36Sopenharmony_ci	fs_iso_source_desc.bInterval = ss->isoc_interval;
35862306a36Sopenharmony_ci	fs_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket > 1023 ?
35962306a36Sopenharmony_ci						1023 : ss->isoc_maxpacket;
36062306a36Sopenharmony_ci	fs_iso_sink_desc.bInterval = ss->isoc_interval;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* allocate iso endpoints */
36362306a36Sopenharmony_ci	ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc);
36462306a36Sopenharmony_ci	if (!ss->iso_in_ep)
36562306a36Sopenharmony_ci		goto no_iso;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc);
36862306a36Sopenharmony_ci	if (!ss->iso_out_ep) {
36962306a36Sopenharmony_ci		usb_ep_autoconfig_release(ss->iso_in_ep);
37062306a36Sopenharmony_ci		ss->iso_in_ep = NULL;
37162306a36Sopenharmony_cino_iso:
37262306a36Sopenharmony_ci		/*
37362306a36Sopenharmony_ci		 * We still want to work even if the UDC doesn't have isoc
37462306a36Sopenharmony_ci		 * endpoints, so null out the alt interface that contains
37562306a36Sopenharmony_ci		 * them and continue.
37662306a36Sopenharmony_ci		 */
37762306a36Sopenharmony_ci		fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL;
37862306a36Sopenharmony_ci		hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL;
37962306a36Sopenharmony_ci		ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (ss->isoc_maxpacket > 1024)
38362306a36Sopenharmony_ci		ss->isoc_maxpacket = 1024;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/* support high speed hardware */
38662306a36Sopenharmony_ci	hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
38762306a36Sopenharmony_ci	hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/*
39062306a36Sopenharmony_ci	 * Fill in the HS isoc descriptors from the module parameters.
39162306a36Sopenharmony_ci	 * We assume that the user knows what they are doing and won't
39262306a36Sopenharmony_ci	 * give parameters that their UDC doesn't support.
39362306a36Sopenharmony_ci	 */
39462306a36Sopenharmony_ci	hs_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket;
39562306a36Sopenharmony_ci	hs_iso_source_desc.wMaxPacketSize |= ss->isoc_mult << 11;
39662306a36Sopenharmony_ci	hs_iso_source_desc.bInterval = ss->isoc_interval;
39762306a36Sopenharmony_ci	hs_iso_source_desc.bEndpointAddress =
39862306a36Sopenharmony_ci		fs_iso_source_desc.bEndpointAddress;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	hs_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket;
40162306a36Sopenharmony_ci	hs_iso_sink_desc.wMaxPacketSize |= ss->isoc_mult << 11;
40262306a36Sopenharmony_ci	hs_iso_sink_desc.bInterval = ss->isoc_interval;
40362306a36Sopenharmony_ci	hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* support super speed hardware */
40662306a36Sopenharmony_ci	ss_source_desc.bEndpointAddress =
40762306a36Sopenharmony_ci		fs_source_desc.bEndpointAddress;
40862306a36Sopenharmony_ci	ss_sink_desc.bEndpointAddress =
40962306a36Sopenharmony_ci		fs_sink_desc.bEndpointAddress;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	/*
41262306a36Sopenharmony_ci	 * Fill in the SS isoc descriptors from the module parameters.
41362306a36Sopenharmony_ci	 * We assume that the user knows what they are doing and won't
41462306a36Sopenharmony_ci	 * give parameters that their UDC doesn't support.
41562306a36Sopenharmony_ci	 */
41662306a36Sopenharmony_ci	ss_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket;
41762306a36Sopenharmony_ci	ss_iso_source_desc.bInterval = ss->isoc_interval;
41862306a36Sopenharmony_ci	ss_iso_source_comp_desc.bmAttributes = ss->isoc_mult;
41962306a36Sopenharmony_ci	ss_iso_source_comp_desc.bMaxBurst = ss->isoc_maxburst;
42062306a36Sopenharmony_ci	ss_iso_source_comp_desc.wBytesPerInterval = ss->isoc_maxpacket *
42162306a36Sopenharmony_ci		(ss->isoc_mult + 1) * (ss->isoc_maxburst + 1);
42262306a36Sopenharmony_ci	ss_iso_source_desc.bEndpointAddress =
42362306a36Sopenharmony_ci		fs_iso_source_desc.bEndpointAddress;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	ss_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket;
42662306a36Sopenharmony_ci	ss_iso_sink_desc.bInterval = ss->isoc_interval;
42762306a36Sopenharmony_ci	ss_iso_sink_comp_desc.bmAttributes = ss->isoc_mult;
42862306a36Sopenharmony_ci	ss_iso_sink_comp_desc.bMaxBurst = ss->isoc_maxburst;
42962306a36Sopenharmony_ci	ss_iso_sink_comp_desc.wBytesPerInterval = ss->isoc_maxpacket *
43062306a36Sopenharmony_ci		(ss->isoc_mult + 1) * (ss->isoc_maxburst + 1);
43162306a36Sopenharmony_ci	ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	ret = usb_assign_descriptors(f, fs_source_sink_descs,
43462306a36Sopenharmony_ci			hs_source_sink_descs, ss_source_sink_descs,
43562306a36Sopenharmony_ci			ss_source_sink_descs);
43662306a36Sopenharmony_ci	if (ret)
43762306a36Sopenharmony_ci		return ret;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	DBG(cdev, "%s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
44062306a36Sopenharmony_ci			f->name, ss->in_ep->name, ss->out_ep->name,
44162306a36Sopenharmony_ci			ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
44262306a36Sopenharmony_ci			ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
44362306a36Sopenharmony_ci	return 0;
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic void
44762306a36Sopenharmony_cisourcesink_free_func(struct usb_function *f)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct f_ss_opts *opts;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	opts = container_of(f->fi, struct f_ss_opts, func_inst);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	mutex_lock(&opts->lock);
45462306a36Sopenharmony_ci	opts->refcnt--;
45562306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	usb_free_all_descriptors(f);
45862306a36Sopenharmony_ci	kfree(func_to_ss(f));
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/* optionally require specific source/sink data patterns  */
46262306a36Sopenharmony_cistatic int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	unsigned		i;
46562306a36Sopenharmony_ci	u8			*buf = req->buf;
46662306a36Sopenharmony_ci	struct usb_composite_dev *cdev = ss->function.config->cdev;
46762306a36Sopenharmony_ci	int max_packet_size = le16_to_cpu(ss->out_ep->desc->wMaxPacketSize);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (ss->pattern == 2)
47062306a36Sopenharmony_ci		return 0;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	for (i = 0; i < req->actual; i++, buf++) {
47362306a36Sopenharmony_ci		switch (ss->pattern) {
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		/* all-zeroes has no synchronization issues */
47662306a36Sopenharmony_ci		case 0:
47762306a36Sopenharmony_ci			if (*buf == 0)
47862306a36Sopenharmony_ci				continue;
47962306a36Sopenharmony_ci			break;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		/* "mod63" stays in sync with short-terminated transfers,
48262306a36Sopenharmony_ci		 * OR otherwise when host and gadget agree on how large
48362306a36Sopenharmony_ci		 * each usb transfer request should be.  Resync is done
48462306a36Sopenharmony_ci		 * with set_interface or set_config.  (We *WANT* it to
48562306a36Sopenharmony_ci		 * get quickly out of sync if controllers or their drivers
48662306a36Sopenharmony_ci		 * stutter for any reason, including buffer duplication...)
48762306a36Sopenharmony_ci		 */
48862306a36Sopenharmony_ci		case 1:
48962306a36Sopenharmony_ci			if (*buf == (u8)((i % max_packet_size) % 63))
49062306a36Sopenharmony_ci				continue;
49162306a36Sopenharmony_ci			break;
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci		ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
49462306a36Sopenharmony_ci		usb_ep_set_halt(ss->out_ep);
49562306a36Sopenharmony_ci		return -EINVAL;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci	return 0;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	unsigned	i;
50362306a36Sopenharmony_ci	u8		*buf = req->buf;
50462306a36Sopenharmony_ci	int max_packet_size = le16_to_cpu(ep->desc->wMaxPacketSize);
50562306a36Sopenharmony_ci	struct f_sourcesink *ss = ep->driver_data;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	switch (ss->pattern) {
50862306a36Sopenharmony_ci	case 0:
50962306a36Sopenharmony_ci		memset(req->buf, 0, req->length);
51062306a36Sopenharmony_ci		break;
51162306a36Sopenharmony_ci	case 1:
51262306a36Sopenharmony_ci		for  (i = 0; i < req->length; i++)
51362306a36Sopenharmony_ci			*buf++ = (u8) ((i % max_packet_size) % 63);
51462306a36Sopenharmony_ci		break;
51562306a36Sopenharmony_ci	case 2:
51662306a36Sopenharmony_ci		break;
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct usb_composite_dev	*cdev;
52362306a36Sopenharmony_ci	struct f_sourcesink		*ss = ep->driver_data;
52462306a36Sopenharmony_ci	int				status = req->status;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/* driver_data will be null if ep has been disabled */
52762306a36Sopenharmony_ci	if (!ss)
52862306a36Sopenharmony_ci		return;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	cdev = ss->function.config->cdev;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	switch (status) {
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	case 0:				/* normal completion? */
53562306a36Sopenharmony_ci		if (ep == ss->out_ep) {
53662306a36Sopenharmony_ci			check_read_data(ss, req);
53762306a36Sopenharmony_ci			if (ss->pattern != 2)
53862306a36Sopenharmony_ci				memset(req->buf, 0x55, req->length);
53962306a36Sopenharmony_ci		}
54062306a36Sopenharmony_ci		break;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	/* this endpoint is normally active while we're configured */
54362306a36Sopenharmony_ci	case -ECONNABORTED:		/* hardware forced ep reset */
54462306a36Sopenharmony_ci	case -ECONNRESET:		/* request dequeued */
54562306a36Sopenharmony_ci	case -ESHUTDOWN:		/* disconnect from host */
54662306a36Sopenharmony_ci		VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
54762306a36Sopenharmony_ci				req->actual, req->length);
54862306a36Sopenharmony_ci		if (ep == ss->out_ep)
54962306a36Sopenharmony_ci			check_read_data(ss, req);
55062306a36Sopenharmony_ci		free_ep_req(ep, req);
55162306a36Sopenharmony_ci		return;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	case -EOVERFLOW:		/* buffer overrun on read means that
55462306a36Sopenharmony_ci					 * we didn't provide a big enough
55562306a36Sopenharmony_ci					 * buffer.
55662306a36Sopenharmony_ci					 */
55762306a36Sopenharmony_ci	default:
55862306a36Sopenharmony_ci#if 1
55962306a36Sopenharmony_ci		DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
56062306a36Sopenharmony_ci				status, req->actual, req->length);
56162306a36Sopenharmony_ci		break;
56262306a36Sopenharmony_ci#endif
56362306a36Sopenharmony_ci	case -EREMOTEIO:		/* short read */
56462306a36Sopenharmony_ci		break;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	status = usb_ep_queue(ep, req, GFP_ATOMIC);
56862306a36Sopenharmony_ci	if (status) {
56962306a36Sopenharmony_ci		ERROR(cdev, "kill %s:  resubmit %d bytes --> %d\n",
57062306a36Sopenharmony_ci				ep->name, req->length, status);
57162306a36Sopenharmony_ci		usb_ep_set_halt(ep);
57262306a36Sopenharmony_ci		/* FIXME recover later ... somehow */
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
57762306a36Sopenharmony_ci		bool is_iso, int speed)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct usb_ep		*ep;
58062306a36Sopenharmony_ci	struct usb_request	*req;
58162306a36Sopenharmony_ci	int			i, size, qlen, status = 0;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	if (is_iso) {
58462306a36Sopenharmony_ci		switch (speed) {
58562306a36Sopenharmony_ci		case USB_SPEED_SUPER_PLUS:
58662306a36Sopenharmony_ci		case USB_SPEED_SUPER:
58762306a36Sopenharmony_ci			size = ss->isoc_maxpacket *
58862306a36Sopenharmony_ci					(ss->isoc_mult + 1) *
58962306a36Sopenharmony_ci					(ss->isoc_maxburst + 1);
59062306a36Sopenharmony_ci			break;
59162306a36Sopenharmony_ci		case USB_SPEED_HIGH:
59262306a36Sopenharmony_ci			size = ss->isoc_maxpacket * (ss->isoc_mult + 1);
59362306a36Sopenharmony_ci			break;
59462306a36Sopenharmony_ci		default:
59562306a36Sopenharmony_ci			size = ss->isoc_maxpacket > 1023 ?
59662306a36Sopenharmony_ci					1023 : ss->isoc_maxpacket;
59762306a36Sopenharmony_ci			break;
59862306a36Sopenharmony_ci		}
59962306a36Sopenharmony_ci		ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
60062306a36Sopenharmony_ci		qlen = ss->iso_qlen;
60162306a36Sopenharmony_ci	} else {
60262306a36Sopenharmony_ci		ep = is_in ? ss->in_ep : ss->out_ep;
60362306a36Sopenharmony_ci		qlen = ss->bulk_qlen;
60462306a36Sopenharmony_ci		size = ss->buflen;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	for (i = 0; i < qlen; i++) {
60862306a36Sopenharmony_ci		req = ss_alloc_ep_req(ep, size);
60962306a36Sopenharmony_ci		if (!req)
61062306a36Sopenharmony_ci			return -ENOMEM;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		req->complete = source_sink_complete;
61362306a36Sopenharmony_ci		if (is_in)
61462306a36Sopenharmony_ci			reinit_write_data(ep, req);
61562306a36Sopenharmony_ci		else if (ss->pattern != 2)
61662306a36Sopenharmony_ci			memset(req->buf, 0x55, req->length);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		status = usb_ep_queue(ep, req, GFP_ATOMIC);
61962306a36Sopenharmony_ci		if (status) {
62062306a36Sopenharmony_ci			struct usb_composite_dev	*cdev;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci			cdev = ss->function.config->cdev;
62362306a36Sopenharmony_ci			ERROR(cdev, "start %s%s %s --> %d\n",
62462306a36Sopenharmony_ci			      is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
62562306a36Sopenharmony_ci			      ep->name, status);
62662306a36Sopenharmony_ci			free_ep_req(ep, req);
62762306a36Sopenharmony_ci			return status;
62862306a36Sopenharmony_ci		}
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	return status;
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic void disable_source_sink(struct f_sourcesink *ss)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	struct usb_composite_dev	*cdev;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	cdev = ss->function.config->cdev;
63962306a36Sopenharmony_ci	disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
64062306a36Sopenharmony_ci			ss->iso_out_ep);
64162306a36Sopenharmony_ci	VDBG(cdev, "%s disabled\n", ss->function.name);
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic int
64562306a36Sopenharmony_cienable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
64662306a36Sopenharmony_ci		int alt)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	int					result = 0;
64962306a36Sopenharmony_ci	int					speed = cdev->gadget->speed;
65062306a36Sopenharmony_ci	struct usb_ep				*ep;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	/* one bulk endpoint writes (sources) zeroes IN (to the host) */
65362306a36Sopenharmony_ci	ep = ss->in_ep;
65462306a36Sopenharmony_ci	result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
65562306a36Sopenharmony_ci	if (result)
65662306a36Sopenharmony_ci		return result;
65762306a36Sopenharmony_ci	result = usb_ep_enable(ep);
65862306a36Sopenharmony_ci	if (result < 0)
65962306a36Sopenharmony_ci		return result;
66062306a36Sopenharmony_ci	ep->driver_data = ss;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	result = source_sink_start_ep(ss, true, false, speed);
66362306a36Sopenharmony_ci	if (result < 0) {
66462306a36Sopenharmony_cifail:
66562306a36Sopenharmony_ci		ep = ss->in_ep;
66662306a36Sopenharmony_ci		usb_ep_disable(ep);
66762306a36Sopenharmony_ci		return result;
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/* one bulk endpoint reads (sinks) anything OUT (from the host) */
67162306a36Sopenharmony_ci	ep = ss->out_ep;
67262306a36Sopenharmony_ci	result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
67362306a36Sopenharmony_ci	if (result)
67462306a36Sopenharmony_ci		goto fail;
67562306a36Sopenharmony_ci	result = usb_ep_enable(ep);
67662306a36Sopenharmony_ci	if (result < 0)
67762306a36Sopenharmony_ci		goto fail;
67862306a36Sopenharmony_ci	ep->driver_data = ss;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	result = source_sink_start_ep(ss, false, false, speed);
68162306a36Sopenharmony_ci	if (result < 0) {
68262306a36Sopenharmony_cifail2:
68362306a36Sopenharmony_ci		ep = ss->out_ep;
68462306a36Sopenharmony_ci		usb_ep_disable(ep);
68562306a36Sopenharmony_ci		goto fail;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	if (alt == 0)
68962306a36Sopenharmony_ci		goto out;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	/* one iso endpoint writes (sources) zeroes IN (to the host) */
69262306a36Sopenharmony_ci	ep = ss->iso_in_ep;
69362306a36Sopenharmony_ci	if (ep) {
69462306a36Sopenharmony_ci		result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
69562306a36Sopenharmony_ci		if (result)
69662306a36Sopenharmony_ci			goto fail2;
69762306a36Sopenharmony_ci		result = usb_ep_enable(ep);
69862306a36Sopenharmony_ci		if (result < 0)
69962306a36Sopenharmony_ci			goto fail2;
70062306a36Sopenharmony_ci		ep->driver_data = ss;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		result = source_sink_start_ep(ss, true, true, speed);
70362306a36Sopenharmony_ci		if (result < 0) {
70462306a36Sopenharmony_cifail3:
70562306a36Sopenharmony_ci			ep = ss->iso_in_ep;
70662306a36Sopenharmony_ci			if (ep)
70762306a36Sopenharmony_ci				usb_ep_disable(ep);
70862306a36Sopenharmony_ci			goto fail2;
70962306a36Sopenharmony_ci		}
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/* one iso endpoint reads (sinks) anything OUT (from the host) */
71362306a36Sopenharmony_ci	ep = ss->iso_out_ep;
71462306a36Sopenharmony_ci	if (ep) {
71562306a36Sopenharmony_ci		result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
71662306a36Sopenharmony_ci		if (result)
71762306a36Sopenharmony_ci			goto fail3;
71862306a36Sopenharmony_ci		result = usb_ep_enable(ep);
71962306a36Sopenharmony_ci		if (result < 0)
72062306a36Sopenharmony_ci			goto fail3;
72162306a36Sopenharmony_ci		ep->driver_data = ss;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		result = source_sink_start_ep(ss, false, true, speed);
72462306a36Sopenharmony_ci		if (result < 0) {
72562306a36Sopenharmony_ci			usb_ep_disable(ep);
72662306a36Sopenharmony_ci			goto fail3;
72762306a36Sopenharmony_ci		}
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ciout:
73062306a36Sopenharmony_ci	ss->cur_alt = alt;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	DBG(cdev, "%s enabled, alt intf %d\n", ss->function.name, alt);
73362306a36Sopenharmony_ci	return result;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic int sourcesink_set_alt(struct usb_function *f,
73762306a36Sopenharmony_ci		unsigned intf, unsigned alt)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct f_sourcesink		*ss = func_to_ss(f);
74062306a36Sopenharmony_ci	struct usb_composite_dev	*cdev = f->config->cdev;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	disable_source_sink(ss);
74362306a36Sopenharmony_ci	return enable_source_sink(cdev, ss, alt);
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_cistatic int sourcesink_get_alt(struct usb_function *f, unsigned intf)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	struct f_sourcesink		*ss = func_to_ss(f);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	return ss->cur_alt;
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic void sourcesink_disable(struct usb_function *f)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct f_sourcesink	*ss = func_to_ss(f);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	disable_source_sink(ss);
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic int sourcesink_setup(struct usb_function *f,
76362306a36Sopenharmony_ci		const struct usb_ctrlrequest *ctrl)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct usb_configuration        *c = f->config;
76662306a36Sopenharmony_ci	struct usb_request	*req = c->cdev->req;
76762306a36Sopenharmony_ci	int			value = -EOPNOTSUPP;
76862306a36Sopenharmony_ci	u16			w_index = le16_to_cpu(ctrl->wIndex);
76962306a36Sopenharmony_ci	u16			w_value = le16_to_cpu(ctrl->wValue);
77062306a36Sopenharmony_ci	u16			w_length = le16_to_cpu(ctrl->wLength);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	req->length = USB_COMP_EP0_BUFSIZ;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	/* composite driver infrastructure handles everything except
77562306a36Sopenharmony_ci	 * the two control test requests.
77662306a36Sopenharmony_ci	 */
77762306a36Sopenharmony_ci	switch (ctrl->bRequest) {
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	/*
78062306a36Sopenharmony_ci	 * These are the same vendor-specific requests supported by
78162306a36Sopenharmony_ci	 * Intel's USB 2.0 compliance test devices.  We exceed that
78262306a36Sopenharmony_ci	 * device spec by allowing multiple-packet requests.
78362306a36Sopenharmony_ci	 *
78462306a36Sopenharmony_ci	 * NOTE:  the Control-OUT data stays in req->buf ... better
78562306a36Sopenharmony_ci	 * would be copying it into a scratch buffer, so that other
78662306a36Sopenharmony_ci	 * requests may safely intervene.
78762306a36Sopenharmony_ci	 */
78862306a36Sopenharmony_ci	case 0x5b:	/* control WRITE test -- fill the buffer */
78962306a36Sopenharmony_ci		if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
79062306a36Sopenharmony_ci			goto unknown;
79162306a36Sopenharmony_ci		if (w_value || w_index)
79262306a36Sopenharmony_ci			break;
79362306a36Sopenharmony_ci		/* just read that many bytes into the buffer */
79462306a36Sopenharmony_ci		if (w_length > req->length)
79562306a36Sopenharmony_ci			break;
79662306a36Sopenharmony_ci		value = w_length;
79762306a36Sopenharmony_ci		break;
79862306a36Sopenharmony_ci	case 0x5c:	/* control READ test -- return the buffer */
79962306a36Sopenharmony_ci		if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
80062306a36Sopenharmony_ci			goto unknown;
80162306a36Sopenharmony_ci		if (w_value || w_index)
80262306a36Sopenharmony_ci			break;
80362306a36Sopenharmony_ci		/* expect those bytes are still in the buffer; send back */
80462306a36Sopenharmony_ci		if (w_length > req->length)
80562306a36Sopenharmony_ci			break;
80662306a36Sopenharmony_ci		value = w_length;
80762306a36Sopenharmony_ci		break;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	default:
81062306a36Sopenharmony_ciunknown:
81162306a36Sopenharmony_ci		VDBG(c->cdev,
81262306a36Sopenharmony_ci			"unknown control req%02x.%02x v%04x i%04x l%d\n",
81362306a36Sopenharmony_ci			ctrl->bRequestType, ctrl->bRequest,
81462306a36Sopenharmony_ci			w_value, w_index, w_length);
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	/* respond with data transfer or status phase? */
81862306a36Sopenharmony_ci	if (value >= 0) {
81962306a36Sopenharmony_ci		VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n",
82062306a36Sopenharmony_ci			ctrl->bRequestType, ctrl->bRequest,
82162306a36Sopenharmony_ci			w_value, w_index, w_length);
82262306a36Sopenharmony_ci		req->zero = 0;
82362306a36Sopenharmony_ci		req->length = value;
82462306a36Sopenharmony_ci		value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
82562306a36Sopenharmony_ci		if (value < 0)
82662306a36Sopenharmony_ci			ERROR(c->cdev, "source/sink response, err %d\n",
82762306a36Sopenharmony_ci					value);
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	/* device either stalls (value < 0) or reports success */
83162306a36Sopenharmony_ci	return value;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic struct usb_function *source_sink_alloc_func(
83562306a36Sopenharmony_ci		struct usb_function_instance *fi)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	struct f_sourcesink     *ss;
83862306a36Sopenharmony_ci	struct f_ss_opts	*ss_opts;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	ss = kzalloc(sizeof(*ss), GFP_KERNEL);
84162306a36Sopenharmony_ci	if (!ss)
84262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	ss_opts =  container_of(fi, struct f_ss_opts, func_inst);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	mutex_lock(&ss_opts->lock);
84762306a36Sopenharmony_ci	ss_opts->refcnt++;
84862306a36Sopenharmony_ci	mutex_unlock(&ss_opts->lock);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	ss->pattern = ss_opts->pattern;
85162306a36Sopenharmony_ci	ss->isoc_interval = ss_opts->isoc_interval;
85262306a36Sopenharmony_ci	ss->isoc_maxpacket = ss_opts->isoc_maxpacket;
85362306a36Sopenharmony_ci	ss->isoc_mult = ss_opts->isoc_mult;
85462306a36Sopenharmony_ci	ss->isoc_maxburst = ss_opts->isoc_maxburst;
85562306a36Sopenharmony_ci	ss->buflen = ss_opts->bulk_buflen;
85662306a36Sopenharmony_ci	ss->bulk_qlen = ss_opts->bulk_qlen;
85762306a36Sopenharmony_ci	ss->iso_qlen = ss_opts->iso_qlen;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	ss->function.name = "source/sink";
86062306a36Sopenharmony_ci	ss->function.bind = sourcesink_bind;
86162306a36Sopenharmony_ci	ss->function.set_alt = sourcesink_set_alt;
86262306a36Sopenharmony_ci	ss->function.get_alt = sourcesink_get_alt;
86362306a36Sopenharmony_ci	ss->function.disable = sourcesink_disable;
86462306a36Sopenharmony_ci	ss->function.setup = sourcesink_setup;
86562306a36Sopenharmony_ci	ss->function.strings = sourcesink_strings;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	ss->function.free_func = sourcesink_free_func;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	return &ss->function;
87062306a36Sopenharmony_ci}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_cistatic inline struct f_ss_opts *to_f_ss_opts(struct config_item *item)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	return container_of(to_config_group(item), struct f_ss_opts,
87562306a36Sopenharmony_ci			    func_inst.group);
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic void ss_attr_release(struct config_item *item)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	struct f_ss_opts *ss_opts = to_f_ss_opts(item);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	usb_put_function_instance(&ss_opts->func_inst);
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_cistatic struct configfs_item_operations ss_item_ops = {
88662306a36Sopenharmony_ci	.release		= ss_attr_release,
88762306a36Sopenharmony_ci};
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistatic ssize_t f_ss_opts_pattern_show(struct config_item *item, char *page)
89062306a36Sopenharmony_ci{
89162306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
89262306a36Sopenharmony_ci	int result;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	mutex_lock(&opts->lock);
89562306a36Sopenharmony_ci	result = sprintf(page, "%u\n", opts->pattern);
89662306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	return result;
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cistatic ssize_t f_ss_opts_pattern_store(struct config_item *item,
90262306a36Sopenharmony_ci				       const char *page, size_t len)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
90562306a36Sopenharmony_ci	int ret;
90662306a36Sopenharmony_ci	u8 num;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	mutex_lock(&opts->lock);
90962306a36Sopenharmony_ci	if (opts->refcnt) {
91062306a36Sopenharmony_ci		ret = -EBUSY;
91162306a36Sopenharmony_ci		goto end;
91262306a36Sopenharmony_ci	}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	ret = kstrtou8(page, 0, &num);
91562306a36Sopenharmony_ci	if (ret)
91662306a36Sopenharmony_ci		goto end;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (num != 0 && num != 1 && num != 2) {
91962306a36Sopenharmony_ci		ret = -EINVAL;
92062306a36Sopenharmony_ci		goto end;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	opts->pattern = num;
92462306a36Sopenharmony_ci	ret = len;
92562306a36Sopenharmony_ciend:
92662306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
92762306a36Sopenharmony_ci	return ret;
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ciCONFIGFS_ATTR(f_ss_opts_, pattern);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_cistatic ssize_t f_ss_opts_isoc_interval_show(struct config_item *item, char *page)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
93562306a36Sopenharmony_ci	int result;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	mutex_lock(&opts->lock);
93862306a36Sopenharmony_ci	result = sprintf(page, "%u\n", opts->isoc_interval);
93962306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	return result;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_cistatic ssize_t f_ss_opts_isoc_interval_store(struct config_item *item,
94562306a36Sopenharmony_ci				       const char *page, size_t len)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
94862306a36Sopenharmony_ci	int ret;
94962306a36Sopenharmony_ci	u8 num;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	mutex_lock(&opts->lock);
95262306a36Sopenharmony_ci	if (opts->refcnt) {
95362306a36Sopenharmony_ci		ret = -EBUSY;
95462306a36Sopenharmony_ci		goto end;
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	ret = kstrtou8(page, 0, &num);
95862306a36Sopenharmony_ci	if (ret)
95962306a36Sopenharmony_ci		goto end;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	if (num > 16) {
96262306a36Sopenharmony_ci		ret = -EINVAL;
96362306a36Sopenharmony_ci		goto end;
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	opts->isoc_interval = num;
96762306a36Sopenharmony_ci	ret = len;
96862306a36Sopenharmony_ciend:
96962306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
97062306a36Sopenharmony_ci	return ret;
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ciCONFIGFS_ATTR(f_ss_opts_, isoc_interval);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic ssize_t f_ss_opts_isoc_maxpacket_show(struct config_item *item, char *page)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
97862306a36Sopenharmony_ci	int result;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	mutex_lock(&opts->lock);
98162306a36Sopenharmony_ci	result = sprintf(page, "%u\n", opts->isoc_maxpacket);
98262306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	return result;
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistatic ssize_t f_ss_opts_isoc_maxpacket_store(struct config_item *item,
98862306a36Sopenharmony_ci				       const char *page, size_t len)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
99162306a36Sopenharmony_ci	int ret;
99262306a36Sopenharmony_ci	u16 num;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	mutex_lock(&opts->lock);
99562306a36Sopenharmony_ci	if (opts->refcnt) {
99662306a36Sopenharmony_ci		ret = -EBUSY;
99762306a36Sopenharmony_ci		goto end;
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	ret = kstrtou16(page, 0, &num);
100162306a36Sopenharmony_ci	if (ret)
100262306a36Sopenharmony_ci		goto end;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	if (num > 1024) {
100562306a36Sopenharmony_ci		ret = -EINVAL;
100662306a36Sopenharmony_ci		goto end;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	opts->isoc_maxpacket = num;
101062306a36Sopenharmony_ci	ret = len;
101162306a36Sopenharmony_ciend:
101262306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
101362306a36Sopenharmony_ci	return ret;
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ciCONFIGFS_ATTR(f_ss_opts_, isoc_maxpacket);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cistatic ssize_t f_ss_opts_isoc_mult_show(struct config_item *item, char *page)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
102162306a36Sopenharmony_ci	int result;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	mutex_lock(&opts->lock);
102462306a36Sopenharmony_ci	result = sprintf(page, "%u\n", opts->isoc_mult);
102562306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	return result;
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic ssize_t f_ss_opts_isoc_mult_store(struct config_item *item,
103162306a36Sopenharmony_ci				       const char *page, size_t len)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
103462306a36Sopenharmony_ci	int ret;
103562306a36Sopenharmony_ci	u8 num;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	mutex_lock(&opts->lock);
103862306a36Sopenharmony_ci	if (opts->refcnt) {
103962306a36Sopenharmony_ci		ret = -EBUSY;
104062306a36Sopenharmony_ci		goto end;
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	ret = kstrtou8(page, 0, &num);
104462306a36Sopenharmony_ci	if (ret)
104562306a36Sopenharmony_ci		goto end;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	if (num > 2) {
104862306a36Sopenharmony_ci		ret = -EINVAL;
104962306a36Sopenharmony_ci		goto end;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	opts->isoc_mult = num;
105362306a36Sopenharmony_ci	ret = len;
105462306a36Sopenharmony_ciend:
105562306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
105662306a36Sopenharmony_ci	return ret;
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ciCONFIGFS_ATTR(f_ss_opts_, isoc_mult);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic ssize_t f_ss_opts_isoc_maxburst_show(struct config_item *item, char *page)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
106462306a36Sopenharmony_ci	int result;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	mutex_lock(&opts->lock);
106762306a36Sopenharmony_ci	result = sprintf(page, "%u\n", opts->isoc_maxburst);
106862306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	return result;
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cistatic ssize_t f_ss_opts_isoc_maxburst_store(struct config_item *item,
107462306a36Sopenharmony_ci				       const char *page, size_t len)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
107762306a36Sopenharmony_ci	int ret;
107862306a36Sopenharmony_ci	u8 num;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	mutex_lock(&opts->lock);
108162306a36Sopenharmony_ci	if (opts->refcnt) {
108262306a36Sopenharmony_ci		ret = -EBUSY;
108362306a36Sopenharmony_ci		goto end;
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	ret = kstrtou8(page, 0, &num);
108762306a36Sopenharmony_ci	if (ret)
108862306a36Sopenharmony_ci		goto end;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	if (num > 15) {
109162306a36Sopenharmony_ci		ret = -EINVAL;
109262306a36Sopenharmony_ci		goto end;
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	opts->isoc_maxburst = num;
109662306a36Sopenharmony_ci	ret = len;
109762306a36Sopenharmony_ciend:
109862306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
109962306a36Sopenharmony_ci	return ret;
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ciCONFIGFS_ATTR(f_ss_opts_, isoc_maxburst);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic ssize_t f_ss_opts_bulk_buflen_show(struct config_item *item, char *page)
110562306a36Sopenharmony_ci{
110662306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
110762306a36Sopenharmony_ci	int result;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	mutex_lock(&opts->lock);
111062306a36Sopenharmony_ci	result = sprintf(page, "%u\n", opts->bulk_buflen);
111162306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	return result;
111462306a36Sopenharmony_ci}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_cistatic ssize_t f_ss_opts_bulk_buflen_store(struct config_item *item,
111762306a36Sopenharmony_ci					   const char *page, size_t len)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
112062306a36Sopenharmony_ci	int ret;
112162306a36Sopenharmony_ci	u32 num;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	mutex_lock(&opts->lock);
112462306a36Sopenharmony_ci	if (opts->refcnt) {
112562306a36Sopenharmony_ci		ret = -EBUSY;
112662306a36Sopenharmony_ci		goto end;
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	ret = kstrtou32(page, 0, &num);
113062306a36Sopenharmony_ci	if (ret)
113162306a36Sopenharmony_ci		goto end;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	opts->bulk_buflen = num;
113462306a36Sopenharmony_ci	ret = len;
113562306a36Sopenharmony_ciend:
113662306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
113762306a36Sopenharmony_ci	return ret;
113862306a36Sopenharmony_ci}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ciCONFIGFS_ATTR(f_ss_opts_, bulk_buflen);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_cistatic ssize_t f_ss_opts_bulk_qlen_show(struct config_item *item, char *page)
114362306a36Sopenharmony_ci{
114462306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
114562306a36Sopenharmony_ci	int result;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	mutex_lock(&opts->lock);
114862306a36Sopenharmony_ci	result = sprintf(page, "%u\n", opts->bulk_qlen);
114962306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	return result;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic ssize_t f_ss_opts_bulk_qlen_store(struct config_item *item,
115562306a36Sopenharmony_ci					   const char *page, size_t len)
115662306a36Sopenharmony_ci{
115762306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
115862306a36Sopenharmony_ci	int ret;
115962306a36Sopenharmony_ci	u32 num;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	mutex_lock(&opts->lock);
116262306a36Sopenharmony_ci	if (opts->refcnt) {
116362306a36Sopenharmony_ci		ret = -EBUSY;
116462306a36Sopenharmony_ci		goto end;
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	ret = kstrtou32(page, 0, &num);
116862306a36Sopenharmony_ci	if (ret)
116962306a36Sopenharmony_ci		goto end;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	opts->bulk_qlen = num;
117262306a36Sopenharmony_ci	ret = len;
117362306a36Sopenharmony_ciend:
117462306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
117562306a36Sopenharmony_ci	return ret;
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ciCONFIGFS_ATTR(f_ss_opts_, bulk_qlen);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_cistatic ssize_t f_ss_opts_iso_qlen_show(struct config_item *item, char *page)
118162306a36Sopenharmony_ci{
118262306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
118362306a36Sopenharmony_ci	int result;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	mutex_lock(&opts->lock);
118662306a36Sopenharmony_ci	result = sprintf(page, "%u\n", opts->iso_qlen);
118762306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	return result;
119062306a36Sopenharmony_ci}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_cistatic ssize_t f_ss_opts_iso_qlen_store(struct config_item *item,
119362306a36Sopenharmony_ci					   const char *page, size_t len)
119462306a36Sopenharmony_ci{
119562306a36Sopenharmony_ci	struct f_ss_opts *opts = to_f_ss_opts(item);
119662306a36Sopenharmony_ci	int ret;
119762306a36Sopenharmony_ci	u32 num;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	mutex_lock(&opts->lock);
120062306a36Sopenharmony_ci	if (opts->refcnt) {
120162306a36Sopenharmony_ci		ret = -EBUSY;
120262306a36Sopenharmony_ci		goto end;
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	ret = kstrtou32(page, 0, &num);
120662306a36Sopenharmony_ci	if (ret)
120762306a36Sopenharmony_ci		goto end;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	opts->iso_qlen = num;
121062306a36Sopenharmony_ci	ret = len;
121162306a36Sopenharmony_ciend:
121262306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
121362306a36Sopenharmony_ci	return ret;
121462306a36Sopenharmony_ci}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ciCONFIGFS_ATTR(f_ss_opts_, iso_qlen);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_cistatic struct configfs_attribute *ss_attrs[] = {
121962306a36Sopenharmony_ci	&f_ss_opts_attr_pattern,
122062306a36Sopenharmony_ci	&f_ss_opts_attr_isoc_interval,
122162306a36Sopenharmony_ci	&f_ss_opts_attr_isoc_maxpacket,
122262306a36Sopenharmony_ci	&f_ss_opts_attr_isoc_mult,
122362306a36Sopenharmony_ci	&f_ss_opts_attr_isoc_maxburst,
122462306a36Sopenharmony_ci	&f_ss_opts_attr_bulk_buflen,
122562306a36Sopenharmony_ci	&f_ss_opts_attr_bulk_qlen,
122662306a36Sopenharmony_ci	&f_ss_opts_attr_iso_qlen,
122762306a36Sopenharmony_ci	NULL,
122862306a36Sopenharmony_ci};
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cistatic const struct config_item_type ss_func_type = {
123162306a36Sopenharmony_ci	.ct_item_ops    = &ss_item_ops,
123262306a36Sopenharmony_ci	.ct_attrs	= ss_attrs,
123362306a36Sopenharmony_ci	.ct_owner       = THIS_MODULE,
123462306a36Sopenharmony_ci};
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_cistatic void source_sink_free_instance(struct usb_function_instance *fi)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	struct f_ss_opts *ss_opts;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	ss_opts = container_of(fi, struct f_ss_opts, func_inst);
124162306a36Sopenharmony_ci	kfree(ss_opts);
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic struct usb_function_instance *source_sink_alloc_inst(void)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	struct f_ss_opts *ss_opts;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL);
124962306a36Sopenharmony_ci	if (!ss_opts)
125062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
125162306a36Sopenharmony_ci	mutex_init(&ss_opts->lock);
125262306a36Sopenharmony_ci	ss_opts->func_inst.free_func_inst = source_sink_free_instance;
125362306a36Sopenharmony_ci	ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
125462306a36Sopenharmony_ci	ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
125562306a36Sopenharmony_ci	ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
125662306a36Sopenharmony_ci	ss_opts->bulk_qlen = GZERO_SS_BULK_QLEN;
125762306a36Sopenharmony_ci	ss_opts->iso_qlen = GZERO_SS_ISO_QLEN;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	config_group_init_type_name(&ss_opts->func_inst.group, "",
126062306a36Sopenharmony_ci				    &ss_func_type);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	return &ss_opts->func_inst;
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ciDECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst,
126562306a36Sopenharmony_ci		source_sink_alloc_func);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_cistatic int __init sslb_modinit(void)
126862306a36Sopenharmony_ci{
126962306a36Sopenharmony_ci	int ret;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	ret = usb_function_register(&SourceSinkusb_func);
127262306a36Sopenharmony_ci	if (ret)
127362306a36Sopenharmony_ci		return ret;
127462306a36Sopenharmony_ci	ret = lb_modinit();
127562306a36Sopenharmony_ci	if (ret)
127662306a36Sopenharmony_ci		usb_function_unregister(&SourceSinkusb_func);
127762306a36Sopenharmony_ci	return ret;
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_cistatic void __exit sslb_modexit(void)
128062306a36Sopenharmony_ci{
128162306a36Sopenharmony_ci	usb_function_unregister(&SourceSinkusb_func);
128262306a36Sopenharmony_ci	lb_modexit();
128362306a36Sopenharmony_ci}
128462306a36Sopenharmony_cimodule_init(sslb_modinit);
128562306a36Sopenharmony_cimodule_exit(sslb_modexit);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1288