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