1/*
2 * acm_ms.c -- Composite driver, with ACM and mass storage support
3 *
4 * Copyright (C) 2008 David Brownell
5 * Copyright (C) 2008 Nokia Corporation
6 * Author: David Brownell
7 * Modified: Klaus Schwarzkopf <schwarzkopf@sensortherm.de>
8 *
9 * Heavily based on multi.c and cdc2.c
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 */
16
17#include <linux/kernel.h>
18#include <linux/module.h>
19
20#include "u_serial.h"
21
22#define DRIVER_DESC		"Composite Gadget (ACM + MS)"
23#define DRIVER_VERSION		"2011/10/10"
24
25/*-------------------------------------------------------------------------*/
26
27/*
28 * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
29 * Instead:  allocate your own, using normal USB-IF procedures.
30 */
31#define ACM_MS_VENDOR_NUM	0x1d6b	/* Linux Foundation */
32#define ACM_MS_PRODUCT_NUM	0x0106	/* Composite Gadget: ACM + MS*/
33
34#include "f_mass_storage.h"
35
36/*-------------------------------------------------------------------------*/
37USB_GADGET_COMPOSITE_OPTIONS();
38
39static struct usb_device_descriptor device_desc = {
40	.bLength =		sizeof device_desc,
41	.bDescriptorType =	USB_DT_DEVICE,
42
43	.bcdUSB =		cpu_to_le16(0x0200),
44
45	.bDeviceClass =		USB_CLASS_MISC /* 0xEF */,
46	.bDeviceSubClass =	2,
47	.bDeviceProtocol =	1,
48
49	/* .bMaxPacketSize0 = f(hardware) */
50
51	/* Vendor and product id can be overridden by module parameters.  */
52	.idVendor =		cpu_to_le16(ACM_MS_VENDOR_NUM),
53	.idProduct =		cpu_to_le16(ACM_MS_PRODUCT_NUM),
54	/* .bcdDevice = f(hardware) */
55	/* .iManufacturer = DYNAMIC */
56	/* .iProduct = DYNAMIC */
57	/* NO SERIAL NUMBER */
58	/*.bNumConfigurations =	DYNAMIC*/
59};
60
61static struct usb_otg_descriptor otg_descriptor = {
62	.bLength =		sizeof otg_descriptor,
63	.bDescriptorType =	USB_DT_OTG,
64
65	/*
66	 * REVISIT SRP-only hardware is possible, although
67	 * it would not be called "OTG" ...
68	 */
69	.bmAttributes =		USB_OTG_SRP | USB_OTG_HNP,
70};
71
72static const struct usb_descriptor_header *otg_desc[] = {
73	(struct usb_descriptor_header *) &otg_descriptor,
74	NULL,
75};
76
77/* string IDs are assigned dynamically */
78static struct usb_string strings_dev[] = {
79	[USB_GADGET_MANUFACTURER_IDX].s = "",
80	[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
81	[USB_GADGET_SERIAL_IDX].s = "",
82	{  } /* end of list */
83};
84
85static struct usb_gadget_strings stringtab_dev = {
86	.language	= 0x0409,	/* en-us */
87	.strings	= strings_dev,
88};
89
90static struct usb_gadget_strings *dev_strings[] = {
91	&stringtab_dev,
92	NULL,
93};
94
95/****************************** Configurations ******************************/
96
97static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
98#ifdef CONFIG_USB_GADGET_DEBUG_FILES
99
100static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
101
102#else
103
104/*
105 * Number of buffers we will use.
106 * 2 is usually enough for good buffering pipeline
107 */
108#define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
109
110#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
111
112FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
113
114/*-------------------------------------------------------------------------*/
115static struct usb_function *f_acm;
116static struct usb_function_instance *f_acm_inst;
117
118static struct usb_function_instance *fi_msg;
119static struct usb_function *f_msg;
120
121/*
122 * We _always_ have both ACM and mass storage functions.
123 */
124static int acm_ms_do_config(struct usb_configuration *c)
125{
126	struct fsg_opts *opts;
127	int	status;
128
129	if (gadget_is_otg(c->cdev->gadget)) {
130		c->descriptors = otg_desc;
131		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
132	}
133
134	opts = fsg_opts_from_func_inst(fi_msg);
135
136	f_acm = usb_get_function(f_acm_inst);
137	if (IS_ERR(f_acm))
138		return PTR_ERR(f_acm);
139
140	f_msg = usb_get_function(fi_msg);
141	if (IS_ERR(f_msg)) {
142		status = PTR_ERR(f_msg);
143		goto put_acm;
144	}
145
146	status = usb_add_function(c, f_acm);
147	if (status < 0)
148		goto put_msg;
149
150	status = usb_add_function(c, f_msg);
151	if (status)
152		goto remove_acm;
153
154	return 0;
155remove_acm:
156	usb_remove_function(c, f_acm);
157put_msg:
158	usb_put_function(f_msg);
159put_acm:
160	usb_put_function(f_acm);
161	return status;
162}
163
164static struct usb_configuration acm_ms_config_driver = {
165	.label			= DRIVER_DESC,
166	.bConfigurationValue	= 1,
167	/* .iConfiguration = DYNAMIC */
168	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
169};
170
171/*-------------------------------------------------------------------------*/
172
173static int acm_ms_bind(struct usb_composite_dev *cdev)
174{
175	struct usb_gadget	*gadget = cdev->gadget;
176	struct fsg_opts		*opts;
177	struct fsg_config	config;
178	int			status;
179
180	f_acm_inst = usb_get_function_instance("acm");
181	if (IS_ERR(f_acm_inst))
182		return PTR_ERR(f_acm_inst);
183
184	fi_msg = usb_get_function_instance("mass_storage");
185	if (IS_ERR(fi_msg)) {
186		status = PTR_ERR(fi_msg);
187		goto fail_get_msg;
188	}
189
190	/* set up mass storage function */
191	fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
192	opts = fsg_opts_from_func_inst(fi_msg);
193
194	opts->no_configfs = true;
195	status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
196	if (status)
197		goto fail;
198
199	status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
200	if (status)
201		goto fail_set_cdev;
202
203	fsg_common_set_sysfs(opts->common, true);
204	status = fsg_common_create_luns(opts->common, &config);
205	if (status)
206		goto fail_set_cdev;
207
208	fsg_common_set_inquiry_string(opts->common, config.vendor_name,
209				      config.product_name);
210	/*
211	 * Allocate string descriptor numbers ... note that string
212	 * contents can be overridden by the composite_dev glue.
213	 */
214	status = usb_string_ids_tab(cdev, strings_dev);
215	if (status < 0)
216		goto fail_string_ids;
217	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
218	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
219
220	/* register our configuration */
221	status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
222	if (status < 0)
223		goto fail_string_ids;
224
225	usb_composite_overwrite_options(cdev, &coverwrite);
226	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
227			DRIVER_DESC);
228	return 0;
229
230	/* error recovery */
231fail_string_ids:
232	fsg_common_remove_luns(opts->common);
233fail_set_cdev:
234	fsg_common_free_buffers(opts->common);
235fail:
236	usb_put_function_instance(fi_msg);
237fail_get_msg:
238	usb_put_function_instance(f_acm_inst);
239	return status;
240}
241
242static int acm_ms_unbind(struct usb_composite_dev *cdev)
243{
244	usb_put_function(f_msg);
245	usb_put_function_instance(fi_msg);
246	usb_put_function(f_acm);
247	usb_put_function_instance(f_acm_inst);
248	return 0;
249}
250
251static struct usb_composite_driver acm_ms_driver = {
252	.name		= "g_acm_ms",
253	.dev		= &device_desc,
254	.max_speed	= USB_SPEED_SUPER,
255	.strings	= dev_strings,
256	.bind		= acm_ms_bind,
257	.unbind		= acm_ms_unbind,
258};
259
260module_usb_composite_driver(acm_ms_driver);
261
262MODULE_DESCRIPTION(DRIVER_DESC);
263MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>");
264MODULE_LICENSE("GPL v2");
265