diff options
author | Markus Grabner <grabner@icg.tugraz.at> | 2009-02-27 22:43:04 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-04-03 17:54:24 -0400 |
commit | 705ececd1c60d0f5d6ef2a719008847883516970 (patch) | |
tree | e2a96ac85e15850919e493181fe47f7fdd471af1 /drivers/staging | |
parent | e642f09951f7cbb69983781b07bb9cd881546ac4 (diff) |
Staging: add line6 usb driver
This is an experimental Linux driver for the guitar amp, cab, and
effects modeller PODxt Pro by Line6 (and similar devices), supporting
the following features:
- Reading/writing individual parameters
- Reading/writing complete channel, effects setup, and amp setup data
- Channel switching
- Virtual MIDI interface
- Tuner access
- Playback/capture/mixer device for any ALSA-compatible PCM audio
application
- Signal routing (record clean/processed guitar signal, re-amping)
Moreover, preliminary support for the Variax Workbench is included.
From: Markus Grabner <grabner@icg.tugraz.at>
Cc: Mariusz Kozlowski <m.kozlowski@tuxland.pl>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging')
29 files changed, 6962 insertions, 0 deletions
diff --git a/drivers/staging/line6/Kconfig b/drivers/staging/line6/Kconfig new file mode 100644 index 000000000000..3c1ffcb8cb12 --- /dev/null +++ b/drivers/staging/line6/Kconfig | |||
@@ -0,0 +1,20 @@ | |||
1 | config LINE6_USB | ||
2 | tristate "Line6 USB support" | ||
3 | depends on USB | ||
4 | help | ||
5 | This is a driver for the guitar amp, cab, and effects modeller | ||
6 | PODxt Pro by Line6 (and similar devices), supporting the | ||
7 | following features: | ||
8 | * Reading/writing individual parameters | ||
9 | * Reading/writing complete channel, effects setup, and amp | ||
10 | setup data | ||
11 | * Channel switching | ||
12 | * Virtual MIDI interface | ||
13 | * Tuner access | ||
14 | * Playback/capture/mixer device for any ALSA-compatible PCM | ||
15 | audio application | ||
16 | * Signal routing (record clean/processed guitar signal, | ||
17 | re-amping) | ||
18 | |||
19 | Preliminary support for the Variax Workbench is included. | ||
20 | |||
diff --git a/drivers/staging/line6/Makefile b/drivers/staging/line6/Makefile new file mode 100644 index 000000000000..a1c93edc6b1d --- /dev/null +++ b/drivers/staging/line6/Makefile | |||
@@ -0,0 +1,15 @@ | |||
1 | obj-$(CONFIG_LINE6_USB) += line6usb.o | ||
2 | |||
3 | line6usb-objs := \ | ||
4 | audio.o \ | ||
5 | capture.o \ | ||
6 | control.o \ | ||
7 | driver.o \ | ||
8 | dumprequest.o \ | ||
9 | midi.o \ | ||
10 | midibuf.o \ | ||
11 | pcm.o \ | ||
12 | playback.o \ | ||
13 | pod.o \ | ||
14 | toneport.o \ | ||
15 | variax.o | ||
diff --git a/drivers/staging/line6/audio.c b/drivers/staging/line6/audio.c new file mode 100644 index 000000000000..e15fa1f6ee48 --- /dev/null +++ b/drivers/staging/line6/audio.c | |||
@@ -0,0 +1,69 @@ | |||
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 <sound/core.h> | ||
15 | #include <sound/initval.h> | ||
16 | |||
17 | |||
18 | static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; | ||
19 | static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; | ||
20 | |||
21 | |||
22 | /* | ||
23 | Initialize the Line6 USB audio system. | ||
24 | */ | ||
25 | int line6_init_audio(struct usb_line6 *line6) | ||
26 | { | ||
27 | static int dev = 0; | ||
28 | struct snd_card *card; | ||
29 | |||
30 | card = snd_card_new(line6_index[dev], line6_id[dev], THIS_MODULE, 0); | ||
31 | |||
32 | if(card == NULL) | ||
33 | return -ENOMEM; | ||
34 | |||
35 | line6->card = card; | ||
36 | |||
37 | strcpy(card->driver, DRIVER_NAME); | ||
38 | strcpy(card->shortname, "Line6-USB"); | ||
39 | sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name, line6->ifcdev->bus_id); /* 80 chars - see asound.h */ | ||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | /* | ||
44 | Register the Line6 USB audio system. | ||
45 | */ | ||
46 | int line6_register_audio(struct usb_line6 *line6) | ||
47 | { | ||
48 | int err; | ||
49 | |||
50 | if((err = snd_card_register(line6->card)) < 0) | ||
51 | return err; | ||
52 | |||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | Cleanup the Line6 USB audio system. | ||
58 | */ | ||
59 | void line6_cleanup_audio(struct usb_line6 *line6) | ||
60 | { | ||
61 | struct snd_card *card = line6->card; | ||
62 | |||
63 | if(card == 0) | ||
64 | return; | ||
65 | |||
66 | snd_card_disconnect(card); | ||
67 | snd_card_free(card); | ||
68 | line6->card = 0; | ||
69 | } | ||
diff --git a/drivers/staging/line6/audio.h b/drivers/staging/line6/audio.h new file mode 100644 index 000000000000..cc0245adbcd9 --- /dev/null +++ b/drivers/staging/line6/audio.h | |||
@@ -0,0 +1,24 @@ | |||
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 | #ifndef AUDIO_H | ||
13 | #define AUDIO_H | ||
14 | |||
15 | |||
16 | #include "driver.h" | ||
17 | |||
18 | |||
19 | extern void line6_cleanup_audio(struct usb_line6 *); | ||
20 | extern int line6_init_audio(struct usb_line6 *); | ||
21 | extern int line6_register_audio(struct usb_line6 *); | ||
22 | |||
23 | |||
24 | #endif | ||
diff --git a/drivers/staging/line6/capture.c b/drivers/staging/line6/capture.c new file mode 100644 index 000000000000..5dec3bfff04d --- /dev/null +++ b/drivers/staging/line6/capture.c | |||
@@ -0,0 +1,370 @@ | |||
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 <sound/core.h> | ||
15 | #include <sound/pcm.h> | ||
16 | #include <sound/pcm_params.h> | ||
17 | |||
18 | #include "audio.h" | ||
19 | #include "pcm.h" | ||
20 | #include "pod.h" | ||
21 | |||
22 | |||
23 | /* | ||
24 | Find a free URB and submit it. | ||
25 | */ | ||
26 | static int submit_audio_in_urb(struct snd_pcm_substream *substream) | ||
27 | { | ||
28 | int index; | ||
29 | unsigned long flags; | ||
30 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
31 | int i, urb_size; | ||
32 | struct urb *urb_in; | ||
33 | |||
34 | spin_lock_irqsave(&line6pcm->lock_audio_in, flags); | ||
35 | index = find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); | ||
36 | |||
37 | if(index < 0 || index >= LINE6_ISO_BUFFERS) { | ||
38 | spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); | ||
39 | dev_err(s2m(substream), "no free URB found\n"); | ||
40 | return -EINVAL; | ||
41 | } | ||
42 | |||
43 | urb_in = line6pcm->urb_audio_in[index]; | ||
44 | urb_size = 0; | ||
45 | |||
46 | for(i = 0; i < LINE6_ISO_PACKETS; ++i) { | ||
47 | struct usb_iso_packet_descriptor *fin = &urb_in->iso_frame_desc[i]; | ||
48 | fin->offset = urb_size; | ||
49 | fin->length = line6pcm->max_packet_size; | ||
50 | urb_size += line6pcm->max_packet_size; | ||
51 | } | ||
52 | |||
53 | urb_in->transfer_buffer = line6pcm->buffer_in + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; | ||
54 | urb_in->transfer_buffer_length = urb_size; | ||
55 | urb_in->context = substream; | ||
56 | |||
57 | if(usb_submit_urb(urb_in, GFP_ATOMIC) == 0) | ||
58 | set_bit(index, &line6pcm->active_urb_in); | ||
59 | else | ||
60 | dev_err(s2m(substream), "URB in #%d submission failed\n", index); | ||
61 | |||
62 | spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | Submit all currently available capture URBs. | ||
68 | */ | ||
69 | static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream) | ||
70 | { | ||
71 | int ret, i; | ||
72 | |||
73 | for(i = 0; i < LINE6_ISO_BUFFERS; ++i) | ||
74 | if((ret = submit_audio_in_urb(substream)) < 0) | ||
75 | return ret; | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | Unlink all currently active capture URBs. | ||
82 | */ | ||
83 | static void unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) | ||
84 | { | ||
85 | unsigned int i; | ||
86 | |||
87 | for(i = LINE6_ISO_BUFFERS; i--;) { | ||
88 | if(test_bit(i, &line6pcm->active_urb_in)) { | ||
89 | if(!test_and_set_bit(i, &line6pcm->unlink_urb_in)) { | ||
90 | struct urb *u = line6pcm->urb_audio_in[i]; | ||
91 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) | ||
92 | u->transfer_flags |= URB_ASYNC_UNLINK; | ||
93 | #endif | ||
94 | usb_unlink_urb(u); | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | Wait until unlinking of all currently active capture URBs has been finished. | ||
102 | */ | ||
103 | static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) | ||
104 | { | ||
105 | int timeout = HZ; | ||
106 | unsigned int i; | ||
107 | int alive; | ||
108 | |||
109 | do { | ||
110 | alive = 0; | ||
111 | for (i = LINE6_ISO_BUFFERS; i--;) { | ||
112 | if (test_bit(i, &line6pcm->active_urb_in)) | ||
113 | alive++; | ||
114 | } | ||
115 | if (! alive) | ||
116 | break; | ||
117 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
118 | schedule_timeout(1); | ||
119 | } while (--timeout > 0); | ||
120 | if (alive) | ||
121 | snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); | ||
122 | |||
123 | line6pcm->active_urb_in = 0; | ||
124 | line6pcm->unlink_urb_in = 0; | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | Unlink all currently active capture URBs, and wait for finishing. | ||
129 | */ | ||
130 | void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) | ||
131 | { | ||
132 | unlink_audio_in_urbs(line6pcm); | ||
133 | wait_clear_audio_in_urbs(line6pcm); | ||
134 | } | ||
135 | |||
136 | /* | ||
137 | Callback for completed capture URB. | ||
138 | */ | ||
139 | static void audio_in_callback(struct urb *urb PT_REGS) | ||
140 | { | ||
141 | int i, index, length = 0, shutdown = 0; | ||
142 | int frames; | ||
143 | unsigned long flags; | ||
144 | |||
145 | struct snd_pcm_substream *substream = (struct snd_pcm_substream *)urb->context; | ||
146 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
147 | const int bytes_per_frame = line6pcm->properties->bytes_per_frame; | ||
148 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
149 | |||
150 | /* find index of URB */ | ||
151 | for(index = 0; index < LINE6_ISO_BUFFERS; ++index) | ||
152 | if(urb == line6pcm->urb_audio_in[index]) | ||
153 | break; | ||
154 | |||
155 | #if DO_DUMP_PCM_RECEIVE | ||
156 | for(i = 0; i < LINE6_ISO_PACKETS; ++i) { | ||
157 | struct usb_iso_packet_descriptor *fout = &urb->iso_frame_desc[i]; | ||
158 | line6_write_hexdump(line6pcm->line6, 'C', urb->transfer_buffer + fout->offset, fout->length); | ||
159 | } | ||
160 | #endif | ||
161 | |||
162 | spin_lock_irqsave(&line6pcm->lock_audio_in, flags); | ||
163 | |||
164 | for(i = 0; i < LINE6_ISO_PACKETS; ++i) { | ||
165 | char *fbuf; | ||
166 | int fsize; | ||
167 | struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i]; | ||
168 | |||
169 | if(fin->status == -18) { | ||
170 | shutdown = 1; | ||
171 | break; | ||
172 | } | ||
173 | |||
174 | fbuf = urb->transfer_buffer + fin->offset; | ||
175 | fsize = fin->actual_length; | ||
176 | length += fsize; | ||
177 | |||
178 | if(fsize > 0) { | ||
179 | frames = fsize / bytes_per_frame; | ||
180 | |||
181 | if(line6pcm->pos_in_done + frames > runtime->buffer_size) { | ||
182 | /* | ||
183 | The transferred area goes over buffer boundary, | ||
184 | copy two separate chunks. | ||
185 | */ | ||
186 | int len; | ||
187 | len = runtime->buffer_size - line6pcm->pos_in_done; | ||
188 | |||
189 | if(len > 0) { | ||
190 | memcpy(runtime->dma_area + line6pcm->pos_in_done * bytes_per_frame, fbuf, len * bytes_per_frame); | ||
191 | memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, (frames - len) * bytes_per_frame); | ||
192 | } | ||
193 | else | ||
194 | dev_err(s2m(substream), "driver bug: len = %d\n", len); /* this is somewhat paranoid */ | ||
195 | } | ||
196 | else { | ||
197 | /* copy single chunk */ | ||
198 | memcpy(runtime->dma_area + line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize * bytes_per_frame); | ||
199 | } | ||
200 | |||
201 | if((line6pcm->pos_in_done += frames) >= runtime->buffer_size) | ||
202 | line6pcm->pos_in_done -= runtime->buffer_size; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | clear_bit(index, &line6pcm->active_urb_in); | ||
207 | |||
208 | if(test_bit(index, &line6pcm->unlink_urb_in)) | ||
209 | shutdown = 1; | ||
210 | |||
211 | spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); | ||
212 | |||
213 | if(!shutdown) { | ||
214 | submit_audio_in_urb(substream); | ||
215 | |||
216 | if((line6pcm->bytes_in += length) >= line6pcm->period_in) { | ||
217 | line6pcm->bytes_in -= line6pcm->period_in; | ||
218 | snd_pcm_period_elapsed(substream); | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | |||
223 | /* open capture callback */ | ||
224 | static int snd_line6_capture_open(struct snd_pcm_substream *substream) | ||
225 | { | ||
226 | int err; | ||
227 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
228 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
229 | |||
230 | if((err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
231 | (&line6pcm->properties->snd_line6_rates))) < 0) | ||
232 | return err; | ||
233 | |||
234 | runtime->hw = line6pcm->properties->snd_line6_capture_hw; | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | /* close capture callback */ | ||
239 | static int snd_line6_capture_close(struct snd_pcm_substream *substream) | ||
240 | { | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | /* hw_params capture callback */ | ||
245 | static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) | ||
246 | { | ||
247 | int ret; | ||
248 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
249 | |||
250 | /* -- Florian Demski [FD] */ | ||
251 | /* don't ask me why, but this fixes the bug on my machine */ | ||
252 | if ( line6pcm == NULL ) { | ||
253 | if ( substream->pcm == NULL ) | ||
254 | return -ENOMEM; | ||
255 | if ( substream->pcm->private_data == NULL ) | ||
256 | return -ENOMEM; | ||
257 | substream->private_data = substream->pcm->private_data; | ||
258 | line6pcm = snd_pcm_substream_chip( substream ); | ||
259 | } | ||
260 | /* -- [FD] end */ | ||
261 | |||
262 | if((ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) | ||
263 | return ret; | ||
264 | |||
265 | line6pcm->period_in = params_period_bytes(hw_params); | ||
266 | line6pcm->buffer_in = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL); | ||
267 | |||
268 | if(!line6pcm->buffer_in) { | ||
269 | dev_err(s2m(substream), "cannot malloc buffer_in\n"); | ||
270 | return -ENOMEM; | ||
271 | } | ||
272 | |||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | /* hw_free capture callback */ | ||
277 | static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) | ||
278 | { | ||
279 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
280 | unlink_wait_clear_audio_in_urbs(line6pcm); | ||
281 | |||
282 | if(line6pcm->buffer_in) { | ||
283 | kfree(line6pcm->buffer_in); | ||
284 | line6pcm->buffer_in = 0; | ||
285 | } | ||
286 | |||
287 | return snd_pcm_lib_free_pages(substream); | ||
288 | } | ||
289 | |||
290 | /* trigger callback */ | ||
291 | int snd_line6_capture_trigger(struct snd_pcm_substream *substream, int cmd) | ||
292 | { | ||
293 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
294 | int err; | ||
295 | line6pcm->count_in = 0; | ||
296 | |||
297 | switch(cmd) { | ||
298 | case SNDRV_PCM_TRIGGER_START: | ||
299 | if(!test_and_set_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) { | ||
300 | err = submit_audio_in_all_urbs(substream); | ||
301 | |||
302 | if(err < 0) { | ||
303 | clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags); | ||
304 | return err; | ||
305 | } | ||
306 | } | ||
307 | |||
308 | break; | ||
309 | |||
310 | case SNDRV_PCM_TRIGGER_STOP: | ||
311 | if(test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) | ||
312 | unlink_audio_in_urbs(line6pcm); | ||
313 | |||
314 | break; | ||
315 | |||
316 | default: | ||
317 | return -EINVAL; | ||
318 | } | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | /* capture pointer callback */ | ||
324 | static snd_pcm_uframes_t | ||
325 | snd_line6_capture_pointer(struct snd_pcm_substream *substream) | ||
326 | { | ||
327 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
328 | return line6pcm->pos_in_done; | ||
329 | } | ||
330 | |||
331 | /* capture operators */ | ||
332 | struct snd_pcm_ops snd_line6_capture_ops = { | ||
333 | .open = snd_line6_capture_open, | ||
334 | .close = snd_line6_capture_close, | ||
335 | .ioctl = snd_pcm_lib_ioctl, | ||
336 | .hw_params = snd_line6_capture_hw_params, | ||
337 | .hw_free = snd_line6_capture_hw_free, | ||
338 | .prepare = snd_line6_prepare, | ||
339 | .trigger = snd_line6_trigger, | ||
340 | .pointer = snd_line6_capture_pointer, | ||
341 | }; | ||
342 | |||
343 | int create_audio_in_urbs(struct snd_line6_pcm *line6pcm) | ||
344 | { | ||
345 | int i; | ||
346 | |||
347 | /* create audio URBs and fill in constant values: */ | ||
348 | for(i = 0; i < LINE6_ISO_BUFFERS; ++i) { | ||
349 | struct urb *urb; | ||
350 | |||
351 | /* URB for audio in: */ | ||
352 | urb = line6pcm->urb_audio_in[i] = usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); | ||
353 | |||
354 | if(urb == NULL) { | ||
355 | dev_err(line6pcm->line6->ifcdev, "Out of memory\n"); | ||
356 | return -ENOMEM; | ||
357 | } | ||
358 | |||
359 | urb->dev = line6pcm->line6->usbdev; | ||
360 | urb->pipe = usb_rcvisocpipe(line6pcm->line6->usbdev, line6pcm->ep_audio_read & USB_ENDPOINT_NUMBER_MASK); | ||
361 | urb->transfer_flags = URB_ISO_ASAP; | ||
362 | urb->start_frame = -1; | ||
363 | urb->number_of_packets = LINE6_ISO_PACKETS; | ||
364 | urb->interval = LINE6_ISO_INTERVAL; | ||
365 | urb->error_count = 0; | ||
366 | urb->complete = audio_in_callback; | ||
367 | } | ||
368 | |||
369 | return 0; | ||
370 | } | ||
diff --git a/drivers/staging/line6/capture.h b/drivers/staging/line6/capture.h new file mode 100644 index 000000000000..7b92e4de3927 --- /dev/null +++ b/drivers/staging/line6/capture.h | |||
@@ -0,0 +1,31 @@ | |||
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 | #ifndef CAPTURE_H | ||
13 | #define CAPTURE_H | ||
14 | |||
15 | |||
16 | #include "driver.h" | ||
17 | |||
18 | #include <sound/pcm.h> | ||
19 | |||
20 | #include "pcm.h" | ||
21 | |||
22 | |||
23 | extern struct snd_pcm_ops snd_line6_capture_ops; | ||
24 | |||
25 | |||
26 | extern int create_audio_in_urbs(struct snd_line6_pcm *line6pcm); | ||
27 | extern int snd_line6_capture_trigger(struct snd_pcm_substream *substream, int cmd); | ||
28 | extern void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm); | ||
29 | |||
30 | |||
31 | #endif | ||
diff --git a/drivers/staging/line6/config.h b/drivers/staging/line6/config.h new file mode 100644 index 000000000000..d5ed1a740b0d --- /dev/null +++ b/drivers/staging/line6/config.h | |||
@@ -0,0 +1,73 @@ | |||
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 | #ifndef CONFIG_H | ||
13 | #define CONFIG_H | ||
14 | |||
15 | |||
16 | #include <linux/version.h> | ||
17 | |||
18 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | ||
19 | #include <linux/config.h> | ||
20 | #endif | ||
21 | |||
22 | #ifdef CONFIG_USB_DEBUG | ||
23 | #define DEBUG 1 | ||
24 | #endif | ||
25 | |||
26 | |||
27 | /** | ||
28 | Development tools. | ||
29 | */ | ||
30 | #define DO_DEBUG_MESSAGES 0 | ||
31 | #define DO_DUMP_URB_SEND DO_DEBUG_MESSAGES | ||
32 | #define DO_DUMP_URB_RECEIVE DO_DEBUG_MESSAGES | ||
33 | #define DO_DUMP_PCM_SEND 0 | ||
34 | #define DO_DUMP_PCM_RECEIVE 0 | ||
35 | #define DO_DUMP_MIDI_SEND DO_DEBUG_MESSAGES | ||
36 | #define DO_DUMP_MIDI_RECEIVE DO_DEBUG_MESSAGES | ||
37 | #define DO_DUMP_ANY (DO_DUMP_URB_SEND || DO_DUMP_URB_RECEIVE || \ | ||
38 | DO_DUMP_PCM_SEND || DO_DUMP_PCM_RECEIVE || \ | ||
39 | DO_DUMP_MIDI_SEND || DO_DUMP_MIDI_RECEIVE) | ||
40 | #define CREATE_RAW_FILE 0 | ||
41 | |||
42 | #if DO_DEBUG_MESSAGES | ||
43 | #define CHECKPOINT printk("line6usb: %s (%s:%d)\n", __FUNCTION__, __FILE__, __LINE__) | ||
44 | #endif | ||
45 | |||
46 | /** | ||
47 | In Linux 2.6.13 and later, the device_attribute is passed to the sysfs | ||
48 | get/set functions (see /usr/src/linux/include/linux/device.h). | ||
49 | */ | ||
50 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) | ||
51 | #define DEVICE_ATTRIBUTE struct device_attribute *attr, | ||
52 | #else | ||
53 | #define DEVICE_ATTRIBUTE | ||
54 | #endif | ||
55 | |||
56 | /** | ||
57 | In Linux 2.6.20 and later, the pt_regs is no longer passed to USB callback | ||
58 | functions. | ||
59 | */ | ||
60 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | ||
61 | #define PT_REGS | ||
62 | #else | ||
63 | #define PT_REGS , struct pt_regs *regs | ||
64 | #endif | ||
65 | |||
66 | #if DO_DEBUG_MESSAGES | ||
67 | #define DEBUG_MESSAGES(x) (x) | ||
68 | #else | ||
69 | #define DEBUG_MESSAGES(x) | ||
70 | #endif | ||
71 | |||
72 | |||
73 | #endif | ||
diff --git a/drivers/staging/line6/control.c b/drivers/staging/line6/control.c new file mode 100644 index 000000000000..d44d06d7b136 --- /dev/null +++ b/drivers/staging/line6/control.c | |||
@@ -0,0 +1,702 @@ | |||
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 "control.h" | ||
17 | #include "pod.h" | ||
18 | #include "usbdefs.h" | ||
19 | #include "variax.h" | ||
20 | |||
21 | #define DEVICE_ATTR2(_name1,_name2,_mode,_show,_store) \ | ||
22 | struct device_attribute dev_attr_##_name1 = __ATTR(_name2,_mode,_show,_store) | ||
23 | |||
24 | #define LINE6_PARAM_R(PREFIX, prefix, type, param) \ | ||
25 | static ssize_t prefix ## _get_ ## param(struct device *dev, DEVICE_ATTRIBUTE char *buf) \ | ||
26 | { \ | ||
27 | return prefix ## _get_param_ ## type(dev, buf, PREFIX ## _ ## param); \ | ||
28 | } | ||
29 | |||
30 | #define LINE6_PARAM_RW(PREFIX, prefix, type, param) \ | ||
31 | LINE6_PARAM_R(PREFIX, prefix, type, param); \ | ||
32 | static ssize_t prefix ## _set_ ## param(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) \ | ||
33 | { \ | ||
34 | return prefix ## _set_param_ ## type(dev, buf, count, PREFIX ## _ ## param); \ | ||
35 | } | ||
36 | |||
37 | #define POD_PARAM_R(type, param) LINE6_PARAM_R(POD, pod, type, param) | ||
38 | #define POD_PARAM_RW(type, param) LINE6_PARAM_RW(POD, pod, type, param) | ||
39 | #define VARIAX_PARAM_R(type, param) LINE6_PARAM_R(VARIAX, variax, type, param) | ||
40 | #define VARIAX_PARAM_RW(type, param) LINE6_PARAM_RW(VARIAX, variax, type, param) | ||
41 | |||
42 | |||
43 | static ssize_t pod_get_param_int(struct device *dev, char *buf, int param) | ||
44 | { | ||
45 | struct usb_interface *interface = to_usb_interface(dev); | ||
46 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
47 | int retval = line6_wait_dump(&pod->dumpreq, 0); | ||
48 | if(retval < 0) return retval; | ||
49 | return sprintf(buf, "%d\n", pod->prog_data.control[param]); | ||
50 | } | ||
51 | |||
52 | static ssize_t pod_set_param_int(struct device *dev, const char *buf, size_t count, int param) | ||
53 | { | ||
54 | struct usb_interface *interface = to_usb_interface(dev); | ||
55 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
56 | int value = simple_strtoul(buf, NULL, 10); | ||
57 | pod_transmit_parameter(pod, param, value); | ||
58 | return count; | ||
59 | } | ||
60 | |||
61 | static ssize_t variax_get_param_int(struct device *dev, char *buf, int param) | ||
62 | { | ||
63 | struct usb_interface *interface = to_usb_interface(dev); | ||
64 | struct usb_line6_variax *variax = usb_get_intfdata(interface); | ||
65 | int retval = line6_wait_dump(&variax->dumpreq, 0); | ||
66 | if(retval < 0) return retval; | ||
67 | return sprintf(buf, "%d\n", variax->model_data.control[param]); | ||
68 | } | ||
69 | |||
70 | static ssize_t variax_get_param_float(struct device *dev, char *buf, int param) | ||
71 | { | ||
72 | /* | ||
73 | We do our own floating point handling here since floats in the kernel are | ||
74 | problematic for at least two reasons: | ||
75 | - many distros are still shipped with binary kernels optimized for the | ||
76 | ancient 80386 without FPU | ||
77 | - there isn't a printf("%f") | ||
78 | (see http://www.kernelthread.com/publications/faq/335.html) | ||
79 | */ | ||
80 | |||
81 | static const int BIAS = 0x7f; | ||
82 | static const int OFFSET = 0xf; | ||
83 | static const int PRECISION = 1000; | ||
84 | |||
85 | int len = 0; | ||
86 | unsigned part_int, part_frac; | ||
87 | struct usb_interface *interface = to_usb_interface(dev); | ||
88 | struct usb_line6_variax *variax = usb_get_intfdata(interface); | ||
89 | const unsigned char *p = variax->model_data.control + param; | ||
90 | int retval = line6_wait_dump(&variax->dumpreq, 0); | ||
91 | if(retval < 0) return retval; | ||
92 | |||
93 | if((p[0] == 0) && (p[1] == 0) && (p[2] == 0)) | ||
94 | part_int = part_frac = 0; | ||
95 | else { | ||
96 | int exponent = (((p[0] & 0x7f) << 1) | (p[1] >> 7)) - BIAS; | ||
97 | unsigned mantissa = (p[1] << 8) | p[2] | 0x8000; | ||
98 | exponent -= OFFSET; | ||
99 | |||
100 | if(exponent >= 0) { | ||
101 | part_int = mantissa << exponent; | ||
102 | part_frac = 0; | ||
103 | } | ||
104 | else { | ||
105 | part_int = mantissa >> -exponent; | ||
106 | part_frac = (mantissa << (32 + exponent)) & 0xffffffff; | ||
107 | } | ||
108 | |||
109 | part_frac = (part_frac / ((1UL << 31) / (PRECISION / 2 * 10)) + 5) / 10; | ||
110 | } | ||
111 | |||
112 | len += sprintf(buf + len, "%s%d.%03d\n", ((p[0] & 0x80) ? "-" : ""), part_int, part_frac); | ||
113 | return len; | ||
114 | } | ||
115 | |||
116 | POD_PARAM_RW(int, tweak); | ||
117 | POD_PARAM_RW(int, wah_position); | ||
118 | POD_PARAM_RW(int, compression_gain); | ||
119 | POD_PARAM_RW(int, vol_pedal_position); | ||
120 | POD_PARAM_RW(int, compression_threshold); | ||
121 | POD_PARAM_RW(int, pan); | ||
122 | POD_PARAM_RW(int, amp_model_setup); | ||
123 | POD_PARAM_RW(int, amp_model); | ||
124 | POD_PARAM_RW(int, drive); | ||
125 | POD_PARAM_RW(int, bass); | ||
126 | POD_PARAM_RW(int, mid); | ||
127 | POD_PARAM_RW(int, lowmid); | ||
128 | POD_PARAM_RW(int, treble); | ||
129 | POD_PARAM_RW(int, highmid); | ||
130 | POD_PARAM_RW(int, chan_vol); | ||
131 | POD_PARAM_RW(int, reverb_mix); | ||
132 | POD_PARAM_RW(int, effect_setup); | ||
133 | POD_PARAM_RW(int, band_1_frequency); | ||
134 | POD_PARAM_RW(int, presence); | ||
135 | POD_PARAM_RW(int, treble__bass); | ||
136 | POD_PARAM_RW(int, noise_gate_enable); | ||
137 | POD_PARAM_RW(int, gate_threshold); | ||
138 | POD_PARAM_RW(int, gate_decay_time); | ||
139 | POD_PARAM_RW(int, stomp_enable); | ||
140 | POD_PARAM_RW(int, comp_enable); | ||
141 | POD_PARAM_RW(int, stomp_time); | ||
142 | POD_PARAM_RW(int, delay_enable); | ||
143 | POD_PARAM_RW(int, mod_param_1); | ||
144 | POD_PARAM_RW(int, delay_param_1); | ||
145 | POD_PARAM_RW(int, delay_param_1_note_value); | ||
146 | POD_PARAM_RW(int, band_2_frequency__bass); | ||
147 | POD_PARAM_RW(int, delay_param_2); | ||
148 | POD_PARAM_RW(int, delay_volume_mix); | ||
149 | POD_PARAM_RW(int, delay_param_3); | ||
150 | POD_PARAM_RW(int, reverb_enable); | ||
151 | POD_PARAM_RW(int, reverb_type); | ||
152 | POD_PARAM_RW(int, reverb_decay); | ||
153 | POD_PARAM_RW(int, reverb_tone); | ||
154 | POD_PARAM_RW(int, reverb_pre_delay); | ||
155 | POD_PARAM_RW(int, reverb_pre_post); | ||
156 | POD_PARAM_RW(int, band_2_frequency); | ||
157 | POD_PARAM_RW(int, band_3_frequency__bass); | ||
158 | POD_PARAM_RW(int, wah_enable); | ||
159 | POD_PARAM_RW(int, modulation_lo_cut); | ||
160 | POD_PARAM_RW(int, delay_reverb_lo_cut); | ||
161 | POD_PARAM_RW(int, volume_pedal_minimum); | ||
162 | POD_PARAM_RW(int, eq_pre_post); | ||
163 | POD_PARAM_RW(int, volume_pre_post); | ||
164 | POD_PARAM_RW(int, di_model); | ||
165 | POD_PARAM_RW(int, di_delay); | ||
166 | POD_PARAM_RW(int, mod_enable); | ||
167 | POD_PARAM_RW(int, mod_param_1_note_value); | ||
168 | POD_PARAM_RW(int, mod_param_2); | ||
169 | POD_PARAM_RW(int, mod_param_3); | ||
170 | POD_PARAM_RW(int, mod_param_4); | ||
171 | POD_PARAM_RW(int, mod_param_5); | ||
172 | POD_PARAM_RW(int, mod_volume_mix); | ||
173 | POD_PARAM_RW(int, mod_pre_post); | ||
174 | POD_PARAM_RW(int, modulation_model); | ||
175 | POD_PARAM_RW(int, band_3_frequency); | ||
176 | POD_PARAM_RW(int, band_4_frequency__bass); | ||
177 | POD_PARAM_RW(int, mod_param_1_double_precision); | ||
178 | POD_PARAM_RW(int, delay_param_1_double_precision); | ||
179 | POD_PARAM_RW(int, eq_enable); | ||
180 | POD_PARAM_RW(int, tap); | ||
181 | POD_PARAM_RW(int, volume_tweak_pedal_assign); | ||
182 | POD_PARAM_RW(int, band_5_frequency); | ||
183 | POD_PARAM_RW(int, tuner); | ||
184 | POD_PARAM_RW(int, mic_selection); | ||
185 | POD_PARAM_RW(int, cabinet_model); | ||
186 | POD_PARAM_RW(int, stomp_model); | ||
187 | POD_PARAM_RW(int, roomlevel); | ||
188 | POD_PARAM_RW(int, band_4_frequency); | ||
189 | POD_PARAM_RW(int, band_6_frequency); | ||
190 | POD_PARAM_RW(int, stomp_param_1_note_value); | ||
191 | POD_PARAM_RW(int, stomp_param_2); | ||
192 | POD_PARAM_RW(int, stomp_param_3); | ||
193 | POD_PARAM_RW(int, stomp_param_4); | ||
194 | POD_PARAM_RW(int, stomp_param_5); | ||
195 | POD_PARAM_RW(int, stomp_param_6); | ||
196 | POD_PARAM_RW(int, amp_switch_select); | ||
197 | POD_PARAM_RW(int, delay_param_4); | ||
198 | POD_PARAM_RW(int, delay_param_5); | ||
199 | POD_PARAM_RW(int, delay_pre_post); | ||
200 | POD_PARAM_RW(int, delay_model); | ||
201 | POD_PARAM_RW(int, delay_verb_model); | ||
202 | POD_PARAM_RW(int, tempo_msb); | ||
203 | POD_PARAM_RW(int, tempo_lsb); | ||
204 | POD_PARAM_RW(int, wah_model); | ||
205 | POD_PARAM_RW(int, bypass_volume); | ||
206 | POD_PARAM_RW(int, fx_loop_on_off); | ||
207 | POD_PARAM_RW(int, tweak_param_select); | ||
208 | POD_PARAM_RW(int, amp1_engage); | ||
209 | POD_PARAM_RW(int, band_1_gain); | ||
210 | POD_PARAM_RW(int, band_2_gain__bass); | ||
211 | POD_PARAM_RW(int, band_2_gain); | ||
212 | POD_PARAM_RW(int, band_3_gain__bass); | ||
213 | POD_PARAM_RW(int, band_3_gain); | ||
214 | POD_PARAM_RW(int, band_4_gain__bass); | ||
215 | POD_PARAM_RW(int, band_5_gain__bass); | ||
216 | POD_PARAM_RW(int, band_4_gain); | ||
217 | POD_PARAM_RW(int, band_6_gain__bass); | ||
218 | VARIAX_PARAM_R(int, body); | ||
219 | VARIAX_PARAM_R(int, pickup1_enable); | ||
220 | VARIAX_PARAM_R(int, pickup1_type); | ||
221 | VARIAX_PARAM_R(float, pickup1_position); | ||
222 | VARIAX_PARAM_R(float, pickup1_angle); | ||
223 | VARIAX_PARAM_R(float, pickup1_level); | ||
224 | VARIAX_PARAM_R(int, pickup2_enable); | ||
225 | VARIAX_PARAM_R(int, pickup2_type); | ||
226 | VARIAX_PARAM_R(float, pickup2_position); | ||
227 | VARIAX_PARAM_R(float, pickup2_angle); | ||
228 | VARIAX_PARAM_R(float, pickup2_level); | ||
229 | VARIAX_PARAM_R(int, pickup_phase); | ||
230 | VARIAX_PARAM_R(float, capacitance); | ||
231 | VARIAX_PARAM_R(float, tone_resistance); | ||
232 | VARIAX_PARAM_R(float, volume_resistance); | ||
233 | VARIAX_PARAM_R(int, taper); | ||
234 | VARIAX_PARAM_R(float, tone_dump); | ||
235 | VARIAX_PARAM_R(int, save_tone); | ||
236 | VARIAX_PARAM_R(float, volume_dump); | ||
237 | VARIAX_PARAM_R(int, tuning_enable); | ||
238 | VARIAX_PARAM_R(int, tuning6); | ||
239 | VARIAX_PARAM_R(int, tuning5); | ||
240 | VARIAX_PARAM_R(int, tuning4); | ||
241 | VARIAX_PARAM_R(int, tuning3); | ||
242 | VARIAX_PARAM_R(int, tuning2); | ||
243 | VARIAX_PARAM_R(int, tuning1); | ||
244 | VARIAX_PARAM_R(float, detune6); | ||
245 | VARIAX_PARAM_R(float, detune5); | ||
246 | VARIAX_PARAM_R(float, detune4); | ||
247 | VARIAX_PARAM_R(float, detune3); | ||
248 | VARIAX_PARAM_R(float, detune2); | ||
249 | VARIAX_PARAM_R(float, detune1); | ||
250 | VARIAX_PARAM_R(float, mix6); | ||
251 | VARIAX_PARAM_R(float, mix5); | ||
252 | VARIAX_PARAM_R(float, mix4); | ||
253 | VARIAX_PARAM_R(float, mix3); | ||
254 | VARIAX_PARAM_R(float, mix2); | ||
255 | VARIAX_PARAM_R(float, mix1); | ||
256 | VARIAX_PARAM_R(int, pickup_wiring); | ||
257 | |||
258 | static DEVICE_ATTR(tweak, S_IWUGO | S_IRUGO, pod_get_tweak, pod_set_tweak); | ||
259 | static DEVICE_ATTR(wah_position, S_IWUGO | S_IRUGO, pod_get_wah_position, pod_set_wah_position); | ||
260 | static DEVICE_ATTR(compression_gain, S_IWUGO | S_IRUGO, pod_get_compression_gain, pod_set_compression_gain); | ||
261 | static DEVICE_ATTR(vol_pedal_position, S_IWUGO | S_IRUGO, pod_get_vol_pedal_position, pod_set_vol_pedal_position); | ||
262 | static DEVICE_ATTR(compression_threshold, S_IWUGO | S_IRUGO, pod_get_compression_threshold, pod_set_compression_threshold); | ||
263 | static DEVICE_ATTR(pan, S_IWUGO | S_IRUGO, pod_get_pan, pod_set_pan); | ||
264 | static DEVICE_ATTR(amp_model_setup, S_IWUGO | S_IRUGO, pod_get_amp_model_setup, pod_set_amp_model_setup); | ||
265 | static DEVICE_ATTR(amp_model, S_IWUGO | S_IRUGO, pod_get_amp_model, pod_set_amp_model); | ||
266 | static DEVICE_ATTR(drive, S_IWUGO | S_IRUGO, pod_get_drive, pod_set_drive); | ||
267 | static DEVICE_ATTR(bass, S_IWUGO | S_IRUGO, pod_get_bass, pod_set_bass); | ||
268 | static DEVICE_ATTR(mid, S_IWUGO | S_IRUGO, pod_get_mid, pod_set_mid); | ||
269 | static DEVICE_ATTR(lowmid, S_IWUGO | S_IRUGO, pod_get_lowmid, pod_set_lowmid); | ||
270 | static DEVICE_ATTR(treble, S_IWUGO | S_IRUGO, pod_get_treble, pod_set_treble); | ||
271 | static DEVICE_ATTR(highmid, S_IWUGO | S_IRUGO, pod_get_highmid, pod_set_highmid); | ||
272 | static DEVICE_ATTR(chan_vol, S_IWUGO | S_IRUGO, pod_get_chan_vol, pod_set_chan_vol); | ||
273 | static DEVICE_ATTR(reverb_mix, S_IWUGO | S_IRUGO, pod_get_reverb_mix, pod_set_reverb_mix); | ||
274 | static DEVICE_ATTR(effect_setup, S_IWUGO | S_IRUGO, pod_get_effect_setup, pod_set_effect_setup); | ||
275 | static DEVICE_ATTR(band_1_frequency, S_IWUGO | S_IRUGO, pod_get_band_1_frequency, pod_set_band_1_frequency); | ||
276 | static DEVICE_ATTR(presence, S_IWUGO | S_IRUGO, pod_get_presence, pod_set_presence); | ||
277 | static DEVICE_ATTR2(treble__bass, treble, S_IWUGO | S_IRUGO, pod_get_treble__bass, pod_set_treble__bass); | ||
278 | static DEVICE_ATTR(noise_gate_enable, S_IWUGO | S_IRUGO, pod_get_noise_gate_enable, pod_set_noise_gate_enable); | ||
279 | static DEVICE_ATTR(gate_threshold, S_IWUGO | S_IRUGO, pod_get_gate_threshold, pod_set_gate_threshold); | ||
280 | static DEVICE_ATTR(gate_decay_time, S_IWUGO | S_IRUGO, pod_get_gate_decay_time, pod_set_gate_decay_time); | ||
281 | static DEVICE_ATTR(stomp_enable, S_IWUGO | S_IRUGO, pod_get_stomp_enable, pod_set_stomp_enable); | ||
282 | static DEVICE_ATTR(comp_enable, S_IWUGO | S_IRUGO, pod_get_comp_enable, pod_set_comp_enable); | ||
283 | static DEVICE_ATTR(stomp_time, S_IWUGO | S_IRUGO, pod_get_stomp_time, pod_set_stomp_time); | ||
284 | static DEVICE_ATTR(delay_enable, S_IWUGO | S_IRUGO, pod_get_delay_enable, pod_set_delay_enable); | ||
285 | static DEVICE_ATTR(mod_param_1, S_IWUGO | S_IRUGO, pod_get_mod_param_1, pod_set_mod_param_1); | ||
286 | static DEVICE_ATTR(delay_param_1, S_IWUGO | S_IRUGO, pod_get_delay_param_1, pod_set_delay_param_1); | ||
287 | static DEVICE_ATTR(delay_param_1_note_value, S_IWUGO | S_IRUGO, pod_get_delay_param_1_note_value, pod_set_delay_param_1_note_value); | ||
288 | static DEVICE_ATTR2(band_2_frequency__bass, band_2_frequency, S_IWUGO | S_IRUGO, pod_get_band_2_frequency__bass, pod_set_band_2_frequency__bass); | ||
289 | static DEVICE_ATTR(delay_param_2, S_IWUGO | S_IRUGO, pod_get_delay_param_2, pod_set_delay_param_2); | ||
290 | static DEVICE_ATTR(delay_volume_mix, S_IWUGO | S_IRUGO, pod_get_delay_volume_mix, pod_set_delay_volume_mix); | ||
291 | static DEVICE_ATTR(delay_param_3, S_IWUGO | S_IRUGO, pod_get_delay_param_3, pod_set_delay_param_3); | ||
292 | static DEVICE_ATTR(reverb_enable, S_IWUGO | S_IRUGO, pod_get_reverb_enable, pod_set_reverb_enable); | ||
293 | static DEVICE_ATTR(reverb_type, S_IWUGO | S_IRUGO, pod_get_reverb_type, pod_set_reverb_type); | ||
294 | static DEVICE_ATTR(reverb_decay, S_IWUGO | S_IRUGO, pod_get_reverb_decay, pod_set_reverb_decay); | ||
295 | static DEVICE_ATTR(reverb_tone, S_IWUGO | S_IRUGO, pod_get_reverb_tone, pod_set_reverb_tone); | ||
296 | static DEVICE_ATTR(reverb_pre_delay, S_IWUGO | S_IRUGO, pod_get_reverb_pre_delay, pod_set_reverb_pre_delay); | ||
297 | static DEVICE_ATTR(reverb_pre_post, S_IWUGO | S_IRUGO, pod_get_reverb_pre_post, pod_set_reverb_pre_post); | ||
298 | static DEVICE_ATTR(band_2_frequency, S_IWUGO | S_IRUGO, pod_get_band_2_frequency, pod_set_band_2_frequency); | ||
299 | static DEVICE_ATTR2(band_3_frequency__bass, band_3_frequency, S_IWUGO | S_IRUGO, pod_get_band_3_frequency__bass, pod_set_band_3_frequency__bass); | ||
300 | static DEVICE_ATTR(wah_enable, S_IWUGO | S_IRUGO, pod_get_wah_enable, pod_set_wah_enable); | ||
301 | static DEVICE_ATTR(modulation_lo_cut, S_IWUGO | S_IRUGO, pod_get_modulation_lo_cut, pod_set_modulation_lo_cut); | ||
302 | static DEVICE_ATTR(delay_reverb_lo_cut, S_IWUGO | S_IRUGO, pod_get_delay_reverb_lo_cut, pod_set_delay_reverb_lo_cut); | ||
303 | static DEVICE_ATTR(volume_pedal_minimum, S_IWUGO | S_IRUGO, pod_get_volume_pedal_minimum, pod_set_volume_pedal_minimum); | ||
304 | static DEVICE_ATTR(eq_pre_post, S_IWUGO | S_IRUGO, pod_get_eq_pre_post, pod_set_eq_pre_post); | ||
305 | static DEVICE_ATTR(volume_pre_post, S_IWUGO | S_IRUGO, pod_get_volume_pre_post, pod_set_volume_pre_post); | ||
306 | static DEVICE_ATTR(di_model, S_IWUGO | S_IRUGO, pod_get_di_model, pod_set_di_model); | ||
307 | static DEVICE_ATTR(di_delay, S_IWUGO | S_IRUGO, pod_get_di_delay, pod_set_di_delay); | ||
308 | static DEVICE_ATTR(mod_enable, S_IWUGO | S_IRUGO, pod_get_mod_enable, pod_set_mod_enable); | ||
309 | static DEVICE_ATTR(mod_param_1_note_value, S_IWUGO | S_IRUGO, pod_get_mod_param_1_note_value, pod_set_mod_param_1_note_value); | ||
310 | static DEVICE_ATTR(mod_param_2, S_IWUGO | S_IRUGO, pod_get_mod_param_2, pod_set_mod_param_2); | ||
311 | static DEVICE_ATTR(mod_param_3, S_IWUGO | S_IRUGO, pod_get_mod_param_3, pod_set_mod_param_3); | ||
312 | static DEVICE_ATTR(mod_param_4, S_IWUGO | S_IRUGO, pod_get_mod_param_4, pod_set_mod_param_4); | ||
313 | static DEVICE_ATTR(mod_param_5, S_IWUGO | S_IRUGO, pod_get_mod_param_5, pod_set_mod_param_5); | ||
314 | static DEVICE_ATTR(mod_volume_mix, S_IWUGO | S_IRUGO, pod_get_mod_volume_mix, pod_set_mod_volume_mix); | ||
315 | static DEVICE_ATTR(mod_pre_post, S_IWUGO | S_IRUGO, pod_get_mod_pre_post, pod_set_mod_pre_post); | ||
316 | static DEVICE_ATTR(modulation_model, S_IWUGO | S_IRUGO, pod_get_modulation_model, pod_set_modulation_model); | ||
317 | static DEVICE_ATTR(band_3_frequency, S_IWUGO | S_IRUGO, pod_get_band_3_frequency, pod_set_band_3_frequency); | ||
318 | static DEVICE_ATTR2(band_4_frequency__bass, band_4_frequency, S_IWUGO | S_IRUGO, pod_get_band_4_frequency__bass, pod_set_band_4_frequency__bass); | ||
319 | static DEVICE_ATTR(mod_param_1_double_precision, S_IWUGO | S_IRUGO, pod_get_mod_param_1_double_precision, pod_set_mod_param_1_double_precision); | ||
320 | static DEVICE_ATTR(delay_param_1_double_precision, S_IWUGO | S_IRUGO, pod_get_delay_param_1_double_precision, pod_set_delay_param_1_double_precision); | ||
321 | static DEVICE_ATTR(eq_enable, S_IWUGO | S_IRUGO, pod_get_eq_enable, pod_set_eq_enable); | ||
322 | static DEVICE_ATTR(tap, S_IWUGO | S_IRUGO, pod_get_tap, pod_set_tap); | ||
323 | static DEVICE_ATTR(volume_tweak_pedal_assign, S_IWUGO | S_IRUGO, pod_get_volume_tweak_pedal_assign, pod_set_volume_tweak_pedal_assign); | ||
324 | static DEVICE_ATTR(band_5_frequency, S_IWUGO | S_IRUGO, pod_get_band_5_frequency, pod_set_band_5_frequency); | ||
325 | static DEVICE_ATTR(tuner, S_IWUGO | S_IRUGO, pod_get_tuner, pod_set_tuner); | ||
326 | static DEVICE_ATTR(mic_selection, S_IWUGO | S_IRUGO, pod_get_mic_selection, pod_set_mic_selection); | ||
327 | static DEVICE_ATTR(cabinet_model, S_IWUGO | S_IRUGO, pod_get_cabinet_model, pod_set_cabinet_model); | ||
328 | static DEVICE_ATTR(stomp_model, S_IWUGO | S_IRUGO, pod_get_stomp_model, pod_set_stomp_model); | ||
329 | static DEVICE_ATTR(roomlevel, S_IWUGO | S_IRUGO, pod_get_roomlevel, pod_set_roomlevel); | ||
330 | static DEVICE_ATTR(band_4_frequency, S_IWUGO | S_IRUGO, pod_get_band_4_frequency, pod_set_band_4_frequency); | ||
331 | static DEVICE_ATTR(band_6_frequency, S_IWUGO | S_IRUGO, pod_get_band_6_frequency, pod_set_band_6_frequency); | ||
332 | static DEVICE_ATTR(stomp_param_1_note_value, S_IWUGO | S_IRUGO, pod_get_stomp_param_1_note_value, pod_set_stomp_param_1_note_value); | ||
333 | static DEVICE_ATTR(stomp_param_2, S_IWUGO | S_IRUGO, pod_get_stomp_param_2, pod_set_stomp_param_2); | ||
334 | static DEVICE_ATTR(stomp_param_3, S_IWUGO | S_IRUGO, pod_get_stomp_param_3, pod_set_stomp_param_3); | ||
335 | static DEVICE_ATTR(stomp_param_4, S_IWUGO | S_IRUGO, pod_get_stomp_param_4, pod_set_stomp_param_4); | ||
336 | static DEVICE_ATTR(stomp_param_5, S_IWUGO | S_IRUGO, pod_get_stomp_param_5, pod_set_stomp_param_5); | ||
337 | static DEVICE_ATTR(stomp_param_6, S_IWUGO | S_IRUGO, pod_get_stomp_param_6, pod_set_stomp_param_6); | ||
338 | static DEVICE_ATTR(amp_switch_select, S_IWUGO | S_IRUGO, pod_get_amp_switch_select, pod_set_amp_switch_select); | ||
339 | static DEVICE_ATTR(delay_param_4, S_IWUGO | S_IRUGO, pod_get_delay_param_4, pod_set_delay_param_4); | ||
340 | static DEVICE_ATTR(delay_param_5, S_IWUGO | S_IRUGO, pod_get_delay_param_5, pod_set_delay_param_5); | ||
341 | static DEVICE_ATTR(delay_pre_post, S_IWUGO | S_IRUGO, pod_get_delay_pre_post, pod_set_delay_pre_post); | ||
342 | static DEVICE_ATTR(delay_model, S_IWUGO | S_IRUGO, pod_get_delay_model, pod_set_delay_model); | ||
343 | static DEVICE_ATTR(delay_verb_model, S_IWUGO | S_IRUGO, pod_get_delay_verb_model, pod_set_delay_verb_model); | ||
344 | static DEVICE_ATTR(tempo_msb, S_IWUGO | S_IRUGO, pod_get_tempo_msb, pod_set_tempo_msb); | ||
345 | static DEVICE_ATTR(tempo_lsb, S_IWUGO | S_IRUGO, pod_get_tempo_lsb, pod_set_tempo_lsb); | ||
346 | static DEVICE_ATTR(wah_model, S_IWUGO | S_IRUGO, pod_get_wah_model, pod_set_wah_model); | ||
347 | static DEVICE_ATTR(bypass_volume, S_IWUGO | S_IRUGO, pod_get_bypass_volume, pod_set_bypass_volume); | ||
348 | static DEVICE_ATTR(fx_loop_on_off, S_IWUGO | S_IRUGO, pod_get_fx_loop_on_off, pod_set_fx_loop_on_off); | ||
349 | static DEVICE_ATTR(tweak_param_select, S_IWUGO | S_IRUGO, pod_get_tweak_param_select, pod_set_tweak_param_select); | ||
350 | static DEVICE_ATTR(amp1_engage, S_IWUGO | S_IRUGO, pod_get_amp1_engage, pod_set_amp1_engage); | ||
351 | static DEVICE_ATTR(band_1_gain, S_IWUGO | S_IRUGO, pod_get_band_1_gain, pod_set_band_1_gain); | ||
352 | static DEVICE_ATTR2(band_2_gain__bass, band_2_gain, S_IWUGO | S_IRUGO, pod_get_band_2_gain__bass, pod_set_band_2_gain__bass); | ||
353 | static DEVICE_ATTR(band_2_gain, S_IWUGO | S_IRUGO, pod_get_band_2_gain, pod_set_band_2_gain); | ||
354 | static DEVICE_ATTR2(band_3_gain__bass, band_3_gain, S_IWUGO | S_IRUGO, pod_get_band_3_gain__bass, pod_set_band_3_gain__bass); | ||
355 | static DEVICE_ATTR(band_3_gain, S_IWUGO | S_IRUGO, pod_get_band_3_gain, pod_set_band_3_gain); | ||
356 | static DEVICE_ATTR2(band_4_gain__bass, band_4_gain, S_IWUGO | S_IRUGO, pod_get_band_4_gain__bass, pod_set_band_4_gain__bass); | ||
357 | static DEVICE_ATTR2(band_5_gain__bass, band_5_gain, S_IWUGO | S_IRUGO, pod_get_band_5_gain__bass, pod_set_band_5_gain__bass); | ||
358 | static DEVICE_ATTR(band_4_gain, S_IWUGO | S_IRUGO, pod_get_band_4_gain, pod_set_band_4_gain); | ||
359 | static DEVICE_ATTR2(band_6_gain__bass, band_6_gain, S_IWUGO | S_IRUGO, pod_get_band_6_gain__bass, pod_set_band_6_gain__bass); | ||
360 | static DEVICE_ATTR(body, S_IRUGO, variax_get_body, line6_nop_write); | ||
361 | static DEVICE_ATTR(pickup1_enable, S_IRUGO, variax_get_pickup1_enable, line6_nop_write); | ||
362 | static DEVICE_ATTR(pickup1_type, S_IRUGO, variax_get_pickup1_type, line6_nop_write); | ||
363 | static DEVICE_ATTR(pickup1_position, S_IRUGO, variax_get_pickup1_position, line6_nop_write); | ||
364 | static DEVICE_ATTR(pickup1_angle, S_IRUGO, variax_get_pickup1_angle, line6_nop_write); | ||
365 | static DEVICE_ATTR(pickup1_level, S_IRUGO, variax_get_pickup1_level, line6_nop_write); | ||
366 | static DEVICE_ATTR(pickup2_enable, S_IRUGO, variax_get_pickup2_enable, line6_nop_write); | ||
367 | static DEVICE_ATTR(pickup2_type, S_IRUGO, variax_get_pickup2_type, line6_nop_write); | ||
368 | static DEVICE_ATTR(pickup2_position, S_IRUGO, variax_get_pickup2_position, line6_nop_write); | ||
369 | static DEVICE_ATTR(pickup2_angle, S_IRUGO, variax_get_pickup2_angle, line6_nop_write); | ||
370 | static DEVICE_ATTR(pickup2_level, S_IRUGO, variax_get_pickup2_level, line6_nop_write); | ||
371 | static DEVICE_ATTR(pickup_phase, S_IRUGO, variax_get_pickup_phase, line6_nop_write); | ||
372 | static DEVICE_ATTR(capacitance, S_IRUGO, variax_get_capacitance, line6_nop_write); | ||
373 | static DEVICE_ATTR(tone_resistance, S_IRUGO, variax_get_tone_resistance, line6_nop_write); | ||
374 | static DEVICE_ATTR(volume_resistance, S_IRUGO, variax_get_volume_resistance, line6_nop_write); | ||
375 | static DEVICE_ATTR(taper, S_IRUGO, variax_get_taper, line6_nop_write); | ||
376 | static DEVICE_ATTR(tone_dump, S_IRUGO, variax_get_tone_dump, line6_nop_write); | ||
377 | static DEVICE_ATTR(save_tone, S_IRUGO, variax_get_save_tone, line6_nop_write); | ||
378 | static DEVICE_ATTR(volume_dump, S_IRUGO, variax_get_volume_dump, line6_nop_write); | ||
379 | static DEVICE_ATTR(tuning_enable, S_IRUGO, variax_get_tuning_enable, line6_nop_write); | ||
380 | static DEVICE_ATTR(tuning6, S_IRUGO, variax_get_tuning6, line6_nop_write); | ||
381 | static DEVICE_ATTR(tuning5, S_IRUGO, variax_get_tuning5, line6_nop_write); | ||
382 | static DEVICE_ATTR(tuning4, S_IRUGO, variax_get_tuning4, line6_nop_write); | ||
383 | static DEVICE_ATTR(tuning3, S_IRUGO, variax_get_tuning3, line6_nop_write); | ||
384 | static DEVICE_ATTR(tuning2, S_IRUGO, variax_get_tuning2, line6_nop_write); | ||
385 | static DEVICE_ATTR(tuning1, S_IRUGO, variax_get_tuning1, line6_nop_write); | ||
386 | static DEVICE_ATTR(detune6, S_IRUGO, variax_get_detune6, line6_nop_write); | ||
387 | static DEVICE_ATTR(detune5, S_IRUGO, variax_get_detune5, line6_nop_write); | ||
388 | static DEVICE_ATTR(detune4, S_IRUGO, variax_get_detune4, line6_nop_write); | ||
389 | static DEVICE_ATTR(detune3, S_IRUGO, variax_get_detune3, line6_nop_write); | ||
390 | static DEVICE_ATTR(detune2, S_IRUGO, variax_get_detune2, line6_nop_write); | ||
391 | static DEVICE_ATTR(detune1, S_IRUGO, variax_get_detune1, line6_nop_write); | ||
392 | static DEVICE_ATTR(mix6, S_IRUGO, variax_get_mix6, line6_nop_write); | ||
393 | static DEVICE_ATTR(mix5, S_IRUGO, variax_get_mix5, line6_nop_write); | ||
394 | static DEVICE_ATTR(mix4, S_IRUGO, variax_get_mix4, line6_nop_write); | ||
395 | static DEVICE_ATTR(mix3, S_IRUGO, variax_get_mix3, line6_nop_write); | ||
396 | static DEVICE_ATTR(mix2, S_IRUGO, variax_get_mix2, line6_nop_write); | ||
397 | static DEVICE_ATTR(mix1, S_IRUGO, variax_get_mix1, line6_nop_write); | ||
398 | static DEVICE_ATTR(pickup_wiring, S_IRUGO, variax_get_pickup_wiring, line6_nop_write); | ||
399 | |||
400 | int pod_create_files(int firmware, int type, struct device *dev) { | ||
401 | int err; | ||
402 | CHECK_RETURN(device_create_file(dev, &dev_attr_tweak)); | ||
403 | CHECK_RETURN(device_create_file(dev, &dev_attr_wah_position)); | ||
404 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_compression_gain)); | ||
405 | CHECK_RETURN(device_create_file(dev, &dev_attr_vol_pedal_position)); | ||
406 | CHECK_RETURN(device_create_file(dev, &dev_attr_compression_threshold)); | ||
407 | CHECK_RETURN(device_create_file(dev, &dev_attr_pan)); | ||
408 | CHECK_RETURN(device_create_file(dev, &dev_attr_amp_model_setup)); | ||
409 | if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_amp_model)); | ||
410 | CHECK_RETURN(device_create_file(dev, &dev_attr_drive)); | ||
411 | CHECK_RETURN(device_create_file(dev, &dev_attr_bass)); | ||
412 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_mid)); | ||
413 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_lowmid)); | ||
414 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_treble)); | ||
415 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_highmid)); | ||
416 | CHECK_RETURN(device_create_file(dev, &dev_attr_chan_vol)); | ||
417 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_mix)); | ||
418 | CHECK_RETURN(device_create_file(dev, &dev_attr_effect_setup)); | ||
419 | if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_1_frequency)); | ||
420 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_presence)); | ||
421 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_treble__bass)); | ||
422 | CHECK_RETURN(device_create_file(dev, &dev_attr_noise_gate_enable)); | ||
423 | CHECK_RETURN(device_create_file(dev, &dev_attr_gate_threshold)); | ||
424 | CHECK_RETURN(device_create_file(dev, &dev_attr_gate_decay_time)); | ||
425 | CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_enable)); | ||
426 | CHECK_RETURN(device_create_file(dev, &dev_attr_comp_enable)); | ||
427 | CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_time)); | ||
428 | CHECK_RETURN(device_create_file(dev, &dev_attr_delay_enable)); | ||
429 | CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_1)); | ||
430 | CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_1)); | ||
431 | CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_1_note_value)); | ||
432 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_2_frequency__bass)); | ||
433 | CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_2)); | ||
434 | CHECK_RETURN(device_create_file(dev, &dev_attr_delay_volume_mix)); | ||
435 | CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_3)); | ||
436 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_enable)); | ||
437 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_type)); | ||
438 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_decay)); | ||
439 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_tone)); | ||
440 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_pre_delay)); | ||
441 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_pre_post)); | ||
442 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_2_frequency)); | ||
443 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_3_frequency__bass)); | ||
444 | CHECK_RETURN(device_create_file(dev, &dev_attr_wah_enable)); | ||
445 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_modulation_lo_cut)); | ||
446 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_delay_reverb_lo_cut)); | ||
447 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_volume_pedal_minimum)); | ||
448 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_eq_pre_post)); | ||
449 | CHECK_RETURN(device_create_file(dev, &dev_attr_volume_pre_post)); | ||
450 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_di_model)); | ||
451 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_di_delay)); | ||
452 | CHECK_RETURN(device_create_file(dev, &dev_attr_mod_enable)); | ||
453 | CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_1_note_value)); | ||
454 | CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_2)); | ||
455 | CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_3)); | ||
456 | CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_4)); | ||
457 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_5)); | ||
458 | CHECK_RETURN(device_create_file(dev, &dev_attr_mod_volume_mix)); | ||
459 | CHECK_RETURN(device_create_file(dev, &dev_attr_mod_pre_post)); | ||
460 | CHECK_RETURN(device_create_file(dev, &dev_attr_modulation_model)); | ||
461 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_3_frequency)); | ||
462 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_4_frequency__bass)); | ||
463 | CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_1_double_precision)); | ||
464 | CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_1_double_precision)); | ||
465 | if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_eq_enable)); | ||
466 | CHECK_RETURN(device_create_file(dev, &dev_attr_tap)); | ||
467 | CHECK_RETURN(device_create_file(dev, &dev_attr_volume_tweak_pedal_assign)); | ||
468 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_5_frequency)); | ||
469 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuner)); | ||
470 | CHECK_RETURN(device_create_file(dev, &dev_attr_mic_selection)); | ||
471 | CHECK_RETURN(device_create_file(dev, &dev_attr_cabinet_model)); | ||
472 | CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_model)); | ||
473 | CHECK_RETURN(device_create_file(dev, &dev_attr_roomlevel)); | ||
474 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_4_frequency)); | ||
475 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_6_frequency)); | ||
476 | CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_1_note_value)); | ||
477 | CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_2)); | ||
478 | CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_3)); | ||
479 | CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_4)); | ||
480 | CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_5)); | ||
481 | CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_6)); | ||
482 | if((type & (LINE6_BITS_LIVE)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_amp_switch_select)); | ||
483 | CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_4)); | ||
484 | CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_5)); | ||
485 | CHECK_RETURN(device_create_file(dev, &dev_attr_delay_pre_post)); | ||
486 | if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_delay_model)); | ||
487 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_delay_verb_model)); | ||
488 | CHECK_RETURN(device_create_file(dev, &dev_attr_tempo_msb)); | ||
489 | CHECK_RETURN(device_create_file(dev, &dev_attr_tempo_lsb)); | ||
490 | if(firmware >= 300) CHECK_RETURN(device_create_file(dev, &dev_attr_wah_model)); | ||
491 | if(firmware >= 214) CHECK_RETURN(device_create_file(dev, &dev_attr_bypass_volume)); | ||
492 | if((type & (LINE6_BITS_PRO)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_fx_loop_on_off)); | ||
493 | CHECK_RETURN(device_create_file(dev, &dev_attr_tweak_param_select)); | ||
494 | CHECK_RETURN(device_create_file(dev, &dev_attr_amp1_engage)); | ||
495 | if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_1_gain)); | ||
496 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_2_gain__bass)); | ||
497 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_2_gain)); | ||
498 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_3_gain__bass)); | ||
499 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_3_gain)); | ||
500 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_4_gain__bass)); | ||
501 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_5_gain__bass)); | ||
502 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_4_gain)); | ||
503 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_6_gain__bass)); | ||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | void pod_remove_files(int firmware, int type, struct device *dev) { | ||
508 | device_remove_file(dev, &dev_attr_tweak); | ||
509 | device_remove_file(dev, &dev_attr_wah_position); | ||
510 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_compression_gain); | ||
511 | device_remove_file(dev, &dev_attr_vol_pedal_position); | ||
512 | device_remove_file(dev, &dev_attr_compression_threshold); | ||
513 | device_remove_file(dev, &dev_attr_pan); | ||
514 | device_remove_file(dev, &dev_attr_amp_model_setup); | ||
515 | if(firmware >= 200) device_remove_file(dev, &dev_attr_amp_model); | ||
516 | device_remove_file(dev, &dev_attr_drive); | ||
517 | device_remove_file(dev, &dev_attr_bass); | ||
518 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_mid); | ||
519 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_lowmid); | ||
520 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_treble); | ||
521 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_highmid); | ||
522 | device_remove_file(dev, &dev_attr_chan_vol); | ||
523 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_mix); | ||
524 | device_remove_file(dev, &dev_attr_effect_setup); | ||
525 | if(firmware >= 200) device_remove_file(dev, &dev_attr_band_1_frequency); | ||
526 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_presence); | ||
527 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_treble__bass); | ||
528 | device_remove_file(dev, &dev_attr_noise_gate_enable); | ||
529 | device_remove_file(dev, &dev_attr_gate_threshold); | ||
530 | device_remove_file(dev, &dev_attr_gate_decay_time); | ||
531 | device_remove_file(dev, &dev_attr_stomp_enable); | ||
532 | device_remove_file(dev, &dev_attr_comp_enable); | ||
533 | device_remove_file(dev, &dev_attr_stomp_time); | ||
534 | device_remove_file(dev, &dev_attr_delay_enable); | ||
535 | device_remove_file(dev, &dev_attr_mod_param_1); | ||
536 | device_remove_file(dev, &dev_attr_delay_param_1); | ||
537 | device_remove_file(dev, &dev_attr_delay_param_1_note_value); | ||
538 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_2_frequency__bass); | ||
539 | device_remove_file(dev, &dev_attr_delay_param_2); | ||
540 | device_remove_file(dev, &dev_attr_delay_volume_mix); | ||
541 | device_remove_file(dev, &dev_attr_delay_param_3); | ||
542 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_enable); | ||
543 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_type); | ||
544 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_decay); | ||
545 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_tone); | ||
546 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_pre_delay); | ||
547 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_pre_post); | ||
548 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_2_frequency); | ||
549 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_3_frequency__bass); | ||
550 | device_remove_file(dev, &dev_attr_wah_enable); | ||
551 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_modulation_lo_cut); | ||
552 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_delay_reverb_lo_cut); | ||
553 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_volume_pedal_minimum); | ||
554 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_eq_pre_post); | ||
555 | device_remove_file(dev, &dev_attr_volume_pre_post); | ||
556 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_di_model); | ||
557 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_di_delay); | ||
558 | device_remove_file(dev, &dev_attr_mod_enable); | ||
559 | device_remove_file(dev, &dev_attr_mod_param_1_note_value); | ||
560 | device_remove_file(dev, &dev_attr_mod_param_2); | ||
561 | device_remove_file(dev, &dev_attr_mod_param_3); | ||
562 | device_remove_file(dev, &dev_attr_mod_param_4); | ||
563 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_mod_param_5); | ||
564 | device_remove_file(dev, &dev_attr_mod_volume_mix); | ||
565 | device_remove_file(dev, &dev_attr_mod_pre_post); | ||
566 | device_remove_file(dev, &dev_attr_modulation_model); | ||
567 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_3_frequency); | ||
568 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_4_frequency__bass); | ||
569 | device_remove_file(dev, &dev_attr_mod_param_1_double_precision); | ||
570 | device_remove_file(dev, &dev_attr_delay_param_1_double_precision); | ||
571 | if(firmware >= 200) device_remove_file(dev, &dev_attr_eq_enable); | ||
572 | device_remove_file(dev, &dev_attr_tap); | ||
573 | device_remove_file(dev, &dev_attr_volume_tweak_pedal_assign); | ||
574 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_5_frequency); | ||
575 | device_remove_file(dev, &dev_attr_tuner); | ||
576 | device_remove_file(dev, &dev_attr_mic_selection); | ||
577 | device_remove_file(dev, &dev_attr_cabinet_model); | ||
578 | device_remove_file(dev, &dev_attr_stomp_model); | ||
579 | device_remove_file(dev, &dev_attr_roomlevel); | ||
580 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_4_frequency); | ||
581 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_6_frequency); | ||
582 | device_remove_file(dev, &dev_attr_stomp_param_1_note_value); | ||
583 | device_remove_file(dev, &dev_attr_stomp_param_2); | ||
584 | device_remove_file(dev, &dev_attr_stomp_param_3); | ||
585 | device_remove_file(dev, &dev_attr_stomp_param_4); | ||
586 | device_remove_file(dev, &dev_attr_stomp_param_5); | ||
587 | device_remove_file(dev, &dev_attr_stomp_param_6); | ||
588 | if((type & (LINE6_BITS_LIVE)) != 0) device_remove_file(dev, &dev_attr_amp_switch_select); | ||
589 | device_remove_file(dev, &dev_attr_delay_param_4); | ||
590 | device_remove_file(dev, &dev_attr_delay_param_5); | ||
591 | device_remove_file(dev, &dev_attr_delay_pre_post); | ||
592 | if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_delay_model); | ||
593 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_delay_verb_model); | ||
594 | device_remove_file(dev, &dev_attr_tempo_msb); | ||
595 | device_remove_file(dev, &dev_attr_tempo_lsb); | ||
596 | if(firmware >= 300) device_remove_file(dev, &dev_attr_wah_model); | ||
597 | if(firmware >= 214) device_remove_file(dev, &dev_attr_bypass_volume); | ||
598 | if((type & (LINE6_BITS_PRO)) != 0) device_remove_file(dev, &dev_attr_fx_loop_on_off); | ||
599 | device_remove_file(dev, &dev_attr_tweak_param_select); | ||
600 | device_remove_file(dev, &dev_attr_amp1_engage); | ||
601 | if(firmware >= 200) device_remove_file(dev, &dev_attr_band_1_gain); | ||
602 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_2_gain__bass); | ||
603 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_2_gain); | ||
604 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_3_gain__bass); | ||
605 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_3_gain); | ||
606 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_4_gain__bass); | ||
607 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_5_gain__bass); | ||
608 | if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_4_gain); | ||
609 | if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_6_gain__bass); | ||
610 | } | ||
611 | |||
612 | EXPORT_SYMBOL(pod_create_files); | ||
613 | EXPORT_SYMBOL(pod_remove_files); | ||
614 | |||
615 | int variax_create_files(int firmware, int type, struct device *dev) { | ||
616 | int err; | ||
617 | CHECK_RETURN(device_create_file(dev, &dev_attr_body)); | ||
618 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_enable)); | ||
619 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_type)); | ||
620 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_position)); | ||
621 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_angle)); | ||
622 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_level)); | ||
623 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_enable)); | ||
624 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_type)); | ||
625 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_position)); | ||
626 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_angle)); | ||
627 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_level)); | ||
628 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_phase)); | ||
629 | CHECK_RETURN(device_create_file(dev, &dev_attr_capacitance)); | ||
630 | CHECK_RETURN(device_create_file(dev, &dev_attr_tone_resistance)); | ||
631 | CHECK_RETURN(device_create_file(dev, &dev_attr_volume_resistance)); | ||
632 | CHECK_RETURN(device_create_file(dev, &dev_attr_taper)); | ||
633 | CHECK_RETURN(device_create_file(dev, &dev_attr_tone_dump)); | ||
634 | CHECK_RETURN(device_create_file(dev, &dev_attr_save_tone)); | ||
635 | CHECK_RETURN(device_create_file(dev, &dev_attr_volume_dump)); | ||
636 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuning_enable)); | ||
637 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuning6)); | ||
638 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuning5)); | ||
639 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuning4)); | ||
640 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuning3)); | ||
641 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuning2)); | ||
642 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuning1)); | ||
643 | CHECK_RETURN(device_create_file(dev, &dev_attr_detune6)); | ||
644 | CHECK_RETURN(device_create_file(dev, &dev_attr_detune5)); | ||
645 | CHECK_RETURN(device_create_file(dev, &dev_attr_detune4)); | ||
646 | CHECK_RETURN(device_create_file(dev, &dev_attr_detune3)); | ||
647 | CHECK_RETURN(device_create_file(dev, &dev_attr_detune2)); | ||
648 | CHECK_RETURN(device_create_file(dev, &dev_attr_detune1)); | ||
649 | CHECK_RETURN(device_create_file(dev, &dev_attr_mix6)); | ||
650 | CHECK_RETURN(device_create_file(dev, &dev_attr_mix5)); | ||
651 | CHECK_RETURN(device_create_file(dev, &dev_attr_mix4)); | ||
652 | CHECK_RETURN(device_create_file(dev, &dev_attr_mix3)); | ||
653 | CHECK_RETURN(device_create_file(dev, &dev_attr_mix2)); | ||
654 | CHECK_RETURN(device_create_file(dev, &dev_attr_mix1)); | ||
655 | CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_wiring)); | ||
656 | return 0; | ||
657 | } | ||
658 | |||
659 | void variax_remove_files(int firmware, int type, struct device *dev) { | ||
660 | device_remove_file(dev, &dev_attr_body); | ||
661 | device_remove_file(dev, &dev_attr_pickup1_enable); | ||
662 | device_remove_file(dev, &dev_attr_pickup1_type); | ||
663 | device_remove_file(dev, &dev_attr_pickup1_position); | ||
664 | device_remove_file(dev, &dev_attr_pickup1_angle); | ||
665 | device_remove_file(dev, &dev_attr_pickup1_level); | ||
666 | device_remove_file(dev, &dev_attr_pickup2_enable); | ||
667 | device_remove_file(dev, &dev_attr_pickup2_type); | ||
668 | device_remove_file(dev, &dev_attr_pickup2_position); | ||
669 | device_remove_file(dev, &dev_attr_pickup2_angle); | ||
670 | device_remove_file(dev, &dev_attr_pickup2_level); | ||
671 | device_remove_file(dev, &dev_attr_pickup_phase); | ||
672 | device_remove_file(dev, &dev_attr_capacitance); | ||
673 | device_remove_file(dev, &dev_attr_tone_resistance); | ||
674 | device_remove_file(dev, &dev_attr_volume_resistance); | ||
675 | device_remove_file(dev, &dev_attr_taper); | ||
676 | device_remove_file(dev, &dev_attr_tone_dump); | ||
677 | device_remove_file(dev, &dev_attr_save_tone); | ||
678 | device_remove_file(dev, &dev_attr_volume_dump); | ||
679 | device_remove_file(dev, &dev_attr_tuning_enable); | ||
680 | device_remove_file(dev, &dev_attr_tuning6); | ||
681 | device_remove_file(dev, &dev_attr_tuning5); | ||
682 | device_remove_file(dev, &dev_attr_tuning4); | ||
683 | device_remove_file(dev, &dev_attr_tuning3); | ||
684 | device_remove_file(dev, &dev_attr_tuning2); | ||
685 | device_remove_file(dev, &dev_attr_tuning1); | ||
686 | device_remove_file(dev, &dev_attr_detune6); | ||
687 | device_remove_file(dev, &dev_attr_detune5); | ||
688 | device_remove_file(dev, &dev_attr_detune4); | ||
689 | device_remove_file(dev, &dev_attr_detune3); | ||
690 | device_remove_file(dev, &dev_attr_detune2); | ||
691 | device_remove_file(dev, &dev_attr_detune1); | ||
692 | device_remove_file(dev, &dev_attr_mix6); | ||
693 | device_remove_file(dev, &dev_attr_mix5); | ||
694 | device_remove_file(dev, &dev_attr_mix4); | ||
695 | device_remove_file(dev, &dev_attr_mix3); | ||
696 | device_remove_file(dev, &dev_attr_mix2); | ||
697 | device_remove_file(dev, &dev_attr_mix1); | ||
698 | device_remove_file(dev, &dev_attr_pickup_wiring); | ||
699 | } | ||
700 | |||
701 | EXPORT_SYMBOL(variax_create_files); | ||
702 | EXPORT_SYMBOL(variax_remove_files); | ||
diff --git a/drivers/staging/line6/control.h b/drivers/staging/line6/control.h new file mode 100644 index 000000000000..2f19665d95a9 --- /dev/null +++ b/drivers/staging/line6/control.h | |||
@@ -0,0 +1,187 @@ | |||
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 | #ifndef LINE6_CONTROL_H | ||
13 | #define LINE6_CONTROL_H | ||
14 | |||
15 | |||
16 | /** | ||
17 | List of PODxt Pro controls. | ||
18 | See Appendix C of the "PODxt (Pro) Pilot's Handbook" by Line6. | ||
19 | Comments after the number refer to the PODxt Pro firmware version required | ||
20 | for this feature. | ||
21 | */ | ||
22 | enum { | ||
23 | POD_tweak = 1, | ||
24 | POD_wah_position = 4, | ||
25 | POD_compression_gain = 5, /* device: LINE6_BITS_PODXTALL */ | ||
26 | POD_vol_pedal_position = 7, | ||
27 | POD_compression_threshold = 9, | ||
28 | POD_pan = 10, | ||
29 | POD_amp_model_setup = 11, | ||
30 | POD_amp_model = 12, /* firmware: 2.0 */ | ||
31 | POD_drive = 13, | ||
32 | POD_bass = 14, | ||
33 | POD_mid = 15, /* device: LINE6_BITS_PODXTALL */ | ||
34 | POD_lowmid = 15, /* device: LINE6_BITS_BASSPODXTALL */ | ||
35 | POD_treble = 16, /* device: LINE6_BITS_PODXTALL */ | ||
36 | POD_highmid = 16, /* device: LINE6_BITS_BASSPODXTALL */ | ||
37 | POD_chan_vol = 17, | ||
38 | POD_reverb_mix = 18, /* device: LINE6_BITS_PODXTALL */ | ||
39 | POD_effect_setup = 19, | ||
40 | POD_band_1_frequency = 20, /* firmware: 2.0 */ | ||
41 | POD_presence = 21, /* device: LINE6_BITS_PODXTALL */ | ||
42 | POD_treble__bass = 21, /* device: LINE6_BITS_BASSPODXTALL */ | ||
43 | POD_noise_gate_enable = 22, | ||
44 | POD_gate_threshold = 23, | ||
45 | POD_gate_decay_time = 24, | ||
46 | POD_stomp_enable = 25, | ||
47 | POD_comp_enable = 26, | ||
48 | POD_stomp_time = 27, | ||
49 | POD_delay_enable = 28, | ||
50 | POD_mod_param_1 = 29, | ||
51 | POD_delay_param_1 = 30, | ||
52 | POD_delay_param_1_note_value = 31, | ||
53 | POD_band_2_frequency__bass = 32, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ | ||
54 | POD_delay_param_2 = 33, | ||
55 | POD_delay_volume_mix = 34, | ||
56 | POD_delay_param_3 = 35, | ||
57 | POD_reverb_enable = 36, /* device: LINE6_BITS_PODXTALL */ | ||
58 | POD_reverb_type = 37, /* device: LINE6_BITS_PODXTALL */ | ||
59 | POD_reverb_decay = 38, /* device: LINE6_BITS_PODXTALL */ | ||
60 | POD_reverb_tone = 39, /* device: LINE6_BITS_PODXTALL */ | ||
61 | POD_reverb_pre_delay = 40, /* device: LINE6_BITS_PODXTALL */ | ||
62 | POD_reverb_pre_post = 41, /* device: LINE6_BITS_PODXTALL */ | ||
63 | POD_band_2_frequency = 42, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ | ||
64 | POD_band_3_frequency__bass = 42, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ | ||
65 | POD_wah_enable = 43, | ||
66 | POD_modulation_lo_cut = 44, /* device: LINE6_BITS_BASSPODXTALL */ | ||
67 | POD_delay_reverb_lo_cut = 45, /* device: LINE6_BITS_BASSPODXTALL */ | ||
68 | POD_volume_pedal_minimum = 46, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ | ||
69 | POD_eq_pre_post = 46, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ | ||
70 | POD_volume_pre_post = 47, | ||
71 | POD_di_model = 48, /* device: LINE6_BITS_BASSPODXTALL */ | ||
72 | POD_di_delay = 49, /* device: LINE6_BITS_BASSPODXTALL */ | ||
73 | POD_mod_enable = 50, | ||
74 | POD_mod_param_1_note_value = 51, | ||
75 | POD_mod_param_2 = 52, | ||
76 | POD_mod_param_3 = 53, | ||
77 | POD_mod_param_4 = 54, | ||
78 | POD_mod_param_5 = 55, /* device: LINE6_BITS_BASSPODXTALL */ | ||
79 | POD_mod_volume_mix = 56, | ||
80 | POD_mod_pre_post = 57, | ||
81 | POD_modulation_model = 58, | ||
82 | POD_band_3_frequency = 60, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ | ||
83 | POD_band_4_frequency__bass = 60, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ | ||
84 | POD_mod_param_1_double_precision = 61, | ||
85 | POD_delay_param_1_double_precision = 62, | ||
86 | POD_eq_enable = 63, /* firmware: 2.0 */ | ||
87 | POD_tap = 64, | ||
88 | POD_volume_tweak_pedal_assign = 65, | ||
89 | POD_band_5_frequency = 68, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ | ||
90 | POD_tuner = 69, | ||
91 | POD_mic_selection = 70, | ||
92 | POD_cabinet_model = 71, | ||
93 | POD_stomp_model = 75, | ||
94 | POD_roomlevel = 76, | ||
95 | POD_band_4_frequency = 77, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ | ||
96 | POD_band_6_frequency = 77, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ | ||
97 | POD_stomp_param_1_note_value = 78, | ||
98 | POD_stomp_param_2 = 79, | ||
99 | POD_stomp_param_3 = 80, | ||
100 | POD_stomp_param_4 = 81, | ||
101 | POD_stomp_param_5 = 82, | ||
102 | POD_stomp_param_6 = 83, | ||
103 | POD_amp_switch_select = 84, /* device: LINE6_BITS_LIVE */ | ||
104 | POD_delay_param_4 = 85, | ||
105 | POD_delay_param_5 = 86, | ||
106 | POD_delay_pre_post = 87, | ||
107 | POD_delay_model = 88, /* device: LINE6_BITS_PODXTALL */ | ||
108 | POD_delay_verb_model = 88, /* device: LINE6_BITS_BASSPODXTALL */ | ||
109 | POD_tempo_msb = 89, | ||
110 | POD_tempo_lsb = 90, | ||
111 | POD_wah_model = 91, /* firmware: 3.0 */ | ||
112 | POD_bypass_volume = 105, /* firmware: 2.14 */ | ||
113 | POD_fx_loop_on_off = 107, /* device: LINE6_BITS_PRO */ | ||
114 | POD_tweak_param_select = 108, | ||
115 | POD_amp1_engage = 111, | ||
116 | POD_band_1_gain = 114, /* firmware: 2.0 */ | ||
117 | POD_band_2_gain__bass = 115, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ | ||
118 | POD_band_2_gain = 116, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ | ||
119 | POD_band_3_gain__bass = 116, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ | ||
120 | POD_band_3_gain = 117, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ | ||
121 | POD_band_4_gain__bass = 117, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ | ||
122 | POD_band_5_gain__bass = 118, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ | ||
123 | POD_band_4_gain = 119, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */ | ||
124 | POD_band_6_gain__bass = 119 /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */ | ||
125 | }; | ||
126 | |||
127 | /** | ||
128 | List of Variax workbench controls (dump). | ||
129 | */ | ||
130 | enum { | ||
131 | VARIAX_body = 3, | ||
132 | VARIAX_pickup1_enable = 4, /* 0: enabled, 1: disabled */ | ||
133 | VARIAX_pickup1_type = 8, | ||
134 | VARIAX_pickup1_position = 9, /* type: 24 bit float */ | ||
135 | VARIAX_pickup1_angle = 12, /* type: 24 bit float */ | ||
136 | VARIAX_pickup1_level = 15, /* type: 24 bit float */ | ||
137 | VARIAX_pickup2_enable = 18, /* 0: enabled, 1: disabled */ | ||
138 | VARIAX_pickup2_type = 22, | ||
139 | VARIAX_pickup2_position = 23, /* type: 24 bit float */ | ||
140 | VARIAX_pickup2_angle = 26, /* type: 24 bit float */ | ||
141 | VARIAX_pickup2_level = 29, /* type: 24 bit float */ | ||
142 | VARIAX_pickup_phase = 32, /* 0: in phase, 1: out of phase */ | ||
143 | VARIAX_capacitance = 33, /* type: 24 bit float */ | ||
144 | VARIAX_tone_resistance = 36, /* type: 24 bit float */ | ||
145 | VARIAX_volume_resistance = 39, /* type: 24 bit float */ | ||
146 | VARIAX_taper = 42, /* 0: Linear, 1: Audio */ | ||
147 | VARIAX_tone_dump = 43, /* type: 24 bit float */ | ||
148 | VARIAX_save_tone = 46, | ||
149 | VARIAX_volume_dump = 47, /* type: 24 bit float */ | ||
150 | VARIAX_tuning_enable = 50, | ||
151 | VARIAX_tuning6 = 51, | ||
152 | VARIAX_tuning5 = 52, | ||
153 | VARIAX_tuning4 = 53, | ||
154 | VARIAX_tuning3 = 54, | ||
155 | VARIAX_tuning2 = 55, | ||
156 | VARIAX_tuning1 = 56, | ||
157 | VARIAX_detune6 = 57, /* type: 24 bit float */ | ||
158 | VARIAX_detune5 = 60, /* type: 24 bit float */ | ||
159 | VARIAX_detune4 = 63, /* type: 24 bit float */ | ||
160 | VARIAX_detune3 = 66, /* type: 24 bit float */ | ||
161 | VARIAX_detune2 = 69, /* type: 24 bit float */ | ||
162 | VARIAX_detune1 = 72, /* type: 24 bit float */ | ||
163 | VARIAX_mix6 = 75, /* type: 24 bit float */ | ||
164 | VARIAX_mix5 = 78, /* type: 24 bit float */ | ||
165 | VARIAX_mix4 = 81, /* type: 24 bit float */ | ||
166 | VARIAX_mix3 = 84, /* type: 24 bit float */ | ||
167 | VARIAX_mix2 = 87, /* type: 24 bit float */ | ||
168 | VARIAX_mix1 = 90, /* type: 24 bit float */ | ||
169 | VARIAX_pickup_wiring = 96 /* 0: parallel, 1: series */ | ||
170 | }; | ||
171 | |||
172 | /** | ||
173 | List of Variax workbench controls (MIDI). | ||
174 | */ | ||
175 | enum { | ||
176 | VARIAXMIDI_volume = 7, | ||
177 | VARIAXMIDI_tone = 79, | ||
178 | }; | ||
179 | |||
180 | |||
181 | extern int pod_create_files(int firmware, int type, struct device *dev); | ||
182 | extern void pod_remove_files(int firmware, int type, struct device *dev); | ||
183 | extern int variax_create_files(int firmware, int type, struct device *dev); | ||
184 | extern void variax_remove_files(int firmware, int type, struct device *dev); | ||
185 | |||
186 | |||
187 | #endif | ||
diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c new file mode 100644 index 000000000000..f20efa583691 --- /dev/null +++ b/drivers/staging/line6/driver.c | |||
@@ -0,0 +1,1050 @@ | |||
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/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/usb.h> | ||
17 | |||
18 | #include "audio.h" | ||
19 | #include "capture.h" | ||
20 | #include "control.h" | ||
21 | #include "midi.h" | ||
22 | #include "playback.h" | ||
23 | #include "pod.h" | ||
24 | #include "revision.h" | ||
25 | #include "toneport.h" | ||
26 | #include "usbdefs.h" | ||
27 | #include "variax.h" | ||
28 | |||
29 | |||
30 | #define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>" | ||
31 | #define DRIVER_DESC "Line6 USB Driver" | ||
32 | #define DRIVER_VERSION "0.8.0" | ||
33 | |||
34 | |||
35 | /* table of devices that work with this driver */ | ||
36 | static struct usb_device_id line6_id_table[] = { | ||
37 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXT) }, | ||
38 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTLIVE) }, | ||
39 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTPRO) }, | ||
40 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_GUITARPORT) }, | ||
41 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_POCKETPOD) }, | ||
42 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3) }, | ||
43 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3LIVE) }, | ||
44 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT) }, | ||
45 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTLIVE) }, | ||
46 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTPRO) }, | ||
47 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_GX) }, | ||
48 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX1) }, | ||
49 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX2) }, | ||
50 | { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_VARIAX) }, | ||
51 | { }, | ||
52 | }; | ||
53 | MODULE_DEVICE_TABLE (usb, line6_id_table); | ||
54 | |||
55 | static struct line6_properties line6_properties_table[] = { | ||
56 | { "BassPODxt", LINE6_BIT_BASSPODXT, LINE6_BIT_CONTROL_PCM }, | ||
57 | { "BassPODxt Live", LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM }, | ||
58 | { "BassPODxt Pro", LINE6_BIT_BASSPODXTPRO, LINE6_BIT_CONTROL_PCM }, | ||
59 | { "GuitarPort", LINE6_BIT_GUITARPORT, LINE6_BIT_PCM }, | ||
60 | { "Pocket POD", LINE6_BIT_POCKETPOD, LINE6_BIT_CONTROL_PCM }, | ||
61 | { "POD X3", LINE6_BIT_PODX3, LINE6_BIT_PCM }, | ||
62 | { "POD X3 Live", LINE6_BIT_PODX3LIVE, LINE6_BIT_PCM }, | ||
63 | { "PODxt", LINE6_BIT_PODXT, LINE6_BIT_CONTROL_PCM }, | ||
64 | { "PODxt Live", LINE6_BIT_PODXTLIVE, LINE6_BIT_CONTROL_PCM }, | ||
65 | { "PODxt Pro", LINE6_BIT_PODXTPRO, LINE6_BIT_CONTROL_PCM }, | ||
66 | { "TonePort GX", LINE6_BIT_TONEPORT_GX, LINE6_BIT_PCM }, | ||
67 | { "TonePort UX1", LINE6_BIT_TONEPORT_UX1, LINE6_BIT_PCM }, | ||
68 | { "TonePort UX2", LINE6_BIT_TONEPORT_UX2, LINE6_BIT_PCM }, | ||
69 | { "Variax Workbench", LINE6_BIT_VARIAX, LINE6_BIT_CONTROL } | ||
70 | }; | ||
71 | |||
72 | |||
73 | /* | ||
74 | This is Line6's MIDI manufacturer ID. | ||
75 | */ | ||
76 | const unsigned char line6_midi_id[] = { 0x00, 0x01, 0x0c }; | ||
77 | |||
78 | struct usb_line6 *line6_devices[LINE6_MAX_DEVICES]; | ||
79 | struct workqueue_struct *line6_workqueue; | ||
80 | |||
81 | |||
82 | /** | ||
83 | Class for asynchronous messages. | ||
84 | */ | ||
85 | struct message | ||
86 | { | ||
87 | struct usb_line6 *line6; | ||
88 | const char *buffer; | ||
89 | int size; | ||
90 | int done; | ||
91 | }; | ||
92 | |||
93 | |||
94 | /* | ||
95 | Forward declarations. | ||
96 | */ | ||
97 | static void line6_data_received(struct urb *urb PT_REGS); | ||
98 | static int line6_send_raw_message_async_part(struct message *msg, struct urb *urb); | ||
99 | |||
100 | |||
101 | /* | ||
102 | Start to listen on endpoint. | ||
103 | */ | ||
104 | static int line6_start_listen(struct usb_line6 *line6) | ||
105 | { | ||
106 | usb_fill_int_urb(line6->urb_listen, | ||
107 | line6->usbdev, | ||
108 | usb_rcvintpipe(line6->usbdev, line6->ep_control_read), | ||
109 | line6->buffer_listen, LINE6_BUFSIZE_LISTEN, | ||
110 | line6_data_received, | ||
111 | line6, | ||
112 | line6->interval); | ||
113 | line6->urb_listen->actual_length = 0; | ||
114 | return usb_submit_urb(line6->urb_listen, GFP_KERNEL); | ||
115 | } | ||
116 | |||
117 | #if DO_DUMP_ANY | ||
118 | /* | ||
119 | Write hexdump to syslog. | ||
120 | */ | ||
121 | void line6_write_hexdump(struct usb_line6 *line6, char dir, const unsigned char *buffer, int size) | ||
122 | { | ||
123 | static const int BYTES_PER_LINE = 8; | ||
124 | char hexdump[100]; | ||
125 | char asc[BYTES_PER_LINE + 1]; | ||
126 | int i, j; | ||
127 | |||
128 | for(i = 0; i < size; i += BYTES_PER_LINE) { | ||
129 | int hexdumpsize = sizeof(hexdump); | ||
130 | char *p = hexdump; | ||
131 | int n = min(size - i, BYTES_PER_LINE); | ||
132 | asc[n] = 0; | ||
133 | |||
134 | for(j = 0; j < BYTES_PER_LINE; ++j) { | ||
135 | int bytes; | ||
136 | |||
137 | if(j < n) { | ||
138 | unsigned char val = buffer[i + j]; | ||
139 | bytes = snprintf(p, hexdumpsize, " %02X", val); | ||
140 | asc[j] = ((val >= 0x20) && (val < 0x7f)) ? val : '.'; | ||
141 | } | ||
142 | else | ||
143 | bytes = snprintf(p, hexdumpsize, " "); | ||
144 | |||
145 | if(bytes > hexdumpsize) | ||
146 | break; /* buffer overflow */ | ||
147 | |||
148 | p += bytes; | ||
149 | hexdumpsize -= bytes; | ||
150 | } | ||
151 | |||
152 | dev_info(line6->ifcdev, "%c%04X:%s %s\n", dir, i, hexdump, asc); | ||
153 | } | ||
154 | } | ||
155 | #endif | ||
156 | |||
157 | #if DO_DUMP_URB_RECEIVE | ||
158 | /* | ||
159 | Dump URB data to syslog. | ||
160 | */ | ||
161 | static void line6_dump_urb(struct urb *urb) | ||
162 | { | ||
163 | struct usb_line6 *line6 = (struct usb_line6 *)urb->context; | ||
164 | |||
165 | if(urb->status < 0) | ||
166 | return; | ||
167 | |||
168 | line6_write_hexdump(line6, 'R', (unsigned char *)urb->transfer_buffer, urb->actual_length); | ||
169 | } | ||
170 | #endif | ||
171 | |||
172 | /* | ||
173 | Send raw message in pieces of wMaxPacketSize bytes. | ||
174 | */ | ||
175 | int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, int size) | ||
176 | { | ||
177 | int i, done = 0; | ||
178 | |||
179 | #if DO_DUMP_URB_SEND | ||
180 | line6_write_hexdump(line6, 'S', buffer, size); | ||
181 | #endif | ||
182 | |||
183 | for(i = 0; i < size; i += line6->max_packet_size) { | ||
184 | int partial; | ||
185 | const char *frag_buf = buffer + i; | ||
186 | int frag_size = min(line6->max_packet_size, size - i); | ||
187 | int retval = usb_interrupt_msg(line6->usbdev, | ||
188 | usb_sndintpipe(line6->usbdev, line6->ep_control_write), | ||
189 | (char *)frag_buf, frag_size, &partial, LINE6_TIMEOUT * HZ); | ||
190 | |||
191 | if(retval) { | ||
192 | dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval); | ||
193 | break; | ||
194 | } | ||
195 | |||
196 | done += frag_size; | ||
197 | } | ||
198 | |||
199 | return done; | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | Notification of completion of asynchronous request transmission. | ||
204 | */ | ||
205 | static void line6_async_request_sent(struct urb *urb PT_REGS) | ||
206 | { | ||
207 | struct message *msg = (struct message *)urb->context; | ||
208 | |||
209 | if(msg->done >= msg->size) { | ||
210 | usb_free_urb(urb); | ||
211 | kfree(msg); | ||
212 | } | ||
213 | else | ||
214 | line6_send_raw_message_async_part(msg, urb); | ||
215 | } | ||
216 | |||
217 | /* | ||
218 | Asynchronously send part of a raw message. | ||
219 | */ | ||
220 | static int line6_send_raw_message_async_part(struct message *msg, struct urb *urb) | ||
221 | { | ||
222 | int retval; | ||
223 | struct usb_line6 *line6 = msg->line6; | ||
224 | int done = msg->done; | ||
225 | int bytes = min(msg->size - done, line6->max_packet_size); | ||
226 | |||
227 | usb_fill_int_urb(urb, | ||
228 | line6->usbdev, | ||
229 | usb_sndintpipe(line6->usbdev, line6->ep_control_write), | ||
230 | (char *)msg->buffer + done, bytes, | ||
231 | line6_async_request_sent, msg, line6->interval); | ||
232 | |||
233 | #if DO_DUMP_URB_SEND | ||
234 | line6_write_hexdump(line6, 'S', (char *)msg->buffer + done, bytes); | ||
235 | #endif | ||
236 | |||
237 | msg->done += bytes; | ||
238 | retval = usb_submit_urb(urb, GFP_ATOMIC); | ||
239 | |||
240 | if(retval < 0) { | ||
241 | dev_err(line6->ifcdev, "line6_send_raw_message_async: usb_submit_urb failed (%d)\n", retval); | ||
242 | usb_free_urb(urb); | ||
243 | kfree(msg); | ||
244 | return -EINVAL; | ||
245 | } | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | Asynchronously send raw message. | ||
252 | */ | ||
253 | int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, int size) | ||
254 | { | ||
255 | struct message *msg; | ||
256 | struct urb *urb; | ||
257 | |||
258 | /* create message: */ | ||
259 | msg = kmalloc(sizeof(struct message), GFP_ATOMIC); | ||
260 | |||
261 | if(msg == NULL) { | ||
262 | dev_err(line6->ifcdev, "Out of memory\n"); | ||
263 | return -ENOMEM; | ||
264 | } | ||
265 | |||
266 | /* create URB: */ | ||
267 | urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
268 | |||
269 | if(urb == NULL) { | ||
270 | kfree(msg); | ||
271 | dev_err(line6->ifcdev, "Out of memory\n"); | ||
272 | return -ENOMEM; | ||
273 | } | ||
274 | |||
275 | /* set message data: */ | ||
276 | msg->line6 = line6; | ||
277 | msg->buffer = buffer; | ||
278 | msg->size = size; | ||
279 | msg->done = 0; | ||
280 | |||
281 | /* start sending: */ | ||
282 | return line6_send_raw_message_async_part(msg, urb); | ||
283 | } | ||
284 | |||
285 | /* | ||
286 | Send sysex message in pieces of wMaxPacketSize bytes. | ||
287 | */ | ||
288 | int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, int size) | ||
289 | { | ||
290 | return line6_send_raw_message(line6, buffer, size + SYSEX_EXTRA_SIZE) - SYSEX_EXTRA_SIZE; | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | Allocate buffer for sysex message and prepare header. | ||
295 | @param code sysex message code | ||
296 | @param size number of bytes between code and sysex end | ||
297 | */ | ||
298 | char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, int size) | ||
299 | { | ||
300 | char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_KERNEL); | ||
301 | |||
302 | if(!buffer) { | ||
303 | dev_err(line6->ifcdev, "out of memory\n"); | ||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | buffer[0] = LINE6_SYSEX_BEGIN; | ||
308 | memcpy(buffer + 1, line6_midi_id, sizeof(line6_midi_id)); | ||
309 | buffer[sizeof(line6_midi_id) + 1] = code1; | ||
310 | buffer[sizeof(line6_midi_id) + 2] = code2; | ||
311 | buffer[sizeof(line6_midi_id) + 3 + size] = LINE6_SYSEX_END; | ||
312 | return buffer; | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | Notification of data received from the Line6 device. | ||
317 | */ | ||
318 | static void line6_data_received(struct urb *urb PT_REGS) | ||
319 | { | ||
320 | struct usb_line6 *line6 = (struct usb_line6 *)urb->context; | ||
321 | struct MidiBuffer *mb = &line6->line6midi->midibuf_in; | ||
322 | int done; | ||
323 | |||
324 | if(urb->status == -ESHUTDOWN) | ||
325 | return; | ||
326 | |||
327 | #if DO_DUMP_URB_RECEIVE | ||
328 | line6_dump_urb(urb); | ||
329 | #endif | ||
330 | |||
331 | done = midibuf_write(mb, urb->transfer_buffer, urb->actual_length); | ||
332 | |||
333 | if(done < urb->actual_length) { | ||
334 | midibuf_ignore(mb, done); | ||
335 | DEBUG_MESSAGES(dev_err(line6->ifcdev, "%d %d buffer overflow - message skipped\n", done, urb->actual_length)); | ||
336 | } | ||
337 | |||
338 | for(;;) { | ||
339 | done = midibuf_read(mb, line6->buffer_message, LINE6_MESSAGE_MAXLEN); | ||
340 | |||
341 | if(done == 0) | ||
342 | break; | ||
343 | |||
344 | /* MIDI input filter */ | ||
345 | if(midibuf_skip_message(mb, line6->line6midi->midi_mask_receive)) | ||
346 | continue; | ||
347 | |||
348 | line6->message_length = done; | ||
349 | #if DO_DUMP_MIDI_RECEIVE | ||
350 | line6_write_hexdump(line6, 'r', line6->buffer_message, done); | ||
351 | #endif | ||
352 | line6_midi_receive(line6, line6->buffer_message, done); | ||
353 | |||
354 | switch(line6->usbdev->descriptor.idProduct) { | ||
355 | case LINE6_DEVID_BASSPODXT: | ||
356 | case LINE6_DEVID_BASSPODXTLIVE: | ||
357 | case LINE6_DEVID_BASSPODXTPRO: | ||
358 | case LINE6_DEVID_PODXT: | ||
359 | case LINE6_DEVID_PODXTPRO: | ||
360 | case LINE6_DEVID_POCKETPOD: | ||
361 | pod_process_message((struct usb_line6_pod *)line6); | ||
362 | break; | ||
363 | |||
364 | case LINE6_DEVID_PODXTLIVE: | ||
365 | switch(line6->interface_number) { | ||
366 | case PODXTLIVE_INTERFACE_POD: | ||
367 | pod_process_message((struct usb_line6_pod *)line6); | ||
368 | break; | ||
369 | |||
370 | case PODXTLIVE_INTERFACE_VARIAX: | ||
371 | variax_process_message((struct usb_line6_variax *)line6); | ||
372 | break; | ||
373 | |||
374 | default: | ||
375 | dev_err(line6->ifcdev, "PODxt Live interface %d not supported\n", line6->interface_number); | ||
376 | } | ||
377 | break; | ||
378 | |||
379 | case LINE6_DEVID_VARIAX: | ||
380 | variax_process_message((struct usb_line6_variax *)line6); | ||
381 | break; | ||
382 | |||
383 | default: | ||
384 | MISSING_CASE; | ||
385 | } | ||
386 | } | ||
387 | |||
388 | line6_start_listen(line6); | ||
389 | } | ||
390 | |||
391 | /* | ||
392 | Send channel number (i.e., switch to a different sound). | ||
393 | */ | ||
394 | int line6_send_program(struct usb_line6 *line6, int value) | ||
395 | { | ||
396 | int retval; | ||
397 | unsigned char *buffer; | ||
398 | unsigned int partial; | ||
399 | |||
400 | buffer = kmalloc(2, GFP_KERNEL); | ||
401 | |||
402 | if(!buffer) { | ||
403 | dev_err(line6->ifcdev, "out of memory\n"); | ||
404 | return -ENOMEM; | ||
405 | } | ||
406 | |||
407 | buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST; | ||
408 | buffer[1] = value; | ||
409 | |||
410 | #if DO_DUMP_URB_SEND | ||
411 | line6_write_hexdump(line6, 'S', buffer, 2); | ||
412 | #endif | ||
413 | |||
414 | retval = usb_interrupt_msg(line6->usbdev, | ||
415 | usb_sndintpipe(line6->usbdev, line6->ep_control_write), | ||
416 | buffer, 2, &partial, LINE6_TIMEOUT * HZ); | ||
417 | |||
418 | if(retval) | ||
419 | dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval); | ||
420 | |||
421 | kfree(buffer); | ||
422 | return retval; | ||
423 | } | ||
424 | |||
425 | /* | ||
426 | Transmit Line6 control parameter. | ||
427 | */ | ||
428 | int line6_transmit_parameter(struct usb_line6 *line6, int param, int value) | ||
429 | { | ||
430 | int retval; | ||
431 | unsigned char *buffer; | ||
432 | unsigned int partial; | ||
433 | |||
434 | buffer = kmalloc(3, GFP_KERNEL); | ||
435 | |||
436 | if(!buffer) { | ||
437 | dev_err(line6->ifcdev, "out of memory\n"); | ||
438 | return -ENOMEM; | ||
439 | } | ||
440 | |||
441 | buffer[0] = LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST; | ||
442 | buffer[1] = param; | ||
443 | buffer[2] = value; | ||
444 | |||
445 | #if DO_DUMP_URB_SEND | ||
446 | line6_write_hexdump(line6, 'S', buffer, 3); | ||
447 | #endif | ||
448 | |||
449 | retval = usb_interrupt_msg(line6->usbdev, | ||
450 | usb_sndintpipe(line6->usbdev, line6->ep_control_write), | ||
451 | buffer, 3, &partial, LINE6_TIMEOUT * HZ); | ||
452 | |||
453 | if(retval) | ||
454 | dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval); | ||
455 | |||
456 | kfree(buffer); | ||
457 | return retval; | ||
458 | } | ||
459 | |||
460 | /* | ||
461 | Read data from device. | ||
462 | */ | ||
463 | int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t datalen) | ||
464 | { | ||
465 | struct usb_device *usbdev = line6->usbdev; | ||
466 | int ret; | ||
467 | unsigned char len; | ||
468 | |||
469 | /* query the serial number: */ | ||
470 | ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, | ||
471 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, | ||
472 | (datalen << 8) | 0x21, address, 0, 0, LINE6_TIMEOUT * HZ); | ||
473 | |||
474 | if(ret < 0) { | ||
475 | dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); | ||
476 | return ret; | ||
477 | } | ||
478 | |||
479 | /* Wait for data length. We'll get a couple of 0xff until length arrives. */ | ||
480 | do { | ||
481 | ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, | ||
482 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | ||
483 | 0x0012, 0x0000, &len, 1, LINE6_TIMEOUT * HZ); | ||
484 | if(ret < 0) { | ||
485 | dev_err(line6->ifcdev, "receive length failed (error %d)\n", ret); | ||
486 | return ret; | ||
487 | } | ||
488 | } | ||
489 | while(len == 0xff); | ||
490 | |||
491 | if(len != datalen) { /* should be equal or something went wrong */ | ||
492 | dev_err(line6->ifcdev, "length mismatch (expected %d, got %d)\n", (int)datalen, (int)len); | ||
493 | return -EINVAL; | ||
494 | } | ||
495 | |||
496 | /* receive the result: */ | ||
497 | ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, | ||
498 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | ||
499 | 0x0013, 0x0000, data, datalen, LINE6_TIMEOUT * HZ); | ||
500 | |||
501 | if(ret < 0) { | ||
502 | dev_err(line6->ifcdev, "read failed (error %d)\n", ret); | ||
503 | return ret; | ||
504 | } | ||
505 | |||
506 | return 0; | ||
507 | } | ||
508 | |||
509 | /* | ||
510 | Write data to device. | ||
511 | */ | ||
512 | int line6_write_data(struct usb_line6 *line6, int address, void *data, size_t datalen) | ||
513 | { | ||
514 | struct usb_device *usbdev = line6->usbdev; | ||
515 | int ret; | ||
516 | unsigned char status; | ||
517 | |||
518 | ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev,0), 0x67, | ||
519 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, | ||
520 | 0x0022, address, data, datalen, LINE6_TIMEOUT * HZ); | ||
521 | |||
522 | if(ret < 0) { | ||
523 | dev_err(line6->ifcdev, "write request failed (error %d)\n", ret); | ||
524 | return ret; | ||
525 | } | ||
526 | |||
527 | do { | ||
528 | ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev,0), 0x67, | ||
529 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | ||
530 | 0x0012, 0x0000, &status, 1, LINE6_TIMEOUT * HZ); | ||
531 | |||
532 | if(ret < 0) { | ||
533 | dev_err(line6->ifcdev, "receiving status failed (error %d)\n", ret); | ||
534 | return ret; | ||
535 | } | ||
536 | } | ||
537 | while(status == 0xff); | ||
538 | |||
539 | if(status != 0) { | ||
540 | dev_err(line6->ifcdev, "write failed (error %d)\n", ret); | ||
541 | return -EINVAL; | ||
542 | } | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | /* | ||
548 | Read Line6 device serial number. | ||
549 | (POD, TonePort, GuitarPort) | ||
550 | */ | ||
551 | int line6_read_serial_number(struct usb_line6 *line6, int *serial_number) | ||
552 | { | ||
553 | return line6_read_data(line6, 0x80d0, serial_number, sizeof(*serial_number)); | ||
554 | } | ||
555 | |||
556 | /* | ||
557 | No operation (i.e., unsupported). | ||
558 | */ | ||
559 | ssize_t line6_nop_read(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
560 | { | ||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | /* | ||
565 | No operation (i.e., unsupported). | ||
566 | */ | ||
567 | ssize_t line6_nop_write(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
568 | { | ||
569 | return count; | ||
570 | } | ||
571 | |||
572 | /* | ||
573 | "write" request on "raw" special file. | ||
574 | */ | ||
575 | #if CREATE_RAW_FILE | ||
576 | ssize_t line6_set_raw(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
577 | { | ||
578 | struct usb_interface *interface = to_usb_interface(dev); | ||
579 | struct usb_line6 *line6 = usb_get_intfdata(interface); | ||
580 | line6_send_raw_message(line6, buf, count); | ||
581 | return count; | ||
582 | } | ||
583 | #endif | ||
584 | |||
585 | /* | ||
586 | Generic destructor. | ||
587 | */ | ||
588 | static void line6_destruct(struct usb_interface *interface) | ||
589 | { | ||
590 | struct usb_line6 *line6; | ||
591 | if(interface == NULL) return; | ||
592 | line6 = usb_get_intfdata(interface); | ||
593 | if(line6 == NULL) return; | ||
594 | |||
595 | /* free buffer memory first: */ | ||
596 | if(line6->buffer_message != NULL) kfree(line6->buffer_message); | ||
597 | if(line6->buffer_listen != NULL) kfree(line6->buffer_listen); | ||
598 | |||
599 | /* then free URBs: */ | ||
600 | if(line6->urb_listen != NULL) usb_free_urb(line6->urb_listen); | ||
601 | |||
602 | /* make sure the device isn't destructed twice: */ | ||
603 | usb_set_intfdata(interface, NULL); | ||
604 | |||
605 | /* free interface data: */ | ||
606 | kfree(line6); | ||
607 | } | ||
608 | |||
609 | static void line6_list_devices(void) | ||
610 | { | ||
611 | int i; | ||
612 | |||
613 | for(i = 0; i < LINE6_MAX_DEVICES; ++i) { | ||
614 | struct usb_line6 *dev = line6_devices[i]; | ||
615 | printk(KERN_INFO "Line6 device %d: ", i); | ||
616 | |||
617 | if(dev == NULL) | ||
618 | printk("(not used)\n"); | ||
619 | else | ||
620 | printk("%s:%d\n", dev->properties->name, dev->interface_number); | ||
621 | } | ||
622 | } | ||
623 | |||
624 | /* | ||
625 | Probe USB device. | ||
626 | */ | ||
627 | static int line6_probe(struct usb_interface *interface, const struct usb_device_id *id) | ||
628 | { | ||
629 | int devtype; | ||
630 | struct usb_device *usbdev = 0; | ||
631 | struct usb_line6 *line6 = 0; | ||
632 | const struct line6_properties *properties; | ||
633 | int devnum; | ||
634 | int interface_number, alternate = 0; | ||
635 | int product; | ||
636 | int size = 0; | ||
637 | int ep_read = 0, ep_write = 0; | ||
638 | int ret; | ||
639 | |||
640 | if(interface == NULL) return -ENODEV; | ||
641 | usbdev = interface_to_usbdev(interface); | ||
642 | if(usbdev == NULL) return -ENODEV; | ||
643 | |||
644 | /* increment reference counters: */ | ||
645 | usb_get_intf(interface); | ||
646 | usb_get_dev(usbdev); | ||
647 | |||
648 | /* we don't handle multiple configurations */ | ||
649 | if(usbdev->descriptor.bNumConfigurations != 1) | ||
650 | return -ENODEV; | ||
651 | |||
652 | /* check vendor and product id */ | ||
653 | for(devtype = sizeof(line6_id_table) / sizeof(line6_id_table[0]) - 1; devtype--;) | ||
654 | if((le16_to_cpu(usbdev->descriptor.idVendor) == line6_id_table[devtype].idVendor) && | ||
655 | (le16_to_cpu(usbdev->descriptor.idProduct) == line6_id_table[devtype].idProduct)) | ||
656 | break; | ||
657 | |||
658 | if(devtype < 0) | ||
659 | return -ENODEV; | ||
660 | |||
661 | /* find free slot in device table: */ | ||
662 | for(devnum = 0; devnum < LINE6_MAX_DEVICES; ++devnum) | ||
663 | if(line6_devices[devnum] == NULL) | ||
664 | break; | ||
665 | |||
666 | if(devnum == LINE6_MAX_DEVICES) | ||
667 | return -ENODEV; | ||
668 | |||
669 | /* initialize device info: */ | ||
670 | properties = &line6_properties_table[devtype]; | ||
671 | dev_info(&interface->dev, "Line6 %s found\n", properties->name); | ||
672 | product = le16_to_cpu(usbdev->descriptor.idProduct); | ||
673 | |||
674 | /* query interface number */ | ||
675 | interface_number = interface->cur_altsetting->desc.bInterfaceNumber; | ||
676 | |||
677 | switch(product) { | ||
678 | case LINE6_DEVID_BASSPODXTLIVE: | ||
679 | case LINE6_DEVID_POCKETPOD: | ||
680 | case LINE6_DEVID_PODXTLIVE: | ||
681 | case LINE6_DEVID_VARIAX: | ||
682 | alternate = 1; | ||
683 | break; | ||
684 | |||
685 | case LINE6_DEVID_PODX3: | ||
686 | case LINE6_DEVID_PODX3LIVE: | ||
687 | switch(interface_number) { | ||
688 | case 0: alternate = 1; break; | ||
689 | case 1: alternate = 0; break; | ||
690 | default: MISSING_CASE; | ||
691 | } | ||
692 | break; | ||
693 | |||
694 | case LINE6_DEVID_BASSPODXT: | ||
695 | case LINE6_DEVID_BASSPODXTPRO: | ||
696 | case LINE6_DEVID_PODXT: | ||
697 | case LINE6_DEVID_PODXTPRO: | ||
698 | alternate = 5; | ||
699 | break; | ||
700 | |||
701 | case LINE6_DEVID_TONEPORT_GX: | ||
702 | case LINE6_DEVID_GUITARPORT: | ||
703 | alternate = 2; // 1..4 seem to be ok | ||
704 | break; | ||
705 | |||
706 | case LINE6_DEVID_TONEPORT_UX1: | ||
707 | case LINE6_DEVID_TONEPORT_UX2: | ||
708 | switch(interface_number) { | ||
709 | case 0: alternate = 2; break; /* defaults to 44.1kHz, 16-bit */ | ||
710 | case 1: alternate = 0; break; | ||
711 | default: MISSING_CASE; | ||
712 | } | ||
713 | break; | ||
714 | |||
715 | default: | ||
716 | MISSING_CASE; | ||
717 | return -ENODEV; | ||
718 | } | ||
719 | |||
720 | if((ret = usb_set_interface(usbdev, interface_number, alternate)) < 0) { | ||
721 | dev_err(&interface->dev, "set_interface failed\n"); | ||
722 | return ret; | ||
723 | } | ||
724 | |||
725 | /* initialize device data based on product id: */ | ||
726 | switch(product) { | ||
727 | case LINE6_DEVID_BASSPODXT: | ||
728 | case LINE6_DEVID_BASSPODXTLIVE: | ||
729 | case LINE6_DEVID_BASSPODXTPRO: | ||
730 | case LINE6_DEVID_POCKETPOD: | ||
731 | case LINE6_DEVID_PODXT: | ||
732 | case LINE6_DEVID_PODXTPRO: | ||
733 | size = sizeof(struct usb_line6_pod); | ||
734 | ep_read = 0x84; | ||
735 | ep_write = 0x03; | ||
736 | break; | ||
737 | |||
738 | case LINE6_DEVID_PODX3: | ||
739 | case LINE6_DEVID_PODX3LIVE: | ||
740 | /* currently unused! */ | ||
741 | size = sizeof(struct usb_line6_pod); | ||
742 | ep_read = 0x81; | ||
743 | ep_write = 0x01; | ||
744 | break; | ||
745 | |||
746 | case LINE6_DEVID_TONEPORT_GX: | ||
747 | case LINE6_DEVID_TONEPORT_UX1: | ||
748 | case LINE6_DEVID_TONEPORT_UX2: | ||
749 | case LINE6_DEVID_GUITARPORT: | ||
750 | size = sizeof(struct usb_line6_toneport); | ||
751 | /* these don't have a control channel */ | ||
752 | break; | ||
753 | |||
754 | case LINE6_DEVID_PODXTLIVE: | ||
755 | switch(interface_number) { | ||
756 | case PODXTLIVE_INTERFACE_POD: | ||
757 | size = sizeof(struct usb_line6_pod); | ||
758 | ep_read = 0x84; | ||
759 | ep_write = 0x03; | ||
760 | break; | ||
761 | |||
762 | case PODXTLIVE_INTERFACE_VARIAX: | ||
763 | size = sizeof(struct usb_line6_variax); | ||
764 | ep_read = 0x86; | ||
765 | ep_write = 0x05; | ||
766 | break; | ||
767 | |||
768 | default: | ||
769 | return -ENODEV; | ||
770 | } | ||
771 | break; | ||
772 | |||
773 | case LINE6_DEVID_VARIAX: | ||
774 | size = sizeof(struct usb_line6_variax); | ||
775 | ep_read = 0x82; | ||
776 | ep_write = 0x01; | ||
777 | break; | ||
778 | |||
779 | default: | ||
780 | MISSING_CASE; | ||
781 | return -ENODEV; | ||
782 | } | ||
783 | |||
784 | if(size == 0) { | ||
785 | dev_err(line6->ifcdev, "driver bug: interface data size not set\n"); | ||
786 | return -ENODEV; | ||
787 | } | ||
788 | |||
789 | line6 = kzalloc(size, GFP_KERNEL); | ||
790 | |||
791 | if(line6 == NULL) { | ||
792 | dev_err(&interface->dev, "Out of memory\n"); | ||
793 | return -ENOMEM; | ||
794 | } | ||
795 | |||
796 | /* store basic data: */ | ||
797 | line6->interface_number = interface_number; | ||
798 | line6->properties = properties; | ||
799 | line6->usbdev = usbdev; | ||
800 | line6->ifcdev = &interface->dev; | ||
801 | line6->ep_control_read = ep_read; | ||
802 | line6->ep_control_write = ep_write; | ||
803 | line6->product = product; | ||
804 | |||
805 | /* get data from endpoint descriptor (see usb_maxpacket): */ | ||
806 | { | ||
807 | struct usb_host_endpoint *ep; | ||
808 | unsigned epnum = usb_pipeendpoint(usb_rcvintpipe(usbdev, ep_read)); | ||
809 | ep = usbdev->ep_in[epnum]; | ||
810 | |||
811 | if(ep != NULL) { | ||
812 | line6->interval = ep->desc.bInterval; | ||
813 | line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); | ||
814 | } | ||
815 | else { | ||
816 | line6->interval = LINE6_FALLBACK_INTERVAL; | ||
817 | line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE; | ||
818 | dev_err(line6->ifcdev, "endpoint not available, using fallback values"); | ||
819 | } | ||
820 | } | ||
821 | |||
822 | usb_set_intfdata(interface, line6); | ||
823 | |||
824 | if(properties->capabilities & LINE6_BIT_CONTROL) { | ||
825 | /* initialize USB buffers: */ | ||
826 | line6->buffer_listen = kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); | ||
827 | |||
828 | if(line6->buffer_listen == NULL) { | ||
829 | dev_err(&interface->dev, "Out of memory\n"); | ||
830 | line6_destruct(interface); | ||
831 | return -ENOMEM; | ||
832 | } | ||
833 | |||
834 | line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL); | ||
835 | |||
836 | if(line6->buffer_message == NULL) { | ||
837 | dev_err(&interface->dev, "Out of memory\n"); | ||
838 | line6_destruct(interface); | ||
839 | return -ENOMEM; | ||
840 | } | ||
841 | |||
842 | line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL); | ||
843 | |||
844 | if(line6->urb_listen == NULL) { | ||
845 | dev_err(&interface->dev, "Out of memory\n"); | ||
846 | line6_destruct(interface); | ||
847 | return -ENOMEM; | ||
848 | } | ||
849 | |||
850 | if((ret = line6_start_listen(line6)) < 0) { | ||
851 | dev_err(&interface->dev, " line6_probe: usb_submit_urb failed\n"); | ||
852 | line6_destruct(interface); | ||
853 | return ret; | ||
854 | } | ||
855 | } | ||
856 | |||
857 | /* initialize device data based on product id: */ | ||
858 | switch(product) { | ||
859 | case LINE6_DEVID_BASSPODXT: | ||
860 | case LINE6_DEVID_BASSPODXTLIVE: | ||
861 | case LINE6_DEVID_BASSPODXTPRO: | ||
862 | case LINE6_DEVID_POCKETPOD: | ||
863 | case LINE6_DEVID_PODX3: | ||
864 | case LINE6_DEVID_PODX3LIVE: | ||
865 | case LINE6_DEVID_PODXT: | ||
866 | case LINE6_DEVID_PODXTPRO: | ||
867 | ret = pod_init(interface, (struct usb_line6_pod *)line6); | ||
868 | break; | ||
869 | |||
870 | case LINE6_DEVID_PODXTLIVE: | ||
871 | switch(interface_number) { | ||
872 | case PODXTLIVE_INTERFACE_POD: | ||
873 | ret = pod_init(interface, (struct usb_line6_pod *)line6); | ||
874 | break; | ||
875 | |||
876 | case PODXTLIVE_INTERFACE_VARIAX: | ||
877 | ret = variax_init(interface, (struct usb_line6_variax *)line6); | ||
878 | break; | ||
879 | |||
880 | default: | ||
881 | dev_err(&interface->dev, "PODxt Live interface %d not supported\n", interface_number); | ||
882 | ret = -ENODEV; | ||
883 | } | ||
884 | |||
885 | break; | ||
886 | |||
887 | case LINE6_DEVID_VARIAX: | ||
888 | ret = variax_init(interface, (struct usb_line6_variax *)line6); | ||
889 | break; | ||
890 | |||
891 | case LINE6_DEVID_TONEPORT_GX: | ||
892 | case LINE6_DEVID_TONEPORT_UX1: | ||
893 | case LINE6_DEVID_TONEPORT_UX2: | ||
894 | case LINE6_DEVID_GUITARPORT: | ||
895 | ret = toneport_init(interface, (struct usb_line6_toneport *)line6); | ||
896 | break; | ||
897 | |||
898 | default: | ||
899 | MISSING_CASE; | ||
900 | ret = -ENODEV; | ||
901 | } | ||
902 | |||
903 | if(ret < 0) { | ||
904 | line6_destruct(interface); | ||
905 | return ret; | ||
906 | } | ||
907 | |||
908 | if((ret = sysfs_create_link(&interface->dev.kobj, &usbdev->dev.kobj, "usb_device")) < 0) { | ||
909 | line6_destruct(interface); | ||
910 | return ret; | ||
911 | } | ||
912 | |||
913 | dev_info(&interface->dev, "Line6 %s now attached\n", line6->properties->name); | ||
914 | line6_devices[devnum] = line6; | ||
915 | line6_list_devices(); | ||
916 | return ret; | ||
917 | } | ||
918 | |||
919 | /* | ||
920 | Line6 device disconnected. | ||
921 | */ | ||
922 | static void line6_disconnect(struct usb_interface *interface) | ||
923 | { | ||
924 | struct usb_line6 *line6; | ||
925 | struct usb_device *usbdev; | ||
926 | int interface_number, i; | ||
927 | |||
928 | if(interface == NULL) return; | ||
929 | usbdev = interface_to_usbdev(interface); | ||
930 | if(usbdev == NULL) return; | ||
931 | |||
932 | sysfs_remove_link(&interface->dev.kobj, "usb_device"); | ||
933 | |||
934 | interface_number = interface->cur_altsetting->desc.bInterfaceNumber; | ||
935 | line6 = usb_get_intfdata(interface); | ||
936 | |||
937 | if(line6 != NULL) { | ||
938 | if(line6->urb_listen != NULL) usb_kill_urb(line6->urb_listen); | ||
939 | |||
940 | if(usbdev != line6->usbdev) | ||
941 | dev_err(line6->ifcdev, "driver bug: inconsistent usb device\n"); | ||
942 | |||
943 | switch(line6->usbdev->descriptor.idProduct) { | ||
944 | case LINE6_DEVID_BASSPODXT: | ||
945 | case LINE6_DEVID_BASSPODXTLIVE: | ||
946 | case LINE6_DEVID_BASSPODXTPRO: | ||
947 | case LINE6_DEVID_POCKETPOD: | ||
948 | case LINE6_DEVID_PODX3: | ||
949 | case LINE6_DEVID_PODX3LIVE: | ||
950 | case LINE6_DEVID_PODXT: | ||
951 | case LINE6_DEVID_PODXTPRO: | ||
952 | pod_disconnect(interface); | ||
953 | break; | ||
954 | |||
955 | case LINE6_DEVID_PODXTLIVE: | ||
956 | switch(interface_number) { | ||
957 | case PODXTLIVE_INTERFACE_POD: | ||
958 | pod_disconnect(interface); | ||
959 | break; | ||
960 | |||
961 | case PODXTLIVE_INTERFACE_VARIAX: | ||
962 | variax_disconnect(interface); | ||
963 | break; | ||
964 | } | ||
965 | |||
966 | break; | ||
967 | |||
968 | case LINE6_DEVID_VARIAX: | ||
969 | variax_disconnect(interface); | ||
970 | break; | ||
971 | |||
972 | case LINE6_DEVID_TONEPORT_GX: | ||
973 | case LINE6_DEVID_TONEPORT_UX1: | ||
974 | case LINE6_DEVID_TONEPORT_UX2: | ||
975 | case LINE6_DEVID_GUITARPORT: | ||
976 | toneport_disconnect(interface); | ||
977 | break; | ||
978 | |||
979 | default: | ||
980 | MISSING_CASE; | ||
981 | } | ||
982 | |||
983 | dev_info(&interface->dev, "Line6 %s now disconnected\n", line6->properties->name); | ||
984 | |||
985 | for(i = LINE6_MAX_DEVICES; i--;) | ||
986 | if(line6_devices[i] == line6) | ||
987 | line6_devices[i] = 0; | ||
988 | } | ||
989 | |||
990 | line6_destruct(interface); | ||
991 | |||
992 | /* decrement reference counters: */ | ||
993 | usb_put_intf(interface); | ||
994 | usb_put_dev(usbdev); | ||
995 | |||
996 | line6_list_devices(); | ||
997 | } | ||
998 | |||
999 | static struct usb_driver line6_driver = { | ||
1000 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) | ||
1001 | .owner = THIS_MODULE, | ||
1002 | #endif | ||
1003 | .name = DRIVER_NAME, | ||
1004 | .probe = line6_probe, | ||
1005 | .disconnect = line6_disconnect, | ||
1006 | .id_table = line6_id_table, | ||
1007 | }; | ||
1008 | |||
1009 | /* | ||
1010 | Module initialization. | ||
1011 | */ | ||
1012 | static int __init line6_init(void) | ||
1013 | { | ||
1014 | int i, retval; | ||
1015 | |||
1016 | printk("%s driver version %s%s\n", DRIVER_NAME, DRIVER_VERSION, DRIVER_REVISION); | ||
1017 | line6_workqueue = create_workqueue(DRIVER_NAME); | ||
1018 | |||
1019 | if(line6_workqueue == 0) { | ||
1020 | err("couldn't create workqueue"); | ||
1021 | return -EINVAL; | ||
1022 | } | ||
1023 | |||
1024 | for(i = LINE6_MAX_DEVICES; i--;) | ||
1025 | line6_devices[i] = 0; | ||
1026 | |||
1027 | retval = usb_register(&line6_driver); | ||
1028 | |||
1029 | if(retval) | ||
1030 | err("usb_register failed. Error number %d", retval); | ||
1031 | |||
1032 | return retval; | ||
1033 | } | ||
1034 | |||
1035 | /* | ||
1036 | Module cleanup. | ||
1037 | */ | ||
1038 | static void __exit line6_exit(void) | ||
1039 | { | ||
1040 | destroy_workqueue(line6_workqueue); | ||
1041 | usb_deregister(&line6_driver); | ||
1042 | } | ||
1043 | |||
1044 | module_init(line6_init); | ||
1045 | module_exit(line6_exit); | ||
1046 | |||
1047 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
1048 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
1049 | MODULE_LICENSE("GPL"); | ||
1050 | MODULE_VERSION(DRIVER_VERSION); | ||
diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h new file mode 100644 index 000000000000..e5179d99b9f6 --- /dev/null +++ b/drivers/staging/line6/driver.h | |||
@@ -0,0 +1,190 @@ | |||
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 | #ifndef DRIVER_H | ||
13 | #define DRIVER_H | ||
14 | |||
15 | |||
16 | #include "config.h" | ||
17 | |||
18 | #include <linux/spinlock.h> | ||
19 | #include <linux/usb.h> | ||
20 | #include <linux/wait.h> | ||
21 | |||
22 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) | ||
23 | #include <sound/driver.h> | ||
24 | #endif | ||
25 | |||
26 | #include <sound/core.h> | ||
27 | |||
28 | #include "midi.h" | ||
29 | |||
30 | |||
31 | #define DRIVER_NAME "line6usb" | ||
32 | |||
33 | #define LINE6_TIMEOUT 1 | ||
34 | #define LINE6_MAX_DEVICES 8 | ||
35 | #define LINE6_BUFSIZE_LISTEN 32 | ||
36 | #define LINE6_MESSAGE_MAXLEN 256 | ||
37 | |||
38 | |||
39 | /* | ||
40 | Line6 MIDI control commands | ||
41 | */ | ||
42 | #define LINE6_PARAM_CHANGE 0xb0 | ||
43 | #define LINE6_PROGRAM_CHANGE 0xc0 | ||
44 | #define LINE6_SYSEX_BEGIN 0xf0 | ||
45 | #define LINE6_SYSEX_END 0xf7 | ||
46 | #define LINE6_RESET 0xff | ||
47 | |||
48 | /* | ||
49 | MIDI channel for messages initiated by the host | ||
50 | (and eventually echoed back by the device) | ||
51 | */ | ||
52 | #define LINE6_CHANNEL_HOST 0x00 | ||
53 | |||
54 | /* | ||
55 | MIDI channel for messages initiated by the device | ||
56 | */ | ||
57 | #define LINE6_CHANNEL_DEVICE 0x02 | ||
58 | |||
59 | #define LINE6_CHANNEL_UNKNOWN 5 /* don't know yet what this is good for */ | ||
60 | |||
61 | #define LINE6_CHANNEL_MASK 0x0f | ||
62 | |||
63 | |||
64 | #define MISSING_CASE printk("line6usb driver bug: missing case in %s:%d\n", __FILE__, __LINE__) | ||
65 | |||
66 | |||
67 | #define CHECK_RETURN(x) if((err = x) < 0) return err | ||
68 | |||
69 | |||
70 | extern const unsigned char line6_midi_id[3]; | ||
71 | extern struct usb_line6 *line6_devices[LINE6_MAX_DEVICES]; | ||
72 | extern struct workqueue_struct *line6_workqueue; | ||
73 | |||
74 | static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3; | ||
75 | static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4; | ||
76 | |||
77 | |||
78 | /** | ||
79 | Common properties of Line6 devices. | ||
80 | */ | ||
81 | struct line6_properties { | ||
82 | const char *name; | ||
83 | int device_bit; | ||
84 | int capabilities; | ||
85 | }; | ||
86 | |||
87 | /** | ||
88 | Common data shared by all Line6 devices. | ||
89 | Corresponds to a pair of USB endpoints. | ||
90 | */ | ||
91 | struct usb_line6 { | ||
92 | /** | ||
93 | USB device. | ||
94 | */ | ||
95 | struct usb_device *usbdev; | ||
96 | |||
97 | /** | ||
98 | Product id. | ||
99 | */ | ||
100 | int product; | ||
101 | |||
102 | /** | ||
103 | Properties. | ||
104 | */ | ||
105 | const struct line6_properties *properties; | ||
106 | |||
107 | /** | ||
108 | Interface number. | ||
109 | */ | ||
110 | int interface_number; | ||
111 | |||
112 | /** | ||
113 | Interval (ms). | ||
114 | */ | ||
115 | int interval; | ||
116 | |||
117 | /** | ||
118 | Maximum size of USB packet. | ||
119 | */ | ||
120 | int max_packet_size; | ||
121 | |||
122 | /** | ||
123 | Device representing the USB interface. | ||
124 | */ | ||
125 | struct device *ifcdev; | ||
126 | |||
127 | /** | ||
128 | Line6 sound card data structure. | ||
129 | Each device has at least MIDI or PCM. | ||
130 | */ | ||
131 | struct snd_card *card; | ||
132 | |||
133 | /** | ||
134 | Line6 PCM device data structure. | ||
135 | */ | ||
136 | struct snd_line6_pcm *line6pcm; | ||
137 | |||
138 | /** | ||
139 | Line6 MIDI device data structure. | ||
140 | */ | ||
141 | struct snd_line6_midi *line6midi; | ||
142 | |||
143 | /** | ||
144 | USB endpoint for listening to control commands. | ||
145 | */ | ||
146 | int ep_control_read; | ||
147 | |||
148 | /** | ||
149 | USB endpoint for writing control commands. | ||
150 | */ | ||
151 | int ep_control_write; | ||
152 | |||
153 | /** | ||
154 | URB for listening to PODxt Pro control endpoint. | ||
155 | */ | ||
156 | struct urb *urb_listen; | ||
157 | |||
158 | /** | ||
159 | Buffer for listening to PODxt Pro control endpoint. | ||
160 | */ | ||
161 | unsigned char *buffer_listen; | ||
162 | |||
163 | /** | ||
164 | Buffer for message to be processed. | ||
165 | */ | ||
166 | unsigned char *buffer_message; | ||
167 | |||
168 | /** | ||
169 | Length of message to be processed. | ||
170 | */ | ||
171 | int message_length; | ||
172 | }; | ||
173 | |||
174 | |||
175 | extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, int size); | ||
176 | extern ssize_t line6_nop_read(struct device *dev, DEVICE_ATTRIBUTE char *buf); | ||
177 | extern ssize_t line6_nop_write(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count); | ||
178 | extern int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t datalen); | ||
179 | extern int line6_read_serial_number(struct usb_line6 *line6, int *serial_number); | ||
180 | extern int line6_send_program(struct usb_line6 *line6, int value); | ||
181 | extern int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, int size); | ||
182 | extern int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, int size); | ||
183 | extern int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, int size); | ||
184 | extern ssize_t line6_set_raw(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count); | ||
185 | extern int line6_transmit_parameter(struct usb_line6 *line6, int param, int value); | ||
186 | extern int line6_write_data(struct usb_line6 *line6, int address, void *data, size_t datalen); | ||
187 | extern void line6_write_hexdump(struct usb_line6 *line6, char dir, const unsigned char *buffer, int size); | ||
188 | |||
189 | |||
190 | #endif | ||
diff --git a/drivers/staging/line6/dumprequest.c b/drivers/staging/line6/dumprequest.c new file mode 100644 index 000000000000..89bc0994d07b --- /dev/null +++ b/drivers/staging/line6/dumprequest.c | |||
@@ -0,0 +1,143 @@ | |||
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 | #include "dumprequest.h" | ||
14 | |||
15 | |||
16 | /* | ||
17 | Set "dump in progress" flag. | ||
18 | */ | ||
19 | void line6_dump_started(struct line6_dump_request *l6dr, int dest) | ||
20 | { | ||
21 | l6dr->in_progress = dest; | ||
22 | } | ||
23 | |||
24 | /* | ||
25 | Invalidate current channel, i.e., set "dump in progress" flag. | ||
26 | Reading from the "dump" special file blocks until dump is completed. | ||
27 | */ | ||
28 | void line6_invalidate_current(struct line6_dump_request *l6dr) | ||
29 | { | ||
30 | line6_dump_started(l6dr, LINE6_DUMP_CURRENT); | ||
31 | } | ||
32 | |||
33 | /* | ||
34 | Clear "dump in progress" flag and notify waiting processes. | ||
35 | */ | ||
36 | void line6_dump_finished(struct line6_dump_request *l6dr) | ||
37 | { | ||
38 | l6dr->in_progress = LINE6_DUMP_NONE; | ||
39 | wake_up_interruptible(&l6dr->wait); | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | Send an asynchronous channel dump request. | ||
44 | */ | ||
45 | int line6_dump_request_async(struct line6_dump_request *l6dr, struct usb_line6 *line6, int num) | ||
46 | { | ||
47 | int ret; | ||
48 | line6_invalidate_current(l6dr); | ||
49 | ret = line6_send_raw_message_async(line6, l6dr->reqbufs[num].buffer, l6dr->reqbufs[num].length); | ||
50 | |||
51 | if(ret < 0) | ||
52 | line6_dump_finished(l6dr); | ||
53 | |||
54 | return ret; | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | Send an asynchronous dump request after a given interval. | ||
59 | */ | ||
60 | void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds, | ||
61 | void (*function)(unsigned long), void *data) | ||
62 | { | ||
63 | l6dr->timer.expires = jiffies + seconds * HZ; | ||
64 | l6dr->timer.function = function; | ||
65 | l6dr->timer.data = (unsigned long)data; | ||
66 | add_timer(&l6dr->timer); | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | Wait for completion. | ||
71 | */ | ||
72 | int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock) | ||
73 | { | ||
74 | int retval = 0; | ||
75 | DECLARE_WAITQUEUE(wait, current); | ||
76 | add_wait_queue(&l6dr->wait, &wait); | ||
77 | current->state = TASK_INTERRUPTIBLE; | ||
78 | |||
79 | while(l6dr->in_progress) { | ||
80 | if(nonblock) { | ||
81 | retval = -EAGAIN; | ||
82 | break; | ||
83 | } | ||
84 | |||
85 | if(signal_pending(current)) { | ||
86 | retval = -ERESTARTSYS; | ||
87 | break; | ||
88 | } | ||
89 | else | ||
90 | schedule(); | ||
91 | } | ||
92 | |||
93 | current->state = TASK_RUNNING; | ||
94 | remove_wait_queue(&l6dr->wait, &wait); | ||
95 | return retval; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | Initialize dump request buffer. | ||
100 | */ | ||
101 | int line6_dumpreq_initbuf(struct line6_dump_request *l6dr, const void *buf, size_t len, int num) | ||
102 | { | ||
103 | l6dr->reqbufs[num].buffer = kmalloc(len, GFP_KERNEL); | ||
104 | if(l6dr->reqbufs[num].buffer == NULL) return -ENOMEM; | ||
105 | memcpy(l6dr->reqbufs[num].buffer, buf, len); | ||
106 | l6dr->reqbufs[num].length = len; | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | Initialize dump request data structure (including one buffer). | ||
112 | */ | ||
113 | int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf, size_t len) | ||
114 | { | ||
115 | int ret; | ||
116 | ret = line6_dumpreq_initbuf(l6dr, buf, len, 0); | ||
117 | if(ret < 0) return ret; | ||
118 | init_waitqueue_head(&l6dr->wait); | ||
119 | init_timer(&l6dr->timer); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | Destruct dump request data structure. | ||
125 | */ | ||
126 | void line6_dumpreq_destructbuf(struct line6_dump_request *l6dr, int num) | ||
127 | { | ||
128 | if(l6dr == NULL) return; | ||
129 | if(l6dr->reqbufs[num].buffer == NULL) return; | ||
130 | kfree(l6dr->reqbufs[num].buffer); | ||
131 | l6dr->reqbufs[num].buffer = NULL; | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | Destruct dump request data structure. | ||
136 | */ | ||
137 | void line6_dumpreq_destruct(struct line6_dump_request *l6dr) | ||
138 | { | ||
139 | if(l6dr->reqbufs[0].buffer == NULL) return; | ||
140 | line6_dumpreq_destructbuf(l6dr, 0); | ||
141 | l6dr->ok = 1; | ||
142 | del_timer_sync(&l6dr->timer); | ||
143 | } | ||
diff --git a/drivers/staging/line6/dumprequest.h b/drivers/staging/line6/dumprequest.h new file mode 100644 index 000000000000..e0b38bbc1ad2 --- /dev/null +++ b/drivers/staging/line6/dumprequest.h | |||
@@ -0,0 +1,87 @@ | |||
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 | #ifndef DUMPREQUEST_H | ||
13 | #define DUMPREQUEST_H | ||
14 | |||
15 | |||
16 | #include <linux/usb.h> | ||
17 | #include <linux/wait.h> | ||
18 | |||
19 | #include <sound/core.h> | ||
20 | |||
21 | |||
22 | enum { | ||
23 | LINE6_DUMP_NONE, | ||
24 | LINE6_DUMP_CURRENT | ||
25 | }; | ||
26 | |||
27 | |||
28 | struct line6_dump_reqbuf { | ||
29 | /** | ||
30 | Buffer for dump requests. | ||
31 | */ | ||
32 | unsigned char *buffer; | ||
33 | |||
34 | /** | ||
35 | Size of dump request. | ||
36 | */ | ||
37 | size_t length; | ||
38 | }; | ||
39 | |||
40 | /** | ||
41 | Provides the functionality to request channel/model/... dump data from a | ||
42 | Line6 device. | ||
43 | */ | ||
44 | struct line6_dump_request { | ||
45 | /** | ||
46 | Wait queue for access to program dump data. | ||
47 | */ | ||
48 | wait_queue_head_t wait; | ||
49 | |||
50 | /** | ||
51 | Indicates an unfinished program dump request. | ||
52 | 0: no dump | ||
53 | 1: dump current settings | ||
54 | Other device-specific values are also allowed. | ||
55 | */ | ||
56 | int in_progress; | ||
57 | |||
58 | /** | ||
59 | Timer for delayed dump request. | ||
60 | */ | ||
61 | struct timer_list timer; | ||
62 | |||
63 | /** | ||
64 | Flag if initial dump request has been successful. | ||
65 | */ | ||
66 | char ok; | ||
67 | |||
68 | /** | ||
69 | Dump request buffers | ||
70 | */ | ||
71 | struct line6_dump_reqbuf reqbufs[1]; | ||
72 | }; | ||
73 | |||
74 | extern void line6_dump_finished(struct line6_dump_request *l6dr); | ||
75 | extern int line6_dump_request_async(struct line6_dump_request *l6dr, struct usb_line6 *line6, int num); | ||
76 | extern void line6_dump_started(struct line6_dump_request *l6dr, int dest); | ||
77 | extern void line6_dumpreq_destruct(struct line6_dump_request *l6dr); | ||
78 | extern void line6_dumpreq_destructbuf(struct line6_dump_request *l6dr, int num); | ||
79 | extern int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf, size_t len); | ||
80 | extern int line6_dumpreq_initbuf(struct line6_dump_request *l6dr, const void *buf, size_t len, int num); | ||
81 | extern void line6_invalidate_current(struct line6_dump_request *l6dr); | ||
82 | extern void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds, | ||
83 | void (*function)(unsigned long), void *data); | ||
84 | extern int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock); | ||
85 | |||
86 | |||
87 | #endif | ||
diff --git a/drivers/staging/line6/midi.c b/drivers/staging/line6/midi.c new file mode 100644 index 000000000000..74489ee77bff --- /dev/null +++ b/drivers/staging/line6/midi.c | |||
@@ -0,0 +1,398 @@ | |||
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 | } | ||
diff --git a/drivers/staging/line6/midi.h b/drivers/staging/line6/midi.h new file mode 100644 index 000000000000..be05a54205c4 --- /dev/null +++ b/drivers/staging/line6/midi.h | |||
@@ -0,0 +1,87 @@ | |||
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 | #ifndef MIDI_H | ||
13 | #define MIDI_H | ||
14 | |||
15 | |||
16 | #include <sound/rawmidi.h> | ||
17 | |||
18 | #include "midibuf.h" | ||
19 | |||
20 | |||
21 | #define MIDI_BUFFER_SIZE 1024 | ||
22 | |||
23 | |||
24 | struct snd_line6_midi | ||
25 | { | ||
26 | /** | ||
27 | Pointer back to the Line6 driver data structure. | ||
28 | */ | ||
29 | struct usb_line6 *line6; | ||
30 | |||
31 | /** | ||
32 | MIDI substream for receiving (or NULL if not active). | ||
33 | */ | ||
34 | struct snd_rawmidi_substream *substream_receive; | ||
35 | |||
36 | /** | ||
37 | MIDI substream for transmitting (or NULL if not active). | ||
38 | */ | ||
39 | struct snd_rawmidi_substream *substream_transmit; | ||
40 | |||
41 | /** | ||
42 | Number of currently active MIDI send URBs. | ||
43 | */ | ||
44 | int num_active_send_urbs; | ||
45 | |||
46 | /** | ||
47 | Spin lock to protect updates of send_urb. | ||
48 | */ | ||
49 | spinlock_t send_urb_lock; | ||
50 | |||
51 | /** | ||
52 | Spin lock to protect MIDI buffer handling. | ||
53 | */ | ||
54 | spinlock_t midi_transmit_lock; | ||
55 | |||
56 | /** | ||
57 | Wait queue for MIDI transmission. | ||
58 | */ | ||
59 | wait_queue_head_t send_wait; | ||
60 | |||
61 | /** | ||
62 | Bit mask for output MIDI channels. | ||
63 | */ | ||
64 | int midi_mask_transmit; | ||
65 | |||
66 | /** | ||
67 | Bit mask for input MIDI channels. | ||
68 | */ | ||
69 | int midi_mask_receive; | ||
70 | |||
71 | /** | ||
72 | Buffer for incoming MIDI stream. | ||
73 | */ | ||
74 | struct MidiBuffer midibuf_in; | ||
75 | |||
76 | /** | ||
77 | Buffer for outgoing MIDI stream. | ||
78 | */ | ||
79 | struct MidiBuffer midibuf_out; | ||
80 | }; | ||
81 | |||
82 | |||
83 | extern int line6_init_midi(struct usb_line6 *line6); | ||
84 | extern void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, int length); | ||
85 | |||
86 | |||
87 | #endif | ||
diff --git a/drivers/staging/line6/midibuf.c b/drivers/staging/line6/midibuf.c new file mode 100644 index 000000000000..2f86c6692516 --- /dev/null +++ b/drivers/staging/line6/midibuf.c | |||
@@ -0,0 +1,268 @@ | |||
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 "config.h" | ||
13 | |||
14 | #include <linux/slab.h> | ||
15 | |||
16 | #include "midibuf.h" | ||
17 | |||
18 | |||
19 | int midibuf_message_length(unsigned char code) | ||
20 | { | ||
21 | if(code < 0x80) | ||
22 | return -1; | ||
23 | else if(code < 0xf0) { | ||
24 | static const int length[] = { 3, 3, 3, 3, 2, 2, 3 }; | ||
25 | return length[(code >> 4) - 8]; | ||
26 | } | ||
27 | else { | ||
28 | /* | ||
29 | Note that according to the MIDI specification 0xf2 is the "Song Position | ||
30 | Pointer", but this is used by Line6 to send sysex messages to the host. | ||
31 | */ | ||
32 | static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1 }; | ||
33 | return length[code & 0x0f]; | ||
34 | } | ||
35 | } | ||
36 | |||
37 | void midibuf_reset(struct MidiBuffer *this) | ||
38 | { | ||
39 | this->pos_read = this->pos_write = this->full = 0; | ||
40 | this->command_prev = -1; | ||
41 | } | ||
42 | |||
43 | int midibuf_init(struct MidiBuffer *this, int size, int split) | ||
44 | { | ||
45 | this->buf = (unsigned char *)kmalloc(size, GFP_KERNEL); | ||
46 | |||
47 | if(this->buf == 0) | ||
48 | return -ENOMEM; | ||
49 | |||
50 | this->size = size; | ||
51 | this->split = split; | ||
52 | midibuf_reset(this); | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | void midibuf_status(struct MidiBuffer *this) | ||
57 | { | ||
58 | printk("midibuf size=%d split=%d pos_read=%d pos_write=%d full=%d command_prev=%02x\n", | ||
59 | this->size, this->split, this->pos_read, this->pos_write, this->full, this->command_prev); | ||
60 | } | ||
61 | |||
62 | int midibuf_is_empty(struct MidiBuffer *this) | ||
63 | { | ||
64 | return (this->pos_read == this->pos_write) && !this->full; | ||
65 | } | ||
66 | |||
67 | int midibuf_is_full(struct MidiBuffer *this) | ||
68 | { | ||
69 | return this->full; | ||
70 | } | ||
71 | |||
72 | int midibuf_bytes_free(struct MidiBuffer *this) | ||
73 | { | ||
74 | return | ||
75 | midibuf_is_full(this) ? | ||
76 | 0 : | ||
77 | (this->pos_read - this->pos_write + this->size - 1) % this->size + 1; | ||
78 | } | ||
79 | |||
80 | int midibuf_bytes_used(struct MidiBuffer *this) | ||
81 | { | ||
82 | return | ||
83 | midibuf_is_empty(this) ? | ||
84 | 0 : | ||
85 | (this->pos_write - this->pos_read + this->size - 1) % this->size + 1; | ||
86 | } | ||
87 | |||
88 | int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length) | ||
89 | { | ||
90 | int bytes_free; | ||
91 | int length1, length2; | ||
92 | int skip_active_sense = 0; | ||
93 | |||
94 | if(midibuf_is_full(this) || (length <= 0)) | ||
95 | return 0; | ||
96 | |||
97 | /* skip trailing active sense */ | ||
98 | if(data[length - 1] == 0xfe) { | ||
99 | --length; | ||
100 | skip_active_sense = 1; | ||
101 | } | ||
102 | |||
103 | bytes_free = midibuf_bytes_free(this); | ||
104 | |||
105 | if(length > bytes_free) | ||
106 | length = bytes_free; | ||
107 | |||
108 | if(length > 0) { | ||
109 | length1 = this->size - this->pos_write; | ||
110 | |||
111 | if(length < length1) { | ||
112 | /* no buffer wraparound */ | ||
113 | memcpy(this->buf + this->pos_write, data, length); | ||
114 | this->pos_write += length; | ||
115 | } | ||
116 | else { | ||
117 | /* buffer wraparound */ | ||
118 | length2 = length - length1; | ||
119 | memcpy(this->buf + this->pos_write, data, length1); | ||
120 | memcpy(this->buf, data + length1, length2); | ||
121 | this->pos_write = length2; | ||
122 | } | ||
123 | |||
124 | if(this->pos_write == this->pos_read) | ||
125 | this->full = 1; | ||
126 | } | ||
127 | |||
128 | return length + skip_active_sense; | ||
129 | } | ||
130 | |||
131 | int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) | ||
132 | { | ||
133 | int bytes_used; | ||
134 | int length1, length2; | ||
135 | int command; | ||
136 | int midi_length; | ||
137 | int repeat = 0; | ||
138 | int i; | ||
139 | |||
140 | if(length < 3) | ||
141 | return -EINVAL; /* we need to be able to store at least a 3 byte MIDI message */ | ||
142 | |||
143 | if(midibuf_is_empty(this)) | ||
144 | return 0; | ||
145 | |||
146 | bytes_used = midibuf_bytes_used(this); | ||
147 | |||
148 | if(length > bytes_used) | ||
149 | length = bytes_used; | ||
150 | |||
151 | length1 = this->size - this->pos_read; | ||
152 | |||
153 | /* check MIDI command length */ | ||
154 | command = this->buf[this->pos_read]; | ||
155 | |||
156 | if(command & 0x80) { | ||
157 | midi_length = midibuf_message_length(command); | ||
158 | this->command_prev = command; | ||
159 | } | ||
160 | else { | ||
161 | if(this->command_prev > 0) { | ||
162 | int midi_length_prev = midibuf_message_length(this->command_prev); | ||
163 | |||
164 | if(midi_length_prev > 0) { | ||
165 | midi_length = midi_length_prev - 1; | ||
166 | repeat = 1; | ||
167 | } | ||
168 | else | ||
169 | midi_length = -1; | ||
170 | } | ||
171 | else | ||
172 | midi_length = -1; | ||
173 | } | ||
174 | |||
175 | if(midi_length < 0) { | ||
176 | /* search for end of message */ | ||
177 | if(length < length1) { | ||
178 | /* no buffer wraparound */ | ||
179 | for(i = 1; i < length; ++i) | ||
180 | if(this->buf[this->pos_read + i] & 0x80) | ||
181 | break; | ||
182 | |||
183 | midi_length = i; | ||
184 | } | ||
185 | else { | ||
186 | /* buffer wraparound */ | ||
187 | length2 = length - length1; | ||
188 | |||
189 | for(i = 1; i < length1; ++i) | ||
190 | if(this->buf[this->pos_read + i] & 0x80) | ||
191 | break; | ||
192 | |||
193 | if(i < length1) | ||
194 | midi_length = i; | ||
195 | else { | ||
196 | for(i = 0; i < length2; ++i) | ||
197 | if(this->buf[i] & 0x80) | ||
198 | break; | ||
199 | |||
200 | midi_length = length1 + i; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | if(midi_length == length) | ||
205 | midi_length = -1; /* end of message not found */ | ||
206 | } | ||
207 | |||
208 | if(midi_length < 0) { | ||
209 | if(!this->split) | ||
210 | return 0; /* command is not yet complete */ | ||
211 | } | ||
212 | else { | ||
213 | if(length < midi_length) | ||
214 | return 0; /* command is not yet complete */ | ||
215 | |||
216 | length = midi_length; | ||
217 | } | ||
218 | |||
219 | if(length < length1) { | ||
220 | /* no buffer wraparound */ | ||
221 | memcpy(data + repeat, this->buf + this->pos_read, length); | ||
222 | this->pos_read += length; | ||
223 | } | ||
224 | else { | ||
225 | /* buffer wraparound */ | ||
226 | length2 = length - length1; | ||
227 | memcpy(data + repeat, this->buf + this->pos_read, length1); | ||
228 | memcpy(data + repeat + length1, this->buf, length2); | ||
229 | this->pos_read = length2; | ||
230 | } | ||
231 | |||
232 | if(repeat) | ||
233 | data[0] = this->command_prev; | ||
234 | |||
235 | this->full = 0; | ||
236 | return length + repeat; | ||
237 | } | ||
238 | |||
239 | int midibuf_ignore(struct MidiBuffer *this, int length) | ||
240 | { | ||
241 | int bytes_used = midibuf_bytes_used(this); | ||
242 | |||
243 | if(length > bytes_used) | ||
244 | length = bytes_used; | ||
245 | |||
246 | this->pos_read = (this->pos_read + length) % this->size; | ||
247 | this->full = 0; | ||
248 | return length; | ||
249 | } | ||
250 | |||
251 | int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask) | ||
252 | { | ||
253 | int cmd = this->command_prev; | ||
254 | |||
255 | if((cmd >= 0x80) && (cmd < 0xf0)) | ||
256 | if((mask & (1 << (cmd & 0x0f))) == 0) | ||
257 | return 1; | ||
258 | |||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | void midibuf_destroy(struct MidiBuffer *this) | ||
263 | { | ||
264 | if(this->buf != 0) { | ||
265 | kfree(this->buf); | ||
266 | this->buf = 0; | ||
267 | } | ||
268 | } | ||
diff --git a/drivers/staging/line6/midibuf.h b/drivers/staging/line6/midibuf.h new file mode 100644 index 000000000000..0e7762c677c4 --- /dev/null +++ b/drivers/staging/line6/midibuf.h | |||
@@ -0,0 +1,39 @@ | |||
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 | #ifndef MIDIBUF_H | ||
13 | #define MIDIBUF_H | ||
14 | |||
15 | |||
16 | struct MidiBuffer | ||
17 | { | ||
18 | unsigned char *buf; | ||
19 | int size; | ||
20 | int split; | ||
21 | int pos_read, pos_write; | ||
22 | int full; | ||
23 | int command_prev; | ||
24 | }; | ||
25 | |||
26 | |||
27 | extern int midibuf_bytes_used(struct MidiBuffer *mb); | ||
28 | extern int midibuf_bytes_free(struct MidiBuffer *mb); | ||
29 | extern void midibuf_destroy(struct MidiBuffer *mb); | ||
30 | extern int midibuf_ignore(struct MidiBuffer *mb, int length); | ||
31 | extern int midibuf_init(struct MidiBuffer *mb, int size, int split); | ||
32 | extern int midibuf_read(struct MidiBuffer *mb, unsigned char *data, int length); | ||
33 | extern void midibuf_reset(struct MidiBuffer *mb); | ||
34 | extern int midibuf_skip_message(struct MidiBuffer *mb, unsigned short mask); | ||
35 | extern void midibuf_status(struct MidiBuffer *mb); | ||
36 | extern int midibuf_write(struct MidiBuffer *mb, unsigned char *data, int length); | ||
37 | |||
38 | |||
39 | #endif | ||
diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c new file mode 100644 index 000000000000..725184b2f308 --- /dev/null +++ b/drivers/staging/line6/pcm.c | |||
@@ -0,0 +1,289 @@ | |||
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 <sound/core.h> | ||
15 | #include <sound/control.h> | ||
16 | #include <sound/pcm.h> | ||
17 | #include <sound/pcm_params.h> | ||
18 | |||
19 | #include "audio.h" | ||
20 | #include "capture.h" | ||
21 | #include "playback.h" | ||
22 | #include "pod.h" | ||
23 | |||
24 | |||
25 | /* trigger callback */ | ||
26 | int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) | ||
27 | { | ||
28 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
29 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) | ||
30 | struct list_head *pos; | ||
31 | #endif | ||
32 | struct snd_pcm_substream *s; | ||
33 | int err; | ||
34 | unsigned long flags; | ||
35 | |||
36 | spin_lock_irqsave(&line6pcm->lock_trigger, flags); | ||
37 | clear_bit(BIT_PREPARED, &line6pcm->flags); | ||
38 | |||
39 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) | ||
40 | snd_pcm_group_for_each(pos, substream) { | ||
41 | s = snd_pcm_group_substream_entry(pos); | ||
42 | #else | ||
43 | snd_pcm_group_for_each_entry(s, substream) { | ||
44 | #endif | ||
45 | switch(s->stream) { | ||
46 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
47 | err = snd_line6_playback_trigger(s, cmd); | ||
48 | |||
49 | if(err < 0) { | ||
50 | spin_unlock_irqrestore(&line6pcm->lock_trigger, flags); | ||
51 | return err; | ||
52 | } | ||
53 | |||
54 | break; | ||
55 | |||
56 | case SNDRV_PCM_STREAM_CAPTURE: | ||
57 | err = snd_line6_capture_trigger(s, cmd); | ||
58 | |||
59 | if(err < 0) { | ||
60 | spin_unlock_irqrestore(&line6pcm->lock_trigger, flags); | ||
61 | return err; | ||
62 | } | ||
63 | |||
64 | break; | ||
65 | |||
66 | default: | ||
67 | dev_err(s2m(substream), "Unknown stream direction %d\n", s->stream); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | spin_unlock_irqrestore(&line6pcm->lock_trigger, flags); | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | /* control info callback */ | ||
76 | static int snd_line6_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { | ||
77 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
78 | uinfo->count = 2; | ||
79 | uinfo->value.integer.min = 0; | ||
80 | uinfo->value.integer.max = 256; | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | /* control get callback */ | ||
85 | static int snd_line6_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { | ||
86 | int i; | ||
87 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
88 | |||
89 | for(i = 2; i--;) | ||
90 | ucontrol->value.integer.value[i] = line6pcm->volume[i]; | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | /* control put callback */ | ||
96 | static int snd_line6_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { | ||
97 | int i, changed = 0; | ||
98 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
99 | |||
100 | for(i = 2; i--;) | ||
101 | if(line6pcm->volume[i] != ucontrol->value.integer.value[i]) { | ||
102 | line6pcm->volume[i] = ucontrol->value.integer.value[i]; | ||
103 | changed = 1; | ||
104 | } | ||
105 | |||
106 | return changed; | ||
107 | } | ||
108 | |||
109 | /* control definition */ | ||
110 | static struct snd_kcontrol_new line6_control = { | ||
111 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
112 | .name = "PCM Playback Volume", | ||
113 | .index = 0, | ||
114 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
115 | .info = snd_line6_control_info, | ||
116 | .get = snd_line6_control_get, | ||
117 | .put = snd_line6_control_put | ||
118 | }; | ||
119 | |||
120 | /* | ||
121 | Cleanup the PCM device. | ||
122 | */ | ||
123 | static void line6_cleanup_pcm(struct snd_pcm *pcm) | ||
124 | { | ||
125 | int i; | ||
126 | struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); | ||
127 | |||
128 | for(i = LINE6_ISO_BUFFERS; i--;) { | ||
129 | if(line6pcm->urb_audio_out[i]) { | ||
130 | usb_kill_urb(line6pcm->urb_audio_out[i]); | ||
131 | usb_free_urb(line6pcm->urb_audio_out[i]); | ||
132 | } | ||
133 | if(line6pcm->urb_audio_in[i]) { | ||
134 | usb_kill_urb(line6pcm->urb_audio_in[i]); | ||
135 | usb_free_urb(line6pcm->urb_audio_in[i]); | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | |||
140 | /* create a PCM device */ | ||
141 | static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm) | ||
142 | { | ||
143 | struct snd_pcm *pcm; | ||
144 | int err; | ||
145 | |||
146 | if((err = snd_pcm_new(line6pcm->line6->card, (char *)line6pcm->line6->properties->name, 0, 1, 1, &pcm)) < 0) | ||
147 | return err; | ||
148 | |||
149 | pcm->private_data = line6pcm; | ||
150 | pcm->private_free = line6_cleanup_pcm; | ||
151 | line6pcm->pcm = pcm; | ||
152 | strcpy(pcm->name, line6pcm->line6->properties->name); | ||
153 | |||
154 | /* set operators */ | ||
155 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_line6_playback_ops); | ||
156 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); | ||
157 | |||
158 | /* pre-allocation of buffers */ | ||
159 | snd_pcm_lib_preallocate_pages_for_all(pcm, | ||
160 | SNDRV_DMA_TYPE_CONTINUOUS, | ||
161 | snd_dma_continuous_data(GFP_KERNEL), | ||
162 | 64 * 1024, 128 * 1024); | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | /* PCM device destructor */ | ||
168 | static int snd_line6_pcm_free(struct snd_device *device) | ||
169 | { | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | /* | ||
174 | Create and register the PCM device and mixer entries. | ||
175 | Create URBs for playback and capture. | ||
176 | */ | ||
177 | int line6_init_pcm(struct usb_line6 *line6, struct line6_pcm_properties *properties) | ||
178 | { | ||
179 | static struct snd_device_ops pcm_ops = { | ||
180 | .dev_free = snd_line6_pcm_free, | ||
181 | }; | ||
182 | |||
183 | int err; | ||
184 | int ep_read = 0, ep_write = 0; | ||
185 | struct snd_line6_pcm *line6pcm; | ||
186 | |||
187 | if(!(line6->properties->capabilities & LINE6_BIT_PCM)) | ||
188 | return 0; /* skip PCM initialization and report success */ | ||
189 | |||
190 | /* initialize PCM subsystem based on product id: */ | ||
191 | switch(line6->product) { | ||
192 | case LINE6_DEVID_BASSPODXT: | ||
193 | case LINE6_DEVID_BASSPODXTLIVE: | ||
194 | case LINE6_DEVID_BASSPODXTPRO: | ||
195 | case LINE6_DEVID_PODXT: | ||
196 | case LINE6_DEVID_PODXTLIVE: | ||
197 | case LINE6_DEVID_PODXTPRO: | ||
198 | ep_read = 0x82; | ||
199 | ep_write = 0x01; | ||
200 | break; | ||
201 | |||
202 | case LINE6_DEVID_PODX3: | ||
203 | case LINE6_DEVID_PODX3LIVE: | ||
204 | ep_read = 0x86; | ||
205 | ep_write = 0x02; | ||
206 | break; | ||
207 | |||
208 | case LINE6_DEVID_POCKETPOD: | ||
209 | ep_read = 0x82; | ||
210 | ep_write = 0x02; | ||
211 | break; | ||
212 | |||
213 | case LINE6_DEVID_GUITARPORT: | ||
214 | case LINE6_DEVID_TONEPORT_GX: | ||
215 | ep_read = 0x82; | ||
216 | ep_write = 0x01; | ||
217 | break; | ||
218 | |||
219 | case LINE6_DEVID_TONEPORT_UX1: | ||
220 | ep_read = 0x00; | ||
221 | ep_write = 0x00; | ||
222 | break; | ||
223 | |||
224 | case LINE6_DEVID_TONEPORT_UX2: | ||
225 | ep_read = 0x87; | ||
226 | ep_write = 0x00; | ||
227 | break; | ||
228 | |||
229 | default: | ||
230 | MISSING_CASE; | ||
231 | } | ||
232 | |||
233 | line6pcm = kzalloc(sizeof(struct snd_line6_pcm), GFP_KERNEL); | ||
234 | |||
235 | if(line6pcm == NULL) | ||
236 | return -ENOMEM; | ||
237 | |||
238 | line6pcm->volume[0] = line6pcm->volume[1] = 128; | ||
239 | line6pcm->line6 = line6; | ||
240 | line6pcm->ep_audio_read = ep_read; | ||
241 | line6pcm->ep_audio_write = ep_write; | ||
242 | line6pcm->max_packet_size = usb_maxpacket(line6->usbdev, usb_rcvintpipe(line6->usbdev, ep_read), 0); | ||
243 | line6pcm->properties = properties; | ||
244 | line6->line6pcm = line6pcm; | ||
245 | |||
246 | /* PCM device: */ | ||
247 | if((err = snd_device_new(line6->card, SNDRV_DEV_PCM, line6, &pcm_ops)) < 0) | ||
248 | return err; | ||
249 | |||
250 | snd_card_set_dev(line6->card, line6->ifcdev); | ||
251 | |||
252 | if((err = snd_line6_new_pcm(line6pcm)) < 0) | ||
253 | return err; | ||
254 | |||
255 | spin_lock_init(&line6pcm->lock_audio_out); | ||
256 | spin_lock_init(&line6pcm->lock_audio_in); | ||
257 | spin_lock_init(&line6pcm->lock_trigger); | ||
258 | |||
259 | if((err = create_audio_out_urbs(line6pcm)) < 0) | ||
260 | return err; | ||
261 | |||
262 | if((err = create_audio_in_urbs(line6pcm)) < 0) | ||
263 | return err; | ||
264 | |||
265 | /* mixer: */ | ||
266 | if((err = snd_ctl_add(line6->card, snd_ctl_new1(&line6_control, line6pcm))) < 0) | ||
267 | return err; | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | /* prepare pcm callback */ | ||
273 | int snd_line6_prepare(struct snd_pcm_substream *substream) | ||
274 | { | ||
275 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
276 | |||
277 | if(!test_and_set_bit(BIT_PREPARED, &line6pcm->flags)) { | ||
278 | unlink_wait_clear_audio_out_urbs(line6pcm); | ||
279 | line6pcm->pos_out = 0; | ||
280 | line6pcm->pos_out_done = 0; | ||
281 | |||
282 | unlink_wait_clear_audio_in_urbs(line6pcm); | ||
283 | line6pcm->bytes_out = 0; | ||
284 | line6pcm->pos_in_done = 0; | ||
285 | line6pcm->bytes_in = 0; | ||
286 | } | ||
287 | |||
288 | return 0; | ||
289 | } | ||
diff --git a/drivers/staging/line6/pcm.h b/drivers/staging/line6/pcm.h new file mode 100644 index 000000000000..90f8bb9816d5 --- /dev/null +++ b/drivers/staging/line6/pcm.h | |||
@@ -0,0 +1,210 @@ | |||
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 | /* | ||
13 | PCM interface to POD series devices. | ||
14 | */ | ||
15 | |||
16 | #ifndef PCM_H | ||
17 | #define PCM_H | ||
18 | |||
19 | |||
20 | #include <sound/pcm.h> | ||
21 | |||
22 | #include "driver.h" | ||
23 | #include "usbdefs.h" | ||
24 | |||
25 | |||
26 | #define LINE6_ISO_BUFFERS 8 /* number of URBs */ | ||
27 | #define LINE6_ISO_PACKETS 2 /* number of USB frames per URB */ | ||
28 | #define LINE6_ISO_INTERVAL 1 /* in a "full speed" device (such as the PODxt Pro) this means 1ms */ | ||
29 | #define LINE6_ISO_PACKET_SIZE_MAX 252 /* this should be queried dynamically from the USB interface! */ | ||
30 | |||
31 | |||
32 | /* | ||
33 | Extract the messaging device from the substream instance | ||
34 | */ | ||
35 | #define s2m(s) (((struct snd_line6_pcm *)snd_pcm_substream_chip(s))->line6->ifcdev) | ||
36 | |||
37 | |||
38 | enum { | ||
39 | BIT_RUNNING_PLAYBACK, | ||
40 | BIT_RUNNING_CAPTURE, | ||
41 | BIT_PAUSE_PLAYBACK, | ||
42 | BIT_PREPARED | ||
43 | }; | ||
44 | |||
45 | struct line6_pcm_properties { | ||
46 | struct snd_pcm_hardware snd_line6_playback_hw, snd_line6_capture_hw; | ||
47 | struct snd_pcm_hw_constraint_ratdens snd_line6_rates; | ||
48 | int bytes_per_frame; | ||
49 | }; | ||
50 | |||
51 | struct snd_line6_pcm | ||
52 | { | ||
53 | /** | ||
54 | Pointer back to the Line6 driver data structure. | ||
55 | */ | ||
56 | struct usb_line6 *line6; | ||
57 | |||
58 | /** | ||
59 | Properties. | ||
60 | */ | ||
61 | struct line6_pcm_properties *properties; | ||
62 | |||
63 | /** | ||
64 | ALSA pcm stream | ||
65 | */ | ||
66 | struct snd_pcm *pcm; | ||
67 | |||
68 | /** | ||
69 | URBs for audio playback. | ||
70 | */ | ||
71 | struct urb *urb_audio_out[LINE6_ISO_BUFFERS]; | ||
72 | |||
73 | /** | ||
74 | URBs for audio capture. | ||
75 | */ | ||
76 | struct urb *urb_audio_in[LINE6_ISO_BUFFERS]; | ||
77 | |||
78 | /** | ||
79 | Temporary buffer to hold data when playback buffer wraps. | ||
80 | */ | ||
81 | unsigned char *wrap_out; | ||
82 | |||
83 | /** | ||
84 | Temporary buffer for capture. | ||
85 | Since the packet size is not known in advance, this buffer is large enough | ||
86 | to store maximum size packets. | ||
87 | */ | ||
88 | unsigned char *buffer_in; | ||
89 | |||
90 | /** | ||
91 | Free frame position in the playback buffer. | ||
92 | */ | ||
93 | snd_pcm_uframes_t pos_out; | ||
94 | |||
95 | /** | ||
96 | Count processed bytes for playback. | ||
97 | This is modulo period size (to determine when a period is finished). | ||
98 | */ | ||
99 | unsigned bytes_out; | ||
100 | |||
101 | /** | ||
102 | Counter to create desired playback sample rate. | ||
103 | */ | ||
104 | unsigned count_out; | ||
105 | |||
106 | /** | ||
107 | Playback period size in bytes | ||
108 | */ | ||
109 | unsigned period_out; | ||
110 | |||
111 | /** | ||
112 | Processed frame position in the playback buffer. | ||
113 | The contents of the output ring buffer have been consumed by the USB | ||
114 | subsystem (i.e., sent to the USB device) up to this position. | ||
115 | */ | ||
116 | snd_pcm_uframes_t pos_out_done; | ||
117 | |||
118 | /** | ||
119 | Count processed bytes for capture. | ||
120 | This is modulo period size (to determine when a period is finished). | ||
121 | */ | ||
122 | unsigned bytes_in; | ||
123 | |||
124 | /** | ||
125 | Counter to create desired capture sample rate. | ||
126 | */ | ||
127 | unsigned count_in; | ||
128 | |||
129 | /** | ||
130 | Capture period size in bytes | ||
131 | */ | ||
132 | unsigned period_in; | ||
133 | |||
134 | /** | ||
135 | Processed frame position in the capture buffer. | ||
136 | The contents of the output ring buffer have been consumed by the USB | ||
137 | subsystem (i.e., sent to the USB device) up to this position. | ||
138 | */ | ||
139 | snd_pcm_uframes_t pos_in_done; | ||
140 | |||
141 | /** | ||
142 | Bit mask of active playback URBs. | ||
143 | */ | ||
144 | unsigned long active_urb_out; | ||
145 | |||
146 | /** | ||
147 | Maximum size of USB packet. | ||
148 | */ | ||
149 | int max_packet_size; | ||
150 | |||
151 | /** | ||
152 | USB endpoint for listening to audio data. | ||
153 | */ | ||
154 | int ep_audio_read; | ||
155 | |||
156 | /** | ||
157 | USB endpoint for writing audio data. | ||
158 | */ | ||
159 | int ep_audio_write; | ||
160 | |||
161 | /** | ||
162 | Bit mask of active capture URBs. | ||
163 | */ | ||
164 | unsigned long active_urb_in; | ||
165 | |||
166 | /** | ||
167 | Bit mask of playback URBs currently being unlinked. | ||
168 | */ | ||
169 | unsigned long unlink_urb_out; | ||
170 | |||
171 | /** | ||
172 | Bit mask of capture URBs currently being unlinked. | ||
173 | */ | ||
174 | unsigned long unlink_urb_in; | ||
175 | |||
176 | /** | ||
177 | Spin lock to protect updates of the playback buffer positions (not | ||
178 | contents!) | ||
179 | */ | ||
180 | spinlock_t lock_audio_out; | ||
181 | |||
182 | /** | ||
183 | Spin lock to protect updates of the capture buffer positions (not | ||
184 | contents!) | ||
185 | */ | ||
186 | spinlock_t lock_audio_in; | ||
187 | |||
188 | /** | ||
189 | Spin lock to protect trigger. | ||
190 | */ | ||
191 | spinlock_t lock_trigger; | ||
192 | |||
193 | /** | ||
194 | PCM playback volume (left and right). | ||
195 | */ | ||
196 | int volume[2]; | ||
197 | |||
198 | /** | ||
199 | Several status bits (see BIT_*). | ||
200 | */ | ||
201 | unsigned long flags; | ||
202 | }; | ||
203 | |||
204 | |||
205 | extern int line6_init_pcm(struct usb_line6 *line6, struct line6_pcm_properties *properties); | ||
206 | extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd); | ||
207 | extern int snd_line6_prepare(struct snd_pcm_substream *substream); | ||
208 | |||
209 | |||
210 | #endif | ||
diff --git a/drivers/staging/line6/playback.c b/drivers/staging/line6/playback.c new file mode 100644 index 000000000000..f6503c23bd08 --- /dev/null +++ b/drivers/staging/line6/playback.c | |||
@@ -0,0 +1,428 @@ | |||
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 <sound/core.h> | ||
15 | #include <sound/pcm.h> | ||
16 | #include <sound/pcm_params.h> | ||
17 | |||
18 | #include "audio.h" | ||
19 | #include "pcm.h" | ||
20 | #include "pod.h" | ||
21 | |||
22 | |||
23 | /* | ||
24 | Software stereo volume control. | ||
25 | */ | ||
26 | static void change_volume(struct urb *urb_out, int volume[], int bytes_per_frame) | ||
27 | { | ||
28 | int chn = 0; | ||
29 | |||
30 | if(volume[0] == 256 && volume[1] == 256) | ||
31 | return; /* maximum volume - no change */ | ||
32 | |||
33 | if(bytes_per_frame == 4) { | ||
34 | short *p, *buf_end; | ||
35 | p = (short *)urb_out->transfer_buffer; | ||
36 | buf_end = p + urb_out->transfer_buffer_length / sizeof(*p); | ||
37 | |||
38 | for(; p < buf_end; ++p) { | ||
39 | *p = (*p * volume[chn & 1]) >> 8; | ||
40 | ++chn; | ||
41 | } | ||
42 | } | ||
43 | else if(bytes_per_frame == 6) { | ||
44 | unsigned char *p, *buf_end; | ||
45 | p = (unsigned char *)urb_out->transfer_buffer; | ||
46 | buf_end = p + urb_out->transfer_buffer_length; | ||
47 | |||
48 | for(; p < buf_end; p += 3) { | ||
49 | int val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16); | ||
50 | val = (val * volume[chn & 1]) >> 8; | ||
51 | p[0] = val; | ||
52 | p[1] = val >> 8; | ||
53 | p[2] = val >> 16; | ||
54 | ++chn; | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | Find a free URB, prepare audio data, and submit URB. | ||
61 | */ | ||
62 | static int submit_audio_out_urb(struct snd_pcm_substream *substream) | ||
63 | { | ||
64 | int index; | ||
65 | unsigned long flags; | ||
66 | int i, urb_size, urb_frames; | ||
67 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
68 | const int bytes_per_frame = line6pcm->properties->bytes_per_frame; | ||
69 | const int frame_increment = line6pcm->properties->snd_line6_rates.rats[0].num_min; | ||
70 | const int frame_factor = line6pcm->properties->snd_line6_rates.rats[0].den * (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL); | ||
71 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
72 | struct urb *urb_out; | ||
73 | |||
74 | spin_lock_irqsave(&line6pcm->lock_audio_out, flags); | ||
75 | index = find_first_zero_bit(&line6pcm->active_urb_out, LINE6_ISO_BUFFERS); | ||
76 | |||
77 | if(index < 0 || index >= LINE6_ISO_BUFFERS) { | ||
78 | spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); | ||
79 | dev_err(s2m(substream), "no free URB found\n"); | ||
80 | return -EINVAL; | ||
81 | } | ||
82 | |||
83 | urb_out = line6pcm->urb_audio_out[index]; | ||
84 | urb_size = 0; | ||
85 | |||
86 | for(i = 0; i < LINE6_ISO_PACKETS; ++i) { | ||
87 | /* compute frame size for given sampling rate */ | ||
88 | int n, fs; | ||
89 | struct usb_iso_packet_descriptor *fout = &urb_out->iso_frame_desc[i]; | ||
90 | line6pcm->count_out += frame_increment; | ||
91 | n = line6pcm->count_out / frame_factor; | ||
92 | line6pcm->count_out -= n * frame_factor; | ||
93 | fs = n * bytes_per_frame; | ||
94 | fout->offset = urb_size; | ||
95 | fout->length = fs; | ||
96 | urb_size += fs; | ||
97 | } | ||
98 | |||
99 | urb_frames = urb_size / bytes_per_frame; | ||
100 | |||
101 | if(test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) { | ||
102 | urb_out->transfer_buffer = line6pcm->wrap_out; | ||
103 | memset(line6pcm->wrap_out, 0, urb_size); | ||
104 | } | ||
105 | else { | ||
106 | if(line6pcm->pos_out + urb_frames > runtime->buffer_size) { | ||
107 | /* | ||
108 | The transferred area goes over buffer boundary, | ||
109 | copy the data to the temp buffer. | ||
110 | */ | ||
111 | int len; | ||
112 | len = runtime->buffer_size - line6pcm->pos_out; | ||
113 | urb_out->transfer_buffer = line6pcm->wrap_out; | ||
114 | |||
115 | if(len > 0) { | ||
116 | memcpy(line6pcm->wrap_out, runtime->dma_area + line6pcm->pos_out * bytes_per_frame, len * bytes_per_frame); | ||
117 | memcpy(line6pcm->wrap_out + len * bytes_per_frame, runtime->dma_area, (urb_frames - len) * bytes_per_frame); | ||
118 | } | ||
119 | else | ||
120 | dev_err(s2m(substream), "driver bug: len = %d\n", len); /* this is somewhat paranoid */ | ||
121 | } | ||
122 | else { | ||
123 | /* set the buffer pointer */ | ||
124 | urb_out->transfer_buffer = runtime->dma_area + line6pcm->pos_out * bytes_per_frame; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | if((line6pcm->pos_out += urb_frames) >= runtime->buffer_size) | ||
129 | line6pcm->pos_out -= runtime->buffer_size; | ||
130 | |||
131 | urb_out->transfer_buffer_length = urb_size; | ||
132 | urb_out->context = substream; | ||
133 | change_volume(urb_out, line6pcm->volume, bytes_per_frame); | ||
134 | |||
135 | #if DO_DUMP_PCM_SEND | ||
136 | for(i = 0; i < LINE6_ISO_PACKETS; ++i) { | ||
137 | struct usb_iso_packet_descriptor *fout = &urb_out->iso_frame_desc[i]; | ||
138 | line6_write_hexdump(line6pcm->line6, 'P', urb_out->transfer_buffer + fout->offset, fout->length); | ||
139 | } | ||
140 | #endif | ||
141 | |||
142 | if(usb_submit_urb(urb_out, GFP_ATOMIC) == 0) | ||
143 | set_bit(index, &line6pcm->active_urb_out); | ||
144 | else | ||
145 | dev_err(s2m(substream), "URB out #%d submission failed\n", index); | ||
146 | |||
147 | spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | Submit all currently available playback URBs. | ||
153 | */ | ||
154 | static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream) | ||
155 | { | ||
156 | int ret, i; | ||
157 | |||
158 | for(i = 0; i < LINE6_ISO_BUFFERS; ++i) | ||
159 | if((ret = submit_audio_out_urb(substream)) < 0) | ||
160 | return ret; | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | Unlink all currently active playback URBs. | ||
167 | */ | ||
168 | static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) | ||
169 | { | ||
170 | unsigned int i; | ||
171 | |||
172 | for(i = LINE6_ISO_BUFFERS; i--;) { | ||
173 | if(test_bit(i, &line6pcm->active_urb_out)) { | ||
174 | if(!test_and_set_bit(i, &line6pcm->unlink_urb_out)) { | ||
175 | struct urb *u = line6pcm->urb_audio_out[i]; | ||
176 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) | ||
177 | u->transfer_flags |= URB_ASYNC_UNLINK; | ||
178 | #endif | ||
179 | usb_unlink_urb(u); | ||
180 | } | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | Wait until unlinking of all currently active playback URBs has been finished. | ||
187 | */ | ||
188 | static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) | ||
189 | { | ||
190 | int timeout = HZ; | ||
191 | unsigned int i; | ||
192 | int alive; | ||
193 | |||
194 | do { | ||
195 | alive = 0; | ||
196 | for (i = LINE6_ISO_BUFFERS; i--;) { | ||
197 | if (test_bit(i, &line6pcm->active_urb_out)) | ||
198 | alive++; | ||
199 | } | ||
200 | if (! alive) | ||
201 | break; | ||
202 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
203 | schedule_timeout(1); | ||
204 | } while (--timeout > 0); | ||
205 | if (alive) | ||
206 | snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); | ||
207 | |||
208 | line6pcm->active_urb_out = 0; | ||
209 | line6pcm->unlink_urb_out = 0; | ||
210 | } | ||
211 | |||
212 | /* | ||
213 | Unlink all currently active playback URBs, and wait for finishing. | ||
214 | */ | ||
215 | void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) | ||
216 | { | ||
217 | unlink_audio_out_urbs(line6pcm); | ||
218 | wait_clear_audio_out_urbs(line6pcm); | ||
219 | } | ||
220 | |||
221 | /* | ||
222 | Callback for completed playback URB. | ||
223 | */ | ||
224 | static void audio_out_callback(struct urb *urb PT_REGS) | ||
225 | { | ||
226 | int i, index, length = 0, shutdown = 0; | ||
227 | unsigned long flags; | ||
228 | |||
229 | struct snd_pcm_substream *substream = (struct snd_pcm_substream *)urb->context; | ||
230 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
231 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
232 | |||
233 | /* find index of URB */ | ||
234 | for(index = LINE6_ISO_BUFFERS; index--;) | ||
235 | if(urb == line6pcm->urb_audio_out[index]) | ||
236 | break; | ||
237 | |||
238 | if(index < 0) | ||
239 | return; /* URB has been unlinked asynchronously */ | ||
240 | |||
241 | for(i = LINE6_ISO_PACKETS; i--;) | ||
242 | length += urb->iso_frame_desc[i].length; | ||
243 | |||
244 | spin_lock_irqsave(&line6pcm->lock_audio_out, flags); | ||
245 | line6pcm->pos_out_done += length / line6pcm->properties->bytes_per_frame; | ||
246 | |||
247 | if(line6pcm->pos_out_done >= runtime->buffer_size) | ||
248 | line6pcm->pos_out_done -= runtime->buffer_size; | ||
249 | |||
250 | clear_bit(index, &line6pcm->active_urb_out); | ||
251 | |||
252 | for(i = LINE6_ISO_PACKETS; i--;) | ||
253 | if(urb->iso_frame_desc[i].status == -ESHUTDOWN) { | ||
254 | shutdown = 1; | ||
255 | break; | ||
256 | } | ||
257 | |||
258 | if(test_bit(index, &line6pcm->unlink_urb_out)) | ||
259 | shutdown = 1; | ||
260 | |||
261 | spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); | ||
262 | |||
263 | if(!shutdown) { | ||
264 | submit_audio_out_urb(substream); | ||
265 | |||
266 | if((line6pcm->bytes_out += length) >= line6pcm->period_out) { | ||
267 | line6pcm->bytes_out -= line6pcm->period_out; | ||
268 | snd_pcm_period_elapsed(substream); | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | |||
273 | /* open playback callback */ | ||
274 | static int snd_line6_playback_open(struct snd_pcm_substream *substream) | ||
275 | { | ||
276 | int err; | ||
277 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
278 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
279 | |||
280 | if((err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
281 | (&line6pcm->properties->snd_line6_rates))) < 0) | ||
282 | return err; | ||
283 | |||
284 | runtime->hw = line6pcm->properties->snd_line6_playback_hw; | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | /* close playback callback */ | ||
289 | static int snd_line6_playback_close(struct snd_pcm_substream *substream) | ||
290 | { | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | /* hw_params playback callback */ | ||
295 | static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) | ||
296 | { | ||
297 | int ret; | ||
298 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
299 | |||
300 | /* -- Florian Demski [FD] */ | ||
301 | /* don't ask me why, but this fixes the bug on my machine */ | ||
302 | if ( line6pcm == NULL ) { | ||
303 | if ( substream->pcm == NULL ) | ||
304 | return -ENOMEM; | ||
305 | if ( substream->pcm->private_data == NULL ) | ||
306 | return -ENOMEM; | ||
307 | substream->private_data = substream->pcm->private_data; | ||
308 | line6pcm = snd_pcm_substream_chip( substream ); | ||
309 | } | ||
310 | /* -- [FD] end */ | ||
311 | |||
312 | if((ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) | ||
313 | return ret; | ||
314 | |||
315 | line6pcm->period_out = params_period_bytes(hw_params); | ||
316 | line6pcm->wrap_out = kmalloc(2 * LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL); | ||
317 | |||
318 | if(!line6pcm->wrap_out) { | ||
319 | dev_err(s2m(substream), "cannot malloc wrap_out\n"); | ||
320 | return -ENOMEM; | ||
321 | } | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | /* hw_free playback callback */ | ||
327 | static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) | ||
328 | { | ||
329 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
330 | unlink_wait_clear_audio_out_urbs(line6pcm); | ||
331 | |||
332 | if(line6pcm->wrap_out) { | ||
333 | kfree(line6pcm->wrap_out); | ||
334 | line6pcm->wrap_out = 0; | ||
335 | } | ||
336 | |||
337 | return snd_pcm_lib_free_pages(substream); | ||
338 | } | ||
339 | |||
340 | /* trigger playback callback */ | ||
341 | int snd_line6_playback_trigger(struct snd_pcm_substream *substream, int cmd) | ||
342 | { | ||
343 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
344 | int err; | ||
345 | line6pcm->count_out = 0; | ||
346 | |||
347 | switch(cmd) { | ||
348 | case SNDRV_PCM_TRIGGER_START: | ||
349 | if(!test_and_set_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) { | ||
350 | err = submit_audio_out_all_urbs(substream); | ||
351 | |||
352 | if(err < 0) { | ||
353 | clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags); | ||
354 | return err; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | break; | ||
359 | |||
360 | case SNDRV_PCM_TRIGGER_STOP: | ||
361 | if(test_and_clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) | ||
362 | unlink_audio_out_urbs(line6pcm); | ||
363 | |||
364 | break; | ||
365 | |||
366 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
367 | set_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags); | ||
368 | break; | ||
369 | |||
370 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
371 | clear_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags); | ||
372 | break; | ||
373 | |||
374 | default: | ||
375 | return -EINVAL; | ||
376 | } | ||
377 | |||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | /* playback pointer callback */ | ||
382 | static snd_pcm_uframes_t | ||
383 | snd_line6_playback_pointer(struct snd_pcm_substream *substream) | ||
384 | { | ||
385 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
386 | return line6pcm->pos_out_done; | ||
387 | } | ||
388 | |||
389 | /* playback operators */ | ||
390 | struct snd_pcm_ops snd_line6_playback_ops = { | ||
391 | .open = snd_line6_playback_open, | ||
392 | .close = snd_line6_playback_close, | ||
393 | .ioctl = snd_pcm_lib_ioctl, | ||
394 | .hw_params = snd_line6_playback_hw_params, | ||
395 | .hw_free = snd_line6_playback_hw_free, | ||
396 | .prepare = snd_line6_prepare, | ||
397 | .trigger = snd_line6_trigger, | ||
398 | .pointer = snd_line6_playback_pointer, | ||
399 | }; | ||
400 | |||
401 | int create_audio_out_urbs(struct snd_line6_pcm *line6pcm) | ||
402 | { | ||
403 | int i; | ||
404 | |||
405 | /* create audio URBs and fill in constant values: */ | ||
406 | for(i = 0; i < LINE6_ISO_BUFFERS; ++i) { | ||
407 | struct urb *urb; | ||
408 | |||
409 | /* URB for audio out: */ | ||
410 | urb = line6pcm->urb_audio_out[i] = usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); | ||
411 | |||
412 | if(urb == NULL) { | ||
413 | dev_err(line6pcm->line6->ifcdev, "Out of memory\n"); | ||
414 | return -ENOMEM; | ||
415 | } | ||
416 | |||
417 | urb->dev = line6pcm->line6->usbdev; | ||
418 | urb->pipe = usb_sndisocpipe(line6pcm->line6->usbdev, line6pcm->ep_audio_write & USB_ENDPOINT_NUMBER_MASK); | ||
419 | urb->transfer_flags = URB_ISO_ASAP; | ||
420 | urb->start_frame = -1; | ||
421 | urb->number_of_packets = LINE6_ISO_PACKETS; | ||
422 | urb->interval = LINE6_ISO_INTERVAL; | ||
423 | urb->error_count = 0; | ||
424 | urb->complete = audio_out_callback; | ||
425 | } | ||
426 | |||
427 | return 0; | ||
428 | } | ||
diff --git a/drivers/staging/line6/playback.h b/drivers/staging/line6/playback.h new file mode 100644 index 000000000000..019c40f2cdb4 --- /dev/null +++ b/drivers/staging/line6/playback.h | |||
@@ -0,0 +1,29 @@ | |||
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 | #ifndef PLAYBACK_H | ||
13 | #define PLAYBACK_H | ||
14 | |||
15 | |||
16 | #include "driver.h" | ||
17 | |||
18 | #include <sound/pcm.h> | ||
19 | |||
20 | |||
21 | extern struct snd_pcm_ops snd_line6_playback_ops; | ||
22 | |||
23 | |||
24 | extern int create_audio_out_urbs(struct snd_line6_pcm *line6pcm); | ||
25 | extern int snd_line6_playback_trigger(struct snd_pcm_substream *substream, int cmd); | ||
26 | extern void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm); | ||
27 | |||
28 | |||
29 | #endif | ||
diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c new file mode 100644 index 000000000000..154985aeb698 --- /dev/null +++ b/drivers/staging/line6/pod.c | |||
@@ -0,0 +1,1100 @@ | |||
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 "audio.h" | ||
15 | #include "capture.h" | ||
16 | #include "control.h" | ||
17 | #include "playback.h" | ||
18 | #include "pod.h" | ||
19 | |||
20 | |||
21 | #define POD_SYSEX_CODE 3 | ||
22 | #define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ | ||
23 | |||
24 | |||
25 | enum { | ||
26 | POD_SYSEX_CLIP = 0x0f, | ||
27 | POD_SYSEX_SAVE = 0x24, | ||
28 | POD_SYSEX_SYSTEM = 0x56, | ||
29 | POD_SYSEX_SYSTEMREQ = 0x57, | ||
30 | /* POD_SYSEX_UPDATE = 0x6c, */ /* software update! */ | ||
31 | POD_SYSEX_STORE = 0x71, | ||
32 | POD_SYSEX_FINISH = 0x72, | ||
33 | POD_SYSEX_DUMPMEM = 0x73, | ||
34 | POD_SYSEX_DUMP = 0x74, | ||
35 | POD_SYSEX_DUMPREQ = 0x75 | ||
36 | /* POD_SYSEX_DUMPMEM2 = 0x76 */ /* dumps entire internal memory of PODxt Pro */ | ||
37 | }; | ||
38 | |||
39 | enum { | ||
40 | POD_monitor_level = 0x04, | ||
41 | POD_routing = 0x05, | ||
42 | POD_tuner_mute = 0x13, | ||
43 | POD_tuner_freq = 0x15, | ||
44 | POD_tuner_note = 0x16, | ||
45 | POD_tuner_pitch = 0x17, | ||
46 | POD_system_invalid = 0x7fff | ||
47 | }; | ||
48 | |||
49 | enum { | ||
50 | POD_DUMP_MEMORY = 2 | ||
51 | }; | ||
52 | |||
53 | enum { | ||
54 | POD_BUSY_READ, | ||
55 | POD_BUSY_WRITE, | ||
56 | POD_CHANNEL_DIRTY, | ||
57 | POD_SAVE_PRESSED, | ||
58 | POD_BUSY_MIDISEND | ||
59 | }; | ||
60 | |||
61 | |||
62 | static struct snd_ratden pod_ratden = { | ||
63 | .num_min = 78125, | ||
64 | .num_max = 78125, | ||
65 | .num_step = 1, | ||
66 | .den = 2 | ||
67 | }; | ||
68 | |||
69 | static struct line6_pcm_properties pod_pcm_properties = { | ||
70 | .snd_line6_playback_hw = { | ||
71 | .info = (SNDRV_PCM_INFO_MMAP | | ||
72 | SNDRV_PCM_INFO_INTERLEAVED | | ||
73 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
74 | SNDRV_PCM_INFO_MMAP_VALID | | ||
75 | SNDRV_PCM_INFO_PAUSE | | ||
76 | SNDRV_PCM_INFO_SYNC_START), | ||
77 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | ||
78 | .rates = SNDRV_PCM_RATE_KNOT, | ||
79 | .rate_min = 39062, | ||
80 | .rate_max = 39063, | ||
81 | .channels_min = 2, | ||
82 | .channels_max = 2, | ||
83 | .buffer_bytes_max = 60000, | ||
84 | .period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME, /* at least one URB must fit into one period */ | ||
85 | .period_bytes_max = 8192, | ||
86 | .periods_min = 1, | ||
87 | .periods_max = 1024 | ||
88 | }, | ||
89 | .snd_line6_capture_hw = { | ||
90 | .info = (SNDRV_PCM_INFO_MMAP | | ||
91 | SNDRV_PCM_INFO_INTERLEAVED | | ||
92 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
93 | SNDRV_PCM_INFO_MMAP_VALID | | ||
94 | SNDRV_PCM_INFO_SYNC_START), | ||
95 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | ||
96 | .rates = SNDRV_PCM_RATE_KNOT, | ||
97 | .rate_min = 39062, | ||
98 | .rate_max = 39063, | ||
99 | .channels_min = 2, | ||
100 | .channels_max = 2, | ||
101 | .buffer_bytes_max = 60000, | ||
102 | .period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME, /* at least one URB must fit into one period */ | ||
103 | .period_bytes_max = 8192, | ||
104 | .periods_min = 1, | ||
105 | .periods_max = 1024 | ||
106 | }, | ||
107 | .snd_line6_rates = { | ||
108 | .nrats = 1, | ||
109 | .rats = &pod_ratden | ||
110 | }, | ||
111 | .bytes_per_frame = POD_BYTES_PER_FRAME | ||
112 | }; | ||
113 | |||
114 | static const char pod_request_version[] = { 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 }; | ||
115 | static const char pod_request_channel[] = { 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7 }; | ||
116 | static const char pod_version_header [] = { 0xf2, 0x7e, 0x7f, 0x06, 0x02 }; | ||
117 | |||
118 | |||
119 | /* | ||
120 | Mark all parameters as dirty and notify waiting processes. | ||
121 | */ | ||
122 | static void pod_mark_batch_all_dirty(struct usb_line6_pod *pod) | ||
123 | { | ||
124 | int i; | ||
125 | |||
126 | for(i = POD_CONTROL_SIZE; i--;) | ||
127 | set_bit(i, pod->param_dirty); | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | Send an asynchronous request for the POD firmware version and device ID. | ||
132 | */ | ||
133 | static int pod_version_request_async(struct usb_line6_pod *pod) | ||
134 | { | ||
135 | return line6_send_raw_message_async(&pod->line6, pod->buffer_versionreq, sizeof(pod_request_version)); | ||
136 | } | ||
137 | |||
138 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | ||
139 | static void pod_create_files_work(struct work_struct *work) | ||
140 | { | ||
141 | struct usb_line6_pod *pod = container_of(work, struct usb_line6_pod, create_files_work); | ||
142 | #else | ||
143 | static void pod_create_files_work(void *work) | ||
144 | { | ||
145 | struct usb_line6_pod *pod = (struct usb_line6_pod *)work; | ||
146 | #endif | ||
147 | |||
148 | pod_create_files(pod->firmware_version, pod->line6.properties->device_bit, pod->line6.ifcdev); | ||
149 | } | ||
150 | |||
151 | static void pod_startup_timeout(unsigned long arg) | ||
152 | { | ||
153 | enum { | ||
154 | REQUEST_NONE, | ||
155 | REQUEST_DUMP, | ||
156 | REQUEST_VERSION | ||
157 | }; | ||
158 | |||
159 | int request = REQUEST_NONE; | ||
160 | struct usb_line6_pod *pod = (struct usb_line6_pod *)arg; | ||
161 | |||
162 | if(pod->dumpreq.ok) { | ||
163 | if(!pod->versionreq_ok) | ||
164 | request = REQUEST_VERSION; | ||
165 | } | ||
166 | else { | ||
167 | if(pod->versionreq_ok) | ||
168 | request = REQUEST_DUMP; | ||
169 | else if(pod->startup_count++ & 1) | ||
170 | request = REQUEST_DUMP; | ||
171 | else | ||
172 | request = REQUEST_VERSION; | ||
173 | } | ||
174 | |||
175 | switch(request) { | ||
176 | case REQUEST_DUMP: | ||
177 | line6_dump_request_async(&pod->dumpreq, &pod->line6, 0); | ||
178 | break; | ||
179 | |||
180 | case REQUEST_VERSION: | ||
181 | pod_version_request_async(pod); | ||
182 | break; | ||
183 | |||
184 | default: | ||
185 | return; | ||
186 | } | ||
187 | |||
188 | line6_startup_delayed(&pod->dumpreq, 1, pod_startup_timeout, pod); | ||
189 | } | ||
190 | |||
191 | static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, int size) | ||
192 | { | ||
193 | return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, size); | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | Send channel dump data to the PODxt Pro. | ||
198 | */ | ||
199 | static void pod_dump(struct usb_line6_pod *pod, const unsigned char *data) | ||
200 | { | ||
201 | int size = 1 + sizeof(pod->prog_data); | ||
202 | char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_DUMP, size); | ||
203 | if(!sysex) return; | ||
204 | sysex[SYSEX_DATA_OFS] = 5; /* Don't know what this is good for, but PODxt Pro transmits it, so we also do... */ | ||
205 | memcpy(sysex + SYSEX_DATA_OFS + 1, data, sizeof(pod->prog_data)); | ||
206 | line6_send_sysex_message(&pod->line6, sysex, size); | ||
207 | memcpy(&pod->prog_data, data, sizeof(pod->prog_data)); | ||
208 | pod_mark_batch_all_dirty(pod); | ||
209 | kfree(sysex); | ||
210 | } | ||
211 | |||
212 | /* | ||
213 | Store parameter value in driver memory and mark it as dirty. | ||
214 | */ | ||
215 | static void pod_store_parameter(struct usb_line6_pod *pod, int param, int value) | ||
216 | { | ||
217 | pod->prog_data.control[param] = value; | ||
218 | set_bit(param, pod->param_dirty); | ||
219 | pod->dirty = 1; | ||
220 | } | ||
221 | |||
222 | /* | ||
223 | Handle SAVE button | ||
224 | */ | ||
225 | static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int index) | ||
226 | { | ||
227 | pod->dirty = 0; | ||
228 | set_bit(POD_SAVE_PRESSED, &pod->atomic_flags); | ||
229 | } | ||
230 | |||
231 | /* | ||
232 | Process a completely received message. | ||
233 | */ | ||
234 | void pod_process_message(struct usb_line6_pod *pod) | ||
235 | { | ||
236 | const unsigned char *buf = pod->line6.buffer_message; | ||
237 | |||
238 | /* filter messages by type */ | ||
239 | switch(buf[0] & 0xf0) { | ||
240 | case LINE6_PARAM_CHANGE: | ||
241 | case LINE6_PROGRAM_CHANGE: | ||
242 | case LINE6_SYSEX_BEGIN: | ||
243 | break; /* handle these further down */ | ||
244 | |||
245 | default: | ||
246 | return; /* ignore all others */ | ||
247 | } | ||
248 | |||
249 | /* process all remaining messages */ | ||
250 | switch(buf[0]) { | ||
251 | case LINE6_PARAM_CHANGE | LINE6_CHANNEL_DEVICE: | ||
252 | pod_store_parameter(pod, buf[1], buf[2]); | ||
253 | /* intentionally no break here! */ | ||
254 | |||
255 | case LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST: | ||
256 | if((buf[1] == POD_amp_model_setup) || (buf[1] == POD_effect_setup)) /* these also affect other settings */ | ||
257 | line6_dump_request_async(&pod->dumpreq, &pod->line6, 0); | ||
258 | |||
259 | break; | ||
260 | |||
261 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE: | ||
262 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST: | ||
263 | pod->channel_num = buf[1]; | ||
264 | pod->dirty = 0; | ||
265 | set_bit(POD_CHANNEL_DIRTY, &pod->atomic_flags); | ||
266 | line6_dump_request_async(&pod->dumpreq, &pod->line6, 0); | ||
267 | break; | ||
268 | |||
269 | case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE: | ||
270 | case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN: | ||
271 | if(memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) == 0) { | ||
272 | switch(buf[5]) { | ||
273 | case POD_SYSEX_DUMP: | ||
274 | if(pod->line6.message_length == sizeof(pod->prog_data) + 7) { | ||
275 | switch(pod->dumpreq.in_progress) { | ||
276 | case LINE6_DUMP_CURRENT: | ||
277 | memcpy(&pod->prog_data, buf + 7, sizeof(pod->prog_data)); | ||
278 | pod_mark_batch_all_dirty(pod); | ||
279 | pod->dumpreq.ok = 1; | ||
280 | break; | ||
281 | |||
282 | case POD_DUMP_MEMORY: | ||
283 | memcpy(&pod->prog_data_buf, buf + 7, sizeof(pod->prog_data_buf)); | ||
284 | break; | ||
285 | |||
286 | default: | ||
287 | DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown dump code %02X\n", pod->dumpreq.in_progress)); | ||
288 | } | ||
289 | |||
290 | line6_dump_finished(&pod->dumpreq); | ||
291 | } | ||
292 | else | ||
293 | DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "wrong size of channel dump message (%d instead of %d)\n", | ||
294 | pod->line6.message_length, (int)sizeof(pod->prog_data) + 7)); | ||
295 | |||
296 | break; | ||
297 | |||
298 | case POD_SYSEX_SYSTEM: { | ||
299 | short value = ((int)buf[7] << 12) | ((int)buf[8] << 8) | ((int)buf[9] << 4) | (int)buf[10]; | ||
300 | |||
301 | #define PROCESS_SYSTEM_PARAM(x) \ | ||
302 | case POD_ ## x: \ | ||
303 | pod->x.value = value; \ | ||
304 | wake_up_interruptible(&pod->x.wait); \ | ||
305 | break; | ||
306 | |||
307 | switch(buf[6]) { | ||
308 | PROCESS_SYSTEM_PARAM(monitor_level); | ||
309 | PROCESS_SYSTEM_PARAM(routing); | ||
310 | PROCESS_SYSTEM_PARAM(tuner_mute); | ||
311 | PROCESS_SYSTEM_PARAM(tuner_freq); | ||
312 | PROCESS_SYSTEM_PARAM(tuner_note); | ||
313 | PROCESS_SYSTEM_PARAM(tuner_pitch); | ||
314 | |||
315 | #undef PROCESS_SYSTEM_PARAM | ||
316 | |||
317 | default: | ||
318 | DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown tuner/system response %02X\n", buf[6])); | ||
319 | } | ||
320 | |||
321 | break; | ||
322 | } | ||
323 | |||
324 | case POD_SYSEX_FINISH: | ||
325 | /* do we need to respond to this? */ | ||
326 | break; | ||
327 | |||
328 | case POD_SYSEX_SAVE: | ||
329 | pod_save_button_pressed(pod, buf[6], buf[7]); | ||
330 | break; | ||
331 | |||
332 | case POD_SYSEX_CLIP: | ||
333 | DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "audio clipped\n")); | ||
334 | pod->clipping.value = 1; | ||
335 | wake_up_interruptible(&pod->clipping.wait); | ||
336 | break; | ||
337 | |||
338 | case POD_SYSEX_STORE: | ||
339 | DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "message %02X not yet implemented\n", buf[5])); | ||
340 | break; | ||
341 | |||
342 | default: | ||
343 | DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex message %02X\n", buf[5])); | ||
344 | } | ||
345 | } | ||
346 | else if(memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { | ||
347 | if(pod->versionreq_ok == 0) { | ||
348 | pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; | ||
349 | pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int)buf[10]; | ||
350 | pod->versionreq_ok = 1; | ||
351 | |||
352 | /* Now we know the firmware version, so we schedule a bottom half | ||
353 | handler to create the special files: */ | ||
354 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | ||
355 | INIT_WORK(&pod->create_files_work, pod_create_files_work); | ||
356 | #else | ||
357 | INIT_WORK(&pod->create_files_work, pod_create_files_work, pod); | ||
358 | #endif | ||
359 | queue_work(line6_workqueue, &pod->create_files_work); | ||
360 | } | ||
361 | else | ||
362 | DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "multiple firmware version message\n")); | ||
363 | } | ||
364 | else | ||
365 | DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex header\n")); | ||
366 | |||
367 | break; | ||
368 | |||
369 | case LINE6_SYSEX_END: | ||
370 | break; | ||
371 | |||
372 | default: | ||
373 | DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "POD: unknown message %02X\n", buf[0])); | ||
374 | } | ||
375 | } | ||
376 | |||
377 | /* | ||
378 | Detect some cases that require a channel dump after sending a command to the | ||
379 | device. Important notes: | ||
380 | *) The actual dump request can not be sent here since we are not allowed to | ||
381 | wait for the completion of the first message in this context, and sending | ||
382 | the dump request before completion of the previous message leaves the POD | ||
383 | in an undefined state. The dump request will be sent when the echoed | ||
384 | commands are received. | ||
385 | *) This method fails if a param change message is "chopped" after the first | ||
386 | byte. | ||
387 | */ | ||
388 | void pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, int length) | ||
389 | { | ||
390 | int i; | ||
391 | |||
392 | if(!pod->midi_postprocess) | ||
393 | return; | ||
394 | |||
395 | for(i = 0; i < length; ++i) { | ||
396 | if(data[i] == (LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST)) { | ||
397 | line6_invalidate_current(&pod->dumpreq); | ||
398 | break; | ||
399 | } | ||
400 | else if((data[i] == (LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST)) && (i < length - 1)) | ||
401 | if((data[i + 1] == POD_amp_model_setup) || (data[i + 1] == POD_effect_setup)) { | ||
402 | line6_invalidate_current(&pod->dumpreq); | ||
403 | break; | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | Send channel number (i.e., switch to a different sound). | ||
410 | */ | ||
411 | void pod_send_channel(struct usb_line6_pod *pod, int value) | ||
412 | { | ||
413 | line6_invalidate_current(&pod->dumpreq); | ||
414 | |||
415 | if(line6_send_program(&pod->line6, value) == 0) | ||
416 | pod->channel_num = value; | ||
417 | else | ||
418 | line6_dump_finished(&pod->dumpreq); | ||
419 | } | ||
420 | |||
421 | /* | ||
422 | Transmit PODxt Pro control parameter. | ||
423 | */ | ||
424 | void pod_transmit_parameter(struct usb_line6_pod *pod, int param, int value) | ||
425 | { | ||
426 | if(line6_transmit_parameter(&pod->line6, param, value) == 0) | ||
427 | pod_store_parameter(pod, param, value); | ||
428 | |||
429 | if((param == POD_amp_model_setup) || (param == POD_effect_setup)) /* these also affect other settings */ | ||
430 | line6_invalidate_current(&pod->dumpreq); | ||
431 | } | ||
432 | |||
433 | /* | ||
434 | Resolve value to memory location. | ||
435 | */ | ||
436 | static void pod_resolve(const char *buf, short block0, short block1, unsigned char *location) | ||
437 | { | ||
438 | int value = simple_strtoul(buf, NULL, 10); | ||
439 | short block = (value < 0x40) ? block0 : block1; | ||
440 | value &= 0x3f; | ||
441 | location[0] = block >> 7; | ||
442 | location[1] = value | (block & 0x7f); | ||
443 | } | ||
444 | |||
445 | /* | ||
446 | Send command to store channel/effects setup/amp setup to PODxt Pro. | ||
447 | */ | ||
448 | static ssize_t pod_send_store_command(struct device *dev, const char *buf, size_t count, short block0, short block1) | ||
449 | { | ||
450 | struct usb_interface *interface = to_usb_interface(dev); | ||
451 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
452 | |||
453 | int size = 3 + sizeof(pod->prog_data_buf); | ||
454 | char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_STORE, size); | ||
455 | if(!sysex) return 0; | ||
456 | |||
457 | sysex[SYSEX_DATA_OFS] = 5; /* see pod_dump() */ | ||
458 | pod_resolve(buf, block0, block1, sysex + SYSEX_DATA_OFS + 1); | ||
459 | memcpy(sysex + SYSEX_DATA_OFS + 3, &pod->prog_data_buf, sizeof(pod->prog_data_buf)); | ||
460 | |||
461 | line6_send_sysex_message(&pod->line6, sysex, size); | ||
462 | kfree(sysex); | ||
463 | /* needs some delay here on AMD64 platform */ | ||
464 | return count; | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | Send command to retrieve channel/effects setup/amp setup to PODxt Pro. | ||
469 | */ | ||
470 | static ssize_t pod_send_retrieve_command(struct device *dev, const char *buf, size_t count, short block0, short block1) | ||
471 | { | ||
472 | struct usb_interface *interface = to_usb_interface(dev); | ||
473 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
474 | |||
475 | int size = 4; | ||
476 | char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_DUMPMEM, size); | ||
477 | if(!sysex) return 0; | ||
478 | |||
479 | pod_resolve(buf, block0, block1, sysex + SYSEX_DATA_OFS); | ||
480 | sysex[SYSEX_DATA_OFS + 2] = 0; | ||
481 | sysex[SYSEX_DATA_OFS + 3] = 0; | ||
482 | line6_dump_started(&pod->dumpreq, POD_DUMP_MEMORY); | ||
483 | |||
484 | if(line6_send_sysex_message(&pod->line6, sysex, size) < size) | ||
485 | line6_dump_finished(&pod->dumpreq); | ||
486 | |||
487 | kfree(sysex); | ||
488 | /* needs some delay here on AMD64 platform */ | ||
489 | return count; | ||
490 | } | ||
491 | |||
492 | /* | ||
493 | Generic get name function. | ||
494 | */ | ||
495 | static ssize_t get_name_generic(struct usb_line6_pod *pod, const char *str, char *buf) | ||
496 | { | ||
497 | int length = 0; | ||
498 | const char *p1; | ||
499 | char *p2; | ||
500 | char *last_non_space = buf; | ||
501 | |||
502 | int retval = line6_wait_dump(&pod->dumpreq, 0); | ||
503 | if(retval < 0) return retval; | ||
504 | |||
505 | for(p1 = str, p2 = buf; *p1; ++p1, ++p2) { | ||
506 | *p2 = *p1; | ||
507 | if(*p2 != ' ') last_non_space = p2; | ||
508 | if(++length == POD_NAME_LENGTH) break; | ||
509 | } | ||
510 | |||
511 | *(last_non_space + 1) = '\n'; | ||
512 | return last_non_space - buf + 2; | ||
513 | } | ||
514 | |||
515 | /* | ||
516 | "read" request on "channel" special file. | ||
517 | */ | ||
518 | static ssize_t pod_get_channel(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
519 | { | ||
520 | struct usb_interface *interface = to_usb_interface(dev); | ||
521 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
522 | return sprintf(buf, "%d\n", pod->channel_num); | ||
523 | } | ||
524 | |||
525 | /* | ||
526 | "write" request on "channel" special file. | ||
527 | */ | ||
528 | static ssize_t pod_set_channel(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
529 | { | ||
530 | struct usb_interface *interface = to_usb_interface(dev); | ||
531 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
532 | int value = simple_strtoul(buf, NULL, 10); | ||
533 | pod_send_channel(pod, value); | ||
534 | return count; | ||
535 | } | ||
536 | |||
537 | /* | ||
538 | "read" request on "name" special file. | ||
539 | */ | ||
540 | static ssize_t pod_get_name(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
541 | { | ||
542 | struct usb_interface *interface = to_usb_interface(dev); | ||
543 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
544 | return get_name_generic(pod, pod->prog_data.header + POD_NAME_OFFSET, buf); | ||
545 | } | ||
546 | |||
547 | /* | ||
548 | "read" request on "name" special file. | ||
549 | */ | ||
550 | static ssize_t pod_get_name_buf(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
551 | { | ||
552 | struct usb_interface *interface = to_usb_interface(dev); | ||
553 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
554 | return get_name_generic(pod, pod->prog_data_buf.header + POD_NAME_OFFSET, buf); | ||
555 | } | ||
556 | |||
557 | /* | ||
558 | "read" request on "dump" special file. | ||
559 | */ | ||
560 | static ssize_t pod_get_dump(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
561 | { | ||
562 | struct usb_interface *interface = to_usb_interface(dev); | ||
563 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
564 | int retval = line6_wait_dump(&pod->dumpreq, 0); | ||
565 | if(retval < 0) return retval; | ||
566 | memcpy(buf, &pod->prog_data, sizeof(pod->prog_data)); | ||
567 | return sizeof(pod->prog_data); | ||
568 | } | ||
569 | |||
570 | /* | ||
571 | "write" request on "dump" special file. | ||
572 | */ | ||
573 | static ssize_t pod_set_dump(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
574 | { | ||
575 | struct usb_interface *interface = to_usb_interface(dev); | ||
576 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
577 | |||
578 | if(count != sizeof(pod->prog_data)) { | ||
579 | dev_err(pod->line6.ifcdev, | ||
580 | "data block must be exactly %d bytes\n", | ||
581 | (int)sizeof(pod->prog_data)); | ||
582 | return -EINVAL; | ||
583 | } | ||
584 | |||
585 | pod_dump(pod, buf); | ||
586 | return sizeof(pod->prog_data); | ||
587 | } | ||
588 | |||
589 | /* | ||
590 | Request system parameter. | ||
591 | @param tuner non-zero, if code refers to a tuner parameter | ||
592 | */ | ||
593 | static ssize_t pod_get_system_param(struct usb_line6_pod *pod, char *buf, int code, struct ValueWait *param, int tuner, int sign) | ||
594 | { | ||
595 | char *sysex; | ||
596 | int value; | ||
597 | static const int size = 1; | ||
598 | int retval = 0; | ||
599 | DECLARE_WAITQUEUE(wait, current); | ||
600 | |||
601 | if(((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner) | ||
602 | return -ENODEV; | ||
603 | |||
604 | /* send value request to tuner: */ | ||
605 | param->value = POD_system_invalid; | ||
606 | sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEMREQ, size); | ||
607 | if(!sysex) return 0; | ||
608 | sysex[SYSEX_DATA_OFS] = code; | ||
609 | line6_send_sysex_message(&pod->line6, sysex, size); | ||
610 | kfree(sysex); | ||
611 | |||
612 | /* wait for tuner to respond: */ | ||
613 | add_wait_queue(¶m->wait, &wait); | ||
614 | current->state = TASK_INTERRUPTIBLE; | ||
615 | |||
616 | while(param->value == POD_system_invalid) { | ||
617 | if(signal_pending(current)) { | ||
618 | retval = -ERESTARTSYS; | ||
619 | break; | ||
620 | } | ||
621 | else | ||
622 | schedule(); | ||
623 | } | ||
624 | |||
625 | current->state = TASK_RUNNING; | ||
626 | remove_wait_queue(¶m->wait, &wait); | ||
627 | |||
628 | if(retval < 0) | ||
629 | return retval; | ||
630 | |||
631 | value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value; | ||
632 | return sprintf(buf, "%d\n", value); | ||
633 | } | ||
634 | |||
635 | /* | ||
636 | Send system parameter. | ||
637 | @param tuner non-zero, if code refers to a tuner parameter | ||
638 | */ | ||
639 | static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf, int count, int code, unsigned short mask, int tuner) | ||
640 | { | ||
641 | char *sysex; | ||
642 | static const int size = 5; | ||
643 | unsigned short value; | ||
644 | |||
645 | if(((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner) | ||
646 | return -EINVAL; | ||
647 | |||
648 | /* send value to tuner: */ | ||
649 | sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); | ||
650 | if(!sysex) return 0; | ||
651 | value = simple_strtoul(buf, NULL, 10) & mask; | ||
652 | sysex[SYSEX_DATA_OFS] = code; | ||
653 | sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; | ||
654 | sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; | ||
655 | sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f; | ||
656 | sysex[SYSEX_DATA_OFS + 4] = (value ) & 0x0f; | ||
657 | line6_send_sysex_message(&pod->line6, sysex, size); | ||
658 | kfree(sysex); | ||
659 | return count; | ||
660 | } | ||
661 | |||
662 | /* | ||
663 | "read" request on "dump_buf" special file. | ||
664 | */ | ||
665 | static ssize_t pod_get_dump_buf(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
666 | { | ||
667 | struct usb_interface *interface = to_usb_interface(dev); | ||
668 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
669 | int retval = line6_wait_dump(&pod->dumpreq, 0); | ||
670 | if(retval < 0) return retval; | ||
671 | memcpy(buf, &pod->prog_data_buf, sizeof(pod->prog_data_buf)); | ||
672 | return sizeof(pod->prog_data_buf); | ||
673 | } | ||
674 | |||
675 | /* | ||
676 | "write" request on "dump_buf" special file. | ||
677 | */ | ||
678 | static ssize_t pod_set_dump_buf(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
679 | { | ||
680 | struct usb_interface *interface = to_usb_interface(dev); | ||
681 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
682 | |||
683 | if(count != sizeof(pod->prog_data)) { | ||
684 | dev_err(pod->line6.ifcdev, | ||
685 | "data block must be exactly %d bytes\n", | ||
686 | (int)sizeof(pod->prog_data)); | ||
687 | return -EINVAL; | ||
688 | } | ||
689 | |||
690 | memcpy(&pod->prog_data_buf, buf, sizeof(pod->prog_data)); | ||
691 | return sizeof(pod->prog_data); | ||
692 | } | ||
693 | |||
694 | /* | ||
695 | "write" request on "finish" special file. | ||
696 | */ | ||
697 | static ssize_t pod_set_finish(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
698 | { | ||
699 | struct usb_interface *interface = to_usb_interface(dev); | ||
700 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
701 | int size = 0; | ||
702 | char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_FINISH, size); | ||
703 | if(!sysex) return 0; | ||
704 | line6_send_sysex_message(&pod->line6, sysex, size); | ||
705 | kfree(sysex); | ||
706 | return count; | ||
707 | } | ||
708 | |||
709 | /* | ||
710 | "write" request on "store_channel" special file. | ||
711 | */ | ||
712 | static ssize_t pod_set_store_channel(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
713 | { | ||
714 | return pod_send_store_command(dev, buf, count, 0x0000, 0x00c0); | ||
715 | } | ||
716 | |||
717 | /* | ||
718 | "write" request on "store_effects_setup" special file. | ||
719 | */ | ||
720 | static ssize_t pod_set_store_effects_setup(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
721 | { | ||
722 | return pod_send_store_command(dev, buf, count, 0x0080, 0x0080); | ||
723 | } | ||
724 | |||
725 | /* | ||
726 | "write" request on "store_amp_setup" special file. | ||
727 | */ | ||
728 | static ssize_t pod_set_store_amp_setup(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
729 | { | ||
730 | return pod_send_store_command(dev, buf, count, 0x0040, 0x0100); | ||
731 | } | ||
732 | |||
733 | /* | ||
734 | "write" request on "retrieve_channel" special file. | ||
735 | */ | ||
736 | static ssize_t pod_set_retrieve_channel(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
737 | { | ||
738 | return pod_send_retrieve_command(dev, buf, count, 0x0000, 0x00c0); | ||
739 | } | ||
740 | |||
741 | /* | ||
742 | "write" request on "retrieve_effects_setup" special file. | ||
743 | */ | ||
744 | static ssize_t pod_set_retrieve_effects_setup(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
745 | { | ||
746 | return pod_send_retrieve_command(dev, buf, count, 0x0080, 0x0080); | ||
747 | } | ||
748 | |||
749 | /* | ||
750 | "write" request on "retrieve_amp_setup" special file. | ||
751 | */ | ||
752 | static ssize_t pod_set_retrieve_amp_setup(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
753 | { | ||
754 | return pod_send_retrieve_command(dev, buf, count, 0x0040, 0x0100); | ||
755 | } | ||
756 | |||
757 | /* | ||
758 | "read" request on "dirty" special file. | ||
759 | */ | ||
760 | static ssize_t pod_get_dirty(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
761 | { | ||
762 | struct usb_interface *interface = to_usb_interface(dev); | ||
763 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
764 | buf[0] = pod->dirty ? '1' : '0'; | ||
765 | buf[1] = '\n'; | ||
766 | return 2; | ||
767 | } | ||
768 | |||
769 | /* | ||
770 | "read" request on "midi_postprocess" special file. | ||
771 | */ | ||
772 | static ssize_t pod_get_midi_postprocess(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
773 | { | ||
774 | struct usb_interface *interface = to_usb_interface(dev); | ||
775 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
776 | return sprintf(buf, "%d\n", pod->midi_postprocess); | ||
777 | } | ||
778 | |||
779 | /* | ||
780 | "write" request on "midi_postprocess" special file. | ||
781 | */ | ||
782 | static ssize_t pod_set_midi_postprocess(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
783 | { | ||
784 | struct usb_interface *interface = to_usb_interface(dev); | ||
785 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
786 | int value = simple_strtoul(buf, NULL, 10); | ||
787 | pod->midi_postprocess = value ? 1 : 0; | ||
788 | return count; | ||
789 | } | ||
790 | |||
791 | /* | ||
792 | "read" request on "serial_number" special file. | ||
793 | */ | ||
794 | static ssize_t pod_get_serial_number(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
795 | { | ||
796 | struct usb_interface *interface = to_usb_interface(dev); | ||
797 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
798 | return sprintf(buf, "%d\n", pod->serial_number); | ||
799 | } | ||
800 | |||
801 | /* | ||
802 | "read" request on "firmware_version" special file. | ||
803 | */ | ||
804 | static ssize_t pod_get_firmware_version(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
805 | { | ||
806 | struct usb_interface *interface = to_usb_interface(dev); | ||
807 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
808 | return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100, pod->firmware_version % 100); | ||
809 | } | ||
810 | |||
811 | /* | ||
812 | "read" request on "device_id" special file. | ||
813 | */ | ||
814 | static ssize_t pod_get_device_id(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
815 | { | ||
816 | struct usb_interface *interface = to_usb_interface(dev); | ||
817 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
818 | return sprintf(buf, "%d\n", pod->device_id); | ||
819 | } | ||
820 | |||
821 | /* | ||
822 | "read" request on "clip" special file. | ||
823 | */ | ||
824 | static ssize_t pod_wait_for_clip(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
825 | { | ||
826 | struct usb_interface *interface = to_usb_interface(dev); | ||
827 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
828 | int err = 0; | ||
829 | DECLARE_WAITQUEUE(wait, current); | ||
830 | pod->clipping.value = 0; | ||
831 | add_wait_queue(&pod->clipping.wait, &wait); | ||
832 | current->state = TASK_INTERRUPTIBLE; | ||
833 | |||
834 | while(pod->clipping.value == 0) { | ||
835 | if(signal_pending(current)) { | ||
836 | err = -ERESTARTSYS; | ||
837 | break; | ||
838 | } | ||
839 | else | ||
840 | schedule(); | ||
841 | } | ||
842 | |||
843 | current->state = TASK_RUNNING; | ||
844 | remove_wait_queue(&pod->clipping.wait, &wait); | ||
845 | return err; | ||
846 | } | ||
847 | |||
848 | #define POD_GET_SYSTEM_PARAM(code, tuner, sign) \ | ||
849 | static ssize_t pod_get_ ## code(struct device *dev, DEVICE_ATTRIBUTE char *buf) \ | ||
850 | { \ | ||
851 | struct usb_interface *interface = to_usb_interface(dev); \ | ||
852 | struct usb_line6_pod *pod = usb_get_intfdata(interface); \ | ||
853 | return pod_get_system_param(pod, buf, POD_ ## code, &pod->code, tuner, sign); \ | ||
854 | } | ||
855 | |||
856 | #define POD_GET_SET_SYSTEM_PARAM(code, mask, tuner, sign) \ | ||
857 | POD_GET_SYSTEM_PARAM(code, tuner, sign) \ | ||
858 | static ssize_t pod_set_ ## code(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) \ | ||
859 | { \ | ||
860 | struct usb_interface *interface = to_usb_interface(dev); \ | ||
861 | struct usb_line6_pod *pod = usb_get_intfdata(interface); \ | ||
862 | return pod_set_system_param(pod, buf, count, POD_ ## code, mask, tuner); \ | ||
863 | } | ||
864 | |||
865 | POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0, 0); | ||
866 | POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0, 0); | ||
867 | POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 1, 0); | ||
868 | POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 1, 0); | ||
869 | POD_GET_SYSTEM_PARAM(tuner_note, 1, 1); | ||
870 | POD_GET_SYSTEM_PARAM(tuner_pitch, 1, 1); | ||
871 | |||
872 | #undef GET_SET_SYSTEM_PARAM | ||
873 | #undef GET_SYSTEM_PARAM | ||
874 | |||
875 | /* POD special files: */ | ||
876 | static DEVICE_ATTR(channel, S_IWUGO | S_IRUGO, pod_get_channel, pod_set_channel); | ||
877 | static DEVICE_ATTR(clip, S_IRUGO, pod_wait_for_clip, line6_nop_write); | ||
878 | static DEVICE_ATTR(device_id, S_IRUGO, pod_get_device_id, line6_nop_write); | ||
879 | static DEVICE_ATTR(dirty, S_IRUGO, pod_get_dirty, line6_nop_write); | ||
880 | static DEVICE_ATTR(dump, S_IWUGO | S_IRUGO, pod_get_dump, pod_set_dump); | ||
881 | static DEVICE_ATTR(dump_buf, S_IWUGO | S_IRUGO, pod_get_dump_buf, pod_set_dump_buf); | ||
882 | static DEVICE_ATTR(finish, S_IWUGO, line6_nop_read, pod_set_finish); | ||
883 | static DEVICE_ATTR(firmware_version, S_IRUGO, pod_get_firmware_version, line6_nop_write); | ||
884 | static DEVICE_ATTR(midi_postprocess, S_IWUGO | S_IRUGO, pod_get_midi_postprocess, pod_set_midi_postprocess); | ||
885 | static DEVICE_ATTR(monitor_level, S_IWUGO | S_IRUGO, pod_get_monitor_level, pod_set_monitor_level); | ||
886 | static DEVICE_ATTR(name, S_IRUGO, pod_get_name, line6_nop_write); | ||
887 | static DEVICE_ATTR(name_buf, S_IRUGO, pod_get_name_buf, line6_nop_write); | ||
888 | static DEVICE_ATTR(retrieve_amp_setup, S_IWUGO, line6_nop_read, pod_set_retrieve_amp_setup); | ||
889 | static DEVICE_ATTR(retrieve_channel, S_IWUGO, line6_nop_read, pod_set_retrieve_channel); | ||
890 | static DEVICE_ATTR(retrieve_effects_setup, S_IWUGO, line6_nop_read, pod_set_retrieve_effects_setup); | ||
891 | static DEVICE_ATTR(routing, S_IWUGO | S_IRUGO, pod_get_routing, pod_set_routing); | ||
892 | static DEVICE_ATTR(serial_number, S_IRUGO, pod_get_serial_number, line6_nop_write); | ||
893 | static DEVICE_ATTR(store_amp_setup, S_IWUGO, line6_nop_read, pod_set_store_amp_setup); | ||
894 | static DEVICE_ATTR(store_channel, S_IWUGO, line6_nop_read, pod_set_store_channel); | ||
895 | static DEVICE_ATTR(store_effects_setup, S_IWUGO, line6_nop_read, pod_set_store_effects_setup); | ||
896 | static DEVICE_ATTR(tuner_freq, S_IWUGO | S_IRUGO, pod_get_tuner_freq, pod_set_tuner_freq); | ||
897 | static DEVICE_ATTR(tuner_mute, S_IWUGO | S_IRUGO, pod_get_tuner_mute, pod_set_tuner_mute); | ||
898 | static DEVICE_ATTR(tuner_note, S_IRUGO, pod_get_tuner_note, line6_nop_write); | ||
899 | static DEVICE_ATTR(tuner_pitch, S_IRUGO, pod_get_tuner_pitch, line6_nop_write); | ||
900 | |||
901 | #if CREATE_RAW_FILE | ||
902 | static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw); | ||
903 | #endif | ||
904 | |||
905 | /* | ||
906 | POD destructor. | ||
907 | */ | ||
908 | static void pod_destruct(struct usb_interface *interface) | ||
909 | { | ||
910 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
911 | struct usb_line6 *line6; | ||
912 | |||
913 | if(pod == NULL) return; | ||
914 | line6 = &pod->line6; | ||
915 | if(line6 == NULL) return; | ||
916 | line6_cleanup_audio(line6); | ||
917 | |||
918 | /* free dump request data: */ | ||
919 | line6_dumpreq_destruct(&pod->dumpreq); | ||
920 | |||
921 | if(pod->buffer_versionreq) kfree(pod->buffer_versionreq); | ||
922 | } | ||
923 | |||
924 | /* | ||
925 | Create sysfs entries. | ||
926 | */ | ||
927 | int pod_create_files2(struct device *dev) | ||
928 | { | ||
929 | int err; | ||
930 | |||
931 | CHECK_RETURN(device_create_file(dev, &dev_attr_channel)); | ||
932 | CHECK_RETURN(device_create_file(dev, &dev_attr_clip)); | ||
933 | CHECK_RETURN(device_create_file(dev, &dev_attr_device_id)); | ||
934 | CHECK_RETURN(device_create_file(dev, &dev_attr_dirty)); | ||
935 | CHECK_RETURN(device_create_file(dev, &dev_attr_dump)); | ||
936 | CHECK_RETURN(device_create_file(dev, &dev_attr_dump_buf)); | ||
937 | CHECK_RETURN(device_create_file(dev, &dev_attr_finish)); | ||
938 | CHECK_RETURN(device_create_file(dev, &dev_attr_firmware_version)); | ||
939 | CHECK_RETURN(device_create_file(dev, &dev_attr_midi_postprocess)); | ||
940 | CHECK_RETURN(device_create_file(dev, &dev_attr_monitor_level)); | ||
941 | CHECK_RETURN(device_create_file(dev, &dev_attr_name)); | ||
942 | CHECK_RETURN(device_create_file(dev, &dev_attr_name_buf)); | ||
943 | CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_amp_setup)); | ||
944 | CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_channel)); | ||
945 | CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_effects_setup)); | ||
946 | CHECK_RETURN(device_create_file(dev, &dev_attr_routing)); | ||
947 | CHECK_RETURN(device_create_file(dev, &dev_attr_serial_number)); | ||
948 | CHECK_RETURN(device_create_file(dev, &dev_attr_store_amp_setup)); | ||
949 | CHECK_RETURN(device_create_file(dev, &dev_attr_store_channel)); | ||
950 | CHECK_RETURN(device_create_file(dev, &dev_attr_store_effects_setup)); | ||
951 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_freq)); | ||
952 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_mute)); | ||
953 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_note)); | ||
954 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_pitch)); | ||
955 | |||
956 | #if CREATE_RAW_FILE | ||
957 | CHECK_RETURN(device_create_file(dev, &dev_attr_raw)); | ||
958 | #endif | ||
959 | |||
960 | return 0; | ||
961 | } | ||
962 | |||
963 | /* | ||
964 | Init POD device. | ||
965 | */ | ||
966 | int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) | ||
967 | { | ||
968 | int err; | ||
969 | struct usb_line6 *line6 = &pod->line6; | ||
970 | |||
971 | if((interface == NULL) || (pod == NULL)) return -ENODEV; | ||
972 | |||
973 | pod->channel_num = 255; | ||
974 | |||
975 | /* initialize wait queues: */ | ||
976 | init_waitqueue_head(&pod->monitor_level.wait); | ||
977 | init_waitqueue_head(&pod->routing.wait); | ||
978 | init_waitqueue_head(&pod->tuner_mute.wait); | ||
979 | init_waitqueue_head(&pod->tuner_freq.wait); | ||
980 | init_waitqueue_head(&pod->tuner_note.wait); | ||
981 | init_waitqueue_head(&pod->tuner_pitch.wait); | ||
982 | init_waitqueue_head(&pod->clipping.wait); | ||
983 | |||
984 | memset(pod->param_dirty, 0xff, sizeof(pod->param_dirty)); | ||
985 | |||
986 | /* initialize USB buffers: */ | ||
987 | err = line6_dumpreq_init(&pod->dumpreq, pod_request_channel, sizeof(pod_request_channel)); | ||
988 | |||
989 | if(err < 0) { | ||
990 | dev_err(&interface->dev, "Out of memory\n"); | ||
991 | pod_destruct(interface); | ||
992 | return -ENOMEM; | ||
993 | } | ||
994 | |||
995 | pod->buffer_versionreq = kmalloc(sizeof(pod_request_version), GFP_KERNEL); | ||
996 | |||
997 | if(pod->buffer_versionreq == NULL) { | ||
998 | dev_err(&interface->dev, "Out of memory\n"); | ||
999 | pod_destruct(interface); | ||
1000 | return -ENOMEM; | ||
1001 | } | ||
1002 | |||
1003 | memcpy(pod->buffer_versionreq, pod_request_version, sizeof(pod_request_version)); | ||
1004 | |||
1005 | /* create sysfs entries: */ | ||
1006 | if((err = pod_create_files2(&interface->dev)) < 0) { | ||
1007 | pod_destruct(interface); | ||
1008 | return err; | ||
1009 | } | ||
1010 | |||
1011 | /* initialize audio system: */ | ||
1012 | if((err = line6_init_audio(line6)) < 0) { | ||
1013 | pod_destruct(interface); | ||
1014 | return err; | ||
1015 | } | ||
1016 | |||
1017 | /* initialize MIDI subsystem: */ | ||
1018 | if((err = line6_init_midi(line6)) < 0) { | ||
1019 | pod_destruct(interface); | ||
1020 | return err; | ||
1021 | } | ||
1022 | |||
1023 | /* initialize PCM subsystem: */ | ||
1024 | if((err = line6_init_pcm(line6, &pod_pcm_properties)) < 0) { | ||
1025 | pod_destruct(interface); | ||
1026 | return err; | ||
1027 | } | ||
1028 | |||
1029 | /* register audio system: */ | ||
1030 | if((err = line6_register_audio(line6)) < 0) { | ||
1031 | pod_destruct(interface); | ||
1032 | return err; | ||
1033 | } | ||
1034 | |||
1035 | if(pod->line6.properties->capabilities & LINE6_BIT_CONTROL) { | ||
1036 | /* query some data: */ | ||
1037 | line6_startup_delayed(&pod->dumpreq, POD_STARTUP_DELAY, pod_startup_timeout, pod); | ||
1038 | line6_read_serial_number(&pod->line6, &pod->serial_number); | ||
1039 | } | ||
1040 | |||
1041 | return 0; | ||
1042 | } | ||
1043 | |||
1044 | /* | ||
1045 | POD device disconnected. | ||
1046 | */ | ||
1047 | void pod_disconnect(struct usb_interface *interface) | ||
1048 | { | ||
1049 | struct usb_line6_pod *pod; | ||
1050 | |||
1051 | if(interface == NULL) return; | ||
1052 | pod = usb_get_intfdata(interface); | ||
1053 | |||
1054 | if(pod != NULL) { | ||
1055 | struct snd_line6_pcm *line6pcm = pod->line6.line6pcm; | ||
1056 | struct device *dev = &interface->dev; | ||
1057 | |||
1058 | if(line6pcm != NULL) { | ||
1059 | unlink_wait_clear_audio_out_urbs(line6pcm); | ||
1060 | unlink_wait_clear_audio_in_urbs(line6pcm); | ||
1061 | } | ||
1062 | |||
1063 | if(dev != NULL) { | ||
1064 | /* remove sysfs entries: */ | ||
1065 | if(pod->versionreq_ok) | ||
1066 | pod_remove_files(pod->firmware_version, pod->line6.properties->device_bit, dev); | ||
1067 | |||
1068 | device_remove_file(dev, &dev_attr_channel); | ||
1069 | device_remove_file(dev, &dev_attr_clip); | ||
1070 | device_remove_file(dev, &dev_attr_device_id); | ||
1071 | device_remove_file(dev, &dev_attr_dirty); | ||
1072 | device_remove_file(dev, &dev_attr_dump); | ||
1073 | device_remove_file(dev, &dev_attr_dump_buf); | ||
1074 | device_remove_file(dev, &dev_attr_finish); | ||
1075 | device_remove_file(dev, &dev_attr_firmware_version); | ||
1076 | device_remove_file(dev, &dev_attr_midi_postprocess); | ||
1077 | device_remove_file(dev, &dev_attr_monitor_level); | ||
1078 | device_remove_file(dev, &dev_attr_name); | ||
1079 | device_remove_file(dev, &dev_attr_name_buf); | ||
1080 | device_remove_file(dev, &dev_attr_retrieve_amp_setup); | ||
1081 | device_remove_file(dev, &dev_attr_retrieve_channel); | ||
1082 | device_remove_file(dev, &dev_attr_retrieve_effects_setup); | ||
1083 | device_remove_file(dev, &dev_attr_routing); | ||
1084 | device_remove_file(dev, &dev_attr_serial_number); | ||
1085 | device_remove_file(dev, &dev_attr_store_amp_setup); | ||
1086 | device_remove_file(dev, &dev_attr_store_channel); | ||
1087 | device_remove_file(dev, &dev_attr_store_effects_setup); | ||
1088 | device_remove_file(dev, &dev_attr_tuner_freq); | ||
1089 | device_remove_file(dev, &dev_attr_tuner_mute); | ||
1090 | device_remove_file(dev, &dev_attr_tuner_note); | ||
1091 | device_remove_file(dev, &dev_attr_tuner_pitch); | ||
1092 | |||
1093 | #if CREATE_RAW_FILE | ||
1094 | device_remove_file(dev, &dev_attr_raw); | ||
1095 | #endif | ||
1096 | } | ||
1097 | } | ||
1098 | |||
1099 | pod_destruct(interface); | ||
1100 | } | ||
diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h new file mode 100644 index 000000000000..0db59484c4ba --- /dev/null +++ b/drivers/staging/line6/pod.h | |||
@@ -0,0 +1,203 @@ | |||
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 | #ifndef POD_H | ||
13 | #define POD_H | ||
14 | |||
15 | |||
16 | #include "driver.h" | ||
17 | |||
18 | #include <linux/spinlock.h> | ||
19 | #include <linux/usb.h> | ||
20 | #include <linux/wait.h> | ||
21 | #include <linux/workqueue.h> | ||
22 | |||
23 | #include <sound/core.h> | ||
24 | |||
25 | #include "dumprequest.h" | ||
26 | |||
27 | |||
28 | /* | ||
29 | PODxt Live interfaces | ||
30 | */ | ||
31 | #define PODXTLIVE_INTERFACE_POD 0 | ||
32 | #define PODXTLIVE_INTERFACE_VARIAX 1 | ||
33 | |||
34 | /* | ||
35 | Locate name in binary program dump | ||
36 | */ | ||
37 | #define POD_NAME_OFFSET 0 | ||
38 | #define POD_NAME_LENGTH 16 | ||
39 | |||
40 | /* | ||
41 | Other constants | ||
42 | */ | ||
43 | #define POD_CONTROL_SIZE 0x80 | ||
44 | #define POD_BUFSIZE_DUMPREQ 7 | ||
45 | #define POD_STARTUP_DELAY 3 | ||
46 | |||
47 | |||
48 | /** | ||
49 | Data structure for values that need to be requested explicitly. | ||
50 | This is the case for system and tuner settings. | ||
51 | */ | ||
52 | struct ValueWait | ||
53 | { | ||
54 | unsigned short value; | ||
55 | wait_queue_head_t wait; | ||
56 | }; | ||
57 | |||
58 | /** | ||
59 | Binary PodXT Pro program dump | ||
60 | */ | ||
61 | struct pod_program { | ||
62 | /** | ||
63 | Header information (including program name). | ||
64 | */ | ||
65 | unsigned char header[0x20]; | ||
66 | |||
67 | /** | ||
68 | Program parameters. | ||
69 | */ | ||
70 | unsigned char control[POD_CONTROL_SIZE]; | ||
71 | }; | ||
72 | |||
73 | struct usb_line6_pod { | ||
74 | /** | ||
75 | Generic Line6 USB data. | ||
76 | */ | ||
77 | struct usb_line6 line6; | ||
78 | |||
79 | /** | ||
80 | Dump request structure. | ||
81 | */ | ||
82 | struct line6_dump_request dumpreq; | ||
83 | |||
84 | /** | ||
85 | Current program number. | ||
86 | */ | ||
87 | unsigned char channel_num; | ||
88 | |||
89 | /** | ||
90 | Current program settings. | ||
91 | */ | ||
92 | struct pod_program prog_data; | ||
93 | |||
94 | /** | ||
95 | Buffer for data retrieved from or to be stored on PODxt Pro. | ||
96 | */ | ||
97 | struct pod_program prog_data_buf; | ||
98 | |||
99 | /** | ||
100 | Buffer for requesting version number. | ||
101 | */ | ||
102 | unsigned char *buffer_versionreq; | ||
103 | |||
104 | /** | ||
105 | Tuner mute mode. | ||
106 | */ | ||
107 | struct ValueWait tuner_mute; | ||
108 | |||
109 | /** | ||
110 | Tuner base frequency (typically 440Hz). | ||
111 | */ | ||
112 | struct ValueWait tuner_freq; | ||
113 | |||
114 | /** | ||
115 | Note received from tuner. | ||
116 | */ | ||
117 | struct ValueWait tuner_note; | ||
118 | |||
119 | /** | ||
120 | Pitch value received from tuner. | ||
121 | */ | ||
122 | struct ValueWait tuner_pitch; | ||
123 | |||
124 | /** | ||
125 | Instrument monitor level. | ||
126 | */ | ||
127 | struct ValueWait monitor_level; | ||
128 | |||
129 | /** | ||
130 | Audio routing mode. | ||
131 | 0: send processed guitar | ||
132 | 1: send clean guitar | ||
133 | 2: send clean guitar re-amp playback | ||
134 | 3: send re-amp playback | ||
135 | */ | ||
136 | struct ValueWait routing; | ||
137 | |||
138 | /** | ||
139 | Wait for audio clipping event. | ||
140 | */ | ||
141 | struct ValueWait clipping; | ||
142 | |||
143 | /** | ||
144 | Bottom-half for creation of sysfs special files. | ||
145 | */ | ||
146 | struct work_struct create_files_work; | ||
147 | |||
148 | /** | ||
149 | Dirty flags for access to parameter data. | ||
150 | */ | ||
151 | unsigned long param_dirty[POD_CONTROL_SIZE / sizeof(unsigned long)]; | ||
152 | |||
153 | /** | ||
154 | Some atomic flags. | ||
155 | */ | ||
156 | unsigned long atomic_flags; | ||
157 | |||
158 | /** | ||
159 | Counter for startup process. | ||
160 | */ | ||
161 | int startup_count; | ||
162 | |||
163 | /** | ||
164 | Serial number of device. | ||
165 | */ | ||
166 | int serial_number; | ||
167 | |||
168 | /** | ||
169 | Firmware version (x 100). | ||
170 | */ | ||
171 | int firmware_version; | ||
172 | |||
173 | /** | ||
174 | Device ID. | ||
175 | */ | ||
176 | int device_id; | ||
177 | |||
178 | /** | ||
179 | Flag to indicate modification of current program settings. | ||
180 | */ | ||
181 | char dirty; | ||
182 | |||
183 | /** | ||
184 | Flag if initial firmware version request has been successful. | ||
185 | */ | ||
186 | char versionreq_ok; | ||
187 | |||
188 | /** | ||
189 | Flag to enable MIDI postprocessing. | ||
190 | */ | ||
191 | char midi_postprocess; | ||
192 | }; | ||
193 | |||
194 | |||
195 | extern void pod_disconnect(struct usb_interface *interface); | ||
196 | extern int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod); | ||
197 | extern void pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, int length); | ||
198 | extern void pod_process_message(struct usb_line6_pod *pod); | ||
199 | extern void pod_receive_parameter(struct usb_line6_pod *pod, int param); | ||
200 | extern void pod_transmit_parameter(struct usb_line6_pod *pod, int param, int value); | ||
201 | |||
202 | |||
203 | #endif | ||
diff --git a/drivers/staging/line6/revision.h b/drivers/staging/line6/revision.h new file mode 100644 index 000000000000..b2a0a85efe69 --- /dev/null +++ b/drivers/staging/line6/revision.h | |||
@@ -0,0 +1,4 @@ | |||
1 | #ifndef DRIVER_REVISION | ||
2 | /* current subversion revision */ | ||
3 | #define DRIVER_REVISION " (revision 529)" | ||
4 | #endif | ||
diff --git a/drivers/staging/line6/toneport.c b/drivers/staging/line6/toneport.c new file mode 100644 index 000000000000..c9fe07feab69 --- /dev/null +++ b/drivers/staging/line6/toneport.c | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * Line6 Linux USB driver - 0.8.0 | ||
3 | * | ||
4 | * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) | ||
5 | * Emil Myhrman (emil.myhrman@gmail.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation, version 2. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include "driver.h" | ||
14 | |||
15 | #include "audio.h" | ||
16 | #include "capture.h" | ||
17 | #include "playback.h" | ||
18 | #include "toneport.h" | ||
19 | |||
20 | |||
21 | static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); | ||
22 | |||
23 | |||
24 | static struct snd_ratden toneport_ratden = { | ||
25 | .num_min = 44100, | ||
26 | .num_max = 44100, | ||
27 | .num_step = 1, | ||
28 | .den = 1 | ||
29 | }; | ||
30 | |||
31 | static struct line6_pcm_properties toneport_pcm_properties = { | ||
32 | .snd_line6_playback_hw = { | ||
33 | .info = (SNDRV_PCM_INFO_MMAP | | ||
34 | SNDRV_PCM_INFO_INTERLEAVED | | ||
35 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
36 | SNDRV_PCM_INFO_MMAP_VALID | | ||
37 | SNDRV_PCM_INFO_PAUSE | | ||
38 | SNDRV_PCM_INFO_SYNC_START), | ||
39 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
40 | .rates = SNDRV_PCM_RATE_KNOT, | ||
41 | .rate_min = 44100, | ||
42 | .rate_max = 44100, | ||
43 | .channels_min = 2, | ||
44 | .channels_max = 2, | ||
45 | .buffer_bytes_max = 60000, | ||
46 | .period_bytes_min = 180 * 4, | ||
47 | .period_bytes_max = 8192, | ||
48 | .periods_min = 1, | ||
49 | .periods_max = 1024 | ||
50 | }, | ||
51 | .snd_line6_capture_hw = { | ||
52 | .info = (SNDRV_PCM_INFO_MMAP | | ||
53 | SNDRV_PCM_INFO_INTERLEAVED | | ||
54 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
55 | SNDRV_PCM_INFO_MMAP_VALID | | ||
56 | SNDRV_PCM_INFO_SYNC_START), | ||
57 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
58 | .rates = SNDRV_PCM_RATE_KNOT, | ||
59 | .rate_min = 44100, | ||
60 | .rate_max = 44100, | ||
61 | .channels_min = 2, | ||
62 | .channels_max = 2, | ||
63 | .buffer_bytes_max = 60000, | ||
64 | .period_bytes_min = 188 * 4, | ||
65 | .period_bytes_max = 8192, | ||
66 | .periods_min = 1, | ||
67 | .periods_max = 1024 | ||
68 | }, | ||
69 | .snd_line6_rates = { | ||
70 | .nrats = 1, | ||
71 | .rats = &toneport_ratden | ||
72 | }, | ||
73 | .bytes_per_frame = 4 | ||
74 | }; | ||
75 | |||
76 | /* | ||
77 | For the led on Guitarport. | ||
78 | Brightness goes from 0x00 to 0x26. Set a value above this to have led blink. | ||
79 | (void cmd_0x02(byte red, byte green) | ||
80 | */ | ||
81 | static int led_red = 0x00; | ||
82 | static int led_green = 0x26; | ||
83 | |||
84 | static void toneport_update_led(struct device *dev) { | ||
85 | struct usb_interface *interface; | ||
86 | struct usb_line6_toneport *tp; | ||
87 | struct usb_line6* line6; | ||
88 | |||
89 | if ((interface = to_usb_interface(dev)) && | ||
90 | (tp = usb_get_intfdata(interface)) && | ||
91 | (line6 = &tp->line6)) | ||
92 | toneport_send_cmd(line6->usbdev, (led_red<<8)|0x0002, led_green); // for setting the LED on Guitarport | ||
93 | } | ||
94 | static ssize_t toneport_set_led_red(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) { | ||
95 | char* c; | ||
96 | led_red = simple_strtol(buf, &c, 10); | ||
97 | toneport_update_led(dev); | ||
98 | return count; | ||
99 | } | ||
100 | static ssize_t toneport_set_led_green(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) { | ||
101 | char* c; | ||
102 | led_green = simple_strtol(buf, &c, 10); | ||
103 | toneport_update_led(dev); | ||
104 | return count; | ||
105 | } | ||
106 | |||
107 | static DEVICE_ATTR(led_red, S_IWUGO | S_IRUGO, line6_nop_read, toneport_set_led_red); | ||
108 | static DEVICE_ATTR(led_green, S_IWUGO | S_IRUGO, line6_nop_read, toneport_set_led_green); | ||
109 | |||
110 | |||
111 | static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) | ||
112 | { | ||
113 | int ret; | ||
114 | ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev,0), 0x67, | ||
115 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, | ||
116 | cmd1, cmd2, 0, 0, LINE6_TIMEOUT * HZ); | ||
117 | |||
118 | if(ret < 0) { | ||
119 | err("send failed (error %d)\n", ret); | ||
120 | return ret; | ||
121 | } | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | Toneport destructor. | ||
128 | */ | ||
129 | static void toneport_destruct(struct usb_interface *interface) | ||
130 | { | ||
131 | struct usb_line6_toneport *toneport = usb_get_intfdata(interface); | ||
132 | struct usb_line6 *line6; | ||
133 | |||
134 | if(toneport == NULL) return; | ||
135 | line6 = &toneport->line6; | ||
136 | if(line6 == NULL) return; | ||
137 | line6_cleanup_audio(line6); | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | Init Toneport device. | ||
142 | */ | ||
143 | int toneport_init(struct usb_interface *interface, struct usb_line6_toneport *toneport) | ||
144 | { | ||
145 | int err, ticks; | ||
146 | struct usb_line6 *line6 = &toneport->line6; | ||
147 | struct usb_device *usbdev; | ||
148 | |||
149 | if((interface == NULL) || (toneport == NULL)) | ||
150 | return -ENODEV; | ||
151 | |||
152 | /* initialize audio system: */ | ||
153 | if((err = line6_init_audio(line6)) < 0) { | ||
154 | toneport_destruct(interface); | ||
155 | return err; | ||
156 | } | ||
157 | |||
158 | /* initialize PCM subsystem: */ | ||
159 | if((err = line6_init_pcm(line6, &toneport_pcm_properties)) < 0) { | ||
160 | toneport_destruct(interface); | ||
161 | return err; | ||
162 | } | ||
163 | |||
164 | /* register audio system: */ | ||
165 | if((err = line6_register_audio(line6)) < 0) { | ||
166 | toneport_destruct(interface); | ||
167 | return err; | ||
168 | } | ||
169 | |||
170 | usbdev = line6->usbdev; | ||
171 | line6_read_serial_number(line6, &toneport->serial_number); | ||
172 | line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); | ||
173 | |||
174 | /* sync time on device with host: */ | ||
175 | ticks = (int)get_seconds(); | ||
176 | line6_write_data(line6, 0x80c6, &ticks, 4); | ||
177 | |||
178 | /* | ||
179 | seems to work without the first two... | ||
180 | */ | ||
181 | //toneport_send_cmd(usbdev, 0x0201, 0x0002); // .. | ||
182 | //toneport_send_cmd(usbdev, 0x0801, 0x0000); // .. | ||
183 | toneport_send_cmd(usbdev, 0x0301, 0x0000); // only one that works for me; on GP, TP might be different? | ||
184 | |||
185 | if (usbdev->descriptor.idProduct!=LINE6_DEVID_GUITARPORT) { | ||
186 | CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_red)); | ||
187 | CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_green)); | ||
188 | toneport_update_led(&usbdev->dev); | ||
189 | } | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | Toneport device disconnected. | ||
196 | */ | ||
197 | void toneport_disconnect(struct usb_interface *interface) | ||
198 | { | ||
199 | struct usb_line6_toneport *toneport; | ||
200 | |||
201 | if(interface == NULL) return; | ||
202 | toneport = usb_get_intfdata(interface); | ||
203 | |||
204 | if (toneport->line6.usbdev->descriptor.idProduct != LINE6_DEVID_GUITARPORT) { | ||
205 | device_remove_file(&interface->dev, &dev_attr_led_red); | ||
206 | device_remove_file(&interface->dev, &dev_attr_led_green); | ||
207 | } | ||
208 | |||
209 | if(toneport != NULL) { | ||
210 | struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; | ||
211 | |||
212 | if(line6pcm != NULL) { | ||
213 | unlink_wait_clear_audio_out_urbs(line6pcm); | ||
214 | unlink_wait_clear_audio_in_urbs(line6pcm); | ||
215 | } | ||
216 | } | ||
217 | |||
218 | toneport_destruct(interface); | ||
219 | } | ||
diff --git a/drivers/staging/line6/toneport.h b/drivers/staging/line6/toneport.h new file mode 100644 index 000000000000..cd0b19fe7c84 --- /dev/null +++ b/drivers/staging/line6/toneport.h | |||
@@ -0,0 +1,44 @@ | |||
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 | #ifndef TONEPORT_H | ||
13 | #define TONEPORT_H | ||
14 | |||
15 | |||
16 | #include "driver.h" | ||
17 | |||
18 | #include <linux/usb.h> | ||
19 | #include <sound/core.h> | ||
20 | |||
21 | |||
22 | struct usb_line6_toneport { | ||
23 | /** | ||
24 | Generic Line6 USB data. | ||
25 | */ | ||
26 | struct usb_line6 line6; | ||
27 | |||
28 | /** | ||
29 | Serial number of device. | ||
30 | */ | ||
31 | int serial_number; | ||
32 | |||
33 | /** | ||
34 | Firmware version (x 100). | ||
35 | */ | ||
36 | int firmware_version; | ||
37 | }; | ||
38 | |||
39 | |||
40 | extern void toneport_disconnect(struct usb_interface *interface); | ||
41 | extern int toneport_init(struct usb_interface *interface, struct usb_line6_toneport *toneport); | ||
42 | |||
43 | |||
44 | #endif | ||
diff --git a/drivers/staging/line6/usbdefs.h b/drivers/staging/line6/usbdefs.h new file mode 100644 index 000000000000..cbf5d0d62a1f --- /dev/null +++ b/drivers/staging/line6/usbdefs.h | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * Line6 Linux USB driver - 0.8.0 | ||
3 | * | ||
4 | * Copyright (C) 2005-2008 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 | #ifndef USBDEFS_H | ||
13 | #define USBDEFS_H | ||
14 | |||
15 | |||
16 | #include <linux/version.h> | ||
17 | |||
18 | |||
19 | #define LINE6_VENDOR_ID 0x0e41 | ||
20 | |||
21 | #define USB_INTERVALS_PER_SECOND 1000 | ||
22 | |||
23 | /* | ||
24 | Device ids. | ||
25 | */ | ||
26 | #define LINE6_DEVID_BASSPODXT 0x4250 | ||
27 | #define LINE6_DEVID_BASSPODXTLIVE 0x4642 | ||
28 | #define LINE6_DEVID_BASSPODXTPRO 0x4252 | ||
29 | #define LINE6_DEVID_GUITARPORT 0x4750 | ||
30 | #define LINE6_DEVID_POCKETPOD 0x5051 | ||
31 | #define LINE6_DEVID_PODX3 0x414a | ||
32 | #define LINE6_DEVID_PODX3LIVE 0x414b | ||
33 | #define LINE6_DEVID_PODXT 0x5044 | ||
34 | #define LINE6_DEVID_PODXTLIVE 0x4650 | ||
35 | #define LINE6_DEVID_PODXTPRO 0x5050 | ||
36 | #define LINE6_DEVID_TONEPORT_GX 0x4147 | ||
37 | #define LINE6_DEVID_TONEPORT_UX1 0x4141 | ||
38 | #define LINE6_DEVID_TONEPORT_UX2 0x4142 | ||
39 | #define LINE6_DEVID_VARIAX 0x534d | ||
40 | |||
41 | #define LINE6_BIT_BASSPODXT (1 << 0) | ||
42 | #define LINE6_BIT_BASSPODXTLIVE (1 << 1) | ||
43 | #define LINE6_BIT_BASSPODXTPRO (1 << 2) | ||
44 | #define LINE6_BIT_GUITARPORT (1 << 3) | ||
45 | #define LINE6_BIT_POCKETPOD (1 << 4) | ||
46 | #define LINE6_BIT_PODX3 (1 << 5) | ||
47 | #define LINE6_BIT_PODX3LIVE (1 << 6) | ||
48 | #define LINE6_BIT_PODXT (1 << 7) | ||
49 | #define LINE6_BIT_PODXTLIVE (1 << 8) | ||
50 | #define LINE6_BIT_PODXTPRO (1 << 9) | ||
51 | #define LINE6_BIT_TONEPORT_GX (1 << 10) | ||
52 | #define LINE6_BIT_TONEPORT_UX1 (1 << 11) | ||
53 | #define LINE6_BIT_TONEPORT_UX2 (1 << 12) | ||
54 | #define LINE6_BIT_VARIAX (1 << 13) | ||
55 | |||
56 | #define LINE6_BITS_PRO (LINE6_BIT_BASSPODXTPRO | LINE6_BIT_PODXTPRO) | ||
57 | #define LINE6_BITS_LIVE (LINE6_BIT_BASSPODXTLIVE | LINE6_BIT_PODXTLIVE | LINE6_BIT_PODX3LIVE) | ||
58 | #define LINE6_BITS_PODXTALL (LINE6_BIT_PODXT | LINE6_BIT_PODXTLIVE | LINE6_BIT_PODXTPRO) | ||
59 | #define LINE6_BITS_BASSPODXTALL (LINE6_BIT_BASSPODXT | LINE6_BIT_BASSPODXTLIVE | LINE6_BIT_BASSPODXTPRO) | ||
60 | |||
61 | #define LINE6_BIT_CONTROL (1 << 0) /* device supports settings parameter via USB */ | ||
62 | #define LINE6_BIT_PCM (1 << 1) /* device supports PCM input/output via USB */ | ||
63 | #define LINE6_BIT_CONTROL_PCM (LINE6_BIT_CONTROL | LINE6_BIT_PCM) | ||
64 | |||
65 | #define LINE6_FALLBACK_INTERVAL 10 | ||
66 | #define LINE6_FALLBACK_MAXPACKETSIZE 16 | ||
67 | |||
68 | |||
69 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) | ||
70 | #define usb_interrupt_msg(usb_dev, pipe, data, len, actual_length, timeout) \ | ||
71 | usb_bulk_msg(usb_dev, pipe, data, len, actual_length, timeout) | ||
72 | #endif | ||
73 | |||
74 | |||
75 | #endif | ||
diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c new file mode 100644 index 000000000000..edb02a39d820 --- /dev/null +++ b/drivers/staging/line6/variax.c | |||
@@ -0,0 +1,501 @@ | |||
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 "audio.h" | ||
15 | #include "control.h" | ||
16 | #include "variax.h" | ||
17 | |||
18 | |||
19 | #define VARIAX_SYSEX_CODE 7 | ||
20 | #define VARIAX_SYSEX_PARAM 0x3b | ||
21 | #define VARIAX_SYSEX_ACTIVATE 0x2a | ||
22 | #define VARIAX_MODEL_HEADER_LENGTH 7 | ||
23 | #define VARIAX_MODEL_MESSAGE_LENGTH 199 | ||
24 | #define VARIAX_OFFSET_ACTIVATE 7 | ||
25 | |||
26 | |||
27 | static const char variax_activate[] = { | ||
28 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, | ||
29 | 0xf7 | ||
30 | }; | ||
31 | static const char variax_request_bank[] = { | ||
32 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6d, 0xf7 | ||
33 | }; | ||
34 | static const char variax_request_model1[] = { | ||
35 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, | ||
36 | 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x03, | ||
37 | 0x00, 0x00, 0x00, 0xf7 | ||
38 | }; | ||
39 | static const char variax_request_model2[] = { | ||
40 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, | ||
41 | 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x03, | ||
42 | 0x00, 0x00, 0x00, 0xf7 | ||
43 | }; | ||
44 | |||
45 | |||
46 | /* | ||
47 | Decode data transmitted by workbench. | ||
48 | */ | ||
49 | static void variax_decode(const unsigned char *raw_data, unsigned char *data, int raw_size) | ||
50 | { | ||
51 | for(; raw_size > 0; raw_size -= 6) { | ||
52 | data[2] = raw_data[0] | (raw_data[1] << 4); | ||
53 | data[1] = raw_data[2] | (raw_data[3] << 4); | ||
54 | data[0] = raw_data[4] | (raw_data[5] << 4); | ||
55 | raw_data += 6; | ||
56 | data += 3; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | static void variax_activate_timeout(unsigned long arg) | ||
61 | { | ||
62 | struct usb_line6_variax *variax = (struct usb_line6_variax *)arg; | ||
63 | variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = 1; | ||
64 | line6_send_raw_message_async(&variax->line6, variax->buffer_activate, sizeof(variax_activate)); | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | Send an asynchronous activation request after a given interval. | ||
69 | */ | ||
70 | static void variax_activate_delayed(struct usb_line6_variax *variax, int seconds) | ||
71 | { | ||
72 | variax->activate_timer.expires = jiffies + seconds * HZ; | ||
73 | variax->activate_timer.function = variax_activate_timeout; | ||
74 | variax->activate_timer.data = (unsigned long)variax; | ||
75 | add_timer(&variax->activate_timer); | ||
76 | } | ||
77 | |||
78 | static void variax_startup_timeout(unsigned long arg) | ||
79 | { | ||
80 | struct usb_line6_variax *variax = (struct usb_line6_variax *)arg; | ||
81 | |||
82 | if(variax->dumpreq.ok) | ||
83 | return; | ||
84 | |||
85 | line6_dump_request_async(&variax->dumpreq, &variax->line6, 0); | ||
86 | line6_startup_delayed(&variax->dumpreq, 1, variax_startup_timeout, variax); | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | Process a completely received message. | ||
91 | */ | ||
92 | void variax_process_message(struct usb_line6_variax *variax) | ||
93 | { | ||
94 | const unsigned char *buf = variax->line6.buffer_message; | ||
95 | |||
96 | switch(buf[0]) { | ||
97 | case LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST: | ||
98 | switch(buf[1]) { | ||
99 | case VARIAXMIDI_volume: | ||
100 | variax->volume = buf[2]; | ||
101 | break; | ||
102 | |||
103 | case VARIAXMIDI_tone: | ||
104 | variax->tone = buf[2]; | ||
105 | } | ||
106 | |||
107 | break; | ||
108 | |||
109 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE: | ||
110 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST: | ||
111 | variax->model = buf[1]; | ||
112 | line6_dump_request_async(&variax->dumpreq, &variax->line6, 0); | ||
113 | break; | ||
114 | |||
115 | case LINE6_RESET: | ||
116 | dev_info(variax->line6.ifcdev, "VARIAX reset\n"); | ||
117 | variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY); | ||
118 | break; | ||
119 | |||
120 | case LINE6_SYSEX_BEGIN: | ||
121 | if(memcmp(buf + 1, variax_request_model1 + 1, VARIAX_MODEL_HEADER_LENGTH - 1) == 0) { | ||
122 | if(variax->line6.message_length == VARIAX_MODEL_MESSAGE_LENGTH) { | ||
123 | switch(variax->dumpreq.in_progress) { | ||
124 | case VARIAX_DUMP_PASS1: | ||
125 | variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH, (unsigned char *)&variax->model_data, | ||
126 | (sizeof(variax->model_data.name) + sizeof(variax->model_data.control) / 2) * 2); | ||
127 | line6_dump_request_async(&variax->dumpreq, &variax->line6, 1); | ||
128 | line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS2); | ||
129 | break; | ||
130 | |||
131 | case VARIAX_DUMP_PASS2: | ||
132 | /* model name is transmitted twice, so skip it here: */ | ||
133 | variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH, | ||
134 | (unsigned char *)&variax->model_data.control + sizeof(variax->model_data.control) / 2, | ||
135 | sizeof(variax->model_data.control) / 2 * 2); | ||
136 | variax->dumpreq.ok = 1; | ||
137 | line6_dump_request_async(&variax->dumpreq, &variax->line6, 2); | ||
138 | line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS3); | ||
139 | } | ||
140 | } | ||
141 | else { | ||
142 | DEBUG_MESSAGES(dev_err(variax->line6.ifcdev, "illegal length %d of model data\n", variax->line6.message_length)); | ||
143 | line6_dump_finished(&variax->dumpreq); | ||
144 | } | ||
145 | } | ||
146 | else if(memcmp(buf + 1, variax_request_bank + 1, sizeof(variax_request_bank) - 2) == 0) { | ||
147 | memcpy(variax->bank, buf + sizeof(variax_request_bank) - 1, sizeof(variax->bank)); | ||
148 | variax->dumpreq.ok = 1; | ||
149 | line6_dump_finished(&variax->dumpreq); | ||
150 | } | ||
151 | |||
152 | break; | ||
153 | |||
154 | case LINE6_SYSEX_END: | ||
155 | break; | ||
156 | |||
157 | default: | ||
158 | DEBUG_MESSAGES(dev_err(variax->line6.ifcdev, "Variax: unknown message %02X\n", buf[0])); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | /* | ||
163 | "read" request on "volume" special file. | ||
164 | */ | ||
165 | static ssize_t variax_get_volume(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
166 | { | ||
167 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | ||
168 | return sprintf(buf, "%d\n", variax->volume); | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | "write" request on "volume" special file. | ||
173 | */ | ||
174 | static ssize_t variax_set_volume(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
175 | { | ||
176 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | ||
177 | int value = simple_strtoul(buf, NULL, 10); | ||
178 | |||
179 | if(line6_transmit_parameter(&variax->line6, VARIAXMIDI_volume, value) == 0) | ||
180 | variax->volume = value; | ||
181 | |||
182 | return count; | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | "read" request on "model" special file. | ||
187 | */ | ||
188 | static ssize_t variax_get_model(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
189 | { | ||
190 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | ||
191 | return sprintf(buf, "%d\n", variax->model); | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | "write" request on "model" special file. | ||
196 | */ | ||
197 | static ssize_t variax_set_model(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
198 | { | ||
199 | struct usb_line6_variax *variax = usb_get_intfdata( to_usb_interface(dev)); | ||
200 | int value = simple_strtoul(buf, NULL, 10); | ||
201 | |||
202 | if(line6_send_program(&variax->line6, value) == 0) | ||
203 | variax->model = value; | ||
204 | |||
205 | return count; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | "read" request on "active" special file. | ||
210 | */ | ||
211 | static ssize_t variax_get_active(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
212 | { | ||
213 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | ||
214 | return sprintf(buf, "%d\n", variax->buffer_activate[VARIAX_OFFSET_ACTIVATE]); | ||
215 | } | ||
216 | |||
217 | /* | ||
218 | "write" request on "active" special file. | ||
219 | */ | ||
220 | static ssize_t variax_set_active(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
221 | { | ||
222 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | ||
223 | int value = simple_strtoul(buf, NULL, 10) ? 1 : 0; | ||
224 | variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = value; | ||
225 | line6_send_raw_message_async(&variax->line6, variax->buffer_activate, sizeof(variax_activate)); | ||
226 | return count; | ||
227 | } | ||
228 | |||
229 | /* | ||
230 | "read" request on "tone" special file. | ||
231 | */ | ||
232 | static ssize_t variax_get_tone(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
233 | { | ||
234 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | ||
235 | return sprintf(buf, "%d\n", variax->tone); | ||
236 | } | ||
237 | |||
238 | /* | ||
239 | "write" request on "tone" special file. | ||
240 | */ | ||
241 | static ssize_t variax_set_tone(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
242 | { | ||
243 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | ||
244 | int value = simple_strtoul(buf, NULL, 10); | ||
245 | |||
246 | if(line6_transmit_parameter(&variax->line6, VARIAXMIDI_tone, value) == 0) | ||
247 | variax->tone = value; | ||
248 | |||
249 | return count; | ||
250 | } | ||
251 | |||
252 | static ssize_t get_string(char *buf, const char *data, int length) | ||
253 | { | ||
254 | int i; | ||
255 | memcpy(buf, data, length); | ||
256 | |||
257 | for(i = length; i--;) { | ||
258 | char c = buf[i]; | ||
259 | |||
260 | if((c != 0) && (c != ' ')) | ||
261 | break; | ||
262 | } | ||
263 | |||
264 | buf[i + 1] = '\n'; | ||
265 | return i + 2; | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | "read" request on "name" special file. | ||
270 | */ | ||
271 | static ssize_t variax_get_name(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
272 | { | ||
273 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | ||
274 | line6_wait_dump(&variax->dumpreq, 0); | ||
275 | return get_string(buf, variax->model_data.name, sizeof(variax->model_data.name)); | ||
276 | } | ||
277 | |||
278 | /* | ||
279 | "read" request on "bank" special file. | ||
280 | */ | ||
281 | static ssize_t variax_get_bank(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
282 | { | ||
283 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | ||
284 | line6_wait_dump(&variax->dumpreq, 0); | ||
285 | return get_string(buf, variax->bank, sizeof(variax->bank)); | ||
286 | } | ||
287 | |||
288 | /* | ||
289 | "read" request on "dump" special file. | ||
290 | */ | ||
291 | static ssize_t variax_get_dump(struct device *dev, DEVICE_ATTRIBUTE char *buf) | ||
292 | { | ||
293 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | ||
294 | int retval; | ||
295 | retval = line6_wait_dump(&variax->dumpreq, 0); | ||
296 | if(retval < 0) return retval; | ||
297 | memcpy(buf, &variax->model_data.control, sizeof(variax->model_data.control)); | ||
298 | return sizeof(variax->model_data.control); | ||
299 | } | ||
300 | |||
301 | #if CREATE_RAW_FILE | ||
302 | |||
303 | /* | ||
304 | "write" request on "raw" special file. | ||
305 | */ | ||
306 | static ssize_t variax_set_raw2(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) | ||
307 | { | ||
308 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | ||
309 | int size; | ||
310 | int i; | ||
311 | char *sysex; | ||
312 | |||
313 | count -= count % 3; | ||
314 | size = count * 2; | ||
315 | sysex = variax_alloc_sysex_buffer(variax, VARIAX_SYSEX_PARAM, size); | ||
316 | |||
317 | if(!sysex) | ||
318 | return 0; | ||
319 | |||
320 | for(i = 0; i < count; i += 3) { | ||
321 | const unsigned char *p1 = buf + i; | ||
322 | char *p2 = sysex + SYSEX_DATA_OFS + i * 2; | ||
323 | p2[0] = p1[2] & 0x0f; | ||
324 | p2[1] = p1[2] >> 4; | ||
325 | p2[2] = p1[1] & 0x0f; | ||
326 | p2[3] = p1[1] >> 4; | ||
327 | p2[4] = p1[0] & 0x0f; | ||
328 | p2[5] = p1[0] >> 4; | ||
329 | } | ||
330 | |||
331 | line6_send_sysex_message(&variax->line6, sysex, size); | ||
332 | kfree(sysex); | ||
333 | return count; | ||
334 | } | ||
335 | |||
336 | #endif | ||
337 | |||
338 | /* Variax workbench special files: */ | ||
339 | static DEVICE_ATTR(model, S_IWUGO | S_IRUGO, variax_get_model, variax_set_model); | ||
340 | static DEVICE_ATTR(volume, S_IWUGO | S_IRUGO, variax_get_volume, variax_set_volume); | ||
341 | static DEVICE_ATTR(tone, S_IWUGO | S_IRUGO, variax_get_tone, variax_set_tone); | ||
342 | static DEVICE_ATTR(name, S_IRUGO, variax_get_name, line6_nop_write); | ||
343 | static DEVICE_ATTR(bank, S_IRUGO, variax_get_bank, line6_nop_write); | ||
344 | static DEVICE_ATTR(dump, S_IRUGO, variax_get_dump, line6_nop_write); | ||
345 | static DEVICE_ATTR(active, S_IWUGO | S_IRUGO, variax_get_active, variax_set_active); | ||
346 | |||
347 | #if CREATE_RAW_FILE | ||
348 | static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw); | ||
349 | static DEVICE_ATTR(raw2, S_IWUGO, line6_nop_read, variax_set_raw2); | ||
350 | #endif | ||
351 | |||
352 | |||
353 | /* | ||
354 | Variax destructor. | ||
355 | */ | ||
356 | static void variax_destruct(struct usb_interface *interface) | ||
357 | { | ||
358 | struct usb_line6_variax *variax = usb_get_intfdata(interface); | ||
359 | struct usb_line6 *line6; | ||
360 | |||
361 | if(variax == NULL) return; | ||
362 | line6 = &variax->line6; | ||
363 | if(line6 == NULL) return; | ||
364 | line6_cleanup_audio(line6); | ||
365 | |||
366 | /* free dump request data: */ | ||
367 | line6_dumpreq_destructbuf(&variax->dumpreq, 2); | ||
368 | line6_dumpreq_destructbuf(&variax->dumpreq, 1); | ||
369 | line6_dumpreq_destruct(&variax->dumpreq); | ||
370 | |||
371 | if(variax->buffer_activate) kfree(variax->buffer_activate); | ||
372 | del_timer_sync(&variax->activate_timer); | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | Create sysfs entries. | ||
377 | */ | ||
378 | int variax_create_files2(struct device *dev) | ||
379 | { | ||
380 | int err; | ||
381 | CHECK_RETURN(device_create_file(dev, &dev_attr_model)); | ||
382 | CHECK_RETURN(device_create_file(dev, &dev_attr_volume)); | ||
383 | CHECK_RETURN(device_create_file(dev, &dev_attr_tone)); | ||
384 | CHECK_RETURN(device_create_file(dev, &dev_attr_name)); | ||
385 | CHECK_RETURN(device_create_file(dev, &dev_attr_bank)); | ||
386 | CHECK_RETURN(device_create_file(dev, &dev_attr_dump)); | ||
387 | CHECK_RETURN(device_create_file(dev, &dev_attr_active)); | ||
388 | #if CREATE_RAW_FILE | ||
389 | CHECK_RETURN(device_create_file(dev, &dev_attr_raw)); | ||
390 | CHECK_RETURN(device_create_file(dev, &dev_attr_raw2)); | ||
391 | #endif | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | /* | ||
396 | Init workbench device. | ||
397 | */ | ||
398 | int variax_init(struct usb_interface *interface, struct usb_line6_variax *variax) | ||
399 | { | ||
400 | int err; | ||
401 | |||
402 | if((interface == NULL) || (variax == NULL)) return -ENODEV; | ||
403 | |||
404 | /* initialize USB buffers: */ | ||
405 | err = line6_dumpreq_init(&variax->dumpreq, variax_request_model1, sizeof(variax_request_model1)); | ||
406 | |||
407 | if(err < 0) { | ||
408 | dev_err(&interface->dev, "Out of memory\n"); | ||
409 | variax_destruct(interface); | ||
410 | return err; | ||
411 | } | ||
412 | |||
413 | err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_model2, sizeof(variax_request_model2), 1); | ||
414 | |||
415 | if(err < 0) { | ||
416 | dev_err(&interface->dev, "Out of memory\n"); | ||
417 | variax_destruct(interface); | ||
418 | return err; | ||
419 | } | ||
420 | |||
421 | err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_bank, sizeof(variax_request_bank), 2); | ||
422 | |||
423 | if(err < 0) { | ||
424 | dev_err(&interface->dev, "Out of memory\n"); | ||
425 | variax_destruct(interface); | ||
426 | return err; | ||
427 | } | ||
428 | |||
429 | variax->buffer_activate = kmalloc(sizeof(variax_activate), GFP_KERNEL); | ||
430 | |||
431 | if(variax->buffer_activate == NULL) { | ||
432 | dev_err(&interface->dev, "Out of memory\n"); | ||
433 | variax_destruct(interface); | ||
434 | return -ENOMEM; | ||
435 | } | ||
436 | |||
437 | memcpy(variax->buffer_activate, variax_activate, sizeof(variax_activate)); | ||
438 | init_timer(&variax->activate_timer); | ||
439 | |||
440 | /* create sysfs entries: */ | ||
441 | if((err = variax_create_files(0, 0, &interface->dev)) < 0) { | ||
442 | variax_destruct(interface); | ||
443 | return err; | ||
444 | } | ||
445 | |||
446 | if((err = variax_create_files2(&interface->dev)) < 0) { | ||
447 | variax_destruct(interface); | ||
448 | return err; | ||
449 | } | ||
450 | |||
451 | /* initialize audio system: */ | ||
452 | if((err = line6_init_audio(&variax->line6)) < 0) { | ||
453 | variax_destruct(interface); | ||
454 | return err; | ||
455 | } | ||
456 | |||
457 | /* initialize MIDI subsystem: */ | ||
458 | if((err = line6_init_midi(&variax->line6)) < 0) { | ||
459 | variax_destruct(interface); | ||
460 | return err; | ||
461 | } | ||
462 | |||
463 | /* register audio system: */ | ||
464 | if((err = line6_register_audio(&variax->line6)) < 0) { | ||
465 | variax_destruct(interface); | ||
466 | return err; | ||
467 | } | ||
468 | |||
469 | variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY); | ||
470 | line6_startup_delayed(&variax->dumpreq, VARIAX_STARTUP_DELAY, variax_startup_timeout, variax); | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | /* | ||
475 | Workbench device disconnected. | ||
476 | */ | ||
477 | void variax_disconnect(struct usb_interface *interface) | ||
478 | { | ||
479 | struct device *dev; | ||
480 | |||
481 | if(interface == NULL) return; | ||
482 | dev = &interface->dev; | ||
483 | |||
484 | if(dev != NULL) { | ||
485 | /* remove sysfs entries: */ | ||
486 | variax_remove_files(0, 0, dev); | ||
487 | device_remove_file(dev, &dev_attr_model); | ||
488 | device_remove_file(dev, &dev_attr_volume); | ||
489 | device_remove_file(dev, &dev_attr_tone); | ||
490 | device_remove_file(dev, &dev_attr_name); | ||
491 | device_remove_file(dev, &dev_attr_bank); | ||
492 | device_remove_file(dev, &dev_attr_dump); | ||
493 | device_remove_file(dev, &dev_attr_active); | ||
494 | #if CREATE_RAW_FILE | ||
495 | device_remove_file(dev, &dev_attr_raw); | ||
496 | device_remove_file(dev, &dev_attr_raw2); | ||
497 | #endif | ||
498 | } | ||
499 | |||
500 | variax_destruct(interface); | ||
501 | } | ||
diff --git a/drivers/staging/line6/variax.h b/drivers/staging/line6/variax.h new file mode 100644 index 000000000000..286df248c816 --- /dev/null +++ b/drivers/staging/line6/variax.h | |||
@@ -0,0 +1,107 @@ | |||
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 | #ifndef VARIAX_H | ||
13 | #define VARIAX_H | ||
14 | |||
15 | |||
16 | #include "driver.h" | ||
17 | |||
18 | #include <linux/spinlock.h> | ||
19 | #include <linux/usb.h> | ||
20 | #include <linux/wait.h> | ||
21 | |||
22 | #include <sound/core.h> | ||
23 | |||
24 | #include "dumprequest.h" | ||
25 | |||
26 | |||
27 | #define VARIAX_ACTIVATE_DELAY 10 | ||
28 | #define VARIAX_STARTUP_DELAY 3 | ||
29 | |||
30 | |||
31 | enum { | ||
32 | VARIAX_DUMP_PASS1 = LINE6_DUMP_CURRENT, | ||
33 | VARIAX_DUMP_PASS2, | ||
34 | VARIAX_DUMP_PASS3 | ||
35 | }; | ||
36 | |||
37 | |||
38 | /** | ||
39 | Binary Variax model dump | ||
40 | */ | ||
41 | struct variax_model { | ||
42 | /** | ||
43 | Header information (including program name). | ||
44 | */ | ||
45 | unsigned char name[18]; | ||
46 | |||
47 | /** | ||
48 | Model parameters. | ||
49 | */ | ||
50 | unsigned char control[78 * 2]; | ||
51 | }; | ||
52 | |||
53 | struct usb_line6_variax { | ||
54 | /** | ||
55 | Generic Line6 USB data. | ||
56 | */ | ||
57 | struct usb_line6 line6; | ||
58 | |||
59 | /** | ||
60 | Dump request structure. | ||
61 | Append two extra buffers for 3-pass data query. | ||
62 | */ | ||
63 | struct line6_dump_request dumpreq; struct line6_dump_reqbuf extrabuf[2]; | ||
64 | |||
65 | /** | ||
66 | Buffer for activation code. | ||
67 | */ | ||
68 | unsigned char *buffer_activate; | ||
69 | |||
70 | /** | ||
71 | Model number. | ||
72 | */ | ||
73 | int model; | ||
74 | |||
75 | /** | ||
76 | Current model settings. | ||
77 | */ | ||
78 | struct variax_model model_data; | ||
79 | |||
80 | /** | ||
81 | Name of current model bank. | ||
82 | */ | ||
83 | unsigned char bank[18]; | ||
84 | |||
85 | /** | ||
86 | Position of volume dial. | ||
87 | */ | ||
88 | int volume; | ||
89 | |||
90 | /** | ||
91 | Position of tone control dial. | ||
92 | */ | ||
93 | int tone; | ||
94 | |||
95 | /** | ||
96 | Timer for delayed activation request. | ||
97 | */ | ||
98 | struct timer_list activate_timer; | ||
99 | }; | ||
100 | |||
101 | |||
102 | extern void variax_disconnect(struct usb_interface *interface); | ||
103 | extern int variax_init(struct usb_interface *interface, struct usb_line6_variax *variax); | ||
104 | extern void variax_process_message(struct usb_line6_variax *variax); | ||
105 | |||
106 | |||
107 | #endif | ||