1 /*
2  * ff-midi.c - a part of driver for RME Fireface series
3  *
4  * Copyright (c) 2015-2017 Takashi Sakamoto
5  *
6  * Licensed under the terms of the GNU General Public License, version 2.
7  */
8 
9 #include "ff.h"
10 
11 static int midi_capture_open(struct snd_rawmidi_substream *substream)
12 {
13 	/* Do nothing. */
14 	return 0;
15 }
16 
17 static int midi_playback_open(struct snd_rawmidi_substream *substream)
18 {
19 	struct snd_ff *ff = substream->rmidi->private_data;
20 
21 	/* Initialize internal status. */
22 	ff->on_sysex[substream->number] = 0;
23 	ff->rx_midi_error[substream->number] = false;
24 
25 	WRITE_ONCE(ff->rx_midi_substreams[substream->number], substream);
26 
27 	return 0;
28 }
29 
30 static int midi_capture_close(struct snd_rawmidi_substream *substream)
31 {
32 	/* Do nothing. */
33 	return 0;
34 }
35 
36 static int midi_playback_close(struct snd_rawmidi_substream *substream)
37 {
38 	struct snd_ff *ff = substream->rmidi->private_data;
39 
40 	cancel_work_sync(&ff->rx_midi_work[substream->number]);
41 	WRITE_ONCE(ff->rx_midi_substreams[substream->number], NULL);
42 
43 	return 0;
44 }
45 
46 static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
47 				 int up)
48 {
49 	struct snd_ff *ff = substream->rmidi->private_data;
50 	unsigned long flags;
51 
52 	spin_lock_irqsave(&ff->lock, flags);
53 
54 	if (up)
55 		WRITE_ONCE(ff->tx_midi_substreams[substream->number],
56 			   substream);
57 	else
58 		WRITE_ONCE(ff->tx_midi_substreams[substream->number], NULL);
59 
60 	spin_unlock_irqrestore(&ff->lock, flags);
61 }
62 
63 static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
64 				  int up)
65 {
66 	struct snd_ff *ff = substream->rmidi->private_data;
67 	unsigned long flags;
68 
69 	spin_lock_irqsave(&ff->lock, flags);
70 
71 	if (up || !ff->rx_midi_error[substream->number])
72 		schedule_work(&ff->rx_midi_work[substream->number]);
73 
74 	spin_unlock_irqrestore(&ff->lock, flags);
75 }
76 
77 static void set_midi_substream_names(struct snd_rawmidi_str *stream,
78 				     const char *const name)
79 {
80 	struct snd_rawmidi_substream *substream;
81 
82 	list_for_each_entry(substream, &stream->substreams, list) {
83 		snprintf(substream->name, sizeof(substream->name),
84 			 "%s MIDI %d", name, substream->number + 1);
85 	}
86 }
87 
88 int snd_ff_create_midi_devices(struct snd_ff *ff)
89 {
90 	static const struct snd_rawmidi_ops midi_capture_ops = {
91 		.open		= midi_capture_open,
92 		.close		= midi_capture_close,
93 		.trigger	= midi_capture_trigger,
94 	};
95 	static const struct snd_rawmidi_ops midi_playback_ops = {
96 		.open		= midi_playback_open,
97 		.close		= midi_playback_close,
98 		.trigger	= midi_playback_trigger,
99 	};
100 	struct snd_rawmidi *rmidi;
101 	struct snd_rawmidi_str *stream;
102 	int err;
103 
104 	err = snd_rawmidi_new(ff->card, ff->card->driver, 0,
105 			      ff->spec->midi_out_ports, ff->spec->midi_in_ports,
106 			      &rmidi);
107 	if (err < 0)
108 		return err;
109 
110 	snprintf(rmidi->name, sizeof(rmidi->name),
111 		 "%s MIDI", ff->card->shortname);
112 	rmidi->private_data = ff;
113 
114 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
115 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
116 			    &midi_capture_ops);
117 	stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
118 	set_midi_substream_names(stream, ff->card->shortname);
119 
120 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
121 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
122 			    &midi_playback_ops);
123 	stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
124 	set_midi_substream_names(stream, ff->card->shortname);
125 
126 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
127 
128 	return 0;
129 }
130