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