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