mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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: Clemens Ladisch <clemens@ladisch.de>
Tested-by: Clemens Ladisch <clemens@ladisch.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
			
			
This commit is contained in:
		
							parent
							
								
									59866da9e4
								
							
						
					
					
						commit
						f5f165418c
					
				
					 1 changed files with 46 additions and 42 deletions
				
			
		| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
			ctl = umidi->roland_load_ctl;
 | 
			
		||||
			ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
 | 
			
		||||
			snd_ctl_notify(umidi->card,
 | 
			
		||||
		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);
 | 
			
		||||
				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) {
 | 
			
		||||
			ctl = umidi->roland_load_ctl;
 | 
			
		||||
			ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
 | 
			
		||||
			snd_ctl_notify(umidi->card,
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue