usb: gadget: MBIM function driver for QCOM chipsets

This driver implements Mobile Broadband Interface Model protocol.
Control messages are transferred through QBI module
running in user space and communicating with mbim
using file operations interface android_mbim.
Data is transferred on SPS, Bam-to-Bam.

Change-Id: Ifc4b3f7e21b1a0dac377af272f9ae922ec9c7e2e
Signed-off-by: Anna Perel <aperel@codeaurora.org>
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 3e5c6f1..3fb9c83 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -70,6 +70,8 @@
 #include "f_rndis.c"
 #include "rndis.c"
 #include "u_ether.c"
+#include "u_bam_data.c"
+#include "f_mbim.c"
 
 MODULE_AUTHOR("Mike Lockwood");
 MODULE_DESCRIPTION("Android Composite USB Driver");
@@ -386,6 +388,35 @@
 	.attributes	= rmnet_function_attributes,
 };
 
+
+/* MBIM - used with BAM */
+#define MAX_MBIM_INSTANCES 1
+
+static int mbim_function_init(struct android_usb_function *f,
+					 struct usb_composite_dev *cdev)
+{
+	return mbim_init(MAX_MBIM_INSTANCES);
+}
+
+static void mbim_function_cleanup(struct android_usb_function *f)
+{
+	fmbim_cleanup();
+}
+
+static int mbim_function_bind_config(struct android_usb_function *f,
+					  struct usb_configuration *c)
+{
+	return mbim_bind_config(c, 0);
+}
+
+static struct android_usb_function mbim_function = {
+	.name		= "usb_mbim",
+	.cleanup	= mbim_function_cleanup,
+	.bind_config	= mbim_function_bind_config,
+	.init		= mbim_function_init,
+};
+
+
 /* DIAG */
 static char diag_clients[32];	    /*enabled DIAG clients- "diag[,diag_mdm]" */
 static ssize_t clients_store(
@@ -1004,6 +1035,7 @@
 
 
 static struct android_usb_function *supported_functions[] = {
+	&mbim_function,
 	&rmnet_smd_function,
 	&rmnet_sdio_function,
 	&rmnet_smd_sdio_function,
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
new file mode 100644
index 0000000..8288496
--- /dev/null
+++ b/drivers/usb/gadget/f_mbim.c
@@ -0,0 +1,1772 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include <linux/usb/cdc.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/android_composite.h>
+#include <linux/platform_device.h>
+
+#include <linux/spinlock.h>
+
+/*
+ * This function is a "Mobile Broadband Interface Model" (MBIM) link.
+ * MBIM is intended to be used with high-speed network attachments.
+ *
+ * Note that MBIM requires the use of "alternate settings" for its data
+ * interface.  This means that the set_alt() method has real work to do,
+ * and also means that a get_alt() method is required.
+ */
+
+#define MBIM_BULK_BUFFER_SIZE		4096
+
+#define MBIM_IOCTL_MAGIC		'o'
+#define MBIM_GET_NTB_SIZE		_IOR(MBIM_IOCTL_MAGIC, 2, u32)
+#define MBIM_GET_DATAGRAM_COUNT		_IOR(MBIM_IOCTL_MAGIC, 3, u16)
+
+#define NR_MBIM_PORTS			1
+
+struct ctrl_pkt {
+	void			*buf;
+	int			len;
+	struct list_head	list;
+};
+
+struct mbim_ep_descs {
+	struct usb_endpoint_descriptor	*in;
+	struct usb_endpoint_descriptor	*out;
+	struct usb_endpoint_descriptor	*notify;
+};
+
+struct mbim_notify_port {
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+	u8				notify_state;
+	atomic_t			notify_count;
+};
+
+enum mbim_notify_state {
+	NCM_NOTIFY_NONE,
+	NCM_NOTIFY_CONNECT,
+	NCM_NOTIFY_SPEED,
+};
+
+struct f_mbim {
+	struct usb_function function;
+	struct usb_composite_dev *cdev;
+
+	atomic_t	online;
+	bool		is_open;
+
+	atomic_t	open_excl;
+	atomic_t	ioctl_excl;
+	atomic_t	read_excl;
+	atomic_t	write_excl;
+
+	wait_queue_head_t read_wq;
+	wait_queue_head_t write_wq;
+
+	u8				port_num;
+	struct data_port		bam_port;
+	struct mbim_notify_port		not_port;
+
+	struct mbim_ep_descs		fs;
+	struct mbim_ep_descs		hs;
+
+	u8				ctrl_id, data_id;
+
+	struct ndp_parser_opts		*parser_opts;
+
+	spinlock_t			lock;
+
+	struct list_head	cpkt_req_q;
+	struct list_head	cpkt_resp_q;
+
+	u32			ntb_input_size;
+	u16			ntb_max_datagrams;
+
+	pid_t			user_pid;
+
+	atomic_t		error;
+};
+
+struct mbim_ntb_input_size {
+	u32	ntb_input_size;
+	u16	ntb_max_datagrams;
+	u16	reserved;
+};
+
+/* temporary variable used between mbim_open() and mbim_gadget_bind() */
+static struct f_mbim *_mbim_dev;
+
+static unsigned int nr_mbim_ports;
+
+static struct mbim_ports {
+	struct f_mbim	*port;
+	unsigned	port_num;
+} mbim_ports[NR_MBIM_PORTS];
+
+static inline struct f_mbim *func_to_mbim(struct usb_function *f)
+{
+	return container_of(f, struct f_mbim, function);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static inline unsigned mbim_bitrate(struct usb_gadget *g)
+{
+	if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+		return 13 * 512 * 8 * 1000 * 8;
+	else
+		return 19 *  64 * 1 * 1000 * 8;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define NTB_DEFAULT_IN_SIZE	(0x4000)
+#define NTB_OUT_SIZE		(0x1000)
+#define NDP_IN_DIVISOR		(0x4)
+
+#define FORMATS_SUPPORTED	USB_CDC_NCM_NTB16_SUPPORTED
+
+static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
+	.wLength = sizeof ntb_parameters,
+	.bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),
+	.dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),
+	.wNdpInDivisor = cpu_to_le16(NDP_IN_DIVISOR),
+	.wNdpInPayloadRemainder = cpu_to_le16(0),
+	.wNdpInAlignment = cpu_to_le16(4),
+
+	.dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),
+	.wNdpOutDivisor = cpu_to_le16(4),
+	.wNdpOutPayloadRemainder = cpu_to_le16(0),
+	.wNdpOutAlignment = cpu_to_le16(4),
+	.wNtbOutMaxDatagrams = cpu_to_le16(4),
+};
+
+/*
+ * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
+ * packet, to simplify cancellation; and a big transfer interval, to
+ * waste less bandwidth.
+ */
+
+#define LOG2_STATUS_INTERVAL_MSEC	5	/* 1 << 5 == 32 msec */
+#define NCM_STATUS_BYTECOUNT		16	/* 8 byte header + data */
+
+static struct usb_interface_assoc_descriptor mbim_iad_desc = {
+	.bLength =		sizeof mbim_iad_desc,
+	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
+
+	/* .bFirstInterface =	DYNAMIC, */
+	.bInterfaceCount =	2,	/* control + data */
+	.bFunctionClass =	2,
+	.bFunctionSubClass =	0x0e,
+	.bFunctionProtocol =	0,
+	/* .iFunction =		DYNAMIC */
+};
+
+/* interface descriptor: */
+static struct usb_interface_descriptor mbim_control_intf = {
+	.bLength =		sizeof mbim_control_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	0x02,
+	.bInterfaceSubClass =	0x0e,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc mbim_header_desc = {
+	.bLength =		sizeof mbim_header_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_union_desc mbim_union_desc = {
+	.bLength =		sizeof(mbim_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+
+static struct usb_cdc_mbb_desc mbb_desc = {
+	.bLength =		sizeof mbb_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_MBB_TYPE,
+
+	.bcdMbbVersion =	cpu_to_le16(0x0100),
+
+	.wMaxControlMessage =	cpu_to_le16(0x1000),
+	.bNumberFilters =	0x10,
+	.bMaxFilterSize =	0x80,
+	.wMaxSegmentSize =	cpu_to_le16(0x1000),
+	.bmNetworkCapabilities = 0x20,
+};
+
+/* the default data interface has no endpoints ... */
+static struct usb_interface_descriptor mbim_data_nop_intf = {
+	.bLength =		sizeof mbim_data_nop_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	0x0a,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0x02,
+	/* .iInterface = DYNAMIC */
+};
+
+/* ... but the "real" data interface has two bulk endpoints */
+static struct usb_interface_descriptor mbim_data_intf = {
+	.bLength =		sizeof mbim_data_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	0x0a,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0x02,
+	/* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_mbim_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+
+static struct usb_endpoint_descriptor fs_mbim_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_mbim_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *mbim_fs_function[] = {
+	(struct usb_descriptor_header *) &mbim_iad_desc,
+	/* MBIM control descriptors */
+	(struct usb_descriptor_header *) &mbim_control_intf,
+	(struct usb_descriptor_header *) &mbim_header_desc,
+	(struct usb_descriptor_header *) &mbb_desc,
+	(struct usb_descriptor_header *) &fs_mbim_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &mbim_data_nop_intf,
+	(struct usb_descriptor_header *) &mbim_data_intf,
+	(struct usb_descriptor_header *) &fs_mbim_in_desc,
+	(struct usb_descriptor_header *) &fs_mbim_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_mbim_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+static struct usb_endpoint_descriptor hs_mbim_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_mbim_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *mbim_hs_function[] = {
+	(struct usb_descriptor_header *) &mbim_iad_desc,
+	/* MBIM control descriptors */
+	(struct usb_descriptor_header *) &mbim_control_intf,
+	(struct usb_descriptor_header *) &mbim_header_desc,
+	(struct usb_descriptor_header *) &mbb_desc,
+	(struct usb_descriptor_header *) &hs_mbim_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &mbim_data_nop_intf,
+	(struct usb_descriptor_header *) &mbim_data_intf,
+	(struct usb_descriptor_header *) &hs_mbim_in_desc,
+	(struct usb_descriptor_header *) &hs_mbim_out_desc,
+	NULL,
+};
+
+/* string descriptors: */
+
+#define STRING_CTRL_IDX	0
+#define STRING_DATA_IDX	1
+
+static struct usb_string mbim_string_defs[] = {
+	[STRING_CTRL_IDX].s = "MBIM Control",
+	[STRING_DATA_IDX].s = "MBIM Data",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings mbim_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		mbim_string_defs,
+};
+
+static struct usb_gadget_strings *mbim_strings[] = {
+	&mbim_string_table,
+	NULL,
+};
+
+/*
+ * Here are options for the Datagram Pointer table (NDP) parser.
+ * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
+ * in NDP16 offsets and sizes fields are 1 16bit word wide,
+ * in NDP32 -- 2 16bit words wide. Also signatures are different.
+ * To make the parser code the same, put the differences in the structure,
+ * and switch pointers to the structures when the format is changed.
+ */
+
+struct ndp_parser_opts {
+	u32		nth_sign;
+	u32		ndp_sign;
+	unsigned	nth_size;
+	unsigned	ndp_size;
+	unsigned	ndplen_align;
+	/* sizes in u16 units */
+	unsigned	dgram_item_len; /* index or length */
+	unsigned	block_length;
+	unsigned	fp_index;
+	unsigned	reserved1;
+	unsigned	reserved2;
+	unsigned	next_fp_index;
+};
+
+#define INIT_NDP16_OPTS {				\
+	.nth_sign = USB_CDC_NCM_NTH16_SIGN,		\
+	.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,	\
+	.nth_size = sizeof(struct usb_cdc_ncm_nth16),	\
+	.ndp_size = sizeof(struct usb_cdc_ncm_ndp16),	\
+	.ndplen_align = 4,				\
+	.dgram_item_len = 1,				\
+	.block_length = 1,				\
+	.fp_index = 1,					\
+	.reserved1 = 0,					\
+	.reserved2 = 0,					\
+	.next_fp_index = 1,				\
+}
+
+#define INIT_NDP32_OPTS {				\
+	.nth_sign = USB_CDC_NCM_NTH32_SIGN,		\
+	.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,	\
+	.nth_size = sizeof(struct usb_cdc_ncm_nth32),	\
+	.ndp_size = sizeof(struct usb_cdc_ncm_ndp32),	\
+	.ndplen_align = 8,				\
+	.dgram_item_len = 2,				\
+	.block_length = 2,				\
+	.fp_index = 2,					\
+	.reserved1 = 1,					\
+	.reserved2 = 2,					\
+	.next_fp_index = 2,				\
+}
+
+static struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
+static struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
+
+static inline int mbim_lock(atomic_t *excl)
+{
+	if (atomic_inc_return(excl) == 1) {
+		return 0;
+	} else {
+		atomic_dec(excl);
+		return -EBUSY;
+	}
+}
+
+static inline void mbim_unlock(atomic_t *excl)
+{
+	atomic_dec(excl);
+}
+
+static struct ctrl_pkt *mbim_alloc_ctrl_pkt(unsigned len, gfp_t flags)
+{
+	struct ctrl_pkt *pkt;
+
+	pkt = kzalloc(sizeof(struct ctrl_pkt), flags);
+	if (!pkt)
+		return ERR_PTR(-ENOMEM);
+
+	pkt->buf = kmalloc(len, flags);
+	if (!pkt->buf) {
+		kfree(pkt);
+		return ERR_PTR(-ENOMEM);
+	}
+	pkt->len = len;
+
+	return pkt;
+}
+
+static void mbim_free_ctrl_pkt(struct ctrl_pkt *pkt)
+{
+	if (pkt) {
+		kfree(pkt->buf);
+		kfree(pkt);
+	}
+}
+
+static struct usb_request *mbim_alloc_req(struct usb_ep *ep, int buffer_size)
+{
+	struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+	if (!req)
+		return NULL;
+
+	req->buf = kmalloc(buffer_size, GFP_KERNEL);
+	if (!req->buf) {
+		usb_ep_free_request(ep, req);
+		return NULL;
+	}
+	req->length = buffer_size;
+	return req;
+}
+
+void fmbim_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	if (req) {
+		kfree(req->buf);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static void fmbim_ctrl_response_available(struct f_mbim *dev)
+{
+	struct usb_request		*req = dev->not_port.notify_req;
+	struct usb_cdc_notification	*event = NULL;
+	unsigned long			flags;
+	int				ret;
+
+	int notif_c = 0;
+
+	pr_info("dev:%p portno#%d\n", dev, dev->port_num);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!atomic_read(&dev->online)) {
+		pr_info("dev:%p is not online\n", dev);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	if (!req) {
+		pr_info("dev:%p req is NULL\n", dev);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	if (!req->buf) {
+		pr_info("dev:%p req->buf is NULL\n", dev);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
+	notif_c = atomic_inc_return(&dev->not_port.notify_count);
+	pr_info("atomic_inc_return[notif_c] = %d", notif_c);
+
+	event = req->buf;
+	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+	event->wValue = cpu_to_le16(0);
+	event->wIndex = cpu_to_le16(dev->ctrl_id);
+	event->wLength = cpu_to_le16(0);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	pr_info("Call usb_ep_queue");
+
+	ret = usb_ep_queue(dev->not_port.notify,
+			   dev->not_port.notify_req, GFP_ATOMIC);
+	if (ret) {
+		atomic_dec(&dev->not_port.notify_count);
+		pr_err("ep enqueue error %d\n", ret);
+	}
+
+	pr_info("Succcessfull Exit");
+}
+
+static int
+fmbim_send_cpkt_response(struct f_mbim *gr, struct ctrl_pkt *cpkt)
+{
+	struct f_mbim	*dev = gr;
+	unsigned long	flags;
+
+	if (!gr || !cpkt) {
+		pr_err("Invalid cpkt, dev:%p cpkt:%p\n",
+				gr, cpkt);
+		return -ENODEV;
+	}
+
+	pr_info("dev:%p port_num#%d\n", dev, dev->port_num);
+
+	if (!atomic_read(&dev->online)) {
+		pr_info("dev:%p is not connected\n", dev);
+		mbim_free_ctrl_pkt(cpkt);
+		return 0;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	list_add(&cpkt->list, &dev->cpkt_resp_q);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	fmbim_ctrl_response_available(dev);
+
+	return 0;
+}
+
+/* ---------------------------- BAM INTERFACE ----------------------------- */
+
+static int mbim_bam_setup(int no_ports)
+{
+	int ret;
+
+	pr_info("no_ports:%d\n", no_ports);
+
+	ret = bam_data_setup(no_ports);
+	if (ret) {
+		pr_err("bam_data_setup failed err: %d\n", ret);
+		return ret;
+	}
+
+	pr_info("Initialized %d ports\n", no_ports);
+	return 0;
+}
+
+static int mbim_bam_connect(struct f_mbim *dev)
+{
+	int ret;
+
+	pr_info("dev:%p portno:%d\n", dev, dev->port_num);
+
+	ret = bam_data_connect(&dev->bam_port, dev->port_num, dev->port_num);
+	if (ret) {
+		pr_err("bam_data_setup failed: err:%d\n",
+				ret);
+		return ret;
+	} else {
+		pr_info("mbim bam connected\n");
+	}
+
+	return 0;
+}
+
+static int mbim_bam_disconnect(struct f_mbim *dev)
+{
+	pr_info("dev:%p port:%d. Do nothing.\n",
+			dev, dev->port_num);
+
+	/* bam_data_disconnect(&dev->bam_port, dev->port_num); */
+
+	return 0;
+}
+
+/* -------------------------------------------------------------------------*/
+
+static inline void mbim_reset_values(struct f_mbim *mbim)
+{
+	mbim->parser_opts = &ndp16_opts;
+
+	mbim->ntb_input_size = NTB_DEFAULT_IN_SIZE;
+
+	atomic_set(&mbim->not_port.notify_count, 0);
+	atomic_set(&mbim->online, 0);
+}
+
+/*
+ * Context: mbim->lock held
+ */
+static void mbim_do_notify(struct f_mbim *mbim)
+{
+	struct usb_request		*req = mbim->not_port.notify_req;
+	struct usb_cdc_notification	*event;
+	struct usb_composite_dev	*cdev = mbim->cdev;
+	__le32				*data;
+	int				status;
+
+	pr_info("notify_state: %d", mbim->not_port.notify_state);
+
+	if (!req)
+		return;
+
+	event = req->buf;
+
+	switch (mbim->not_port.notify_state) {
+
+	case NCM_NOTIFY_NONE:
+		return;
+
+	case NCM_NOTIFY_CONNECT:
+		event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+		if (mbim->is_open)
+			event->wValue = cpu_to_le16(1);
+		else
+			event->wValue = cpu_to_le16(0);
+		event->wLength = 0;
+		req->length = sizeof *event;
+
+		pr_info("notify connect %s\n",
+			mbim->is_open ? "true" : "false");
+		mbim->not_port.notify_state = NCM_NOTIFY_NONE;
+		break;
+
+	case NCM_NOTIFY_SPEED:
+		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
+		event->wValue = cpu_to_le16(0);
+		event->wLength = cpu_to_le16(8);
+		req->length = NCM_STATUS_BYTECOUNT;
+
+		/* SPEED_CHANGE data is up/down speeds in bits/sec */
+		data = req->buf + sizeof *event;
+		data[0] = cpu_to_le32(mbim_bitrate(cdev->gadget));
+		data[1] = data[0];
+
+		pr_info("notify speed %d\n",
+			mbim_bitrate(cdev->gadget));
+		mbim->not_port.notify_state = NCM_NOTIFY_CONNECT;
+		break;
+	}
+	event->bmRequestType = 0xA1;
+	event->wIndex = cpu_to_le16(mbim->ctrl_id);
+
+	mbim->not_port.notify_req = NULL;
+	/*
+	 * In double buffering if there is a space in FIFO,
+	 * completion callback can be called right after the call,
+	 * so unlocking
+	 */
+	spin_unlock(&mbim->lock);
+	status = usb_ep_queue(mbim->not_port.notify, req, GFP_ATOMIC);
+	spin_lock(&mbim->lock);
+	if (status < 0) {
+		mbim->not_port.notify_req = req;
+		atomic_dec(&mbim->not_port.notify_count);
+		pr_err("usb_ep_queue failed, err: %d", status);
+	}
+}
+
+/*
+ * Context: mbim->lock held
+ */
+static void mbim_notify(struct f_mbim *mbim)
+{
+	/*
+	 * If mbim_notify() is called before the second (CONNECT)
+	 * notification is sent, then it will reset to send the SPEED
+	 * notificaion again (and again, and again), but it's not a problem
+	 */
+	pr_info("dev:%p\n", mbim);
+
+	mbim->not_port.notify_state = NCM_NOTIFY_SPEED;
+	mbim_do_notify(mbim);
+}
+
+static void mbim_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_mbim			*mbim = req->context;
+	struct usb_cdc_notification	*event = req->buf;
+
+	int notif_c = 0;
+
+	pr_info("dev:%p\n", mbim);
+
+	spin_lock(&mbim->lock);
+	switch (req->status) {
+	case 0:
+		pr_info("Notification %02x sent\n",
+			event->bNotificationType);
+
+		notif_c = atomic_dec_return(&mbim->not_port.notify_count);
+
+		if (notif_c != 0) {
+			pr_info("Continue to mbim_do_notify()");
+			break;
+		} else {
+			pr_info("notify_count decreased to 0. Do not notify");
+			spin_unlock(&mbim->lock);
+			return;
+		}
+
+		break;
+
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		mbim->not_port.notify_state = NCM_NOTIFY_NONE;
+		atomic_set(&mbim->not_port.notify_count, 0);
+		pr_info("ESHUTDOWN/ECONNRESET, connection gone");
+		break;
+	default:
+		pr_err("Unknown event %02x --> %d\n",
+			event->bNotificationType, req->status);
+		break;
+	}
+
+	mbim->not_port.notify_req = req;
+	mbim_do_notify(mbim);
+
+	spin_unlock(&mbim->lock);
+
+	pr_info("dev:%p Exit\n", mbim);
+}
+
+static void mbim_ep0out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	/* now for SET_NTB_INPUT_SIZE only */
+	unsigned		in_size = 0;
+	struct usb_function	*f = req->context;
+	struct f_mbim		*mbim = func_to_mbim(f);
+	struct mbim_ntb_input_size *ntb = NULL;
+
+	pr_info("dev:%p\n", mbim);
+
+	req->context = NULL;
+	if (req->status || req->actual != req->length) {
+		pr_err("Bad control-OUT transfer\n");
+		goto invalid;
+	}
+
+	if (req->length == 4) {
+		in_size = get_unaligned_le32(req->buf);
+		if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
+		    in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
+			pr_err("Illegal INPUT SIZE (%d) from host\n", in_size);
+			goto invalid;
+		}
+	} else if (req->length == 8) {
+		ntb = (struct mbim_ntb_input_size *)req->buf;
+		in_size = get_unaligned_le32(&(ntb->ntb_input_size));
+		if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
+		    in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
+			pr_err("Illegal INPUT SIZE (%d) from host\n", in_size);
+			goto invalid;
+		}
+		mbim->ntb_max_datagrams =
+			get_unaligned_le16(&(ntb->ntb_max_datagrams));
+	} else {
+		pr_err("Illegal NTB length %d\n", in_size);
+		goto invalid;
+	}
+
+	pr_info("Set NTB INPUT SIZE %d\n", in_size);
+
+	mbim->ntb_input_size = in_size;
+	return;
+
+invalid:
+	usb_ep_set_halt(ep);
+
+	pr_err("dev:%p Failed\n", mbim);
+
+	return;
+}
+
+static void
+fmbim_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_mbim		*dev = req->context;
+	struct ctrl_pkt		*cpkt = NULL;
+	int			len = req->actual;
+
+	if (!dev) {
+		pr_err("mbim dev is null\n");
+		return;
+	}
+
+	if (req->status < 0) {
+		pr_err("mbim command error %d\n", req->status);
+		return;
+	}
+
+	pr_info("dev:%p port#%d\n", dev, dev->port_num);
+
+	spin_lock(&dev->lock);
+	if (!dev->is_open) {
+		pr_err("mbim file handler %p is not open", dev);
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	cpkt = mbim_alloc_ctrl_pkt(len, GFP_ATOMIC);
+	if (!cpkt) {
+		pr_err("Unable to allocate ctrl pkt\n");
+		spin_unlock(&dev->lock);
+		return;
+	}
+
+	pr_info("Add to cpkt_req_q packet with len = %d\n", len);
+	memcpy(cpkt->buf, req->buf, len);
+	list_add_tail(&cpkt->list, &dev->cpkt_req_q);
+	spin_unlock(&dev->lock);
+
+	/* wakeup read thread */
+	pr_info("Wake up read queue");
+	wake_up(&dev->read_wq);
+
+	return;
+}
+
+static int
+mbim_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_mbim			*mbim = func_to_mbim(f);
+	struct usb_composite_dev	*cdev = mbim->cdev;
+	struct usb_request		*req = cdev->req;
+	struct ctrl_pkt		*cpkt = NULL;
+	int	value = -EOPNOTSUPP;
+	u16	w_index = le16_to_cpu(ctrl->wIndex);
+	u16	w_value = le16_to_cpu(ctrl->wValue);
+	u16	w_length = le16_to_cpu(ctrl->wLength);
+
+	/*
+	 * composite driver infrastructure handles everything except
+	 * CDC class messages; interface activation uses set_alt().
+	 */
+
+	if (!atomic_read(&mbim->online)) {
+		pr_info("usb cable is not connected\n");
+		return -ENOTCONN;
+	}
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_RESET_FUNCTION:
+
+		pr_info("USB_CDC_RESET_FUNCTION");
+		value = 0;
+		if (!_mbim_dev->user_pid) {
+			pr_err("QBI pid is not set");
+			break;
+		}
+
+		if (!_mbim_dev->is_open) {
+			pr_err("QBI is not up yet");
+			break;
+		}
+
+		send_sig_info(SIGUSR1, SEND_SIG_NOINFO,
+			find_task_by_vpid(_mbim_dev->user_pid));
+
+		pr_info("Sent signal to QBI pid %d",
+			_mbim_dev->user_pid);
+		break;
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+
+		pr_info("USB_CDC_SEND_ENCAPSULATED_COMMAND");
+
+		if (w_length > req->length) {
+			pr_err("w_length > req->length: %d > %d",
+			w_length, req->length);
+		}
+		value = w_length;
+		req->complete = fmbim_cmd_complete;
+		req->context = mbim;
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+
+		pr_info("USB_CDC_GET_ENCAPSULATED_RESPONSE");
+
+		if (w_value) {
+			pr_err("w_length > 0: %d", w_length);
+			break;
+		}
+
+		pr_info("req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+
+		spin_lock(&mbim->lock);
+		if (list_empty(&mbim->cpkt_resp_q)) {
+			pr_err("ctrl resp queue empty\n");
+			spin_unlock(&mbim->lock);
+			break;
+		}
+
+		cpkt = list_first_entry(&mbim->cpkt_resp_q,
+					struct ctrl_pkt, list);
+		list_del(&cpkt->list);
+		spin_unlock(&mbim->lock);
+
+		value = min_t(unsigned, w_length, cpkt->len);
+		memcpy(req->buf, cpkt->buf, value);
+		mbim_free_ctrl_pkt(cpkt);
+
+		pr_info("copied encapsulated_response %d bytes",
+			value);
+
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_PARAMETERS:
+
+		pr_info("USB_CDC_GET_NTB_PARAMETERS");
+
+		if (w_length == 0 || w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		value = w_length > sizeof ntb_parameters ?
+			sizeof ntb_parameters : w_length;
+		memcpy(req->buf, &ntb_parameters, value);
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_INPUT_SIZE:
+
+		pr_info("USB_CDC_GET_NTB_INPUT_SIZE");
+
+		if (w_length < 4 || w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		put_unaligned_le32(mbim->ntb_input_size, req->buf);
+		value = 4;
+		pr_info("Reply to host INPUT SIZE %d\n",
+		     mbim->ntb_input_size);
+		break;
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SET_NTB_INPUT_SIZE:
+
+		pr_info("USB_CDC_SET_NTB_INPUT_SIZE");
+
+		if (w_length != 4 && w_length != 8) {
+			pr_err("wrong NTB length %d", w_length);
+			break;
+		}
+
+		if (w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		req->complete = mbim_ep0out_complete;
+		req->length = w_length;
+		req->context = f;
+
+		value = req->length;
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_FORMAT:
+	{
+		uint16_t format;
+
+		pr_info("USB_CDC_GET_NTB_FORMAT");
+
+		if (w_length < 2 || w_value != 0 || w_index != mbim->ctrl_id)
+			break;
+
+		format = (mbim->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
+		put_unaligned_le16(format, req->buf);
+		value = 2;
+		pr_info("NTB FORMAT: sending %d\n", format);
+		break;
+	}
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SET_NTB_FORMAT:
+	{
+		pr_info("USB_CDC_SET_NTB_FORMAT");
+
+		if (w_length != 0 || w_index != mbim->ctrl_id)
+			break;
+		switch (w_value) {
+		case 0x0000:
+			mbim->parser_opts = &ndp16_opts;
+			pr_info("NCM16 selected\n");
+			break;
+		case 0x0001:
+			mbim->parser_opts = &ndp32_opts;
+			pr_info("NCM32 selected\n");
+			break;
+		default:
+			break;
+		}
+		value = 0;
+		break;
+	}
+
+	/* optional in mbim descriptor: */
+	/* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */
+	/* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */
+
+	default:
+	pr_err("invalid control req: %02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest,
+		w_value, w_index, w_length);
+	}
+
+	 /* respond with data transfer or status phase? */
+	if (value >= 0) {
+		pr_info("control request: %02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			pr_err("queueing req failed: %02x.%02x, err %d\n",
+				ctrl->bRequestType,
+			       ctrl->bRequest, value);
+		}
+	} else {
+		pr_err("ctrl req err %d: %02x.%02x v%04x i%04x l%d\n",
+			value, ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static int mbim_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_mbim		*mbim = func_to_mbim(f);
+	struct usb_composite_dev *cdev = mbim->cdev;
+	int ret = 0;
+
+	/* Control interface has only altsetting 0 */
+	if (intf == mbim->ctrl_id) {
+
+		pr_info("CONTROL_INTERFACE");
+
+		if (alt != 0)
+			goto fail;
+
+		if (mbim->not_port.notify->driver_data) {
+			pr_info("reset mbim control %d\n", intf);
+			usb_ep_disable(mbim->not_port.notify);
+		}
+
+		ret = config_ep_by_speed(cdev->gadget, f,
+					mbim->not_port.notify);
+		if (ret) {
+			mbim->not_port.notify->desc = NULL;
+			pr_err("Failed configuring notify ep %s: err %d\n",
+				mbim->not_port.notify->name, ret);
+			return ret;
+		}
+
+		ret = usb_ep_enable(mbim->not_port.notify);
+		if (ret) {
+			pr_err("usb ep#%s enable failed, err#%d\n",
+				mbim->not_port.notify->name, ret);
+			return ret;
+		}
+		mbim->not_port.notify->driver_data = mbim;
+
+	/* Data interface has two altsettings, 0 and 1 */
+	} else if (intf == mbim->data_id) {
+
+		pr_info("DATA_INTERFACE");
+
+		if (alt > 1)
+			goto fail;
+
+		if (mbim->bam_port.in->driver_data) {
+			pr_info("reset mbim\n");
+			mbim_reset_values(mbim);
+			mbim_bam_disconnect(mbim);
+		}
+
+		/*
+		 * CDC Network only sends data in non-default altsettings.
+		 * Changing altsettings resets filters, statistics, etc.
+		 */
+		if (alt == 1) {
+			pr_info("Alt set 1, initialize ports");
+
+			if (!mbim->bam_port.in->desc) {
+
+				pr_info("Choose endpoints");
+
+				ret = config_ep_by_speed(cdev->gadget, f,
+							mbim->bam_port.in);
+				if (ret) {
+					mbim->bam_port.in->desc = NULL;
+					pr_err("IN ep %s failed: %d\n",
+					mbim->bam_port.in->name, ret);
+					return ret;
+				}
+
+				pr_info("Set mbim port in_desc = 0x%p",
+					mbim->bam_port.in->desc);
+
+				ret = config_ep_by_speed(cdev->gadget, f,
+							mbim->bam_port.out);
+				if (ret) {
+					mbim->bam_port.out->desc = NULL;
+					pr_err("OUT ep %s failed: %d\n",
+					mbim->bam_port.out->name, ret);
+					return ret;
+				}
+
+				pr_info("Set mbim port out_desc = 0x%p",
+					mbim->bam_port.out->desc);
+			} else {
+				pr_info("PORTS already SET");
+			}
+
+			pr_info("Activate mbim\n");
+			mbim_bam_connect(mbim);
+		}
+
+		spin_lock(&mbim->lock);
+		mbim_notify(mbim);
+		spin_unlock(&mbim->lock);
+	} else {
+		goto fail;
+	}
+
+	atomic_set(&mbim->online, 1);
+
+	pr_info("SET DEVICE ONLINE");
+
+	/* wakeup file threads */
+	wake_up(&mbim->read_wq);
+	wake_up(&mbim->write_wq);
+
+	return 0;
+
+fail:
+	pr_err("ERROR: Illegal Interface");
+	return -EINVAL;
+}
+
+/*
+ * Because the data interface supports multiple altsettings,
+ * this MBIM function *MUST* implement a get_alt() method.
+ */
+static int mbim_get_alt(struct usb_function *f, unsigned intf)
+{
+	struct f_mbim	*mbim = func_to_mbim(f);
+
+	if (intf == mbim->ctrl_id)
+		return 0;
+	return mbim->bam_port.in->driver_data ? 1 : 0;
+}
+
+static void mbim_disable(struct usb_function *f)
+{
+	struct f_mbim	*mbim = func_to_mbim(f);
+
+	pr_info("SET DEVICE OFFLINE");
+	atomic_set(&mbim->online, 0);
+
+	if (_mbim_dev && _mbim_dev->user_pid && _mbim_dev->is_open) {
+		send_sig_info(SIGUSR1, SEND_SIG_NOINFO,
+			find_task_by_vpid(_mbim_dev->user_pid));
+		pr_info("Sending reset signal to QBI pid %d",
+			_mbim_dev->user_pid);
+	}
+
+	mbim_bam_disconnect(mbim);
+
+	if (mbim->not_port.notify->driver_data) {
+		usb_ep_disable(mbim->not_port.notify);
+		mbim->not_port.notify->driver_data = NULL;
+	}
+
+	pr_info("mbim deactivated\n");
+}
+
+/*---------------------- function driver setup/binding ---------------------*/
+
+static int
+mbim_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_mbim		*mbim = func_to_mbim(f);
+	int			status;
+	struct usb_ep		*ep;
+
+	pr_info("Enter");
+
+	mbim->cdev = cdev;
+
+	/* allocate instance-specific interface IDs */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	mbim->ctrl_id = status;
+	mbim_iad_desc.bFirstInterface = status;
+
+	mbim_control_intf.bInterfaceNumber = status;
+	mbim_union_desc.bMasterInterface0 = status;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	mbim->data_id = status;
+
+	mbim_data_nop_intf.bInterfaceNumber = status;
+	mbim_data_intf.bInterfaceNumber = status;
+	mbim_union_desc.bSlaveInterface0 = status;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_mbim_in_desc);
+	if (!ep) {
+		pr_err("usb epin autoconfig failed\n");
+		goto fail;
+	}
+	pr_info("usb epin autoconfig succeeded\n");
+	ep->driver_data = cdev;	/* claim */
+	mbim->bam_port.in = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_mbim_out_desc);
+	if (!ep) {
+		pr_err("usb epout autoconfig failed\n");
+		goto fail;
+	}
+	pr_info("usb epout autoconfig succeeded\n");
+	ep->driver_data = cdev;	/* claim */
+	mbim->bam_port.out = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_mbim_notify_desc);
+	if (!ep) {
+		pr_err("usb notify ep autoconfig failed\n");
+		goto fail;
+	}
+	pr_info("usb notify ep autoconfig succeeded\n");
+	mbim->not_port.notify = ep;
+	ep->driver_data = cdev;	/* claim */
+
+	status = -ENOMEM;
+
+	/* allocate notification request and buffer */
+	mbim->not_port.notify_req = mbim_alloc_req(ep, NCM_STATUS_BYTECOUNT);
+	if (!mbim->not_port.notify_req) {
+		pr_info("failed to allocate notify request\n");
+		goto fail;
+	}
+	pr_info("allocated notify ep request & request buffer\n");
+
+	mbim->not_port.notify_req->context = mbim;
+	mbim->not_port.notify_req->complete = mbim_notify_complete;
+
+	/* copy descriptors, and track endpoint copies */
+	f->descriptors = usb_copy_descriptors(mbim_fs_function);
+	if (!f->descriptors)
+		goto fail;
+
+	/*
+	 * support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		hs_mbim_in_desc.bEndpointAddress =
+				fs_mbim_in_desc.bEndpointAddress;
+		hs_mbim_out_desc.bEndpointAddress =
+				fs_mbim_out_desc.bEndpointAddress;
+		hs_mbim_notify_desc.bEndpointAddress =
+				fs_mbim_notify_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->hs_descriptors = usb_copy_descriptors(mbim_hs_function);
+		if (!f->hs_descriptors)
+			goto fail;
+	}
+
+	pr_info("mbim(%d): %s speed IN/%s OUT/%s NOTIFY/%s\n",
+			mbim->port_num,
+			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+			mbim->bam_port.in->name, mbim->bam_port.out->name,
+			mbim->not_port.notify->name);
+
+	return 0;
+
+fail:
+	pr_err("%s failed to bind, err %d\n", f->name, status);
+
+	if (f->descriptors)
+		usb_free_descriptors(f->descriptors);
+
+	if (mbim->not_port.notify_req) {
+		kfree(mbim->not_port.notify_req->buf);
+		usb_ep_free_request(mbim->not_port.notify,
+				    mbim->not_port.notify_req);
+	}
+
+	/* we might as well release our claims on endpoints */
+	if (mbim->not_port.notify)
+		mbim->not_port.notify->driver_data = NULL;
+	if (mbim->bam_port.out)
+		mbim->bam_port.out->driver_data = NULL;
+	if (mbim->bam_port.in)
+		mbim->bam_port.in->driver_data = NULL;
+
+	return status;
+}
+
+static void mbim_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_mbim	*mbim = func_to_mbim(f);
+
+	if (gadget_is_dualspeed(c->cdev->gadget))
+		usb_free_descriptors(f->hs_descriptors);
+	usb_free_descriptors(f->descriptors);
+
+	kfree(mbim->not_port.notify_req->buf);
+	usb_ep_free_request(mbim->not_port.notify, mbim->not_port.notify_req);
+}
+
+/**
+ * mbim_bind_config - add MBIM link to a configuration
+ * @c: the configuration to support the network link
+ * Context: single threaded during gadget setup
+ * Returns zero on success, else negative errno.
+ */
+int mbim_bind_config(struct usb_configuration *c, unsigned portno)
+{
+	struct f_mbim	*mbim = NULL;
+	int status = 0;
+
+	pr_info("port number %u", portno);
+
+	if (portno >= nr_mbim_ports) {
+		pr_err("Can not add port %u. Max ports = %d",
+		       portno, nr_mbim_ports);
+		return -ENODEV;
+	}
+
+	status = mbim_bam_setup(nr_mbim_ports);
+	if (status) {
+		pr_err("bam setup failed");
+		return status;
+	}
+
+	/* maybe allocate device-global string IDs */
+	if (mbim_string_defs[0].id == 0) {
+
+		/* control interface label */
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			return status;
+		mbim_string_defs[STRING_CTRL_IDX].id = status;
+		mbim_control_intf.iInterface = status;
+
+		/* data interface label */
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			return status;
+		mbim_string_defs[STRING_DATA_IDX].id = status;
+		mbim_data_nop_intf.iInterface = status;
+		mbim_data_intf.iInterface = status;
+	}
+
+	/* allocate and initialize one new instance */
+	mbim = mbim_ports[0].port;
+	if (!mbim) {
+		pr_info("mbim struct not allocated");
+		return -ENOMEM;
+	}
+
+	mbim->cdev = c->cdev;
+
+	spin_lock_init(&mbim->lock);
+
+	mbim_reset_values(mbim);
+
+	mbim->function.name = "usb_mbim";
+	mbim->function.strings = mbim_strings;
+	mbim->function.bind = mbim_bind;
+	mbim->function.unbind = mbim_unbind;
+	mbim->function.set_alt = mbim_set_alt;
+	mbim->function.get_alt = mbim_get_alt;
+	mbim->function.setup = mbim_setup;
+	mbim->function.disable = mbim_disable;
+
+	INIT_LIST_HEAD(&mbim->cpkt_req_q);
+	INIT_LIST_HEAD(&mbim->cpkt_resp_q);
+
+	status = usb_add_function(c, &mbim->function);
+
+	pr_info("Exit status %d", status);
+
+	return status;
+}
+
+/* ------------ MBIM DRIVER File Operations API for USER SPACE ------------ */
+
+static ssize_t
+mbim_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
+{
+	struct f_mbim *dev = fp->private_data;
+	struct ctrl_pkt *cpkt = NULL;
+	int ret = 0;
+
+	pr_debug("Enter(%d)\n", count);
+
+	if (!dev) {
+		pr_err("Received NULL mbim pointer\n");
+		return -ENODEV;
+	}
+
+	if (count > MBIM_BULK_BUFFER_SIZE) {
+		pr_err("Buffer size is too big %d, should be at most %d\n",
+			count, MBIM_BULK_BUFFER_SIZE);
+		return -EINVAL;
+	}
+
+	if (mbim_lock(&dev->read_excl)) {
+		pr_err("Previous reading is not finished yet\n");
+		return -EBUSY;
+	}
+
+	/* block until mbim online */
+	while (!(atomic_read(&dev->online) || atomic_read(&dev->error))) {
+		pr_err("USB cable not connected. Wait.\n");
+		ret = wait_event_interruptible(dev->read_wq,
+			(atomic_read(&dev->online) ||
+			atomic_read(&dev->error)));
+		if (ret < 0) {
+			mbim_unlock(&dev->read_excl);
+			return 0;
+		}
+	}
+
+	if (atomic_read(&dev->error)) {
+		mbim_unlock(&dev->read_excl);
+		return -EIO;
+	}
+
+	while (list_empty(&dev->cpkt_req_q)) {
+		pr_err("Requests list is empty. Wait.\n");
+		ret = wait_event_interruptible(dev->read_wq,
+			!list_empty(&dev->cpkt_req_q));
+		if (ret < 0) {
+			pr_err("Waiting failed\n");
+			mbim_unlock(&dev->read_excl);
+			return 0;
+		}
+		pr_debug("Received request packet\n");
+	}
+
+	cpkt = list_first_entry(&dev->cpkt_req_q, struct ctrl_pkt,
+							list);
+	if (cpkt->len > count) {
+		mbim_unlock(&dev->read_excl);
+		pr_err("cpkt size too big:%d > buf size:%d\n",
+				cpkt->len, count);
+		return -ENOMEM;
+	}
+
+	pr_debug("cpkt size:%d\n", cpkt->len);
+
+	list_del(&cpkt->list);
+	mbim_unlock(&dev->read_excl);
+
+	ret = copy_to_user(buf, cpkt->buf, cpkt->len);
+	if (ret) {
+		pr_err("copy_to_user failed: err %d\n", ret);
+		ret = 0;
+	} else {
+		pr_debug("copied %d bytes to user\n", cpkt->len);
+		ret = cpkt->len;
+	}
+
+	mbim_free_ctrl_pkt(cpkt);
+
+	return ret;
+}
+
+static ssize_t
+mbim_write(struct file *fp, const char __user *buf, size_t count, loff_t *pos)
+{
+	struct f_mbim *dev = fp->private_data;
+	struct ctrl_pkt *cpkt = NULL;
+	int ret = 0;
+
+	pr_debug("Enter(%d)", count);
+
+	if (!dev) {
+		pr_err("Received NULL mbim pointer\n");
+		return -ENODEV;
+	}
+
+	if (!count) {
+		pr_err("zero length ctrl pkt\n");
+		return -ENODEV;
+	}
+
+	if (count > MAX_CTRL_PKT_SIZE) {
+		pr_err("given pkt size too big:%d > max_pkt_size:%d\n",
+				count, MAX_CTRL_PKT_SIZE);
+		return -ENOMEM;
+	}
+
+	if (mbim_lock(&dev->write_excl)) {
+		pr_err("Previous writing not finished yet\n");
+		return -EBUSY;
+	}
+
+	if (!atomic_read(&dev->online)) {
+		pr_err("USB cable not connected\n");
+		mbim_unlock(&dev->write_excl);
+		return -EPIPE;
+	}
+
+	cpkt = mbim_alloc_ctrl_pkt(count, GFP_KERNEL);
+	if (!cpkt) {
+		pr_err("failed to allocate ctrl pkt\n");
+		mbim_unlock(&dev->write_excl);
+		return -ENOMEM;
+	}
+
+	ret = copy_from_user(cpkt->buf, buf, count);
+	if (ret) {
+		pr_err("copy_from_user failed err:%d\n", ret);
+		mbim_free_ctrl_pkt(cpkt);
+		mbim_unlock(&dev->write_excl);
+		return 0;
+	}
+
+	fmbim_send_cpkt_response(dev, cpkt);
+
+	mbim_unlock(&dev->write_excl);
+
+	pr_debug("Exit(%d)", count);
+
+	return count;
+}
+
+static int mbim_open(struct inode *ip, struct file *fp)
+{
+	pr_info("Open mbim driver\n");
+
+	while (!_mbim_dev) {
+		pr_err("mbim_dev not created yet\n");
+		return -ENODEV;
+	}
+
+	if (mbim_lock(&_mbim_dev->open_excl)) {
+		pr_err("Already opened\n");
+		return -EBUSY;
+	}
+
+	pr_info("Lock mbim_dev->open_excl for open\n");
+
+	if (!atomic_read(&_mbim_dev->online))
+		pr_err("USB cable not connected\n");
+
+	pr_info("Set QBI pid %d\n", pid_nr(task_pid(current)));
+	_mbim_dev->user_pid = pid_nr(task_pid(current));
+
+	fp->private_data = _mbim_dev;
+
+	atomic_set(&_mbim_dev->error, 0);
+
+	spin_lock(&_mbim_dev->lock);
+	_mbim_dev->is_open = true;
+	mbim_notify(_mbim_dev);
+	spin_unlock(&_mbim_dev->lock);
+
+	pr_info("Exit, mbim file opened\n");
+
+	return 0;
+}
+
+static int mbim_release(struct inode *ip, struct file *fp)
+{
+	struct f_mbim *mbim = fp->private_data;
+
+	pr_info("Close mbim file");
+
+	spin_lock(&mbim->lock);
+	mbim->is_open = false;
+	mbim_notify(mbim);
+	spin_unlock(&mbim->lock);
+
+	mbim->user_pid = 0;
+
+	mbim_unlock(&_mbim_dev->open_excl);
+
+	return 0;
+}
+
+static long mbim_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
+{
+	struct f_mbim *mbim = fp->private_data;
+	int ret = 0;
+
+	pr_info("Received command %d", cmd);
+
+	if (mbim_lock(&mbim->ioctl_excl))
+		return -EBUSY;
+
+	switch (cmd) {
+	case MBIM_GET_NTB_SIZE:
+		ret = copy_to_user((void __user *)arg,
+			&mbim->ntb_input_size, sizeof(mbim->ntb_input_size));
+		if (ret) {
+			pr_err("copying to user space failed");
+			ret = -EFAULT;
+		}
+		pr_info("Sent NTB size %d", mbim->ntb_input_size);
+		break;
+	case MBIM_GET_DATAGRAM_COUNT:
+		ret = copy_to_user((void __user *)arg,
+			&mbim->ntb_max_datagrams,
+			sizeof(mbim->ntb_max_datagrams));
+		if (ret) {
+			pr_err("copying to user space failed");
+			ret = -EFAULT;
+		}
+		pr_info("Sent NTB datagrams count %d",
+			mbim->ntb_max_datagrams);
+		break;
+	default:
+		pr_err("wrong parameter");
+		ret = -EINVAL;
+	}
+
+	mbim_unlock(&mbim->ioctl_excl);
+
+	return ret;
+}
+
+/* file operations for MBIM device /dev/android_mbim */
+static const struct file_operations mbim_fops = {
+	.owner = THIS_MODULE,
+	.open = mbim_open,
+	.release = mbim_release,
+	.read = mbim_read,
+	.write = mbim_write,
+	.unlocked_ioctl	= mbim_ioctl,
+};
+
+static struct miscdevice mbim_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "android_mbim",
+	.fops = &mbim_fops,
+};
+
+static int mbim_init(int instances)
+{
+	int i;
+	struct f_mbim *dev = NULL;
+	int ret;
+
+	pr_info("initialize %d instances\n", instances);
+
+	if (instances > NR_MBIM_PORTS) {
+		pr_err("Max-%d instances supported\n", NR_MBIM_PORTS);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < instances; i++) {
+		dev = kzalloc(sizeof(struct f_mbim), GFP_KERNEL);
+		if (!dev) {
+			pr_err("Failed to allocate mbim dev\n");
+			ret = -ENOMEM;
+			goto fail_probe;
+		}
+
+		dev->port_num = i;
+		spin_lock_init(&dev->lock);
+		INIT_LIST_HEAD(&dev->cpkt_req_q);
+		INIT_LIST_HEAD(&dev->cpkt_resp_q);
+
+		mbim_ports[i].port = dev;
+		mbim_ports[i].port_num = i;
+
+		init_waitqueue_head(&dev->read_wq);
+		init_waitqueue_head(&dev->write_wq);
+
+		atomic_set(&dev->open_excl, 0);
+		atomic_set(&dev->ioctl_excl, 0);
+		atomic_set(&dev->read_excl, 0);
+		atomic_set(&dev->write_excl, 0);
+
+		nr_mbim_ports++;
+
+	}
+
+	_mbim_dev = dev;
+	ret = misc_register(&mbim_device);
+	if (ret) {
+		pr_err("mbim driver failed to register");
+		goto fail_probe;
+	}
+
+	pr_info("Initialized %d ports\n", nr_mbim_ports);
+
+	return ret;
+
+fail_probe:
+	pr_err("Failed");
+	for (i = 0; i < nr_mbim_ports; i++) {
+		kfree(mbim_ports[i].port);
+		mbim_ports[i].port = NULL;
+	}
+
+	return ret;
+}
+
+static void fmbim_cleanup(void)
+{
+	int i = 0;
+
+	pr_info("Enter");
+
+	for (i = 0; i < nr_mbim_ports; i++) {
+		kfree(mbim_ports[i].port);
+		mbim_ports[i].port = NULL;
+	}
+	nr_mbim_ports = 0;
+
+	misc_deregister(&mbim_device);
+
+	_mbim_dev = NULL;
+}
+
diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c
new file mode 100644
index 0000000..73b4e75
--- /dev/null
+++ b/drivers/usb/gadget/u_bam_data.c
@@ -0,0 +1,328 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/bitops.h>
+#include <linux/usb/gadget.h>
+
+#include <mach/bam_dmux.h>
+#include <mach/usb_gadget_xport.h>
+#include <mach/usb_bam.h>
+
+#define BAM2BAM_DATA_N_PORTS	1
+
+static struct workqueue_struct *bam_data_wq;
+static int n_bam2bam_data_ports;
+
+#define SPS_PARAMS_SPS_MODE		BIT(5)
+#define SPS_PARAMS_TBE		        BIT(6)
+#define MSM_VENDOR_ID			BIT(16)
+
+struct data_port {
+	struct usb_function		func;
+	struct usb_ep			*in;
+	struct usb_ep			*out;
+};
+
+struct bam_data_ch_info {
+	unsigned long		flags;
+	unsigned		id;
+
+	struct bam_data_port	*port;
+	struct work_struct	write_tobam_w;
+
+	struct usb_request	*rx_req;
+	struct usb_request	*tx_req;
+
+	u8			src_pipe_idx;
+	u8			dst_pipe_idx;
+	u8			connection_idx;
+};
+
+struct bam_data_port {
+	unsigned			port_num;
+	struct data_port		*port_usb;
+	struct bam_data_ch_info		data_ch;
+
+	struct work_struct		connect_w;
+	struct work_struct		disconnect_w;
+};
+
+struct bam_data_port *bam2bam_data_ports[BAM2BAM_DATA_N_PORTS];
+
+/*------------data_path----------------------------*/
+
+static void bam_data_endless_rx_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_info("status: %d\n", status);
+}
+
+static void bam_data_endless_tx_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_info("status: %d\n", status);
+}
+
+static void bam_data_start_endless_rx(struct bam_data_port *port)
+{
+	struct bam_data_ch_info *d = &port->data_ch;
+	int status;
+
+	status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
+	if (status)
+		pr_err("error enqueuing transfer, %d\n", status);
+}
+
+static void bam_data_start_endless_tx(struct bam_data_port *port)
+{
+	struct bam_data_ch_info *d = &port->data_ch;
+	int status;
+
+	status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
+	if (status)
+		pr_err("error enqueuing transfer, %d\n", status);
+}
+
+static void bam2bam_data_disconnect_work(struct work_struct *w)
+{
+	struct bam_data_port *port =
+			container_of(w, struct bam_data_port, disconnect_w);
+
+	pr_info("Enter");
+
+	/* disable endpoints */
+	if (!port->port_usb || !port->port_usb->out || !port->port_usb->in) {
+		pr_err("port_usb->out/in == NULL. Exit");
+		return;
+	}
+	usb_ep_disable(port->port_usb->out);
+	usb_ep_disable(port->port_usb->in);
+
+	port->port_usb->in->driver_data = NULL;
+	port->port_usb->out->driver_data = NULL;
+
+	port->port_usb = 0;
+
+	pr_info("Exit");
+}
+
+static void bam2bam_data_connect_work(struct work_struct *w)
+{
+	struct bam_data_port *port = container_of(w, struct bam_data_port,
+						  connect_w);
+	struct bam_data_ch_info *d = &port->data_ch;
+	u32 sps_params;
+	int ret;
+
+	pr_info("Enter");
+
+	ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
+						  &d->dst_pipe_idx);
+	d->src_pipe_idx = 11;
+	d->dst_pipe_idx = 10;
+
+	if (ret) {
+		pr_err("usb_bam_connect failed: err:%d\n", ret);
+		return;
+	}
+
+	if (!port->port_usb) {
+		pr_err("port_usb is NULL");
+		return;
+	}
+
+	if (!port->port_usb->out) {
+		pr_err("port_usb->out (bulk out ep) is NULL");
+		return;
+	}
+
+	d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
+	if (!d->rx_req)
+		return;
+
+	d->rx_req->context = port;
+	d->rx_req->complete = bam_data_endless_rx_complete;
+	d->rx_req->length = 0;
+	sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx |
+				 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
+	d->rx_req->udc_priv = sps_params;
+	d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
+	if (!d->tx_req)
+		return;
+
+	d->tx_req->context = port;
+	d->tx_req->complete = bam_data_endless_tx_complete;
+	d->tx_req->length = 0;
+	sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx |
+				 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
+	d->tx_req->udc_priv = sps_params;
+
+	/* queue in & out requests */
+	bam_data_start_endless_rx(port);
+	bam_data_start_endless_tx(port);
+
+	pr_info("Done\n");
+}
+
+static void bam2bam_data_port_free(int portno)
+{
+	kfree(bam2bam_data_ports[portno]);
+	bam2bam_data_ports[portno] = NULL;
+}
+
+static int bam2bam_data_port_alloc(int portno)
+{
+	struct bam_data_port	*port = NULL;
+	struct bam_data_ch_info	*d = NULL;
+
+	port = kzalloc(sizeof(struct bam_data_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+
+	INIT_WORK(&port->connect_w, bam2bam_data_connect_work);
+	INIT_WORK(&port->disconnect_w, bam2bam_data_disconnect_work);
+
+	/* data ch */
+	d = &port->data_ch;
+	d->port = port;
+	bam2bam_data_ports[portno] = port;
+
+	pr_info("port:%p portno:%d\n", port, portno);
+
+	return 0;
+}
+
+void bam_data_disconnect(struct data_port *gr, u8 port_num)
+{
+	struct bam_data_port	*port;
+	struct bam_data_ch_info	*d;
+
+	pr_info("dev:%p port#%d\n", gr, port_num);
+
+	if (port_num >= n_bam2bam_data_ports) {
+		pr_err("invalid bam2bam portno#%d\n", port_num);
+		return;
+	}
+
+	if (!gr) {
+		pr_err("mbim data port is null\n");
+		return;
+	}
+
+	port = bam2bam_data_ports[port_num];
+
+	d = &port->data_ch;
+	port->port_usb = gr;
+
+	queue_work(bam_data_wq, &port->disconnect_w);
+}
+
+int bam_data_connect(struct data_port *gr, u8 port_num,
+				 u8 connection_idx)
+{
+	struct bam_data_port	*port;
+	struct bam_data_ch_info	*d;
+	int			ret;
+
+	pr_info("dev:%p port#%d\n", gr, port_num);
+
+	if (port_num >= n_bam2bam_data_ports) {
+		pr_err("invalid portno#%d\n", port_num);
+		return -ENODEV;
+	}
+
+	if (!gr) {
+		pr_err("mbim data port is null\n");
+		return -ENODEV;
+	}
+
+	port = bam2bam_data_ports[port_num];
+
+	d = &port->data_ch;
+
+	ret = usb_ep_enable(gr->in);
+	if (ret) {
+		pr_err("usb_ep_enable failed eptype:IN ep:%p", gr->in);
+		return ret;
+	}
+	gr->in->driver_data = port;
+
+	ret = usb_ep_enable(gr->out);
+	if (ret) {
+		pr_err("usb_ep_enable failed eptype:OUT ep:%p", gr->out);
+		gr->in->driver_data = 0;
+		return ret;
+	}
+	gr->out->driver_data = port;
+
+	port->port_usb = gr;
+
+	d->connection_idx = connection_idx;
+
+	queue_work(bam_data_wq, &port->connect_w);
+
+	return 0;
+}
+
+int bam_data_setup(unsigned int no_bam2bam_port)
+{
+	int	i;
+	int	ret;
+
+	pr_info("requested %d BAM2BAM ports", no_bam2bam_port);
+
+	if (!no_bam2bam_port || no_bam2bam_port > BAM2BAM_DATA_N_PORTS) {
+		pr_err("Invalid num of ports count:%d\n", no_bam2bam_port);
+		return -EINVAL;
+	}
+
+	bam_data_wq = alloc_workqueue("k_bam_data",
+				      WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!bam_data_wq) {
+		pr_err("Failed to create workqueue\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < no_bam2bam_port; i++) {
+		n_bam2bam_data_ports++;
+		ret = bam2bam_data_port_alloc(i);
+		if (ret) {
+			n_bam2bam_data_ports--;
+			pr_err("Failed to alloc port:%d\n", i);
+			goto free_bam_ports;
+		}
+	}
+
+	return 0;
+
+free_bam_ports:
+	for (i = 0; i < n_bam2bam_data_ports; i++)
+		bam2bam_data_port_free(i);
+	destroy_workqueue(bam_data_wq);
+
+	return ret;
+}
+
diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h
index 81a9279..2b39f69 100644
--- a/include/linux/usb/cdc.h
+++ b/include/linux/usb/cdc.h
@@ -53,6 +53,7 @@
 #define USB_CDC_DMM_TYPE		0x14
 #define USB_CDC_OBEX_TYPE		0x15
 #define USB_CDC_NCM_TYPE		0x1a
+#define USB_CDC_MBB_TYPE		0x1b	/* mbb_desc */
 
 /* "Header Functional Descriptor" from CDC spec  5.2.3.1 */
 struct usb_cdc_header_desc {
@@ -187,6 +188,21 @@
 	__le16	bcdNcmVersion;
 	__u8	bmNetworkCapabilities;
 } __attribute__ ((packed));
+
+/* "MBIM Functional Descriptor" */
+struct usb_cdc_mbb_desc {
+	__u8	bLength;
+	__u8	bDescriptorType;
+	__u8	bDescriptorSubType;
+
+	__le16	bcdMbbVersion;
+	__le16	wMaxControlMessage;
+	__u8	bNumberFilters;
+	__u8	bMaxFilterSize;
+	__le16	wMaxSegmentSize;
+	__u8	bmNetworkCapabilities;
+} __packed;
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -201,6 +217,7 @@
 
 #define USB_CDC_SEND_ENCAPSULATED_COMMAND	0x00
 #define USB_CDC_GET_ENCAPSULATED_RESPONSE	0x01
+#define USB_CDC_RESET_FUNCTION			0x05
 #define USB_CDC_REQ_SET_LINE_CODING		0x20
 #define USB_CDC_REQ_GET_LINE_CODING		0x21
 #define USB_CDC_REQ_SET_CONTROL_LINE_STATE	0x22