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

Commit dbca823c authored by Mayank Rana's avatar Mayank Rana
Browse files

f_gsi: Don't send end xfer command when USB is into U3 state



It is recommended to avoid sending end xfer command with USB GSI
endpoint when USB is into U3 state (i.e. bus suspended) as USB
controller may hang. USB GSI driver sends end xfer command with
suspend implementation when remote wakeup is not allowed. Hence
update IPA driver about remote wakeup status to suspend or
disconnect IPA GSI channel whereas remove sending end xfer command
to USB controller when remote wakeup is not allowed.

CRs-Fixed: 1077546
Change-Id: I3ac3714ccaf4ac9f16c26f59b9550aa825483344
Signed-off-by: default avatarMayank Rana <mrana@codeaurora.org>
parent 19a63f49
Loading
Loading
Loading
Loading
+70 −117
Original line number Diff line number Diff line
@@ -58,7 +58,6 @@ struct usb_gsi_debugfs {

static struct usb_gsi_debugfs debugfs;

static void gsi_rndis_ipa_reset_trigger(void);
static void ipa_disconnect_handler(struct gsi_data_port *d_port);
static int gsi_ctrl_send_notification(struct f_gsi *gsi);
static int gsi_alloc_trb_buffer(struct f_gsi *gsi);
@@ -66,6 +65,20 @@ static void gsi_free_trb_buffer(struct f_gsi *gsi);
static struct gsi_ctrl_pkt *gsi_ctrl_pkt_alloc(unsigned len, gfp_t flags);
static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt);

static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f)
{
	bool remote_wakeup_allowed;

	if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
		remote_wakeup_allowed = f->func_wakeup_allowed;
	else
		remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;

	log_event_dbg("%s: remote_wakeup_allowed:%s", __func__,
			remote_wakeup_allowed ? "true" : "false");
	return remote_wakeup_allowed;
}

void post_event(struct gsi_data_port *port, u8 event)
{
	unsigned long flags;
@@ -884,16 +897,22 @@ static void ipa_disconnect_handler(struct gsi_data_port *d_port)

	log_event_dbg("%s: EP Disable for data", __func__);

	/* Block doorbell to GSI to avoid USB wrapper from
	 * ringing doorbell in case IPA clocks are OFF
	if (gsi->d_port.in_ep) {
		/*
		 * Block doorbell to GSI to avoid USB wrapper from
		 * ringing doorbell in case IPA clocks are OFF.
		 */
		usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
				GSI_EP_OP_SET_CLR_BLOCK_DBL);

		gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc;
		usb_ep_disable(gsi->d_port.in_ep);
	}

	if (gsi->d_port.out_ep)
	if (gsi->d_port.out_ep) {
		gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc;
		usb_ep_disable(gsi->d_port.out_ep);
	}

	gsi->d_port.net_ready_trigger = false;
}

@@ -930,18 +949,21 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
	int ret = 0;
	bool block_db, f_suspend;
	struct f_gsi *gsi = d_port_to_gsi(d_port);
	struct usb_function *f = &gsi->function;

	f_suspend = f->func_wakeup_allowed;
	log_event_dbg("%s: f_suspend:%d", __func__, f_suspend);

	f_suspend = gsi->function.func_wakeup_allowed;
	if (!usb_gsi_ep_op(gsi->d_port.in_ep, (void *) &f_suspend,
				GSI_EP_OP_CHECK_FOR_SUSPEND)) {
		ret = -EFAULT;
		goto done;
	}
	log_event_dbg("%s: Calling xdci_suspend", __func__);

	log_event_dbg("%s: Calling xdci_suspend", __func__);
	ret = ipa_usb_xdci_suspend(gsi->d_port.out_channel_handle,
				gsi->d_port.in_channel_handle, gsi->prot_id,
				true);
				usb_gsi_remote_wakeup_allowed(f));

	if (!ret) {
		d_port->sm_state = STATE_SUSPENDED;
@@ -960,10 +982,8 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
		log_event_err("%s: Error %d for %d", __func__, ret,
							gsi->prot_id);
	}

	log_event_dbg("%s: xdci_suspend ret %d", __func__, ret);

done:
	log_event_dbg("%s: xdci_suspend ret %d", __func__, ret);
	return ret;
}

@@ -998,7 +1018,6 @@ static void ipa_work_handler(struct work_struct *w)
	struct device *dev;
	struct device *gad_dev;
	struct f_gsi *gsi;
	bool block_db;

	event = read_event(d_port);

@@ -1060,6 +1079,27 @@ static void ipa_work_handler(struct work_struct *w)
								__func__);
				break;
			}

			if (d_port->out_ep && !d_port->out_ep->desc &&
					gsi->out_ep_desc_backup) {
				d_port->out_ep->desc = gsi->out_ep_desc_backup;
				d_port->out_ep->ep_intr_num = 1;
			}

			if (d_port->in_ep && !d_port->in_ep->desc &&
					gsi->in_ep_desc_backup) {
				d_port->in_ep->desc = gsi->in_ep_desc_backup;
				d_port->in_ep->ep_intr_num = 2;
			}

			if (d_port->out_ep)
				usb_gsi_ep_op(d_port->out_ep,
					&d_port->out_request, GSI_EP_OP_CONFIG);

			if (d_port->in_ep)
				usb_gsi_ep_op(d_port->in_ep,
					&d_port->in_request, GSI_EP_OP_CONFIG);

			ipa_connect_channels(d_port);
			ipa_data_path_enable(d_port);
			d_port->sm_state = STATE_CONNECTED;
@@ -1121,15 +1161,7 @@ static void ipa_work_handler(struct work_struct *w)
			if (event == EVT_HOST_NRDY) {
				log_event_dbg("%s: ST_CON_HOST_NRDY\n",
								__func__);
				block_db = true;
				/* stop USB ringing doorbell to GSI(OUT_EP) */
				usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
						GSI_EP_OP_SET_CLR_BLOCK_DBL);
				gsi_rndis_ipa_reset_trigger();
				usb_gsi_ep_op(d_port->in_ep, NULL,
						GSI_EP_OP_ENDXFER);
				usb_gsi_ep_op(d_port->out_ep, NULL,
						GSI_EP_OP_ENDXFER);
				ipa_disconnect_handler(d_port);
			}

			ipa_disconnect_work_handler(d_port);
@@ -1743,27 +1775,6 @@ static void gsi_rndis_open(struct f_gsi *rndis)
	rndis_signal_connect(rndis->config);
}

static void gsi_rndis_ipa_reset_trigger(void)
{
	struct f_gsi *rndis = gsi_prot_ctx[IPA_USB_RNDIS];
	unsigned long flags;

	if (!rndis) {
		log_event_err("%s: gsi prot ctx is %p", __func__, rndis);
		return;
	}

	spin_lock_irqsave(&rndis->d_port.lock, flags);
	if (!rndis) {
		log_event_err("%s: No RNDIS instance", __func__);
		spin_unlock_irqrestore(&rndis->d_port.lock, flags);
		return;
	}

	rndis->d_port.net_ready_trigger = false;
	spin_unlock_irqrestore(&rndis->d_port.lock, flags);
}

void gsi_rndis_flow_ctrl_enable(bool enable)
{
	struct f_gsi *rndis = gsi_prot_ctx[IPA_USB_RNDIS];
@@ -2573,7 +2584,6 @@ static void gsi_suspend(struct usb_function *f)
{
	bool block_db;
	struct f_gsi *gsi = func_to_gsi(f);
	bool remote_wakeup_allowed;

	if (!gsi->data_interface_up) {
		log_event_dbg("%s: Data interface not up\n", __func__);
@@ -2586,49 +2596,17 @@ static void gsi_suspend(struct usb_function *f)
		return;
	}

	if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
		remote_wakeup_allowed = f->func_wakeup_allowed;
	else
		remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;

	log_event_info("%s: remote_wakeup_allowed %d",
					__func__, remote_wakeup_allowed);

	if (!remote_wakeup_allowed) {
		if (gsi->prot_id == IPA_USB_RNDIS)
			rndis_flow_control(gsi->config, true);
		/*
		 * When remote wakeup is disabled, IPA is disconnected
		 * because it cannot send new data until the USB bus is
		 * resumed. Endpoint descriptors info is saved before it
		 * gets reset by the BAM disconnect API. This lets us
		 * restore this info when the USB bus is resumed.
		 */
		if (gsi->d_port.in_ep)
			gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc;
		if (gsi->d_port.out_ep)
			gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc;

		ipa_disconnect_handler(&gsi->d_port);

		post_event(&gsi->d_port, EVT_DISCONNECTED);
		queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
		log_event_dbg("%s: Disconnecting", __func__);
	} else {
	block_db = true;
	usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db,
			GSI_EP_OP_SET_CLR_BLOCK_DBL);
	post_event(&gsi->d_port, EVT_SUSPEND);
	queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
	}

	log_event_dbg("gsi suspended");
}

static void gsi_resume(struct usb_function *f)
{
	struct f_gsi *gsi = func_to_gsi(f);
	bool remote_wakeup_allowed;
	struct usb_composite_dev *cdev = f->config->cdev;

	log_event_dbg("%s", __func__);
@@ -2646,49 +2624,24 @@ static void gsi_resume(struct usb_function *f)
		f->func_is_suspended)
		return;

	if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
		remote_wakeup_allowed = f->func_wakeup_allowed;
	else
		remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;

	if (gsi->c_port.notify && !gsi->c_port.notify->desc)
		config_ep_by_speed(cdev->gadget, f, gsi->c_port.notify);

	/* Check any pending cpkt, and queue immediately on resume */
	gsi_ctrl_send_notification(gsi);

	if (!remote_wakeup_allowed) {

		/* Configure EPs for GSI */
		if (gsi->d_port.out_ep) {
			gsi->d_port.out_ep->desc = gsi->out_ep_desc_backup;
			gsi->d_port.out_ep->ep_intr_num = 1;
			usb_gsi_ep_op(gsi->d_port.out_ep,
				&gsi->d_port.out_request, GSI_EP_OP_CONFIG);
		}
		gsi->d_port.in_ep->desc = gsi->in_ep_desc_backup;
		if (gsi->prot_id != IPA_USB_DIAG)
			gsi->d_port.in_ep->ep_intr_num = 2;
		else
			gsi->d_port.in_ep->ep_intr_num = 3;

		usb_gsi_ep_op(gsi->d_port.in_ep, &gsi->d_port.in_request,
				GSI_EP_OP_CONFIG);
		post_event(&gsi->d_port, EVT_CONNECT_IN_PROGRESS);

	/*
	 * Linux host does not send RNDIS_MSG_INIT or non-zero
	 * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume.
	 * Trigger state machine explicitly on resume.
	 */
		if (gsi->prot_id == IPA_USB_RNDIS)
	if (gsi->prot_id == IPA_USB_RNDIS &&
			!usb_gsi_remote_wakeup_allowed(f))
		rndis_flow_control(gsi->config, false);
	} else
		post_event(&gsi->d_port, EVT_RESUMED);

	post_event(&gsi->d_port, EVT_RESUMED);
	queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);


	log_event_dbg("%s: completed", __func__);
}