mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
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: Prashanth K <prashanth.k@oss.qualcomm.com> Link: https://lore.kernel.org/r/20250424121142.4180241-1-prashanth.k@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
11e80d371b
commit
3baea29dc0
2 changed files with 50 additions and 0 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue