18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * nokia.c -- Nokia Composite Gadget Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008-2010 Nokia Corporation
68c2ecf20Sopenharmony_ci * Contact: Felipe Balbi <felipe.balbi@nokia.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This gadget driver borrows from serial.c which is:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
118c2ecf20Sopenharmony_ci * Copyright (C) 2008 by David Brownell
128c2ecf20Sopenharmony_ci * Copyright (C) 2008 by Nokia Corporation
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/device.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "u_serial.h"
208c2ecf20Sopenharmony_ci#include "u_ether.h"
218c2ecf20Sopenharmony_ci#include "u_phonet.h"
228c2ecf20Sopenharmony_ci#include "u_ecm.h"
238c2ecf20Sopenharmony_ci#include "f_mass_storage.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Defines */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define NOKIA_VERSION_NUM		0x0211
288c2ecf20Sopenharmony_ci#define NOKIA_LONG_NAME			"N900 (PC-Suite Mode)"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciUSB_GADGET_COMPOSITE_OPTIONS();
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ciUSB_ETHERNET_MODULE_PARAMETERS();
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic struct fsg_module_parameters fsg_mod_data = {
358c2ecf20Sopenharmony_ci	.stall = 0,
368c2ecf20Sopenharmony_ci	.luns = 2,
378c2ecf20Sopenharmony_ci	.removable_count = 2,
388c2ecf20Sopenharmony_ci	.removable = { 1, 1, },
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_GADGET_DEBUG_FILES
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#else
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci * Number of buffers we will use.
498c2ecf20Sopenharmony_ci * 2 is usually enough for good buffering pipeline
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_ci#define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#endif /* CONFIG_USB_DEBUG */
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ciFSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define NOKIA_VENDOR_ID			0x0421	/* Nokia */
588c2ecf20Sopenharmony_ci#define NOKIA_PRODUCT_ID		0x01c8	/* Nokia Gadget */
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* string IDs are assigned dynamically */
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define STRING_DESCRIPTION_IDX		USB_GADGET_FIRST_AVAIL_IDX
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic char manufacturer_nokia[] = "Nokia";
658c2ecf20Sopenharmony_cistatic const char description_nokia[] = "PC-Suite Configuration";
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic struct usb_string strings_dev[] = {
688c2ecf20Sopenharmony_ci	[USB_GADGET_MANUFACTURER_IDX].s = manufacturer_nokia,
698c2ecf20Sopenharmony_ci	[USB_GADGET_PRODUCT_IDX].s = NOKIA_LONG_NAME,
708c2ecf20Sopenharmony_ci	[USB_GADGET_SERIAL_IDX].s = "",
718c2ecf20Sopenharmony_ci	[STRING_DESCRIPTION_IDX].s = description_nokia,
728c2ecf20Sopenharmony_ci	{  } /* end of list */
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic struct usb_gadget_strings stringtab_dev = {
768c2ecf20Sopenharmony_ci	.language	= 0x0409,	/* en-us */
778c2ecf20Sopenharmony_ci	.strings	= strings_dev,
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic struct usb_gadget_strings *dev_strings[] = {
818c2ecf20Sopenharmony_ci	&stringtab_dev,
828c2ecf20Sopenharmony_ci	NULL,
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic struct usb_device_descriptor device_desc = {
868c2ecf20Sopenharmony_ci	.bLength		= USB_DT_DEVICE_SIZE,
878c2ecf20Sopenharmony_ci	.bDescriptorType	= USB_DT_DEVICE,
888c2ecf20Sopenharmony_ci	/* .bcdUSB = DYNAMIC */
898c2ecf20Sopenharmony_ci	.bDeviceClass		= USB_CLASS_COMM,
908c2ecf20Sopenharmony_ci	.idVendor		= cpu_to_le16(NOKIA_VENDOR_ID),
918c2ecf20Sopenharmony_ci	.idProduct		= cpu_to_le16(NOKIA_PRODUCT_ID),
928c2ecf20Sopenharmony_ci	.bcdDevice		= cpu_to_le16(NOKIA_VERSION_NUM),
938c2ecf20Sopenharmony_ci	/* .iManufacturer = DYNAMIC */
948c2ecf20Sopenharmony_ci	/* .iProduct = DYNAMIC */
958c2ecf20Sopenharmony_ci	.bNumConfigurations =	1,
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/* Module */
1018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Nokia composite gadget driver for N900");
1028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Felipe Balbi");
1038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/
1068c2ecf20Sopenharmony_cistatic struct usb_function *f_acm_cfg1;
1078c2ecf20Sopenharmony_cistatic struct usb_function *f_acm_cfg2;
1088c2ecf20Sopenharmony_cistatic struct usb_function *f_ecm_cfg1;
1098c2ecf20Sopenharmony_cistatic struct usb_function *f_ecm_cfg2;
1108c2ecf20Sopenharmony_cistatic struct usb_function *f_obex1_cfg1;
1118c2ecf20Sopenharmony_cistatic struct usb_function *f_obex2_cfg1;
1128c2ecf20Sopenharmony_cistatic struct usb_function *f_obex1_cfg2;
1138c2ecf20Sopenharmony_cistatic struct usb_function *f_obex2_cfg2;
1148c2ecf20Sopenharmony_cistatic struct usb_function *f_phonet_cfg1;
1158c2ecf20Sopenharmony_cistatic struct usb_function *f_phonet_cfg2;
1168c2ecf20Sopenharmony_cistatic struct usb_function *f_msg_cfg1;
1178c2ecf20Sopenharmony_cistatic struct usb_function *f_msg_cfg2;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic struct usb_configuration nokia_config_500ma_driver = {
1218c2ecf20Sopenharmony_ci	.label		= "Bus Powered",
1228c2ecf20Sopenharmony_ci	.bConfigurationValue = 1,
1238c2ecf20Sopenharmony_ci	/* .iConfiguration = DYNAMIC */
1248c2ecf20Sopenharmony_ci	.bmAttributes	= USB_CONFIG_ATT_ONE,
1258c2ecf20Sopenharmony_ci	.MaxPower	= 500,
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic struct usb_configuration nokia_config_100ma_driver = {
1298c2ecf20Sopenharmony_ci	.label		= "Self Powered",
1308c2ecf20Sopenharmony_ci	.bConfigurationValue = 2,
1318c2ecf20Sopenharmony_ci	/* .iConfiguration = DYNAMIC */
1328c2ecf20Sopenharmony_ci	.bmAttributes	= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
1338c2ecf20Sopenharmony_ci	.MaxPower	= 100,
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_acm;
1378c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_ecm;
1388c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_obex1;
1398c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_obex2;
1408c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_phonet;
1418c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_msg;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic int nokia_bind_config(struct usb_configuration *c)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct usb_function *f_acm;
1468c2ecf20Sopenharmony_ci	struct usb_function *f_phonet = NULL;
1478c2ecf20Sopenharmony_ci	struct usb_function *f_obex1 = NULL;
1488c2ecf20Sopenharmony_ci	struct usb_function *f_ecm;
1498c2ecf20Sopenharmony_ci	struct usb_function *f_obex2 = NULL;
1508c2ecf20Sopenharmony_ci	struct usb_function *f_msg;
1518c2ecf20Sopenharmony_ci	int status = 0;
1528c2ecf20Sopenharmony_ci	int obex1_stat = -1;
1538c2ecf20Sopenharmony_ci	int obex2_stat = -1;
1548c2ecf20Sopenharmony_ci	int phonet_stat = -1;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (!IS_ERR(fi_phonet)) {
1578c2ecf20Sopenharmony_ci		f_phonet = usb_get_function(fi_phonet);
1588c2ecf20Sopenharmony_ci		if (IS_ERR(f_phonet))
1598c2ecf20Sopenharmony_ci			pr_debug("could not get phonet function\n");
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (!IS_ERR(fi_obex1)) {
1638c2ecf20Sopenharmony_ci		f_obex1 = usb_get_function(fi_obex1);
1648c2ecf20Sopenharmony_ci		if (IS_ERR(f_obex1))
1658c2ecf20Sopenharmony_ci			pr_debug("could not get obex function 0\n");
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (!IS_ERR(fi_obex2)) {
1698c2ecf20Sopenharmony_ci		f_obex2 = usb_get_function(fi_obex2);
1708c2ecf20Sopenharmony_ci		if (IS_ERR(f_obex2))
1718c2ecf20Sopenharmony_ci			pr_debug("could not get obex function 1\n");
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	f_acm = usb_get_function(fi_acm);
1758c2ecf20Sopenharmony_ci	if (IS_ERR(f_acm)) {
1768c2ecf20Sopenharmony_ci		status = PTR_ERR(f_acm);
1778c2ecf20Sopenharmony_ci		goto err_get_acm;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	f_ecm = usb_get_function(fi_ecm);
1818c2ecf20Sopenharmony_ci	if (IS_ERR(f_ecm)) {
1828c2ecf20Sopenharmony_ci		status = PTR_ERR(f_ecm);
1838c2ecf20Sopenharmony_ci		goto err_get_ecm;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	f_msg = usb_get_function(fi_msg);
1878c2ecf20Sopenharmony_ci	if (IS_ERR(f_msg)) {
1888c2ecf20Sopenharmony_ci		status = PTR_ERR(f_msg);
1898c2ecf20Sopenharmony_ci		goto err_get_msg;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_phonet)) {
1938c2ecf20Sopenharmony_ci		phonet_stat = usb_add_function(c, f_phonet);
1948c2ecf20Sopenharmony_ci		if (phonet_stat)
1958c2ecf20Sopenharmony_ci			pr_debug("could not add phonet function\n");
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_obex1)) {
1998c2ecf20Sopenharmony_ci		obex1_stat = usb_add_function(c, f_obex1);
2008c2ecf20Sopenharmony_ci		if (obex1_stat)
2018c2ecf20Sopenharmony_ci			pr_debug("could not add obex function 0\n");
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_obex2)) {
2058c2ecf20Sopenharmony_ci		obex2_stat = usb_add_function(c, f_obex2);
2068c2ecf20Sopenharmony_ci		if (obex2_stat)
2078c2ecf20Sopenharmony_ci			pr_debug("could not add obex function 1\n");
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	status = usb_add_function(c, f_acm);
2118c2ecf20Sopenharmony_ci	if (status)
2128c2ecf20Sopenharmony_ci		goto err_conf;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	status = usb_add_function(c, f_ecm);
2158c2ecf20Sopenharmony_ci	if (status) {
2168c2ecf20Sopenharmony_ci		pr_debug("could not bind ecm config %d\n", status);
2178c2ecf20Sopenharmony_ci		goto err_ecm;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	status = usb_add_function(c, f_msg);
2218c2ecf20Sopenharmony_ci	if (status)
2228c2ecf20Sopenharmony_ci		goto err_msg;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (c == &nokia_config_500ma_driver) {
2258c2ecf20Sopenharmony_ci		f_acm_cfg1 = f_acm;
2268c2ecf20Sopenharmony_ci		f_ecm_cfg1 = f_ecm;
2278c2ecf20Sopenharmony_ci		f_phonet_cfg1 = f_phonet;
2288c2ecf20Sopenharmony_ci		f_obex1_cfg1 = f_obex1;
2298c2ecf20Sopenharmony_ci		f_obex2_cfg1 = f_obex2;
2308c2ecf20Sopenharmony_ci		f_msg_cfg1 = f_msg;
2318c2ecf20Sopenharmony_ci	} else {
2328c2ecf20Sopenharmony_ci		f_acm_cfg2 = f_acm;
2338c2ecf20Sopenharmony_ci		f_ecm_cfg2 = f_ecm;
2348c2ecf20Sopenharmony_ci		f_phonet_cfg2 = f_phonet;
2358c2ecf20Sopenharmony_ci		f_obex1_cfg2 = f_obex1;
2368c2ecf20Sopenharmony_ci		f_obex2_cfg2 = f_obex2;
2378c2ecf20Sopenharmony_ci		f_msg_cfg2 = f_msg;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return status;
2418c2ecf20Sopenharmony_cierr_msg:
2428c2ecf20Sopenharmony_ci	usb_remove_function(c, f_ecm);
2438c2ecf20Sopenharmony_cierr_ecm:
2448c2ecf20Sopenharmony_ci	usb_remove_function(c, f_acm);
2458c2ecf20Sopenharmony_cierr_conf:
2468c2ecf20Sopenharmony_ci	if (!obex2_stat)
2478c2ecf20Sopenharmony_ci		usb_remove_function(c, f_obex2);
2488c2ecf20Sopenharmony_ci	if (!obex1_stat)
2498c2ecf20Sopenharmony_ci		usb_remove_function(c, f_obex1);
2508c2ecf20Sopenharmony_ci	if (!phonet_stat)
2518c2ecf20Sopenharmony_ci		usb_remove_function(c, f_phonet);
2528c2ecf20Sopenharmony_ci	usb_put_function(f_msg);
2538c2ecf20Sopenharmony_cierr_get_msg:
2548c2ecf20Sopenharmony_ci	usb_put_function(f_ecm);
2558c2ecf20Sopenharmony_cierr_get_ecm:
2568c2ecf20Sopenharmony_ci	usb_put_function(f_acm);
2578c2ecf20Sopenharmony_cierr_get_acm:
2588c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_obex2))
2598c2ecf20Sopenharmony_ci		usb_put_function(f_obex2);
2608c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_obex1))
2618c2ecf20Sopenharmony_ci		usb_put_function(f_obex1);
2628c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_phonet))
2638c2ecf20Sopenharmony_ci		usb_put_function(f_phonet);
2648c2ecf20Sopenharmony_ci	return status;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int nokia_bind(struct usb_composite_dev *cdev)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct usb_gadget	*gadget = cdev->gadget;
2708c2ecf20Sopenharmony_ci	struct fsg_opts		*fsg_opts;
2718c2ecf20Sopenharmony_ci	struct fsg_config	fsg_config;
2728c2ecf20Sopenharmony_ci	int			status;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	status = usb_string_ids_tab(cdev, strings_dev);
2758c2ecf20Sopenharmony_ci	if (status < 0)
2768c2ecf20Sopenharmony_ci		goto err_usb;
2778c2ecf20Sopenharmony_ci	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
2788c2ecf20Sopenharmony_ci	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
2798c2ecf20Sopenharmony_ci	status = strings_dev[STRING_DESCRIPTION_IDX].id;
2808c2ecf20Sopenharmony_ci	nokia_config_500ma_driver.iConfiguration = status;
2818c2ecf20Sopenharmony_ci	nokia_config_100ma_driver.iConfiguration = status;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (!gadget_is_altset_supported(gadget)) {
2848c2ecf20Sopenharmony_ci		status = -ENODEV;
2858c2ecf20Sopenharmony_ci		goto err_usb;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	fi_phonet = usb_get_function_instance("phonet");
2898c2ecf20Sopenharmony_ci	if (IS_ERR(fi_phonet))
2908c2ecf20Sopenharmony_ci		pr_debug("could not find phonet function\n");
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	fi_obex1 = usb_get_function_instance("obex");
2938c2ecf20Sopenharmony_ci	if (IS_ERR(fi_obex1))
2948c2ecf20Sopenharmony_ci		pr_debug("could not find obex function 1\n");
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	fi_obex2 = usb_get_function_instance("obex");
2978c2ecf20Sopenharmony_ci	if (IS_ERR(fi_obex2))
2988c2ecf20Sopenharmony_ci		pr_debug("could not find obex function 2\n");
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	fi_acm = usb_get_function_instance("acm");
3018c2ecf20Sopenharmony_ci	if (IS_ERR(fi_acm)) {
3028c2ecf20Sopenharmony_ci		status = PTR_ERR(fi_acm);
3038c2ecf20Sopenharmony_ci		goto err_obex2_inst;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	fi_ecm = usb_get_function_instance("ecm");
3078c2ecf20Sopenharmony_ci	if (IS_ERR(fi_ecm)) {
3088c2ecf20Sopenharmony_ci		status = PTR_ERR(fi_ecm);
3098c2ecf20Sopenharmony_ci		goto err_acm_inst;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	fi_msg = usb_get_function_instance("mass_storage");
3138c2ecf20Sopenharmony_ci	if (IS_ERR(fi_msg)) {
3148c2ecf20Sopenharmony_ci		status = PTR_ERR(fi_msg);
3158c2ecf20Sopenharmony_ci		goto err_ecm_inst;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* set up mass storage function */
3198c2ecf20Sopenharmony_ci	fsg_config_from_params(&fsg_config, &fsg_mod_data, fsg_num_buffers);
3208c2ecf20Sopenharmony_ci	fsg_config.vendor_name = "Nokia";
3218c2ecf20Sopenharmony_ci	fsg_config.product_name = "N900";
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	fsg_opts = fsg_opts_from_func_inst(fi_msg);
3248c2ecf20Sopenharmony_ci	fsg_opts->no_configfs = true;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	status = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers);
3278c2ecf20Sopenharmony_ci	if (status)
3288c2ecf20Sopenharmony_ci		goto err_msg_inst;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	status = fsg_common_set_cdev(fsg_opts->common, cdev, fsg_config.can_stall);
3318c2ecf20Sopenharmony_ci	if (status)
3328c2ecf20Sopenharmony_ci		goto err_msg_buf;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	fsg_common_set_sysfs(fsg_opts->common, true);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	status = fsg_common_create_luns(fsg_opts->common, &fsg_config);
3378c2ecf20Sopenharmony_ci	if (status)
3388c2ecf20Sopenharmony_ci		goto err_msg_buf;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	fsg_common_set_inquiry_string(fsg_opts->common, fsg_config.vendor_name,
3418c2ecf20Sopenharmony_ci				      fsg_config.product_name);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* finally register the configuration */
3448c2ecf20Sopenharmony_ci	status = usb_add_config(cdev, &nokia_config_500ma_driver,
3458c2ecf20Sopenharmony_ci			nokia_bind_config);
3468c2ecf20Sopenharmony_ci	if (status < 0)
3478c2ecf20Sopenharmony_ci		goto err_msg_luns;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	status = usb_add_config(cdev, &nokia_config_100ma_driver,
3508c2ecf20Sopenharmony_ci			nokia_bind_config);
3518c2ecf20Sopenharmony_ci	if (status < 0)
3528c2ecf20Sopenharmony_ci		goto err_put_cfg1;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	usb_composite_overwrite_options(cdev, &coverwrite);
3558c2ecf20Sopenharmony_ci	dev_info(&gadget->dev, "%s\n", NOKIA_LONG_NAME);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return 0;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cierr_put_cfg1:
3608c2ecf20Sopenharmony_ci	usb_put_function(f_acm_cfg1);
3618c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_obex1_cfg1))
3628c2ecf20Sopenharmony_ci		usb_put_function(f_obex1_cfg1);
3638c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_obex2_cfg1))
3648c2ecf20Sopenharmony_ci		usb_put_function(f_obex2_cfg1);
3658c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_phonet_cfg1))
3668c2ecf20Sopenharmony_ci		usb_put_function(f_phonet_cfg1);
3678c2ecf20Sopenharmony_ci	usb_put_function(f_ecm_cfg1);
3688c2ecf20Sopenharmony_cierr_msg_luns:
3698c2ecf20Sopenharmony_ci	fsg_common_remove_luns(fsg_opts->common);
3708c2ecf20Sopenharmony_cierr_msg_buf:
3718c2ecf20Sopenharmony_ci	fsg_common_free_buffers(fsg_opts->common);
3728c2ecf20Sopenharmony_cierr_msg_inst:
3738c2ecf20Sopenharmony_ci	usb_put_function_instance(fi_msg);
3748c2ecf20Sopenharmony_cierr_ecm_inst:
3758c2ecf20Sopenharmony_ci	usb_put_function_instance(fi_ecm);
3768c2ecf20Sopenharmony_cierr_acm_inst:
3778c2ecf20Sopenharmony_ci	usb_put_function_instance(fi_acm);
3788c2ecf20Sopenharmony_cierr_obex2_inst:
3798c2ecf20Sopenharmony_ci	if (!IS_ERR(fi_obex2))
3808c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_obex2);
3818c2ecf20Sopenharmony_ci	if (!IS_ERR(fi_obex1))
3828c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_obex1);
3838c2ecf20Sopenharmony_ci	if (!IS_ERR(fi_phonet))
3848c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_phonet);
3858c2ecf20Sopenharmony_cierr_usb:
3868c2ecf20Sopenharmony_ci	return status;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int nokia_unbind(struct usb_composite_dev *cdev)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_obex1_cfg2))
3928c2ecf20Sopenharmony_ci		usb_put_function(f_obex1_cfg2);
3938c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_obex2_cfg2))
3948c2ecf20Sopenharmony_ci		usb_put_function(f_obex2_cfg2);
3958c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_obex1_cfg1))
3968c2ecf20Sopenharmony_ci		usb_put_function(f_obex1_cfg1);
3978c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_obex2_cfg1))
3988c2ecf20Sopenharmony_ci		usb_put_function(f_obex2_cfg1);
3998c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_phonet_cfg1))
4008c2ecf20Sopenharmony_ci		usb_put_function(f_phonet_cfg1);
4018c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(f_phonet_cfg2))
4028c2ecf20Sopenharmony_ci		usb_put_function(f_phonet_cfg2);
4038c2ecf20Sopenharmony_ci	usb_put_function(f_acm_cfg1);
4048c2ecf20Sopenharmony_ci	usb_put_function(f_acm_cfg2);
4058c2ecf20Sopenharmony_ci	usb_put_function(f_ecm_cfg1);
4068c2ecf20Sopenharmony_ci	usb_put_function(f_ecm_cfg2);
4078c2ecf20Sopenharmony_ci	usb_put_function(f_msg_cfg1);
4088c2ecf20Sopenharmony_ci	usb_put_function(f_msg_cfg2);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	usb_put_function_instance(fi_msg);
4118c2ecf20Sopenharmony_ci	usb_put_function_instance(fi_ecm);
4128c2ecf20Sopenharmony_ci	if (!IS_ERR(fi_obex2))
4138c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_obex2);
4148c2ecf20Sopenharmony_ci	if (!IS_ERR(fi_obex1))
4158c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_obex1);
4168c2ecf20Sopenharmony_ci	if (!IS_ERR(fi_phonet))
4178c2ecf20Sopenharmony_ci		usb_put_function_instance(fi_phonet);
4188c2ecf20Sopenharmony_ci	usb_put_function_instance(fi_acm);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return 0;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic struct usb_composite_driver nokia_driver = {
4248c2ecf20Sopenharmony_ci	.name		= "g_nokia",
4258c2ecf20Sopenharmony_ci	.dev		= &device_desc,
4268c2ecf20Sopenharmony_ci	.strings	= dev_strings,
4278c2ecf20Sopenharmony_ci	.max_speed	= USB_SPEED_HIGH,
4288c2ecf20Sopenharmony_ci	.bind		= nokia_bind,
4298c2ecf20Sopenharmony_ci	.unbind		= nokia_unbind,
4308c2ecf20Sopenharmony_ci};
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cimodule_usb_composite_driver(nokia_driver);
433