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 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_PROD;
        d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
        if (d->func_type == USB_FUNC_ECM) {
            d->ipa_params.notify = ecm_qc_get_ipa_rx_cb();
            d->ipa_params.priv = ecm_qc_get_ipa_priv();
        }
        ret = usb_bam_connect_ipa(&d->ipa_params);
        if (ret) {
            pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
                   __func__, ret);
            return;
        }

        d->ipa_params.client = IPA_CLIENT_USB_CONS;
        d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
        if (d->func_type == USB_FUNC_ECM) {
            d->ipa_params.notify = ecm_qc_get_ipa_tx_cb();
            d->ipa_params.priv = ecm_qc_get_ipa_priv();
        }
        ret = usb_bam_connect_ipa(&d->ipa_params);
        if (ret) {
            pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
                   __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,
                                  d->ipa_params.priv);
            if (ret) {
                pr_err("%s: failed to connect IPA: err:%d\n",
                       __func__, ret);
                return;
            }
        }
    } else {
        usb_bam_reset_complete();
        ret = usb_bam_connect(d->src_connection_idx, &d->src_pipe_idx);
        if (ret) {
            pr_err("usb_bam_connect (src) failed: err:%d\n", ret);
            return;
        }
        ret = usb_bam_connect(d->dst_connection_idx, &d->dst_pipe_idx);
        if (ret) {
            pr_err("usb_bam_connect (dst) 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;
    d->rx_req->no_interrupt = 1;
    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;
    d->tx_req->no_interrupt = 1;
    sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx |
                  MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
    d->tx_req->udc_priv = sps_params;


    bam_data_start_endless_rx(port);
    bam_data_start_endless_tx(port);


    if (d->trans != USB_GADGET_XPORT_BAM2BAM_IPA) {
        usb_bam_register_peer_reset_cb(bam_data_peer_reset_cb, port);

        ret = usb_bam_client_ready(true);
        if (ret) {
            pr_err("%s: usb_bam_client_ready failed: err:%d\n",
                   __func__, ret);
            return;
        }
    }

    pr_debug("%s: Connect workqueue done", __func__);
}
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 teth_bridge_connect_params connect_params;
	struct teth_bridge_init_params teth_bridge_params;
	struct bam_data_ch_info *d = &port->data_ch;
	struct data_port	*d_port = port->port_usb;
	struct usb_gadget	*gadget = d_port->cdev->gadget;
	u32			sps_params;
	int			ret;
	unsigned long		flags;

	pr_debug("%s: Connect workqueue started", __func__);

	if (port->is_connected) {
		pr_info("%s: Already connected. Bailing out.\n", __func__);
		return;
	}

	spin_lock_irqsave(&port->port_lock_ul, flags);
	if (!port->port_usb) {
		spin_unlock_irqrestore(&port->port_lock_ul, flags);
		pr_err("port_usb is NULL");
		return;
	}

	if (!port->port_usb->out) {
		spin_unlock_irqrestore(&port->port_lock_ul, flags);
		pr_err("port_usb->out (bulk out ep) is NULL");
		return;
	}

	d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_ATOMIC);
	if (!d->rx_req) {
		spin_unlock_irqrestore(&port->port_lock_ul, flags);
		pr_err("%s: failed to allocate rx_req\n", __func__);
		return;
	}

	d->rx_req->context = port;
	d->rx_req->complete = bam_data_endless_rx_complete;
	d->rx_req->length = 0;
	d->rx_req->no_interrupt = 1;

	d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_ATOMIC);
	if (!d->tx_req) {
		spin_unlock_irqrestore(&port->port_lock_ul, flags);
		pr_err("%s: failed to allocate tx_req\n", __func__);
		return;
	}
	spin_unlock_irqrestore(&port->port_lock_ul, flags);

	d->tx_req->context = port;
	d->tx_req->complete = bam_data_endless_tx_complete;
	d->tx_req->length = 0;
	d->tx_req->no_interrupt = 1;

	if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {

		if (d->dst_pipe_type != USB_BAM_PIPE_BAM2BAM) {
			pr_err("%s: no software preparation for DL not using bam2bam\n",
					__func__);
			return;
		}

		if (d->func_type == USB_FUNC_MBIM) {
			teth_bridge_params.client = d->ipa_params.src_client;
			ret = teth_bridge_init(&teth_bridge_params);
			if (ret) {
				pr_err("%s:teth_bridge_init() failed\n",
				      __func__);
				return;
			}
			d->ipa_params.notify =
				teth_bridge_params.usb_notify_cb;
			d->ipa_params.priv =
				teth_bridge_params.private_data;
			d->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
			d->ipa_params.skip_ep_cfg =
				teth_bridge_params.skip_ep_cfg;
			d->ipa_params.keep_ipa_awake = true;
		}
		d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
		if (d->func_type == USB_FUNC_ECM) {
			d->ipa_params.notify = ecm_qc_get_ipa_rx_cb();
			d->ipa_params.priv = ecm_qc_get_ipa_priv();
			d->ipa_params.skip_ep_cfg = ecm_qc_get_skip_ep_config();
			d->ipa_params.keep_ipa_awake = true;
		}

		if (d->func_type == USB_FUNC_RNDIS) {
			d->ipa_params.notify = rndis_qc_get_ipa_rx_cb();
			d->ipa_params.priv = rndis_qc_get_ipa_priv();
			d->ipa_params.skip_ep_cfg =
				rndis_qc_get_skip_ep_config();
			d->ipa_params.keep_ipa_awake = true;
		}

		/* Support for UL using system-to-IPA */
		if (d->src_pipe_type == USB_BAM_PIPE_SYS2BAM) {
			d->ul_params.teth_cb = d->ipa_params.notify;
			d->ipa_params.notify =
				bam_data_ipa_sys2bam_notify_cb;
			d->ul_params.teth_priv = d->ipa_params.priv;
			d->ipa_params.priv = &d->ul_params;
		} else {
			d->ipa_params.reset_pipe_after_lpm =
				(gadget_is_dwc3(gadget) &&
				 msm_dwc3_reset_ep_after_lpm(gadget));
		}

		ret = usb_bam_connect_ipa(&d->ipa_params);
		if (ret) {
			pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
				__func__, ret);
			return;
		}

		d_port->ipa_consumer_ep = d->ipa_params.ipa_cons_ep_idx;

		if (gadget_is_dwc3(gadget)) {
			d->src_bam_idx = usb_bam_get_connection_idx(
					gadget->name,
					IPA_P_BAM, USB_TO_PEER_PERIPHERAL,
					USB_BAM_DEVICE, 0);
			if (d->src_bam_idx < 0) {
				pr_err("%s: get_connection_idx failed\n",
					__func__);
				return;
			}

			configure_usb_data_fifo(d->src_bam_idx,
					port->port_usb->out,
					d->src_pipe_type);
		}


		/* Remove support for UL using system-to-IPA towards DL */
		if (d->src_pipe_type == USB_BAM_PIPE_SYS2BAM) {
			d->ipa_params.notify = d->ul_params.teth_cb;
			d->ipa_params.priv = d->ul_params.teth_priv;
		}

		d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
		if (d->func_type == USB_FUNC_ECM) {
			d->ipa_params.notify = ecm_qc_get_ipa_tx_cb();
			d->ipa_params.priv = ecm_qc_get_ipa_priv();
			d->ipa_params.skip_ep_cfg = ecm_qc_get_skip_ep_config();
		}
		if (d->func_type == USB_FUNC_RNDIS) {
			d->ipa_params.notify = rndis_qc_get_ipa_tx_cb();
			d->ipa_params.priv = rndis_qc_get_ipa_priv();
			d->ipa_params.skip_ep_cfg =
				rndis_qc_get_skip_ep_config();
		}

		if (d->src_pipe_type == USB_BAM_PIPE_BAM2BAM) {
			d->ipa_params.reset_pipe_after_lpm =
				(gadget_is_dwc3(gadget) &&
				 msm_dwc3_reset_ep_after_lpm(gadget));
		}

		ret = usb_bam_connect_ipa(&d->ipa_params);
		if (ret) {
			pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
				__func__, ret);
			return;
		}

		d_port->ipa_producer_ep = d->ipa_params.ipa_prod_ep_idx;
		pr_debug("%s(): ipa_producer_ep:%d ipa_consumer_ep:%d\n",
				__func__, d_port->ipa_producer_ep,
				d_port->ipa_consumer_ep);

		if (gadget_is_dwc3(gadget)) {
			d->dst_bam_idx = usb_bam_get_connection_idx(
					gadget->name,
					IPA_P_BAM, PEER_PERIPHERAL_TO_USB,
					USB_BAM_DEVICE, 0);
			if (d->dst_bam_idx < 0) {
				pr_err("%s: get_connection_idx failed\n",
					__func__);
				return;
			}

			configure_usb_data_fifo(d->dst_bam_idx,
					port->port_usb->in,
					d->dst_pipe_type);
		}

		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;
			connect_params.client_type = d->ipa_params.src_client;
			ret = teth_bridge_connect(&connect_params);
			if (ret) {
				pr_err("%s:teth_bridge_connect() failed\n",
				      __func__);
				return;
			}
		}

		if (d->func_type == USB_FUNC_ECM) {
			ret = ecm_ipa_connect(d->ipa_params.cons_clnt_hdl,
				d->ipa_params.prod_clnt_hdl,
				d->ipa_params.priv);
			if (ret) {
				pr_err("%s: failed to connect IPA: err:%d\n",
					__func__, ret);
				return;
			}
		}
		if (d->func_type == USB_FUNC_RNDIS) {
			rndis_data.prod_clnt_hdl =
				d->ipa_params.prod_clnt_hdl;
			rndis_data.cons_clnt_hdl =
				d->ipa_params.cons_clnt_hdl;
			rndis_data.priv = d->ipa_params.priv;

			ret = rndis_ipa_pipe_connect_notify(
				rndis_data.cons_clnt_hdl,
				rndis_data.prod_clnt_hdl,
				rndis_data.max_transfer_size,
				rndis_data.max_packets_number,
				rndis_data.priv);
			if (ret) {
				pr_err("%s: failed to connect IPA: err:%d\n",
					__func__, ret);
				return;
			}
			is_ipa_rndis_net_on = true;
		}
	} else { /* transport type is USB_GADGET_XPORT_BAM2BAM */
		usb_bam_reset_complete();
		/* Setup BAM connection and fetch USB PIPE index */
		ret = usb_bam_connect(d->src_connection_idx, &d->src_pipe_idx);
		if (ret) {
			pr_err("usb_bam_connect (src) failed: err:%d\n", ret);
			return;
		}
		ret = usb_bam_connect(d->dst_connection_idx, &d->dst_pipe_idx);
		if (ret) {
			pr_err("usb_bam_connect (dst) failed: err:%d\n", ret);
			return;
		}
	}
	/* Upadate BAM specific attributes in usb_request */
	if (gadget_is_dwc3(gadget)) {
		sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | MSM_PRODUCER |
				d->src_pipe_idx;
		d->rx_req->length = 32*1024;
	} else {
		sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx |
			MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
	}
	d->rx_req->udc_priv = sps_params;

	if (gadget_is_dwc3(gadget)) {
		sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | d->dst_pipe_idx;
		d->tx_req->length = 32*1024;
	} else {
		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 */
	if (d->trans == USB_GADGET_XPORT_BAM2BAM ||
		d->src_pipe_type == USB_BAM_PIPE_BAM2BAM)
		bam_data_start_endless_rx(port);
	else {
		/* The use-case of UL (OUT) ports using sys2bam is based on
		 * partial reuse of the system-to-bam_demux code. The following
		 * lines perform the branching out of the standard bam2bam flow
		 * on the USB side of the UL channel
		 */
		if (_bam_data_start_io(port, false)) {
			pr_err("%s: _bam_data_start_io\n", __func__);
			return;
		}
		bam_data_start_rx(port);
	}
	bam_data_start_endless_tx(port);

	/* Register for peer reset callback if USB_GADGET_XPORT_BAM2BAM */
	if (d->trans != USB_GADGET_XPORT_BAM2BAM_IPA) {
		usb_bam_register_peer_reset_cb(bam_data_peer_reset_cb, port);

		ret = usb_bam_client_ready(true);
		if (ret) {
			pr_err("%s: usb_bam_client_ready failed: err:%d\n",
			__func__, ret);
			return;
		}
	}

	port->is_connected = true;
	pr_debug("Connect workqueue done (port %p)", port);
}