midi.c 7.5 KB
Newer Older
M
Markus Grabner 已提交
1
/*
2
 * Line6 Linux USB driver - 0.9.1beta
M
Markus Grabner 已提交
3
 *
4
 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
M
Markus Grabner 已提交
5 6 7 8 9 10 11
 *
 *	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, version 2.
 *
 */

12
#include <linux/slab.h>
13
#include <linux/usb.h>
M
Markus Grabner 已提交
14 15 16 17
#include <sound/core.h>
#include <sound/rawmidi.h>

#include "audio.h"
18
#include "driver.h"
M
Markus Grabner 已提交
19 20 21 22
#include "midi.h"
#include "pod.h"
#include "usbdefs.h"

23 24
#define line6_rawmidi_substream_midi(substream) \
	((struct snd_line6_midi *)((substream)->rmidi->private_data))
M
Markus Grabner 已提交
25

26 27
static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
			   int length);
M
Markus Grabner 已提交
28 29 30 31

/*
	Pass data received via USB to MIDI.
*/
32 33
void line6_midi_receive(struct usb_line6 *line6, unsigned char *data,
			int length)
M
Markus Grabner 已提交
34
{
35 36 37
	if (line6->line6midi->substream_receive)
		snd_rawmidi_receive(line6->line6midi->substream_receive,
				    data, length);
M
Markus Grabner 已提交
38 39 40 41 42 43 44
}

/*
	Read data from MIDI buffer and transmit them via USB.
*/
static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
{
45 46
	struct usb_line6 *line6 =
	    line6_rawmidi_substream_midi(substream)->line6;
M
Markus Grabner 已提交
47
	struct snd_line6_midi *line6midi = line6->line6midi;
48
	struct midi_buffer *mb = &line6midi->midibuf_out;
M
Markus Grabner 已提交
49
	unsigned long flags;
50
	unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE];
M
Markus Grabner 已提交
51 52 53 54
	int req, done;

	spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags);

55
	for (;;) {
56
		req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size);
M
Markus Grabner 已提交
57 58
		done = snd_rawmidi_transmit_peek(substream, chunk, req);

59
		if (done == 0)
M
Markus Grabner 已提交
60 61
			break;

62
		line6_midibuf_write(mb, chunk, done);
M
Markus Grabner 已提交
63 64 65
		snd_rawmidi_transmit_ack(substream, done);
	}

66
	for (;;) {
67 68
		done = line6_midibuf_read(mb, chunk,
					  LINE6_FALLBACK_MAXPACKETSIZE);
M
Markus Grabner 已提交
69

70
		if (done == 0)
M
Markus Grabner 已提交
71 72 73 74 75 76 77 78 79 80 81
			break;

		send_midi_async(line6, chunk, done);
	}

	spin_unlock_irqrestore(&line6->line6midi->midi_transmit_lock, flags);
}

/*
	Notification of completion of MIDI transmission.
*/
82
static void midi_sent(struct urb *urb)
M
Markus Grabner 已提交
83 84 85 86 87 88 89 90 91 92
{
	unsigned long flags;
	int status;
	int num;
	struct usb_line6 *line6 = (struct usb_line6 *)urb->context;

	status = urb->status;
	kfree(urb->transfer_buffer);
	usb_free_urb(urb);

93
	if (status == -ESHUTDOWN)
M
Markus Grabner 已提交
94 95 96 97 98
		return;

	spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags);
	num = --line6->line6midi->num_active_send_urbs;

99
	if (num == 0) {
M
Markus Grabner 已提交
100 101 102 103
		line6_midi_transmit(line6->line6midi->substream_transmit);
		num = line6->line6midi->num_active_send_urbs;
	}

104
	if (num == 0)
105
		wake_up(&line6->line6midi->send_wait);
M
Markus Grabner 已提交
106 107 108 109 110 111 112 113 114

	spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags);
}

/*
	Send an asynchronous MIDI message.
	Assumes that line6->line6midi->send_urb_lock is held
	(i.e., this function is serialized).
*/
115 116
static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
			   int length)
M
Markus Grabner 已提交
117 118 119 120 121 122 123
{
	struct urb *urb;
	int retval;
	unsigned char *transfer_buffer;

	urb = usb_alloc_urb(0, GFP_ATOMIC);

124
	if (urb == NULL) {
M
Markus Grabner 已提交
125 126 127 128
		dev_err(line6->ifcdev, "Out of memory\n");
		return -ENOMEM;
	}

129
	transfer_buffer = kmemdup(data, length, GFP_ATOMIC);
M
Markus Grabner 已提交
130

131
	if (transfer_buffer == NULL) {
M
Markus Grabner 已提交
132 133 134 135 136
		usb_free_urb(urb);
		dev_err(line6->ifcdev, "Out of memory\n");
		return -ENOMEM;
	}

137 138
	usb_fill_int_urb(urb, line6->usbdev,
			 usb_sndbulkpipe(line6->usbdev,
139
					 line6->properties->ep_ctrl_w),
140 141
			 transfer_buffer, length, midi_sent, line6,
			 line6->interval);
M
Markus Grabner 已提交
142 143 144
	urb->actual_length = 0;
	retval = usb_submit_urb(urb, GFP_ATOMIC);

145
	if (retval < 0) {
M
Markus Grabner 已提交
146 147
		dev_err(line6->ifcdev, "usb_submit_urb failed\n");
		usb_free_urb(urb);
148
		return retval;
M
Markus Grabner 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
	}

	++line6->line6midi->num_active_send_urbs;
	return 0;
}

static int line6_midi_output_open(struct snd_rawmidi_substream *substream)
{
	return 0;
}

static int line6_midi_output_close(struct snd_rawmidi_substream *substream)
{
	return 0;
}

165 166
static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream,
				      int up)
M
Markus Grabner 已提交
167 168
{
	unsigned long flags;
169 170
	struct usb_line6 *line6 =
	    line6_rawmidi_substream_midi(substream)->line6;
M
Markus Grabner 已提交
171 172 173 174

	line6->line6midi->substream_transmit = substream;
	spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags);

175
	if (line6->line6midi->num_active_send_urbs == 0)
M
Markus Grabner 已提交
176 177 178 179 180 181 182
		line6_midi_transmit(substream);

	spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags);
}

static void line6_midi_output_drain(struct snd_rawmidi_substream *substream)
{
183 184
	struct usb_line6 *line6 =
	    line6_rawmidi_substream_midi(substream)->line6;
185
	struct snd_line6_midi *midi = line6->line6midi;
186

187 188
	wait_event_interruptible(midi->send_wait,
				 midi->num_active_send_urbs == 0);
M
Markus Grabner 已提交
189 190 191 192 193 194 195 196 197 198 199 200
}

static int line6_midi_input_open(struct snd_rawmidi_substream *substream)
{
	return 0;
}

static int line6_midi_input_close(struct snd_rawmidi_substream *substream)
{
	return 0;
}

201 202
static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream,
				     int up)
M
Markus Grabner 已提交
203
{
204 205
	struct usb_line6 *line6 =
	    line6_rawmidi_substream_midi(substream)->line6;
M
Markus Grabner 已提交
206

207
	if (up)
M
Markus Grabner 已提交
208 209
		line6->line6midi->substream_receive = substream;
	else
210
		line6->line6midi->substream_receive = NULL;
M
Markus Grabner 已提交
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
}

static struct snd_rawmidi_ops line6_midi_output_ops = {
	.open = line6_midi_output_open,
	.close = line6_midi_output_close,
	.trigger = line6_midi_output_trigger,
	.drain = line6_midi_output_drain,
};

static struct snd_rawmidi_ops line6_midi_input_ops = {
	.open = line6_midi_input_open,
	.close = line6_midi_input_close,
	.trigger = line6_midi_input_trigger,
};

/*
	Cleanup the Line6 MIDI device.
*/
static void line6_cleanup_midi(struct snd_rawmidi *rmidi)
{
}

/* Create a MIDI device */
static int snd_line6_new_midi(struct snd_line6_midi *line6midi)
{
	struct snd_rawmidi *rmidi;
	int err;

239 240 241
	err = snd_rawmidi_new(line6midi->line6->card, "Line6 MIDI", 0, 1, 1,
			      &rmidi);
	if (err < 0)
M
Markus Grabner 已提交
242 243 244 245
		return err;

	rmidi->private_data = line6midi;
	rmidi->private_free = line6_cleanup_midi;
246
	strcpy(rmidi->id, line6midi->line6->properties->id);
M
Markus Grabner 已提交
247 248 249
	strcpy(rmidi->name, line6midi->line6->properties->name);

	rmidi->info_flags =
250 251
	    SNDRV_RAWMIDI_INFO_OUTPUT |
	    SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
M
Markus Grabner 已提交
252

253 254 255 256
	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
			    &line6_midi_output_ops);
	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
			    &line6_midi_input_ops);
M
Markus Grabner 已提交
257 258 259 260 261 262 263
	return 0;
}

/* MIDI device destructor */
static int snd_line6_midi_free(struct snd_device *device)
{
	struct snd_line6_midi *line6midi = device->device_data;
264

265 266
	line6_midibuf_destroy(&line6midi->midibuf_in);
	line6_midibuf_destroy(&line6midi->midibuf_out);
M
Markus Grabner 已提交
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
	return 0;
}

/*
	Initialize the Line6 MIDI subsystem.
*/
int line6_init_midi(struct usb_line6 *line6)
{
	static struct snd_device_ops midi_ops = {
		.dev_free = snd_line6_midi_free,
	};

	int err;
	struct snd_line6_midi *line6midi;

282
	if (!(line6->properties->capabilities & LINE6_CAP_CONTROL)) {
283 284 285
		/* skip MIDI initialization and report success */
		return 0;
	}
M
Markus Grabner 已提交
286 287 288

	line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL);

289
	if (line6midi == NULL)
M
Markus Grabner 已提交
290 291
		return -ENOMEM;

292
	err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);
293 294
	if (err < 0) {
		kfree(line6midi);
M
Markus Grabner 已提交
295
		return err;
296
	}
M
Markus Grabner 已提交
297

298
	err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1);
299 300 301
	if (err < 0) {
		kfree(line6midi->midibuf_in.buf);
		kfree(line6midi);
M
Markus Grabner 已提交
302
		return err;
303
	}
M
Markus Grabner 已提交
304 305 306 307

	line6midi->line6 = line6;
	line6->line6midi = line6midi;

308 309 310
	err = snd_device_new(line6->card, SNDRV_DEV_RAWMIDI, line6midi,
			     &midi_ops);
	if (err < 0)
M
Markus Grabner 已提交
311 312
		return err;

313 314
	err = snd_line6_new_midi(line6midi);
	if (err < 0)
M
Markus Grabner 已提交
315 316 317 318 319 320 321
		return err;

	init_waitqueue_head(&line6midi->send_wait);
	spin_lock_init(&line6midi->send_urb_lock);
	spin_lock_init(&line6midi->midi_transmit_lock);
	return 0;
}