mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02: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);
 | 
						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)
 | 
					static struct usb_function *gser_alloc(struct usb_function_instance *fi)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct f_gser	*gser;
 | 
						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.free_func = gser_free;
 | 
				
			||||||
	gser->port.func.resume = gser_resume;
 | 
						gser->port.func.resume = gser_resume;
 | 
				
			||||||
	gser->port.func.suspend = gser_suspend;
 | 
						gser->port.func.suspend = gser_suspend;
 | 
				
			||||||
 | 
						gser->port.func.get_status = gser_get_status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &gser->port.func;
 | 
						return &gser->port.func;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -592,6 +592,17 @@ static int gs_start_io(struct gs_port *port)
 | 
				
			||||||
	return status;
 | 
						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 */
 | 
					/* 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;
 | 
						struct gs_port	*port = tty->driver_data;
 | 
				
			||||||
	unsigned long	flags;
 | 
						unsigned long	flags;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
						struct gserial  *gser = port->port_usb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pr_vdebug("gs_write: ttyGS%d (%p) writing %zu bytes\n",
 | 
						pr_vdebug("gs_write: ttyGS%d (%p) writing %zu bytes\n",
 | 
				
			||||||
			port->port_num, tty, count);
 | 
								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);
 | 
						spin_lock_irqsave(&port->port_lock, flags);
 | 
				
			||||||
	if (count)
 | 
						if (count)
 | 
				
			||||||
		count = kfifo_in(&port->port_write_buf, buf, 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() */
 | 
						/* treat count == 0 as flush_chars() */
 | 
				
			||||||
	if (port->port_usb)
 | 
						if (port->port_usb)
 | 
				
			||||||
		gs_start_tx(port);
 | 
							gs_start_tx(port);
 | 
				
			||||||
| 
						 | 
					@ -781,10 +805,22 @@ static void gs_flush_chars(struct tty_struct *tty)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gs_port	*port = tty->driver_data;
 | 
						struct gs_port	*port = tty->driver_data;
 | 
				
			||||||
	unsigned long	flags;
 | 
						unsigned long	flags;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
						struct gserial  *gser = port->port_usb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
 | 
						pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock_irqsave(&port->port_lock, flags);
 | 
						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)
 | 
						if (port->port_usb)
 | 
				
			||||||
		gs_start_tx(port);
 | 
							gs_start_tx(port);
 | 
				
			||||||
	spin_unlock_irqrestore(&port->port_lock, flags);
 | 
						spin_unlock_irqrestore(&port->port_lock, flags);
 | 
				
			||||||
| 
						 | 
					@ -1464,6 +1500,13 @@ void gserial_suspend(struct gserial *gser)
 | 
				
			||||||
		return;
 | 
							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_lock(&port->port_lock);
 | 
				
			||||||
	spin_unlock(&serial_port_lock);
 | 
						spin_unlock(&serial_port_lock);
 | 
				
			||||||
	port->suspended = true;
 | 
						port->suspended = true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue