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 11static 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 30static 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 43static 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 61static 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 79static struct snd_rawmidi_ops midi_phys_capture_ops = { 80 .open = midi_phys_open, 81 .close = midi_phys_close, 82 .trigger = midi_phys_capture_trigger, 83}; 84 85static struct snd_rawmidi_ops midi_phys_playback_ops = { 86 .open = midi_phys_open, 87 .close = midi_phys_close, 88 .trigger = midi_phys_playback_trigger, 89}; 90 91static int midi_ctl_open(struct snd_rawmidi_substream *substream) 92{ 93 /* Do nothing. */ 94 return 0; 95} 96 97static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream) 98{ 99 /* Do nothing. */ 100 return 0; 101} 102 103static int midi_ctl_playback_close(struct snd_rawmidi_substream *substream) 104{ 105 struct snd_dg00x *dg00x = substream->rmidi->private_data; 106 107 snd_fw_async_midi_port_finish(&dg00x->out_control); 108 109 return 0; 110} 111 112static void midi_ctl_capture_trigger(struct snd_rawmidi_substream *substream, 113 int up) 114{ 115 struct snd_dg00x *dg00x = substream->rmidi->private_data; 116 unsigned long flags; 117 118 spin_lock_irqsave(&dg00x->lock, flags); 119 120 if (up) 121 dg00x->in_control = substream; 122 else 123 dg00x->in_control = NULL; 124 125 spin_unlock_irqrestore(&dg00x->lock, flags); 126} 127 128static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream, 129 int up) 130{ 131 struct snd_dg00x *dg00x = substream->rmidi->private_data; 132 unsigned long flags; 133 134 spin_lock_irqsave(&dg00x->lock, flags); 135 136 if (up) 137 snd_fw_async_midi_port_run(&dg00x->out_control, substream); 138 139 spin_unlock_irqrestore(&dg00x->lock, flags); 140} 141 142static struct snd_rawmidi_ops midi_ctl_capture_ops = { 143 .open = midi_ctl_open, 144 .close = midi_ctl_capture_close, 145 .trigger = midi_ctl_capture_trigger, 146}; 147 148static struct snd_rawmidi_ops midi_ctl_playback_ops = { 149 .open = midi_ctl_open, 150 .close = midi_ctl_playback_close, 151 .trigger = midi_ctl_playback_trigger, 152}; 153 154static void set_midi_substream_names(struct snd_dg00x *dg00x, 155 struct snd_rawmidi_str *str, 156 bool is_ctl) 157{ 158 struct snd_rawmidi_substream *subs; 159 160 list_for_each_entry(subs, &str->substreams, list) { 161 if (!is_ctl) 162 snprintf(subs->name, sizeof(subs->name), 163 "%s MIDI %d", 164 dg00x->card->shortname, subs->number + 1); 165 else 166 /* This port is for asynchronous transaction. */ 167 snprintf(subs->name, sizeof(subs->name), 168 "%s control", 169 dg00x->card->shortname); 170 } 171} 172 173int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) 174{ 175 struct snd_rawmidi *rmidi[2]; 176 struct snd_rawmidi_str *str; 177 unsigned int i; 178 int err; 179 180 /* Add physical midi ports. */ 181 err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0, 182 DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi[0]); 183 if (err < 0) 184 return err; 185 186 snprintf(rmidi[0]->name, sizeof(rmidi[0]->name), 187 "%s MIDI", dg00x->card->shortname); 188 189 snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT, 190 &midi_phys_capture_ops); 191 snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT, 192 &midi_phys_playback_ops); 193 194 /* Add a pair of control midi ports. */ 195 err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1, 196 1, 1, &rmidi[1]); 197 if (err < 0) 198 return err; 199 200 snprintf(rmidi[1]->name, sizeof(rmidi[1]->name), 201 "%s control", dg00x->card->shortname); 202 203 snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT, 204 &midi_ctl_capture_ops); 205 snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT, 206 &midi_ctl_playback_ops); 207 208 for (i = 0; i < ARRAY_SIZE(rmidi); i++) { 209 rmidi[i]->private_data = dg00x; 210 211 rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; 212 str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_INPUT]; 213 set_midi_substream_names(dg00x, str, i); 214 215 rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; 216 str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; 217 set_midi_substream_names(dg00x, str, i); 218 219 rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 220 } 221 222 return 0; 223} 224