Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8cdf59a0 authored by Manu Gautam's avatar Manu Gautam
Browse files

USB: gadget: u_bam: Add synchronization between IRQ and workqueue



USB cable connect/disconnect and suspend/resume processing
happens in IRQ context. Driver also uses workqueue to process some
part of connect and disconnect handling. Add spin_lock to achieve
synchronization between these contexts. And check for port_usb
(cable connect status) early in connect_wq before initializing
tethering or data path setup. Otherwise there is a chance of
connect_wq bailing out in middle of processing (e.g. on fast
connect/suspend/resume/disconnect) affecting or rather breaking
the functionality on next connect.

CRs-Fixed: 614257
Change-Id: I141cfdd619672db5ab8549ea9fdeab46058e1b74
Signed-off-by: default avatarManu Gautam <mgautam@codeaurora.org>
parent 71ee1119
Loading
Loading
Loading
Loading
+39 −39
Original line number Diff line number Diff line
@@ -1083,6 +1083,40 @@ static void gbam2bam_connect_work(struct work_struct *w)
	if (dev && dev->cdev)
		gadget = dev->cdev->gadget;

	spin_lock_irqsave(&port->port_lock_ul, flags);
	spin_lock(&port->port_lock_dl);
	if (!port->port_usb) {
		pr_debug("%s: usb cable is disconnected, exiting\n", __func__);
		spin_unlock(&port->port_lock_dl);
		spin_unlock_irqrestore(&port->port_lock_ul, flags);
		return;
	}
	d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_ATOMIC);
	if (!d->rx_req) {
		spin_unlock(&port->port_lock_dl);
		spin_unlock_irqrestore(&port->port_lock_ul, flags);
		pr_err("%s: out of memory\n", __func__);
		return;
	}

	d->rx_req->context = port;
	d->rx_req->complete = gbam_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);
	spin_unlock(&port->port_lock_dl);
	spin_unlock_irqrestore(&port->port_lock_ul, flags);
	if (!d->tx_req) {
		pr_err("%s: out of memory\n", __func__);
		return;
	}

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

	if (d->trans == USB_GADGET_XPORT_BAM2BAM) {
		usb_bam_reset_complete();
		ret = usb_bam_connect(d->src_connection_idx, &d->src_pipe_idx);
@@ -1205,58 +1239,24 @@ static void gbam2bam_connect_work(struct work_struct *w)
			return;
		}
	}

	spin_lock_irqsave(&port->port_lock_ul, flags);
	spin_lock(&port->port_lock_dl);
	if (!port->port_usb) {
		pr_debug("%s: usb cable is disconnected, exiting\n", __func__);
		spin_unlock(&port->port_lock_dl);
		spin_unlock_irqrestore(&port->port_lock_ul, flags);
		return;
	}
	d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_ATOMIC);
	if (!d->rx_req) {
		spin_unlock(&port->port_lock_dl);
		spin_unlock_irqrestore(&port->port_lock_ul, flags);
		pr_err("%s: out of memory\n", __func__);
		return;
	}

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

	/* Update BAM specific attributes */
	if (gadget && 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
	} else {
		sps_params = (MSM_SPS_MODE | d->src_pipe_idx |
				MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;

	d->rx_req->udc_priv = sps_params;

	d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_ATOMIC);
	spin_unlock(&port->port_lock_dl);
	spin_unlock_irqrestore(&port->port_lock_ul, flags);
	if (!d->tx_req) {
		pr_err("%s: out of memory\n", __func__);
		return;
	}

	d->tx_req->context = port;
	d->tx_req->complete = gbam_endless_tx_complete;
	d->tx_req->length = 0;
	d->tx_req->no_interrupt = 1;
	d->rx_req->udc_priv = sps_params;

	if (gadget && gadget_is_dwc3(gadget)) {
		sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | d->dst_pipe_idx;
		d->tx_req->length = 32*1024;
	} else
	} else {
		sps_params = (MSM_SPS_MODE | d->dst_pipe_idx |
				MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;

	}
	d->tx_req->udc_priv = sps_params;

	/* queue in & out requests */
+53 −38
Original line number Diff line number Diff line
@@ -712,7 +712,7 @@ static void bam2bam_data_connect_work(struct work_struct *w)
	struct usb_gadget	*gadget = d_port->cdev->gadget;
	u32			sps_params;
	int			ret;

	unsigned long		flags;

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

@@ -721,6 +721,44 @@ static void bam2bam_data_connect_work(struct work_struct *w)
		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) {
@@ -902,6 +940,7 @@ static void bam2bam_data_connect_work(struct work_struct *w)
		}
	} 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);
@@ -913,51 +952,24 @@ static void bam2bam_data_connect_work(struct work_struct *w)
			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;

	/* 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
	} else {
		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;

	if (gadget_is_dwc3(gadget)) {
		sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | d->dst_pipe_idx;
		d->tx_req->length = 32*1024;
	} else
	} 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 */
@@ -1116,6 +1128,7 @@ int bam_data_connect(struct data_port *gr, u8 port_num,
	struct bam_data_port	*port;
	struct bam_data_ch_info	*d;
	int			ret;
	unsigned long		flags;

	pr_debug("dev:%p port#%d\n", gr, port_num);

@@ -1131,8 +1144,10 @@ int bam_data_connect(struct data_port *gr, u8 port_num,

	port = bam2bam_data_ports[port_num];

	spin_lock_irqsave(&port->port_lock_ul, flags);
	port->port_usb = gr;
	d = &port->data_ch;
	spin_unlock_irqrestore(&port->port_lock_ul, flags);

	d->src_connection_idx = src_connection_idx;
	d->dst_connection_idx = dst_connection_idx;