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