1a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell/*
2a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * f_sourcesink.c - USB peripheral source/sink configuration driver
3a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell *
4a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * Copyright (C) 2003-2008 David Brownell
5a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * Copyright (C) 2008 by Nokia Corporation
6a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell *
7a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * This program is free software; you can redistribute it and/or modify
8a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * it under the terms of the GNU General Public License as published by
9a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * the Free Software Foundation; either version 2 of the License, or
10a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * (at your option) any later version.
11a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell */
12a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
13a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell/* #define VERBOSE_DEBUG */
14a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
155a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
16a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell#include <linux/kernel.h>
17a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell#include <linux/device.h>
186eb0de827084060e6607c8f8542d9e9566214538Paul Gortmaker#include <linux/module.h>
19a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
20a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell#include "g_zero.h"
21a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell#include "gadget_chips.h"
22a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
23a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
24a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell/*
25a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
26a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * controller drivers.
27a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell *
28a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * This just sinks bulk packets OUT to the peripheral and sources them IN
29a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * to the host, optionally with specific data patterns for integrity tests.
30a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * As such it supports basic functionality and load tests.
31a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell *
32a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * In terms of control messaging, this supports all the standard requests
33a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * plus two that support control-OUT tests.  If the optional "autoresume"
34a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * mode is enabled, it provides good functional coverage for the "USBCV"
35a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * test harness from USB-IF.
36a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell *
37a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * Note that because this doesn't queue more than one request at a time,
38a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * some other function must be used to test queueing logic.  The network
39a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * link (g_ether) is the best overall option for that, since its TX and RX
40a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * queues are relatively independent, will receive a range of packet sizes,
41a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * and can often be made to run out completely.  Those issues are important
42a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * when stress testing peripheral controller drivers.
43a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell *
44a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell *
45a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * This is currently packaged as a configuration driver, which can't be
46a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * combined with other functions to make composite devices.  However, it
47a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * can be combined with other independent configurations.
48a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell */
49a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstruct f_sourcesink {
50a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_function	function;
51a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
52a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_ep		*in_ep;
53a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_ep		*out_ep;
54a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
55a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
56a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic inline struct f_sourcesink *func_to_ss(struct usb_function *f)
57a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
58a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	return container_of(f, struct f_sourcesink, function);
59a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
60a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
61a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic unsigned pattern;
62a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellmodule_param(pattern, uint, 0);
63a400cadc0774c31f67c419a835d80ba611128c2aDavid BrownellMODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 ");
64a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
65a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell/*-------------------------------------------------------------------------*/
66a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
67a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic struct usb_interface_descriptor source_sink_intf = {
68a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bLength =		sizeof source_sink_intf,
69a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bDescriptorType =	USB_DT_INTERFACE,
70a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
71a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bNumEndpoints =	2,
72a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
73a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* .iInterface = DYNAMIC */
74a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
75a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
76a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell/* full speed support: */
77a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
78a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic struct usb_endpoint_descriptor fs_source_desc = {
79a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bLength =		USB_DT_ENDPOINT_SIZE,
80a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bDescriptorType =	USB_DT_ENDPOINT,
81a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
82a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bEndpointAddress =	USB_DIR_IN,
83a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
84a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
85a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
86a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic struct usb_endpoint_descriptor fs_sink_desc = {
87a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bLength =		USB_DT_ENDPOINT_SIZE,
88a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bDescriptorType =	USB_DT_ENDPOINT,
89a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
90a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bEndpointAddress =	USB_DIR_OUT,
91a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
92a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
93a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
94a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic struct usb_descriptor_header *fs_source_sink_descs[] = {
95a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	(struct usb_descriptor_header *) &source_sink_intf,
96a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	(struct usb_descriptor_header *) &fs_sink_desc,
97a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	(struct usb_descriptor_header *) &fs_source_desc,
98a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	NULL,
99a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
100a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
101a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell/* high speed support: */
102a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
103a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic struct usb_endpoint_descriptor hs_source_desc = {
104a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bLength =		USB_DT_ENDPOINT_SIZE,
105a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bDescriptorType =	USB_DT_ENDPOINT,
106a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
107a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
108551509d267905705f6d723e51ec706916f06b859Harvey Harrison	.wMaxPacketSize =	cpu_to_le16(512),
109a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
110a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
111a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic struct usb_endpoint_descriptor hs_sink_desc = {
112a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bLength =		USB_DT_ENDPOINT_SIZE,
113a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bDescriptorType =	USB_DT_ENDPOINT,
114a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
115a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
116551509d267905705f6d723e51ec706916f06b859Harvey Harrison	.wMaxPacketSize =	cpu_to_le16(512),
117a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
118a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
119a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic struct usb_descriptor_header *hs_source_sink_descs[] = {
120a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	(struct usb_descriptor_header *) &source_sink_intf,
121a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	(struct usb_descriptor_header *) &hs_source_desc,
122a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	(struct usb_descriptor_header *) &hs_sink_desc,
123a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	NULL,
124a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
125a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
12657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay/* super speed support: */
12757c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay
12857c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blaystatic struct usb_endpoint_descriptor ss_source_desc = {
12957c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bLength =		USB_DT_ENDPOINT_SIZE,
13057c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bDescriptorType =	USB_DT_ENDPOINT,
13157c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay
13257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
13357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.wMaxPacketSize =	cpu_to_le16(1024),
13457c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay};
13557c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay
13657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blaystruct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
13757c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bLength =		USB_DT_SS_EP_COMP_SIZE,
13857c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
13957c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bMaxBurst =		0,
14057c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bmAttributes =		0,
14157c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.wBytesPerInterval =	0,
14257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay};
14357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay
14457c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blaystatic struct usb_endpoint_descriptor ss_sink_desc = {
14557c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bLength =		USB_DT_ENDPOINT_SIZE,
14657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bDescriptorType =	USB_DT_ENDPOINT,
14757c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay
14857c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
14957c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.wMaxPacketSize =	cpu_to_le16(1024),
15057c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay};
15157c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay
15257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blaystruct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
15357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bLength =		USB_DT_SS_EP_COMP_SIZE,
15457c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
15557c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bMaxBurst =		0,
15657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.bmAttributes =		0,
15757c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	.wBytesPerInterval =	0,
15857c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay};
15957c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay
16057c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blaystatic struct usb_descriptor_header *ss_source_sink_descs[] = {
16157c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	(struct usb_descriptor_header *) &source_sink_intf,
16257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	(struct usb_descriptor_header *) &ss_source_desc,
16357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	(struct usb_descriptor_header *) &ss_source_comp_desc,
16457c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	(struct usb_descriptor_header *) &ss_sink_desc,
16557c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	(struct usb_descriptor_header *) &ss_sink_comp_desc,
16657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	NULL,
16757c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay};
16857c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay
169a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell/* function-specific strings: */
170a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
171a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic struct usb_string strings_sourcesink[] = {
172a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	[0].s = "source and sink data",
173a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	{  }			/* end of list */
174a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
175a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
176a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic struct usb_gadget_strings stringtab_sourcesink = {
177a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.language	= 0x0409,	/* en-us */
178a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.strings	= strings_sourcesink,
179a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
180a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
181a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic struct usb_gadget_strings *sourcesink_strings[] = {
182a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	&stringtab_sourcesink,
183a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	NULL,
184a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
185a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
186a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell/*-------------------------------------------------------------------------*/
187a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
188a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic int __init
189a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellsourcesink_bind(struct usb_configuration *c, struct usb_function *f)
190a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
191a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_composite_dev *cdev = c->cdev;
192a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct f_sourcesink	*ss = func_to_ss(f);
193a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	int	id;
194a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
195a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* allocate interface ID(s) */
196a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	id = usb_interface_id(c, f);
197a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (id < 0)
198a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		return id;
199a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	source_sink_intf.bInterfaceNumber = id;
200a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
201a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* allocate endpoints */
202a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
203a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (!ss->in_ep) {
204a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellautoconf_fail:
205a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		ERROR(cdev, "%s: can't autoconfigure on %s\n",
206a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			f->name, cdev->gadget->name);
207a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		return -ENODEV;
208a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
209a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ss->in_ep->driver_data = cdev;	/* claim */
210a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
211a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
212a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (!ss->out_ep)
213a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		goto autoconf_fail;
214a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ss->out_ep->driver_data = cdev;	/* claim */
215a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
216a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* support high speed hardware */
217a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (gadget_is_dualspeed(c->cdev->gadget)) {
218a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		hs_source_desc.bEndpointAddress =
219a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell				fs_source_desc.bEndpointAddress;
220a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		hs_sink_desc.bEndpointAddress =
221a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell				fs_sink_desc.bEndpointAddress;
222a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		f->hs_descriptors = hs_source_sink_descs;
223a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
224a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
22557c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	/* support super speed hardware */
22657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	if (gadget_is_superspeed(c->cdev->gadget)) {
22757c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay		ss_source_desc.bEndpointAddress =
22857c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay				fs_source_desc.bEndpointAddress;
22957c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay		ss_sink_desc.bEndpointAddress =
23057c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay				fs_sink_desc.bEndpointAddress;
23157c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay		f->ss_descriptors = ss_source_sink_descs;
23257c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	}
23357c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay
234a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
23557c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	    (gadget_is_superspeed(c->cdev->gadget) ? "super" :
23657c97c02de0e7a59cb48d3d7666f4afaf9968e84Amit Blay	     (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
237a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			f->name, ss->in_ep->name, ss->out_ep->name);
238a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	return 0;
239a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
240a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
241a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic void
242a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellsourcesink_unbind(struct usb_configuration *c, struct usb_function *f)
243a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
244a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	kfree(func_to_ss(f));
245a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
246a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
247a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell/* optionally require specific source/sink data patterns  */
248a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
249a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
250a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	unsigned		i;
251a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	u8			*buf = req->buf;
252a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_composite_dev *cdev = ss->function.config->cdev;
253a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
254a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	for (i = 0; i < req->actual; i++, buf++) {
255a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		switch (pattern) {
256a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
257a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		/* all-zeroes has no synchronization issues */
258a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		case 0:
259a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			if (*buf == 0)
260a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell				continue;
261a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			break;
262a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
263a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		/* "mod63" stays in sync with short-terminated transfers,
264a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		 * OR otherwise when host and gadget agree on how large
265a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		 * each usb transfer request should be.  Resync is done
266a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		 * with set_interface or set_config.  (We *WANT* it to
267a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		 * get quickly out of sync if controllers or their drivers
268a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		 * stutter for any reason, including buffer duplcation...)
269a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		 */
270a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		case 1:
271a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			if (*buf == (u8)(i % 63))
272a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell				continue;
273a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			break;
274a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		}
275a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
276a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		usb_ep_set_halt(ss->out_ep);
277a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		return -EINVAL;
278a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
279a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	return 0;
280a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
281a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
282a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
283a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
284a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	unsigned	i;
285a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	u8		*buf = req->buf;
286a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
287a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	switch (pattern) {
288a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	case 0:
289a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		memset(req->buf, 0, req->length);
290a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		break;
291a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	case 1:
292a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		for  (i = 0; i < req->length; i++)
293a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			*buf++ = (u8) (i % 63);
294a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		break;
295a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
296a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
297a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
298a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
299a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
300a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct f_sourcesink	*ss = ep->driver_data;
301a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_composite_dev *cdev = ss->function.config->cdev;
302a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	int			status = req->status;
303a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
304a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	switch (status) {
305a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
306a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	case 0:				/* normal completion? */
307a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		if (ep == ss->out_ep) {
308a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			check_read_data(ss, req);
309a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			memset(req->buf, 0x55, req->length);
310a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		} else
311a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			reinit_write_data(ep, req);
312a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		break;
313a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
314a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* this endpoint is normally active while we're configured */
315a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	case -ECONNABORTED:		/* hardware forced ep reset */
316a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	case -ECONNRESET:		/* request dequeued */
317a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	case -ESHUTDOWN:		/* disconnect from host */
318a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
319a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell				req->actual, req->length);
320a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		if (ep == ss->out_ep)
321a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			check_read_data(ss, req);
322a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		free_ep_req(ep, req);
323a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		return;
324a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
325a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	case -EOVERFLOW:		/* buffer overrun on read means that
326a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell					 * we didn't provide a big enough
327a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell					 * buffer.
328a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell					 */
329a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	default:
330a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell#if 1
331a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
332a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell				status, req->actual, req->length);
333a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell#endif
334a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	case -EREMOTEIO:		/* short read */
335a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		break;
336a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
337a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
338a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	status = usb_ep_queue(ep, req, GFP_ATOMIC);
339a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (status) {
340a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		ERROR(cdev, "kill %s:  resubmit %d bytes --> %d\n",
341a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell				ep->name, req->length, status);
342a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		usb_ep_set_halt(ep);
343a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		/* FIXME recover later ... somehow */
344a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
345a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
346a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
347a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic int source_sink_start_ep(struct f_sourcesink *ss, bool is_in)
348a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
349a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_ep		*ep;
350a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_request	*req;
351a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	int			status;
352a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
353a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ep = is_in ? ss->in_ep : ss->out_ep;
354a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	req = alloc_ep_req(ep);
355a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (!req)
356a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		return -ENOMEM;
357a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
358a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	req->complete = source_sink_complete;
359a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (is_in)
360a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		reinit_write_data(ep, req);
361a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	else
362a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		memset(req->buf, 0x55, req->length);
363a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
364a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	status = usb_ep_queue(ep, req, GFP_ATOMIC);
365a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (status) {
366a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		struct usb_composite_dev	*cdev;
367a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
368a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		cdev = ss->function.config->cdev;
369a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		ERROR(cdev, "start %s %s --> %d\n",
370a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell				is_in ? "IN" : "OUT",
371a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell				ep->name, status);
372a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		free_ep_req(ep, req);
373a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
374a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
375a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	return status;
376a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
377a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
378a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic void disable_source_sink(struct f_sourcesink *ss)
379a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
380a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_composite_dev	*cdev;
381a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
382a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	cdev = ss->function.config->cdev;
383a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	disable_endpoints(cdev, ss->in_ep, ss->out_ep);
384a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	VDBG(cdev, "%s disabled\n", ss->function.name);
385a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
386a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
387a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic int
388a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellenable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
389a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
390a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	int					result = 0;
391a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_ep				*ep;
392a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
393a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* one endpoint writes (sources) zeroes IN (to the host) */
394a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ep = ss->in_ep;
395ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman	result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
396ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman	if (result)
397ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman		return result;
39872c973dd2b01b212a159faa330a2bc641a3ed809Tatyana Brokhman	result = usb_ep_enable(ep);
399a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (result < 0)
400a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		return result;
401a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ep->driver_data = ss;
402a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
403a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	result = source_sink_start_ep(ss, true);
404a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (result < 0) {
405a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellfail:
406a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		ep = ss->in_ep;
407a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		usb_ep_disable(ep);
408a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		ep->driver_data = NULL;
409a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		return result;
410a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
411a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
412a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* one endpoint reads (sinks) anything OUT (from the host) */
413a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ep = ss->out_ep;
414ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman	result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
415ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman	if (result)
416ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman		goto fail;
41772c973dd2b01b212a159faa330a2bc641a3ed809Tatyana Brokhman	result = usb_ep_enable(ep);
418a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (result < 0)
419a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		goto fail;
420a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ep->driver_data = ss;
421a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
422a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	result = source_sink_start_ep(ss, false);
423a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (result < 0) {
424a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		usb_ep_disable(ep);
425a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		ep->driver_data = NULL;
426a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		goto fail;
427a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
428a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
429a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	DBG(cdev, "%s enabled\n", ss->function.name);
430a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	return result;
431a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
432a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
433a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic int sourcesink_set_alt(struct usb_function *f,
434a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		unsigned intf, unsigned alt)
435a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
436a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct f_sourcesink	*ss = func_to_ss(f);
437a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_composite_dev *cdev = f->config->cdev;
438a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
439a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* we know alt is zero */
440a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (ss->in_ep->driver_data)
441a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		disable_source_sink(ss);
442a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	return enable_source_sink(cdev, ss);
443a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
444a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
445a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic void sourcesink_disable(struct usb_function *f)
446a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
447a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct f_sourcesink	*ss = func_to_ss(f);
448a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
449a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	disable_source_sink(ss);
450a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
451a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
452a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell/*-------------------------------------------------------------------------*/
453a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
454e12995ec8f8d99f2a339541fc28998af2d60af0fMichal Nazarewiczstatic int __init sourcesink_bind_config(struct usb_configuration *c)
455a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
456a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct f_sourcesink	*ss;
457a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	int			status;
458a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
459a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ss = kzalloc(sizeof *ss, GFP_KERNEL);
460a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (!ss)
461a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		return -ENOMEM;
462a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
463a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ss->function.name = "source/sink";
464a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ss->function.descriptors = fs_source_sink_descs;
465a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ss->function.bind = sourcesink_bind;
466a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ss->function.unbind = sourcesink_unbind;
467a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ss->function.set_alt = sourcesink_set_alt;
468a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	ss->function.disable = sourcesink_disable;
469a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
470a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	status = usb_add_function(c, &ss->function);
471a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (status)
472a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		kfree(ss);
473a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	return status;
474a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
475a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
476a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic int sourcesink_setup(struct usb_configuration *c,
477a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		const struct usb_ctrlrequest *ctrl)
478a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
479a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	struct usb_request	*req = c->cdev->req;
480a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	int			value = -EOPNOTSUPP;
481a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	u16			w_index = le16_to_cpu(ctrl->wIndex);
482a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	u16			w_value = le16_to_cpu(ctrl->wValue);
483a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	u16			w_length = le16_to_cpu(ctrl->wLength);
484a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
4855030ec730279eabafccf862f3c8393af90c258a5Bob Liu	req->length = USB_BUFSIZ;
4865030ec730279eabafccf862f3c8393af90c258a5Bob Liu
487a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* composite driver infrastructure handles everything except
488a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	 * the two control test requests.
489a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	 */
490a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	switch (ctrl->bRequest) {
491a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
492a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/*
493a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	 * These are the same vendor-specific requests supported by
494a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	 * Intel's USB 2.0 compliance test devices.  We exceed that
495a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	 * device spec by allowing multiple-packet requests.
496a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	 *
497a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	 * NOTE:  the Control-OUT data stays in req->buf ... better
498a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	 * would be copying it into a scratch buffer, so that other
499a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	 * requests may safely intervene.
500a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	 */
501a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	case 0x5b:	/* control WRITE test -- fill the buffer */
502a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
503a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			goto unknown;
504a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		if (w_value || w_index)
505a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			break;
506a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		/* just read that many bytes into the buffer */
507a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		if (w_length > req->length)
508a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			break;
509a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		value = w_length;
510a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		break;
511a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	case 0x5c:	/* control READ test -- return the buffer */
512a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
513a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			goto unknown;
514a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		if (w_value || w_index)
515a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			break;
516a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		/* expect those bytes are still in the buffer; send back */
517a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		if (w_length > req->length)
518a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			break;
519a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		value = w_length;
520a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		break;
521a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
522a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	default:
523a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellunknown:
524a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		VDBG(c->cdev,
525a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			"unknown control req%02x.%02x v%04x i%04x l%d\n",
526a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			ctrl->bRequestType, ctrl->bRequest,
527a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			w_value, w_index, w_length);
528a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
529a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
530a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* respond with data transfer or status phase? */
531a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (value >= 0) {
532a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n",
533a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			ctrl->bRequestType, ctrl->bRequest,
534a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			w_value, w_index, w_length);
535a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		req->zero = 0;
536a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		req->length = value;
537a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
538a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		if (value < 0)
539a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell			ERROR(c->cdev, "source/sinkc response, err %d\n",
540a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell					value);
541a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
542a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
543a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* device either stalls (value < 0) or reports success */
544a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	return value;
545a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
546a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
547a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownellstatic struct usb_configuration sourcesink_driver = {
548a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.label		= "source/sink",
549a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.strings	= sourcesink_strings,
550a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.setup		= sourcesink_setup,
551a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bConfigurationValue = 3,
552a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	.bmAttributes	= USB_CONFIG_ATT_SELFPOWER,
553a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* .iConfiguration = DYNAMIC */
554a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell};
555a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
556a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell/**
557a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * sourcesink_add - add a source/sink testing configuration to a device
558a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell * @cdev: the device to support the configuration
559a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell */
560ab943a2e125b098489ccaa0166c2c52f8266d9edDavid Brownellint __init sourcesink_add(struct usb_composite_dev *cdev, bool autoresume)
561a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell{
562a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	int id;
563a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
564a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* allocate string ID(s) */
565a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	id = usb_string_id(cdev);
566a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (id < 0)
567a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		return id;
568a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	strings_sourcesink[0].id = id;
569a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
570a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	source_sink_intf.iInterface = id;
571a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	sourcesink_driver.iConfiguration = id;
572a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
573a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* support autoresume for remote wakeup testing */
574a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (autoresume)
575a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
576a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
577a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	/* support OTG systems */
578a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	if (gadget_is_otg(cdev->gadget)) {
579a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		sourcesink_driver.descriptors = otg_desc;
580a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell		sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
581a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell	}
582a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell
583c9bfff9c98671ad50e4abbfe1ab606a9957f7539Uwe Kleine-König	return usb_add_config(cdev, &sourcesink_driver, sourcesink_bind_config);
584a400cadc0774c31f67c419a835d80ba611128c2aDavid Brownell}
585