Linux-libre 4.14.138-gnu
[librecmc/linux-libre.git] / sound / firewire / fireface / ff-midi.c
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->running_status[substream->number] = 0;
23         ff->rx_midi_error[substream->number] = false;
24
25         ACCESS_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         ACCESS_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                 ACCESS_ONCE(ff->tx_midi_substreams[substream->number]) =
56                                                                 substream;
57         else
58                 ACCESS_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 }