root/sound/usb/line6/midi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. line6_midi_receive
  2. line6_midi_transmit
  3. midi_sent
  4. send_midi_async
  5. line6_midi_output_open
  6. line6_midi_output_close
  7. line6_midi_output_trigger
  8. line6_midi_output_drain
  9. line6_midi_input_open
  10. line6_midi_input_close
  11. line6_midi_input_trigger
  12. snd_line6_new_midi
  13. snd_line6_midi_free
  14. line6_init_midi

   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 
  20 static 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 */
  26 void 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 */
  37 static 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 */
  71 static 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 */
 104 static 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 
 146 static int line6_midi_output_open(struct snd_rawmidi_substream *substream)
 147 {
 148         return 0;
 149 }
 150 
 151 static int line6_midi_output_close(struct snd_rawmidi_substream *substream)
 152 {
 153         return 0;
 154 }
 155 
 156 static 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 
 172 static 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 
 182 static int line6_midi_input_open(struct snd_rawmidi_substream *substream)
 183 {
 184         return 0;
 185 }
 186 
 187 static int line6_midi_input_close(struct snd_rawmidi_substream *substream)
 188 {
 189         return 0;
 190 }
 191 
 192 static 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 
 204 static 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 
 211 static 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 */
 218 static 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 */
 244 static 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 */
 256 int 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 }
 293 EXPORT_SYMBOL_GPL(line6_init_midi);

/* [<][>][^][v][top][bottom][index][help] */