1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * bebob_midi.c - a part of driver for BeBoB based devices
4  *
5  * Copyright (c) 2013-2014 Takashi Sakamoto
6  */
7 
8 #include "bebob.h"
9 
10 static int midi_capture_open(struct snd_rawmidi_substream *substream)
11 {
12 	struct snd_bebob *bebob = substream->rmidi->private_data;
13 	int err;
14 
15 	err = snd_bebob_stream_lock_try(bebob);
16 	if (err < 0)
17 		goto end;
18 
19 	mutex_lock(&bebob->mutex);
20 	bebob->substreams_counter++;
21 	err = snd_bebob_stream_start_duplex(bebob, 0);
22 	mutex_unlock(&bebob->mutex);
23 	if (err < 0)
24 		snd_bebob_stream_lock_release(bebob);
25 end:
26 	return err;
27 }
28 
29 static int midi_playback_open(struct snd_rawmidi_substream *substream)
30 {
31 	struct snd_bebob *bebob = substream->rmidi->private_data;
32 	int err;
33 
34 	err = snd_bebob_stream_lock_try(bebob);
35 	if (err < 0)
36 		goto end;
37 
38 	mutex_lock(&bebob->mutex);
39 	bebob->substreams_counter++;
40 	err = snd_bebob_stream_start_duplex(bebob, 0);
41 	mutex_unlock(&bebob->mutex);
42 	if (err < 0)
43 		snd_bebob_stream_lock_release(bebob);
44 end:
45 	return err;
46 }
47 
48 static int midi_capture_close(struct snd_rawmidi_substream *substream)
49 {
50 	struct snd_bebob *bebob = substream->rmidi->private_data;
51 
52 	mutex_lock(&bebob->mutex);
53 	bebob->substreams_counter--;
54 	snd_bebob_stream_stop_duplex(bebob);
55 	mutex_unlock(&bebob->mutex);
56 
57 	snd_bebob_stream_lock_release(bebob);
58 	return 0;
59 }
60 
61 static int midi_playback_close(struct snd_rawmidi_substream *substream)
62 {
63 	struct snd_bebob *bebob = substream->rmidi->private_data;
64 
65 	mutex_lock(&bebob->mutex);
66 	bebob->substreams_counter--;
67 	snd_bebob_stream_stop_duplex(bebob);
68 	mutex_unlock(&bebob->mutex);
69 
70 	snd_bebob_stream_lock_release(bebob);
71 	return 0;
72 }
73 
74 static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
75 {
76 	struct snd_bebob *bebob = substrm->rmidi->private_data;
77 	unsigned long flags;
78 
79 	spin_lock_irqsave(&bebob->lock, flags);
80 
81 	if (up)
82 		amdtp_am824_midi_trigger(&bebob->tx_stream,
83 					 substrm->number, substrm);
84 	else
85 		amdtp_am824_midi_trigger(&bebob->tx_stream,
86 					 substrm->number, NULL);
87 
88 	spin_unlock_irqrestore(&bebob->lock, flags);
89 }
90 
91 static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
92 {
93 	struct snd_bebob *bebob = substrm->rmidi->private_data;
94 	unsigned long flags;
95 
96 	spin_lock_irqsave(&bebob->lock, flags);
97 
98 	if (up)
99 		amdtp_am824_midi_trigger(&bebob->rx_stream,
100 					 substrm->number, substrm);
101 	else
102 		amdtp_am824_midi_trigger(&bebob->rx_stream,
103 					 substrm->number, NULL);
104 
105 	spin_unlock_irqrestore(&bebob->lock, flags);
106 }
107 
108 static void set_midi_substream_names(struct snd_bebob *bebob,
109 				     struct snd_rawmidi_str *str)
110 {
111 	struct snd_rawmidi_substream *subs;
112 
113 	list_for_each_entry(subs, &str->substreams, list) {
114 		snprintf(subs->name, sizeof(subs->name),
115 			 "%s MIDI %d",
116 			 bebob->card->shortname, subs->number + 1);
117 	}
118 }
119 
120 int snd_bebob_create_midi_devices(struct snd_bebob *bebob)
121 {
122 	static const struct snd_rawmidi_ops capture_ops = {
123 		.open		= midi_capture_open,
124 		.close		= midi_capture_close,
125 		.trigger	= midi_capture_trigger,
126 	};
127 	static const struct snd_rawmidi_ops playback_ops = {
128 		.open		= midi_playback_open,
129 		.close		= midi_playback_close,
130 		.trigger	= midi_playback_trigger,
131 	};
132 	struct snd_rawmidi *rmidi;
133 	struct snd_rawmidi_str *str;
134 	int err;
135 
136 	/* create midi ports */
137 	err = snd_rawmidi_new(bebob->card, bebob->card->driver, 0,
138 			      bebob->midi_output_ports, bebob->midi_input_ports,
139 			      &rmidi);
140 	if (err < 0)
141 		return err;
142 
143 	snprintf(rmidi->name, sizeof(rmidi->name),
144 		 "%s MIDI", bebob->card->shortname);
145 	rmidi->private_data = bebob;
146 
147 	if (bebob->midi_input_ports > 0) {
148 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
149 
150 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
151 				    &capture_ops);
152 
153 		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
154 
155 		set_midi_substream_names(bebob, str);
156 	}
157 
158 	if (bebob->midi_output_ports > 0) {
159 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
160 
161 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
162 				    &playback_ops);
163 
164 		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
165 
166 		set_midi_substream_names(bebob, str);
167 	}
168 
169 	if ((bebob->midi_output_ports > 0) && (bebob->midi_input_ports > 0))
170 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
171 
172 	return 0;
173 }
174