brintos

brintos / linux-shallow public Read only

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