Re: usbaudio autosuspend status

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

 



Am Montag, 5. April 2010 16:32:40 schrieb William Lovaton:
> --- El lun 5-abr-10, Oliver Neukum <oliver@xxxxxxxxxx> escribió:
> 
> > > The support is not present in the current git
> > > tree.  Apparently it was never merged.
> > 
> > This is most unfortunate. The patch is so old that I don't
> > have it on this
> > laptop. I'll search the old files on the desktop. I'll port
> > this to a current kernel.
> 
> I'm willing to test the patch if you want.  I hope the new patch fits nicely into Fedora 13 Rawhide kernel.
> 
> Thanks for your help.

Very well, here's the first version of the patch ported to the current kernel.
At least it compiles.
Usbmidi will crash with this version.

	Regards
		Oliver

commit b061e9c3560aab547ef763aca8d6783de32a0e5b
Author: Oliver Neukum <oliver@xxxxxxxxxx>
Date:   Mon Apr 5 17:49:28 2010 +0200

    USB: audio: autosuspend
    
    FIrst port to a modern kernel
    
    Signed-off-by: Oliver Neukum <oliver@xxxxxxxxxx>

diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 11b0826..bee502f 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -247,6 +247,25 @@ 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;
+
+	mutex_lock(&register_mutex);
+	if (!chip->shutdown && !chip->probing)
+		err = usb_autopm_get_interface(chip->pm_intf);
+	mutex_unlock(&register_mutex);
+
+	return err;
+}
+
+void snd_usb_autosuspend(struct snd_usb_audio *chip)
+{
+	mutex_lock(&register_mutex);
+	if (!chip->shutdown && !chip->probing)
+		usb_autopm_put_interface(chip->pm_intf);
+	mutex_unlock(&register_mutex);
+}
 
 /*
  * prepare urb for full speed capture sync pipe
@@ -1896,6 +1915,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 +1935,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 +1959,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 +1994,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 +2162,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 +3765,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 +3903,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
 				goto __error;
 			}
 			chip = usb_chip[i];
+			chip->probing = 1;
 			break;
 		}
 	}
@@ -3888,6 +3919,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 +3951,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 +4034,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 +4063,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,
--
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