diff options
author | Andrzej Pietrasiewicz <andrzej.p@samsung.com> | 2012-05-14 09:51:52 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-14 12:25:44 -0400 |
commit | 581791f5c7a480b2cc3431af9a6e799ffd51eb5e (patch) | |
tree | 3284544e5a67e91ec612fc0a715cff66b250a283 | |
parent | 304f0b2453ea377b8f987aa5f9e1ccda0e3adfa7 (diff) |
FunctionFS: enable multiple functions
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: Felipe Balbi <balbi@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | Documentation/usb/functionfs.txt | 67 | ||||
-rw-r--r-- | drivers/usb/gadget/f_fs.c | 38 | ||||
-rw-r--r-- | drivers/usb/gadget/g_ffs.c | 200 | ||||
-rw-r--r-- | include/linux/usb/functionfs.h | 4 |
4 files changed, 277 insertions, 32 deletions
diff --git a/Documentation/usb/functionfs.txt b/Documentation/usb/functionfs.txt new file mode 100644 index 000000000000..eaaaea019fc7 --- /dev/null +++ b/Documentation/usb/functionfs.txt | |||
@@ -0,0 +1,67 @@ | |||
1 | *How FunctionFS works* | ||
2 | |||
3 | From kernel point of view it is just a composite function with some | ||
4 | unique behaviour. It may be added to an USB configuration only after | ||
5 | the user space driver has registered by writing descriptors and | ||
6 | strings (the user space program has to provide the same information | ||
7 | that kernel level composite functions provide when they are added to | ||
8 | the configuration). | ||
9 | |||
10 | This in particular means that the composite initialisation functions | ||
11 | may not be in init section (ie. may not use the __init tag). | ||
12 | |||
13 | From user space point of view it is a file system which when | ||
14 | mounted provides an "ep0" file. User space driver need to | ||
15 | write descriptors and strings to that file. It does not need | ||
16 | to worry about endpoints, interfaces or strings numbers but | ||
17 | simply provide descriptors such as if the function was the | ||
18 | only one (endpoints and strings numbers starting from one and | ||
19 | interface numbers starting from zero). The FunctionFS changes | ||
20 | them as needed also handling situation when numbers differ in | ||
21 | different configurations. | ||
22 | |||
23 | When descriptors and strings are written "ep#" files appear | ||
24 | (one for each declared endpoint) which handle communication on | ||
25 | a single endpoint. Again, FunctionFS takes care of the real | ||
26 | numbers and changing of the configuration (which means that | ||
27 | "ep1" file may be really mapped to (say) endpoint 3 (and when | ||
28 | configuration changes to (say) endpoint 2)). "ep0" is used | ||
29 | for receiving events and handling setup requests. | ||
30 | |||
31 | When all files are closed the function disables itself. | ||
32 | |||
33 | What I also want to mention is that the FunctionFS is designed in such | ||
34 | a way that it is possible to mount it several times so in the end | ||
35 | a gadget could use several FunctionFS functions. The idea is that | ||
36 | each FunctionFS instance is identified by the device name used | ||
37 | when mounting. | ||
38 | |||
39 | One can imagine a gadget that has an Ethernet, MTP and HID interfaces | ||
40 | where the last two are implemented via FunctionFS. On user space | ||
41 | level it would look like this: | ||
42 | |||
43 | $ insmod g_ffs.ko idVendor=<ID> iSerialNumber=<string> functions=mtp,hid | ||
44 | $ mkdir /dev/ffs-mtp && mount -t functionfs mtp /dev/ffs-mtp | ||
45 | $ ( cd /dev/ffs-mtp && mtp-daemon ) & | ||
46 | $ mkdir /dev/ffs-hid && mount -t functionfs hid /dev/ffs-hid | ||
47 | $ ( cd /dev/ffs-hid && hid-daemon ) & | ||
48 | |||
49 | On kernel level the gadget checks ffs_data->dev_name to identify | ||
50 | whether it's FunctionFS designed for MTP ("mtp") or HID ("hid"). | ||
51 | |||
52 | If no "functions" module parameters is supplied, the driver accepts | ||
53 | just one function with any name. | ||
54 | |||
55 | When "functions" module parameter is supplied, only functions | ||
56 | with listed names are accepted. In particular, if the "functions" | ||
57 | parameter's value is just a one-element list, then the behaviour | ||
58 | is similar to when there is no "functions" at all; however, | ||
59 | only a function with the specified name is accepted. | ||
60 | |||
61 | The gadget is registered only after all the declared function | ||
62 | filesystems have been mounted and USB descriptors of all functions | ||
63 | have been written to their ep0's. | ||
64 | |||
65 | Conversely, the gadget is unregistered after the first USB function | ||
66 | closes its endpoints. | ||
67 | |||
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 1d4c535671a7..dcd1c7fbb016 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c | |||
@@ -1031,6 +1031,12 @@ struct ffs_sb_fill_data { | |||
1031 | struct ffs_file_perms perms; | 1031 | struct ffs_file_perms perms; |
1032 | umode_t root_mode; | 1032 | umode_t root_mode; |
1033 | const char *dev_name; | 1033 | const char *dev_name; |
1034 | union { | ||
1035 | /* set by ffs_fs_mount(), read by ffs_sb_fill() */ | ||
1036 | void *private_data; | ||
1037 | /* set by ffs_sb_fill(), read by ffs_fs_mount */ | ||
1038 | struct ffs_data *ffs_data; | ||
1039 | }; | ||
1034 | }; | 1040 | }; |
1035 | 1041 | ||
1036 | static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) | 1042 | static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) |
@@ -1047,8 +1053,14 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) | |||
1047 | goto Enomem; | 1053 | goto Enomem; |
1048 | 1054 | ||
1049 | ffs->sb = sb; | 1055 | ffs->sb = sb; |
1050 | ffs->dev_name = data->dev_name; | 1056 | ffs->dev_name = kstrdup(data->dev_name, GFP_KERNEL); |
1057 | if (unlikely(!ffs->dev_name)) | ||
1058 | goto Enomem; | ||
1051 | ffs->file_perms = data->perms; | 1059 | ffs->file_perms = data->perms; |
1060 | ffs->private_data = data->private_data; | ||
1061 | |||
1062 | /* used by the caller of this function */ | ||
1063 | data->ffs_data = ffs; | ||
1052 | 1064 | ||
1053 | sb->s_fs_info = ffs; | 1065 | sb->s_fs_info = ffs; |
1054 | sb->s_blocksize = PAGE_CACHE_SIZE; | 1066 | sb->s_blocksize = PAGE_CACHE_SIZE; |
@@ -1167,20 +1179,29 @@ ffs_fs_mount(struct file_system_type *t, int flags, | |||
1167 | }, | 1179 | }, |
1168 | .root_mode = S_IFDIR | 0500, | 1180 | .root_mode = S_IFDIR | 0500, |
1169 | }; | 1181 | }; |
1182 | struct dentry *rv; | ||
1170 | int ret; | 1183 | int ret; |
1184 | void *ffs_dev; | ||
1171 | 1185 | ||
1172 | ENTER(); | 1186 | ENTER(); |
1173 | 1187 | ||
1174 | ret = functionfs_check_dev_callback(dev_name); | ||
1175 | if (unlikely(ret < 0)) | ||
1176 | return ERR_PTR(ret); | ||
1177 | |||
1178 | ret = ffs_fs_parse_opts(&data, opts); | 1188 | ret = ffs_fs_parse_opts(&data, opts); |
1179 | if (unlikely(ret < 0)) | 1189 | if (unlikely(ret < 0)) |
1180 | return ERR_PTR(ret); | 1190 | return ERR_PTR(ret); |
1181 | 1191 | ||
1192 | ffs_dev = functionfs_acquire_dev_callback(dev_name); | ||
1193 | if (IS_ERR(ffs_dev)) | ||
1194 | return ffs_dev; | ||
1195 | |||
1182 | data.dev_name = dev_name; | 1196 | data.dev_name = dev_name; |
1183 | return mount_single(t, flags, &data, ffs_sb_fill); | 1197 | data.private_data = ffs_dev; |
1198 | rv = mount_nodev(t, flags, &data, ffs_sb_fill); | ||
1199 | |||
1200 | /* data.ffs_data is set by ffs_sb_fill */ | ||
1201 | if (IS_ERR(rv)) | ||
1202 | functionfs_release_dev_callback(data.ffs_data); | ||
1203 | |||
1204 | return rv; | ||
1184 | } | 1205 | } |
1185 | 1206 | ||
1186 | static void | 1207 | static void |
@@ -1189,8 +1210,10 @@ ffs_fs_kill_sb(struct super_block *sb) | |||
1189 | ENTER(); | 1210 | ENTER(); |
1190 | 1211 | ||
1191 | kill_litter_super(sb); | 1212 | kill_litter_super(sb); |
1192 | if (sb->s_fs_info) | 1213 | if (sb->s_fs_info) { |
1214 | functionfs_release_dev_callback(sb->s_fs_info); | ||
1193 | ffs_data_put(sb->s_fs_info); | 1215 | ffs_data_put(sb->s_fs_info); |
1216 | } | ||
1194 | } | 1217 | } |
1195 | 1218 | ||
1196 | static struct file_system_type ffs_fs_type = { | 1219 | static struct file_system_type ffs_fs_type = { |
@@ -1256,6 +1279,7 @@ static void ffs_data_put(struct ffs_data *ffs) | |||
1256 | ffs_data_clear(ffs); | 1279 | ffs_data_clear(ffs); |
1257 | BUG_ON(waitqueue_active(&ffs->ev.waitq) || | 1280 | BUG_ON(waitqueue_active(&ffs->ev.waitq) || |
1258 | waitqueue_active(&ffs->ep0req_completion.wait)); | 1281 | waitqueue_active(&ffs->ep0req_completion.wait)); |
1282 | kfree(ffs->dev_name); | ||
1259 | kfree(ffs); | 1283 | kfree(ffs); |
1260 | } | 1284 | } |
1261 | } | 1285 | } |
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index a85eaf40b948..d3ace9002a6a 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c | |||
@@ -67,6 +67,15 @@ MODULE_LICENSE("GPL"); | |||
67 | #define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */ | 67 | #define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */ |
68 | #define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */ | 68 | #define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */ |
69 | 69 | ||
70 | #define GFS_MAX_DEVS 10 | ||
71 | |||
72 | struct gfs_ffs_obj { | ||
73 | const char *name; | ||
74 | bool mounted; | ||
75 | bool desc_ready; | ||
76 | struct ffs_data *ffs_data; | ||
77 | }; | ||
78 | |||
70 | static struct usb_device_descriptor gfs_dev_desc = { | 79 | static struct usb_device_descriptor gfs_dev_desc = { |
71 | .bLength = sizeof gfs_dev_desc, | 80 | .bLength = sizeof gfs_dev_desc, |
72 | .bDescriptorType = USB_DT_DEVICE, | 81 | .bDescriptorType = USB_DT_DEVICE, |
@@ -78,12 +87,17 @@ static struct usb_device_descriptor gfs_dev_desc = { | |||
78 | .idProduct = cpu_to_le16(GFS_PRODUCT_ID), | 87 | .idProduct = cpu_to_le16(GFS_PRODUCT_ID), |
79 | }; | 88 | }; |
80 | 89 | ||
90 | static char *func_names[GFS_MAX_DEVS]; | ||
91 | static unsigned int func_num; | ||
92 | |||
81 | module_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644); | 93 | module_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644); |
82 | MODULE_PARM_DESC(bDeviceClass, "USB Device class"); | 94 | MODULE_PARM_DESC(bDeviceClass, "USB Device class"); |
83 | module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644); | 95 | module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644); |
84 | MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass"); | 96 | MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass"); |
85 | module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644); | 97 | module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644); |
86 | MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol"); | 98 | MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol"); |
99 | module_param_array_named(functions, func_names, charp, &func_num, 0); | ||
100 | MODULE_PARM_DESC(functions, "USB Functions list"); | ||
87 | 101 | ||
88 | static const struct usb_descriptor_header *gfs_otg_desc[] = { | 102 | static const struct usb_descriptor_header *gfs_otg_desc[] = { |
89 | (const struct usb_descriptor_header *) | 103 | (const struct usb_descriptor_header *) |
@@ -158,13 +172,34 @@ static struct usb_composite_driver gfs_driver = { | |||
158 | .iProduct = DRIVER_DESC, | 172 | .iProduct = DRIVER_DESC, |
159 | }; | 173 | }; |
160 | 174 | ||
161 | static struct ffs_data *gfs_ffs_data; | 175 | static DEFINE_MUTEX(gfs_lock); |
162 | static unsigned long gfs_registered; | 176 | static unsigned int missing_funcs; |
177 | static bool gfs_ether_setup; | ||
178 | static bool gfs_registered; | ||
179 | static bool gfs_single_func; | ||
180 | static struct gfs_ffs_obj *ffs_tab; | ||
163 | 181 | ||
164 | static int __init gfs_init(void) | 182 | static int __init gfs_init(void) |
165 | { | 183 | { |
184 | int i; | ||
185 | |||
166 | ENTER(); | 186 | ENTER(); |
167 | 187 | ||
188 | if (!func_num) { | ||
189 | gfs_single_func = true; | ||
190 | func_num = 1; | ||
191 | } | ||
192 | |||
193 | ffs_tab = kcalloc(func_num, sizeof *ffs_tab, GFP_KERNEL); | ||
194 | if (!ffs_tab) | ||
195 | return -ENOMEM; | ||
196 | |||
197 | if (!gfs_single_func) | ||
198 | for (i = 0; i < func_num; i++) | ||
199 | ffs_tab[i].name = func_names[i]; | ||
200 | |||
201 | missing_funcs = func_num; | ||
202 | |||
168 | return functionfs_init(); | 203 | return functionfs_init(); |
169 | } | 204 | } |
170 | module_init(gfs_init); | 205 | module_init(gfs_init); |
@@ -172,63 +207,165 @@ module_init(gfs_init); | |||
172 | static void __exit gfs_exit(void) | 207 | static void __exit gfs_exit(void) |
173 | { | 208 | { |
174 | ENTER(); | 209 | ENTER(); |
210 | mutex_lock(&gfs_lock); | ||
175 | 211 | ||
176 | if (test_and_clear_bit(0, &gfs_registered)) | 212 | if (gfs_registered) |
177 | usb_composite_unregister(&gfs_driver); | 213 | usb_composite_unregister(&gfs_driver); |
214 | gfs_registered = false; | ||
178 | 215 | ||
179 | functionfs_cleanup(); | 216 | functionfs_cleanup(); |
217 | |||
218 | mutex_unlock(&gfs_lock); | ||
219 | kfree(ffs_tab); | ||
180 | } | 220 | } |
181 | module_exit(gfs_exit); | 221 | module_exit(gfs_exit); |
182 | 222 | ||
223 | static struct gfs_ffs_obj *gfs_find_dev(const char *dev_name) | ||
224 | { | ||
225 | int i; | ||
226 | |||
227 | ENTER(); | ||
228 | |||
229 | if (gfs_single_func) | ||
230 | return &ffs_tab[0]; | ||
231 | |||
232 | for (i = 0; i < func_num; i++) | ||
233 | if (strcmp(ffs_tab[i].name, dev_name) == 0) | ||
234 | return &ffs_tab[i]; | ||
235 | |||
236 | return NULL; | ||
237 | } | ||
238 | |||
183 | static int functionfs_ready_callback(struct ffs_data *ffs) | 239 | static int functionfs_ready_callback(struct ffs_data *ffs) |
184 | { | 240 | { |
241 | struct gfs_ffs_obj *ffs_obj; | ||
185 | int ret; | 242 | int ret; |
186 | 243 | ||
187 | ENTER(); | 244 | ENTER(); |
245 | mutex_lock(&gfs_lock); | ||
246 | |||
247 | ffs_obj = ffs->private_data; | ||
248 | if (!ffs_obj) { | ||
249 | ret = -EINVAL; | ||
250 | goto done; | ||
251 | } | ||
252 | |||
253 | if (WARN_ON(ffs_obj->desc_ready)) { | ||
254 | ret = -EBUSY; | ||
255 | goto done; | ||
256 | } | ||
257 | ffs_obj->desc_ready = true; | ||
258 | ffs_obj->ffs_data = ffs; | ||
188 | 259 | ||
189 | if (WARN_ON(test_and_set_bit(0, &gfs_registered))) | 260 | if (--missing_funcs) { |
190 | return -EBUSY; | 261 | ret = 0; |
262 | goto done; | ||
263 | } | ||
264 | |||
265 | if (gfs_registered) { | ||
266 | ret = -EBUSY; | ||
267 | goto done; | ||
268 | } | ||
269 | gfs_registered = true; | ||
191 | 270 | ||
192 | gfs_ffs_data = ffs; | ||
193 | ret = usb_composite_probe(&gfs_driver, gfs_bind); | 271 | ret = usb_composite_probe(&gfs_driver, gfs_bind); |
194 | if (unlikely(ret < 0)) | 272 | if (unlikely(ret < 0)) |
195 | clear_bit(0, &gfs_registered); | 273 | gfs_registered = false; |
274 | |||
275 | done: | ||
276 | mutex_unlock(&gfs_lock); | ||
196 | return ret; | 277 | return ret; |
197 | } | 278 | } |
198 | 279 | ||
199 | static void functionfs_closed_callback(struct ffs_data *ffs) | 280 | static void functionfs_closed_callback(struct ffs_data *ffs) |
200 | { | 281 | { |
282 | struct gfs_ffs_obj *ffs_obj; | ||
283 | |||
201 | ENTER(); | 284 | ENTER(); |
285 | mutex_lock(&gfs_lock); | ||
202 | 286 | ||
203 | if (test_and_clear_bit(0, &gfs_registered)) | 287 | ffs_obj = ffs->private_data; |
288 | if (!ffs_obj) | ||
289 | goto done; | ||
290 | |||
291 | ffs_obj->desc_ready = false; | ||
292 | missing_funcs++; | ||
293 | |||
294 | if (gfs_registered) | ||
204 | usb_composite_unregister(&gfs_driver); | 295 | usb_composite_unregister(&gfs_driver); |
296 | gfs_registered = false; | ||
297 | |||
298 | done: | ||
299 | mutex_unlock(&gfs_lock); | ||
205 | } | 300 | } |
206 | 301 | ||
207 | static int functionfs_check_dev_callback(const char *dev_name) | 302 | static void *functionfs_acquire_dev_callback(const char *dev_name) |
208 | { | 303 | { |
209 | return 0; | 304 | struct gfs_ffs_obj *ffs_dev; |
305 | |||
306 | ENTER(); | ||
307 | mutex_lock(&gfs_lock); | ||
308 | |||
309 | ffs_dev = gfs_find_dev(dev_name); | ||
310 | if (!ffs_dev) { | ||
311 | ffs_dev = ERR_PTR(-ENODEV); | ||
312 | goto done; | ||
313 | } | ||
314 | |||
315 | if (ffs_dev->mounted) { | ||
316 | ffs_dev = ERR_PTR(-EBUSY); | ||
317 | goto done; | ||
318 | } | ||
319 | ffs_dev->mounted = true; | ||
320 | |||
321 | done: | ||
322 | mutex_unlock(&gfs_lock); | ||
323 | return ffs_dev; | ||
210 | } | 324 | } |
211 | 325 | ||
326 | static void functionfs_release_dev_callback(struct ffs_data *ffs_data) | ||
327 | { | ||
328 | struct gfs_ffs_obj *ffs_dev; | ||
329 | |||
330 | ENTER(); | ||
331 | mutex_lock(&gfs_lock); | ||
332 | |||
333 | ffs_dev = ffs_data->private_data; | ||
334 | if (ffs_dev) | ||
335 | ffs_dev->mounted = false; | ||
336 | |||
337 | mutex_unlock(&gfs_lock); | ||
338 | } | ||
339 | |||
340 | /* | ||
341 | * It is assumed that gfs_bind is called from a context where gfs_lock is held | ||
342 | */ | ||
212 | static int gfs_bind(struct usb_composite_dev *cdev) | 343 | static int gfs_bind(struct usb_composite_dev *cdev) |
213 | { | 344 | { |
214 | int ret, i; | 345 | int ret, i; |
215 | 346 | ||
216 | ENTER(); | 347 | ENTER(); |
217 | 348 | ||
218 | if (WARN_ON(!gfs_ffs_data)) | 349 | if (missing_funcs) |
219 | return -ENODEV; | 350 | return -ENODEV; |
220 | 351 | ||
221 | ret = gether_setup(cdev->gadget, gfs_hostaddr); | 352 | ret = gether_setup(cdev->gadget, gfs_hostaddr); |
222 | if (unlikely(ret < 0)) | 353 | if (unlikely(ret < 0)) |
223 | goto error_quick; | 354 | goto error_quick; |
355 | gfs_ether_setup = true; | ||
224 | 356 | ||
225 | ret = usb_string_ids_tab(cdev, gfs_strings); | 357 | ret = usb_string_ids_tab(cdev, gfs_strings); |
226 | if (unlikely(ret < 0)) | 358 | if (unlikely(ret < 0)) |
227 | goto error; | 359 | goto error; |
228 | 360 | ||
229 | ret = functionfs_bind(gfs_ffs_data, cdev); | 361 | for (i = func_num; --i; ) { |
230 | if (unlikely(ret < 0)) | 362 | ret = functionfs_bind(ffs_tab[i].ffs_data, cdev); |
231 | goto error; | 363 | if (unlikely(ret < 0)) { |
364 | while (++i < func_num) | ||
365 | functionfs_unbind(ffs_tab[i].ffs_data); | ||
366 | goto error; | ||
367 | } | ||
368 | } | ||
232 | 369 | ||
233 | for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { | 370 | for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { |
234 | struct gfs_configuration *c = gfs_configurations + i; | 371 | struct gfs_configuration *c = gfs_configurations + i; |
@@ -246,16 +383,22 @@ static int gfs_bind(struct usb_composite_dev *cdev) | |||
246 | return 0; | 383 | return 0; |
247 | 384 | ||
248 | error_unbind: | 385 | error_unbind: |
249 | functionfs_unbind(gfs_ffs_data); | 386 | for (i = 0; i < func_num; i++) |
387 | functionfs_unbind(ffs_tab[i].ffs_data); | ||
250 | error: | 388 | error: |
251 | gether_cleanup(); | 389 | gether_cleanup(); |
252 | error_quick: | 390 | error_quick: |
253 | gfs_ffs_data = NULL; | 391 | gfs_ether_setup = false; |
254 | return ret; | 392 | return ret; |
255 | } | 393 | } |
256 | 394 | ||
395 | /* | ||
396 | * It is assumed that gfs_unbind is called from a context where gfs_lock is held | ||
397 | */ | ||
257 | static int gfs_unbind(struct usb_composite_dev *cdev) | 398 | static int gfs_unbind(struct usb_composite_dev *cdev) |
258 | { | 399 | { |
400 | int i; | ||
401 | |||
259 | ENTER(); | 402 | ENTER(); |
260 | 403 | ||
261 | /* | 404 | /* |
@@ -266,22 +409,29 @@ static int gfs_unbind(struct usb_composite_dev *cdev) | |||
266 | * from composite on orror recovery, but what you're gonna | 409 | * from composite on orror recovery, but what you're gonna |
267 | * do...? | 410 | * do...? |
268 | */ | 411 | */ |
269 | if (gfs_ffs_data) { | 412 | if (gfs_ether_setup) |
270 | gether_cleanup(); | 413 | gether_cleanup(); |
271 | functionfs_unbind(gfs_ffs_data); | 414 | gfs_ether_setup = false; |
272 | gfs_ffs_data = NULL; | 415 | |
273 | } | 416 | for (i = func_num; --i; ) |
417 | if (ffs_tab[i].ffs_data) | ||
418 | functionfs_unbind(ffs_tab[i].ffs_data); | ||
274 | 419 | ||
275 | return 0; | 420 | return 0; |
276 | } | 421 | } |
277 | 422 | ||
423 | /* | ||
424 | * It is assumed that gfs_do_config is called from a context where | ||
425 | * gfs_lock is held | ||
426 | */ | ||
278 | static int gfs_do_config(struct usb_configuration *c) | 427 | static int gfs_do_config(struct usb_configuration *c) |
279 | { | 428 | { |
280 | struct gfs_configuration *gc = | 429 | struct gfs_configuration *gc = |
281 | container_of(c, struct gfs_configuration, c); | 430 | container_of(c, struct gfs_configuration, c); |
431 | int i; | ||
282 | int ret; | 432 | int ret; |
283 | 433 | ||
284 | if (WARN_ON(!gfs_ffs_data)) | 434 | if (missing_funcs) |
285 | return -ENODEV; | 435 | return -ENODEV; |
286 | 436 | ||
287 | if (gadget_is_otg(c->cdev->gadget)) { | 437 | if (gadget_is_otg(c->cdev->gadget)) { |
@@ -295,9 +445,11 @@ static int gfs_do_config(struct usb_configuration *c) | |||
295 | return ret; | 445 | return ret; |
296 | } | 446 | } |
297 | 447 | ||
298 | ret = functionfs_bind_config(c->cdev, c, gfs_ffs_data); | 448 | for (i = 0; i < func_num; i++) { |
299 | if (unlikely(ret < 0)) | 449 | ret = functionfs_bind_config(c->cdev, c, ffs_tab[i].ffs_data); |
300 | return ret; | 450 | if (unlikely(ret < 0)) |
451 | return ret; | ||
452 | } | ||
301 | 453 | ||
302 | /* | 454 | /* |
303 | * After previous do_configs there may be some invalid | 455 | * After previous do_configs there may be some invalid |
diff --git a/include/linux/usb/functionfs.h b/include/linux/usb/functionfs.h index 7587ef934ba8..a843d0851364 100644 --- a/include/linux/usb/functionfs.h +++ b/include/linux/usb/functionfs.h | |||
@@ -190,8 +190,10 @@ static int functionfs_ready_callback(struct ffs_data *ffs) | |||
190 | __attribute__((warn_unused_result, nonnull)); | 190 | __attribute__((warn_unused_result, nonnull)); |
191 | static void functionfs_closed_callback(struct ffs_data *ffs) | 191 | static void functionfs_closed_callback(struct ffs_data *ffs) |
192 | __attribute__((nonnull)); | 192 | __attribute__((nonnull)); |
193 | static int functionfs_check_dev_callback(const char *dev_name) | 193 | static void *functionfs_acquire_dev_callback(const char *dev_name) |
194 | __attribute__((warn_unused_result, nonnull)); | 194 | __attribute__((warn_unused_result, nonnull)); |
195 | static void functionfs_release_dev_callback(struct ffs_data *ffs_data) | ||
196 | __attribute__((nonnull)); | ||
195 | 197 | ||
196 | 198 | ||
197 | #endif | 199 | #endif |