usb: gadget: Fix race condition in connect/disconnect
This commit fixes race condition in fast connect/disconnect
of USB cable. This race condition can also be reproduced in
USB composition change. Thus this commit also adds support
for USB composition change for BAMtoBAM compositions.
CRs-fixed: 463705
Change-Id: I21abaff6a64bb405ce2d698605aff8e0050ef8d6
Signed-off-by: Anna Perel <aperel@codeaurora.org>
Signed-off-by: Bar Weiner <bweiner@codeaurora.org>
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
index aaacd43..4db4b7a 100644
--- a/drivers/usb/gadget/f_mbim.c
+++ b/drivers/usb/gadget/f_mbim.c
@@ -1455,6 +1455,7 @@
mbim_union_desc.bSlaveInterface0 = status;
mbim->bam_port.cdev = cdev;
+ mbim->bam_port.func = &mbim->function;
status = -ENODEV;
@@ -1574,6 +1575,7 @@
{
struct f_mbim *mbim = func_to_mbim(f);
+ bam_data_destroy(mbim->port_num);
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
diff --git a/drivers/usb/gadget/f_qc_ecm.c b/drivers/usb/gadget/f_qc_ecm.c
index a6b443f..3db5b51 100644
--- a/drivers/usb/gadget/f_qc_ecm.c
+++ b/drivers/usb/gadget/f_qc_ecm.c
@@ -846,6 +846,7 @@
DBG(c->cdev, "ecm unbind\n");
+ bam_data_destroy(0);
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c
index 267cf53..c1ab552 100644
--- a/drivers/usb/gadget/f_qc_rndis.c
+++ b/drivers/usb/gadget/f_qc_rndis.c
@@ -725,14 +725,14 @@
*/
rndis->port.cdc_filter = 0;
+ if (rndis_qc_bam_connect(rndis))
+ goto fail;
+
DBG(cdev, "RNDIS RX/TX early activation ...\n");
net = gether_qc_connect_name(&rndis->port, "rndis0", false);
if (IS_ERR(net))
return PTR_ERR(net);
- if (rndis_qc_bam_connect(rndis))
- goto fail;
-
rndis_set_param_dev(rndis->config, net,
&rndis->port.cdc_filter);
} else
@@ -976,6 +976,8 @@
{
struct f_rndis_qc *rndis = func_to_rndis_qc(f);
+ pr_debug("rndis_qc_unbind: free");
+ bam_data_destroy(0);
rndis_deregister(rndis->config);
rndis_exit();
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index b71f903..9dd9978 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -778,6 +778,7 @@
unsigned long flags;
if (d->trans == USB_GADGET_XPORT_BAM2BAM) {
+ usb_bam_reset_complete();
ret = usb_bam_connect(d->src_connection_idx, &d->src_pipe_idx);
if (ret) {
pr_err("%s: usb_bam_connect (src) failed: err:%d\n",
@@ -916,7 +917,7 @@
msm_hw_bam_disable(1);
/* Reset BAM */
- ret = usb_bam_a2_reset();
+ ret = usb_bam_a2_reset(0);
if (ret) {
pr_err("%s: BAM reset failed %d\n", __func__, ret);
goto reenable_eps;
diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c
index c638164..5c7e52f 100644
--- a/drivers/usb/gadget/u_bam_data.c
+++ b/drivers/usb/gadget/u_bam_data.c
@@ -145,69 +145,24 @@
struct bam_data_port *port = (struct bam_data_port *)param;
struct bam_data_ch_info *d;
int ret;
- bool reenable_eps = false;
d = &port->data_ch;
pr_debug("%s: reset by peer\n", __func__);
- /* Disable the relevant EPs if currently EPs are enabled */
- if (port->port_usb && port->port_usb->in &&
- port->port_usb->in->driver_data) {
- usb_ep_disable(port->port_usb->out);
- usb_ep_disable(port->port_usb->in);
-
- port->port_usb->in->driver_data = NULL;
- port->port_usb->out->driver_data = NULL;
- reenable_eps = true;
- }
-
/* Disable BAM */
msm_hw_bam_disable(1);
/* Reset BAM */
- ret = usb_bam_a2_reset();
+ ret = usb_bam_a2_reset(0);
if (ret) {
pr_err("%s: BAM reset failed %d\n", __func__, ret);
- goto reenable_eps;
+ return ret;
}
/* Enable BAM */
msm_hw_bam_disable(0);
-reenable_eps:
- /* Re-Enable the relevant EPs, if EPs were originally enabled */
- if (reenable_eps) {
- if (config_ep_by_speed(port->port_usb->cdev->gadget,
- port->port_usb->func, port->port_usb->in) ||
- config_ep_by_speed(port->port_usb->cdev->gadget,
- port->port_usb->func, port->port_usb->out)) {
- pr_err("%s: config_ep_by_speed failed", __func__);
- port->port_usb->in->desc = NULL;
- port->port_usb->out->desc = NULL;
- return -EINVAL;
- }
- ret = usb_ep_enable(port->port_usb->in);
- if (ret) {
- pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
- __func__, port->port_usb->in);
- return ret;
- }
- port->port_usb->in->driver_data = port;
-
- ret = usb_ep_enable(port->port_usb->out);
- if (ret) {
- pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
- __func__, port->port_usb->out);
- port->port_usb->in->driver_data = 0;
- return ret;
- }
- port->port_usb->out->driver_data = port;
-
- bam_data_start_endless_rx(port);
- bam_data_start_endless_tx(port);
- }
-
/* Unregister the peer reset callback */
usb_bam_register_peer_reset_cb(NULL, NULL);
@@ -244,6 +199,7 @@
int ret;
pr_debug("%s: Connect workqueue started", __func__);
+ usb_bam_reset_complete();
if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
if (d->func_type == USB_FUNC_MBIM) {
@@ -504,6 +460,23 @@
return 0;
}
+int bam_data_destroy(unsigned int no_bam2bam_port)
+{
+ struct bam_data_ch_info *d;
+ struct bam_data_port *port;
+
+ port = bam2bam_data_ports[no_bam2bam_port];
+ d = &port->data_ch;
+
+ pr_debug("bam_data_destroy: Freeing ports\n");
+ bam2bam_data_port_free(no_bam2bam_port);
+ if (bam_data_wq)
+ destroy_workqueue(bam_data_wq);
+ bam_data_wq = NULL;
+
+ return 0;
+}
+
int bam_data_setup(unsigned int no_bam2bam_port)
{
int i;
@@ -516,6 +489,11 @@
return -EINVAL;
}
+ if (bam_data_wq) {
+ pr_debug("bam_data is already setup");
+ return 0;
+ }
+
bam_data_wq = alloc_workqueue("k_bam_data",
WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
if (!bam_data_wq) {
diff --git a/drivers/usb/gadget/u_bam_data.h b/drivers/usb/gadget/u_bam_data.h
index 5ce678d..61f653a 100644
--- a/drivers/usb/gadget/u_bam_data.h
+++ b/drivers/usb/gadget/u_bam_data.h
@@ -36,6 +36,8 @@
int bam_data_setup(unsigned int no_bam2bam_port);
+int bam_data_destroy(unsigned int no_bam2bam_port);
+
void bam_data_suspend(u8 port_num);
void bam_data_resume(u8 port_num);