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

Commit 2941284e authored by ChandanaKishori Chiluveru's avatar ChandanaKishori Chiluveru Committed by Manu Gautam
Browse files

usb: gadget: Do not drop the request upon suspend



If a write(IN) request is queued after usb bus suspend in smd driver,
it queues the request in tx_pull which fails with -EAGAIN. As a result
response/indication generating a remote wakeup signal is dropped
and never received by the host.

Fix this by not dropping response packets on enqueue error
but now wake up to the host connected and queue the request on resume.

Change-Id: I2272b728699159e14581d8415b6ad9617d95685f
Signed-off-by: default avatarChandanaKishori Chiluveru <cchilu@codeaurora.org>
parent a660e4d0
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -619,6 +619,48 @@ static void gser_disable(struct usb_function *f)
	gser->online = 0;
}

static void gser_suspend(struct usb_function *f)
{
	struct f_gser	*gser = func_to_gser(f);
	unsigned port_num;

	port_num = gserial_ports[gser->port_num].client_port_num;

	pr_debug("%s: transport: %s f_gser: %p gserial: %p port_num: %d\n",
			__func__, xport_to_str(gser->transport),
			gser, &gser->port, gser->port_num);

	switch (gser->transport) {
	case USB_GADGET_XPORT_SMD:
		gsmd_suspend(&gser->port, port_num);
		break;
	default:
		pr_err("%s: Un-supported transport: %s\n", __func__,
			xport_to_str(gser->transport));
	}
}

static void gser_resume(struct usb_function *f)
{
	struct f_gser	*gser = func_to_gser(f);
	unsigned port_num;

	port_num = gserial_ports[gser->port_num].client_port_num;

	pr_debug("%s: transport: %s f_gser: %p gserial: %p port_num: %d\n",
			__func__, xport_to_str(gser->transport),
			gser, &gser->port, gser->port_num);

	switch (gser->transport) {
	case USB_GADGET_XPORT_SMD:
		gsmd_resume(&gser->port, port_num);
		break;
	default:
		pr_err("%s: Un-supported transport: %s\n", __func__,
			xport_to_str(gser->transport));
	}
}

static int gser_notify(struct f_gser *gser, u8 type, u16 value,
		void *data, unsigned length)
{
@@ -1056,6 +1098,8 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
	else
		gser->port.func.name = "modem2";
	gser->port.func.setup = gser_setup;
	gser->port.func.suspend = gser_suspend;
	gser->port.func.resume = gser_resume;
	gser->port.connect = gser_connect;
	gser->port.get_dtr = gser_get_dtr;
	gser->port.get_rts = gser_get_rts;
+2 −0
Original line number Diff line number Diff line
@@ -81,6 +81,8 @@ void gserial_disconnect(struct gserial *);
int gsmd_setup(struct usb_gadget *g, unsigned n_ports);
int gsmd_connect(struct gserial *, u8 port_num);
void gsmd_disconnect(struct gserial *, u8 portno);
void gsmd_suspend(struct gserial *, u8 port_num);
void gsmd_resume(struct gserial *, u8 port_num);
int gsmd_write(u8 portno, char *buf, unsigned int size);

/* functions are bound to configurations by a config or gadget driver */
+50 −0
Original line number Diff line number Diff line
@@ -92,6 +92,7 @@ struct gsmd_port {
	/* pkt counters */
	unsigned long		nbytes_tomodem;
	unsigned long		nbytes_tolaptop;
	bool			is_suspended;
};

static struct smd_portmaster {
@@ -299,7 +300,10 @@ static void gsmd_tx_pull(struct work_struct *w)
	struct gsmd_port *port = container_of(w, struct gsmd_port, pull);
	struct list_head *pool = &port->write_pool;
	struct smd_port_info *pi = port->pi;
	struct usb_function *func;
	struct usb_gadget	*gadget;
	struct usb_ep *in;
	int ret;

	pr_debug("%s: port:%p port#%d pool:%p\n", __func__,
			port, port->port_num, pool);
@@ -314,6 +318,27 @@ static void gsmd_tx_pull(struct work_struct *w)
	}

	in = port->port_usb->in;
	func = &port->port_usb->func;
	gadget = func->config->cdev->gadget;
	if (port->is_suspended) {
		spin_unlock_irq(&port->port_lock);
		ret = usb_gadget_wakeup(gadget);
		spin_lock_irq(&port->port_lock);

		if (ret)
			pr_err("Failed to wake up the USB core. ret=%d.\n",
				ret);

		if (!port->port_usb) {
			pr_debug("%s: USB disconnected\n", __func__);
			spin_unlock_irq(&port->port_lock);
			gsmd_read_pending(port);
			return;
		}
		spin_unlock_irq(&port->port_lock);
		return;
	}

	while (pi->ch && !list_empty(pool)) {
		struct usb_request *req;
		int avail;
@@ -1003,6 +1028,31 @@ free_smd_ports:
	return ret;
}

void gsmd_suspend(struct gserial *gser, u8 portno)
{
	struct gsmd_port *port;

	pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);

	port = smd_ports[portno].port;
	spin_lock(&port->port_lock);
	port->is_suspended = true;
	spin_unlock(&port->port_lock);
}

void gsmd_resume(struct gserial *gser, u8 portno)
{
	struct gsmd_port *port;

	pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);

	port = smd_ports[portno].port;
	spin_lock(&port->port_lock);
	port->is_suspended = false;
	spin_unlock(&port->port_lock);
	queue_work(gsmd_wq, &port->pull);
}

void gsmd_cleanup(struct usb_gadget *g, unsigned count)
{
	/* TBD */