Hi,

this is a first implementation of autosuspend for USB audio devices.
It works for me, but my device is rather rudimentary. So I am looking
for testers. This patch is against 2.6.24-rc5 with the suspend/resume
patch for usb audio (attached for your convinience)

        Happy Testing
                Oliver

----

--- linux-2.6.24-rc5-audio/sound/usb/usbaudio.h 2007-12-14 15:00:39.000000000 
+0100
+++ linux-2.6.24-rc5-work/sound/usb/usbaudio.h  2007-12-14 12:50:20.000000000 
+0100
@@ -123,6 +123,7 @@ 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 num_interfaces;
--- linux-2.6.24-rc5-audio/sound/usb/usbaudio.c 2007-12-14 15:00:39.000000000 
+0100
+++ linux-2.6.24-rc5-work/sound/usb/usbaudio.c  2007-12-14 15:13:18.000000000 
+0100
@@ -1880,6 +1880,10 @@ static int setup_hw_info(struct snd_pcm_
                }
        }
 
+       err = usb_autopm_get_interface(subs->stream->chip->pm_intf);
+       if (err < 0)
+               return err;
+
        /* set the period time minimum 1ms */
        /* FIXME: high-speed mode allows 125us minimum period, but many parts
         * in the current code assume the 1ms period.
@@ -1895,23 +1899,27 @@ static int setup_hw_info(struct snd_pcm_
                                               SNDRV_PCM_HW_PARAM_FORMAT,
                                               SNDRV_PCM_HW_PARAM_CHANNELS,
                                               -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,
                                               -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,
                                               -1)) < 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:
+       usb_autopm_put_interface(subs->stream->chip->pm_intf);
+       return err;
 }
 
 static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
@@ -1938,6 +1946,7 @@ static int snd_usb_pcm_close(struct snd_
                subs->interface = -1;
        }
        subs->pcm_substream = NULL;
+       usb_autopm_put_interface(subs->stream->chip->pm_intf);
        return 0;
 }
 
@@ -2097,6 +2106,7 @@ static struct usb_driver usb_audio_drive
        .suspend =      usb_audio_suspend,
        .resume =       usb_audio_resume,
        .id_table =     usb_audio_ids,
+       .supports_autosuspend = 1,
 };
 
 
@@ -3557,6 +3567,7 @@ static void *snd_usb_audio_probe(struct 
                                        goto __error;
                                }
                                snd_card_set_dev(chip->card, &intf->dev);
+                               chip->pm_intf = intf;
                                break;
                        }
                if (! chip) {
@@ -3661,26 +3672,38 @@ static void usb_audio_disconnect(struct 
 static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev);
+       struct usb_device *udev = interface_to_usbdev(intf);
        struct list_head *p;
        struct snd_usb_stream *as;
 
        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 (!udev->auto_pm) {
+               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
+                */
+               chip->num_suspended_intf++;
        }
 
+
        return 0;
 }
 
 static int usb_audio_resume(struct usb_interface *intf)
 {
        struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev);
+       struct usb_device *udev = interface_to_usbdev(intf);
 
        if (chip == (void *)-1L)
                return 0;
@@ -3688,10 +3711,13 @@ static int usb_audio_resume(struct usb_i
                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 (!udev->auto_pm)
+               snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
 
        return 0;
 }
--- linux-2.6.24-rc5-audio/sound/usb/usbmixer.c 2007-12-14 14:59:21.000000000 
+0100
+++ linux-2.6.24-rc5-work/sound/usb/usbmixer.c  2007-12-14 15:51:05.000000000 
+0100
@@ -353,7 +353,11 @@ static int get_ctl_value(struct usb_mixe
        unsigned char buf[2];
        int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
        int timeout = 10;
+       int err;
 
+       err = usb_autopm_get_interface(cval->mixer->chip->pm_intf);
+       if (err < 0)
+               return -EIO;
        while (timeout-- > 0) {
                if (snd_usb_ctl_msg(cval->mixer->chip->dev,
                                    usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
@@ -362,9 +366,11 @@ static int get_ctl_value(struct usb_mixe
                                    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));
+                       usb_autopm_put_interface(cval->mixer->chip->pm_intf);
                        return 0;
                }
        }
+       usb_autopm_put_interface(cval->mixer->chip->pm_intf);
        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;
@@ -390,18 +396,27 @@ static int set_ctl_value(struct usb_mixe
        unsigned char buf[2];
        int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
        int timeout = 10;
+       int err;
 
        value_set = convert_bytes_value(cval, value_set);
        buf[0] = value_set & 0xff;
        buf[1] = (value_set >> 8) & 0xff;
-       while (timeout -- > 0)
+       err = usb_autopm_get_interface(cval->mixer->chip->pm_intf);
+       if (err < 0)
+               return -EIO;
+
+       while (timeout -- > 0) {
                if (snd_usb_ctl_msg(cval->mixer->chip->dev,
                                    usb_sndctrlpipe(cval->mixer->chip->dev, 0),
                                    request,
                                    USB_RECIP_INTERFACE | USB_TYPE_CLASS | 
USB_DIR_OUT,
                                    validx, cval->mixer->ctrlif | (cval->id << 
8),
-                                   buf, val_len, 100) >= 0)
+                                   buf, val_len, 100) >= 0) {
+                       usb_autopm_put_interface(cval->mixer->chip->pm_intf);
                        return 0;
+                       }
+       }
+       usb_autopm_put_interface(cval->mixer->chip->pm_intf);
        snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, 
wIndex = %#x, type = %d, data = %#x/%#x\n",
                    request, validx, cval->mixer->ctrlif | (cval->id << 8), 
cval->val_type, buf[0], buf[1]);
        return -EINVAL;
@@ -1906,16 +1921,28 @@ static int snd_audigy2nx_led_put(struct 
        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 = usb_autopm_get_interface(mixer->chip->pm_intf);
+       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:
+       usb_autopm_put_interface(mixer->chip->pm_intf);
+err_out:
        return changed;
 }
 
@@ -1977,6 +2004,9 @@ static void snd_audigy2nx_proc_read(stru
        u8 buf[3];
 
        snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
+       err = usb_autopm_get_interface(mixer->chip->pm_intf);
+       if (err < 0)
+               return;
        for (i = 0; i < ARRAY_SIZE(jacks); ++i) {
                snd_iprintf(buffer, "%s: ", jacks[i].name);
                err = snd_usb_ctl_msg(mixer->chip->dev,
@@ -1989,6 +2019,7 @@ static void snd_audigy2nx_proc_read(stru
                else
                        snd_iprintf(buffer, "?\n");
        }
+       usb_autopm_put_interface(mixer->chip->pm_intf);
 }
 
 int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif)
--- a/sound/usb/usbaudio.h	2007-08-31 12:27:57.000000000 +0200
+++ b/sound/usb/usbaudio.h	2007-08-31 12:29:55.000000000 +0200
@@ -126,6 +126,7 @@ struct snd_usb_audio {
 	u32 usb_id;
 	int shutdown;
 	int num_interfaces;
+	int num_suspended_intf;
 
 	struct list_head pcm_list;	/* list of pcm streams */
 	int pcm_devs;
--- a/sound/usb/usbaudio.c	2007-08-31 12:21:21.000000000 +0200
+++ b/sound/usb/usbaudio.c	2007-08-31 15:32:09.000000000 +0200
@@ -2078,6 +2078,8 @@ int snd_usb_ctl_msg(struct usb_device *d
 static int usb_audio_probe(struct usb_interface *intf,
 			   const struct usb_device_id *id);
 static void usb_audio_disconnect(struct usb_interface *intf);
+static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message);
+static int usb_audio_resume(struct usb_interface *intf);
 
 static struct usb_device_id usb_audio_ids [] = {
 #include "usbquirks.h"
@@ -2093,6 +2095,8 @@ static struct usb_driver usb_audio_drive
 	.name =		"snd-usb-audio",
 	.probe =	usb_audio_probe,
 	.disconnect =	usb_audio_disconnect,
+	.suspend =	usb_audio_suspend,
+	.resume =	usb_audio_resume,
 	.id_table =	usb_audio_ids,
 };
 
@@ -3664,6 +3668,43 @@ static void usb_audio_disconnect(struct 
 				 dev_get_drvdata(&intf->dev));
 }
 
+static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev);
+	struct list_head *p;
+	struct snd_usb_stream *as;
+
+	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);
+		}
+	}
+
+	return 0;
+}
+
+static int usb_audio_resume(struct usb_interface *intf)
+{
+	struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev);
+
+	if (chip == (void *)-1L)
+		return 0;
+	if (--chip->num_suspended_intf)
+		return 0;
+	/*
+	 * ALSA leaves material resumption to user space
+	 * we just notify
+	 */
+
+	snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
 
 static int __init snd_usb_audio_init(void)
 {

Reply via email to