Am Dienstag, 5. Februar 2008 08:36:01 schrieb Clemens Ladisch:
> > Documentation/DocBook/writing-an-alsa-driver, section
> > "RawMIDI Interface".
> >
> > Just use the *_open/*_close and *_disconnect callbacks.
> 
> Sorry, what I wrote isn't true for input because the driver uses an
> active URB regardless of whether some input MIDI device is open.
> 
> If you do autosuspend changes, they would have to go on top of the patch
> below, which makes the driver submit the endpoint's URB only when some
> MIDI stream is open.

Ok, this turns out to be more complicated than I thought.
This version includes your suggestions. It compiles. More
I cannot promise.

        Regards
                Oliver

----

--- linux-2.6/sound/usb/usbmixer.c      2008-02-05 13:24:54.000000000 +0100
+++ Desktop/Trees/linux-2.6/sound/usb/usbmixer.c        2008-02-07 
15:04:45.000000000 +0100
@@ -352,7 +352,11 @@
        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),
@@ -361,9 +365,11 @@
                                    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;
@@ -389,18 +395,27 @@
        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 = snd_usb_autoresume(cval->mixer->chip);
+       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) {
+                       snd_usb_autosuspend(cval->mixer->chip);
                        return 0;
+                       }
+       }
+       snd_usb_autosuspend(cval->mixer->chip);
        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;
@@ -1910,16 +1925,28 @@
        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;
 }
 
@@ -1998,6 +2025,10 @@
        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,
@@ -2010,6 +2041,7 @@
                else
                        snd_iprintf(buffer, "?\n");
        }
+       snd_usb_autosuspend(mixer->chip);
 }
 
 int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif)
--- linux-2.6/sound/usb/usbaudio.c      2008-02-05 13:24:54.000000000 +0100
+++ Desktop/Trees/linux-2.6/sound/usb/usbaudio.c        2008-02-07 
14:54:26.000000000 +0100
@@ -240,6 +240,25 @@
        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)
+               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)
+               usb_autopm_put_interface(chip->pm_intf);
+       mutex_unlock(&register_mutex);
+}
 
 /*
  * prepare urb for full speed capture sync pipe
@@ -1879,6 +1898,10 @@
                }
        }
 
+       err = snd_usb_autoresume(subs->stream->chip);
+       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.
@@ -1894,23 +1917,27 @@
                                               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:
+       snd_usb_autosuspend(subs->stream->chip);
+       return err;
 }
 
 static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
@@ -1937,6 +1964,7 @@
                subs->interface = -1;
        }
        subs->pcm_substream = NULL;
+       snd_usb_autosuspend(subs->stream->chip);
        return 0;
 }
 
@@ -2102,6 +2130,7 @@
        .suspend =      usb_audio_suspend,
        .resume =       usb_audio_resume,
        .id_table =     usb_audio_ids,
+       .supports_autosuspend = 1,
 };
 
 
@@ -3562,6 +3591,7 @@
                                        goto __error;
                                }
                                snd_card_set_dev(chip->card, &intf->dev);
+                               chip->pm_intf = intf;
                                break;
                        }
                if (! chip) {
@@ -3667,26 +3697,37 @@
 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;
@@ -3694,10 +3735,12 @@
                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/sound/usb/usbaudio.h      2008-02-05 13:24:54.000000000 +0100
+++ Desktop/Trees/linux-2.6/sound/usb/usbaudio.h        2008-02-07 
15:06:23.000000000 +0100
@@ -123,6 +123,7 @@
        int index;
        struct usb_device *dev;
        struct snd_card *card;
+       struct usb_interface *pm_intf;
        u32 usb_id;
        int shutdown;
        int num_interfaces;
@@ -230,7 +231,8 @@
 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);
 /*
  * retrieve usb_interface descriptor from the host interface
  * (conditional for compatibility with the older API)
--- linux-2.6/sound/usb/usbmidi.c       2008-02-07 14:51:36.000000000 +0100
+++ Desktop/Trees/linux-2.6/sound/usb/usbmidi.c 2008-02-07 14:29:11.000000000 
+0100
@@ -45,6 +45,7 @@
 #include <linux/slab.h>
 #include <linux/timer.h>
 #include <linux/usb.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/rawmidi.h>
 #include <sound/asequencer.h>
@@ -105,6 +106,7 @@
        struct list_head list;
        struct timer_list error_timer;
        spinlock_t disc_lock;
+       struct mutex disc_mut;
 
        struct snd_usb_midi_endpoint {
                struct snd_usb_midi_out_endpoint *out;
@@ -151,6 +153,7 @@
        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);
@@ -170,6 +173,14 @@
        return err;
 }
 
+static void snd_usbmidi_autosuspend(struct snd_usb_midi* umidi)
+{
+       mutex_lock(&umidi->disc_mut);
+       if (!umidi->disconnected)
+               usb_autopm_put_interface(umidi->iface);
+       mutex_unlock(&umidi->disc_mut);
+}
+
 /*
  * Error handling for URB completion functions.
  */
@@ -245,8 +256,12 @@
                }
        }
 
-       urb->dev = ep->umidi->chip->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)
@@ -808,8 +823,11 @@
 {
        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)
@@ -828,6 +846,9 @@
 
 static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
 {
+       struct snd_usb_midi* umidi = substream->rmidi->private_data;
+
+       snd_usbmidi_autosuspend(umidi);
        return 0;
 }
 
@@ -850,11 +871,57 @@
 
 static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream)
 {
+       struct snd_usb_midi* umidi = substream->rmidi->private_data;
+       struct snd_usb_midi_in_endpoint *endpoint;
+       unsigned int ep, p;
+       int needs_submit, err;
+
+       endpoint = NULL;
+       for (ep = 0; ep < MIDI_MAX_ENDPOINTS; ++ep) {
+               struct snd_usb_midi_in_endpoint *in = umidi->endpoints[ep].in;
+               if (!in)
+                       continue;
+               for (p = 0; p < 0x10; ++p)
+                       if (in->ports[p].substream == substream) {
+                               endpoint = in;
+                               break;
+                       }
+       }
+       if (!endpoint) {
+               snd_BUG();
+               return -ENXIO;
+       }
+       err = usb_autopm_get_interface(umidi->iface);
+       if (err < 0)
+               return -EIO;
+       substream->runtime->private_data = endpoint;
+       spin_lock_irq(&umidi->disc_lock);
+       needs_submit = endpoint->open_substreams == 0;
+       endpoint->open_substreams++;
+       spin_unlock_irq(&umidi->disc_lock);
+       if (needs_submit) {
+               endpoint->urb->dev = umidi->chip->dev;
+               snd_usbmidi_submit_urb(endpoint->urb, GFP_KERNEL);
+       }
        return 0;
 }
 
 static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream)
 {
+       struct snd_usb_midi* umidi = substream->rmidi->private_data;
+       struct snd_usb_midi_in_endpoint *endpoint;
+       int needs_kill;
+       
+       endpoint = substream->runtime->private_data;
+       spin_lock_irq(&endpoint->umidi->disc_lock);
+       endpoint->open_substreams--;
+       needs_kill = endpoint->open_substreams == 0;
+       if (needs_kill)
+               endpoint->error_resubmit = 0;
+       spin_unlock_irq(&endpoint->umidi->disc_lock);
+       if (needs_kill)
+               usb_kill_urb(endpoint->urb);
+       snd_usbmidi_autosuspend(umidi);
        return 0;
 }
 
@@ -1062,9 +1129,11 @@
         * 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)
@@ -1661,7 +1730,7 @@
 
 static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep)
 {
-       if (ep) {
+       if (ep && ep->open_substreams) {
                struct urb* urb = ep->urb;
                urb->dev = ep->umidi->chip->dev;
                snd_usbmidi_submit_urb(urb, GFP_KERNEL);
@@ -1702,6 +1771,7 @@
        umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
        init_timer(&umidi->error_timer);
        spin_lock_init(&umidi->disc_lock);
+       mutex_init(&umidi->disc_mut);
        umidi->error_timer.function = snd_usbmidi_error_timer;
        umidi->error_timer.data = (unsigned long)umidi;
 
@@ -1780,9 +1850,6 @@
        }
 
        list_add(&umidi->list, &umidi->chip->midi_list);
-
-       for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
-               snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
        return 0;
 }
 

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

Reply via email to