linux/sound/firewire/fireface/ff-midi.c
<<
>>
Prefs
   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
  10static int midi_capture_open(struct snd_rawmidi_substream *substream)
  11{
  12        /* Do nothing. */
  13        return 0;
  14}
  15
  16static 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
  29static int midi_capture_close(struct snd_rawmidi_substream *substream)
  30{
  31        /* Do nothing. */
  32        return 0;
  33}
  34
  35static int midi_playback_close(struct snd_rawmidi_substream *substream)
  36{
  37        struct snd_ff *ff = substream->rmidi->private_data;
  38
  39        cancel_work_sync(&ff->rx_midi_work[substream->number]);
  40        WRITE_ONCE(ff->rx_midi_substreams[substream->number], NULL);
  41
  42        return 0;
  43}
  44
  45static 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(&ff->lock, flags);
  60}
  61
  62static 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(&ff->rx_midi_work[substream->number]);
  72
  73        spin_unlock_irqrestore(&ff->lock, flags);
  74}
  75
  76static 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                snprintf(substream->name, sizeof(substream->name),
  83                         "%s MIDI %d", name, substream->number + 1);
  84        }
  85}
  86
  87int 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(ff->card, ff->card->driver, 0,
 104                              ff->spec->midi_out_ports, ff->spec->midi_in_ports,
 105                              &rmidi);
 106        if (err < 0)
 107                return err;
 108
 109        snprintf(rmidi->name, sizeof(rmidi->name),
 110                 "%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, SNDRV_RAWMIDI_STREAM_INPUT,
 115                            &midi_capture_ops);
 116        stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
 117        set_midi_substream_names(stream, ff->card->shortname);
 118
 119        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
 120        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
 121                            &midi_playback_ops);
 122        stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
 123        set_midi_substream_names(stream, ff->card->shortname);
 124
 125        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
 126
 127        return 0;
 128}
 129