Commit 3baea29d authored by Prashanth K's avatar Prashanth K Committed by Greg Kroah-Hartman
Browse files

usb: gadget: u_serial: Implement remote wakeup capability



Implement the remote wakeup capability for u_serial. The newly added
function gserial_wakeup_host() wakes up the host when there is some
data to be sent while the device is suspended. Add gser_get_status()
callbacks to advertise f_serial interface as function wakeup capable.

Signed-off-by: default avatarPrashanth K <prashanth.k@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250424121142.4180241-1-prashanth.k@oss.qualcomm.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 11e80d37
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -364,6 +364,12 @@ static void gser_suspend(struct usb_function *f)
	gserial_suspend(&gser->port);
}

static int gser_get_status(struct usb_function *f)
{
	return (f->func_wakeup_armed ? USB_INTRF_STAT_FUNC_RW : 0) |
		USB_INTRF_STAT_FUNC_RW_CAP;
}

static struct usb_function *gser_alloc(struct usb_function_instance *fi)
{
	struct f_gser	*gser;
@@ -387,6 +393,7 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
	gser->port.func.free_func = gser_free;
	gser->port.func.resume = gser_resume;
	gser->port.func.suspend = gser_suspend;
	gser->port.func.get_status = gser_get_status;

	return &gser->port.func;
}
+43 −0
Original line number Diff line number Diff line
@@ -592,6 +592,17 @@ static int gs_start_io(struct gs_port *port)
	return status;
}

static int gserial_wakeup_host(struct gserial *gser)
{
	struct usb_function	*func = &gser->func;
	struct usb_gadget	*gadget = func->config->cdev->gadget;

	if (func->func_suspended)
		return usb_func_wakeup(func);
	else
		return usb_gadget_wakeup(gadget);
}

/*-------------------------------------------------------------------------*/

/* TTY Driver */
@@ -746,6 +757,8 @@ static ssize_t gs_write(struct tty_struct *tty, const u8 *buf, size_t count)
{
	struct gs_port	*port = tty->driver_data;
	unsigned long	flags;
	int ret = 0;
	struct gserial  *gser = port->port_usb;

	pr_vdebug("gs_write: ttyGS%d (%p) writing %zu bytes\n",
			port->port_num, tty, count);
@@ -753,6 +766,17 @@ static ssize_t gs_write(struct tty_struct *tty, const u8 *buf, size_t count)
	spin_lock_irqsave(&port->port_lock, flags);
	if (count)
		count = kfifo_in(&port->port_write_buf, buf, count);

	if (port->suspended) {
		spin_unlock_irqrestore(&port->port_lock, flags);
		ret = gserial_wakeup_host(gser);
		if (ret) {
			pr_debug("ttyGS%d: Remote wakeup failed:%d\n", port->port_num, ret);
			return count;
		}
		spin_lock_irqsave(&port->port_lock, flags);
	}

	/* treat count == 0 as flush_chars() */
	if (port->port_usb)
		gs_start_tx(port);
@@ -781,10 +805,22 @@ static void gs_flush_chars(struct tty_struct *tty)
{
	struct gs_port	*port = tty->driver_data;
	unsigned long	flags;
	int ret = 0;
	struct gserial  *gser = port->port_usb;

	pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);

	spin_lock_irqsave(&port->port_lock, flags);
	if (port->suspended) {
		spin_unlock_irqrestore(&port->port_lock, flags);
		ret = gserial_wakeup_host(gser);
		if (ret) {
			pr_debug("ttyGS%d: Remote wakeup failed:%d\n", port->port_num, ret);
			return;
		}
		spin_lock_irqsave(&port->port_lock, flags);
	}

	if (port->port_usb)
		gs_start_tx(port);
	spin_unlock_irqrestore(&port->port_lock, flags);
@@ -1464,6 +1500,13 @@ void gserial_suspend(struct gserial *gser)
		return;
	}

	if (port->write_busy || port->write_started) {
		/* Wakeup to host if there are ongoing transfers */
		spin_unlock_irqrestore(&serial_port_lock, flags);
		if (!gserial_wakeup_host(gser))
			return;
	}

	spin_lock(&port->port_lock);
	spin_unlock(&serial_port_lock);
	port->suspended = true;