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