diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c index 1850aa6ea19d..cc9e5f3652d4 100644 --- a/drivers/usb/gadget/function/u_data_ipa.c +++ b/drivers/usb/gadget/function/u_data_ipa.c @@ -93,12 +93,17 @@ static void ipa_data_endless_complete(struct usb_ep *ep, */ static void ipa_data_start_endless_xfer(struct ipa_data_ch_info *port, bool in) { + unsigned long flags; int status; - if (!port->port_usb) { - pr_err("%s(): port_usb is NULL.\n", __func__); + spin_lock_irqsave(&port->port_lock, flags); + if (!port->port_usb || (in && !port->tx_req) + || (!in && !port->rx_req)) { + spin_unlock_irqrestore(&port->port_lock, flags); + pr_err("%s(): port_usb/req is NULL.\n", __func__); return; } + spin_unlock_irqrestore(&port->port_lock, flags); if (in) { pr_debug("%s: enqueue endless TX_REQ(IN)\n", __func__); @@ -125,12 +130,17 @@ static void ipa_data_start_endless_xfer(struct ipa_data_ch_info *port, bool in) */ static void ipa_data_stop_endless_xfer(struct ipa_data_ch_info *port, bool in) { + unsigned long flags; int status; - if (!port->port_usb) { - pr_err("%s(): port_usb is NULL.\n", __func__); + spin_lock_irqsave(&port->port_lock, flags); + if (!port->port_usb || (in && !port->tx_req) + || (!in && !port->rx_req)) { + spin_unlock_irqrestore(&port->port_lock, flags); + pr_err("%s(): port_usb/req is NULL.\n", __func__); return; } + spin_unlock_irqrestore(&port->port_lock, flags); if (in) { pr_debug("%s: dequeue endless TX_REQ(IN)\n", __func__); @@ -253,6 +263,8 @@ static void ipa_data_disconnect_work(struct work_struct *w) usb_bam_free_fifos(port->usb_bam_type, port->src_connection_idx); + if (port->func_type == USB_IPA_FUNC_RMNET) + teth_bridge_disconnect(port->ipa_params.src_client); /* * Decrement usage count which was incremented * upon cable connect or cable disconnect in suspended state. @@ -313,6 +325,11 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func) spin_unlock_irqrestore(&port->port_lock, flags); usb_ep_disable(port->port_usb->in); spin_lock_irqsave(&port->port_lock, flags); + if (port->tx_req) { + usb_ep_free_request(port->port_usb->in, + port->tx_req); + port->tx_req = NULL; + } port->port_usb->in->endless = false; } @@ -321,6 +338,11 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func) spin_unlock_irqrestore(&port->port_lock, flags); usb_ep_disable(port->port_usb->out); spin_lock_irqsave(&port->port_lock, flags); + if (port->rx_req) { + usb_ep_free_request(port->port_usb->out, + port->rx_req); + port->rx_req = NULL; + } port->port_usb->out->endless = false; } @@ -367,6 +389,8 @@ static void ipa_data_connect_work(struct work_struct *w) connect_w); struct gadget_ipa_port *gport; struct usb_gadget *gadget = NULL; + struct teth_bridge_connect_params connect_params; + struct teth_bridge_init_params teth_bridge_params; u32 sps_params; int ret; unsigned long flags; @@ -407,34 +431,7 @@ static void ipa_data_connect_work(struct work_struct *w) gport->ipa_consumer_ep = -1; gport->ipa_producer_ep = -1; - if (gport->out) { - port->rx_req = usb_ep_alloc_request(gport->out, GFP_ATOMIC); - if (!port->rx_req) { - spin_unlock_irqrestore(&port->port_lock, flags); - pr_err("%s: failed to allocate rx_req\n", __func__); - return; - } - port->rx_req->context = port; - port->rx_req->complete = ipa_data_endless_complete; - port->rx_req->length = 0; - port->rx_req->no_interrupt = 1; - } - - if (gport->in) { - port->tx_req = usb_ep_alloc_request(gport->in, GFP_ATOMIC); - if (!port->tx_req) { - spin_unlock_irqrestore(&port->port_lock, flags); - pr_err("%s: failed to allocate tx_req\n", __func__); - goto free_rx_req; - } - port->tx_req->context = port; - port->tx_req->complete = ipa_data_endless_complete; - port->tx_req->length = 0; - port->tx_req->no_interrupt = 1; - } - port->is_connected = true; - spin_unlock_irqrestore(&port->port_lock, flags); /* update IPA Parameteres here. */ port->ipa_params.usb_connection_speed = gadget->speed; @@ -445,11 +442,12 @@ static void ipa_data_connect_work(struct work_struct *w) port->ipa_params.cons_clnt_hdl = -1; port->ipa_params.prod_clnt_hdl = -1; - if (gport->out) { + spin_unlock_irqrestore(&port->port_lock, flags); usb_bam_alloc_fifos(port->usb_bam_type, port->src_connection_idx); + spin_lock_irqsave(&port->port_lock, flags); sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | MSM_PRODUCER | port->src_pipe_idx; port->rx_req->length = 32*1024; @@ -460,15 +458,18 @@ static void ipa_data_connect_work(struct work_struct *w) ret = msm_ep_config(gport->out, port->rx_req, GFP_ATOMIC); if (ret) { pr_err("msm_ep_config() failed for OUT EP\n"); + spin_unlock_irqrestore(&port->port_lock, flags); usb_bam_free_fifos(port->usb_bam_type, port->src_connection_idx); - goto free_rx_tx_req; + goto out; } } if (gport->in) { + spin_unlock_irqrestore(&port->port_lock, flags); usb_bam_alloc_fifos(port->usb_bam_type, port->dst_connection_idx); + spin_lock_irqsave(&port->port_lock, flags); sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | port->dst_pipe_idx; port->tx_req->length = 32*1024; @@ -478,10 +479,21 @@ static void ipa_data_connect_work(struct work_struct *w) ret = msm_ep_config(gport->in, port->tx_req, GFP_ATOMIC); if (ret) { pr_err("msm_ep_config() failed for IN EP\n"); + spin_unlock_irqrestore(&port->port_lock, flags); goto unconfig_msm_ep_out; } } + if (port->func_type == USB_IPA_FUNC_RMNET) { + teth_bridge_params.client = port->ipa_params.src_client; + ret = teth_bridge_init(&teth_bridge_params); + if (ret) { + pr_err("%s:teth_bridge_init() failed\n", __func__); + spin_unlock_irqrestore(&port->port_lock, flags); + goto unconfig_msm_ep_in; + } + } + /* * Perform below operations for Tx from Device (OUT transfer) * 1. Connect with pipe of USB BAM with IPA BAM pipe @@ -498,17 +510,35 @@ static void ipa_data_connect_work(struct work_struct *w) port->ipa_params.priv = rndis_qc_get_ipa_priv(); port->ipa_params.skip_ep_cfg = rndis_qc_get_skip_ep_config(); + } else if (port->func_type == USB_IPA_FUNC_RMNET) { + port->ipa_params.notify = + teth_bridge_params.usb_notify_cb; + port->ipa_params.priv = + teth_bridge_params.private_data; + port->ipa_params.reset_pipe_after_lpm = + msm_dwc3_reset_ep_after_lpm(gadget); + port->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC; + port->ipa_params.skip_ep_cfg = + teth_bridge_params.skip_ep_cfg; } + spin_unlock_irqrestore(&port->port_lock, flags); ret = usb_bam_connect_ipa(port->usb_bam_type, &port->ipa_params); if (ret) { pr_err("usb_bam_connect_ipa out failed err:%d\n", ret); - goto unconfig_msm_ep_in; + goto disconnect_usb_bam_ipa_out; } - - gport->ipa_consumer_ep = port->ipa_params.ipa_cons_ep_idx; + spin_lock_irqsave(&port->port_lock, flags); is_ipa_disconnected = false; + /* check if USB cable is disconnected or not */ + if (!port->port_usb) { + pr_debug("%s:%d: cable is disconnected.\n", + __func__, __LINE__); + spin_unlock_irqrestore(&port->port_lock, flags); + goto disconnect_usb_bam_ipa_out; + } + gport->ipa_consumer_ep = port->ipa_params.ipa_cons_ep_idx; } if (gport->in) { @@ -520,22 +550,41 @@ static void ipa_data_connect_work(struct work_struct *w) port->ipa_params.priv = rndis_qc_get_ipa_priv(); port->ipa_params.skip_ep_cfg = rndis_qc_get_skip_ep_config(); + } else if (port->func_type == USB_IPA_FUNC_RMNET) { + port->ipa_params.notify = + teth_bridge_params.usb_notify_cb; + port->ipa_params.priv = + teth_bridge_params.private_data; + port->ipa_params.reset_pipe_after_lpm = + msm_dwc3_reset_ep_after_lpm(gadget); + port->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC; + port->ipa_params.skip_ep_cfg = + teth_bridge_params.skip_ep_cfg; } if (port->func_type == USB_IPA_FUNC_DPL) port->ipa_params.dst_client = IPA_CLIENT_USB_DPL_CONS; + spin_unlock_irqrestore(&port->port_lock, flags); ret = usb_bam_connect_ipa(port->usb_bam_type, &port->ipa_params); if (ret) { pr_err("usb_bam_connect_ipa IN failed err:%d\n", ret); goto disconnect_usb_bam_ipa_out; } + spin_lock_irqsave(&port->port_lock, flags); + /* check if USB cable is disconnected or not */ + if (!port->port_usb) { + pr_debug("%s:%d: cable is disconnected.\n", + __func__, __LINE__); + spin_unlock_irqrestore(&port->port_lock, flags); + goto disconnect_usb_bam_ipa_out; + } gport->ipa_producer_ep = port->ipa_params.ipa_prod_ep_idx; is_ipa_disconnected = false; } - /* For DPL need to update_ipa_pipes to qti */ + spin_unlock_irqrestore(&port->port_lock, flags); if (port->func_type == USB_IPA_FUNC_RNDIS) { rndis_data->prod_clnt_hdl = port->ipa_params.prod_clnt_hdl; @@ -563,11 +612,28 @@ static void ipa_data_connect_work(struct work_struct *w) return; } atomic_set(&port->pipe_connect_notified, 1); + } else { + /* For RmNet and DPL need to update_ipa_pipes to qti */ + enum qti_port_type qti_port_type = port->func_type == + USB_IPA_FUNC_RMNET ? QTI_PORT_RMNET : QTI_PORT_DPL; + gqti_ctrl_update_ipa_pipes(port->port_usb, qti_port_type, + gport->ipa_producer_ep, gport->ipa_consumer_ep); } if (port->func_type == USB_IPA_FUNC_RMNET) { - gqti_ctrl_update_ipa_pipes(port->port_usb, QTI_PORT_RMNET, - gport->ipa_producer_ep, gport->ipa_consumer_ep); + connect_params.ipa_usb_pipe_hdl = + port->ipa_params.prod_clnt_hdl; + connect_params.usb_ipa_pipe_hdl = + port->ipa_params.cons_clnt_hdl; + connect_params.tethering_mode = + TETH_TETHERING_MODE_RMNET; + connect_params.client_type = + port->ipa_params.src_client; + ret = teth_bridge_connect(&connect_params); + if (ret) { + pr_err("%s:teth_bridge_connect() failed\n", __func__); + goto disconnect_usb_bam_ipa_out; + } } pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n", @@ -598,26 +664,27 @@ disconnect_usb_bam_ipa_out: is_ipa_disconnected = true; } unconfig_msm_ep_in: - if (gport->in) + spin_lock_irqsave(&port->port_lock, flags); + /* check if USB cable is disconnected or not */ + if (port->port_usb && gport->in) msm_ep_unconfig(port->port_usb->in); + spin_unlock_irqrestore(&port->port_lock, flags); unconfig_msm_ep_out: if (gport->in) usb_bam_free_fifos(port->usb_bam_type, port->dst_connection_idx); - if (gport->out) { + spin_lock_irqsave(&port->port_lock, flags); + /* check if USB cable is disconnected or not */ + if (port->port_usb && gport->out) { msm_ep_unconfig(port->port_usb->out); usb_bam_free_fifos(port->usb_bam_type, port->src_connection_idx); } -free_rx_tx_req: + spin_unlock_irqrestore(&port->port_lock, flags); +out: spin_lock_irqsave(&port->port_lock, flags); port->is_connected = false; spin_unlock_irqrestore(&port->port_lock, flags); - if (gport->in && port->tx_req) - usb_ep_free_request(gport->in, port->tx_req); -free_rx_req: - if (gport->out && port->rx_req) - usb_ep_free_request(gport->out, port->rx_req); } /** @@ -658,6 +725,31 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func, spin_lock_irqsave(&port->port_lock, flags); port->port_usb = gp; port->gadget = gp->cdev->gadget; + + if (gp->out) { + port->rx_req = usb_ep_alloc_request(gp->out, GFP_ATOMIC); + if (!port->rx_req) { + spin_unlock_irqrestore(&port->port_lock, flags); + pr_err("%s: failed to allocate rx_req\n", __func__); + goto err; + } + port->rx_req->context = port; + port->rx_req->complete = ipa_data_endless_complete; + port->rx_req->length = 0; + port->rx_req->no_interrupt = 1; + } + + if (gp->in) { + port->tx_req = usb_ep_alloc_request(gp->in, GFP_ATOMIC); + if (!port->tx_req) { + pr_err("%s: failed to allocate tx_req\n", __func__); + goto free_rx_req; + } + port->tx_req->context = port; + port->tx_req->complete = ipa_data_endless_complete; + port->tx_req->length = 0; + port->tx_req->no_interrupt = 1; + } port->src_connection_idx = src_connection_idx; port->dst_connection_idx = dst_connection_idx; port->usb_bam_type = usb_bam_get_bam_type(gp->cdev->gadget->name); @@ -679,6 +771,8 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func, if (ret) { pr_err("usb_ep_enable failed eptype:IN ep:%p", port->port_usb->in); + usb_ep_free_request(port->port_usb->in, port->tx_req); + port->tx_req = NULL; port->port_usb->in->endless = false; goto err_usb_in; } @@ -690,21 +784,18 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func, if (ret) { pr_err("usb_ep_enable failed eptype:OUT ep:%p", port->port_usb->out); + usb_ep_free_request(port->port_usb->out, port->rx_req); + port->rx_req = NULL; port->port_usb->out->endless = false; goto err_usb_out; } } - if (!port->port_usb->out && !port->port_usb->in) { - pr_err("%s(): No USB endpoint enabled.\n", __func__); - ret = -EINVAL; - goto err_usb_in; - } - /* Wait for host to enable flow_control */ if (port->func_type == USB_IPA_FUNC_RNDIS) { + spin_unlock_irqrestore(&port->port_lock, flags); ret = 0; - goto err_usb_in; + return ret; } /* @@ -720,9 +811,20 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func, return ret; err_usb_out: - if (port->port_usb->in) + if (port->port_usb->in) { + usb_ep_disable(port->port_usb->in); port->port_usb->in->endless = false; + } err_usb_in: + if (gp->in && port->tx_req) { + usb_ep_free_request(gp->in, port->tx_req); + port->tx_req = NULL; + } +free_rx_req: + if (gp->out && port->rx_req) { + usb_ep_free_request(gp->out, port->rx_req); + port->rx_req = NULL; + } spin_unlock_irqrestore(&port->port_lock, flags); err: pr_debug("%s(): failed with error:%d\n", __func__, ret);