linux/sound/usb/line6/midi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Line 6 Linux USB driver
   4 *
   5 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
   6 */
   7
   8#include <linux/slab.h>
   9#include <linux/usb.h>
  10#include <linux/export.h>
  11#include <sound/core.h>
  12#include <sound/rawmidi.h>
  13
  14#include "driver.h"
  15#include "midi.h"
  16
  17#define line6_rawmidi_substream_midi(substream) \
  18        ((struct snd_line6_midi *)((substream)->rmidi->private_data))
  19
  20static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
  21                           int length);
  22
  23/*
  24        Pass data received via USB to MIDI.
  25*/
  26void line6_midi_receive(struct usb_line6 *line6, unsigned char *data,
  27                        int length)
  28{
  29        if (line6->line6midi->substream_receive)
  30                snd_rawmidi_receive(line6->line6midi->substream_receive,
  31                                    data, length);
  32}
  33
  34/*
  35        Read data from MIDI buffer and transmit them via USB.
  36*/
  37static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
  38{
  39        struct usb_line6 *line6 =
  40            line6_rawmidi_substream_midi(substream)->line6;
  41        struct snd_line6_midi *line6midi = line6->line6midi;
  42        struct midi_buffer *mb = &line6midi->midibuf_out;
  43        unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE];
  44        int req, done;
  45
  46        for (;;) {
  47                req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size);
  48                done = snd_rawmidi_transmit_peek(substream, chunk, req);
  49
  50                if (done == 0)
  51                        break;
  52
  53                line6_midibuf_write(mb, chunk, done);
  54                snd_rawmidi_transmit_ack(substream, done);
  55        }
  56
  57        for (;;) {
  58                done = line6_midibuf_read(mb, chunk,
  59                                          LINE6_FALLBACK_MAXPACKETSIZE);
  60
  61                if (done == 0)
  62                        break;
  63
  64                send_midi_async(line6, chunk, done);
  65        }
  66}
  67
  68/*
  69        Notification of completion of MIDI transmission.
  70*/
  71static void midi_sent(struct urb *urb)
  72{
  73        unsigned long flags;
  74        int status;
  75        int num;
  76        struct usb_line6 *line6 = (struct usb_line6 *)urb->context;
  77
  78        status = urb->status;
  79        kfree(urb->transfer_buffer);
  80        usb_free_urb(urb);
  81
  82        if (status == -ESHUTDOWN)
  83                return;
  84
  85        spin_lock_irqsave(&line6->line6midi->lock, flags);
  86        num = --line6->line6midi->num_active_send_urbs;
  87
  88        if (num == 0) {
  89                line6_midi_transmit(line6->line6midi->substream_transmit);
  90                num = line6->line6midi->num_active_send_urbs;
  91        }
  92
  93        if (num == 0)
  94                wake_up(&line6->line6midi->send_wait);
  95
  96        spin_unlock_irqrestore(&line6->line6midi->lock, flags);
  97}
  98
  99/*
 100        Send an asynchronous MIDI message.
 101        Assumes that line6->line6midi->lock is held
 102        (i.e., this function is serialized).
 103*/
 104static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
 105                           int length)
 106{
 107        struct urb *urb;
 108        int retval;
 109        unsigned char *transfer_buffer;
 110
 111        urb = usb_alloc_urb(0, GFP_ATOMIC);
 112
 113        if (urb == NULL)
 114                return -ENOMEM;
 115
 116        transfer_buffer = kmemdup(data, length, GFP_ATOMIC);
 117
 118        if (transfer_buffer == NULL) {
 119                usb_free_urb(urb);
 120                return -ENOMEM;
 121        }
 122
 123        usb_fill_int_urb(urb, line6->usbdev,
 124                         usb_sndintpipe(line6->usbdev,
 125                                         line6->properties->ep_ctrl_w),
 126                         transfer_buffer, length, midi_sent, line6,
 127                         line6->interval);
 128        urb->actual_length = 0;
 129        retval = usb_urb_ep_type_check(urb);
 130        if (retval < 0)
 131                goto error;
 132
 133        retval = usb_submit_urb(urb, GFP_ATOMIC);
 134        if (retval < 0)
 135                goto error;
 136
 137        ++line6->line6midi->num_active_send_urbs;
 138        return 0;
 139
 140 error:
 141        dev_err(line6->ifcdev, "usb_submit_urb failed\n");
 142        usb_free_urb(urb);
 143        return retval;
 144}
 145
 146static int line6_midi_output_open(struct snd_rawmidi_substream *substream)
 147{
 148        return 0;
 149}
 150
 151static int line6_midi_output_close(struct snd_rawmidi_substream *substream)
 152{
 153        return 0;
 154}
 155
 156static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream,
 157                                      int up)
 158{
 159        unsigned long flags;
 160        struct usb_line6 *line6 =
 161            line6_rawmidi_substream_midi(substream)->line6;
 162
 163        line6->line6midi->substream_transmit = substream;
 164        spin_lock_irqsave(&line6->line6midi->lock, flags);
 165
 166        if (line6->line6midi->num_active_send_urbs == 0)
 167                line6_midi_transmit(substream);
 168
 169        spin_unlock_irqrestore(&line6->line6midi->lock, flags);
 170}
 171
 172static void line6_midi_output_drain(struct snd_rawmidi_substream *substream)
 173{
 174        struct usb_line6 *line6 =
 175            line6_rawmidi_substream_midi(substream)->line6;
 176        struct snd_line6_midi *midi = line6->line6midi;
 177
 178        wait_event_interruptible(midi->send_wait,
 179                                 midi->num_active_send_urbs == 0);
 180}
 181
 182static int line6_midi_input_open(struct snd_rawmidi_substream *substream)
 183{
 184        return 0;
 185}
 186
 187static int line6_midi_input_close(struct snd_rawmidi_substream *substream)
 188{
 189        return 0;
 190}
 191
 192static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream,
 193                                     int up)
 194{
 195        struct usb_line6 *line6 =
 196            line6_rawmidi_substream_midi(substream)->line6;
 197
 198        if (up)
 199                line6->line6midi->substream_receive = substream;
 200        else
 201                line6->line6midi->substream_receive = NULL;
 202}
 203
 204static const struct snd_rawmidi_ops line6_midi_output_ops = {
 205        .open = line6_midi_output_open,
 206        .close = line6_midi_output_close,
 207        .trigger = line6_midi_output_trigger,
 208        .drain = line6_midi_output_drain,
 209};
 210
 211static const struct snd_rawmidi_ops line6_midi_input_ops = {
 212        .open = line6_midi_input_open,
 213        .close = line6_midi_input_close,
 214        .trigger = line6_midi_input_trigger,
 215};
 216
 217/* Create a MIDI device */
 218static int snd_line6_new_midi(struct usb_line6 *line6,
 219                              struct snd_rawmidi **rmidi_ret)
 220{
 221        struct snd_rawmidi *rmidi;
 222        int err;
 223
 224        err = snd_rawmidi_new(line6->card, "Line 6 MIDI", 0, 1, 1, rmidi_ret);
 225        if (err < 0)
 226                return err;
 227
 228        rmidi = *rmidi_ret;
 229        strcpy(rmidi->id, line6->properties->id);
 230        strcpy(rmidi->name, line6->properties->name);
 231
 232        rmidi->info_flags =
 233            SNDRV_RAWMIDI_INFO_OUTPUT |
 234            SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
 235
 236        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
 237                            &line6_midi_output_ops);
 238        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
 239                            &line6_midi_input_ops);
 240        return 0;
 241}
 242
 243/* MIDI device destructor */
 244static void snd_line6_midi_free(struct snd_rawmidi *rmidi)
 245{
 246        struct snd_line6_midi *line6midi = rmidi->private_data;
 247
 248        line6_midibuf_destroy(&line6midi->midibuf_in);
 249        line6_midibuf_destroy(&line6midi->midibuf_out);
 250        kfree(line6midi);
 251}
 252
 253/*
 254        Initialize the Line 6 MIDI subsystem.
 255*/
 256int line6_init_midi(struct usb_line6 *line6)
 257{
 258        int err;
 259        struct snd_rawmidi *rmidi;
 260        struct snd_line6_midi *line6midi;
 261
 262        if (!(line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)) {
 263                /* skip MIDI initialization and report success */
 264                return 0;
 265        }
 266
 267        err = snd_line6_new_midi(line6, &rmidi);
 268        if (err < 0)
 269                return err;
 270
 271        line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL);
 272        if (!line6midi)
 273                return -ENOMEM;
 274
 275        rmidi->private_data = line6midi;
 276        rmidi->private_free = snd_line6_midi_free;
 277
 278        init_waitqueue_head(&line6midi->send_wait);
 279        spin_lock_init(&line6midi->lock);
 280        line6midi->line6 = line6;
 281
 282        err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);
 283        if (err < 0)
 284                return err;
 285
 286        err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1);
 287        if (err < 0)
 288                return err;
 289
 290        line6->line6midi = line6midi;
 291        return 0;
 292}
 293EXPORT_SYMBOL_GPL(line6_init_midi);
 294