* [patch]reset_resume for cdc-ether
@ 2009-11-24 10:59 Oliver Neukum
2009-11-29 8:18 ` David Miller
2009-12-03 6:16 ` David Miller
0 siblings, 2 replies; 16+ messages in thread
From: Oliver Neukum @ 2009-11-24 10:59 UTC (permalink / raw)
To: David Brownell, netdev, USB list, David S. Miller
Hi,
this is quite trivial but might be convenient to many users.
Regards
Oliver
Signed-off-by: Oliver Neukum <oliver@neukum.org>
--
commit 31deb426ccea8faf91f4373c1f12c98bb0d82c73
Author: Oliver Neukum <oliver@neukum.org>
Date: Tue Nov 24 08:30:41 2009 +0100
cdc-ether:imlement reset_resume()
Normal resume can do double duty as reset_resume() for this driver
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index ed55024..11f73dd 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -617,6 +617,7 @@ static struct usb_driver cdc_driver = {
.disconnect = usbnet_disconnect,
.suspend = usbnet_suspend,
.resume = usbnet_resume,
+ .reset_resume = usbnet_resume,
.supports_autosuspend = 1,
};
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
2009-11-24 10:59 [patch]reset_resume for cdc-ether Oliver Neukum
@ 2009-11-29 8:18 ` David Miller
2009-12-03 6:16 ` David Miller
1 sibling, 0 replies; 16+ messages in thread
From: David Miller @ 2009-11-29 8:18 UTC (permalink / raw)
To: oliver; +Cc: david-b, netdev, linux-usb
From: Oliver Neukum <oliver@neukum.org>
Date: Tue, 24 Nov 2009 11:59:02 +0100
> commit 31deb426ccea8faf91f4373c1f12c98bb0d82c73
> Author: Oliver Neukum <oliver@neukum.org>
> Date: Tue Nov 24 08:30:41 2009 +0100
>
> cdc-ether:imlement reset_resume()
>
> Normal resume can do double duty as reset_resume() for this driver
David B., ACK, NACK, indifferent?
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
2009-11-24 10:59 [patch]reset_resume for cdc-ether Oliver Neukum
2009-11-29 8:18 ` David Miller
@ 2009-12-03 6:16 ` David Miller
[not found] ` <20091202.221627.133392199.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
1 sibling, 1 reply; 16+ messages in thread
From: David Miller @ 2009-12-03 6:16 UTC (permalink / raw)
To: oliver; +Cc: david-b, netdev, linux-usb
From: Oliver Neukum <oliver@neukum.org>
Date: Tue, 24 Nov 2009 11:59:02 +0100
> Hi,
>
> this is quite trivial but might be convenient to many users.
>
> Regards
> Oliver
>
> Signed-off-by: Oliver Neukum <oliver@neukum.org>
I'm going to apply this patch and the USB autosuspend one too.
If David B. has some problems with it, we can revert. But as it
stands these patches have been posted for some time and there's
no reason for them to miss the merge window so I'm adding them
to net-next-2.6
Thanks Oliver.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
[not found] ` <20091202.221627.133392199.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
@ 2009-12-03 6:22 ` David Miller
[not found] ` <20091202.222254.61468873.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
0 siblings, 1 reply; 16+ messages in thread
From: David Miller @ 2009-12-03 6:22 UTC (permalink / raw)
To: oliver-GvhC2dPhHPQdnm+yROfE0A
Cc: david-b-yBeKhBN/0LDR7s880joybQ, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA
From: David Miller <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
Date: Wed, 02 Dec 2009 22:16:27 -0800 (PST)
> From: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
> Date: Tue, 24 Nov 2009 11:59:02 +0100
>
>> Hi,
>>
>> this is quite trivial but might be convenient to many users.
>>
>> Regards
>> Oliver
>>
>> Signed-off-by: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
>
> I'm going to apply this patch and the USB autosuspend one too.
Actually, neither patch applies to net-next-2.6
Oliver can you respin your changes and add a proper signoff
to the autosuspend patch?
Thanks.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
[not found] ` <20091202.222254.61468873.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
@ 2009-12-03 9:56 ` Oliver Neukum
2009-12-03 10:14 ` David Miller
[not found] ` <200912031056.17103.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2009-12-03 10:02 ` Oliver Neukum
1 sibling, 2 replies; 16+ messages in thread
From: Oliver Neukum @ 2009-12-03 9:56 UTC (permalink / raw)
To: David Miller
Cc: david-b-yBeKhBN/0LDR7s880joybQ, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA
Am Donnerstag, 3. Dezember 2009 07:22:54 schrieb David Miller:
> > I'm going to apply this patch and the USB autosuspend one too.
>
> Actually, neither patch applies to net-next-2.6
>
> Oliver can you respin your changes and add a proper signoff
> to the autosuspend patch?
Hi,
here's autosuspend again.
Regards
Oliver
Signed-off-by: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
--
commit 276405340ff17a79f6847174d1e737242df60cb5
Author: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
Date: Thu Dec 3 10:53:21 2009 +0100
usbnet & cdc-ether: autosuspend for online devices
using remote wakeup and delayed transmission to allow
online device to go into usb autosuspend
minimal alternate support for devices that don't support
remote wakeup
Signed-off-by: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 71e65fc..b9493ef 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -411,6 +411,12 @@ static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)
return 0;
}
+static int cdc_manage_power(struct usbnet *dev, int on)
+{
+ dev->intf->needs_remote_wakeup = on;
+ return 0;
+}
+
static const struct driver_info cdc_info = {
.description = "CDC Ethernet Device",
.flags = FLAG_ETHER,
@@ -418,6 +424,7 @@ static const struct driver_info cdc_info = {
.bind = cdc_bind,
.unbind = usbnet_cdc_unbind,
.status = cdc_status,
+ .manage_power = cdc_manage_power,
};
static const struct driver_info mbm_info = {
@@ -578,6 +585,7 @@ static struct usb_driver cdc_driver = {
.disconnect = usbnet_disconnect,
.suspend = usbnet_suspend,
.resume = usbnet_resume,
+ .supports_autosuspend = 1,
};
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 378da8c..83cdd46 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -353,7 +353,8 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
if (netif_running (dev->net)
&& netif_device_present (dev->net)
- && !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ && !test_bit (EVENT_RX_HALT, &dev->flags)
+ && !test_bit (EVENT_DEV_ASLEEP, &dev->flags)) {
switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) {
case -EPIPE:
usbnet_defer_kevent (dev, EVENT_RX_HALT);
@@ -611,15 +612,39 @@ EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);
/*-------------------------------------------------------------------------*/
// precondition: never called in_interrupt
+static void usbnet_terminate_urbs(struct usbnet *dev)
+{
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup);
+ DECLARE_WAITQUEUE(wait, current);
+ int temp;
+
+ /* ensure there are no more active urbs */
+ add_wait_queue(&unlink_wakeup, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ dev->wait = &unlink_wakeup;
+ temp = unlink_urbs(dev, &dev->txq) +
+ unlink_urbs(dev, &dev->rxq);
+
+ /* maybe wait for deletions to finish. */
+ while (!skb_queue_empty(&dev->rxq)
+ && !skb_queue_empty(&dev->txq)
+ && !skb_queue_empty(&dev->done)) {
+ schedule_timeout(UNLINK_TIMEOUT_MS);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (netif_msg_ifdown(dev))
+ devdbg(dev, "waited for %d urb completions",
+ temp);
+ }
+ set_current_state(TASK_RUNNING);
+ dev->wait = NULL;
+ remove_wait_queue(&unlink_wakeup, &wait);
+}
int usbnet_stop (struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
struct driver_info *info = dev->driver_info;
- int temp;
int retval;
- DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup);
- DECLARE_WAITQUEUE (wait, current);
netif_stop_queue (net);
@@ -641,25 +666,8 @@ int usbnet_stop (struct net_device *net)
info->description);
}
- if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) {
- /* ensure there are no more active urbs */
- add_wait_queue(&unlink_wakeup, &wait);
- dev->wait = &unlink_wakeup;
- temp = unlink_urbs(dev, &dev->txq) +
- unlink_urbs(dev, &dev->rxq);
-
- /* maybe wait for deletions to finish. */
- while (!skb_queue_empty(&dev->rxq)
- && !skb_queue_empty(&dev->txq)
- && !skb_queue_empty(&dev->done)) {
- msleep(UNLINK_TIMEOUT_MS);
- if (netif_msg_ifdown(dev))
- devdbg(dev, "waited for %d urb completions",
- temp);
- }
- dev->wait = NULL;
- remove_wait_queue(&unlink_wakeup, &wait);
- }
+ if (!(info->flags & FLAG_AVOID_UNLINK_URBS))
+ usbnet_terminate_urbs(dev);
usb_kill_urb(dev->interrupt);
@@ -672,7 +680,10 @@ int usbnet_stop (struct net_device *net)
dev->flags = 0;
del_timer_sync (&dev->delay);
tasklet_kill (&dev->bh);
- usb_autopm_put_interface(dev->intf);
+ if (info->manage_power)
+ info->manage_power(dev, 0);
+ else
+ usb_autopm_put_interface(dev->intf);
return 0;
}
@@ -753,6 +764,12 @@ int usbnet_open (struct net_device *net)
// delay posting reads until we're fully open
tasklet_schedule (&dev->bh);
+ if (info->manage_power) {
+ retval = info->manage_power(dev, 1);
+ if (retval < 0)
+ goto done;
+ usb_autopm_put_interface(dev->intf);
+ }
return retval;
done:
usb_autopm_put_interface(dev->intf);
@@ -881,11 +898,16 @@ kevent (struct work_struct *work)
/* usb_clear_halt() needs a thread context */
if (test_bit (EVENT_TX_HALT, &dev->flags)) {
unlink_urbs (dev, &dev->txq);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto fail_pipe;
status = usb_clear_halt (dev->udev, dev->out);
+ usb_autopm_put_interface(dev->intf);
if (status < 0
&& status != -EPIPE
&& status != -ESHUTDOWN) {
if (netif_msg_tx_err (dev))
+fail_pipe:
deverr (dev, "can't clear tx halt, status %d",
status);
} else {
@@ -896,11 +918,16 @@ kevent (struct work_struct *work)
}
if (test_bit (EVENT_RX_HALT, &dev->flags)) {
unlink_urbs (dev, &dev->rxq);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto fail_halt;
status = usb_clear_halt (dev->udev, dev->in);
+ usb_autopm_put_interface(dev->intf);
if (status < 0
&& status != -EPIPE
&& status != -ESHUTDOWN) {
if (netif_msg_rx_err (dev))
+fail_halt:
deverr (dev, "can't clear rx halt, status %d",
status);
} else {
@@ -919,7 +946,12 @@ kevent (struct work_struct *work)
clear_bit (EVENT_RX_MEMORY, &dev->flags);
if (urb != NULL) {
clear_bit (EVENT_RX_MEMORY, &dev->flags);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto fail_lowmem;
rx_submit (dev, urb, GFP_KERNEL);
+ usb_autopm_put_interface(dev->intf);
+fail_lowmem:
tasklet_schedule (&dev->bh);
}
}
@@ -929,11 +961,18 @@ kevent (struct work_struct *work)
int retval = 0;
clear_bit (EVENT_LINK_RESET, &dev->flags);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto skip_reset;
if(info->link_reset && (retval = info->link_reset(dev)) < 0) {
+ usb_autopm_put_interface(dev->intf);
+skip_reset:
devinfo(dev, "link reset failed (%d) usbnet usb-%s-%s, %s",
retval,
dev->udev->bus->bus_name, dev->udev->devpath,
info->description);
+ } else {
+ usb_autopm_put_interface(dev->intf);
}
}
@@ -971,6 +1010,7 @@ static void tx_complete (struct urb *urb)
case -EPROTO:
case -ETIME:
case -EILSEQ:
+ usb_mark_last_busy(dev->udev);
if (!timer_pending (&dev->delay)) {
mod_timer (&dev->delay,
jiffies + THROTTLE_JIFFIES);
@@ -987,6 +1027,7 @@ static void tx_complete (struct urb *urb)
}
}
+ usb_autopm_put_interface_async(dev->intf);
urb->dev = NULL;
entry->state = tx_done;
defer_bh(dev, skb, &dev->txq);
@@ -1057,14 +1098,34 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
}
}
- spin_lock_irqsave (&dev->txq.lock, flags);
+ spin_lock_irqsave(&dev->txq.lock, flags);
+ retval = usb_autopm_get_interface_async(dev->intf);
+ if (retval < 0) {
+ spin_unlock_irqrestore(&dev->txq.lock, flags);
+ goto drop;
+ }
+
+#ifdef CONFIG_PM
+ /* if this triggers the device is still a sleep */
+ if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) {
+ /* transmission will be done in resume */
+ usb_anchor_urb(urb, &dev->deferred);
+ /* no use to process more packets */
+ netif_stop_queue(net);
+ spin_unlock_irqrestore(&dev->txq.lock, flags);
+ devdbg(dev, "Delaying transmission for resumption");
+ goto deferred;
+ }
+#endif
switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
case -EPIPE:
netif_stop_queue (net);
usbnet_defer_kevent (dev, EVENT_TX_HALT);
+ usb_autopm_put_interface_async(dev->intf);
break;
default:
+ usb_autopm_put_interface_async(dev->intf);
if (netif_msg_tx_err (dev))
devdbg (dev, "tx: submit urb err %d", retval);
break;
@@ -1088,6 +1149,7 @@ drop:
devdbg (dev, "> tx, len %d, type 0x%x",
length, skb->protocol);
}
+deferred:
return NETDEV_TX_OK;
}
EXPORT_SYMBOL_GPL(usbnet_start_xmit);
@@ -1263,6 +1325,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
dev->bh.func = usbnet_bh;
dev->bh.data = (unsigned long) dev;
INIT_WORK (&dev->kevent, kevent);
+ init_usb_anchor(&dev->deferred);
dev->delay.function = usbnet_bh;
dev->delay.data = (unsigned long) dev;
init_timer (&dev->delay);
@@ -1380,13 +1443,23 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
struct usbnet *dev = usb_get_intfdata(intf);
if (!dev->suspend_count++) {
+ spin_lock_irq(&dev->txq.lock);
+ /* don't autosuspend while transmitting */
+ if (dev->txq.qlen && (message.event & PM_EVENT_AUTO)) {
+ spin_unlock_irq(&dev->txq.lock);
+ return -EBUSY;
+ } else {
+ set_bit(EVENT_DEV_ASLEEP, &dev->flags);
+ spin_unlock_irq(&dev->txq.lock);
+ }
/*
* accelerate emptying of the rx and queues, to avoid
* having everything error out.
*/
netif_device_detach (dev->net);
- (void) unlink_urbs (dev, &dev->rxq);
- (void) unlink_urbs (dev, &dev->txq);
+ usbnet_terminate_urbs(dev);
+ usb_kill_urb(dev->interrupt);
+
/*
* reattach so runtime management can use and
* wake the device
@@ -1400,10 +1473,34 @@ EXPORT_SYMBOL_GPL(usbnet_suspend);
int usbnet_resume (struct usb_interface *intf)
{
struct usbnet *dev = usb_get_intfdata(intf);
+ struct sk_buff *skb;
+ struct urb *res;
+ int retval;
+
+ if (!--dev->suspend_count) {
+ spin_lock_irq(&dev->txq.lock);
+ while ((res = usb_get_from_anchor(&dev->deferred))) {
+
+ printk(KERN_INFO"%s has delayed data\n", __func__);
+ skb = (struct sk_buff *)res->context;
+ retval = usb_submit_urb(res, GFP_ATOMIC);
+ if (retval < 0) {
+ dev_kfree_skb_any(skb);
+ usb_free_urb(res);
+ usb_autopm_put_interface_async(dev->intf);
+ } else {
+ dev->net->trans_start = jiffies;
+ __skb_queue_tail(&dev->txq, skb);
+ }
+ }
- if (!--dev->suspend_count)
+ smp_mb();
+ clear_bit(EVENT_DEV_ASLEEP, &dev->flags);
+ spin_unlock_irq(&dev->txq.lock);
+ if (!(dev->txq.qlen >= TX_QLEN(dev)))
+ netif_start_queue(dev->net);
tasklet_schedule (&dev->bh);
-
+ }
return 0;
}
EXPORT_SYMBOL_GPL(usbnet_resume);
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 86c31b7..a7b92d6 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -55,6 +55,7 @@ struct usbnet {
struct sk_buff_head done;
struct sk_buff_head rxq_pause;
struct urb *interrupt;
+ struct usb_anchor deferred;
struct tasklet_struct bh;
struct work_struct kevent;
@@ -65,6 +66,8 @@ struct usbnet {
# define EVENT_STS_SPLIT 3
# define EVENT_LINK_RESET 4
# define EVENT_RX_PAUSED 5
+# define EVENT_DEV_WAKING 6
+# define EVENT_DEV_ASLEEP 7
};
static inline struct usb_driver *driver_of(struct usb_interface *intf)
@@ -108,6 +111,9 @@ struct driver_info {
/* see if peer is connected ... can sleep */
int (*check_connect)(struct usbnet *);
+ /* (dis)activate runtime power management */
+ int (*manage_power)(struct usbnet *, int);
+
/* for status polling */
void (*status)(struct usbnet *, struct urb *);
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
[not found] ` <20091202.222254.61468873.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
2009-12-03 9:56 ` Oliver Neukum
@ 2009-12-03 10:02 ` Oliver Neukum
[not found] ` <200912031102.56903.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
1 sibling, 1 reply; 16+ messages in thread
From: Oliver Neukum @ 2009-12-03 10:02 UTC (permalink / raw)
To: David Miller
Cc: david-b-yBeKhBN/0LDR7s880joybQ, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA
Am Donnerstag, 3. Dezember 2009 07:22:54 schrieb David Miller:
> > I'm going to apply this patch and the USB autosuspend one too.
>
> Actually, neither patch applies to net-next-2.6
>
> Oliver can you respin your changes and add a proper signoff
> to the autosuspend patch?
Hi,
here's reset_resume() again.
Regards
Oliver
Signed-off-by: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
--
commit 0cf82130a576e01d516fcd81a0a6976ab7c49c23
Author: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
Date: Thu Dec 3 10:59:20 2009 +0100
cdc-ether:imlement reset_resume()
Normal resume can do double duty as reset_resume() for this driver
as it keeps no state in the device
Signed-off-by: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index b9493ef..50b8aa9 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -585,6 +585,7 @@ static struct usb_driver cdc_driver = {
.disconnect = usbnet_disconnect,
.suspend = usbnet_suspend,
.resume = usbnet_resume,
+ .reset_resume = usbnet_resume,
.supports_autosuspend = 1,
};
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
2009-12-03 9:56 ` Oliver Neukum
@ 2009-12-03 10:14 ` David Miller
2009-12-03 10:26 ` Oliver Neukum
` (2 more replies)
[not found] ` <200912031056.17103.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
1 sibling, 3 replies; 16+ messages in thread
From: David Miller @ 2009-12-03 10:14 UTC (permalink / raw)
To: oliver; +Cc: david-b, netdev, linux-usb
From: Oliver Neukum <oliver@neukum.org>
Date: Thu, 3 Dec 2009 10:56:17 +0100
> Am Donnerstag, 3. Dezember 2009 07:22:54 schrieb David Miller:
>> > I'm going to apply this patch and the USB autosuspend one too.
>>
>> Actually, neither patch applies to net-next-2.6
>>
>> Oliver can you respin your changes and add a proper signoff
>> to the autosuspend patch?
>
> Hi,
>
> here's autosuspend again.
It doesn't apply to my tree still:
davem@sunset:~/src/GIT/net-next-2.6$ git apply --check --whitespace=error-all diff
error: patch failed: drivers/net/usb/cdc_ether.c:411
error: drivers/net/usb/cdc_ether.c: patch does not apply
davem@sunset:~/src/GIT/net-next-2.6$
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
[not found] ` <200912031102.56903.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
@ 2009-12-03 10:15 ` David Miller
0 siblings, 0 replies; 16+ messages in thread
From: David Miller @ 2009-12-03 10:15 UTC (permalink / raw)
To: oliver-GvhC2dPhHPQdnm+yROfE0A
Cc: david-b-yBeKhBN/0LDR7s880joybQ, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA
From: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
Date: Thu, 3 Dec 2009 11:02:56 +0100
> here's reset_resume() again.
Also doesn't apply.
davem@sunset:~/src/GIT/net-next-2.6$ git apply --check --whitespace=error-all diff
error: patch failed: drivers/net/usb/cdc_ether.c:585
error: drivers/net/usb/cdc_ether.c: patch does not apply
You have new mail in /var/mail/davem
davem@sunset:~/src/GIT/net-next-2.6$
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
2009-12-03 10:14 ` David Miller
@ 2009-12-03 10:26 ` Oliver Neukum
2009-12-03 10:33 ` David Miller
2009-12-03 13:01 ` Oliver Neukum
2009-12-03 13:02 ` Oliver Neukum
2 siblings, 1 reply; 16+ messages in thread
From: Oliver Neukum @ 2009-12-03 10:26 UTC (permalink / raw)
To: David Miller; +Cc: david-b, netdev, linux-usb
Am Donnerstag, 3. Dezember 2009 11:14:56 schrieb David Miller:
> From: Oliver Neukum <oliver@neukum.org>
> Date: Thu, 3 Dec 2009 10:56:17 +0100
>
> > Am Donnerstag, 3. Dezember 2009 07:22:54 schrieb David Miller:
> >> > I'm going to apply this patch and the USB autosuspend one too.
> >>
> >> Actually, neither patch applies to net-next-2.6
> >>
> >> Oliver can you respin your changes and add a proper signoff
> >> to the autosuspend patch?
> >
> > Hi,
> >
> > here's autosuspend again.
>
> It doesn't apply to my tree still:
>
> davem@sunset:~/src/GIT/net-next-2.6$ git apply --check
> --whitespace=error-all diff error: patch failed:
> drivers/net/usb/cdc_ether.c:411
> error: drivers/net/usb/cdc_ether.c: patch does not apply
> davem@sunset:~/src/GIT/net-next-2.6$
Am I making patches against the wrong tree?
Can you tell me exactly which tree I need to clone?
Regards
Oliver
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
2009-12-03 10:26 ` Oliver Neukum
@ 2009-12-03 10:33 ` David Miller
0 siblings, 0 replies; 16+ messages in thread
From: David Miller @ 2009-12-03 10:33 UTC (permalink / raw)
To: oliver; +Cc: david-b, netdev, linux-usb
From: Oliver Neukum <oliver@neukum.org>
Date: Thu, 3 Dec 2009 11:26:03 +0100
> Can you tell me exactly which tree I need to clone?
git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6.git
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
2009-12-03 10:14 ` David Miller
2009-12-03 10:26 ` Oliver Neukum
@ 2009-12-03 13:01 ` Oliver Neukum
2009-12-03 13:02 ` Oliver Neukum
2 siblings, 0 replies; 16+ messages in thread
From: Oliver Neukum @ 2009-12-03 13:01 UTC (permalink / raw)
To: David Miller; +Cc: david-b, netdev, linux-usb
Am Donnerstag, 3. Dezember 2009 11:14:56 schrieb David Miller:
> > Hi,
> >
> > here's autosuspend again.
>
> It doesn't apply to my tree still:
>
Ok, this time against the correct tree comes reset_resume()
Regards
Oliver
Signed-off-by: Oliver Neukum <oliver@neukum.org>
--
commit a0516662d1d281c321410a8792603ba02c14293b
Author: Oliver Neukum <oliver@neukum.org>
Date: Thu Dec 3 13:54:49 2009 +0100
cdc-ether:imlement reset_resume()
Normal resume can do double duty as reset_resume() for this driver
as it keeps no state in the device
Signed-off-by: Oliver Neukum <oliver@neukum.org>
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 21e1ba1..075e33f 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -610,6 +610,7 @@ static struct usb_driver cdc_driver = {
.disconnect = usbnet_disconnect,
.suspend = usbnet_suspend,
.resume = usbnet_resume,
+ .reset_resume = usbnet_resume,
};
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
2009-12-03 10:14 ` David Miller
2009-12-03 10:26 ` Oliver Neukum
2009-12-03 13:01 ` Oliver Neukum
@ 2009-12-03 13:02 ` Oliver Neukum
2009-12-03 20:31 ` David Miller
2 siblings, 1 reply; 16+ messages in thread
From: Oliver Neukum @ 2009-12-03 13:02 UTC (permalink / raw)
To: David Miller, Torgny Johansson; +Cc: david-b, netdev, linux-usb
Am Donnerstag, 3. Dezember 2009 11:14:56 schrieb David Miller:
> It doesn't apply to my tree still:
And this is autosuspend.
Regards
Oliver
Signed-off-by: Oliver Neukum <oliver@neukum.org>
--
commit 8c2dc91a80eca9c2bd7f8074ba2afbd1251ede99
Author: Oliver Neukum <oliver@neukum.org>
Date: Thu Dec 3 13:58:59 2009 +0100
usbnet & cdc-ether: autosuspend for online devices
using remote wakeup and delayed transmission to allow
online device to go into usb autosuspend
minimal alternate support for devices that don't support
remote wakeup
Signed-off-by: Oliver Neukum <oliver@neukum.org>
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 075e33f..11f73dd 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -411,6 +411,12 @@ static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)
return 0;
}
+static int cdc_manage_power(struct usbnet *dev, int on)
+{
+ dev->intf->needs_remote_wakeup = on;
+ return 0;
+}
+
static const struct driver_info cdc_info = {
.description = "CDC Ethernet Device",
.flags = FLAG_ETHER,
@@ -418,6 +424,7 @@ static const struct driver_info cdc_info = {
.bind = cdc_bind,
.unbind = usbnet_cdc_unbind,
.status = cdc_status,
+ .manage_power = cdc_manage_power,
};
/*-------------------------------------------------------------------------*/
@@ -611,6 +618,7 @@ static struct usb_driver cdc_driver = {
.suspend = usbnet_suspend,
.resume = usbnet_resume,
.reset_resume = usbnet_resume,
+ .supports_autosuspend = 1,
};
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index ca5ca5a..4a555b6 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -353,7 +353,8 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
if (netif_running (dev->net)
&& netif_device_present (dev->net)
- && !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ && !test_bit (EVENT_RX_HALT, &dev->flags)
+ && !test_bit (EVENT_DEV_ASLEEP, &dev->flags)) {
switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) {
case -EPIPE:
usbnet_defer_kevent (dev, EVENT_RX_HALT);
@@ -611,15 +612,39 @@ EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);
/*-------------------------------------------------------------------------*/
// precondition: never called in_interrupt
+static void usbnet_terminate_urbs(struct usbnet *dev)
+{
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup);
+ DECLARE_WAITQUEUE(wait, current);
+ int temp;
+
+ /* ensure there are no more active urbs */
+ add_wait_queue(&unlink_wakeup, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ dev->wait = &unlink_wakeup;
+ temp = unlink_urbs(dev, &dev->txq) +
+ unlink_urbs(dev, &dev->rxq);
+
+ /* maybe wait for deletions to finish. */
+ while (!skb_queue_empty(&dev->rxq)
+ && !skb_queue_empty(&dev->txq)
+ && !skb_queue_empty(&dev->done)) {
+ schedule_timeout(UNLINK_TIMEOUT_MS);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (netif_msg_ifdown(dev))
+ devdbg(dev, "waited for %d urb completions",
+ temp);
+ }
+ set_current_state(TASK_RUNNING);
+ dev->wait = NULL;
+ remove_wait_queue(&unlink_wakeup, &wait);
+}
int usbnet_stop (struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
struct driver_info *info = dev->driver_info;
- int temp;
int retval;
- DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup);
- DECLARE_WAITQUEUE (wait, current);
netif_stop_queue (net);
@@ -641,25 +666,8 @@ int usbnet_stop (struct net_device *net)
info->description);
}
- if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) {
- /* ensure there are no more active urbs */
- add_wait_queue(&unlink_wakeup, &wait);
- dev->wait = &unlink_wakeup;
- temp = unlink_urbs(dev, &dev->txq) +
- unlink_urbs(dev, &dev->rxq);
-
- /* maybe wait for deletions to finish. */
- while (!skb_queue_empty(&dev->rxq)
- && !skb_queue_empty(&dev->txq)
- && !skb_queue_empty(&dev->done)) {
- msleep(UNLINK_TIMEOUT_MS);
- if (netif_msg_ifdown(dev))
- devdbg(dev, "waited for %d urb completions",
- temp);
- }
- dev->wait = NULL;
- remove_wait_queue(&unlink_wakeup, &wait);
- }
+ if (!(info->flags & FLAG_AVOID_UNLINK_URBS))
+ usbnet_terminate_urbs(dev);
usb_kill_urb(dev->interrupt);
@@ -672,7 +680,10 @@ int usbnet_stop (struct net_device *net)
dev->flags = 0;
del_timer_sync (&dev->delay);
tasklet_kill (&dev->bh);
- usb_autopm_put_interface(dev->intf);
+ if (info->manage_power)
+ info->manage_power(dev, 0);
+ else
+ usb_autopm_put_interface(dev->intf);
return 0;
}
@@ -753,6 +764,12 @@ int usbnet_open (struct net_device *net)
// delay posting reads until we're fully open
tasklet_schedule (&dev->bh);
+ if (info->manage_power) {
+ retval = info->manage_power(dev, 1);
+ if (retval < 0)
+ goto done;
+ usb_autopm_put_interface(dev->intf);
+ }
return retval;
done:
usb_autopm_put_interface(dev->intf);
@@ -881,11 +898,16 @@ kevent (struct work_struct *work)
/* usb_clear_halt() needs a thread context */
if (test_bit (EVENT_TX_HALT, &dev->flags)) {
unlink_urbs (dev, &dev->txq);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto fail_pipe;
status = usb_clear_halt (dev->udev, dev->out);
+ usb_autopm_put_interface(dev->intf);
if (status < 0
&& status != -EPIPE
&& status != -ESHUTDOWN) {
if (netif_msg_tx_err (dev))
+fail_pipe:
deverr (dev, "can't clear tx halt, status %d",
status);
} else {
@@ -896,11 +918,16 @@ kevent (struct work_struct *work)
}
if (test_bit (EVENT_RX_HALT, &dev->flags)) {
unlink_urbs (dev, &dev->rxq);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto fail_halt;
status = usb_clear_halt (dev->udev, dev->in);
+ usb_autopm_put_interface(dev->intf);
if (status < 0
&& status != -EPIPE
&& status != -ESHUTDOWN) {
if (netif_msg_rx_err (dev))
+fail_halt:
deverr (dev, "can't clear rx halt, status %d",
status);
} else {
@@ -919,7 +946,12 @@ kevent (struct work_struct *work)
clear_bit (EVENT_RX_MEMORY, &dev->flags);
if (urb != NULL) {
clear_bit (EVENT_RX_MEMORY, &dev->flags);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto fail_lowmem;
rx_submit (dev, urb, GFP_KERNEL);
+ usb_autopm_put_interface(dev->intf);
+fail_lowmem:
tasklet_schedule (&dev->bh);
}
}
@@ -929,11 +961,18 @@ kevent (struct work_struct *work)
int retval = 0;
clear_bit (EVENT_LINK_RESET, &dev->flags);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto skip_reset;
if(info->link_reset && (retval = info->link_reset(dev)) < 0) {
+ usb_autopm_put_interface(dev->intf);
+skip_reset:
devinfo(dev, "link reset failed (%d) usbnet usb-%s-%s, %s",
retval,
dev->udev->bus->bus_name, dev->udev->devpath,
info->description);
+ } else {
+ usb_autopm_put_interface(dev->intf);
}
}
@@ -971,6 +1010,7 @@ static void tx_complete (struct urb *urb)
case -EPROTO:
case -ETIME:
case -EILSEQ:
+ usb_mark_last_busy(dev->udev);
if (!timer_pending (&dev->delay)) {
mod_timer (&dev->delay,
jiffies + THROTTLE_JIFFIES);
@@ -987,6 +1027,7 @@ static void tx_complete (struct urb *urb)
}
}
+ usb_autopm_put_interface_async(dev->intf);
urb->dev = NULL;
entry->state = tx_done;
defer_bh(dev, skb, &dev->txq);
@@ -1057,14 +1098,34 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
}
}
- spin_lock_irqsave (&dev->txq.lock, flags);
+ spin_lock_irqsave(&dev->txq.lock, flags);
+ retval = usb_autopm_get_interface_async(dev->intf);
+ if (retval < 0) {
+ spin_unlock_irqrestore(&dev->txq.lock, flags);
+ goto drop;
+ }
+
+#ifdef CONFIG_PM
+ /* if this triggers the device is still a sleep */
+ if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) {
+ /* transmission will be done in resume */
+ usb_anchor_urb(urb, &dev->deferred);
+ /* no use to process more packets */
+ netif_stop_queue(net);
+ spin_unlock_irqrestore(&dev->txq.lock, flags);
+ devdbg(dev, "Delaying transmission for resumption");
+ goto deferred;
+ }
+#endif
switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
case -EPIPE:
netif_stop_queue (net);
usbnet_defer_kevent (dev, EVENT_TX_HALT);
+ usb_autopm_put_interface_async(dev->intf);
break;
default:
+ usb_autopm_put_interface_async(dev->intf);
if (netif_msg_tx_err (dev))
devdbg (dev, "tx: submit urb err %d", retval);
break;
@@ -1088,6 +1149,7 @@ drop:
devdbg (dev, "> tx, len %d, type 0x%x",
length, skb->protocol);
}
+deferred:
return NETDEV_TX_OK;
}
EXPORT_SYMBOL_GPL(usbnet_start_xmit);
@@ -1255,6 +1317,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
dev->bh.func = usbnet_bh;
dev->bh.data = (unsigned long) dev;
INIT_WORK (&dev->kevent, kevent);
+ init_usb_anchor(&dev->deferred);
dev->delay.function = usbnet_bh;
dev->delay.data = (unsigned long) dev;
init_timer (&dev->delay);
@@ -1363,13 +1426,23 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
struct usbnet *dev = usb_get_intfdata(intf);
if (!dev->suspend_count++) {
+ spin_lock_irq(&dev->txq.lock);
+ /* don't autosuspend while transmitting */
+ if (dev->txq.qlen && (message.event & PM_EVENT_AUTO)) {
+ spin_unlock_irq(&dev->txq.lock);
+ return -EBUSY;
+ } else {
+ set_bit(EVENT_DEV_ASLEEP, &dev->flags);
+ spin_unlock_irq(&dev->txq.lock);
+ }
/*
* accelerate emptying of the rx and queues, to avoid
* having everything error out.
*/
netif_device_detach (dev->net);
- (void) unlink_urbs (dev, &dev->rxq);
- (void) unlink_urbs (dev, &dev->txq);
+ usbnet_terminate_urbs(dev);
+ usb_kill_urb(dev->interrupt);
+
/*
* reattach so runtime management can use and
* wake the device
@@ -1383,10 +1456,34 @@ EXPORT_SYMBOL_GPL(usbnet_suspend);
int usbnet_resume (struct usb_interface *intf)
{
struct usbnet *dev = usb_get_intfdata(intf);
+ struct sk_buff *skb;
+ struct urb *res;
+ int retval;
+
+ if (!--dev->suspend_count) {
+ spin_lock_irq(&dev->txq.lock);
+ while ((res = usb_get_from_anchor(&dev->deferred))) {
+
+ printk(KERN_INFO"%s has delayed data\n", __func__);
+ skb = (struct sk_buff *)res->context;
+ retval = usb_submit_urb(res, GFP_ATOMIC);
+ if (retval < 0) {
+ dev_kfree_skb_any(skb);
+ usb_free_urb(res);
+ usb_autopm_put_interface_async(dev->intf);
+ } else {
+ dev->net->trans_start = jiffies;
+ __skb_queue_tail(&dev->txq, skb);
+ }
+ }
- if (!--dev->suspend_count)
+ smp_mb();
+ clear_bit(EVENT_DEV_ASLEEP, &dev->flags);
+ spin_unlock_irq(&dev->txq.lock);
+ if (!(dev->txq.qlen >= TX_QLEN(dev)))
+ netif_start_queue(dev->net);
tasklet_schedule (&dev->bh);
-
+ }
return 0;
}
EXPORT_SYMBOL_GPL(usbnet_resume);
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index f814730..aa174df 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -55,6 +55,7 @@ struct usbnet {
struct sk_buff_head done;
struct sk_buff_head rxq_pause;
struct urb *interrupt;
+ struct usb_anchor deferred;
struct tasklet_struct bh;
struct work_struct kevent;
@@ -65,6 +66,8 @@ struct usbnet {
# define EVENT_STS_SPLIT 3
# define EVENT_LINK_RESET 4
# define EVENT_RX_PAUSED 5
+# define EVENT_DEV_WAKING 6
+# define EVENT_DEV_ASLEEP 7
};
static inline struct usb_driver *driver_of(struct usb_interface *intf)
@@ -107,6 +110,9 @@ struct driver_info {
/* see if peer is connected ... can sleep */
int (*check_connect)(struct usbnet *);
+ /* (dis)activate runtime power management */
+ int (*manage_power)(struct usbnet *, int);
+
/* for status polling */
void (*status)(struct usbnet *, struct urb *);
^ permalink raw reply related [flat|nested] 16+ messages in thread
* RE: [patch]reset_resume for cdc-ether
[not found] ` <200912031056.17103.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
@ 2009-12-03 14:52 ` Torgny Johansson
0 siblings, 0 replies; 16+ messages in thread
From: Torgny Johansson @ 2009-12-03 14:52 UTC (permalink / raw)
To: Oliver Neukum, David Miller
Cc: david-b-yBeKhBN/0LDR7s880joybQ, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA
Tested-by: Torgny Johansson <torgny.johansson-IzeFyvvaP7pWk0Htik3J/w@public.gmane.org>
> -----Original Message-----
> From: netdev-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> [mailto:netdev-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Oliver Neukum
> Sent: den 3 december 2009 10:56
> To: David Miller
> Cc: david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org; netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
> linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Subject: Re: [patch]reset_resume for cdc-ether
>
> Am Donnerstag, 3. Dezember 2009 07:22:54 schrieb David Miller:
> > > I'm going to apply this patch and the USB autosuspend one too.
> >
> > Actually, neither patch applies to net-next-2.6
> >
> > Oliver can you respin your changes and add a proper signoff to the
> > autosuspend patch?
>
> Hi,
>
> here's autosuspend again.
>
> Regards
> Oliver
>
> Signed-off-by: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
>
> --
>
> commit 276405340ff17a79f6847174d1e737242df60cb5
> Author: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
> Date: Thu Dec 3 10:53:21 2009 +0100
>
> usbnet & cdc-ether: autosuspend for online devices
>
> using remote wakeup and delayed transmission to allow
> online device to go into usb autosuspend
> minimal alternate support for devices that don't support
> remote wakeup
>
> Signed-off-by: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
>
> diff --git a/drivers/net/usb/cdc_ether.c
> b/drivers/net/usb/cdc_ether.c index 71e65fc..b9493ef 100644
> --- a/drivers/net/usb/cdc_ether.c
> +++ b/drivers/net/usb/cdc_ether.c
> @@ -411,6 +411,12 @@ static int cdc_bind(struct usbnet *dev,
> struct usb_interface *intf)
> return 0;
> }
>
> +static int cdc_manage_power(struct usbnet *dev, int on) {
> + dev->intf->needs_remote_wakeup = on;
> + return 0;
> +}
> +
> static const struct driver_info cdc_info = {
> .description = "CDC Ethernet Device",
> .flags = FLAG_ETHER,
> @@ -418,6 +424,7 @@ static const struct driver_info cdc_info = {
> .bind = cdc_bind,
> .unbind = usbnet_cdc_unbind,
> .status = cdc_status,
> + .manage_power = cdc_manage_power,
> };
>
> static const struct driver_info mbm_info = { @@ -578,6
> +585,7 @@ static struct usb_driver cdc_driver = {
> .disconnect = usbnet_disconnect,
> .suspend = usbnet_suspend,
> .resume = usbnet_resume,
> + .supports_autosuspend = 1,
> };
>
>
> diff --git a/drivers/net/usb/usbnet.c
> b/drivers/net/usb/usbnet.c index 378da8c..83cdd46 100644
> --- a/drivers/net/usb/usbnet.c
> +++ b/drivers/net/usb/usbnet.c
> @@ -353,7 +353,8 @@ static void rx_submit (struct usbnet
> *dev, struct urb *urb, gfp_t flags)
>
> if (netif_running (dev->net)
> && netif_device_present (dev->net)
> - && !test_bit (EVENT_RX_HALT, &dev->flags)) {
> + && !test_bit (EVENT_RX_HALT, &dev->flags)
> + && !test_bit (EVENT_DEV_ASLEEP, &dev->flags)) {
> switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) {
> case -EPIPE:
> usbnet_defer_kevent (dev,
> EVENT_RX_HALT); @@ -611,15 +612,39 @@
> EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);
>
> /*------------------------------------------------------------
> -------------*/
>
> // precondition: never called in_interrupt
> +static void usbnet_terminate_urbs(struct usbnet *dev) {
> + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup);
> + DECLARE_WAITQUEUE(wait, current);
> + int temp;
> +
> + /* ensure there are no more active urbs */
> + add_wait_queue(&unlink_wakeup, &wait);
> + set_current_state(TASK_UNINTERRUPTIBLE);
> + dev->wait = &unlink_wakeup;
> + temp = unlink_urbs(dev, &dev->txq) +
> + unlink_urbs(dev, &dev->rxq);
> +
> + /* maybe wait for deletions to finish. */
> + while (!skb_queue_empty(&dev->rxq)
> + && !skb_queue_empty(&dev->txq)
> + && !skb_queue_empty(&dev->done)) {
> + schedule_timeout(UNLINK_TIMEOUT_MS);
> + set_current_state(TASK_UNINTERRUPTIBLE);
> + if (netif_msg_ifdown(dev))
> + devdbg(dev, "waited for %d urb
> completions",
> + temp);
> + }
> + set_current_state(TASK_RUNNING);
> + dev->wait = NULL;
> + remove_wait_queue(&unlink_wakeup, &wait); }
>
> int usbnet_stop (struct net_device *net) {
> struct usbnet *dev = netdev_priv(net);
> struct driver_info *info = dev->driver_info;
> - int temp;
> int retval;
> - DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup);
> - DECLARE_WAITQUEUE (wait, current);
>
> netif_stop_queue (net);
>
> @@ -641,25 +666,8 @@ int usbnet_stop (struct net_device *net)
> info->description);
> }
>
> - if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) {
> - /* ensure there are no more active urbs */
> - add_wait_queue(&unlink_wakeup, &wait);
> - dev->wait = &unlink_wakeup;
> - temp = unlink_urbs(dev, &dev->txq) +
> - unlink_urbs(dev, &dev->rxq);
> -
> - /* maybe wait for deletions to finish. */
> - while (!skb_queue_empty(&dev->rxq)
> - && !skb_queue_empty(&dev->txq)
> - && !skb_queue_empty(&dev->done)) {
> - msleep(UNLINK_TIMEOUT_MS);
> - if (netif_msg_ifdown(dev))
> - devdbg(dev, "waited for %d urb
> completions",
> - temp);
> - }
> - dev->wait = NULL;
> - remove_wait_queue(&unlink_wakeup, &wait);
> - }
> + if (!(info->flags & FLAG_AVOID_UNLINK_URBS))
> + usbnet_terminate_urbs(dev);
>
> usb_kill_urb(dev->interrupt);
>
> @@ -672,7 +680,10 @@ int usbnet_stop (struct net_device *net)
> dev->flags = 0;
> del_timer_sync (&dev->delay);
> tasklet_kill (&dev->bh);
> - usb_autopm_put_interface(dev->intf);
> + if (info->manage_power)
> + info->manage_power(dev, 0);
> + else
> + usb_autopm_put_interface(dev->intf);
>
> return 0;
> }
> @@ -753,6 +764,12 @@ int usbnet_open (struct net_device *net)
>
> // delay posting reads until we're fully open
> tasklet_schedule (&dev->bh);
> + if (info->manage_power) {
> + retval = info->manage_power(dev, 1);
> + if (retval < 0)
> + goto done;
> + usb_autopm_put_interface(dev->intf);
> + }
> return retval;
> done:
> usb_autopm_put_interface(dev->intf);
> @@ -881,11 +898,16 @@ kevent (struct work_struct *work)
> /* usb_clear_halt() needs a thread context */
> if (test_bit (EVENT_TX_HALT, &dev->flags)) {
> unlink_urbs (dev, &dev->txq);
> + status = usb_autopm_get_interface(dev->intf);
> + if (status < 0)
> + goto fail_pipe;
> status = usb_clear_halt (dev->udev, dev->out);
> + usb_autopm_put_interface(dev->intf);
> if (status < 0
> && status != -EPIPE
> && status != -ESHUTDOWN) {
> if (netif_msg_tx_err (dev))
> +fail_pipe:
> deverr (dev, "can't clear tx
> halt, status %d",
> status);
> } else {
> @@ -896,11 +918,16 @@ kevent (struct work_struct *work)
> }
> if (test_bit (EVENT_RX_HALT, &dev->flags)) {
> unlink_urbs (dev, &dev->rxq);
> + status = usb_autopm_get_interface(dev->intf);
> + if (status < 0)
> + goto fail_halt;
> status = usb_clear_halt (dev->udev, dev->in);
> + usb_autopm_put_interface(dev->intf);
> if (status < 0
> && status != -EPIPE
> && status != -ESHUTDOWN) {
> if (netif_msg_rx_err (dev))
> +fail_halt:
> deverr (dev, "can't clear rx
> halt, status %d",
> status);
> } else {
> @@ -919,7 +946,12 @@ kevent (struct work_struct *work)
> clear_bit (EVENT_RX_MEMORY, &dev->flags);
> if (urb != NULL) {
> clear_bit (EVENT_RX_MEMORY, &dev->flags);
> + status = usb_autopm_get_interface(dev->intf);
> + if (status < 0)
> + goto fail_lowmem;
> rx_submit (dev, urb, GFP_KERNEL);
> + usb_autopm_put_interface(dev->intf);
> +fail_lowmem:
> tasklet_schedule (&dev->bh);
> }
> }
> @@ -929,11 +961,18 @@ kevent (struct work_struct *work)
> int retval = 0;
>
> clear_bit (EVENT_LINK_RESET, &dev->flags);
> + status = usb_autopm_get_interface(dev->intf);
> + if (status < 0)
> + goto skip_reset;
> if(info->link_reset && (retval =
> info->link_reset(dev)) < 0) {
> + usb_autopm_put_interface(dev->intf);
> +skip_reset:
> devinfo(dev, "link reset failed (%d)
> usbnet usb-%s-%s, %s",
> retval,
> dev->udev->bus->bus_name,
> dev->udev->devpath,
> info->description);
> + } else {
> + usb_autopm_put_interface(dev->intf);
> }
> }
>
> @@ -971,6 +1010,7 @@ static void tx_complete (struct urb *urb)
> case -EPROTO:
> case -ETIME:
> case -EILSEQ:
> + usb_mark_last_busy(dev->udev);
> if (!timer_pending (&dev->delay)) {
> mod_timer (&dev->delay,
> jiffies + THROTTLE_JIFFIES);
> @@ -987,6 +1027,7 @@ static void tx_complete (struct urb *urb)
> }
> }
>
> + usb_autopm_put_interface_async(dev->intf);
> urb->dev = NULL;
> entry->state = tx_done;
> defer_bh(dev, skb, &dev->txq);
> @@ -1057,14 +1098,34 @@ netdev_tx_t usbnet_start_xmit (struct
> sk_buff *skb,
> }
> }
>
> - spin_lock_irqsave (&dev->txq.lock, flags);
> + spin_lock_irqsave(&dev->txq.lock, flags);
> + retval = usb_autopm_get_interface_async(dev->intf);
> + if (retval < 0) {
> + spin_unlock_irqrestore(&dev->txq.lock, flags);
> + goto drop;
> + }
> +
> +#ifdef CONFIG_PM
> + /* if this triggers the device is still a sleep */
> + if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) {
> + /* transmission will be done in resume */
> + usb_anchor_urb(urb, &dev->deferred);
> + /* no use to process more packets */
> + netif_stop_queue(net);
> + spin_unlock_irqrestore(&dev->txq.lock, flags);
> + devdbg(dev, "Delaying transmission for resumption");
> + goto deferred;
> + }
> +#endif
>
> switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
> case -EPIPE:
> netif_stop_queue (net);
> usbnet_defer_kevent (dev, EVENT_TX_HALT);
> + usb_autopm_put_interface_async(dev->intf);
> break;
> default:
> + usb_autopm_put_interface_async(dev->intf);
> if (netif_msg_tx_err (dev))
> devdbg (dev, "tx: submit urb err %d", retval);
> break;
> @@ -1088,6 +1149,7 @@ drop:
> devdbg (dev, "> tx, len %d, type 0x%x",
> length, skb->protocol);
> }
> +deferred:
> return NETDEV_TX_OK;
> }
> EXPORT_SYMBOL_GPL(usbnet_start_xmit);
> @@ -1263,6 +1325,7 @@ usbnet_probe (struct usb_interface
> *udev, const struct usb_device_id *prod)
> dev->bh.func = usbnet_bh;
> dev->bh.data = (unsigned long) dev;
> INIT_WORK (&dev->kevent, kevent);
> + init_usb_anchor(&dev->deferred);
> dev->delay.function = usbnet_bh;
> dev->delay.data = (unsigned long) dev;
> init_timer (&dev->delay);
> @@ -1380,13 +1443,23 @@ int usbnet_suspend (struct
> usb_interface *intf, pm_message_t message)
> struct usbnet *dev = usb_get_intfdata(intf);
>
> if (!dev->suspend_count++) {
> + spin_lock_irq(&dev->txq.lock);
> + /* don't autosuspend while transmitting */
> + if (dev->txq.qlen && (message.event & PM_EVENT_AUTO)) {
> + spin_unlock_irq(&dev->txq.lock);
> + return -EBUSY;
> + } else {
> + set_bit(EVENT_DEV_ASLEEP, &dev->flags);
> + spin_unlock_irq(&dev->txq.lock);
> + }
> /*
> * accelerate emptying of the rx and queues, to avoid
> * having everything error out.
> */
> netif_device_detach (dev->net);
> - (void) unlink_urbs (dev, &dev->rxq);
> - (void) unlink_urbs (dev, &dev->txq);
> + usbnet_terminate_urbs(dev);
> + usb_kill_urb(dev->interrupt);
> +
> /*
> * reattach so runtime management can use and
> * wake the device
> @@ -1400,10 +1473,34 @@ EXPORT_SYMBOL_GPL(usbnet_suspend);
> int usbnet_resume (struct usb_interface *intf) {
> struct usbnet *dev = usb_get_intfdata(intf);
> + struct sk_buff *skb;
> + struct urb *res;
> + int retval;
> +
> + if (!--dev->suspend_count) {
> + spin_lock_irq(&dev->txq.lock);
> + while ((res = usb_get_from_anchor(&dev->deferred))) {
> +
> + printk(KERN_INFO"%s has delayed
> data\n", __func__);
> + skb = (struct sk_buff *)res->context;
> + retval = usb_submit_urb(res, GFP_ATOMIC);
> + if (retval < 0) {
> + dev_kfree_skb_any(skb);
> + usb_free_urb(res);
> +
> usb_autopm_put_interface_async(dev->intf);
> + } else {
> + dev->net->trans_start = jiffies;
> + __skb_queue_tail(&dev->txq, skb);
> + }
> + }
>
> - if (!--dev->suspend_count)
> + smp_mb();
> + clear_bit(EVENT_DEV_ASLEEP, &dev->flags);
> + spin_unlock_irq(&dev->txq.lock);
> + if (!(dev->txq.qlen >= TX_QLEN(dev)))
> + netif_start_queue(dev->net);
> tasklet_schedule (&dev->bh);
> -
> + }
> return 0;
> }
> EXPORT_SYMBOL_GPL(usbnet_resume);
> diff --git a/include/linux/usb/usbnet.h
> b/include/linux/usb/usbnet.h index 86c31b7..a7b92d6 100644
> --- a/include/linux/usb/usbnet.h
> +++ b/include/linux/usb/usbnet.h
> @@ -55,6 +55,7 @@ struct usbnet {
> struct sk_buff_head done;
> struct sk_buff_head rxq_pause;
> struct urb *interrupt;
> + struct usb_anchor deferred;
> struct tasklet_struct bh;
>
> struct work_struct kevent;
> @@ -65,6 +66,8 @@ struct usbnet {
> # define EVENT_STS_SPLIT 3
> # define EVENT_LINK_RESET 4
> # define EVENT_RX_PAUSED 5
> +# define EVENT_DEV_WAKING 6
> +# define EVENT_DEV_ASLEEP 7
> };
>
> static inline struct usb_driver *driver_of(struct
> usb_interface *intf) @@ -108,6 +111,9 @@ struct driver_info {
> /* see if peer is connected ... can sleep */
> int (*check_connect)(struct usbnet *);
>
> + /* (dis)activate runtime power management */
> + int (*manage_power)(struct usbnet *, int);
> +
> /* for status polling */
> void (*status)(struct usbnet *, struct urb *);
>
> --
> To unsubscribe from this list: send the line "unsubscribe
> netdev" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
2009-12-03 13:02 ` Oliver Neukum
@ 2009-12-03 20:31 ` David Miller
[not found] ` <20091203.123150.182603807.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
0 siblings, 1 reply; 16+ messages in thread
From: David Miller @ 2009-12-03 20:31 UTC (permalink / raw)
To: oliver; +Cc: torgny.johansson, david-b, netdev, linux-usb
From: Oliver Neukum <oliver@neukum.org>
Date: Thu, 3 Dec 2009 14:02:48 +0100
> Am Donnerstag, 3. Dezember 2009 11:14:56 schrieb David Miller:
>
>> It doesn't apply to my tree still:
>
> And this is autosuspend.
Oliver, when you send two patches to the same files you
have to make it so that when the first one is applied
the second one applies cleanly on top. That's not what
happens here:
davem@sunset:~/src/GIT/net-next-2.6$ git am --signoff bundle-732.mbox
Applying: cdc-ether: Implement reset_resume()
Applying: usbnet & cdc-ether: autosuspend for online devices
error: patch failed: drivers/net/usb/cdc_ether.c:411
error: drivers/net/usb/cdc_ether.c: patch does not apply
Patch failed at 0002 usbnet & cdc-ether: autosuspend for online devices
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".
Also, please compose your patch emails correctly, I always have
to edit them.
The commit message header line should be your subject (with
something like "[PATCH 01/02] " or similar prefixed).
The body of your email should be the commit message contents
and then the patch.
If you want to make some extra comments that are not meant to
go into the commit message, put a line with just "---" after
the commit message (and before the patch), and then add what
you want to say.
Finally, please capitalize and properly spell words when appropriate.
F.e. "imlement" should be "Implement" in the commit header line of the
reset_resume() commit.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
[not found] ` <20091203.123150.182603807.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
@ 2009-12-03 21:11 ` Oliver Neukum
2009-12-03 21:12 ` David Miller
0 siblings, 1 reply; 16+ messages in thread
From: Oliver Neukum @ 2009-12-03 21:11 UTC (permalink / raw)
To: David Miller
Cc: torgny.johansson-IzeFyvvaP7pWk0Htik3J/w,
david-b-yBeKhBN/0LDR7s880joybQ, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA
Am Donnerstag, 3. Dezember 2009 21:31:50 schrieb David Miller:
> The body of your email should be the commit message contents
> and then the patch.
>
> If you want to make some extra comments that are not meant to
> go into the commit message, put a line with just "---" after
> the commit message (and before the patch), and then add what
> you want to say.
>
> Finally, please capitalize and properly spell words when appropriate.
> F.e. "imlement" should be "Implement" in the commit header line of the
> reset_resume() commit.
>
Sorry, I hope the versions I just sent you are OK.
Regards
Oliver
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch]reset_resume for cdc-ether
2009-12-03 21:11 ` Oliver Neukum
@ 2009-12-03 21:12 ` David Miller
0 siblings, 0 replies; 16+ messages in thread
From: David Miller @ 2009-12-03 21:12 UTC (permalink / raw)
To: oliver; +Cc: torgny.johansson, david-b, netdev, linux-usb
From: Oliver Neukum <oliver@neukum.org>
Date: Thu, 3 Dec 2009 22:11:34 +0100
> Sorry, I hope the versions I just sent you are OK.
Thanks for fixing things up, I'll take a look right now.
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2009-12-03 21:12 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-11-24 10:59 [patch]reset_resume for cdc-ether Oliver Neukum
2009-11-29 8:18 ` David Miller
2009-12-03 6:16 ` David Miller
[not found] ` <20091202.221627.133392199.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
2009-12-03 6:22 ` David Miller
[not found] ` <20091202.222254.61468873.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
2009-12-03 9:56 ` Oliver Neukum
2009-12-03 10:14 ` David Miller
2009-12-03 10:26 ` Oliver Neukum
2009-12-03 10:33 ` David Miller
2009-12-03 13:01 ` Oliver Neukum
2009-12-03 13:02 ` Oliver Neukum
2009-12-03 20:31 ` David Miller
[not found] ` <20091203.123150.182603807.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
2009-12-03 21:11 ` Oliver Neukum
2009-12-03 21:12 ` David Miller
[not found] ` <200912031056.17103.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2009-12-03 14:52 ` Torgny Johansson
2009-12-03 10:02 ` Oliver Neukum
[not found] ` <200912031102.56903.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2009-12-03 10:15 ` David Miller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox