1 /* 2 * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family 3 * 4 * Copyright (c) 2014-2015 Takashi Sakamoto 5 * 6 * Licensed under the terms of the GNU General Public License, version 2. 7 */ 8 9 #include "digi00x.h" 10 11 static int midi_phys_open(struct snd_rawmidi_substream *substream) 12 { 13 struct snd_dg00x *dg00x = substream->rmidi->private_data; 14 int err; 15 16 err = snd_dg00x_stream_lock_try(dg00x); 17 if (err < 0) 18 return err; 19 20 mutex_lock(&dg00x->mutex); 21 dg00x->substreams_counter++; 22 err = snd_dg00x_stream_start_duplex(dg00x, 0); 23 mutex_unlock(&dg00x->mutex); 24 if (err < 0) 25 snd_dg00x_stream_lock_release(dg00x); 26 27 return err; 28 } 29 30 static int midi_phys_close(struct snd_rawmidi_substream *substream) 31 { 32 struct snd_dg00x *dg00x = substream->rmidi->private_data; 33 34 mutex_lock(&dg00x->mutex); 35 dg00x->substreams_counter--; 36 snd_dg00x_stream_stop_duplex(dg00x); 37 mutex_unlock(&dg00x->mutex); 38 39 snd_dg00x_stream_lock_release(dg00x); 40 return 0; 41 } 42 43 static void midi_phys_capture_trigger(struct snd_rawmidi_substream *substream, 44 int up) 45 { 46 struct snd_dg00x *dg00x = substream->rmidi->private_data; 47 unsigned long flags; 48 49 spin_lock_irqsave(&dg00x->lock, flags); 50 51 if (up) 52 amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number, 53 substream); 54 else 55 amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number, 56 NULL); 57 58 spin_unlock_irqrestore(&dg00x->lock, flags); 59 } 60 61 static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream, 62 int up) 63 { 64 struct snd_dg00x *dg00x = substream->rmidi->private_data; 65 unsigned long flags; 66 67 spin_lock_irqsave(&dg00x->lock, flags); 68 69 if (up) 70 amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number, 71 substream); 72 else 73 amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number, 74 NULL); 75 76 spin_unlock_irqrestore(&dg00x->lock, flags); 77 } 78 79 static int midi_ctl_open(struct snd_rawmidi_substream *substream) 80 { 81 /* Do nothing. */ 82 return 0; 83 } 84 85 static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream) 86 { 87 /* Do nothing. */ 88 return 0; 89 } 90 91 static int midi_ctl_playback_close(struct snd_rawmidi_substream *substream) 92 { 93 struct snd_dg00x *dg00x = substream->rmidi->private_data; 94 95 snd_fw_async_midi_port_finish(&dg00x->out_control); 96 97 return 0; 98 } 99 100 static void midi_ctl_capture_trigger(struct snd_rawmidi_substream *substream, 101 int up) 102 { 103 struct snd_dg00x *dg00x = substream->rmidi->private_data; 104 unsigned long flags; 105 106 spin_lock_irqsave(&dg00x->lock, flags); 107 108 if (up) 109 dg00x->in_control = substream; 110 else 111 dg00x->in_control = NULL; 112 113 spin_unlock_irqrestore(&dg00x->lock, flags); 114 } 115 116 static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream, 117 int up) 118 { 119 struct snd_dg00x *dg00x = substream->rmidi->private_data; 120 unsigned long flags; 121 122 spin_lock_irqsave(&dg00x->lock, flags); 123 124 if (up) 125 snd_fw_async_midi_port_run(&dg00x->out_control, substream); 126 127 spin_unlock_irqrestore(&dg00x->lock, flags); 128 } 129 130 static void set_midi_substream_names(struct snd_dg00x *dg00x, 131 struct snd_rawmidi_str *str, 132 bool is_ctl) 133 { 134 struct snd_rawmidi_substream *subs; 135 136 list_for_each_entry(subs, &str->substreams, list) { 137 if (!is_ctl) 138 snprintf(subs->name, sizeof(subs->name), 139 "%s MIDI %d", 140 dg00x->card->shortname, subs->number + 1); 141 else 142 /* This port is for asynchronous transaction. */ 143 snprintf(subs->name, sizeof(subs->name), 144 "%s control", 145 dg00x->card->shortname); 146 } 147 } 148 149 int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) 150 { 151 static const struct snd_rawmidi_ops phys_capture_ops = { 152 .open = midi_phys_open, 153 .close = midi_phys_close, 154 .trigger = midi_phys_capture_trigger, 155 }; 156 static const struct snd_rawmidi_ops phys_playback_ops = { 157 .open = midi_phys_open, 158 .close = midi_phys_close, 159 .trigger = midi_phys_playback_trigger, 160 }; 161 static const struct snd_rawmidi_ops ctl_capture_ops = { 162 .open = midi_ctl_open, 163 .close = midi_ctl_capture_close, 164 .trigger = midi_ctl_capture_trigger, 165 }; 166 static const struct snd_rawmidi_ops ctl_playback_ops = { 167 .open = midi_ctl_open, 168 .close = midi_ctl_playback_close, 169 .trigger = midi_ctl_playback_trigger, 170 }; 171 struct snd_rawmidi *rmidi[2]; 172 struct snd_rawmidi_str *str; 173 unsigned int i; 174 int err; 175 176 /* Add physical midi ports. */ 177 err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0, 178 DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi[0]); 179 if (err < 0) 180 return err; 181 182 snprintf(rmidi[0]->name, sizeof(rmidi[0]->name), 183 "%s MIDI", dg00x->card->shortname); 184 185 snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT, 186 &phys_capture_ops); 187 snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT, 188 &phys_playback_ops); 189 190 /* Add a pair of control midi ports. */ 191 err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1, 192 1, 1, &rmidi[1]); 193 if (err < 0) 194 return err; 195 196 snprintf(rmidi[1]->name, sizeof(rmidi[1]->name), 197 "%s control", dg00x->card->shortname); 198 199 snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT, 200 &ctl_capture_ops); 201 snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT, 202 &ctl_playback_ops); 203 204 for (i = 0; i < ARRAY_SIZE(rmidi); i++) { 205 rmidi[i]->private_data = dg00x; 206 207 rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; 208 str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_INPUT]; 209 set_midi_substream_names(dg00x, str, i); 210 211 rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; 212 str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; 213 set_midi_substream_names(dg00x, str, i); 214 215 rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 216 } 217 218 return 0; 219 } 220