blob: 36d67a32abefebbf410ea10a8ac22915a819cf8e [file] [log] [blame]
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +01001/*
2 * multi.c -- Multifunction Composite driver
3 *
4 * Copyright (C) 2008 David Brownell
5 * Copyright (C) 2008 Nokia Corporation
6 * Copyright (C) 2009 Samsung Electronics
7 * Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24
25#include <linux/kernel.h>
26#include <linux/utsname.h>
Michal Nazarewicz279cc492010-06-21 13:57:03 +020027#include <linux/module.h>
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +010028
29
Michal Nazarewicz396cda92009-11-30 10:55:40 +010030#if defined USB_ETH_RNDIS
31# undef USB_ETH_RNDIS
32#endif
Michal Nazarewiczdbe4a992010-01-22 15:18:21 +010033#ifdef CONFIG_USB_G_MULTI_RNDIS
Michal Nazarewicz396cda92009-11-30 10:55:40 +010034# define USB_ETH_RNDIS y
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +010035#endif
36
37
38#define DRIVER_DESC "Multifunction Composite Gadget"
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +010039
Michal Nazarewicz279cc492010-06-21 13:57:03 +020040MODULE_DESCRIPTION(DRIVER_DESC);
41MODULE_AUTHOR("Michal Nazarewicz");
42MODULE_LICENSE("GPL");
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +010043
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +010044
Michal Nazarewicz279cc492010-06-21 13:57:03 +020045/***************************** All the files... *****************************/
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +010046
47/*
48 * kbuild is not very cooperative with respect to linking separately
49 * compiled library objects into one module. So for now we won't use
50 * separate compilation ... ensuring init/exit sections work to shrink
51 * the runtime footprint, and giving us at least some parts of what
52 * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
53 */
54
55#include "composite.c"
56#include "usbstring.c"
57#include "config.c"
58#include "epautoconf.c"
59
Michal Nazarewicz279cc492010-06-21 13:57:03 +020060#include "f_mass_storage.c"
61
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +010062#include "u_serial.c"
63#include "f_acm.c"
64
65#include "f_ecm.c"
66#include "f_subset.c"
Michal Nazarewicz396cda92009-11-30 10:55:40 +010067#ifdef USB_ETH_RNDIS
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +010068# include "f_rndis.c"
69# include "rndis.c"
70#endif
71#include "u_ether.c"
72
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +010073
Michal Nazarewicz279cc492010-06-21 13:57:03 +020074
75/***************************** Device Descriptor ****************************/
76
Michal Nazarewicz1c6529e2010-08-12 17:43:44 +020077#define MULTI_VENDOR_NUM 0x1d6b /* Linux Foundation */
78#define MULTI_PRODUCT_NUM 0x0104 /* Multifunction Composite Gadget */
Michal Nazarewicz279cc492010-06-21 13:57:03 +020079
80
81enum {
82 __MULTI_NO_CONFIG,
83#ifdef CONFIG_USB_G_MULTI_RNDIS
84 MULTI_RNDIS_CONFIG_NUM,
85#endif
86#ifdef CONFIG_USB_G_MULTI_CDC
87 MULTI_CDC_CONFIG_NUM,
88#endif
89};
90
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +010091
92static struct usb_device_descriptor device_desc = {
93 .bLength = sizeof device_desc,
94 .bDescriptorType = USB_DT_DEVICE,
95
96 .bcdUSB = cpu_to_le16(0x0200),
97
Michal Nazarewicz279cc492010-06-21 13:57:03 +020098 .bDeviceClass = USB_CLASS_MISC /* 0xEF */,
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +010099 .bDeviceSubClass = 2,
100 .bDeviceProtocol = 1,
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100101
102 /* Vendor and product id can be overridden by module parameters. */
103 .idVendor = cpu_to_le16(MULTI_VENDOR_NUM),
104 .idProduct = cpu_to_le16(MULTI_PRODUCT_NUM),
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100105};
106
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100107
108static const struct usb_descriptor_header *otg_desc[] = {
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200109 (struct usb_descriptor_header *) &(struct usb_otg_descriptor){
110 .bLength = sizeof(struct usb_otg_descriptor),
111 .bDescriptorType = USB_DT_OTG,
112
113 /*
114 * REVISIT SRP-only hardware is possible, although
115 * it would not be called "OTG" ...
116 */
117 .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
118 },
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100119 NULL,
120};
121
122
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200123enum {
124 MULTI_STRING_MANUFACTURER_IDX,
125 MULTI_STRING_PRODUCT_IDX,
126#ifdef CONFIG_USB_G_MULTI_RNDIS
127 MULTI_STRING_RNDIS_CONFIG_IDX,
128#endif
129#ifdef CONFIG_USB_G_MULTI_CDC
130 MULTI_STRING_CDC_CONFIG_IDX,
131#endif
132};
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100133
134static char manufacturer[50];
135
136static struct usb_string strings_dev[] = {
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200137 [MULTI_STRING_MANUFACTURER_IDX].s = manufacturer,
138 [MULTI_STRING_PRODUCT_IDX].s = DRIVER_DESC,
139#ifdef CONFIG_USB_G_MULTI_RNDIS
140 [MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS",
141#endif
142#ifdef CONFIG_USB_G_MULTI_CDC
143 [MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM",
144#endif
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100145 { } /* end of list */
146};
147
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100148static struct usb_gadget_strings *dev_strings[] = {
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200149 &(struct usb_gadget_strings){
150 .language = 0x0409, /* en-us */
151 .strings = strings_dev,
152 },
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100153 NULL,
154};
155
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100156
157
158
159/****************************** Configurations ******************************/
160
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200161static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
162FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100163
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200164static struct fsg_common fsg_common;
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100165
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200166static u8 hostaddr[ETH_ALEN];
167
168
169/********** RNDIS **********/
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100170
Michal Nazarewicz396cda92009-11-30 10:55:40 +0100171#ifdef USB_ETH_RNDIS
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100172
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200173static __ref int rndis_do_config(struct usb_configuration *c)
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100174{
175 int ret;
176
177 if (gadget_is_otg(c->cdev->gadget)) {
178 c->descriptors = otg_desc;
179 c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
180 }
181
182 ret = rndis_bind_config(c, hostaddr);
183 if (ret < 0)
184 return ret;
185
186 ret = acm_bind_config(c, 0);
187 if (ret < 0)
188 return ret;
189
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200190 ret = fsg_bind_config(c->cdev, c, &fsg_common);
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100191 if (ret < 0)
192 return ret;
193
194 return 0;
195}
196
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200197static int rndis_config_register(struct usb_composite_dev *cdev)
198{
199 static struct usb_configuration config = {
200 .bind = rndis_do_config,
201 .bConfigurationValue = MULTI_RNDIS_CONFIG_NUM,
202 .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
203 };
204
205 config.label = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].s;
206 config.iConfiguration = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].id;
207
208 return usb_add_config(cdev, &config);
209}
210
211#else
212
213static int rndis_config_register(struct usb_composite_dev *cdev)
214{
215 return 0;
216}
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100217
218#endif
219
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200220
221/********** CDC ECM **********/
222
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100223#ifdef CONFIG_USB_G_MULTI_CDC
224
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200225static __ref int cdc_do_config(struct usb_configuration *c)
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100226{
227 int ret;
228
229 if (gadget_is_otg(c->cdev->gadget)) {
230 c->descriptors = otg_desc;
231 c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
232 }
233
234 ret = ecm_bind_config(c, hostaddr);
235 if (ret < 0)
236 return ret;
237
238 ret = acm_bind_config(c, 0);
239 if (ret < 0)
240 return ret;
241
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200242 ret = fsg_bind_config(c->cdev, c, &fsg_common);
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100243 if (ret < 0)
244 return ret;
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100245
246 return 0;
247}
248
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200249static int cdc_config_register(struct usb_composite_dev *cdev)
250{
251 static struct usb_configuration config = {
252 .bind = cdc_do_config,
253 .bConfigurationValue = MULTI_CDC_CONFIG_NUM,
254 .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
255 };
256
257 config.label = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].s;
258 config.iConfiguration = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].id;
259
260 return usb_add_config(cdev, &config);
261}
262
263#else
264
265static int cdc_config_register(struct usb_composite_dev *cdev)
266{
267 return 0;
268}
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100269
270#endif
271
272
273
274/****************************** Gadget Bind ******************************/
275
276
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200277static int __ref multi_bind(struct usb_composite_dev *cdev)
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100278{
279 struct usb_gadget *gadget = cdev->gadget;
280 int status, gcnum;
281
282 if (!can_support_ecm(cdev->gadget)) {
283 dev_err(&gadget->dev, "controller '%s' not usable\n",
284 gadget->name);
285 return -EINVAL;
286 }
287
288 /* set up network link layer */
289 status = gether_setup(cdev->gadget, hostaddr);
290 if (status < 0)
291 return status;
292
293 /* set up serial link layer */
294 status = gserial_setup(cdev->gadget, 1);
295 if (status < 0)
296 goto fail0;
297
298 /* set up mass storage function */
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200299 {
300 void *retp;
301 retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
302 if (IS_ERR(retp)) {
303 status = PTR_ERR(retp);
304 goto fail1;
305 }
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100306 }
307
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200308 /* set bcdDevice */
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100309 gcnum = usb_gadget_controller_number(gadget);
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200310 if (gcnum >= 0) {
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100311 device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200312 } else {
313 WARNING(cdev, "controller '%s' not recognized\n", gadget->name);
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100314 device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
315 }
316
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200317 /* allocate string descriptor numbers */
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100318 snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
319 init_utsname()->sysname, init_utsname()->release,
320 gadget->name);
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100321
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200322 status = usb_string_ids_tab(cdev, strings_dev);
323 if (unlikely(status < 0))
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100324 goto fail2;
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100325
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200326 device_desc.iManufacturer =
327 strings_dev[MULTI_STRING_MANUFACTURER_IDX].id;
328 device_desc.iProduct =
329 strings_dev[MULTI_STRING_PRODUCT_IDX].id;
330
331 /* register configurations */
332 status = rndis_config_register(cdev);
333 if (unlikely(status < 0))
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100334 goto fail2;
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100335
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200336 status = cdc_config_register(cdev);
337 if (unlikely(status < 0))
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100338 goto fail2;
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100339
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200340 /* we're done */
341 dev_info(&gadget->dev, DRIVER_DESC "\n");
342 fsg_common_put(&fsg_common);
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100343 return 0;
344
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200345
346 /* error recovery */
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100347fail2:
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200348 fsg_common_put(&fsg_common);
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100349fail1:
350 gserial_cleanup();
351fail0:
352 gether_cleanup();
353 return status;
354}
355
356static int __exit multi_unbind(struct usb_composite_dev *cdev)
357{
358 gserial_cleanup();
359 gether_cleanup();
360 return 0;
361}
362
363
364/****************************** Some noise ******************************/
365
366
367static struct usb_composite_driver multi_driver = {
368 .name = "g_multi",
369 .dev = &device_desc,
370 .strings = dev_strings,
371 .bind = multi_bind,
372 .unbind = __exit_p(multi_unbind),
373};
374
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100375
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200376static int __init multi_init(void)
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100377{
378 return usb_composite_register(&multi_driver);
379}
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200380module_init(multi_init);
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100381
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200382static void __exit multi_exit(void)
Michal Nazarewiczf176a5d2009-11-09 14:15:27 +0100383{
384 usb_composite_unregister(&multi_driver);
385}
Michal Nazarewicz279cc492010-06-21 13:57:03 +0200386module_exit(multi_exit);