forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
4.9 KiB
214 lines
4.9 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* Linux driver for TerraTec DMX 6Fire USB |
|
* |
|
* Rawmidi driver |
|
* |
|
* Author: Torsten Schenk <[email protected]> |
|
* Created: Jan 01, 2011 |
|
* Copyright: (C) Torsten Schenk |
|
*/ |
|
|
|
#include <sound/rawmidi.h> |
|
|
|
#include "midi.h" |
|
#include "chip.h" |
|
#include "comm.h" |
|
|
|
enum { |
|
MIDI_BUFSIZE = 64 |
|
}; |
|
|
|
static void usb6fire_midi_out_handler(struct urb *urb) |
|
{ |
|
struct midi_runtime *rt = urb->context; |
|
int ret; |
|
unsigned long flags; |
|
|
|
spin_lock_irqsave(&rt->out_lock, flags); |
|
|
|
if (rt->out) { |
|
ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4, |
|
MIDI_BUFSIZE - 4); |
|
if (ret > 0) { /* more data available, send next packet */ |
|
rt->out_buffer[1] = ret + 2; |
|
rt->out_buffer[3] = rt->out_serial++; |
|
urb->transfer_buffer_length = ret + 4; |
|
|
|
ret = usb_submit_urb(urb, GFP_ATOMIC); |
|
if (ret < 0) |
|
dev_err(&urb->dev->dev, |
|
"midi out urb submit failed: %d\n", |
|
ret); |
|
} else /* no more data to transmit */ |
|
rt->out = NULL; |
|
} |
|
spin_unlock_irqrestore(&rt->out_lock, flags); |
|
} |
|
|
|
static void usb6fire_midi_in_received( |
|
struct midi_runtime *rt, u8 *data, int length) |
|
{ |
|
unsigned long flags; |
|
|
|
spin_lock_irqsave(&rt->in_lock, flags); |
|
if (rt->in) |
|
snd_rawmidi_receive(rt->in, data, length); |
|
spin_unlock_irqrestore(&rt->in_lock, flags); |
|
} |
|
|
|
static int usb6fire_midi_out_open(struct snd_rawmidi_substream *alsa_sub) |
|
{ |
|
return 0; |
|
} |
|
|
|
static int usb6fire_midi_out_close(struct snd_rawmidi_substream *alsa_sub) |
|
{ |
|
return 0; |
|
} |
|
|
|
static void usb6fire_midi_out_trigger( |
|
struct snd_rawmidi_substream *alsa_sub, int up) |
|
{ |
|
struct midi_runtime *rt = alsa_sub->rmidi->private_data; |
|
struct urb *urb = &rt->out_urb; |
|
__s8 ret; |
|
unsigned long flags; |
|
|
|
spin_lock_irqsave(&rt->out_lock, flags); |
|
if (up) { /* start transfer */ |
|
if (rt->out) { /* we are already transmitting so just return */ |
|
spin_unlock_irqrestore(&rt->out_lock, flags); |
|
return; |
|
} |
|
|
|
ret = snd_rawmidi_transmit(alsa_sub, rt->out_buffer + 4, |
|
MIDI_BUFSIZE - 4); |
|
if (ret > 0) { |
|
rt->out_buffer[1] = ret + 2; |
|
rt->out_buffer[3] = rt->out_serial++; |
|
urb->transfer_buffer_length = ret + 4; |
|
|
|
ret = usb_submit_urb(urb, GFP_ATOMIC); |
|
if (ret < 0) |
|
dev_err(&urb->dev->dev, |
|
"midi out urb submit failed: %d\n", |
|
ret); |
|
else |
|
rt->out = alsa_sub; |
|
} |
|
} else if (rt->out == alsa_sub) |
|
rt->out = NULL; |
|
spin_unlock_irqrestore(&rt->out_lock, flags); |
|
} |
|
|
|
static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub) |
|
{ |
|
struct midi_runtime *rt = alsa_sub->rmidi->private_data; |
|
int retry = 0; |
|
|
|
while (rt->out && retry++ < 100) |
|
msleep(10); |
|
} |
|
|
|
static int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub) |
|
{ |
|
return 0; |
|
} |
|
|
|
static int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub) |
|
{ |
|
return 0; |
|
} |
|
|
|
static void usb6fire_midi_in_trigger( |
|
struct snd_rawmidi_substream *alsa_sub, int up) |
|
{ |
|
struct midi_runtime *rt = alsa_sub->rmidi->private_data; |
|
unsigned long flags; |
|
|
|
spin_lock_irqsave(&rt->in_lock, flags); |
|
if (up) |
|
rt->in = alsa_sub; |
|
else |
|
rt->in = NULL; |
|
spin_unlock_irqrestore(&rt->in_lock, flags); |
|
} |
|
|
|
static const struct snd_rawmidi_ops out_ops = { |
|
.open = usb6fire_midi_out_open, |
|
.close = usb6fire_midi_out_close, |
|
.trigger = usb6fire_midi_out_trigger, |
|
.drain = usb6fire_midi_out_drain |
|
}; |
|
|
|
static const struct snd_rawmidi_ops in_ops = { |
|
.open = usb6fire_midi_in_open, |
|
.close = usb6fire_midi_in_close, |
|
.trigger = usb6fire_midi_in_trigger |
|
}; |
|
|
|
int usb6fire_midi_init(struct sfire_chip *chip) |
|
{ |
|
int ret; |
|
struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime), |
|
GFP_KERNEL); |
|
struct comm_runtime *comm_rt = chip->comm; |
|
|
|
if (!rt) |
|
return -ENOMEM; |
|
|
|
rt->out_buffer = kzalloc(MIDI_BUFSIZE, GFP_KERNEL); |
|
if (!rt->out_buffer) { |
|
kfree(rt); |
|
return -ENOMEM; |
|
} |
|
|
|
rt->chip = chip; |
|
rt->in_received = usb6fire_midi_in_received; |
|
rt->out_buffer[0] = 0x80; /* 'send midi' command */ |
|
rt->out_buffer[1] = 0x00; /* size of data */ |
|
rt->out_buffer[2] = 0x00; /* always 0 */ |
|
spin_lock_init(&rt->in_lock); |
|
spin_lock_init(&rt->out_lock); |
|
|
|
comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt, |
|
usb6fire_midi_out_handler); |
|
|
|
ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance); |
|
if (ret < 0) { |
|
kfree(rt->out_buffer); |
|
kfree(rt); |
|
dev_err(&chip->dev->dev, "unable to create midi.\n"); |
|
return ret; |
|
} |
|
rt->instance->private_data = rt; |
|
strcpy(rt->instance->name, "DMX6FireUSB MIDI"); |
|
rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | |
|
SNDRV_RAWMIDI_INFO_INPUT | |
|
SNDRV_RAWMIDI_INFO_DUPLEX; |
|
snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_OUTPUT, |
|
&out_ops); |
|
snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_INPUT, |
|
&in_ops); |
|
|
|
chip->midi = rt; |
|
return 0; |
|
} |
|
|
|
void usb6fire_midi_abort(struct sfire_chip *chip) |
|
{ |
|
struct midi_runtime *rt = chip->midi; |
|
|
|
if (rt) |
|
usb_poison_urb(&rt->out_urb); |
|
} |
|
|
|
void usb6fire_midi_destroy(struct sfire_chip *chip) |
|
{ |
|
struct midi_runtime *rt = chip->midi; |
|
|
|
kfree(rt->out_buffer); |
|
kfree(rt); |
|
chip->midi = NULL; |
|
}
|
|
|