Commit | Line | Data |
---|---|---|
705ececd | 1 | /* |
c078a4aa | 2 | * Line 6 Linux USB driver |
705ececd | 3 | * |
1027f476 | 4 | * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) |
705ececd MG |
5 | * |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License as | |
8 | * published by the Free Software Foundation, version 2. | |
9 | * | |
10 | */ | |
11 | ||
5a0e3ad6 | 12 | #include <linux/slab.h> |
1027f476 | 13 | #include <linux/usb.h> |
ccddbe4a | 14 | #include <linux/export.h> |
705ececd MG |
15 | #include <sound/core.h> |
16 | #include <sound/rawmidi.h> | |
17 | ||
1027f476 | 18 | #include "driver.h" |
705ececd | 19 | #include "midi.h" |
705ececd | 20 | |
d7e37336 GKH |
21 | #define line6_rawmidi_substream_midi(substream) \ |
22 | ((struct snd_line6_midi *)((substream)->rmidi->private_data)) | |
705ececd | 23 | |
d7e37336 GKH |
24 | static int send_midi_async(struct usb_line6 *line6, unsigned char *data, |
25 | int length); | |
705ececd | 26 | |
705ececd MG |
27 | /* |
28 | Pass data received via USB to MIDI. | |
29 | */ | |
d7e37336 GKH |
30 | void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, |
31 | int length) | |
705ececd | 32 | { |
d7e37336 GKH |
33 | if (line6->line6midi->substream_receive) |
34 | snd_rawmidi_receive(line6->line6midi->substream_receive, | |
35 | data, length); | |
705ececd MG |
36 | } |
37 | ||
38 | /* | |
39 | Read data from MIDI buffer and transmit them via USB. | |
40 | */ | |
41 | static void line6_midi_transmit(struct snd_rawmidi_substream *substream) | |
42 | { | |
e1a164d7 MG |
43 | struct usb_line6 *line6 = |
44 | line6_rawmidi_substream_midi(substream)->line6; | |
705ececd | 45 | struct snd_line6_midi *line6midi = line6->line6midi; |
269edc8e | 46 | struct midi_buffer *mb = &line6midi->midibuf_out; |
f2459201 | 47 | unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE]; |
705ececd MG |
48 | int req, done; |
49 | ||
d7e37336 | 50 | for (;;) { |
1027f476 | 51 | req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size); |
705ececd MG |
52 | done = snd_rawmidi_transmit_peek(substream, chunk, req); |
53 | ||
d7e37336 | 54 | if (done == 0) |
705ececd MG |
55 | break; |
56 | ||
1027f476 | 57 | line6_midibuf_write(mb, chunk, done); |
705ececd MG |
58 | snd_rawmidi_transmit_ack(substream, done); |
59 | } | |
60 | ||
d7e37336 | 61 | for (;;) { |
64619003 GK |
62 | done = line6_midibuf_read(mb, chunk, |
63 | LINE6_FALLBACK_MAXPACKETSIZE); | |
705ececd | 64 | |
d7e37336 | 65 | if (done == 0) |
705ececd MG |
66 | break; |
67 | ||
705ececd MG |
68 | send_midi_async(line6, chunk, done); |
69 | } | |
705ececd MG |
70 | } |
71 | ||
72 | /* | |
73 | Notification of completion of MIDI transmission. | |
74 | */ | |
0c7ab158 | 75 | static void midi_sent(struct urb *urb) |
705ececd MG |
76 | { |
77 | unsigned long flags; | |
78 | int status; | |
79 | int num; | |
80 | struct usb_line6 *line6 = (struct usb_line6 *)urb->context; | |
81 | ||
82 | status = urb->status; | |
83 | kfree(urb->transfer_buffer); | |
84 | usb_free_urb(urb); | |
85 | ||
d7e37336 | 86 | if (status == -ESHUTDOWN) |
705ececd MG |
87 | return; |
88 | ||
bc518ba4 | 89 | spin_lock_irqsave(&line6->line6midi->lock, flags); |
705ececd MG |
90 | num = --line6->line6midi->num_active_send_urbs; |
91 | ||
d7e37336 | 92 | if (num == 0) { |
705ececd MG |
93 | line6_midi_transmit(line6->line6midi->substream_transmit); |
94 | num = line6->line6midi->num_active_send_urbs; | |
95 | } | |
96 | ||
d7e37336 | 97 | if (num == 0) |
1027f476 | 98 | wake_up(&line6->line6midi->send_wait); |
705ececd | 99 | |
bc518ba4 | 100 | spin_unlock_irqrestore(&line6->line6midi->lock, flags); |
705ececd MG |
101 | } |
102 | ||
103 | /* | |
104 | Send an asynchronous MIDI message. | |
bc518ba4 | 105 | Assumes that line6->line6midi->lock is held |
705ececd MG |
106 | (i.e., this function is serialized). |
107 | */ | |
d7e37336 GKH |
108 | static int send_midi_async(struct usb_line6 *line6, unsigned char *data, |
109 | int length) | |
705ececd MG |
110 | { |
111 | struct urb *urb; | |
112 | int retval; | |
113 | unsigned char *transfer_buffer; | |
114 | ||
115 | urb = usb_alloc_urb(0, GFP_ATOMIC); | |
116 | ||
a019f5e8 | 117 | if (urb == NULL) |
705ececd | 118 | return -ENOMEM; |
705ececd | 119 | |
cff86387 | 120 | transfer_buffer = kmemdup(data, length, GFP_ATOMIC); |
705ececd | 121 | |
597a1e7c | 122 | if (transfer_buffer == NULL) { |
705ececd | 123 | usb_free_urb(urb); |
705ececd MG |
124 | return -ENOMEM; |
125 | } | |
126 | ||
d7e37336 GKH |
127 | usb_fill_int_urb(urb, line6->usbdev, |
128 | usb_sndbulkpipe(line6->usbdev, | |
9e165be7 | 129 | line6->properties->ep_ctrl_w), |
d7e37336 GKH |
130 | transfer_buffer, length, midi_sent, line6, |
131 | line6->interval); | |
705ececd MG |
132 | urb->actual_length = 0; |
133 | retval = usb_submit_urb(urb, GFP_ATOMIC); | |
134 | ||
d7e37336 | 135 | if (retval < 0) { |
705ececd MG |
136 | dev_err(line6->ifcdev, "usb_submit_urb failed\n"); |
137 | usb_free_urb(urb); | |
88ef9b1b | 138 | return retval; |
705ececd MG |
139 | } |
140 | ||
141 | ++line6->line6midi->num_active_send_urbs; | |
705ececd MG |
142 | return 0; |
143 | } | |
144 | ||
145 | static int line6_midi_output_open(struct snd_rawmidi_substream *substream) | |
146 | { | |
147 | return 0; | |
148 | } | |
149 | ||
150 | static int line6_midi_output_close(struct snd_rawmidi_substream *substream) | |
151 | { | |
152 | return 0; | |
153 | } | |
154 | ||
d7e37336 GKH |
155 | static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, |
156 | int up) | |
705ececd MG |
157 | { |
158 | unsigned long flags; | |
e1a164d7 MG |
159 | struct usb_line6 *line6 = |
160 | line6_rawmidi_substream_midi(substream)->line6; | |
705ececd MG |
161 | |
162 | line6->line6midi->substream_transmit = substream; | |
bc518ba4 | 163 | spin_lock_irqsave(&line6->line6midi->lock, flags); |
705ececd | 164 | |
d7e37336 | 165 | if (line6->line6midi->num_active_send_urbs == 0) |
705ececd MG |
166 | line6_midi_transmit(substream); |
167 | ||
bc518ba4 | 168 | spin_unlock_irqrestore(&line6->line6midi->lock, flags); |
705ececd MG |
169 | } |
170 | ||
171 | static void line6_midi_output_drain(struct snd_rawmidi_substream *substream) | |
172 | { | |
e1a164d7 MG |
173 | struct usb_line6 *line6 = |
174 | line6_rawmidi_substream_midi(substream)->line6; | |
1027f476 | 175 | struct snd_line6_midi *midi = line6->line6midi; |
f3c5261e | 176 | |
e1a164d7 MG |
177 | wait_event_interruptible(midi->send_wait, |
178 | midi->num_active_send_urbs == 0); | |
705ececd MG |
179 | } |
180 | ||
181 | static int line6_midi_input_open(struct snd_rawmidi_substream *substream) | |
182 | { | |
183 | return 0; | |
184 | } | |
185 | ||
186 | static int line6_midi_input_close(struct snd_rawmidi_substream *substream) | |
187 | { | |
188 | return 0; | |
189 | } | |
190 | ||
d7e37336 GKH |
191 | static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, |
192 | int up) | |
705ececd | 193 | { |
e1a164d7 MG |
194 | struct usb_line6 *line6 = |
195 | line6_rawmidi_substream_midi(substream)->line6; | |
705ececd | 196 | |
d7e37336 | 197 | if (up) |
705ececd MG |
198 | line6->line6midi->substream_receive = substream; |
199 | else | |
a0d6f2b1 | 200 | line6->line6midi->substream_receive = NULL; |
705ececd MG |
201 | } |
202 | ||
203 | static struct snd_rawmidi_ops line6_midi_output_ops = { | |
204 | .open = line6_midi_output_open, | |
205 | .close = line6_midi_output_close, | |
206 | .trigger = line6_midi_output_trigger, | |
207 | .drain = line6_midi_output_drain, | |
208 | }; | |
209 | ||
210 | static struct snd_rawmidi_ops line6_midi_input_ops = { | |
211 | .open = line6_midi_input_open, | |
212 | .close = line6_midi_input_close, | |
213 | .trigger = line6_midi_input_trigger, | |
214 | }; | |
215 | ||
705ececd | 216 | /* Create a MIDI device */ |
aaa68d2f TI |
217 | static int snd_line6_new_midi(struct usb_line6 *line6, |
218 | struct snd_rawmidi **rmidi_ret) | |
705ececd MG |
219 | { |
220 | struct snd_rawmidi *rmidi; | |
221 | int err; | |
222 | ||
c6fffce9 | 223 | err = snd_rawmidi_new(line6->card, "Line 6 MIDI", 0, 1, 1, rmidi_ret); |
d7e37336 | 224 | if (err < 0) |
705ececd MG |
225 | return err; |
226 | ||
aaa68d2f TI |
227 | rmidi = *rmidi_ret; |
228 | strcpy(rmidi->id, line6->properties->id); | |
229 | strcpy(rmidi->name, line6->properties->name); | |
705ececd MG |
230 | |
231 | rmidi->info_flags = | |
e1a164d7 MG |
232 | SNDRV_RAWMIDI_INFO_OUTPUT | |
233 | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; | |
705ececd | 234 | |
d7e37336 GKH |
235 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, |
236 | &line6_midi_output_ops); | |
237 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, | |
238 | &line6_midi_input_ops); | |
705ececd MG |
239 | return 0; |
240 | } | |
241 | ||
705ececd | 242 | /* MIDI device destructor */ |
aaa68d2f | 243 | static void snd_line6_midi_free(struct snd_rawmidi *rmidi) |
705ececd | 244 | { |
aaa68d2f | 245 | struct snd_line6_midi *line6midi = rmidi->private_data; |
f3c5261e | 246 | |
1027f476 MG |
247 | line6_midibuf_destroy(&line6midi->midibuf_in); |
248 | line6_midibuf_destroy(&line6midi->midibuf_out); | |
aaa68d2f | 249 | kfree(line6midi); |
705ececd MG |
250 | } |
251 | ||
252 | /* | |
c6fffce9 | 253 | Initialize the Line 6 MIDI subsystem. |
705ececd MG |
254 | */ |
255 | int line6_init_midi(struct usb_line6 *line6) | |
256 | { | |
705ececd | 257 | int err; |
aaa68d2f | 258 | struct snd_rawmidi *rmidi; |
705ececd MG |
259 | struct snd_line6_midi *line6midi; |
260 | ||
7811a3ad | 261 | if (!(line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)) { |
027360c5 GKH |
262 | /* skip MIDI initialization and report success */ |
263 | return 0; | |
264 | } | |
705ececd | 265 | |
aaa68d2f TI |
266 | err = snd_line6_new_midi(line6, &rmidi); |
267 | if (err < 0) | |
268 | return err; | |
705ececd | 269 | |
aaa68d2f TI |
270 | line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL); |
271 | if (!line6midi) | |
705ececd MG |
272 | return -ENOMEM; |
273 | ||
aaa68d2f TI |
274 | rmidi->private_data = line6midi; |
275 | rmidi->private_free = snd_line6_midi_free; | |
705ececd | 276 | |
aaa68d2f | 277 | init_waitqueue_head(&line6midi->send_wait); |
bc518ba4 | 278 | spin_lock_init(&line6midi->lock); |
705ececd | 279 | line6midi->line6 = line6; |
705ececd | 280 | |
aaa68d2f | 281 | err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); |
d7e37336 | 282 | if (err < 0) |
705ececd MG |
283 | return err; |
284 | ||
aaa68d2f | 285 | err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); |
d7e37336 | 286 | if (err < 0) |
705ececd MG |
287 | return err; |
288 | ||
aaa68d2f | 289 | line6->line6midi = line6midi; |
705ececd MG |
290 | return 0; |
291 | } | |
ccddbe4a | 292 | EXPORT_SYMBOL_GPL(line6_init_midi); |