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