]>
Commit | Line | Data |
---|---|---|
705ececd MG |
1 | /* |
2 | * Line6 Linux USB driver - 0.8.0 | |
3 | * | |
4 | * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) | |
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 | ||
12 | #include "driver.h" | |
13 | ||
14 | #include <linux/usb.h> | |
5a0e3ad6 | 15 | #include <linux/slab.h> |
705ececd MG |
16 | |
17 | #include <sound/core.h> | |
18 | #include <sound/rawmidi.h> | |
19 | ||
20 | #include "audio.h" | |
21 | #include "midi.h" | |
22 | #include "pod.h" | |
23 | #include "usbdefs.h" | |
24 | ||
25 | ||
26 | #define USE_MIDIBUF 1 | |
27 | #define OUTPUT_DUMP_ONLY 0 | |
28 | ||
29 | ||
d7e37336 GKH |
30 | #define line6_rawmidi_substream_midi(substream) \ |
31 | ((struct snd_line6_midi *)((substream)->rmidi->private_data)) | |
705ececd MG |
32 | |
33 | ||
d7e37336 GKH |
34 | static int send_midi_async(struct usb_line6 *line6, unsigned char *data, |
35 | int length); | |
705ececd MG |
36 | |
37 | ||
38 | /* | |
39 | Pass data received via USB to MIDI. | |
40 | */ | |
d7e37336 GKH |
41 | void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, |
42 | int length) | |
705ececd | 43 | { |
d7e37336 GKH |
44 | if (line6->line6midi->substream_receive) |
45 | snd_rawmidi_receive(line6->line6midi->substream_receive, | |
46 | data, length); | |
705ececd MG |
47 | } |
48 | ||
49 | /* | |
50 | Read data from MIDI buffer and transmit them via USB. | |
51 | */ | |
52 | static void line6_midi_transmit(struct snd_rawmidi_substream *substream) | |
53 | { | |
54 | struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6; | |
55 | struct snd_line6_midi *line6midi = line6->line6midi; | |
56 | struct MidiBuffer *mb = &line6midi->midibuf_out; | |
57 | unsigned long flags; | |
58 | unsigned char chunk[line6->max_packet_size]; | |
59 | int req, done; | |
60 | ||
61 | spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags); | |
62 | ||
d7e37336 | 63 | for (;;) { |
705ececd MG |
64 | req = min(midibuf_bytes_free(mb), line6->max_packet_size); |
65 | done = snd_rawmidi_transmit_peek(substream, chunk, req); | |
66 | ||
d7e37336 | 67 | if (done == 0) |
705ececd MG |
68 | break; |
69 | ||
70 | #if DO_DUMP_MIDI_SEND | |
71 | line6_write_hexdump(line6, 's', chunk, done); | |
72 | #endif | |
73 | midibuf_write(mb, chunk, done); | |
74 | snd_rawmidi_transmit_ack(substream, done); | |
75 | } | |
76 | ||
d7e37336 | 77 | for (;;) { |
705ececd MG |
78 | done = midibuf_read(mb, chunk, line6->max_packet_size); |
79 | ||
d7e37336 | 80 | if (done == 0) |
705ececd MG |
81 | break; |
82 | ||
d7e37336 | 83 | if (midibuf_skip_message(mb, line6midi->midi_mask_transmit)) |
705ececd MG |
84 | continue; |
85 | ||
86 | send_midi_async(line6, chunk, done); | |
87 | } | |
88 | ||
89 | spin_unlock_irqrestore(&line6->line6midi->midi_transmit_lock, flags); | |
90 | } | |
91 | ||
92 | /* | |
93 | Notification of completion of MIDI transmission. | |
94 | */ | |
0c7ab158 | 95 | static void midi_sent(struct urb *urb) |
705ececd MG |
96 | { |
97 | unsigned long flags; | |
98 | int status; | |
99 | int num; | |
100 | struct usb_line6 *line6 = (struct usb_line6 *)urb->context; | |
101 | ||
102 | status = urb->status; | |
103 | kfree(urb->transfer_buffer); | |
104 | usb_free_urb(urb); | |
105 | ||
d7e37336 | 106 | if (status == -ESHUTDOWN) |
705ececd MG |
107 | return; |
108 | ||
109 | spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); | |
110 | num = --line6->line6midi->num_active_send_urbs; | |
111 | ||
d7e37336 | 112 | if (num == 0) { |
705ececd MG |
113 | line6_midi_transmit(line6->line6midi->substream_transmit); |
114 | num = line6->line6midi->num_active_send_urbs; | |
115 | } | |
116 | ||
d7e37336 | 117 | if (num == 0) |
705ececd MG |
118 | wake_up_interruptible(&line6->line6midi->send_wait); |
119 | ||
120 | spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); | |
121 | } | |
122 | ||
123 | /* | |
124 | Send an asynchronous MIDI message. | |
125 | Assumes that line6->line6midi->send_urb_lock is held | |
126 | (i.e., this function is serialized). | |
127 | */ | |
d7e37336 GKH |
128 | static int send_midi_async(struct usb_line6 *line6, unsigned char *data, |
129 | int length) | |
705ececd MG |
130 | { |
131 | struct urb *urb; | |
132 | int retval; | |
133 | unsigned char *transfer_buffer; | |
134 | ||
135 | urb = usb_alloc_urb(0, GFP_ATOMIC); | |
136 | ||
d7e37336 | 137 | if (urb == 0) { |
705ececd MG |
138 | dev_err(line6->ifcdev, "Out of memory\n"); |
139 | return -ENOMEM; | |
140 | } | |
141 | ||
142 | #if DO_DUMP_URB_SEND | |
143 | line6_write_hexdump(line6, 'S', data, length); | |
144 | #endif | |
145 | ||
d7e37336 | 146 | transfer_buffer = kmalloc(length, GFP_ATOMIC); |
705ececd | 147 | |
d7e37336 | 148 | if (transfer_buffer == 0) { |
705ececd MG |
149 | usb_free_urb(urb); |
150 | dev_err(line6->ifcdev, "Out of memory\n"); | |
151 | return -ENOMEM; | |
152 | } | |
153 | ||
154 | memcpy(transfer_buffer, data, length); | |
d7e37336 GKH |
155 | usb_fill_int_urb(urb, line6->usbdev, |
156 | usb_sndbulkpipe(line6->usbdev, | |
157 | line6->ep_control_write), | |
158 | transfer_buffer, length, midi_sent, line6, | |
159 | line6->interval); | |
705ececd MG |
160 | urb->actual_length = 0; |
161 | retval = usb_submit_urb(urb, GFP_ATOMIC); | |
162 | ||
d7e37336 | 163 | if (retval < 0) { |
705ececd MG |
164 | dev_err(line6->ifcdev, "usb_submit_urb failed\n"); |
165 | usb_free_urb(urb); | |
166 | return -EINVAL; | |
167 | } | |
168 | ||
169 | ++line6->line6midi->num_active_send_urbs; | |
170 | ||
d7e37336 | 171 | switch (line6->usbdev->descriptor.idProduct) { |
705ececd MG |
172 | case LINE6_DEVID_BASSPODXT: |
173 | case LINE6_DEVID_BASSPODXTLIVE: | |
174 | case LINE6_DEVID_BASSPODXTPRO: | |
175 | case LINE6_DEVID_PODXT: | |
176 | case LINE6_DEVID_PODXTLIVE: | |
177 | case LINE6_DEVID_PODXTPRO: | |
178 | case LINE6_DEVID_POCKETPOD: | |
d7e37336 GKH |
179 | pod_midi_postprocess((struct usb_line6_pod *)line6, data, |
180 | length); | |
705ececd MG |
181 | break; |
182 | ||
183 | default: | |
184 | MISSING_CASE; | |
185 | } | |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
190 | static int line6_midi_output_open(struct snd_rawmidi_substream *substream) | |
191 | { | |
192 | return 0; | |
193 | } | |
194 | ||
195 | static int line6_midi_output_close(struct snd_rawmidi_substream *substream) | |
196 | { | |
197 | return 0; | |
198 | } | |
199 | ||
d7e37336 GKH |
200 | static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, |
201 | int up) | |
705ececd MG |
202 | { |
203 | unsigned long flags; | |
204 | struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6; | |
205 | ||
206 | line6->line6midi->substream_transmit = substream; | |
207 | spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); | |
208 | ||
d7e37336 | 209 | if (line6->line6midi->num_active_send_urbs == 0) |
705ececd MG |
210 | line6_midi_transmit(substream); |
211 | ||
212 | spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); | |
213 | } | |
214 | ||
215 | static void line6_midi_output_drain(struct snd_rawmidi_substream *substream) | |
216 | { | |
217 | struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6; | |
218 | wait_queue_head_t *head = &line6->line6midi->send_wait; | |
219 | DECLARE_WAITQUEUE(wait, current); | |
220 | add_wait_queue(head, &wait); | |
221 | current->state = TASK_INTERRUPTIBLE; | |
222 | ||
d7e37336 GKH |
223 | while (line6->line6midi->num_active_send_urbs > 0) |
224 | if (signal_pending(current)) | |
705ececd MG |
225 | break; |
226 | else | |
227 | schedule(); | |
228 | ||
229 | current->state = TASK_RUNNING; | |
230 | remove_wait_queue(head, &wait); | |
231 | } | |
232 | ||
233 | static int line6_midi_input_open(struct snd_rawmidi_substream *substream) | |
234 | { | |
235 | return 0; | |
236 | } | |
237 | ||
238 | static int line6_midi_input_close(struct snd_rawmidi_substream *substream) | |
239 | { | |
240 | return 0; | |
241 | } | |
242 | ||
d7e37336 GKH |
243 | static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, |
244 | int up) | |
705ececd MG |
245 | { |
246 | struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6; | |
247 | ||
d7e37336 | 248 | if (up) |
705ececd MG |
249 | line6->line6midi->substream_receive = substream; |
250 | else | |
251 | line6->line6midi->substream_receive = 0; | |
252 | } | |
253 | ||
254 | static struct snd_rawmidi_ops line6_midi_output_ops = { | |
255 | .open = line6_midi_output_open, | |
256 | .close = line6_midi_output_close, | |
257 | .trigger = line6_midi_output_trigger, | |
258 | .drain = line6_midi_output_drain, | |
259 | }; | |
260 | ||
261 | static struct snd_rawmidi_ops line6_midi_input_ops = { | |
262 | .open = line6_midi_input_open, | |
263 | .close = line6_midi_input_close, | |
264 | .trigger = line6_midi_input_trigger, | |
265 | }; | |
266 | ||
267 | /* | |
268 | Cleanup the Line6 MIDI device. | |
269 | */ | |
270 | static void line6_cleanup_midi(struct snd_rawmidi *rmidi) | |
271 | { | |
272 | } | |
273 | ||
274 | /* Create a MIDI device */ | |
275 | static int snd_line6_new_midi(struct snd_line6_midi *line6midi) | |
276 | { | |
277 | struct snd_rawmidi *rmidi; | |
278 | int err; | |
279 | ||
d7e37336 GKH |
280 | err = snd_rawmidi_new(line6midi->line6->card, "Line6 MIDI", 0, 1, 1, |
281 | &rmidi); | |
282 | if (err < 0) | |
705ececd MG |
283 | return err; |
284 | ||
285 | rmidi->private_data = line6midi; | |
286 | rmidi->private_free = line6_cleanup_midi; | |
287 | strcpy(rmidi->name, line6midi->line6->properties->name); | |
288 | ||
289 | rmidi->info_flags = | |
290 | SNDRV_RAWMIDI_INFO_OUTPUT | | |
291 | SNDRV_RAWMIDI_INFO_INPUT | | |
292 | SNDRV_RAWMIDI_INFO_DUPLEX; | |
293 | ||
d7e37336 GKH |
294 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, |
295 | &line6_midi_output_ops); | |
296 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, | |
297 | &line6_midi_input_ops); | |
705ececd MG |
298 | return 0; |
299 | } | |
300 | ||
301 | /* | |
302 | "read" request on "midi_mask_transmit" special file. | |
303 | */ | |
77491e52 GKH |
304 | static ssize_t midi_get_midi_mask_transmit(struct device *dev, |
305 | struct device_attribute *attr, | |
306 | char *buf) | |
705ececd MG |
307 | { |
308 | struct usb_interface *interface = to_usb_interface(dev); | |
309 | struct usb_line6 *line6 = usb_get_intfdata(interface); | |
310 | return sprintf(buf, "%d\n", line6->line6midi->midi_mask_transmit); | |
311 | } | |
312 | ||
313 | /* | |
314 | "write" request on "midi_mask" special file. | |
315 | */ | |
77491e52 GKH |
316 | static ssize_t midi_set_midi_mask_transmit(struct device *dev, |
317 | struct device_attribute *attr, | |
318 | const char *buf, size_t count) | |
705ececd MG |
319 | { |
320 | struct usb_interface *interface = to_usb_interface(dev); | |
321 | struct usb_line6 *line6 = usb_get_intfdata(interface); | |
334a33d8 SB |
322 | unsigned long value; |
323 | int ret; | |
324 | ||
325 | ret = strict_strtoul(buf, 10, &value); | |
326 | if (ret) | |
327 | return ret; | |
328 | ||
705ececd MG |
329 | line6->line6midi->midi_mask_transmit = value; |
330 | return count; | |
331 | } | |
332 | ||
333 | /* | |
334 | "read" request on "midi_mask_receive" special file. | |
335 | */ | |
77491e52 GKH |
336 | static ssize_t midi_get_midi_mask_receive(struct device *dev, |
337 | struct device_attribute *attr, | |
338 | char *buf) | |
705ececd MG |
339 | { |
340 | struct usb_interface *interface = to_usb_interface(dev); | |
341 | struct usb_line6 *line6 = usb_get_intfdata(interface); | |
342 | return sprintf(buf, "%d\n", line6->line6midi->midi_mask_receive); | |
343 | } | |
344 | ||
345 | /* | |
346 | "write" request on "midi_mask" special file. | |
347 | */ | |
77491e52 GKH |
348 | static ssize_t midi_set_midi_mask_receive(struct device *dev, |
349 | struct device_attribute *attr, | |
350 | const char *buf, size_t count) | |
705ececd MG |
351 | { |
352 | struct usb_interface *interface = to_usb_interface(dev); | |
353 | struct usb_line6 *line6 = usb_get_intfdata(interface); | |
334a33d8 SB |
354 | unsigned long value; |
355 | int ret; | |
356 | ||
357 | ret = strict_strtoul(buf, 10, &value); | |
358 | if (ret) | |
359 | return ret; | |
360 | ||
705ececd MG |
361 | line6->line6midi->midi_mask_receive = value; |
362 | return count; | |
363 | } | |
364 | ||
365 | static DEVICE_ATTR(midi_mask_transmit, S_IWUGO | S_IRUGO, midi_get_midi_mask_transmit, midi_set_midi_mask_transmit); | |
366 | static DEVICE_ATTR(midi_mask_receive, S_IWUGO | S_IRUGO, midi_get_midi_mask_receive, midi_set_midi_mask_receive); | |
367 | ||
368 | /* MIDI device destructor */ | |
369 | static int snd_line6_midi_free(struct snd_device *device) | |
370 | { | |
371 | struct snd_line6_midi *line6midi = device->device_data; | |
372 | device_remove_file(line6midi->line6->ifcdev, &dev_attr_midi_mask_transmit); | |
373 | device_remove_file(line6midi->line6->ifcdev, &dev_attr_midi_mask_receive); | |
374 | midibuf_destroy(&line6midi->midibuf_in); | |
375 | midibuf_destroy(&line6midi->midibuf_out); | |
376 | return 0; | |
377 | } | |
378 | ||
379 | /* | |
380 | Initialize the Line6 MIDI subsystem. | |
381 | */ | |
382 | int line6_init_midi(struct usb_line6 *line6) | |
383 | { | |
384 | static struct snd_device_ops midi_ops = { | |
385 | .dev_free = snd_line6_midi_free, | |
386 | }; | |
387 | ||
388 | int err; | |
389 | struct snd_line6_midi *line6midi; | |
390 | ||
d7e37336 | 391 | if (!(line6->properties->capabilities & LINE6_BIT_CONTROL)) |
705ececd MG |
392 | return 0; /* skip MIDI initialization and report success */ |
393 | ||
394 | line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL); | |
395 | ||
d7e37336 | 396 | if (line6midi == NULL) |
705ececd MG |
397 | return -ENOMEM; |
398 | ||
399 | err = midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); | |
d7e37336 | 400 | if (err < 0) |
705ececd MG |
401 | return err; |
402 | ||
403 | err = midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); | |
d7e37336 | 404 | if (err < 0) |
705ececd MG |
405 | return err; |
406 | ||
407 | line6midi->line6 = line6; | |
408 | line6midi->midi_mask_transmit = 1; | |
409 | line6midi->midi_mask_receive = 4; | |
410 | line6->line6midi = line6midi; | |
411 | ||
d7e37336 GKH |
412 | err = snd_device_new(line6->card, SNDRV_DEV_RAWMIDI, line6midi, |
413 | &midi_ops); | |
414 | if (err < 0) | |
705ececd MG |
415 | return err; |
416 | ||
417 | snd_card_set_dev(line6->card, line6->ifcdev); | |
418 | ||
d7e37336 GKH |
419 | err = snd_line6_new_midi(line6midi); |
420 | if (err < 0) | |
705ececd MG |
421 | return err; |
422 | ||
d7e37336 GKH |
423 | err = device_create_file(line6->ifcdev, &dev_attr_midi_mask_transmit); |
424 | if (err < 0) | |
705ececd MG |
425 | return err; |
426 | ||
d7e37336 GKH |
427 | err = device_create_file(line6->ifcdev, &dev_attr_midi_mask_receive); |
428 | if (err < 0) | |
705ececd MG |
429 | return err; |
430 | ||
431 | init_waitqueue_head(&line6midi->send_wait); | |
432 | spin_lock_init(&line6midi->send_urb_lock); | |
433 | spin_lock_init(&line6midi->midi_transmit_lock); | |
434 | return 0; | |
435 | } |