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(®ister_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