Re: usbaudio autosuspend status

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Am Montag, 12. April 2010 04:26:11 schrieb William Lovaton:
> Opss, I forgot about that.
> 
> Blacklisting the driver (snd_usb_audio) makes the system boot normally with the patched kernel.  The curious thing is that /sys/bus/usb/devices/1-5 is still 100% active according to powertop.
> 

Hi,

can you please try the included patch.

	Regards
		Oliver

>From a690c498c82c75a56fb8d4c9d97aeef954022e84 Mon Sep 17 00:00:00 2001
From: Oliver Neukum <oliver@xxxxxxxxxx>
Date: Wed, 21 Apr 2010 19:30:54 +0200
Subject: [PATCH] USB: usbaudio: implemt autosuspend

This does autosuspend for active devices

Signed-off-by: Oliver Neukum <oneukum@xxxxxxx>
---
 sound/usb/usbaudio.c |   71 +++++++++++++++++++++++++++++++++++++++----------
 sound/usb/usbaudio.h |    7 ++++-
 sound/usb/usbmidi.c  |   30 +++++++++++++++++++--
 sound/usb/usbmixer.c |   31 +++++++++++++++++++---
 4 files changed, 116 insertions(+), 23 deletions(-)

diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 11b0826..6edffb6 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -247,6 +247,21 @@ static inline unsigned get_high_speed_hz(unsigned int usb_rate)
 	return (usb_rate * 125 + (1 << 9)) >> 10;
 }
 
+int snd_usb_autoresume(struct snd_usb_audio *chip)
+{
+	int err = -ENODEV;
+
+	if (!chip->shutdown && !chip->probing)
+		err = usb_autopm_get_interface(chip->pm_intf);
+
+	return err;
+}
+
+void snd_usb_autosuspend(struct snd_usb_audio *chip)
+{
+	if (!chip->shutdown && !chip->probing)
+		usb_autopm_put_interface(chip->pm_intf);
+}
 
 /*
  * prepare urb for full speed capture sync pipe
@@ -1896,6 +1911,10 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
 		ptmin = min(ptmin, pt);
 	}
 
+	err = snd_usb_autoresume(subs->stream->chip);
+	if (err < 0)
+		return err;
+
 	param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
 	if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH)
 		/* full speed devices have fixed data packet interval */
@@ -1912,21 +1931,21 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
 				       SNDRV_PCM_HW_PARAM_CHANNELS,
 				       param_period_time_if_needed,
 				       -1)) < 0)
-		return err;
+		goto rep_err;
 	if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 				       hw_rule_channels, subs,
 				       SNDRV_PCM_HW_PARAM_FORMAT,
 				       SNDRV_PCM_HW_PARAM_RATE,
 				       param_period_time_if_needed,
 				       -1)) < 0)
-		return err;
+		goto rep_err;
 	if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
 				       hw_rule_format, subs,
 				       SNDRV_PCM_HW_PARAM_RATE,
 				       SNDRV_PCM_HW_PARAM_CHANNELS,
 				       param_period_time_if_needed,
 				       -1)) < 0)
-		return err;
+		goto rep_err;
 	if (param_period_time_if_needed >= 0) {
 		err = snd_pcm_hw_rule_add(runtime, 0,
 					  SNDRV_PCM_HW_PARAM_PERIOD_TIME,
@@ -1936,11 +1955,15 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
 					  SNDRV_PCM_HW_PARAM_RATE,
 					  -1);
 		if (err < 0)
-			return err;
+			goto rep_err;
 	}
 	if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
-		return err;
+		goto rep_err;
 	return 0;
+
+rep_err:
+	snd_usb_autosuspend(subs->stream->chip);
+	return err;
 }
 
 static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
@@ -1967,6 +1990,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
 		subs->interface = -1;
 	}
 	subs->pcm_substream = NULL;
+	snd_usb_autosuspend(subs->stream->chip);
 	return 0;
 }
 
@@ -2134,6 +2158,7 @@ static struct usb_driver usb_audio_driver = {
 	.suspend =	usb_audio_suspend,
 	.resume =	usb_audio_resume,
 	.id_table =	usb_audio_ids,
+	.supports_autosuspend = 1,
 };
 
 
@@ -3736,6 +3761,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
 	chip->index = idx;
 	chip->dev = dev;
 	chip->card = card;
+	chip->probing = 1;
 	chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
 			      le16_to_cpu(dev->descriptor.idProduct));
 	INIT_LIST_HEAD(&chip->pcm_list);
@@ -3873,6 +3899,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
 				goto __error;
 			}
 			chip = usb_chip[i];
+			chip->probing = 1;
 			break;
 		}
 	}
@@ -3888,6 +3915,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
 					goto __error;
 				}
 				snd_card_set_dev(chip->card, &intf->dev);
+				chip->pm_intf = intf;
 				break;
 			}
 		if (!chip) {
@@ -3919,6 +3947,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
 
 	usb_chip[chip->index] = chip;
 	chip->num_interfaces++;
+	chip->probing = 0;
 	mutex_unlock(&register_mutex);
 	return chip;
 
@@ -4001,14 +4030,22 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
 	if (chip == (void *)-1L)
 		return 0;
 
-	snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
-	if (!chip->num_suspended_intf++) {
-		list_for_each(p, &chip->pcm_list) {
-			as = list_entry(p, struct snd_usb_stream, list);
-			snd_pcm_suspend_all(as->pcm);
-		}
+	if (!(message.event & PM_EVENT_AUTO)) {
+		snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+		if (!chip->num_suspended_intf++) {
+			list_for_each(p, &chip->pcm_list) {
+				as = list_entry(p, struct snd_usb_stream, list);
+				snd_pcm_suspend_all(as->pcm);
+			}
+ 		}
+	} else {
+		/*
+		 * otherwise we keep the rest of the system in the dark
+		 * to keep this transparent
+		 */
+		if (!chip->num_suspended_intf++)
+			chip->autosuspended = 1;
 	}
-
 	return 0;
 }
 
@@ -4022,10 +4059,14 @@ static int usb_audio_resume(struct usb_interface *intf)
 		return 0;
 	/*
 	 * ALSA leaves material resumption to user space
-	 * we just notify
+	 * we just notify - if the whole system resumes
+	 * if we autoresume we keep quiet to keep this
+	 * transition transparent
 	 */
-
-	snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+	if (!chip->autosuspended)
+		snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+	else
+		chip->autosuspended = 0;
 
 	return 0;
 }
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 42c299c..f659d1f 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -36,8 +36,11 @@ struct snd_usb_audio {
 	int index;
 	struct usb_device *dev;
 	struct snd_card *card;
+	struct usb_interface *pm_intf;
 	u32 usb_id;
-	int shutdown;
+	int shutdown:1;
+	int probing:1;
+	int autosuspended:1;
 	unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
 	int num_interfaces;
 	int num_suspended_intf;
@@ -159,6 +162,8 @@ int snd_usbmidi_create(struct snd_card *card,
 void snd_usbmidi_input_stop(struct list_head* p);
 void snd_usbmidi_input_start(struct list_head* p);
 void snd_usbmidi_disconnect(struct list_head *p);
+int snd_usb_autoresume(struct snd_usb_audio *chip);
+void snd_usb_autosuspend(struct snd_usb_audio *chip);
 
 void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
 			unsigned char samplerate_id);
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 2c59afd..c04c4b6 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -45,6 +45,7 @@
 #include <linux/slab.h>
 #include <linux/timer.h>
 #include <linux/usb.h>
+#include <linux/mutex.h>
 #include <linux/wait.h>
 #include <linux/usb/audio.h>
 
@@ -113,6 +114,8 @@ struct snd_usb_midi {
 	struct list_head list;
 	struct timer_list error_timer;
 	spinlock_t disc_lock;
+	struct mutex disc_mut;
+	
 	struct mutex mutex;
 	u32 usb_id;
 	int next_midi_device;
@@ -171,6 +174,7 @@ struct snd_usb_midi_in_endpoint {
 	u8 seen_f5;
 	u8 error_resubmit;
 	int current_port;
+	int open_substreams;
 };
 
 static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep);
@@ -190,6 +194,14 @@ static int snd_usbmidi_submit_urb(struct urb* urb, gfp_t flags)
 	return err;
 }
 
+static void snd_usbmidi_autosuspend(struct snd_usb_midi* umidi)
+{
+	mutex_lock(&umidi->disc_mut);
+//	if (!umidi->chip->shutdown)
+//		usb_autopm_put_interface(umidi->iface);
+	mutex_unlock(&umidi->disc_mut);
+}
+
 /*
  * Error handling for URB completion functions.
  */
@@ -265,8 +277,12 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb)
 		}
 	}
 
-	urb->dev = ep->umidi->dev;
-	snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
+	spin_lock(&ep->umidi->disc_lock);
+//	if (ep->open_substreams) {
+//		urb->dev = ep->umidi->chip->dev;
+//		snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
+//	}
+	spin_unlock(&ep->umidi->disc_lock);
 }
 
 static void snd_usbmidi_out_urb_complete(struct urb* urb)
@@ -936,8 +952,11 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
 {
 	struct snd_usb_midi* umidi = substream->rmidi->private_data;
 	struct usbmidi_out_port* port = NULL;
-	int i, j;
+	int i, j, err;
 
+	err = usb_autopm_get_interface(umidi->iface);
+	if (err < 0)
+		return -EIO;
 	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
 		if (umidi->endpoints[i].out)
 			for (j = 0; j < 0x10; ++j)
@@ -957,6 +976,9 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
 
 static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
 {
+	struct snd_usb_midi* umidi = substream->rmidi->private_data;
+
+	snd_usbmidi_autosuspend(umidi);
 	substream_open(substream, 0);
 	return 0;
 }
@@ -1250,9 +1272,11 @@ void snd_usbmidi_disconnect(struct list_head* p)
 	 * a timer may submit an URB. To reliably break the cycle
 	 * a flag under lock must be used
 	 */
+	mutex_lock(&umidi->disc_mut);
 	spin_lock_irq(&umidi->disc_lock);
 	umidi->disconnected = 1;
 	spin_unlock_irq(&umidi->disc_lock);
+	mutex_unlock(&umidi->disc_mut);
 	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
 		struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
 		if (ep->out)
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 8e8f871..e39564b 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -388,7 +388,11 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali
 	unsigned char buf[2];
 	int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
 	int timeout = 10;
+	int err;
 
+	err = snd_usb_autoresume(cval->mixer->chip);
+	if (err < 0)
+		return -EIO;
 	while (timeout-- > 0) {
 		if (snd_usb_ctl_msg(cval->mixer->chip->dev,
 				    usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
@@ -397,9 +401,11 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali
 				    validx, cval->mixer->ctrlif | (cval->id << 8),
 				    buf, val_len, 100) >= val_len) {
 			*value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
+			snd_usb_autosuspend(cval->mixer->chip);
 			return 0;
 		}
 	}
+	snd_usb_autosuspend(cval->mixer->chip);
 	snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
 		    request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
 	return -EINVAL;
@@ -2076,16 +2082,28 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
 	int value = ucontrol->value.integer.value[0];
 	int err, changed;
 
-	if (value > 1)
-		return -EINVAL;
+	if (value > 1) {
+		changed = -EINVAL;
+		goto err_out;
+	}
 	changed = value != mixer->audigy2nx_leds[index];
+	err = snd_usb_autoresume(mixer->chip);
+	if (err < 0) {
+		changed = -EIO;
+		goto err_out;
+	}
 	err = snd_usb_ctl_msg(mixer->chip->dev,
 			      usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
 			      value, index + 2, NULL, 0, 100);
-	if (err < 0)
-		return err;
+	if (err < 0) {
+		changed = err;
+		goto err;
+	}
 	mixer->audigy2nx_leds[index] = value;
+err:
+	snd_usb_autosuspend(mixer->chip);
+err_out:
 	return changed;
 }
 
@@ -2166,6 +2184,10 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
 	else
 		return;
 
+	err = snd_usb_autoresume(mixer->chip);
+	if (err < 0)
+		return;
+
 	for (i = 0; jacks[i].name; ++i) {
 		snd_iprintf(buffer, "%s: ", jacks[i].name);
 		err = snd_usb_ctl_msg(mixer->chip->dev,
@@ -2178,6 +2200,7 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
 		else
 			snd_iprintf(buffer, "?\n");
 	}
+	snd_usb_autosuspend(mixer->chip);
 }
 
 static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
-- 
1.6.4.2

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux