diff options
| author | Andrzej Pietrasiewicz <andrzej.p@samsung.com> | 2014-07-22 13:58:35 -0400 |
|---|---|---|
| committer | Felipe Balbi <balbi@ti.com> | 2014-08-20 15:04:42 -0400 |
| commit | 3aeea3c53e73b972ff07a1d03d6cc07f97de4f2f (patch) | |
| tree | e05e46602c596d7b717f5db3bfb93894f782baef /drivers/usb/gadget/function | |
| parent | 065a107cdd70f0621011424009b3ecd4e42481b1 (diff) | |
usb: gadget: f_uac2: add configfs support
Add support for using f_uac2 function as a component of a gadget
composed with configfs.
Tested-by: Sebastian Reimers <sebastian.reimers@googlemail.com>
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/gadget/function')
| -rw-r--r-- | drivers/usb/gadget/function/f_uac2.c | 105 | ||||
| -rw-r--r-- | drivers/usb/gadget/function/u_uac2.h | 3 |
2 files changed, 108 insertions, 0 deletions
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 1b9671effa12..0d65e7c08a93 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c | |||
| @@ -1330,6 +1330,93 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr) | |||
| 1330 | return value; | 1330 | return value; |
| 1331 | } | 1331 | } |
| 1332 | 1332 | ||
| 1333 | static inline struct f_uac2_opts *to_f_uac2_opts(struct config_item *item) | ||
| 1334 | { | ||
| 1335 | return container_of(to_config_group(item), struct f_uac2_opts, | ||
| 1336 | func_inst.group); | ||
| 1337 | } | ||
| 1338 | |||
| 1339 | CONFIGFS_ATTR_STRUCT(f_uac2_opts); | ||
| 1340 | CONFIGFS_ATTR_OPS(f_uac2_opts); | ||
| 1341 | |||
| 1342 | static void f_uac2_attr_release(struct config_item *item) | ||
| 1343 | { | ||
| 1344 | struct f_uac2_opts *opts = to_f_uac2_opts(item); | ||
| 1345 | |||
| 1346 | usb_put_function_instance(&opts->func_inst); | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | static struct configfs_item_operations f_uac2_item_ops = { | ||
| 1350 | .release = f_uac2_attr_release, | ||
| 1351 | .show_attribute = f_uac2_opts_attr_show, | ||
| 1352 | .store_attribute = f_uac2_opts_attr_store, | ||
| 1353 | }; | ||
| 1354 | |||
| 1355 | #define UAC2_ATTRIBUTE(name) \ | ||
| 1356 | static ssize_t f_uac2_opts_##name##_show(struct f_uac2_opts *opts, \ | ||
| 1357 | char *page) \ | ||
| 1358 | { \ | ||
| 1359 | int result; \ | ||
| 1360 | \ | ||
| 1361 | mutex_lock(&opts->lock); \ | ||
| 1362 | result = sprintf(page, "%u\n", opts->name); \ | ||
| 1363 | mutex_unlock(&opts->lock); \ | ||
| 1364 | \ | ||
| 1365 | return result; \ | ||
| 1366 | } \ | ||
| 1367 | \ | ||
| 1368 | static ssize_t f_uac2_opts_##name##_store(struct f_uac2_opts *opts, \ | ||
| 1369 | const char *page, size_t len) \ | ||
| 1370 | { \ | ||
| 1371 | int ret; \ | ||
| 1372 | u32 num; \ | ||
| 1373 | \ | ||
| 1374 | mutex_lock(&opts->lock); \ | ||
| 1375 | if (opts->refcnt) { \ | ||
| 1376 | ret = -EBUSY; \ | ||
| 1377 | goto end; \ | ||
| 1378 | } \ | ||
| 1379 | \ | ||
| 1380 | ret = kstrtou32(page, 0, &num); \ | ||
| 1381 | if (ret) \ | ||
| 1382 | goto end; \ | ||
| 1383 | \ | ||
| 1384 | opts->name = num; \ | ||
| 1385 | ret = len; \ | ||
| 1386 | \ | ||
| 1387 | end: \ | ||
| 1388 | mutex_unlock(&opts->lock); \ | ||
| 1389 | return ret; \ | ||
| 1390 | } \ | ||
| 1391 | \ | ||
| 1392 | static struct f_uac2_opts_attribute f_uac2_opts_##name = \ | ||
| 1393 | __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ | ||
| 1394 | f_uac2_opts_##name##_show, \ | ||
| 1395 | f_uac2_opts_##name##_store) | ||
| 1396 | |||
| 1397 | UAC2_ATTRIBUTE(p_chmask); | ||
| 1398 | UAC2_ATTRIBUTE(p_srate); | ||
| 1399 | UAC2_ATTRIBUTE(p_ssize); | ||
| 1400 | UAC2_ATTRIBUTE(c_chmask); | ||
| 1401 | UAC2_ATTRIBUTE(c_srate); | ||
| 1402 | UAC2_ATTRIBUTE(c_ssize); | ||
| 1403 | |||
| 1404 | static struct configfs_attribute *f_uac2_attrs[] = { | ||
| 1405 | &f_uac2_opts_p_chmask.attr, | ||
| 1406 | &f_uac2_opts_p_srate.attr, | ||
| 1407 | &f_uac2_opts_p_ssize.attr, | ||
| 1408 | &f_uac2_opts_c_chmask.attr, | ||
| 1409 | &f_uac2_opts_c_srate.attr, | ||
| 1410 | &f_uac2_opts_c_ssize.attr, | ||
| 1411 | NULL, | ||
| 1412 | }; | ||
| 1413 | |||
| 1414 | static struct config_item_type f_uac2_func_type = { | ||
| 1415 | .ct_item_ops = &f_uac2_item_ops, | ||
| 1416 | .ct_attrs = f_uac2_attrs, | ||
| 1417 | .ct_owner = THIS_MODULE, | ||
| 1418 | }; | ||
| 1419 | |||
| 1333 | static void afunc_free_inst(struct usb_function_instance *f) | 1420 | static void afunc_free_inst(struct usb_function_instance *f) |
| 1334 | { | 1421 | { |
| 1335 | struct f_uac2_opts *opts; | 1422 | struct f_uac2_opts *opts; |
| @@ -1346,17 +1433,32 @@ static struct usb_function_instance *afunc_alloc_inst(void) | |||
| 1346 | if (!opts) | 1433 | if (!opts) |
| 1347 | return ERR_PTR(-ENOMEM); | 1434 | return ERR_PTR(-ENOMEM); |
| 1348 | 1435 | ||
| 1436 | mutex_init(&opts->lock); | ||
| 1349 | opts->func_inst.free_func_inst = afunc_free_inst; | 1437 | opts->func_inst.free_func_inst = afunc_free_inst; |
| 1350 | 1438 | ||
| 1439 | config_group_init_type_name(&opts->func_inst.group, "", | ||
| 1440 | &f_uac2_func_type); | ||
| 1441 | |||
| 1442 | opts->p_chmask = UAC2_DEF_PCHMASK; | ||
| 1443 | opts->p_srate = UAC2_DEF_PSRATE; | ||
| 1444 | opts->p_ssize = UAC2_DEF_PSSIZE; | ||
| 1445 | opts->c_chmask = UAC2_DEF_CCHMASK; | ||
| 1446 | opts->c_srate = UAC2_DEF_CSRATE; | ||
| 1447 | opts->c_ssize = UAC2_DEF_CSSIZE; | ||
| 1351 | return &opts->func_inst; | 1448 | return &opts->func_inst; |
| 1352 | } | 1449 | } |
| 1353 | 1450 | ||
| 1354 | static void afunc_free(struct usb_function *f) | 1451 | static void afunc_free(struct usb_function *f) |
| 1355 | { | 1452 | { |
| 1356 | struct audio_dev *agdev; | 1453 | struct audio_dev *agdev; |
| 1454 | struct f_uac2_opts *opts; | ||
| 1357 | 1455 | ||
| 1358 | agdev = func_to_agdev(f); | 1456 | agdev = func_to_agdev(f); |
| 1457 | opts = container_of(f->fi, struct f_uac2_opts, func_inst); | ||
| 1359 | kfree(agdev); | 1458 | kfree(agdev); |
| 1459 | mutex_lock(&opts->lock); | ||
| 1460 | --opts->refcnt; | ||
| 1461 | mutex_unlock(&opts->lock); | ||
| 1360 | } | 1462 | } |
| 1361 | 1463 | ||
| 1362 | static void afunc_unbind(struct usb_configuration *c, struct usb_function *f) | 1464 | static void afunc_unbind(struct usb_configuration *c, struct usb_function *f) |
| @@ -1389,6 +1491,9 @@ struct usb_function *afunc_alloc(struct usb_function_instance *fi) | |||
| 1389 | return ERR_PTR(-ENOMEM); | 1491 | return ERR_PTR(-ENOMEM); |
| 1390 | 1492 | ||
| 1391 | opts = container_of(fi, struct f_uac2_opts, func_inst); | 1493 | opts = container_of(fi, struct f_uac2_opts, func_inst); |
| 1494 | mutex_lock(&opts->lock); | ||
| 1495 | ++opts->refcnt; | ||
| 1496 | mutex_unlock(&opts->lock); | ||
| 1392 | 1497 | ||
| 1393 | agdev->func.name = "uac2_func"; | 1498 | agdev->func.name = "uac2_func"; |
| 1394 | agdev->func.bind = afunc_bind; | 1499 | agdev->func.bind = afunc_bind; |
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h index ed7f7361a53e..78dd37279bd4 100644 --- a/drivers/usb/gadget/function/u_uac2.h +++ b/drivers/usb/gadget/function/u_uac2.h | |||
| @@ -34,6 +34,9 @@ struct f_uac2_opts { | |||
| 34 | int c_srate; | 34 | int c_srate; |
| 35 | int c_ssize; | 35 | int c_ssize; |
| 36 | bool bound; | 36 | bool bound; |
| 37 | |||
| 38 | struct mutex lock; | ||
| 39 | int refcnt; | ||
| 37 | }; | 40 | }; |
| 38 | 41 | ||
| 39 | #endif | 42 | #endif |
