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(®ister_mutex); + if (!chip->shutdown) + err = usb_autopm_get_interface(chip->pm_intf); + mutex_unlock(®ister_mutex); + + return err; +} + +void snd_usb_autosuspend(struct snd_usb_audio *chip) +{ + mutex_lock(®ister_mutex); + if (!chip->shutdown) + usb_autopm_put_interface(chip->pm_intf); + mutex_unlock(®ister_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