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