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