Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5f16541 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: usb-audio: Fix missing autopm for MIDI input



The commit [88a8516a: ALSA: usbaudio: implement USB autosuspend] added
the support of autopm for USB MIDI output, but it didn't take the MIDI
input into account.

This patch adds the following for fixing the autopm:
- Manage the URB start at the first MIDI input stream open, instead of
  the time of instance creation
- Move autopm code to the common substream_open()
- Make snd_usbmidi_input_start/_stop() more robust and add the running
  state check

Reviewd-by: default avatarClemens Ladisch <clemens@ladisch.de>
Tested-by: default avatarClemens Ladisch <clemens@ladisch.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 59866da9
Loading
Loading
Loading
Loading
+46 −42
Original line number Diff line number Diff line
@@ -126,8 +126,10 @@ struct snd_usb_midi {
		struct snd_usb_midi_in_endpoint *in;
	} endpoints[MIDI_MAX_ENDPOINTS];
	unsigned long input_triggered;
	unsigned int opened;
	bool autopm_reference;
	unsigned int opened[2];
	unsigned char disconnected;
	unsigned char input_running;

	struct snd_kcontrol *roland_load_ctl;
};
@@ -149,7 +151,6 @@ struct snd_usb_midi_out_endpoint {
		struct snd_usb_midi_out_endpoint* ep;
		struct snd_rawmidi_substream *substream;
		int active;
		bool autopm_reference;
		uint8_t cable;		/* cable number << 4 */
		uint8_t state;
#define STATE_UNKNOWN	0
@@ -1034,36 +1035,58 @@ static void update_roland_altsetting(struct snd_usb_midi* umidi)
	snd_usbmidi_input_start(&umidi->list);
}

static void substream_open(struct snd_rawmidi_substream *substream, int open)
static int substream_open(struct snd_rawmidi_substream *substream, int dir,
			  int open)
{
	struct snd_usb_midi* umidi = substream->rmidi->private_data;
	struct snd_kcontrol *ctl;
	int err;

	down_read(&umidi->disc_rwsem);
	if (umidi->disconnected) {
		up_read(&umidi->disc_rwsem);
		return;
		return open ? -ENODEV : 0;
	}

	mutex_lock(&umidi->mutex);
	if (open) {
		if (umidi->opened++ == 0 && umidi->roland_load_ctl) {
		if (!umidi->opened[0] && !umidi->opened[1]) {
			err = usb_autopm_get_interface(umidi->iface);
			umidi->autopm_reference = err >= 0;
			if (err < 0 && err != -EACCES) {
				mutex_unlock(&umidi->mutex);
				up_read(&umidi->disc_rwsem);
				return -EIO;
			}
			if (umidi->roland_load_ctl) {
				ctl = umidi->roland_load_ctl;
				ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
				snd_ctl_notify(umidi->card,
				       SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
				update_roland_altsetting(umidi);
			}
		}
		umidi->opened[dir]++;
		if (umidi->opened[1])
			snd_usbmidi_input_start(&umidi->list);
	} else {
		if (--umidi->opened == 0 && umidi->roland_load_ctl) {
		umidi->opened[dir]--;
		if (!umidi->opened[1])
			snd_usbmidi_input_stop(&umidi->list);
		if (!umidi->opened[0] && !umidi->opened[1]) {
			if (umidi->roland_load_ctl) {
				ctl = umidi->roland_load_ctl;
				ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
				snd_ctl_notify(umidi->card,
				       SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
			}
			if (umidi->autopm_reference)
				usb_autopm_put_interface(umidi->iface);
		}
	}
	mutex_unlock(&umidi->mutex);
	up_read(&umidi->disc_rwsem);
	return 0;
}

static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
@@ -1071,7 +1094,6 @@ 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 err;

	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
		if (umidi->endpoints[i].out)
@@ -1085,33 +1107,14 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
		return -ENXIO;
	}

	down_read(&umidi->disc_rwsem);
	if (umidi->disconnected) {
		up_read(&umidi->disc_rwsem);
		return -ENODEV;
	}
	err = usb_autopm_get_interface(umidi->iface);
	port->autopm_reference = err >= 0;
	up_read(&umidi->disc_rwsem);
	if (err < 0 && err != -EACCES)
		return -EIO;
	substream->runtime->private_data = port;
	port->state = STATE_UNKNOWN;
	substream_open(substream, 1);
	return 0;
	return substream_open(substream, 0, 1);
}

static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
{
	struct snd_usb_midi* umidi = substream->rmidi->private_data;
	struct usbmidi_out_port *port = substream->runtime->private_data;

	substream_open(substream, 0);
	down_read(&umidi->disc_rwsem);
	if (!umidi->disconnected && port->autopm_reference)
		usb_autopm_put_interface(umidi->iface);
	up_read(&umidi->disc_rwsem);
	return 0;
	return substream_open(substream, 0, 0);
}

static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -1164,14 +1167,12 @@ static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream)

static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream)
{
	substream_open(substream, 1);
	return 0;
	return substream_open(substream, 1, 1);
}

static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream)
{
	substream_open(substream, 0);
	return 0;
	return substream_open(substream, 1, 0);
}

static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -2080,12 +2081,15 @@ void snd_usbmidi_input_stop(struct list_head* p)
	unsigned int i, j;

	umidi = list_entry(p, struct snd_usb_midi, list);
	if (!umidi->input_running)
		return;
	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
		struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
		if (ep->in)
			for (j = 0; j < INPUT_URBS; ++j)
				usb_kill_urb(ep->in->urbs[j]);
	}
	umidi->input_running = 0;
}

static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep)
@@ -2110,8 +2114,11 @@ void snd_usbmidi_input_start(struct list_head* p)
	int i;

	umidi = list_entry(p, struct snd_usb_midi, list);
	if (umidi->input_running || !umidi->opened[1])
		return;
	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
		snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
	umidi->input_running = 1;
}

/*
@@ -2250,9 +2257,6 @@ int snd_usbmidi_create(struct snd_card *card,
	}

	list_add_tail(&umidi->list, midi_list);

	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
		snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
	return 0;
}