diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-12-03 05:30:50 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-12-04 01:27:44 -0500 |
commit | f5f165418cabf2218eb466c0e94693b8b1aee88b (patch) | |
tree | cc5bb9658be8d83c1f4c0533468f52ac2a5348be /sound/usb | |
parent | 59866da9e4ae54819e3c4e0a8f426bdb0c2ef993 (diff) |
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>
Diffstat (limited to 'sound/usb')
-rw-r--r-- | sound/usb/midi.c | 88 |
1 files changed, 46 insertions, 42 deletions
diff --git a/sound/usb/midi.c b/sound/usb/midi.c index c0054ee9389b..34b9bb7fe87c 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c | |||
@@ -126,8 +126,10 @@ struct snd_usb_midi { | |||
126 | struct snd_usb_midi_in_endpoint *in; | 126 | struct snd_usb_midi_in_endpoint *in; |
127 | } endpoints[MIDI_MAX_ENDPOINTS]; | 127 | } endpoints[MIDI_MAX_ENDPOINTS]; |
128 | unsigned long input_triggered; | 128 | unsigned long input_triggered; |
129 | unsigned int opened; | 129 | bool autopm_reference; |
130 | unsigned int opened[2]; | ||
130 | unsigned char disconnected; | 131 | unsigned char disconnected; |
132 | unsigned char input_running; | ||
131 | 133 | ||
132 | struct snd_kcontrol *roland_load_ctl; | 134 | struct snd_kcontrol *roland_load_ctl; |
133 | }; | 135 | }; |
@@ -149,7 +151,6 @@ struct snd_usb_midi_out_endpoint { | |||
149 | struct snd_usb_midi_out_endpoint* ep; | 151 | struct snd_usb_midi_out_endpoint* ep; |
150 | struct snd_rawmidi_substream *substream; | 152 | struct snd_rawmidi_substream *substream; |
151 | int active; | 153 | int active; |
152 | bool autopm_reference; | ||
153 | uint8_t cable; /* cable number << 4 */ | 154 | uint8_t cable; /* cable number << 4 */ |
154 | uint8_t state; | 155 | uint8_t state; |
155 | #define STATE_UNKNOWN 0 | 156 | #define STATE_UNKNOWN 0 |
@@ -1034,36 +1035,58 @@ static void update_roland_altsetting(struct snd_usb_midi* umidi) | |||
1034 | snd_usbmidi_input_start(&umidi->list); | 1035 | snd_usbmidi_input_start(&umidi->list); |
1035 | } | 1036 | } |
1036 | 1037 | ||
1037 | static void substream_open(struct snd_rawmidi_substream *substream, int open) | 1038 | static int substream_open(struct snd_rawmidi_substream *substream, int dir, |
1039 | int open) | ||
1038 | { | 1040 | { |
1039 | struct snd_usb_midi* umidi = substream->rmidi->private_data; | 1041 | struct snd_usb_midi* umidi = substream->rmidi->private_data; |
1040 | struct snd_kcontrol *ctl; | 1042 | struct snd_kcontrol *ctl; |
1043 | int err; | ||
1041 | 1044 | ||
1042 | down_read(&umidi->disc_rwsem); | 1045 | down_read(&umidi->disc_rwsem); |
1043 | if (umidi->disconnected) { | 1046 | if (umidi->disconnected) { |
1044 | up_read(&umidi->disc_rwsem); | 1047 | up_read(&umidi->disc_rwsem); |
1045 | return; | 1048 | return open ? -ENODEV : 0; |
1046 | } | 1049 | } |
1047 | 1050 | ||
1048 | mutex_lock(&umidi->mutex); | 1051 | mutex_lock(&umidi->mutex); |
1049 | if (open) { | 1052 | if (open) { |
1050 | if (umidi->opened++ == 0 && umidi->roland_load_ctl) { | 1053 | if (!umidi->opened[0] && !umidi->opened[1]) { |
1051 | ctl = umidi->roland_load_ctl; | 1054 | err = usb_autopm_get_interface(umidi->iface); |
1052 | ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; | 1055 | umidi->autopm_reference = err >= 0; |
1053 | snd_ctl_notify(umidi->card, | 1056 | if (err < 0 && err != -EACCES) { |
1057 | mutex_unlock(&umidi->mutex); | ||
1058 | up_read(&umidi->disc_rwsem); | ||
1059 | return -EIO; | ||
1060 | } | ||
1061 | if (umidi->roland_load_ctl) { | ||
1062 | ctl = umidi->roland_load_ctl; | ||
1063 | ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; | ||
1064 | snd_ctl_notify(umidi->card, | ||
1054 | SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); | 1065 | SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); |
1055 | update_roland_altsetting(umidi); | 1066 | update_roland_altsetting(umidi); |
1067 | } | ||
1056 | } | 1068 | } |
1069 | umidi->opened[dir]++; | ||
1070 | if (umidi->opened[1]) | ||
1071 | snd_usbmidi_input_start(&umidi->list); | ||
1057 | } else { | 1072 | } else { |
1058 | if (--umidi->opened == 0 && umidi->roland_load_ctl) { | 1073 | umidi->opened[dir]--; |
1059 | ctl = umidi->roland_load_ctl; | 1074 | if (!umidi->opened[1]) |
1060 | ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; | 1075 | snd_usbmidi_input_stop(&umidi->list); |
1061 | snd_ctl_notify(umidi->card, | 1076 | if (!umidi->opened[0] && !umidi->opened[1]) { |
1077 | if (umidi->roland_load_ctl) { | ||
1078 | ctl = umidi->roland_load_ctl; | ||
1079 | ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; | ||
1080 | snd_ctl_notify(umidi->card, | ||
1062 | SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); | 1081 | SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); |
1082 | } | ||
1083 | if (umidi->autopm_reference) | ||
1084 | usb_autopm_put_interface(umidi->iface); | ||
1063 | } | 1085 | } |
1064 | } | 1086 | } |
1065 | mutex_unlock(&umidi->mutex); | 1087 | mutex_unlock(&umidi->mutex); |
1066 | up_read(&umidi->disc_rwsem); | 1088 | up_read(&umidi->disc_rwsem); |
1089 | return 0; | ||
1067 | } | 1090 | } |
1068 | 1091 | ||
1069 | static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) | 1092 | 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) | |||
1071 | struct snd_usb_midi* umidi = substream->rmidi->private_data; | 1094 | struct snd_usb_midi* umidi = substream->rmidi->private_data; |
1072 | struct usbmidi_out_port* port = NULL; | 1095 | struct usbmidi_out_port* port = NULL; |
1073 | int i, j; | 1096 | int i, j; |
1074 | int err; | ||
1075 | 1097 | ||
1076 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) | 1098 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) |
1077 | if (umidi->endpoints[i].out) | 1099 | if (umidi->endpoints[i].out) |
@@ -1085,33 +1107,14 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) | |||
1085 | return -ENXIO; | 1107 | return -ENXIO; |
1086 | } | 1108 | } |
1087 | 1109 | ||
1088 | down_read(&umidi->disc_rwsem); | ||
1089 | if (umidi->disconnected) { | ||
1090 | up_read(&umidi->disc_rwsem); | ||
1091 | return -ENODEV; | ||
1092 | } | ||
1093 | err = usb_autopm_get_interface(umidi->iface); | ||
1094 | port->autopm_reference = err >= 0; | ||
1095 | up_read(&umidi->disc_rwsem); | ||
1096 | if (err < 0 && err != -EACCES) | ||
1097 | return -EIO; | ||
1098 | substream->runtime->private_data = port; | 1110 | substream->runtime->private_data = port; |
1099 | port->state = STATE_UNKNOWN; | 1111 | port->state = STATE_UNKNOWN; |
1100 | substream_open(substream, 1); | 1112 | return substream_open(substream, 0, 1); |
1101 | return 0; | ||
1102 | } | 1113 | } |
1103 | 1114 | ||
1104 | static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) | 1115 | static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) |
1105 | { | 1116 | { |
1106 | struct snd_usb_midi* umidi = substream->rmidi->private_data; | 1117 | return substream_open(substream, 0, 0); |
1107 | struct usbmidi_out_port *port = substream->runtime->private_data; | ||
1108 | |||
1109 | substream_open(substream, 0); | ||
1110 | down_read(&umidi->disc_rwsem); | ||
1111 | if (!umidi->disconnected && port->autopm_reference) | ||
1112 | usb_autopm_put_interface(umidi->iface); | ||
1113 | up_read(&umidi->disc_rwsem); | ||
1114 | return 0; | ||
1115 | } | 1118 | } |
1116 | 1119 | ||
1117 | static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream, int up) | 1120 | 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) | |||
1164 | 1167 | ||
1165 | static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream) | 1168 | static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream) |
1166 | { | 1169 | { |
1167 | substream_open(substream, 1); | 1170 | return substream_open(substream, 1, 1); |
1168 | return 0; | ||
1169 | } | 1171 | } |
1170 | 1172 | ||
1171 | static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream) | 1173 | static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream) |
1172 | { | 1174 | { |
1173 | substream_open(substream, 0); | 1175 | return substream_open(substream, 1, 0); |
1174 | return 0; | ||
1175 | } | 1176 | } |
1176 | 1177 | ||
1177 | static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, int up) | 1178 | 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) | |||
2080 | unsigned int i, j; | 2081 | unsigned int i, j; |
2081 | 2082 | ||
2082 | umidi = list_entry(p, struct snd_usb_midi, list); | 2083 | umidi = list_entry(p, struct snd_usb_midi, list); |
2084 | if (!umidi->input_running) | ||
2085 | return; | ||
2083 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { | 2086 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { |
2084 | struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; | 2087 | struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; |
2085 | if (ep->in) | 2088 | if (ep->in) |
2086 | for (j = 0; j < INPUT_URBS; ++j) | 2089 | for (j = 0; j < INPUT_URBS; ++j) |
2087 | usb_kill_urb(ep->in->urbs[j]); | 2090 | usb_kill_urb(ep->in->urbs[j]); |
2088 | } | 2091 | } |
2092 | umidi->input_running = 0; | ||
2089 | } | 2093 | } |
2090 | 2094 | ||
2091 | static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep) | 2095 | 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) | |||
2110 | int i; | 2114 | int i; |
2111 | 2115 | ||
2112 | umidi = list_entry(p, struct snd_usb_midi, list); | 2116 | umidi = list_entry(p, struct snd_usb_midi, list); |
2117 | if (umidi->input_running || !umidi->opened[1]) | ||
2118 | return; | ||
2113 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) | 2119 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) |
2114 | snd_usbmidi_input_start_ep(umidi->endpoints[i].in); | 2120 | snd_usbmidi_input_start_ep(umidi->endpoints[i].in); |
2121 | umidi->input_running = 1; | ||
2115 | } | 2122 | } |
2116 | 2123 | ||
2117 | /* | 2124 | /* |
@@ -2250,9 +2257,6 @@ int snd_usbmidi_create(struct snd_card *card, | |||
2250 | } | 2257 | } |
2251 | 2258 | ||
2252 | list_add_tail(&umidi->list, midi_list); | 2259 | list_add_tail(&umidi->list, midi_list); |
2253 | |||
2254 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) | ||
2255 | snd_usbmidi_input_start_ep(umidi->endpoints[i].in); | ||
2256 | return 0; | 2260 | return 0; |
2257 | } | 2261 | } |
2258 | 2262 | ||