diff options
Diffstat (limited to 'sound/usb/midi.c')
-rw-r--r-- | sound/usb/midi.c | 91 |
1 files changed, 58 insertions, 33 deletions
diff --git a/sound/usb/midi.c b/sound/usb/midi.c index eeefbce3873c..34b9bb7fe87c 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c | |||
@@ -116,6 +116,7 @@ struct snd_usb_midi { | |||
116 | struct list_head list; | 116 | struct list_head list; |
117 | struct timer_list error_timer; | 117 | struct timer_list error_timer; |
118 | spinlock_t disc_lock; | 118 | spinlock_t disc_lock; |
119 | struct rw_semaphore disc_rwsem; | ||
119 | struct mutex mutex; | 120 | struct mutex mutex; |
120 | u32 usb_id; | 121 | u32 usb_id; |
121 | int next_midi_device; | 122 | int next_midi_device; |
@@ -125,8 +126,10 @@ struct snd_usb_midi { | |||
125 | struct snd_usb_midi_in_endpoint *in; | 126 | struct snd_usb_midi_in_endpoint *in; |
126 | } endpoints[MIDI_MAX_ENDPOINTS]; | 127 | } endpoints[MIDI_MAX_ENDPOINTS]; |
127 | unsigned long input_triggered; | 128 | unsigned long input_triggered; |
128 | unsigned int opened; | 129 | bool autopm_reference; |
130 | unsigned int opened[2]; | ||
129 | unsigned char disconnected; | 131 | unsigned char disconnected; |
132 | unsigned char input_running; | ||
130 | 133 | ||
131 | struct snd_kcontrol *roland_load_ctl; | 134 | struct snd_kcontrol *roland_load_ctl; |
132 | }; | 135 | }; |
@@ -148,7 +151,6 @@ struct snd_usb_midi_out_endpoint { | |||
148 | struct snd_usb_midi_out_endpoint* ep; | 151 | struct snd_usb_midi_out_endpoint* ep; |
149 | struct snd_rawmidi_substream *substream; | 152 | struct snd_rawmidi_substream *substream; |
150 | int active; | 153 | int active; |
151 | bool autopm_reference; | ||
152 | uint8_t cable; /* cable number << 4 */ | 154 | uint8_t cable; /* cable number << 4 */ |
153 | uint8_t state; | 155 | uint8_t state; |
154 | #define STATE_UNKNOWN 0 | 156 | #define STATE_UNKNOWN 0 |
@@ -1033,29 +1035,58 @@ static void update_roland_altsetting(struct snd_usb_midi* umidi) | |||
1033 | snd_usbmidi_input_start(&umidi->list); | 1035 | snd_usbmidi_input_start(&umidi->list); |
1034 | } | 1036 | } |
1035 | 1037 | ||
1036 | 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) | ||
1037 | { | 1040 | { |
1038 | struct snd_usb_midi* umidi = substream->rmidi->private_data; | 1041 | struct snd_usb_midi* umidi = substream->rmidi->private_data; |
1039 | struct snd_kcontrol *ctl; | 1042 | struct snd_kcontrol *ctl; |
1043 | int err; | ||
1044 | |||
1045 | down_read(&umidi->disc_rwsem); | ||
1046 | if (umidi->disconnected) { | ||
1047 | up_read(&umidi->disc_rwsem); | ||
1048 | return open ? -ENODEV : 0; | ||
1049 | } | ||
1040 | 1050 | ||
1041 | mutex_lock(&umidi->mutex); | 1051 | mutex_lock(&umidi->mutex); |
1042 | if (open) { | 1052 | if (open) { |
1043 | if (umidi->opened++ == 0 && umidi->roland_load_ctl) { | 1053 | if (!umidi->opened[0] && !umidi->opened[1]) { |
1044 | ctl = umidi->roland_load_ctl; | 1054 | err = usb_autopm_get_interface(umidi->iface); |
1045 | ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; | 1055 | umidi->autopm_reference = err >= 0; |
1046 | 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, | ||
1047 | SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); | 1065 | SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); |
1048 | update_roland_altsetting(umidi); | 1066 | update_roland_altsetting(umidi); |
1067 | } | ||
1049 | } | 1068 | } |
1069 | umidi->opened[dir]++; | ||
1070 | if (umidi->opened[1]) | ||
1071 | snd_usbmidi_input_start(&umidi->list); | ||
1050 | } else { | 1072 | } else { |
1051 | if (--umidi->opened == 0 && umidi->roland_load_ctl) { | 1073 | umidi->opened[dir]--; |
1052 | ctl = umidi->roland_load_ctl; | 1074 | if (!umidi->opened[1]) |
1053 | ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; | 1075 | snd_usbmidi_input_stop(&umidi->list); |
1054 | 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, | ||
1055 | 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); | ||
1056 | } | 1085 | } |
1057 | } | 1086 | } |
1058 | mutex_unlock(&umidi->mutex); | 1087 | mutex_unlock(&umidi->mutex); |
1088 | up_read(&umidi->disc_rwsem); | ||
1089 | return 0; | ||
1059 | } | 1090 | } |
1060 | 1091 | ||
1061 | static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) | 1092 | static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) |
@@ -1063,7 +1094,6 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) | |||
1063 | struct snd_usb_midi* umidi = substream->rmidi->private_data; | 1094 | struct snd_usb_midi* umidi = substream->rmidi->private_data; |
1064 | struct usbmidi_out_port* port = NULL; | 1095 | struct usbmidi_out_port* port = NULL; |
1065 | int i, j; | 1096 | int i, j; |
1066 | int err; | ||
1067 | 1097 | ||
1068 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) | 1098 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) |
1069 | if (umidi->endpoints[i].out) | 1099 | if (umidi->endpoints[i].out) |
@@ -1076,25 +1106,15 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) | |||
1076 | snd_BUG(); | 1106 | snd_BUG(); |
1077 | return -ENXIO; | 1107 | return -ENXIO; |
1078 | } | 1108 | } |
1079 | err = usb_autopm_get_interface(umidi->iface); | 1109 | |
1080 | port->autopm_reference = err >= 0; | ||
1081 | if (err < 0 && err != -EACCES) | ||
1082 | return -EIO; | ||
1083 | substream->runtime->private_data = port; | 1110 | substream->runtime->private_data = port; |
1084 | port->state = STATE_UNKNOWN; | 1111 | port->state = STATE_UNKNOWN; |
1085 | substream_open(substream, 1); | 1112 | return substream_open(substream, 0, 1); |
1086 | return 0; | ||
1087 | } | 1113 | } |
1088 | 1114 | ||
1089 | static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) | 1115 | static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) |
1090 | { | 1116 | { |
1091 | struct snd_usb_midi* umidi = substream->rmidi->private_data; | 1117 | return substream_open(substream, 0, 0); |
1092 | struct usbmidi_out_port *port = substream->runtime->private_data; | ||
1093 | |||
1094 | substream_open(substream, 0); | ||
1095 | if (port->autopm_reference) | ||
1096 | usb_autopm_put_interface(umidi->iface); | ||
1097 | return 0; | ||
1098 | } | 1118 | } |
1099 | 1119 | ||
1100 | 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) |
@@ -1147,14 +1167,12 @@ static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream) | |||
1147 | 1167 | ||
1148 | static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream) | 1168 | static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream) |
1149 | { | 1169 | { |
1150 | substream_open(substream, 1); | 1170 | return substream_open(substream, 1, 1); |
1151 | return 0; | ||
1152 | } | 1171 | } |
1153 | 1172 | ||
1154 | static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream) | 1173 | static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream) |
1155 | { | 1174 | { |
1156 | substream_open(substream, 0); | 1175 | return substream_open(substream, 1, 0); |
1157 | return 0; | ||
1158 | } | 1176 | } |
1159 | 1177 | ||
1160 | 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) |
@@ -1403,9 +1421,12 @@ void snd_usbmidi_disconnect(struct list_head* p) | |||
1403 | * a timer may submit an URB. To reliably break the cycle | 1421 | * a timer may submit an URB. To reliably break the cycle |
1404 | * a flag under lock must be used | 1422 | * a flag under lock must be used |
1405 | */ | 1423 | */ |
1424 | down_write(&umidi->disc_rwsem); | ||
1406 | spin_lock_irq(&umidi->disc_lock); | 1425 | spin_lock_irq(&umidi->disc_lock); |
1407 | umidi->disconnected = 1; | 1426 | umidi->disconnected = 1; |
1408 | spin_unlock_irq(&umidi->disc_lock); | 1427 | spin_unlock_irq(&umidi->disc_lock); |
1428 | up_write(&umidi->disc_rwsem); | ||
1429 | |||
1409 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { | 1430 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { |
1410 | struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; | 1431 | struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; |
1411 | if (ep->out) | 1432 | if (ep->out) |
@@ -2060,12 +2081,15 @@ void snd_usbmidi_input_stop(struct list_head* p) | |||
2060 | unsigned int i, j; | 2081 | unsigned int i, j; |
2061 | 2082 | ||
2062 | 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; | ||
2063 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { | 2086 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { |
2064 | struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; | 2087 | struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; |
2065 | if (ep->in) | 2088 | if (ep->in) |
2066 | for (j = 0; j < INPUT_URBS; ++j) | 2089 | for (j = 0; j < INPUT_URBS; ++j) |
2067 | usb_kill_urb(ep->in->urbs[j]); | 2090 | usb_kill_urb(ep->in->urbs[j]); |
2068 | } | 2091 | } |
2092 | umidi->input_running = 0; | ||
2069 | } | 2093 | } |
2070 | 2094 | ||
2071 | 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) |
@@ -2090,8 +2114,11 @@ void snd_usbmidi_input_start(struct list_head* p) | |||
2090 | int i; | 2114 | int i; |
2091 | 2115 | ||
2092 | 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; | ||
2093 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) | 2119 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) |
2094 | snd_usbmidi_input_start_ep(umidi->endpoints[i].in); | 2120 | snd_usbmidi_input_start_ep(umidi->endpoints[i].in); |
2121 | umidi->input_running = 1; | ||
2095 | } | 2122 | } |
2096 | 2123 | ||
2097 | /* | 2124 | /* |
@@ -2117,6 +2144,7 @@ int snd_usbmidi_create(struct snd_card *card, | |||
2117 | umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; | 2144 | umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; |
2118 | init_timer(&umidi->error_timer); | 2145 | init_timer(&umidi->error_timer); |
2119 | spin_lock_init(&umidi->disc_lock); | 2146 | spin_lock_init(&umidi->disc_lock); |
2147 | init_rwsem(&umidi->disc_rwsem); | ||
2120 | mutex_init(&umidi->mutex); | 2148 | mutex_init(&umidi->mutex); |
2121 | umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), | 2149 | umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), |
2122 | le16_to_cpu(umidi->dev->descriptor.idProduct)); | 2150 | le16_to_cpu(umidi->dev->descriptor.idProduct)); |
@@ -2229,9 +2257,6 @@ int snd_usbmidi_create(struct snd_card *card, | |||
2229 | } | 2257 | } |
2230 | 2258 | ||
2231 | list_add_tail(&umidi->list, midi_list); | 2259 | list_add_tail(&umidi->list, midi_list); |
2232 | |||
2233 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) | ||
2234 | snd_usbmidi_input_start_ep(umidi->endpoints[i].in); | ||
2235 | return 0; | 2260 | return 0; |
2236 | } | 2261 | } |
2237 | 2262 | ||