linux/sound/usb/6fire/midi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Linux driver for TerraTec DMX 6Fire USB
   4 *
   5 * Rawmidi driver
   6 *
   7 * Author:      Torsten Schenk <torsten.schenk@zoho.com>
   8 * Created:     Jan 01, 2011
   9 * Copyright:   (C) Torsten Schenk
  10 */
  11
  12#include <sound/rawmidi.h>
  13
  14#include "midi.h"
  15#include "chip.h"
  16#include "comm.h"
  17
  18enum {
  19        MIDI_BUFSIZE = 64
  20};
  21
  22static void usb6fire_midi_out_handler(struct urb *urb)
  23{
  24        struct midi_runtime *rt = urb->context;
  25        int ret;
  26        unsigned long flags;
  27
  28        spin_lock_irqsave(&rt->out_lock, flags);
  29
  30        if (rt->out) {
  31                ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4,
  32                                MIDI_BUFSIZE - 4);
  33                if (ret > 0) { /* more data available, send next packet */
  34                        rt->out_buffer[1] = ret + 2;
  35                        rt->out_buffer[3] = rt->out_serial++;
  36                        urb->transfer_buffer_length = ret + 4;
  37
  38                        ret = usb_submit_urb(urb, GFP_ATOMIC);
  39                        if (ret < 0)
  40                                dev_err(&urb->dev->dev,
  41                                        "midi out urb submit failed: %d\n",
  42                                        ret);
  43                } else /* no more data to transmit */
  44                        rt->out = NULL;
  45        }
  46        spin_unlock_irqrestore(&rt->out_lock, flags);
  47}
  48
  49static void usb6fire_midi_in_received(
  50                struct midi_runtime *rt, u8 *data, int length)
  51{
  52        unsigned long flags;
  53
  54        spin_lock_irqsave(&rt->in_lock, flags);
  55        if (rt->in)
  56                snd_rawmidi_receive(rt->in, data, length);
  57        spin_unlock_irqrestore(&rt->in_lock, flags);
  58}
  59
  60static int usb6fire_midi_out_open(struct snd_rawmidi_substream *alsa_sub)
  61{
  62        return 0;
  63}
  64
  65static int usb6fire_midi_out_close(struct snd_rawmidi_substream *alsa_sub)
  66{
  67        return 0;
  68}
  69
  70static void usb6fire_midi_out_trigger(
  71                struct snd_rawmidi_substream *alsa_sub, int up)
  72{
  73        struct midi_runtime *rt = alsa_sub->rmidi->private_data;
  74        struct urb *urb = &rt->out_urb;
  75        __s8 ret;
  76        unsigned long flags;
  77
  78        spin_lock_irqsave(&rt->out_lock, flags);
  79        if (up) { /* start transfer */
  80                if (rt->out) { /* we are already transmitting so just return */
  81                        spin_unlock_irqrestore(&rt->out_lock, flags);
  82                        return;
  83                }
  84
  85                ret = snd_rawmidi_transmit(alsa_sub, rt->out_buffer + 4,
  86                                MIDI_BUFSIZE - 4);
  87                if (ret > 0) {
  88                        rt->out_buffer[1] = ret + 2;
  89                        rt->out_buffer[3] = rt->out_serial++;
  90                        urb->transfer_buffer_length = ret + 4;
  91
  92                        ret = usb_submit_urb(urb, GFP_ATOMIC);
  93                        if (ret < 0)
  94                                dev_err(&urb->dev->dev,
  95                                        "midi out urb submit failed: %d\n",
  96                                        ret);
  97                        else
  98                                rt->out = alsa_sub;
  99                }
 100        } else if (rt->out == alsa_sub)
 101                rt->out = NULL;
 102        spin_unlock_irqrestore(&rt->out_lock, flags);
 103}
 104
 105static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub)
 106{
 107        struct midi_runtime *rt = alsa_sub->rmidi->private_data;
 108        int retry = 0;
 109
 110        while (rt->out && retry++ < 100)
 111                msleep(10);
 112}
 113
 114static int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub)
 115{
 116        return 0;
 117}
 118
 119static int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub)
 120{
 121        return 0;
 122}
 123
 124static void usb6fire_midi_in_trigger(
 125                struct snd_rawmidi_substream *alsa_sub, int up)
 126{
 127        struct midi_runtime *rt = alsa_sub->rmidi->private_data;
 128        unsigned long flags;
 129
 130        spin_lock_irqsave(&rt->in_lock, flags);
 131        if (up)
 132                rt->in = alsa_sub;
 133        else
 134                rt->in = NULL;
 135        spin_unlock_irqrestore(&rt->in_lock, flags);
 136}
 137
 138static const struct snd_rawmidi_ops out_ops = {
 139        .open = usb6fire_midi_out_open,
 140        .close = usb6fire_midi_out_close,
 141        .trigger = usb6fire_midi_out_trigger,
 142        .drain = usb6fire_midi_out_drain
 143};
 144
 145static const struct snd_rawmidi_ops in_ops = {
 146        .open = usb6fire_midi_in_open,
 147        .close = usb6fire_midi_in_close,
 148        .trigger = usb6fire_midi_in_trigger
 149};
 150
 151int usb6fire_midi_init(struct sfire_chip *chip)
 152{
 153        int ret;
 154        struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime),
 155                        GFP_KERNEL);
 156        struct comm_runtime *comm_rt = chip->comm;
 157
 158        if (!rt)
 159                return -ENOMEM;
 160
 161        rt->out_buffer = kzalloc(MIDI_BUFSIZE, GFP_KERNEL);
 162        if (!rt->out_buffer) {
 163                kfree(rt);
 164                return -ENOMEM;
 165        }
 166
 167        rt->chip = chip;
 168        rt->in_received = usb6fire_midi_in_received;
 169        rt->out_buffer[0] = 0x80; /* 'send midi' command */
 170        rt->out_buffer[1] = 0x00; /* size of data */
 171        rt->out_buffer[2] = 0x00; /* always 0 */
 172        spin_lock_init(&rt->in_lock);
 173        spin_lock_init(&rt->out_lock);
 174
 175        comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt,
 176                        usb6fire_midi_out_handler);
 177
 178        ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance);
 179        if (ret < 0) {
 180                kfree(rt->out_buffer);
 181                kfree(rt);
 182                dev_err(&chip->dev->dev, "unable to create midi.\n");
 183                return ret;
 184        }
 185        rt->instance->private_data = rt;
 186        strcpy(rt->instance->name, "DMX6FireUSB MIDI");
 187        rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
 188                        SNDRV_RAWMIDI_INFO_INPUT |
 189                        SNDRV_RAWMIDI_INFO_DUPLEX;
 190        snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_OUTPUT,
 191                        &out_ops);
 192        snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_INPUT,
 193                        &in_ops);
 194
 195        chip->midi = rt;
 196        return 0;
 197}
 198
 199void usb6fire_midi_abort(struct sfire_chip *chip)
 200{
 201        struct midi_runtime *rt = chip->midi;
 202
 203        if (rt)
 204                usb_poison_urb(&rt->out_urb);
 205}
 206
 207void usb6fire_midi_destroy(struct sfire_chip *chip)
 208{
 209        struct midi_runtime *rt = chip->midi;
 210
 211        kfree(rt->out_buffer);
 212        kfree(rt);
 213        chip->midi = NULL;
 214}
 215