usb: gadget: Add SPS BAM-to-BAM support

Adding new support to enable USB-Peripheral
BAM-to-BAM transactions.
Changes were added to the UDC to support
"legacy/regular" USB transfers and
endless BAM-to-BAM transfers.

To avoid adding new vendor specific UDC file
to support the new feature and keep the UDC
as a generic as possiblea, USB request holds
a vendor specific data that distinguish between
the two transfer types.

USB BAM will be added in seperate commit.

Change-Id: I3211a122fe5236cda2dbe844b44f52a2b2063baf
Signed-off-by: Ofir Cohen <ofirc@codeaurora.org>
Signed-off-by: Amit Blay <ablay@codeaurora.org>
diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
index 54566dc..d8a3e60 100644
--- a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
+++ b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
@@ -20,6 +20,7 @@
 	USB_GADGET_XPORT_SDIO,
 	USB_GADGET_XPORT_SMD,
 	USB_GADGET_XPORT_BAM,
+	USB_GADGET_XPORT_BAM2BAM,
 	USB_GADGET_XPORT_HSIC,
 	USB_GADGET_XPORT_NONE,
 };
@@ -37,6 +38,8 @@
 		return "SMD";
 	case USB_GADGET_XPORT_BAM:
 		return "BAM";
+	case USB_GADGET_XPORT_BAM2BAM:
+		return "BAM2BAM";
 	case USB_GADGET_XPORT_HSIC:
 		return "HSIC";
 	case USB_GADGET_XPORT_NONE:
@@ -56,6 +59,8 @@
 		return USB_GADGET_XPORT_SMD;
 	if (!strncasecmp("BAM", name, XPORT_STR_LEN))
 		return USB_GADGET_XPORT_BAM;
+	if (!strncasecmp("BAM2BAM", name, XPORT_STR_LEN))
+		return USB_GADGET_XPORT_BAM2BAM;
 	if (!strncasecmp("HSIC", name, XPORT_STR_LEN))
 		return USB_GADGET_XPORT_HSIC;
 	if (!strncasecmp("", name, XPORT_STR_LEN))
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index e084278..6282389 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -155,6 +155,7 @@
 #define CAP_ENDPTLISTADDR   (0x018UL)
 #define CAP_PORTSC          (0x044UL)
 #define CAP_DEVLC           (0x084UL)
+#define CAP_ENDPTPIPEID     (0x0BCUL)
 #define CAP_USBMODE         (hw_bank.lpm ? 0x0C8UL : 0x068UL)
 #define CAP_ENDPTSETUPSTAT  (hw_bank.lpm ? 0x0D8UL : 0x06CUL)
 #define CAP_ENDPTPRIME      (hw_bank.lpm ? 0x0DCUL : 0x070UL)
@@ -1638,6 +1639,23 @@
 		if (!mReq->req.no_interrupt)
 			mReq->ptr->token  |= TD_IOC;
 	}
+
+	/* MSM Specific: updating the request as required for
+	 * SPS mode. Enable MSM proprietary DMA engine acording
+	 * to the UDC private data in the request.
+	 */
+	if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+		if (mReq->req.udc_priv & MSM_SPS_MODE) {
+			mReq->ptr->token = TD_STATUS_ACTIVE;
+			if (mReq->req.udc_priv & MSM_TBE)
+				mReq->ptr->next = TD_TERMINATE;
+			else
+				mReq->ptr->next = MSM_ETD_TYPE | mReq->dma;
+			if (!mReq->req.no_interrupt)
+				mReq->ptr->token |= MSM_ETD_IOC;
+		}
+	}
+
 	mReq->ptr->page[0]  = mReq->req.dma;
 	for (i = 1; i < 5; i++)
 		mReq->ptr->page[i] =
@@ -1680,6 +1698,39 @@
 	}
 
 	mEp->qh.ptr->td.next   = mReq->dma;    /* TERMINATE = 0 */
+
+	if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+		if (mReq->req.udc_priv & MSM_SPS_MODE) {
+			mEp->qh.ptr->td.next   |= MSM_ETD_TYPE;
+			i = hw_cread(CAP_ENDPTPIPEID +
+						 mEp->num * sizeof(u32), ~0);
+			/* Read current value of this EPs pipe id */
+			i = (mEp->dir == TX) ?
+				((i >> MSM_TX_PIPE_ID_OFS) & MSM_PIPE_ID_MASK) :
+					(i & MSM_PIPE_ID_MASK);
+			/* If requested pipe id is different from current,
+			   then write it */
+			if (i != (mReq->req.udc_priv & MSM_PIPE_ID_MASK)) {
+				if (mEp->dir == TX)
+					hw_cwrite(
+						CAP_ENDPTPIPEID +
+							mEp->num * sizeof(u32),
+						MSM_PIPE_ID_MASK <<
+							MSM_TX_PIPE_ID_OFS,
+						(mReq->req.udc_priv &
+						 MSM_PIPE_ID_MASK)
+							<< MSM_TX_PIPE_ID_OFS);
+				else
+					hw_cwrite(
+						CAP_ENDPTPIPEID +
+							mEp->num * sizeof(u32),
+						MSM_PIPE_ID_MASK,
+						mReq->req.udc_priv &
+							MSM_PIPE_ID_MASK);
+			}
+		}
+	}
+
 	mEp->qh.ptr->td.token &= ~TD_STATUS;   /* clear status */
 	mEp->qh.ptr->cap |=  QH_ZLT;
 
@@ -1712,6 +1763,10 @@
 	if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
 		return -EBUSY;
 
+	if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID)
+		if ((mReq->req.udc_priv & MSM_SPS_MODE) &&
+			(mReq->req.udc_priv & MSM_TBE))
+			return -EBUSY;
 	if (mReq->zptr) {
 		if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0)
 			return -EBUSY;
@@ -1756,6 +1811,7 @@
 __acquires(mEp->lock)
 {
 	struct ci13xxx_ep *mEpTemp = mEp;
+	unsigned val;
 
 	trace("%p", mEp);
 
@@ -1771,6 +1827,21 @@
 			list_entry(mEp->qh.queue.next,
 				   struct ci13xxx_req, queue);
 		list_del_init(&mReq->queue);
+
+		/* MSM Specific: Clear end point proprietary register */
+		if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+			if (mReq->req.udc_priv & MSM_SPS_MODE) {
+				val = hw_cread(CAP_ENDPTPIPEID +
+					mEp->num * sizeof(u32),
+					~0);
+
+				if (val != MSM_EP_PIPE_ID_RESET_VAL)
+					hw_cwrite(
+						CAP_ENDPTPIPEID +
+						 mEp->num * sizeof(u32),
+						~0, MSM_EP_PIPE_ID_RESET_VAL);
+			}
+		}
 		mReq->req.status = -ESHUTDOWN;
 
 		if (mReq->req.complete != NULL) {
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index b917fe9..35b0712 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -25,6 +25,21 @@
 #define RX        (0)  /* similar to USB_DIR_OUT but can be used as an index */
 #define TX        (1)  /* similar to USB_DIR_IN  but can be used as an index */
 
+/* UDC private data:
+ *  16MSb - Vendor ID | 16 LSb Vendor private data
+ */
+#define CI13XX_REQ_VENDOR_ID(id)  (id & 0xFFFF0000UL)
+
+/* MSM specific */
+#define MSM_PIPE_ID_MASK         (0x1F)
+#define MSM_TX_PIPE_ID_OFS       (16)
+#define MSM_SPS_MODE             BIT(5)
+#define MSM_TBE                  BIT(6)
+#define MSM_ETD_TYPE             BIT(1)
+#define MSM_ETD_IOC              BIT(9)
+#define MSM_VENDOR_ID            BIT(16)
+#define MSM_EP_PIPE_ID_RESET_VAL 0x1F001F
+
 /******************************************************************************
  * STRUCTURES
  *****************************************************************************/
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 32791d9..cbcf5ac 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -18,10 +18,10 @@
 #include <linux/spinlock.h>
 
 #include <mach/usb_gadget_xport.h>
+
 #include "u_rmnet.h"
 #include "gadget_chips.h"
 
-
 #define RMNET_NOTIFY_INTERVAL	5
 #define RMNET_MAX_NOTIFY_SIZE	sizeof(struct usb_cdc_notification)
 
@@ -66,6 +66,7 @@
 static unsigned int no_ctrl_smd_ports;
 static unsigned int no_ctrl_hsic_ports;
 static unsigned int no_data_bam_ports;
+static unsigned int no_data_bam2bam_ports;
 static unsigned int no_data_hsic_ports;
 static struct rmnet_ports {
 	enum transport_type		data_xport;
@@ -241,13 +242,16 @@
 	int	port_idx;
 	int	i;
 
-	pr_debug("%s: bam ports: %u data hsic ports: %u smd ports: %u"
-			" ctrl hsic ports: %u nr_rmnet_ports: %u\n",
-			__func__, no_data_bam_ports, no_data_hsic_ports,
-			no_ctrl_smd_ports, no_ctrl_hsic_ports, nr_rmnet_ports);
+	pr_debug("%s: bam ports: %u bam2bam ports: %u data hsic ports: %u"
+		" smd ports: %u ctrl hsic ports: %u"
+	" nr_rmnet_ports: %u\n",
+		__func__, no_data_bam_ports, no_data_bam2bam_ports,
+		no_data_hsic_ports, no_ctrl_smd_ports,
+		no_ctrl_hsic_ports, nr_rmnet_ports);
 
-	if (no_data_bam_ports) {
-		ret = gbam_setup(no_data_bam_ports);
+	if (no_data_bam_ports || no_data_bam2bam_ports) {
+		ret = gbam_setup(no_data_bam_ports,
+						 no_data_bam2bam_ports);
 		if (ret)
 			return ret;
 	}
@@ -329,7 +333,11 @@
 	port_num = rmnet_ports[dev->port_num].data_xport_num;
 	switch (dxport) {
 	case USB_GADGET_XPORT_BAM:
-		ret = gbam_connect(&dev->port, port_num);
+	case USB_GADGET_XPORT_BAM2BAM:
+		/* currently only one connection (idx 0)
+		   is supported */
+		ret = gbam_connect(&dev->port, port_num,
+						   dxport, 0);
 		if (ret) {
 			pr_err("%s: gbam_connect failed: err:%d\n",
 					__func__, ret);
@@ -386,7 +394,8 @@
 	port_num = rmnet_ports[dev->port_num].data_xport_num;
 	switch (dxport) {
 	case USB_GADGET_XPORT_BAM:
-		gbam_disconnect(&dev->port, port_num);
+	case USB_GADGET_XPORT_BAM2BAM:
+		gbam_disconnect(&dev->port, port_num, dxport);
 		break;
 	case USB_GADGET_XPORT_HSIC:
 		ghsic_data_disconnect(&dev->port, port_num);
@@ -954,6 +963,7 @@
 	nr_rmnet_ports = 0;
 	no_ctrl_smd_ports = 0;
 	no_data_bam_ports = 0;
+	no_data_bam2bam_ports = 0;
 	no_ctrl_hsic_ports = 0;
 	no_data_hsic_ports = 0;
 }
@@ -1013,6 +1023,10 @@
 		rmnet_port->data_xport_num = no_data_bam_ports;
 		no_data_bam_ports++;
 		break;
+	case USB_GADGET_XPORT_BAM2BAM:
+		rmnet_port->data_xport_num = no_data_bam2bam_ports;
+		no_data_bam2bam_ports++;
+		break;
 	case USB_GADGET_XPORT_HSIC:
 		rmnet_port->data_xport_num = no_data_hsic_ports;
 		no_data_hsic_ports++;
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 70cbd2f..869a541 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -23,12 +23,17 @@
 #include <linux/bitops.h>
 #include <linux/termios.h>
 
+#include <mach/usb_gadget_xport.h>
+#include <mach/usb_bam.h>
+
 #include "u_rmnet.h"
 
 #define BAM_N_PORTS	1
+#define BAM2BAM_N_PORTS	1
 
 static struct workqueue_struct *gbam_wq;
 static int n_bam_ports;
+static int n_bam2bam_ports;
 static unsigned bam_ch_ids[] = { 8 };
 
 static const char *bam_ch_names[] = { "bam_dmux_ch_8" };
@@ -68,6 +73,11 @@
 
 #define BAM_CH_OPENED	BIT(0)
 #define BAM_CH_READY	BIT(1)
+#define SPS_PARAMS_PIPE_ID_MASK		(0x1F)
+#define SPS_PARAMS_SPS_MODE			BIT(5)
+#define SPS_PARAMS_TBE		        BIT(6)
+#define MSM_VENDOR_ID				BIT(16)
+
 struct bam_ch_info {
 	unsigned long		flags;
 	unsigned		id;
@@ -81,6 +91,13 @@
 	struct gbam_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;
+
 	/* stats */
 	unsigned int		pending_with_bam;
 	unsigned int		tohost_drp_cnt;
@@ -96,6 +113,7 @@
 	spinlock_t		port_lock;
 
 	struct grmnet		*port_usb;
+	struct grmnet		*gr;
 
 	struct bam_ch_info	data_ch;
 
@@ -108,7 +126,10 @@
 	struct platform_driver pdrv;
 } bam_ports[BAM_N_PORTS];
 
+struct gbam_port *bam2bam_ports[BAM2BAM_N_PORTS];
 static void gbam_start_rx(struct gbam_port *port);
+static void gbam_start_endless_rx(struct gbam_port *port);
+static void gbam_start_endless_tx(struct gbam_port *port);
 
 /*---------------misc functions---------------- */
 static void gbam_free_requests(struct usb_ep *ep, struct list_head *head)
@@ -414,6 +435,20 @@
 	}
 }
 
+static void gbam_endless_rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_debug("%s status: %d\n", __func__, status);
+}
+
+static void gbam_endless_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	int status = req->status;
+
+	pr_debug("%s status: %d\n", __func__, status);
+}
+
 static void gbam_start_rx(struct gbam_port *port)
 {
 	struct usb_request		*req;
@@ -469,6 +504,26 @@
 	spin_unlock_irqrestore(&port->port_lock, flags);
 }
 
+static void gbam_start_endless_rx(struct gbam_port *port)
+{
+	struct bam_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("%s: error enqueuing transfer, %d\n", __func__, status);
+}
+
+static void gbam_start_endless_tx(struct gbam_port *port)
+{
+	struct bam_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("%s: error enqueuing transfer, %d\n", __func__, status);
+}
+
 static void gbam_start_io(struct gbam_port *port)
 {
 	unsigned long		flags;
@@ -520,6 +575,32 @@
 	}
 }
 
+static void gbam_free_buffers(struct gbam_port *port)
+{
+	struct sk_buff		*skb;
+	unsigned long		flags;
+	struct bam_ch_info	*d;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+
+	if (!port || !port->port_usb)
+		goto free_buf_out;
+
+	d = &port->data_ch;
+
+	gbam_free_requests(port->port_usb->in, &d->tx_idle);
+	gbam_free_requests(port->port_usb->out, &d->rx_idle);
+
+	while ((skb = __skb_dequeue(&d->tx_skb_q)))
+		dev_kfree_skb_any(skb);
+
+	while ((skb = __skb_dequeue(&d->rx_skb_q)))
+		dev_kfree_skb_any(skb);
+
+free_buf_out:
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
 static void gbam_disconnect_work(struct work_struct *w)
 {
 	struct gbam_port *port =
@@ -533,6 +614,22 @@
 	clear_bit(BAM_CH_OPENED, &d->flags);
 }
 
+static void gbam2bam_disconnect_work(struct work_struct *w)
+{
+	struct gbam_port *port =
+			container_of(w, struct gbam_port, disconnect_w);
+	unsigned long		flags;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	/* disable endpoints */
+	usb_ep_disable(port->gr->out);
+	usb_ep_disable(port->gr->in);
+
+}
+
 static void gbam_connect_work(struct work_struct *w)
 {
 	struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
@@ -563,30 +660,47 @@
 	pr_debug("%s: done\n", __func__);
 }
 
-static void gbam_free_buffers(struct gbam_port *port)
+static void gbam2bam_connect_work(struct work_struct *w)
 {
-	struct sk_buff		*skb;
-	unsigned long		flags;
-	struct bam_ch_info	*d;
+	struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+	struct bam_ch_info *d = &port->data_ch;
+	u32 sps_params;
+	int ret;
 
-	spin_lock_irqsave(&port->port_lock, flags);
+	ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
+						  &d->dst_pipe_idx);
+	if (ret) {
+		pr_err("%s: usb_bam_connect failed: err:%d\n",
+			__func__, ret);
+		return;
+	}
 
-	if (!port || !port->port_usb)
-		goto free_buf_out;
+	d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
+	if (!d->rx_req)
+		return;
 
-	d = &port->data_ch;
+	d->rx_req->context = port;
+	d->rx_req->complete = gbam_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;
 
-	gbam_free_requests(port->port_usb->in, &d->tx_idle);
-	gbam_free_requests(port->port_usb->out, &d->rx_idle);
+	d->tx_req->context = port;
+	d->tx_req->complete = gbam_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;
 
-	while ((skb = __skb_dequeue(&d->tx_skb_q)))
-		dev_kfree_skb_any(skb);
+	/* queue in & out requests */
+	gbam_start_endless_rx(port);
+	gbam_start_endless_tx(port);
 
-	while ((skb = __skb_dequeue(&d->rx_skb_q)))
-		dev_kfree_skb_any(skb);
-
-free_buf_out:
-	spin_unlock_irqrestore(&port->port_lock, flags);
+	pr_debug("%s: done\n", __func__);
 }
 
 /* BAM data channel ready, allow attempt to open */
@@ -673,6 +787,13 @@
 	}
 }
 
+static void gbam2bam_port_free(int portno)
+{
+	struct gbam_port *port = bam2bam_ports[portno];
+
+	kfree(port);
+}
+
 static int gbam_port_alloc(int portno)
 {
 	struct gbam_port	*port;
@@ -709,6 +830,32 @@
 	pdrv->driver.owner = THIS_MODULE;
 
 	platform_driver_register(pdrv);
+	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+	return 0;
+}
+
+static int gbam2bam_port_alloc(int portno)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info	*d;
+
+	port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->port_num = portno;
+
+	/* port initialization */
+	spin_lock_init(&port->port_lock);
+
+	INIT_WORK(&port->connect_w, gbam2bam_connect_work);
+	INIT_WORK(&port->disconnect_w, gbam2bam_disconnect_work);
+
+	/* data ch */
+	d = &port->data_ch;
+	d->port = port;
+	bam2bam_ports[portno] = port;
 
 	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
 
@@ -820,7 +967,7 @@
 static void gam_debugfs_init(void) { }
 #endif
 
-void gbam_disconnect(struct grmnet *gr, u8 port_num)
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans)
 {
 	struct gbam_port	*port;
 	unsigned long		flags;
@@ -828,8 +975,17 @@
 
 	pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
 
-	if (port_num >= n_bam_ports) {
-		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+	if (trans == USB_GADGET_XPORT_BAM &&
+		port_num >= n_bam_ports) {
+		pr_err("%s: invalid bam portno#%d\n",
+			   __func__, port_num);
+		return;
+	}
+
+	if (trans == USB_GADGET_XPORT_BAM2BAM &&
+		port_num >= n_bam2bam_ports) {
+		pr_err("%s: invalid bam2bam portno#%d\n",
+			   __func__, port_num);
 		return;
 	}
 
@@ -837,24 +993,31 @@
 		pr_err("%s: grmnet port is null\n", __func__);
 		return;
 	}
+	if (trans == USB_GADGET_XPORT_BAM)
+		port = bam_ports[port_num].port;
+	else
+		port = bam2bam_ports[port_num];
 
-	port = bam_ports[port_num].port;
 	d = &port->data_ch;
+	port->gr = gr;
 
-	gbam_free_buffers(port);
+	if (trans == USB_GADGET_XPORT_BAM) {
+		gbam_free_buffers(port);
 
-	spin_lock_irqsave(&port->port_lock, flags);
-	port->port_usb = 0;
-	spin_unlock_irqrestore(&port->port_lock, flags);
+		spin_lock_irqsave(&port->port_lock, flags);
+		port->port_usb = 0;
+		spin_unlock_irqrestore(&port->port_lock, flags);
 
-	/* disable endpoints */
-	usb_ep_disable(gr->out);
-	usb_ep_disable(gr->in);
+		/* disable endpoints */
+		usb_ep_disable(gr->out);
+		usb_ep_disable(gr->in);
+	}
 
 	queue_work(gbam_wq, &port->disconnect_w);
 }
 
-int gbam_connect(struct grmnet *gr, u8 port_num)
+int gbam_connect(struct grmnet *gr, u8 port_num,
+				 enum transport_type trans, u8 connection_idx)
 {
 	struct gbam_port	*port;
 	struct bam_ch_info	*d;
@@ -863,7 +1026,12 @@
 
 	pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
 
-	if (port_num >= n_bam_ports) {
+	if (trans == USB_GADGET_XPORT_BAM && port_num >= n_bam_ports) {
+		pr_err("%s: invalid portno#%d\n", __func__, port_num);
+		return -ENODEV;
+	}
+
+	if (trans == USB_GADGET_XPORT_BAM2BAM && port_num >= n_bam2bam_ports) {
 		pr_err("%s: invalid portno#%d\n", __func__, port_num);
 		return -ENODEV;
 	}
@@ -873,7 +1041,11 @@
 		return -ENODEV;
 	}
 
-	port = bam_ports[port_num].port;
+	if (trans == USB_GADGET_XPORT_BAM)
+		port = bam_ports[port_num].port;
+	else
+		port = bam2bam_ports[port_num];
+
 	d = &port->data_ch;
 
 	ret = usb_ep_enable(gr->in, gr->in_desc);
@@ -896,29 +1068,35 @@
 	spin_lock_irqsave(&port->port_lock, flags);
 	port->port_usb = gr;
 
-	d->to_host = 0;
-	d->to_modem = 0;
-	d->pending_with_bam = 0;
-	d->tohost_drp_cnt = 0;
-	d->tomodem_drp_cnt = 0;
+	if (trans == USB_GADGET_XPORT_BAM) {
+		d->to_host = 0;
+		d->to_modem = 0;
+		d->pending_with_bam = 0;
+		d->tohost_drp_cnt = 0;
+		d->tomodem_drp_cnt = 0;
+	}
 	spin_unlock_irqrestore(&port->port_lock, flags);
 
+	if (trans == USB_GADGET_XPORT_BAM2BAM)
+		d->connection_idx = connection_idx;
 
 	queue_work(gbam_wq, &port->connect_w);
 
 	return 0;
 }
 
-int gbam_setup(unsigned int count)
+int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port)
 {
 	int	i;
 	int	ret;
 
-	pr_debug("%s: requested ports:%d\n", __func__, count);
+	pr_debug("%s: requested BAM ports:%d and BAM2BAM ports:%d\n",
+			  __func__, no_bam_port, no_bam2bam_port);
 
-	if (!count || count > BAM_N_PORTS) {
-		pr_err("%s: Invalid num of ports count:%d\n",
-				__func__, count);
+	if ((!no_bam_port && !no_bam2bam_port) || no_bam_port > BAM_N_PORTS
+		|| no_bam2bam_port > BAM2BAM_N_PORTS) {
+		pr_err("%s: Invalid num of ports count:%d,%d\n",
+				__func__, no_bam_port, no_bam2bam_port);
 		return -EINVAL;
 	}
 
@@ -929,7 +1107,7 @@
 		return -ENOMEM;
 	}
 
-	for (i = 0; i < count; i++) {
+	for (i = 0; i < no_bam_port; i++) {
 		n_bam_ports++;
 		ret = gbam_port_alloc(i);
 		if (ret) {
@@ -939,13 +1117,23 @@
 		}
 	}
 
+	for (i = 0; i < no_bam2bam_port; i++) {
+		n_bam2bam_ports++;
+		ret = gbam2bam_port_alloc(i);
+		if (ret) {
+			n_bam2bam_ports--;
+			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			goto free_bam_ports;
+		}
+	}
 	gbam_debugfs_init();
-
 	return 0;
+
 free_bam_ports:
 	for (i = 0; i < n_bam_ports; i++)
 		gbam_port_free(i);
-
+	for (i = 0; i < n_bam2bam_ports; i++)
+		gbam2bam_port_free(i);
 	destroy_workqueue(gbam_wq);
 
 	return ret;
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
index d8de31e..fd1e124 100644
--- a/drivers/usb/gadget/u_rmnet.h
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -48,10 +48,10 @@
 	void (*connect)(struct grmnet *g);
 };
 
-int gbam_setup(unsigned int count);
-int gbam_connect(struct grmnet *, u8 port_num);
-void gbam_disconnect(struct grmnet *, u8 port_num);
-
+int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port);
+int gbam_connect(struct grmnet *gr, u8 port_num,
+				 enum transport_type trans, u8 connection_idx);
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans);
 int gsmd_ctrl_connect(struct grmnet *gr, int port_num);
 void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num);
 int gsmd_ctrl_setup(unsigned int count);
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
index 8b08b7a..90cbe54 100644
--- a/drivers/usb/gadget/u_rmnet_ctrl_smd.c
+++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
@@ -544,12 +544,13 @@
 	}
 
 	for (i = 0; i < count; i++) {
+		n_rmnet_ctrl_ports++;
 		ret = grmnet_ctrl_smd_port_alloc(i);
 		if (ret) {
 			pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+			n_rmnet_ctrl_ports--;
 			goto free_ctrl_smd_ports;
 		}
-		n_rmnet_ctrl_ports++;
 	}
 
 	return 0;
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index c83035d..47e8427 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -57,6 +57,7 @@
  *	Note that for writes (IN transfers) some data bytes may still
  *	reside in a device-side FIFO when the request is reported as
  *	complete.
+ *@udc_priv: Vendor private data in usage by the UDC.
  *
  * These are allocated/freed through the endpoint they're used with.  The
  * hardware's driver can add extra per-request data to the memory it returns,
@@ -92,6 +93,7 @@
 
 	int			status;
 	unsigned		actual;
+	unsigned		udc_priv;
 };
 
 /*-------------------------------------------------------------------------*/
@@ -111,7 +113,6 @@
 	struct usb_request *(*alloc_request) (struct usb_ep *ep,
 		gfp_t gfp_flags);
 	void (*free_request) (struct usb_ep *ep, struct usb_request *req);
-
 	int (*queue) (struct usb_ep *ep, struct usb_request *req,
 		gfp_t gfp_flags);
 	int (*dequeue) (struct usb_ep *ep, struct usb_request *req);