usb: gadget: Add IPA support to mbim

MBIM function on 9x25 should work in IPA mode instead
of BAM to BAM mode. This commit adds this support.

Change-Id: Ief597d4a4db13b995caa9af9d2cb4a62211cd740
Signed-off-by: Lena Salman <esalman@codeaurora.org>
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 5b97148..d4bdf99 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -77,8 +77,8 @@
 #include "f_rndis.c"
 #include "rndis.c"
 #include "f_qc_ecm.c"
-#include "u_bam_data.c"
 #include "f_mbim.c"
+#include "u_bam_data.c"
 #include "f_ecm.c"
 #include "f_qc_rndis.c"
 #include "u_ether.c"
@@ -865,10 +865,18 @@
 	fmbim_cleanup();
 }
 
+
+/* mbim transport string */
+static char mbim_transports[MAX_XPORT_STR_LEN];
+
 static int mbim_function_bind_config(struct android_usb_function *f,
 					  struct usb_configuration *c)
 {
-	return mbim_bind_config(c, 0);
+	char *trans;
+
+	pr_debug("%s: mbim transport is %s", __func__, mbim_transports);
+	trans = strim(mbim_transports);
+	return mbim_bind_config(c, 0, trans);
 }
 
 static int mbim_function_ctrlrequest(struct android_usb_function *f,
@@ -878,12 +886,34 @@
 	return mbim_ctrlrequest(cdev, c);
 }
 
+static ssize_t mbim_transports_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", mbim_transports);
+}
+
+static ssize_t mbim_transports_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	strlcpy(mbim_transports, buf, sizeof(mbim_transports));
+	return size;
+}
+
+static DEVICE_ATTR(mbim_transports, S_IRUGO | S_IWUSR, mbim_transports_show,
+				   mbim_transports_store);
+
+static struct device_attribute *mbim_function_attributes[] = {
+	&dev_attr_mbim_transports,
+	NULL
+};
+
 static struct android_usb_function mbim_function = {
 	.name		= "usb_mbim",
 	.cleanup	= mbim_function_cleanup,
 	.bind_config	= mbim_function_bind_config,
 	.init		= mbim_function_init,
 	.ctrlrequest	= mbim_function_ctrlrequest,
+	.attributes		= mbim_function_attributes,
 };
 
 #ifdef CONFIG_SND_PCM
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
index 893f315..52e3126 100644
--- a/drivers/usb/gadget/f_mbim.c
+++ b/drivers/usb/gadget/f_mbim.c
@@ -84,6 +84,7 @@
 	wait_queue_head_t read_wq;
 	wait_queue_head_t write_wq;
 
+	enum transport_type		xport;
 	u8				port_num;
 	struct data_port		bam_port;
 	struct mbim_notify_port		not_port;
@@ -143,6 +144,9 @@
 #define NTB_OUT_SIZE		(0x1000)
 #define NDP_IN_DIVISOR		(0x4)
 
+#define NTB_DEFAULT_IN_SIZE_IPA	(0x2000)
+#define NTB_OUT_SIZE_IPA		(0x2000)
+
 #define FORMATS_SUPPORTED	USB_CDC_NCM_NTB16_SUPPORTED
 
 static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
@@ -659,26 +663,49 @@
 	return 0;
 }
 
+int mbim_configure_params(void)
+{
+	struct teth_aggr_params aggr_params;
+	int ret = 0;
+
+	aggr_params.dl.aggr_prot = TETH_AGGR_PROTOCOL_MBIM;
+	aggr_params.dl.max_datagrams = ntb_parameters.wNtbOutMaxDatagrams;
+	aggr_params.dl.max_transfer_size_byte = ntb_parameters.dwNtbInMaxSize;
+
+	aggr_params.ul.aggr_prot = TETH_AGGR_PROTOCOL_MBIM;
+	aggr_params.ul.max_datagrams = ntb_parameters.wNtbOutMaxDatagrams;
+	aggr_params.ul.max_transfer_size_byte = ntb_parameters.dwNtbOutMaxSize;
+
+	ret = teth_bridge_set_aggr_params(&aggr_params);
+	if (ret)
+		pr_err("%s: teth_bridge_set_aggr_params failed\n", __func__);
+
+	return ret;
+}
+
 static int mbim_bam_connect(struct f_mbim *dev)
 {
 	int ret;
 	u8 src_connection_idx, dst_connection_idx;
 	struct usb_gadget *gadget = dev->cdev->gadget;
+	enum peer_bam bam_name = (dev->xport == USB_GADGET_XPORT_BAM2BAM_IPA) ?
+							IPA_P_BAM : A2_P_BAM;
 
 	pr_info("dev:%p portno:%d\n", dev, dev->port_num);
 
-	src_connection_idx = usb_bam_get_connection_idx(gadget->name, A2_P_BAM,
-		USB_TO_PEER_PERIPHERAL, dev->port_num);
-	dst_connection_idx = usb_bam_get_connection_idx(gadget->name, A2_P_BAM,
-		PEER_PERIPHERAL_TO_USB, dev->port_num);
+	src_connection_idx = usb_bam_get_connection_idx(gadget->name, bam_name,
+					USB_TO_PEER_PERIPHERAL, dev->port_num);
+	dst_connection_idx = usb_bam_get_connection_idx(gadget->name, bam_name,
+					PEER_PERIPHERAL_TO_USB, dev->port_num);
 	if (src_connection_idx < 0 || dst_connection_idx < 0) {
 		pr_err("%s: usb_bam_get_connection_idx failed\n", __func__);
 		return ret;
 	}
 
 	ret = bam_data_connect(&dev->bam_port, dev->port_num,
-		USB_GADGET_XPORT_BAM2BAM, src_connection_idx,
-		dst_connection_idx, USB_FUNC_MBIM);
+		dev->xport, src_connection_idx, dst_connection_idx,
+		USB_FUNC_MBIM);
+
 	if (ret) {
 		pr_err("bam_data_setup failed: err:%d\n",
 				ret);
@@ -1603,7 +1630,8 @@
  * Context: single threaded during gadget setup
  * Returns zero on success, else negative errno.
  */
-int mbim_bind_config(struct usb_configuration *c, unsigned portno)
+int mbim_bind_config(struct usb_configuration *c, unsigned portno,
+					 char *xport_name)
 {
 	struct f_mbim	*mbim = NULL;
 	int status = 0;
@@ -1662,6 +1690,19 @@
 	mbim->function.disable = mbim_disable;
 	mbim->function.suspend = mbim_suspend;
 	mbim->function.resume = mbim_resume;
+	mbim->xport = str_to_xport(xport_name);
+
+	if (mbim->xport != USB_GADGET_XPORT_BAM2BAM_IPA) {
+		/* Use BAM2BAM by default if not IPA */
+		mbim->xport = USB_GADGET_XPORT_BAM2BAM;
+	} else  {
+		/* For IPA we use limit of 16 */
+		ntb_parameters.wNtbOutMaxDatagrams = 16;
+		/* For IPA this is proven to give maximum throughput */
+		ntb_parameters.dwNtbInMaxSize =
+		cpu_to_le32(NTB_DEFAULT_IN_SIZE_IPA);
+		ntb_parameters.dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE_IPA);
+	}
 
 	INIT_LIST_HEAD(&mbim->cpkt_req_q);
 	INIT_LIST_HEAD(&mbim->cpkt_resp_q);
diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c
index 83f885a..eec9e37 100644
--- a/drivers/usb/gadget/u_bam_data.c
+++ b/drivers/usb/gadget/u_bam_data.c
@@ -183,6 +183,8 @@
 	int ret;
 
 	if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+		if (d->func_type == USB_FUNC_MBIM)
+			teth_bridge_disconnect();
 		if (d->func_type == USB_FUNC_ECM)
 			ecm_ipa_disconnect(d->ipa_params.priv);
 		ret = usb_bam_disconnect_ipa(&d->ipa_params);
@@ -195,13 +197,28 @@
 {
 	struct bam_data_port *port = container_of(w, struct bam_data_port,
 						  connect_w);
+	struct teth_bridge_connect_params connect_params;
 	struct bam_data_ch_info *d = &port->data_ch;
+	ipa_notify_cb usb_notify_cb;
+	void *priv;
 	u32 sps_params;
 	int ret;
 
 	pr_debug("%s: Connect workqueue started", __func__);
 
 	if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+		if (d->func_type == USB_FUNC_MBIM) {
+			ret = teth_bridge_init(&usb_notify_cb, &priv);
+			if (ret) {
+				pr_err("%s:teth_bridge_init() failed\n",
+				      __func__);
+				return;
+			}
+			d->ipa_params.notify = usb_notify_cb;
+			d->ipa_params.priv = priv;
+			d->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
+		}
+
 		d->ipa_params.client = IPA_CLIENT_USB_CONS;
 		d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
 		if (d->func_type == USB_FUNC_ECM) {
@@ -227,6 +244,23 @@
 				__func__, ret);
 			return;
 		}
+
+		if (d->func_type == USB_FUNC_MBIM) {
+			connect_params.ipa_usb_pipe_hdl =
+				d->ipa_params.prod_clnt_hdl;
+			connect_params.usb_ipa_pipe_hdl =
+				d->ipa_params.cons_clnt_hdl;
+			connect_params.tethering_mode =
+				TETH_TETHERING_MODE_MBIM;
+			ret = teth_bridge_connect(&connect_params);
+			if (ret) {
+				pr_err("%s:teth_bridge_connect() failed\n",
+				      __func__);
+				return;
+			}
+			mbim_configure_params();
+		}
+
 		if (d->func_type == USB_FUNC_ECM) {
 			ret = ecm_ipa_connect(d->ipa_params.cons_clnt_hdl,
 				d->ipa_params.prod_clnt_hdl,
@@ -417,6 +451,7 @@
 	d->dst_connection_idx = dst_connection_idx;
 
 	d->trans = trans;
+	d->func_type = func;
 
 	if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
 		d->ipa_params.src_pipe = &(d->src_pipe_idx);
@@ -425,8 +460,6 @@
 		d->ipa_params.dst_idx = dst_connection_idx;
 	}
 
-	d->func_type = func;
-
 	queue_work(bam_data_wq, &port->connect_w);
 
 	return 0;