Merge "USB: gadget: u_data_ipa: Handle usb requests allocation/free correctly"

This commit is contained in:
Linux Build Service Account 2016-12-21 07:30:21 -08:00 committed by Gerrit - the friendly Code Review server
commit 9bb2912be3

View file

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