|  | /* | 
|  | * Linux driver for TerraTec DMX 6Fire USB | 
|  | * | 
|  | * Rawmidi driver | 
|  | * | 
|  | * Author:	Torsten Schenk <torsten.schenk@zoho.com> | 
|  | * Created:	Jan 01, 2011 | 
|  | * Copyright:	(C) Torsten Schenk | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify | 
|  | * it under the terms of the GNU General Public License as published by | 
|  | * the Free Software Foundation; either version 2 of the License, or | 
|  | * (at your option) any later version. | 
|  | */ | 
|  |  | 
|  | #include <sound/rawmidi.h> | 
|  |  | 
|  | #include "midi.h" | 
|  | #include "chip.h" | 
|  | #include "comm.h" | 
|  |  | 
|  | 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) | 
|  | snd_printk(KERN_ERR PREFIX "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) | 
|  | snd_printk(KERN_ERR PREFIX "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 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 struct snd_rawmidi_ops in_ops = { | 
|  | .open = usb6fire_midi_in_open, | 
|  | .close = usb6fire_midi_in_close, | 
|  | .trigger = usb6fire_midi_in_trigger | 
|  | }; | 
|  |  | 
|  | int __devinit 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->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); | 
|  | snd_printk(KERN_ERR PREFIX "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) | 
|  | { | 
|  | kfree(chip->midi); | 
|  | chip->midi = NULL; | 
|  | } |