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); }