aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
authorMichal Nazarewicz <m.nazarewicz@samsung.com>2010-05-05 06:53:15 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-05-20 16:21:43 -0400
commitc6c560085172c16a0141ab12ae765c7d1be68279 (patch)
tree1263b2ff889568d6d49ddef2dfe32a58bc909ed9 /drivers/usb/gadget
parentddf8abd2599491cbad959c700b90ba72a5dce8d0 (diff)
USB: g_ffs: the FunctionFS gadget driver
The Function Filesystem (FunctioFS) lets one create USB composite functions in user space in the same way as GadgetFS lets one create USB gadgets in user space. This allows creation of composite gadgets such that some of the functions are implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> Cc: Kyungmin Park <kyungmin.park@samsung.com> Cc: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/Kconfig37
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/g_ffs.c426
3 files changed, 465 insertions, 0 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 96e1494723ad..0282c50d6404 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -710,6 +710,43 @@ config USB_GADGETFS
710 Say "y" to link the driver statically, or "m" to build a 710 Say "y" to link the driver statically, or "m" to build a
711 dynamically linked module called "gadgetfs". 711 dynamically linked module called "gadgetfs".
712 712
713config USB_FUNCTIONFS
714 tristate "Function Filesystem (EXPERIMENTAL)"
715 depends on EXPERIMENTAL
716 help
717 The Function Filesystem (FunctioFS) lets one create USB
718 composite functions in user space in the same way as GadgetFS
719 lets one create USB gadgets in user space. This allows creation
720 of composite gadgets such that some of the functions are
721 implemented in kernel space (for instance Ethernet, serial or
722 mass storage) and other are implemented in user space.
723
724 Say "y" to link the driver statically, or "m" to build
725 a dynamically linked module called "g_ffs".
726
727config USB_FUNCTIONFS_ETH
728 bool "Include CDC ECM (Ethernet) function"
729 depends on USB_FUNCTIONFS
730 help
731 Include an CDC ECM (Ethernet) funcion in the CDC ECM (Funcion)
732 Filesystem. If you also say "y" to the RNDIS query below the
733 gadget will have two configurations.
734
735config USB_FUNCTIONFS_RNDIS
736 bool "Include RNDIS (Ethernet) function"
737 depends on USB_FUNCTIONFS
738 help
739 Include an RNDIS (Ethernet) funcion in the Funcion Filesystem.
740 If you also say "y" to the CDC ECM query above the gadget will
741 have two configurations.
742
743config USB_FUNCTIONFS_GENERIC
744 bool "Include 'pure' configuration"
745 depends on USB_FUNCTIONFS && (USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
746 help
747 Include a configuration with FunctionFS and no Ethernet
748 configuration.
749
713config USB_FILE_STORAGE 750config USB_FILE_STORAGE
714 tristate "File-backed Storage Gadget" 751 tristate "File-backed Storage Gadget"
715 depends on BLOCK 752 depends on BLOCK
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 03a27d921c26..a55050c17893 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -51,6 +51,8 @@ obj-$(CONFIG_USB_ZERO) += g_zero.o
51obj-$(CONFIG_USB_AUDIO) += g_audio.o 51obj-$(CONFIG_USB_AUDIO) += g_audio.o
52obj-$(CONFIG_USB_ETH) += g_ether.o 52obj-$(CONFIG_USB_ETH) += g_ether.o
53obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o 53obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
54obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
55obj-$(CONFIG_USB_ETH_FUNCTIONFS) += g_eth_ffs.o
54obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o 56obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
55obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o 57obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
56obj-$(CONFIG_USB_G_SERIAL) += g_serial.o 58obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
new file mode 100644
index 000000000000..4b0e4a040d6f
--- /dev/null
+++ b/drivers/usb/gadget/g_ffs.c
@@ -0,0 +1,426 @@
1#include <linux/module.h>
2#include <linux/utsname.h>
3
4
5/*
6 * kbuild is not very cooperative with respect to linking separately
7 * compiled library objects into one module. So for now we won't use
8 * separate compilation ... ensuring init/exit sections work to shrink
9 * the runtime footprint, and giving us at least some parts of what
10 * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
11 */
12
13#include "composite.c"
14#include "usbstring.c"
15#include "config.c"
16#include "epautoconf.c"
17
18#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
19# if defined USB_ETH_RNDIS
20# undef USB_ETH_RNDIS
21# endif
22# ifdef CONFIG_USB_FUNCTIONFS_RNDIS
23# define USB_ETH_RNDIS y
24# endif
25
26# include "f_ecm.c"
27# include "f_subset.c"
28# ifdef USB_ETH_RNDIS
29# include "f_rndis.c"
30# include "rndis.c"
31# endif
32# include "u_ether.c"
33
34static u8 gfs_hostaddr[ETH_ALEN];
35#else
36# if !defined CONFIG_USB_FUNCTIONFS_GENERIC
37# define CONFIG_USB_FUNCTIONFS_GENERIC
38# endif
39# define gether_cleanup() do { } while (0)
40# define gether_setup(gadget, hostaddr) ((int)0)
41#endif
42
43#include "f_fs.c"
44
45
46#define DRIVER_NAME "g_ffs"
47#define DRIVER_DESC "USB Function Filesystem"
48#define DRIVER_VERSION "24 Aug 2004"
49
50MODULE_DESCRIPTION(DRIVER_DESC);
51MODULE_AUTHOR("Michal Nazarewicz");
52MODULE_LICENSE("GPL");
53
54
55static unsigned short gfs_vendor_id = 0x0525; /* XXX NetChip */
56static unsigned short gfs_product_id = 0xa4ac; /* XXX */
57
58static struct usb_device_descriptor gfs_dev_desc = {
59 .bLength = sizeof gfs_dev_desc,
60 .bDescriptorType = USB_DT_DEVICE,
61
62 .bcdUSB = cpu_to_le16(0x0200),
63 .bDeviceClass = USB_CLASS_PER_INTERFACE,
64
65 /* Vendor and product id can be overridden by module parameters. */
66 /* .idVendor = cpu_to_le16(gfs_vendor_id), */
67 /* .idProduct = cpu_to_le16(gfs_product_id), */
68 /* .bcdDevice = f(hardware) */
69 /* .iManufacturer = DYNAMIC */
70 /* .iProduct = DYNAMIC */
71 /* NO SERIAL NUMBER */
72 .bNumConfigurations = 1,
73};
74
75#define GFS_MODULE_PARAM_DESC(name, field) \
76 MODULE_PARM_DESC(name, "Value of the " #field " field of the device descriptor sent to the host. Takes effect only prior to the user-space driver registering to the FunctionFS.")
77
78module_param_named(usb_class, gfs_dev_desc.bDeviceClass, byte, 0644);
79GFS_MODULE_PARAM_DESC(usb_class, bDeviceClass);
80module_param_named(usb_subclass, gfs_dev_desc.bDeviceSubClass, byte, 0644);
81GFS_MODULE_PARAM_DESC(usb_subclass, bDeviceSubClass);
82module_param_named(usb_protocol, gfs_dev_desc.bDeviceProtocol, byte, 0644);
83GFS_MODULE_PARAM_DESC(usb_protocol, bDeviceProtocol);
84module_param_named(usb_vendor, gfs_vendor_id, ushort, 0644);
85GFS_MODULE_PARAM_DESC(usb_vendor, idVendor);
86module_param_named(usb_product, gfs_product_id, ushort, 0644);
87GFS_MODULE_PARAM_DESC(usb_product, idProduct);
88
89
90
91static const struct usb_descriptor_header *gfs_otg_desc[] = {
92 (const struct usb_descriptor_header *)
93 &(const struct usb_otg_descriptor) {
94 .bLength = sizeof(struct usb_otg_descriptor),
95 .bDescriptorType = USB_DT_OTG,
96
97 /* REVISIT SRP-only hardware is possible, although
98 * it would not be called "OTG" ... */
99 .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
100 },
101
102 NULL
103};
104
105/* string IDs are assigned dynamically */
106
107enum {
108 GFS_STRING_MANUFACTURER_IDX,
109 GFS_STRING_PRODUCT_IDX,
110#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
111 GFS_STRING_RNDIS_CONFIG_IDX,
112#endif
113#ifdef CONFIG_USB_FUNCTIONFS_ETH
114 GFS_STRING_ECM_CONFIG_IDX,
115#endif
116#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
117 GFS_STRING_GENERIC_CONFIG_IDX,
118#endif
119};
120
121static char gfs_manufacturer[50];
122static const char gfs_driver_desc[] = DRIVER_DESC;
123static const char gfs_short_name[] = DRIVER_NAME;
124
125static struct usb_string gfs_strings[] = {
126 [GFS_STRING_MANUFACTURER_IDX].s = gfs_manufacturer,
127 [GFS_STRING_PRODUCT_IDX].s = gfs_driver_desc,
128#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
129 [GFS_STRING_RNDIS_CONFIG_IDX].s = "FunctionFS + RNDIS",
130#endif
131#ifdef CONFIG_USB_FUNCTIONFS_ETH
132 [GFS_STRING_ECM_CONFIG_IDX].s = "FunctionFS + ECM",
133#endif
134#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
135 [GFS_STRING_GENERIC_CONFIG_IDX].s = "FunctionFS",
136#endif
137 { } /* end of list */
138};
139
140static struct usb_gadget_strings *gfs_dev_strings[] = {
141 &(struct usb_gadget_strings) {
142 .language = 0x0409, /* en-us */
143 .strings = gfs_strings,
144 },
145 NULL,
146};
147
148
149#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
150static int gfs_do_rndis_config(struct usb_configuration *c);
151
152static struct usb_configuration gfs_rndis_config_driver = {
153 .label = "FunctionFS + RNDIS",
154 .bind = gfs_do_rndis_config,
155 .bConfigurationValue = 1,
156 /* .iConfiguration = DYNAMIC */
157 .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
158};
159# define gfs_add_rndis_config(cdev) \
160 usb_add_config(cdev, &gfs_rndis_config_driver)
161#else
162# define gfs_add_rndis_config(cdev) 0
163#endif
164
165
166#ifdef CONFIG_USB_FUNCTIONFS_ETH
167static int gfs_do_ecm_config(struct usb_configuration *c);
168
169static struct usb_configuration gfs_ecm_config_driver = {
170 .label = "FunctionFS + ECM",
171 .bind = gfs_do_ecm_config,
172 .bConfigurationValue = 1,
173 /* .iConfiguration = DYNAMIC */
174 .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
175};
176# define gfs_add_ecm_config(cdev) \
177 usb_add_config(cdev, &gfs_ecm_config_driver)
178#else
179# define gfs_add_ecm_config(cdev) 0
180#endif
181
182
183#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
184static int gfs_do_generic_config(struct usb_configuration *c);
185
186static struct usb_configuration gfs_generic_config_driver = {
187 .label = "FunctionFS",
188 .bind = gfs_do_generic_config,
189 .bConfigurationValue = 2,
190 /* .iConfiguration = DYNAMIC */
191 .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
192};
193# define gfs_add_generic_config(cdev) \
194 usb_add_config(cdev, &gfs_generic_config_driver)
195#else
196# define gfs_add_generic_config(cdev) 0
197#endif
198
199
200static int gfs_bind(struct usb_composite_dev *cdev);
201static int gfs_unbind(struct usb_composite_dev *cdev);
202
203static struct usb_composite_driver gfs_driver = {
204 .name = gfs_short_name,
205 .dev = &gfs_dev_desc,
206 .strings = gfs_dev_strings,
207 .bind = gfs_bind,
208 .unbind = gfs_unbind,
209};
210
211
212static struct ffs_data *gfs_ffs_data;
213static unsigned long gfs_registered;
214
215
216static int gfs_init(void)
217{
218 ENTER();
219
220 return functionfs_init();
221}
222module_init(gfs_init);
223
224static void gfs_exit(void)
225{
226 ENTER();
227
228 if (test_and_clear_bit(0, &gfs_registered))
229 usb_composite_unregister(&gfs_driver);
230
231 functionfs_cleanup();
232}
233module_exit(gfs_exit);
234
235
236static int functionfs_ready_callback(struct ffs_data *ffs)
237{
238 int ret;
239
240 ENTER();
241
242 if (WARN_ON(test_and_set_bit(0, &gfs_registered)))
243 return -EBUSY;
244
245 gfs_ffs_data = ffs;
246 ret = usb_composite_register(&gfs_driver);
247 if (unlikely(ret < 0))
248 clear_bit(0, &gfs_registered);
249 return ret;
250}
251
252static void functionfs_closed_callback(struct ffs_data *ffs)
253{
254 ENTER();
255
256 if (test_and_clear_bit(0, &gfs_registered))
257 usb_composite_unregister(&gfs_driver);
258}
259
260
261static int functionfs_check_dev_callback(const char *dev_name)
262{
263 return 0;
264}
265
266
267
268static int gfs_bind(struct usb_composite_dev *cdev)
269{
270 int ret;
271
272 ENTER();
273
274 if (WARN_ON(!gfs_ffs_data))
275 return -ENODEV;
276
277 ret = gether_setup(cdev->gadget, gfs_hostaddr);
278 if (unlikely(ret < 0))
279 goto error_quick;
280
281 gfs_dev_desc.idVendor = cpu_to_le16(gfs_vendor_id);
282 gfs_dev_desc.idProduct = cpu_to_le16(gfs_product_id);
283
284 snprintf(gfs_manufacturer, sizeof gfs_manufacturer, "%s %s with %s",
285 init_utsname()->sysname, init_utsname()->release,
286 cdev->gadget->name);
287 ret = usb_string_id(cdev);
288 if (unlikely(ret < 0))
289 goto error;
290 gfs_strings[GFS_STRING_MANUFACTURER_IDX].id = ret;
291 gfs_dev_desc.iManufacturer = ret;
292
293 ret = usb_string_id(cdev);
294 if (unlikely(ret < 0))
295 goto error;
296 gfs_strings[GFS_STRING_PRODUCT_IDX].id = ret;
297 gfs_dev_desc.iProduct = ret;
298
299#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
300 ret = usb_string_id(cdev);
301 if (unlikely(ret < 0))
302 goto error;
303 gfs_strings[GFS_STRING_RNDIS_CONFIG_IDX].id = ret;
304 gfs_rndis_config_driver.iConfiguration = ret;
305#endif
306
307#ifdef CONFIG_USB_FUNCTIONFS_ETH
308 ret = usb_string_id(cdev);
309 if (unlikely(ret < 0))
310 goto error;
311 gfs_strings[GFS_STRING_ECM_CONFIG_IDX].id = ret;
312 gfs_ecm_config_driver.iConfiguration = ret;
313#endif
314
315#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
316 ret = usb_string_id(cdev);
317 if (unlikely(ret < 0))
318 goto error;
319 gfs_strings[GFS_STRING_GENERIC_CONFIG_IDX].id = ret;
320 gfs_generic_config_driver.iConfiguration = ret;
321#endif
322
323 ret = functionfs_bind(gfs_ffs_data, cdev);
324 if (unlikely(ret < 0))
325 goto error;
326
327 ret = gfs_add_rndis_config(cdev);
328 if (unlikely(ret < 0))
329 goto error_unbind;
330
331 ret = gfs_add_ecm_config(cdev);
332 if (unlikely(ret < 0))
333 goto error_unbind;
334
335 ret = gfs_add_generic_config(cdev);
336 if (unlikely(ret < 0))
337 goto error_unbind;
338
339 return 0;
340
341error_unbind:
342 functionfs_unbind(gfs_ffs_data);
343error:
344 gether_cleanup();
345error_quick:
346 gfs_ffs_data = NULL;
347 return ret;
348}
349
350static int gfs_unbind(struct usb_composite_dev *cdev)
351{
352 ENTER();
353
354 /* We may have been called in an error recovery frem
355 * composite_bind() after gfs_unbind() failure so we need to
356 * check if gfs_ffs_data is not NULL since gfs_bind() handles
357 * all error recovery itself. I'd rather we werent called
358 * from composite on orror recovery, but what you're gonna
359 * do...? */
360
361 if (gfs_ffs_data) {
362 gether_cleanup();
363 functionfs_unbind(gfs_ffs_data);
364 gfs_ffs_data = NULL;
365 }
366
367 return 0;
368}
369
370
371static int __gfs_do_config(struct usb_configuration *c,
372 int (*eth)(struct usb_configuration *c, u8 *ethaddr),
373 u8 *ethaddr)
374{
375 int ret;
376
377 if (WARN_ON(!gfs_ffs_data))
378 return -ENODEV;
379
380 if (gadget_is_otg(c->cdev->gadget)) {
381 c->descriptors = gfs_otg_desc;
382 c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
383 }
384
385 if (eth) {
386 ret = eth(c, ethaddr);
387 if (unlikely(ret < 0))
388 return ret;
389 }
390
391 ret = functionfs_add(c->cdev, c, gfs_ffs_data);
392 if (unlikely(ret < 0))
393 return ret;
394
395 return 0;
396}
397
398#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
399static int gfs_do_rndis_config(struct usb_configuration *c)
400{
401 ENTER();
402
403 return __gfs_do_config(c, rndis_bind_config, gfs_hostaddr);
404}
405#endif
406
407#ifdef CONFIG_USB_FUNCTIONFS_ETH
408static int gfs_do_ecm_config(struct usb_configuration *c)
409{
410 ENTER();
411
412 return __gfs_do_config(c,
413 can_support_ecm(c->cdev->gadget)
414 ? ecm_bind_config : geth_bind_config,
415 gfs_hostaddr);
416}
417#endif
418
419#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
420static int gfs_do_generic_config(struct usb_configuration *c)
421{
422 ENTER();
423
424 return __gfs_do_config(c, NULL, NULL);
425}
426#endif