]>
Commit | Line | Data |
---|---|---|
c6c56008 MN |
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 | ||
34 | static 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 | ||
50 | MODULE_DESCRIPTION(DRIVER_DESC); | |
51 | MODULE_AUTHOR("Michal Nazarewicz"); | |
52 | MODULE_LICENSE("GPL"); | |
53 | ||
54 | ||
55 | static unsigned short gfs_vendor_id = 0x0525; /* XXX NetChip */ | |
56 | static unsigned short gfs_product_id = 0xa4ac; /* XXX */ | |
57 | ||
58 | static 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 | ||
78 | module_param_named(usb_class, gfs_dev_desc.bDeviceClass, byte, 0644); | |
79 | GFS_MODULE_PARAM_DESC(usb_class, bDeviceClass); | |
80 | module_param_named(usb_subclass, gfs_dev_desc.bDeviceSubClass, byte, 0644); | |
81 | GFS_MODULE_PARAM_DESC(usb_subclass, bDeviceSubClass); | |
82 | module_param_named(usb_protocol, gfs_dev_desc.bDeviceProtocol, byte, 0644); | |
83 | GFS_MODULE_PARAM_DESC(usb_protocol, bDeviceProtocol); | |
84 | module_param_named(usb_vendor, gfs_vendor_id, ushort, 0644); | |
85 | GFS_MODULE_PARAM_DESC(usb_vendor, idVendor); | |
86 | module_param_named(usb_product, gfs_product_id, ushort, 0644); | |
87 | GFS_MODULE_PARAM_DESC(usb_product, idProduct); | |
88 | ||
89 | ||
90 | ||
91 | static 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 | ||
107 | enum { | |
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 | ||
121 | static char gfs_manufacturer[50]; | |
122 | static const char gfs_driver_desc[] = DRIVER_DESC; | |
123 | static const char gfs_short_name[] = DRIVER_NAME; | |
124 | ||
125 | static 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 | ||
140 | static 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 | |
150 | static int gfs_do_rndis_config(struct usb_configuration *c); | |
151 | ||
152 | static 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 | |
167 | static int gfs_do_ecm_config(struct usb_configuration *c); | |
168 | ||
169 | static 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 | |
184 | static int gfs_do_generic_config(struct usb_configuration *c); | |
185 | ||
186 | static 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 | ||
200 | static int gfs_bind(struct usb_composite_dev *cdev); | |
201 | static int gfs_unbind(struct usb_composite_dev *cdev); | |
202 | ||
203 | static 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 | ||
212 | static struct ffs_data *gfs_ffs_data; | |
213 | static unsigned long gfs_registered; | |
214 | ||
215 | ||
216 | static int gfs_init(void) | |
217 | { | |
218 | ENTER(); | |
219 | ||
220 | return functionfs_init(); | |
221 | } | |
222 | module_init(gfs_init); | |
223 | ||
224 | static 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 | } | |
233 | module_exit(gfs_exit); | |
234 | ||
235 | ||
236 | static 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 | ||
252 | static 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 | ||
261 | static int functionfs_check_dev_callback(const char *dev_name) | |
262 | { | |
263 | return 0; | |
264 | } | |
265 | ||
266 | ||
267 | ||
268 | static 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 | ||
341 | error_unbind: | |
342 | functionfs_unbind(gfs_ffs_data); | |
343 | error: | |
344 | gether_cleanup(); | |
345 | error_quick: | |
346 | gfs_ffs_data = NULL; | |
347 | return ret; | |
348 | } | |
349 | ||
350 | static 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 | ||
371 | static 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 | ||
f588c0db MN |
395 | /* After previous do_configs there may be some invalid |
396 | * pointers in c->interface array. This happens every time | |
397 | * a user space function with fewer interfaces than a user | |
398 | * space function that was run before the new one is run. The | |
399 | * compasit's set_config() assumes that if there is no more | |
400 | * then MAX_CONFIG_INTERFACES interfaces in a configuration | |
401 | * then there is a NULL pointer after the last interface in | |
402 | * c->interface array. We need to make sure this is true. */ | |
403 | if (c->next_interface_id < ARRAY_SIZE(c->interface)) | |
404 | c->interface[c->next_interface_id] = NULL; | |
405 | ||
c6c56008 MN |
406 | return 0; |
407 | } | |
408 | ||
409 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS | |
410 | static int gfs_do_rndis_config(struct usb_configuration *c) | |
411 | { | |
412 | ENTER(); | |
413 | ||
414 | return __gfs_do_config(c, rndis_bind_config, gfs_hostaddr); | |
415 | } | |
416 | #endif | |
417 | ||
418 | #ifdef CONFIG_USB_FUNCTIONFS_ETH | |
419 | static int gfs_do_ecm_config(struct usb_configuration *c) | |
420 | { | |
421 | ENTER(); | |
422 | ||
423 | return __gfs_do_config(c, | |
424 | can_support_ecm(c->cdev->gadget) | |
425 | ? ecm_bind_config : geth_bind_config, | |
426 | gfs_hostaddr); | |
427 | } | |
428 | #endif | |
429 | ||
430 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC | |
431 | static int gfs_do_generic_config(struct usb_configuration *c) | |
432 | { | |
433 | ENTER(); | |
434 | ||
435 | return __gfs_do_config(c, NULL, NULL); | |
436 | } | |
437 | #endif |