diff options
Diffstat (limited to 'drivers/usb/gadget/function/u_ether.c')
| -rw-r--r-- | drivers/usb/gadget/function/u_ether.c | 63 | 
1 files changed, 63 insertions, 0 deletions
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index f259975dfba4..6956ad8ba8dd 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -437,6 +437,20 @@ static inline int is_promisc(u16 cdc_filter)  	return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;  } +static int ether_wakeup_host(struct gether *port) +{ +	int			ret; +	struct usb_function	*func = &port->func; +	struct usb_gadget	*gadget = func->config->cdev->gadget; + +	if (func->func_suspended) +		ret = usb_func_wakeup(func); +	else +		ret = usb_gadget_wakeup(gadget); + +	return ret; +} +  static netdev_tx_t eth_start_xmit(struct sk_buff *skb,  					struct net_device *net)  { @@ -456,6 +470,15 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,  		in = NULL;  		cdc_filter = 0;  	} + +	if (dev->port_usb && dev->port_usb->is_suspend) { +		DBG(dev, "Port suspended. Triggering wakeup\n"); +		netif_stop_queue(net); +		spin_unlock_irqrestore(&dev->lock, flags); +		ether_wakeup_host(dev->port_usb); +		return NETDEV_TX_BUSY; +	} +  	spin_unlock_irqrestore(&dev->lock, flags);  	if (!in) { @@ -1014,6 +1037,45 @@ int gether_set_ifname(struct net_device *net, const char *name, int len)  }  EXPORT_SYMBOL_GPL(gether_set_ifname); +void gether_suspend(struct gether *link) +{ +	struct eth_dev *dev = link->ioport; +	unsigned long flags; + +	if (!dev) +		return; + +	if (atomic_read(&dev->tx_qlen)) { +		/* +		 * There is a transfer in progress. So we trigger a remote +		 * wakeup to inform the host. +		 */ +		ether_wakeup_host(dev->port_usb); +		return; +	} +	spin_lock_irqsave(&dev->lock, flags); +	link->is_suspend = true; +	spin_unlock_irqrestore(&dev->lock, flags); +} +EXPORT_SYMBOL_GPL(gether_suspend); + +void gether_resume(struct gether *link) +{ +	struct eth_dev *dev = link->ioport; +	unsigned long flags; + +	if (!dev) +		return; + +	if (netif_queue_stopped(dev->net)) +		netif_start_queue(dev->net); + +	spin_lock_irqsave(&dev->lock, flags); +	link->is_suspend = false; +	spin_unlock_irqrestore(&dev->lock, flags); +} +EXPORT_SYMBOL_GPL(gether_resume); +  /*   * gether_cleanup - remove Ethernet-over-USB device   * Context: may sleep @@ -1176,6 +1238,7 @@ void gether_disconnect(struct gether *link)  	spin_lock(&dev->lock);  	dev->port_usb = NULL; +	link->is_suspend = false;  	spin_unlock(&dev->lock);  }  EXPORT_SYMBOL_GPL(gether_disconnect);  | 
