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_capture_open(struct snd_rawmidi_substream *substream)
12 {
13 	struct snd_bebob *bebob = substream->rmidi->private_data;
14 
15 	atomic_inc(&bebob->capture_substreams);
16 	return snd_bebob_stream_start_duplex(bebob, 0);
17 }
18 
19 static int midi_playback_open(struct snd_rawmidi_substream *substream)
20 {
21 	struct snd_bebob *bebob = substream->rmidi->private_data;
22 
23 	atomic_inc(&bebob->playback_substreams);
24 	return snd_bebob_stream_start_duplex(bebob, 0);
25 }
26 
27 static int midi_capture_close(struct snd_rawmidi_substream *substream)
28 {
29 	struct snd_bebob *bebob = substream->rmidi->private_data;
30 
31 	atomic_dec(&bebob->capture_substreams);
32 	snd_bebob_stream_stop_duplex(bebob);
33 
34 	return 0;
35 }
36 
37 static int midi_playback_close(struct snd_rawmidi_substream *substream)
38 {
39 	struct snd_bebob *bebob = substream->rmidi->private_data;
40 
41 	atomic_dec(&bebob->playback_substreams);
42 	snd_bebob_stream_stop_duplex(bebob);
43 
44 	return 0;
45 }
46 
47 static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
48 {
49 	struct snd_bebob *bebob = substrm->rmidi->private_data;
50 	unsigned long flags;
51 
52 	spin_lock_irqsave(&bebob->lock, flags);
53 
54 	if (up)
55 		amdtp_stream_midi_trigger(&bebob->tx_stream,
56 					  substrm->number, substrm);
57 	else
58 		amdtp_stream_midi_trigger(&bebob->tx_stream,
59 					  substrm->number, NULL);
60 
61 	spin_unlock_irqrestore(&bebob->lock, flags);
62 }
63 
64 static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
65 {
66 	struct snd_bebob *bebob = substrm->rmidi->private_data;
67 	unsigned long flags;
68 
69 	spin_lock_irqsave(&bebob->lock, flags);
70 
71 	if (up)
72 		amdtp_stream_midi_trigger(&bebob->rx_stream,
73 					  substrm->number, substrm);
74 	else
75 		amdtp_stream_midi_trigger(&bebob->rx_stream,
76 					  substrm->number, NULL);
77 
78 	spin_unlock_irqrestore(&bebob->lock, flags);
79 }
80 
81 static struct snd_rawmidi_ops midi_capture_ops = {
82 	.open		= midi_capture_open,
83 	.close		= midi_capture_close,
84 	.trigger	= midi_capture_trigger,
85 };
86 
87 static struct snd_rawmidi_ops midi_playback_ops = {
88 	.open		= midi_playback_open,
89 	.close		= midi_playback_close,
90 	.trigger	= midi_playback_trigger,
91 };
92 
93 static void set_midi_substream_names(struct snd_bebob *bebob,
94 				     struct snd_rawmidi_str *str)
95 {
96 	struct snd_rawmidi_substream *subs;
97 
98 	list_for_each_entry(subs, &str->substreams, list) {
99 		snprintf(subs->name, sizeof(subs->name),
100 			 "%s MIDI %d",
101 			 bebob->card->shortname, subs->number + 1);
102 	}
103 }
104 
105 int snd_bebob_create_midi_devices(struct snd_bebob *bebob)
106 {
107 	struct snd_rawmidi *rmidi;
108 	struct snd_rawmidi_str *str;
109 	int err;
110 
111 	/* create midi ports */
112 	err = snd_rawmidi_new(bebob->card, bebob->card->driver, 0,
113 			      bebob->midi_output_ports, bebob->midi_input_ports,
114 			      &rmidi);
115 	if (err < 0)
116 		return err;
117 
118 	snprintf(rmidi->name, sizeof(rmidi->name),
119 		 "%s MIDI", bebob->card->shortname);
120 	rmidi->private_data = bebob;
121 
122 	if (bebob->midi_input_ports > 0) {
123 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
124 
125 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
126 					&midi_capture_ops);
127 
128 		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
129 
130 		set_midi_substream_names(bebob, str);
131 	}
132 
133 	if (bebob->midi_output_ports > 0) {
134 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
135 
136 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
137 					&midi_playback_ops);
138 
139 		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
140 
141 		set_midi_substream_names(bebob, str);
142 	}
143 
144 	if ((bebob->midi_output_ports > 0) && (bebob->midi_input_ports > 0))
145 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
146 
147 	return 0;
148 }
149