18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * g_ffs.c -- user mode file system API for USB composite function controllers
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Samsung Electronics
68c2ecf20Sopenharmony_ci * Author: Michal Nazarewicz <mina86@mina86.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "g_ffs: " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
148c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#  if defined USB_ETH_RNDIS
178c2ecf20Sopenharmony_ci#    undef USB_ETH_RNDIS
188c2ecf20Sopenharmony_ci#  endif
198c2ecf20Sopenharmony_ci#  ifdef CONFIG_USB_FUNCTIONFS_RNDIS
208c2ecf20Sopenharmony_ci#    define USB_ETH_RNDIS y
218c2ecf20Sopenharmony_ci#  endif
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#  include "u_ecm.h"
248c2ecf20Sopenharmony_ci#  include "u_gether.h"
258c2ecf20Sopenharmony_ci#  ifdef USB_ETH_RNDIS
268c2ecf20Sopenharmony_ci#    include "u_rndis.h"
278c2ecf20Sopenharmony_ci#    include "rndis.h"
288c2ecf20Sopenharmony_ci#  endif
298c2ecf20Sopenharmony_ci#  include "u_ether.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ciUSB_ETHERNET_MODULE_PARAMETERS();
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#  ifdef CONFIG_USB_FUNCTIONFS_ETH
348c2ecf20Sopenharmony_cistatic int eth_bind_config(struct usb_configuration *c);
358c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_ecm;
368c2ecf20Sopenharmony_cistatic struct usb_function *f_ecm;
378c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_geth;
388c2ecf20Sopenharmony_cistatic struct usb_function *f_geth;
398c2ecf20Sopenharmony_ci#  endif
408c2ecf20Sopenharmony_ci#  ifdef CONFIG_USB_FUNCTIONFS_RNDIS
418c2ecf20Sopenharmony_cistatic int bind_rndis_config(struct usb_configuration *c);
428c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_rndis;
438c2ecf20Sopenharmony_cistatic struct usb_function *f_rndis;
448c2ecf20Sopenharmony_ci#  endif
458c2ecf20Sopenharmony_ci#endif
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#include "u_fs.h"
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define DRIVER_NAME	"g_ffs"
508c2ecf20Sopenharmony_ci#define DRIVER_DESC	"USB Function Filesystem"
518c2ecf20Sopenharmony_ci#define DRIVER_VERSION	"24 Aug 2004"
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michal Nazarewicz");
558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define GFS_VENDOR_ID	0x1d6b	/* Linux Foundation */
588c2ecf20Sopenharmony_ci#define GFS_PRODUCT_ID	0x0105	/* FunctionFS Gadget */
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define GFS_MAX_DEVS	10
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciUSB_GADGET_COMPOSITE_OPTIONS();
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic struct usb_device_descriptor gfs_dev_desc = {
658c2ecf20Sopenharmony_ci	.bLength		= sizeof gfs_dev_desc,
668c2ecf20Sopenharmony_ci	.bDescriptorType	= USB_DT_DEVICE,
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* .bcdUSB = DYNAMIC */
698c2ecf20Sopenharmony_ci	.bDeviceClass		= USB_CLASS_PER_INTERFACE,
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	.idVendor		= cpu_to_le16(GFS_VENDOR_ID),
728c2ecf20Sopenharmony_ci	.idProduct		= cpu_to_le16(GFS_PRODUCT_ID),
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic char *func_names[GFS_MAX_DEVS];
768c2ecf20Sopenharmony_cistatic unsigned int func_num;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cimodule_param_named(bDeviceClass,    gfs_dev_desc.bDeviceClass,    byte,   0644);
798c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bDeviceClass, "USB Device class");
808c2ecf20Sopenharmony_cimodule_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte,   0644);
818c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass");
828c2ecf20Sopenharmony_cimodule_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte,   0644);
838c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol");
848c2ecf20Sopenharmony_cimodule_param_array_named(functions, func_names, charp, &func_num, 0);
858c2ecf20Sopenharmony_ciMODULE_PARM_DESC(functions, "USB Functions list");
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic const struct usb_descriptor_header *gfs_otg_desc[2];
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* String IDs are assigned dynamically */
908c2ecf20Sopenharmony_cistatic struct usb_string gfs_strings[] = {
918c2ecf20Sopenharmony_ci	[USB_GADGET_MANUFACTURER_IDX].s = "",
928c2ecf20Sopenharmony_ci	[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
938c2ecf20Sopenharmony_ci	[USB_GADGET_SERIAL_IDX].s = "",
948c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
958c2ecf20Sopenharmony_ci	{ .s = "FunctionFS + RNDIS" },
968c2ecf20Sopenharmony_ci#endif
978c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_ETH
988c2ecf20Sopenharmony_ci	{ .s = "FunctionFS + ECM" },
998c2ecf20Sopenharmony_ci#endif
1008c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
1018c2ecf20Sopenharmony_ci	{ .s = "FunctionFS" },
1028c2ecf20Sopenharmony_ci#endif
1038c2ecf20Sopenharmony_ci	{  } /* end of list */
1048c2ecf20Sopenharmony_ci};
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic struct usb_gadget_strings *gfs_dev_strings[] = {
1078c2ecf20Sopenharmony_ci	&(struct usb_gadget_strings) {
1088c2ecf20Sopenharmony_ci		.language	= 0x0409,	/* en-us */
1098c2ecf20Sopenharmony_ci		.strings	= gfs_strings,
1108c2ecf20Sopenharmony_ci	},
1118c2ecf20Sopenharmony_ci	NULL,
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistruct gfs_configuration {
1158c2ecf20Sopenharmony_ci	struct usb_configuration c;
1168c2ecf20Sopenharmony_ci	int (*eth)(struct usb_configuration *c);
1178c2ecf20Sopenharmony_ci	int num;
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic struct gfs_configuration gfs_configurations[] = {
1218c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
1228c2ecf20Sopenharmony_ci	{
1238c2ecf20Sopenharmony_ci		.eth		= bind_rndis_config,
1248c2ecf20Sopenharmony_ci	},
1258c2ecf20Sopenharmony_ci#endif
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_ETH
1288c2ecf20Sopenharmony_ci	{
1298c2ecf20Sopenharmony_ci		.eth		= eth_bind_config,
1308c2ecf20Sopenharmony_ci	},
1318c2ecf20Sopenharmony_ci#endif
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
1348c2ecf20Sopenharmony_ci	{
1358c2ecf20Sopenharmony_ci	},
1368c2ecf20Sopenharmony_ci#endif
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic void *functionfs_acquire_dev(struct ffs_dev *dev);
1408c2ecf20Sopenharmony_cistatic void functionfs_release_dev(struct ffs_dev *dev);
1418c2ecf20Sopenharmony_cistatic int functionfs_ready_callback(struct ffs_data *ffs);
1428c2ecf20Sopenharmony_cistatic void functionfs_closed_callback(struct ffs_data *ffs);
1438c2ecf20Sopenharmony_cistatic int gfs_bind(struct usb_composite_dev *cdev);
1448c2ecf20Sopenharmony_cistatic int gfs_unbind(struct usb_composite_dev *cdev);
1458c2ecf20Sopenharmony_cistatic int gfs_do_config(struct usb_configuration *c);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic struct usb_composite_driver gfs_driver = {
1498c2ecf20Sopenharmony_ci	.name		= DRIVER_NAME,
1508c2ecf20Sopenharmony_ci	.dev		= &gfs_dev_desc,
1518c2ecf20Sopenharmony_ci	.strings	= gfs_dev_strings,
1528c2ecf20Sopenharmony_ci	.max_speed	= USB_SPEED_SUPER,
1538c2ecf20Sopenharmony_ci	.bind		= gfs_bind,
1548c2ecf20Sopenharmony_ci	.unbind		= gfs_unbind,
1558c2ecf20Sopenharmony_ci};
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic unsigned int missing_funcs;
1588c2ecf20Sopenharmony_cistatic bool gfs_registered;
1598c2ecf20Sopenharmony_cistatic bool gfs_single_func;
1608c2ecf20Sopenharmony_cistatic struct usb_function_instance **fi_ffs;
1618c2ecf20Sopenharmony_cistatic struct usb_function **f_ffs[] = {
1628c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
1638c2ecf20Sopenharmony_ci	NULL,
1648c2ecf20Sopenharmony_ci#endif
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_ETH
1678c2ecf20Sopenharmony_ci	NULL,
1688c2ecf20Sopenharmony_ci#endif
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
1718c2ecf20Sopenharmony_ci	NULL,
1728c2ecf20Sopenharmony_ci#endif
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci#define N_CONF ARRAY_SIZE(f_ffs)
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int __init gfs_init(void)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct f_fs_opts *opts;
1808c2ecf20Sopenharmony_ci	int i;
1818c2ecf20Sopenharmony_ci	int ret = 0;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	ENTER();
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (func_num < 2) {
1868c2ecf20Sopenharmony_ci		gfs_single_func = true;
1878c2ecf20Sopenharmony_ci		func_num = 1;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/*
1918c2ecf20Sopenharmony_ci	 * Allocate in one chunk for easier maintenance
1928c2ecf20Sopenharmony_ci	 */
1938c2ecf20Sopenharmony_ci	f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs), GFP_KERNEL);
1948c2ecf20Sopenharmony_ci	if (!f_ffs[0]) {
1958c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1968c2ecf20Sopenharmony_ci		goto no_func;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci	for (i = 1; i < N_CONF; ++i)
1998c2ecf20Sopenharmony_ci		f_ffs[i] = f_ffs[0] + i * func_num;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	fi_ffs = kcalloc(func_num, sizeof(*fi_ffs), GFP_KERNEL);
2028c2ecf20Sopenharmony_ci	if (!fi_ffs) {
2038c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2048c2ecf20Sopenharmony_ci		goto no_func;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	for (i = 0; i < func_num; i++) {
2088c2ecf20Sopenharmony_ci		fi_ffs[i] = usb_get_function_instance("ffs");
2098c2ecf20Sopenharmony_ci		if (IS_ERR(fi_ffs[i])) {
2108c2ecf20Sopenharmony_ci			ret = PTR_ERR(fi_ffs[i]);
2118c2ecf20Sopenharmony_ci			--i;
2128c2ecf20Sopenharmony_ci			goto no_dev;
2138c2ecf20Sopenharmony_ci		}
2148c2ecf20Sopenharmony_ci		opts = to_f_fs_opts(fi_ffs[i]);
2158c2ecf20Sopenharmony_ci		if (gfs_single_func)
2168c2ecf20Sopenharmony_ci			ret = ffs_single_dev(opts->dev);
2178c2ecf20Sopenharmony_ci		else
2188c2ecf20Sopenharmony_ci			ret = ffs_name_dev(opts->dev, func_names[i]);
2198c2ecf20Sopenharmony_ci		if (ret)
2208c2ecf20Sopenharmony_ci			goto no_dev;
2218c2ecf20Sopenharmony_ci		opts->dev->ffs_ready_callback = functionfs_ready_callback;
2228c2ecf20Sopenharmony_ci		opts->dev->ffs_closed_callback = functionfs_closed_callback;
2238c2ecf20Sopenharmony_ci		opts->dev->ffs_acquire_dev_callback = functionfs_acquire_dev;
2248c2ecf20Sopenharmony_ci		opts->dev->ffs_release_dev_callback = functionfs_release_dev;
2258c2ecf20Sopenharmony_ci		opts->no_configfs = true;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	missing_funcs = func_num;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	return 0;
2318c2ecf20Sopenharmony_cino_dev:
2328c2ecf20Sopenharmony_ci	while (i >= 0)
2338c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_ffs[i--]);
2348c2ecf20Sopenharmony_ci	kfree(fi_ffs);
2358c2ecf20Sopenharmony_cino_func:
2368c2ecf20Sopenharmony_ci	kfree(f_ffs[0]);
2378c2ecf20Sopenharmony_ci	return ret;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_cimodule_init(gfs_init);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic void __exit gfs_exit(void)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	int i;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	ENTER();
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (gfs_registered)
2488c2ecf20Sopenharmony_ci		usb_composite_unregister(&gfs_driver);
2498c2ecf20Sopenharmony_ci	gfs_registered = false;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	kfree(f_ffs[0]);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	for (i = 0; i < func_num; i++)
2548c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_ffs[i]);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	kfree(fi_ffs);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_cimodule_exit(gfs_exit);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic void *functionfs_acquire_dev(struct ffs_dev *dev)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	if (!try_module_get(THIS_MODULE))
2638c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOENT);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return NULL;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic void functionfs_release_dev(struct ffs_dev *dev)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci/*
2748c2ecf20Sopenharmony_ci * The caller of this function takes ffs_lock
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_cistatic int functionfs_ready_callback(struct ffs_data *ffs)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	int ret = 0;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (--missing_funcs)
2818c2ecf20Sopenharmony_ci		return 0;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (gfs_registered)
2848c2ecf20Sopenharmony_ci		return -EBUSY;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	gfs_registered = true;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	ret = usb_composite_probe(&gfs_driver);
2898c2ecf20Sopenharmony_ci	if (unlikely(ret < 0)) {
2908c2ecf20Sopenharmony_ci		++missing_funcs;
2918c2ecf20Sopenharmony_ci		gfs_registered = false;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return ret;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci/*
2988c2ecf20Sopenharmony_ci * The caller of this function takes ffs_lock
2998c2ecf20Sopenharmony_ci */
3008c2ecf20Sopenharmony_cistatic void functionfs_closed_callback(struct ffs_data *ffs)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	missing_funcs++;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (gfs_registered)
3058c2ecf20Sopenharmony_ci		usb_composite_unregister(&gfs_driver);
3068c2ecf20Sopenharmony_ci	gfs_registered = false;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci/*
3108c2ecf20Sopenharmony_ci * It is assumed that gfs_bind is called from a context where ffs_lock is held
3118c2ecf20Sopenharmony_ci */
3128c2ecf20Sopenharmony_cistatic int gfs_bind(struct usb_composite_dev *cdev)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
3158c2ecf20Sopenharmony_ci	struct net_device *net;
3168c2ecf20Sopenharmony_ci#endif
3178c2ecf20Sopenharmony_ci	int ret, i;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	ENTER();
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (missing_funcs)
3228c2ecf20Sopenharmony_ci		return -ENODEV;
3238c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH
3248c2ecf20Sopenharmony_ci	if (can_support_ecm(cdev->gadget)) {
3258c2ecf20Sopenharmony_ci		struct f_ecm_opts *ecm_opts;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		fi_ecm = usb_get_function_instance("ecm");
3288c2ecf20Sopenharmony_ci		if (IS_ERR(fi_ecm))
3298c2ecf20Sopenharmony_ci			return PTR_ERR(fi_ecm);
3308c2ecf20Sopenharmony_ci		ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
3318c2ecf20Sopenharmony_ci		net = ecm_opts->net;
3328c2ecf20Sopenharmony_ci	} else {
3338c2ecf20Sopenharmony_ci		struct f_gether_opts *geth_opts;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci		fi_geth = usb_get_function_instance("geth");
3368c2ecf20Sopenharmony_ci		if (IS_ERR(fi_geth))
3378c2ecf20Sopenharmony_ci			return PTR_ERR(fi_geth);
3388c2ecf20Sopenharmony_ci		geth_opts = container_of(fi_geth, struct f_gether_opts,
3398c2ecf20Sopenharmony_ci					 func_inst);
3408c2ecf20Sopenharmony_ci		net = geth_opts->net;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci#endif
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
3458c2ecf20Sopenharmony_ci	{
3468c2ecf20Sopenharmony_ci		fi_rndis = usb_get_function_instance("rndis");
3478c2ecf20Sopenharmony_ci		if (IS_ERR(fi_rndis)) {
3488c2ecf20Sopenharmony_ci			ret = PTR_ERR(fi_rndis);
3498c2ecf20Sopenharmony_ci			goto error;
3508c2ecf20Sopenharmony_ci		}
3518c2ecf20Sopenharmony_ci#ifndef CONFIG_USB_FUNCTIONFS_ETH
3528c2ecf20Sopenharmony_ci		net = container_of(fi_rndis, struct f_rndis_opts,
3538c2ecf20Sopenharmony_ci				   func_inst)->net;
3548c2ecf20Sopenharmony_ci#endif
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci#endif
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
3598c2ecf20Sopenharmony_ci	gether_set_qmult(net, qmult);
3608c2ecf20Sopenharmony_ci	if (!gether_set_host_addr(net, host_addr))
3618c2ecf20Sopenharmony_ci		pr_info("using host ethernet address: %s", host_addr);
3628c2ecf20Sopenharmony_ci	if (!gether_set_dev_addr(net, dev_addr))
3638c2ecf20Sopenharmony_ci		pr_info("using self ethernet address: %s", dev_addr);
3648c2ecf20Sopenharmony_ci#endif
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_RNDIS && defined CONFIG_USB_FUNCTIONFS_ETH
3678c2ecf20Sopenharmony_ci	gether_set_gadget(net, cdev->gadget);
3688c2ecf20Sopenharmony_ci	ret = gether_register_netdev(net);
3698c2ecf20Sopenharmony_ci	if (ret)
3708c2ecf20Sopenharmony_ci		goto error_rndis;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (can_support_ecm(cdev->gadget)) {
3738c2ecf20Sopenharmony_ci		struct f_ecm_opts *ecm_opts;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
3768c2ecf20Sopenharmony_ci		ecm_opts->bound = true;
3778c2ecf20Sopenharmony_ci	} else {
3788c2ecf20Sopenharmony_ci		struct f_gether_opts *geth_opts;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		geth_opts = container_of(fi_geth, struct f_gether_opts,
3818c2ecf20Sopenharmony_ci					 func_inst);
3828c2ecf20Sopenharmony_ci		geth_opts->bound = true;
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	rndis_borrow_net(fi_rndis, net);
3868c2ecf20Sopenharmony_ci#endif
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* TODO: gstrings_attach? */
3898c2ecf20Sopenharmony_ci	ret = usb_string_ids_tab(cdev, gfs_strings);
3908c2ecf20Sopenharmony_ci	if (unlikely(ret < 0))
3918c2ecf20Sopenharmony_ci		goto error_rndis;
3928c2ecf20Sopenharmony_ci	gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (gadget_is_otg(cdev->gadget) && !gfs_otg_desc[0]) {
3958c2ecf20Sopenharmony_ci		struct usb_descriptor_header *usb_desc;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
3988c2ecf20Sopenharmony_ci		if (!usb_desc)
3998c2ecf20Sopenharmony_ci			goto error_rndis;
4008c2ecf20Sopenharmony_ci		usb_otg_descriptor_init(cdev->gadget, usb_desc);
4018c2ecf20Sopenharmony_ci		gfs_otg_desc[0] = usb_desc;
4028c2ecf20Sopenharmony_ci		gfs_otg_desc[1] = NULL;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
4068c2ecf20Sopenharmony_ci		struct gfs_configuration *c = gfs_configurations + i;
4078c2ecf20Sopenharmony_ci		int sid = USB_GADGET_FIRST_AVAIL_IDX + i;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci		c->c.label			= gfs_strings[sid].s;
4108c2ecf20Sopenharmony_ci		c->c.iConfiguration		= gfs_strings[sid].id;
4118c2ecf20Sopenharmony_ci		c->c.bConfigurationValue	= 1 + i;
4128c2ecf20Sopenharmony_ci		c->c.bmAttributes		= USB_CONFIG_ATT_SELFPOWER;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci		c->num = i;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		ret = usb_add_config(cdev, &c->c, gfs_do_config);
4178c2ecf20Sopenharmony_ci		if (unlikely(ret < 0))
4188c2ecf20Sopenharmony_ci			goto error_unbind;
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci	usb_composite_overwrite_options(cdev, &coverwrite);
4218c2ecf20Sopenharmony_ci	return 0;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci/* TODO */
4248c2ecf20Sopenharmony_cierror_unbind:
4258c2ecf20Sopenharmony_ci	kfree(gfs_otg_desc[0]);
4268c2ecf20Sopenharmony_ci	gfs_otg_desc[0] = NULL;
4278c2ecf20Sopenharmony_cierror_rndis:
4288c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
4298c2ecf20Sopenharmony_ci	usb_put_function_instance(fi_rndis);
4308c2ecf20Sopenharmony_cierror:
4318c2ecf20Sopenharmony_ci#endif
4328c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH
4338c2ecf20Sopenharmony_ci	if (can_support_ecm(cdev->gadget))
4348c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_ecm);
4358c2ecf20Sopenharmony_ci	else
4368c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_geth);
4378c2ecf20Sopenharmony_ci#endif
4388c2ecf20Sopenharmony_ci	return ret;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci/*
4428c2ecf20Sopenharmony_ci * It is assumed that gfs_unbind is called from a context where ffs_lock is held
4438c2ecf20Sopenharmony_ci */
4448c2ecf20Sopenharmony_cistatic int gfs_unbind(struct usb_composite_dev *cdev)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	int i;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	ENTER();
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
4528c2ecf20Sopenharmony_ci	usb_put_function(f_rndis);
4538c2ecf20Sopenharmony_ci	usb_put_function_instance(fi_rndis);
4548c2ecf20Sopenharmony_ci#endif
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci#if defined CONFIG_USB_FUNCTIONFS_ETH
4578c2ecf20Sopenharmony_ci	if (can_support_ecm(cdev->gadget)) {
4588c2ecf20Sopenharmony_ci		usb_put_function(f_ecm);
4598c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_ecm);
4608c2ecf20Sopenharmony_ci	} else {
4618c2ecf20Sopenharmony_ci		usb_put_function(f_geth);
4628c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_geth);
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci#endif
4658c2ecf20Sopenharmony_ci	for (i = 0; i < N_CONF * func_num; ++i)
4668c2ecf20Sopenharmony_ci		usb_put_function(*(f_ffs[0] + i));
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	kfree(gfs_otg_desc[0]);
4698c2ecf20Sopenharmony_ci	gfs_otg_desc[0] = NULL;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	return 0;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/*
4758c2ecf20Sopenharmony_ci * It is assumed that gfs_do_config is called from a context where
4768c2ecf20Sopenharmony_ci * ffs_lock is held
4778c2ecf20Sopenharmony_ci */
4788c2ecf20Sopenharmony_cistatic int gfs_do_config(struct usb_configuration *c)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct gfs_configuration *gc =
4818c2ecf20Sopenharmony_ci		container_of(c, struct gfs_configuration, c);
4828c2ecf20Sopenharmony_ci	int i;
4838c2ecf20Sopenharmony_ci	int ret;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (missing_funcs)
4868c2ecf20Sopenharmony_ci		return -ENODEV;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (gadget_is_otg(c->cdev->gadget)) {
4898c2ecf20Sopenharmony_ci		c->descriptors = gfs_otg_desc;
4908c2ecf20Sopenharmony_ci		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if (gc->eth) {
4948c2ecf20Sopenharmony_ci		ret = gc->eth(c);
4958c2ecf20Sopenharmony_ci		if (unlikely(ret < 0))
4968c2ecf20Sopenharmony_ci			return ret;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	for (i = 0; i < func_num; i++) {
5008c2ecf20Sopenharmony_ci		f_ffs[gc->num][i] = usb_get_function(fi_ffs[i]);
5018c2ecf20Sopenharmony_ci		if (IS_ERR(f_ffs[gc->num][i])) {
5028c2ecf20Sopenharmony_ci			ret = PTR_ERR(f_ffs[gc->num][i]);
5038c2ecf20Sopenharmony_ci			goto error;
5048c2ecf20Sopenharmony_ci		}
5058c2ecf20Sopenharmony_ci		ret = usb_add_function(c, f_ffs[gc->num][i]);
5068c2ecf20Sopenharmony_ci		if (ret < 0) {
5078c2ecf20Sopenharmony_ci			usb_put_function(f_ffs[gc->num][i]);
5088c2ecf20Sopenharmony_ci			goto error;
5098c2ecf20Sopenharmony_ci		}
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	/*
5138c2ecf20Sopenharmony_ci	 * After previous do_configs there may be some invalid
5148c2ecf20Sopenharmony_ci	 * pointers in c->interface array.  This happens every time
5158c2ecf20Sopenharmony_ci	 * a user space function with fewer interfaces than a user
5168c2ecf20Sopenharmony_ci	 * space function that was run before the new one is run.  The
5178c2ecf20Sopenharmony_ci	 * compasit's set_config() assumes that if there is no more
5188c2ecf20Sopenharmony_ci	 * then MAX_CONFIG_INTERFACES interfaces in a configuration
5198c2ecf20Sopenharmony_ci	 * then there is a NULL pointer after the last interface in
5208c2ecf20Sopenharmony_ci	 * c->interface array.  We need to make sure this is true.
5218c2ecf20Sopenharmony_ci	 */
5228c2ecf20Sopenharmony_ci	if (c->next_interface_id < ARRAY_SIZE(c->interface))
5238c2ecf20Sopenharmony_ci		c->interface[c->next_interface_id] = NULL;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_cierror:
5278c2ecf20Sopenharmony_ci	while (--i >= 0) {
5288c2ecf20Sopenharmony_ci		if (!IS_ERR(f_ffs[gc->num][i]))
5298c2ecf20Sopenharmony_ci			usb_remove_function(c, f_ffs[gc->num][i]);
5308c2ecf20Sopenharmony_ci		usb_put_function(f_ffs[gc->num][i]);
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci	return ret;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_ETH
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic int eth_bind_config(struct usb_configuration *c)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	int status = 0;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	if (can_support_ecm(c->cdev->gadget)) {
5428c2ecf20Sopenharmony_ci		f_ecm = usb_get_function(fi_ecm);
5438c2ecf20Sopenharmony_ci		if (IS_ERR(f_ecm))
5448c2ecf20Sopenharmony_ci			return PTR_ERR(f_ecm);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci		status = usb_add_function(c, f_ecm);
5478c2ecf20Sopenharmony_ci		if (status < 0)
5488c2ecf20Sopenharmony_ci			usb_put_function(f_ecm);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	} else {
5518c2ecf20Sopenharmony_ci		f_geth = usb_get_function(fi_geth);
5528c2ecf20Sopenharmony_ci		if (IS_ERR(f_geth))
5538c2ecf20Sopenharmony_ci			return PTR_ERR(f_geth);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci		status = usb_add_function(c, f_geth);
5568c2ecf20Sopenharmony_ci		if (status < 0)
5578c2ecf20Sopenharmony_ci			usb_put_function(f_geth);
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci	return status;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci#endif
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic int bind_rndis_config(struct usb_configuration *c)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	int status = 0;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	f_rndis = usb_get_function(fi_rndis);
5718c2ecf20Sopenharmony_ci	if (IS_ERR(f_rndis))
5728c2ecf20Sopenharmony_ci		return PTR_ERR(f_rndis);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	status = usb_add_function(c, f_rndis);
5758c2ecf20Sopenharmony_ci	if (status < 0)
5768c2ecf20Sopenharmony_ci		usb_put_function(f_rndis);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	return status;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci#endif
582