Commit | Line | Data |
---|---|---|
5ab54cf7 MN |
1 | /* |
2 | * g_ffs.c -- user mode file system API for USB composite function controllers | |
3 | * | |
4 | * Copyright (C) 2010 Samsung Electronics | |
5 | * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
5ab54cf7 MN |
11 | */ |
12 | ||
aa02f172 MN |
13 | #define pr_fmt(fmt) "g_ffs: " fmt |
14 | ||
c6c56008 MN |
15 | #include <linux/module.h> |
16 | #include <linux/utsname.h> | |
17 | ||
c6c56008 MN |
18 | /* |
19 | * kbuild is not very cooperative with respect to linking separately | |
20 | * compiled library objects into one module. So for now we won't use | |
21 | * separate compilation ... ensuring init/exit sections work to shrink | |
22 | * the runtime footprint, and giving us at least some parts of what | |
23 | * a "gcc --combine ... part1.c part2.c part3.c ... " build would. | |
24 | */ | |
25 | ||
26 | #include "composite.c" | |
27 | #include "usbstring.c" | |
28 | #include "config.c" | |
29 | #include "epautoconf.c" | |
30 | ||
31 | #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS | |
32 | # if defined USB_ETH_RNDIS | |
33 | # undef USB_ETH_RNDIS | |
34 | # endif | |
35 | # ifdef CONFIG_USB_FUNCTIONFS_RNDIS | |
36 | # define USB_ETH_RNDIS y | |
37 | # endif | |
38 | ||
39 | # include "f_ecm.c" | |
40 | # include "f_subset.c" | |
41 | # ifdef USB_ETH_RNDIS | |
42 | # include "f_rndis.c" | |
43 | # include "rndis.c" | |
44 | # endif | |
45 | # include "u_ether.c" | |
46 | ||
47 | static u8 gfs_hostaddr[ETH_ALEN]; | |
f8dae531 MN |
48 | # ifdef CONFIG_USB_FUNCTIONFS_ETH |
49 | static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); | |
c6c56008 | 50 | # endif |
f8dae531 | 51 | #else |
c6c56008 MN |
52 | # define gether_cleanup() do { } while (0) |
53 | # define gether_setup(gadget, hostaddr) ((int)0) | |
f8dae531 | 54 | # define gfs_hostaddr NULL |
c6c56008 MN |
55 | #endif |
56 | ||
57 | #include "f_fs.c" | |
58 | ||
c6c56008 MN |
59 | #define DRIVER_NAME "g_ffs" |
60 | #define DRIVER_DESC "USB Function Filesystem" | |
61 | #define DRIVER_VERSION "24 Aug 2004" | |
62 | ||
63 | MODULE_DESCRIPTION(DRIVER_DESC); | |
64 | MODULE_AUTHOR("Michal Nazarewicz"); | |
65 | MODULE_LICENSE("GPL"); | |
66 | ||
fc19de61 MN |
67 | #define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */ |
68 | #define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */ | |
c6c56008 MN |
69 | |
70 | static struct usb_device_descriptor gfs_dev_desc = { | |
71 | .bLength = sizeof gfs_dev_desc, | |
72 | .bDescriptorType = USB_DT_DEVICE, | |
73 | ||
74 | .bcdUSB = cpu_to_le16(0x0200), | |
75 | .bDeviceClass = USB_CLASS_PER_INTERFACE, | |
76 | ||
fc19de61 MN |
77 | .idVendor = cpu_to_le16(GFS_VENDOR_ID), |
78 | .idProduct = cpu_to_le16(GFS_PRODUCT_ID), | |
c6c56008 MN |
79 | }; |
80 | ||
fc19de61 MN |
81 | module_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644); |
82 | MODULE_PARM_DESC(bDeviceClass, "USB Device class"); | |
83 | module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644); | |
84 | MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass"); | |
85 | module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644); | |
86 | MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol"); | |
c6c56008 | 87 | |
c6c56008 MN |
88 | static const struct usb_descriptor_header *gfs_otg_desc[] = { |
89 | (const struct usb_descriptor_header *) | |
90 | &(const struct usb_otg_descriptor) { | |
91 | .bLength = sizeof(struct usb_otg_descriptor), | |
92 | .bDescriptorType = USB_DT_OTG, | |
93 | ||
fc19de61 MN |
94 | /* |
95 | * REVISIT SRP-only hardware is possible, although | |
96 | * it would not be called "OTG" ... | |
97 | */ | |
c6c56008 MN |
98 | .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, |
99 | }, | |
100 | ||
101 | NULL | |
102 | }; | |
103 | ||
5ab54cf7 | 104 | /* String IDs are assigned dynamically */ |
c6c56008 | 105 | static struct usb_string gfs_strings[] = { |
c6c56008 | 106 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS |
f8dae531 | 107 | { .s = "FunctionFS + RNDIS" }, |
c6c56008 MN |
108 | #endif |
109 | #ifdef CONFIG_USB_FUNCTIONFS_ETH | |
f8dae531 | 110 | { .s = "FunctionFS + ECM" }, |
c6c56008 MN |
111 | #endif |
112 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC | |
f8dae531 | 113 | { .s = "FunctionFS" }, |
c6c56008 MN |
114 | #endif |
115 | { } /* end of list */ | |
116 | }; | |
117 | ||
118 | static struct usb_gadget_strings *gfs_dev_strings[] = { | |
119 | &(struct usb_gadget_strings) { | |
120 | .language = 0x0409, /* en-us */ | |
121 | .strings = gfs_strings, | |
122 | }, | |
123 | NULL, | |
124 | }; | |
125 | ||
f8dae531 MN |
126 | struct gfs_configuration { |
127 | struct usb_configuration c; | |
128 | int (*eth)(struct usb_configuration *c, u8 *ethaddr); | |
129 | } gfs_configurations[] = { | |
c6c56008 | 130 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS |
f8dae531 MN |
131 | { |
132 | .eth = rndis_bind_config, | |
133 | }, | |
c6c56008 MN |
134 | #endif |
135 | ||
c6c56008 | 136 | #ifdef CONFIG_USB_FUNCTIONFS_ETH |
f8dae531 MN |
137 | { |
138 | .eth = eth_bind_config, | |
139 | }, | |
c6c56008 MN |
140 | #endif |
141 | ||
c6c56008 | 142 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC |
f8dae531 MN |
143 | { |
144 | }, | |
c6c56008 | 145 | #endif |
f8dae531 | 146 | }; |
c6c56008 | 147 | |
c6c56008 MN |
148 | static int gfs_bind(struct usb_composite_dev *cdev); |
149 | static int gfs_unbind(struct usb_composite_dev *cdev); | |
f8dae531 | 150 | static int gfs_do_config(struct usb_configuration *c); |
c6c56008 MN |
151 | |
152 | static struct usb_composite_driver gfs_driver = { | |
fc19de61 | 153 | .name = DRIVER_NAME, |
c6c56008 MN |
154 | .dev = &gfs_dev_desc, |
155 | .strings = gfs_dev_strings, | |
35a0e0bf | 156 | .max_speed = USB_SPEED_HIGH, |
c6c56008 | 157 | .unbind = gfs_unbind, |
fc19de61 | 158 | .iProduct = DRIVER_DESC, |
c6c56008 MN |
159 | }; |
160 | ||
c6c56008 MN |
161 | static struct ffs_data *gfs_ffs_data; |
162 | static unsigned long gfs_registered; | |
163 | ||
c6c56008 MN |
164 | static int gfs_init(void) |
165 | { | |
166 | ENTER(); | |
167 | ||
168 | return functionfs_init(); | |
169 | } | |
170 | module_init(gfs_init); | |
171 | ||
172 | static void gfs_exit(void) | |
173 | { | |
174 | ENTER(); | |
175 | ||
176 | if (test_and_clear_bit(0, &gfs_registered)) | |
177 | usb_composite_unregister(&gfs_driver); | |
178 | ||
179 | functionfs_cleanup(); | |
180 | } | |
181 | module_exit(gfs_exit); | |
182 | ||
c6c56008 MN |
183 | static int functionfs_ready_callback(struct ffs_data *ffs) |
184 | { | |
185 | int ret; | |
186 | ||
187 | ENTER(); | |
188 | ||
189 | if (WARN_ON(test_and_set_bit(0, &gfs_registered))) | |
190 | return -EBUSY; | |
191 | ||
192 | gfs_ffs_data = ffs; | |
07a18bd7 | 193 | ret = usb_composite_probe(&gfs_driver, gfs_bind); |
c6c56008 MN |
194 | if (unlikely(ret < 0)) |
195 | clear_bit(0, &gfs_registered); | |
196 | return ret; | |
197 | } | |
198 | ||
199 | static void functionfs_closed_callback(struct ffs_data *ffs) | |
200 | { | |
201 | ENTER(); | |
202 | ||
203 | if (test_and_clear_bit(0, &gfs_registered)) | |
204 | usb_composite_unregister(&gfs_driver); | |
205 | } | |
206 | ||
c6c56008 MN |
207 | static int functionfs_check_dev_callback(const char *dev_name) |
208 | { | |
209 | return 0; | |
210 | } | |
211 | ||
c6c56008 MN |
212 | static int gfs_bind(struct usb_composite_dev *cdev) |
213 | { | |
f8dae531 | 214 | int ret, i; |
c6c56008 MN |
215 | |
216 | ENTER(); | |
217 | ||
218 | if (WARN_ON(!gfs_ffs_data)) | |
219 | return -ENODEV; | |
220 | ||
221 | ret = gether_setup(cdev->gadget, gfs_hostaddr); | |
222 | if (unlikely(ret < 0)) | |
223 | goto error_quick; | |
224 | ||
f8dae531 | 225 | ret = usb_string_ids_tab(cdev, gfs_strings); |
c6c56008 MN |
226 | if (unlikely(ret < 0)) |
227 | goto error; | |
c6c56008 | 228 | |
c6c56008 MN |
229 | ret = functionfs_bind(gfs_ffs_data, cdev); |
230 | if (unlikely(ret < 0)) | |
231 | goto error; | |
232 | ||
f8dae531 MN |
233 | for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { |
234 | struct gfs_configuration *c = gfs_configurations + i; | |
c6c56008 | 235 | |
fc19de61 MN |
236 | c->c.label = gfs_strings[i].s; |
237 | c->c.iConfiguration = gfs_strings[i].id; | |
f8dae531 MN |
238 | c->c.bConfigurationValue = 1 + i; |
239 | c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER; | |
c6c56008 | 240 | |
c9bfff9c | 241 | ret = usb_add_config(cdev, &c->c, gfs_do_config); |
f8dae531 MN |
242 | if (unlikely(ret < 0)) |
243 | goto error_unbind; | |
244 | } | |
c6c56008 MN |
245 | |
246 | return 0; | |
247 | ||
248 | error_unbind: | |
249 | functionfs_unbind(gfs_ffs_data); | |
250 | error: | |
251 | gether_cleanup(); | |
252 | error_quick: | |
253 | gfs_ffs_data = NULL; | |
254 | return ret; | |
255 | } | |
256 | ||
257 | static int gfs_unbind(struct usb_composite_dev *cdev) | |
258 | { | |
259 | ENTER(); | |
260 | ||
fc19de61 MN |
261 | /* |
262 | * We may have been called in an error recovery from | |
c6c56008 MN |
263 | * composite_bind() after gfs_unbind() failure so we need to |
264 | * check if gfs_ffs_data is not NULL since gfs_bind() handles | |
265 | * all error recovery itself. I'd rather we werent called | |
266 | * from composite on orror recovery, but what you're gonna | |
fc19de61 MN |
267 | * do...? |
268 | */ | |
c6c56008 MN |
269 | if (gfs_ffs_data) { |
270 | gether_cleanup(); | |
271 | functionfs_unbind(gfs_ffs_data); | |
272 | gfs_ffs_data = NULL; | |
273 | } | |
274 | ||
275 | return 0; | |
276 | } | |
277 | ||
f8dae531 | 278 | static int gfs_do_config(struct usb_configuration *c) |
c6c56008 | 279 | { |
f8dae531 MN |
280 | struct gfs_configuration *gc = |
281 | container_of(c, struct gfs_configuration, c); | |
c6c56008 MN |
282 | int ret; |
283 | ||
284 | if (WARN_ON(!gfs_ffs_data)) | |
285 | return -ENODEV; | |
286 | ||
287 | if (gadget_is_otg(c->cdev->gadget)) { | |
288 | c->descriptors = gfs_otg_desc; | |
289 | c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | |
290 | } | |
291 | ||
f8dae531 MN |
292 | if (gc->eth) { |
293 | ret = gc->eth(c, gfs_hostaddr); | |
c6c56008 MN |
294 | if (unlikely(ret < 0)) |
295 | return ret; | |
296 | } | |
297 | ||
7898aee1 | 298 | ret = functionfs_bind_config(c->cdev, c, gfs_ffs_data); |
c6c56008 MN |
299 | if (unlikely(ret < 0)) |
300 | return ret; | |
301 | ||
fc19de61 MN |
302 | /* |
303 | * After previous do_configs there may be some invalid | |
f588c0db MN |
304 | * pointers in c->interface array. This happens every time |
305 | * a user space function with fewer interfaces than a user | |
306 | * space function that was run before the new one is run. The | |
307 | * compasit's set_config() assumes that if there is no more | |
308 | * then MAX_CONFIG_INTERFACES interfaces in a configuration | |
309 | * then there is a NULL pointer after the last interface in | |
fc19de61 MN |
310 | * c->interface array. We need to make sure this is true. |
311 | */ | |
f588c0db MN |
312 | if (c->next_interface_id < ARRAY_SIZE(c->interface)) |
313 | c->interface[c->next_interface_id] = NULL; | |
314 | ||
c6c56008 MN |
315 | return 0; |
316 | } | |
317 | ||
c6c56008 | 318 | #ifdef CONFIG_USB_FUNCTIONFS_ETH |
fc19de61 | 319 | |
f8dae531 | 320 | static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) |
c6c56008 | 321 | { |
f8dae531 MN |
322 | return can_support_ecm(c->cdev->gadget) |
323 | ? ecm_bind_config(c, ethaddr) | |
324 | : geth_bind_config(c, ethaddr); | |
c6c56008 | 325 | } |
fc19de61 | 326 | |
c6c56008 | 327 | #endif |