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