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_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 	err = snd_dg00x_stream_reserve_duplex(dg00x, 0);
22 	if (err >= 0) {
23 		++dg00x->substreams_counter;
24 		err = snd_dg00x_stream_start_duplex(dg00x);
25 	}
26 	mutex_unlock(&dg00x->mutex);
27 	if (err < 0)
28 		snd_dg00x_stream_lock_release(dg00x);
29 
30 	return err;
31 }
32 
33 static int midi_close(struct snd_rawmidi_substream *substream)
34 {
35 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
36 
37 	mutex_lock(&dg00x->mutex);
38 	--dg00x->substreams_counter;
39 	snd_dg00x_stream_stop_duplex(dg00x);
40 	mutex_unlock(&dg00x->mutex);
41 
42 	snd_dg00x_stream_lock_release(dg00x);
43 	return 0;
44 }
45 
46 static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
47 				 int up)
48 {
49 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
50 	unsigned int port;
51 	unsigned long flags;
52 
53 	if (substream->rmidi->device == 0)
54 		port = substream->number;
55 	else
56 		port = 2;
57 
58 	spin_lock_irqsave(&dg00x->lock, flags);
59 
60 	if (up)
61 		amdtp_dot_midi_trigger(&dg00x->tx_stream, port, substream);
62 	else
63 		amdtp_dot_midi_trigger(&dg00x->tx_stream, port, NULL);
64 
65 	spin_unlock_irqrestore(&dg00x->lock, flags);
66 }
67 
68 static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
69 				  int up)
70 {
71 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
72 	unsigned int port;
73 	unsigned long flags;
74 
75 	if (substream->rmidi->device == 0)
76 		port = substream->number;
77 	else
78 		port = 2;
79 
80 	spin_lock_irqsave(&dg00x->lock, flags);
81 
82 	if (up)
83 		amdtp_dot_midi_trigger(&dg00x->rx_stream, port, substream);
84 	else
85 		amdtp_dot_midi_trigger(&dg00x->rx_stream, port, NULL);
86 
87 	spin_unlock_irqrestore(&dg00x->lock, flags);
88 }
89 
90 static void set_substream_names(struct snd_dg00x *dg00x,
91 				struct snd_rawmidi *rmidi, bool is_console)
92 {
93 	struct snd_rawmidi_substream *subs;
94 	struct snd_rawmidi_str *str;
95 	int i;
96 
97 	for (i = 0; i < 2; ++i) {
98 		str = &rmidi->streams[i];
99 
100 		list_for_each_entry(subs, &str->substreams, list) {
101 			if (!is_console) {
102 				snprintf(subs->name, sizeof(subs->name),
103 					 "%s MIDI %d",
104 					 dg00x->card->shortname,
105 					 subs->number + 1);
106 			} else {
107 				snprintf(subs->name, sizeof(subs->name),
108 					 "%s control",
109 					 dg00x->card->shortname);
110 			}
111 		}
112 	}
113 }
114 
115 static int add_substream_pair(struct snd_dg00x *dg00x, unsigned int out_ports,
116 			      unsigned int in_ports, bool is_console)
117 {
118 	static const struct snd_rawmidi_ops capture_ops = {
119 		.open = midi_open,
120 		.close = midi_close,
121 		.trigger = midi_capture_trigger,
122 	};
123 	static const struct snd_rawmidi_ops playback_ops = {
124 		.open = midi_open,
125 		.close = midi_close,
126 		.trigger = midi_playback_trigger,
127 	};
128 	const char *label;
129 	struct snd_rawmidi *rmidi;
130 	int err;
131 
132 	/* Add physical midi ports. */
133 	err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, is_console,
134 			      out_ports, in_ports, &rmidi);
135 	if (err < 0)
136 		return err;
137 	rmidi->private_data = dg00x;
138 
139 	if (!is_console)
140 		label = "%s control";
141 	else
142 		label = "%s MIDI";
143 	snprintf(rmidi->name, sizeof(rmidi->name), label,
144 		 dg00x->card->shortname);
145 
146 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &playback_ops);
147 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &capture_ops);
148 
149 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
150 			     SNDRV_RAWMIDI_INFO_OUTPUT |
151 			     SNDRV_RAWMIDI_INFO_DUPLEX;
152 
153 	set_substream_names(dg00x, rmidi, is_console);
154 
155 	return 0;
156 }
157 
158 int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
159 {
160 	int err;
161 
162 	/* Add physical midi ports. */
163 	err = add_substream_pair(dg00x, DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS,
164 				 false);
165 	if (err < 0)
166 		return err;
167 
168 	if (dg00x->is_console)
169 		err = add_substream_pair(dg00x, 1, 1, true);
170 
171 	return err;
172 }
173