Commit | Line | Data |
---|---|---|
705ececd | 1 | /* |
e1a164d7 | 2 | * Line6 Linux USB driver - 0.9.1beta |
705ececd | 3 | * |
1027f476 | 4 | * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) |
705ececd MG |
5 | * |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License as | |
8 | * published by the Free Software Foundation, version 2. | |
9 | * | |
10 | */ | |
11 | ||
5a0e3ad6 | 12 | #include <linux/slab.h> |
1027f476 MG |
13 | #include <linux/wait.h> |
14 | #include <sound/control.h> | |
5a0e3ad6 | 15 | |
705ececd MG |
16 | #include "audio.h" |
17 | #include "capture.h" | |
18 | #include "control.h" | |
1027f476 | 19 | #include "driver.h" |
705ececd MG |
20 | #include "playback.h" |
21 | #include "pod.h" | |
22 | ||
705ececd | 23 | #define POD_SYSEX_CODE 3 |
e1a164d7 | 24 | #define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ |
705ececd | 25 | |
e1a164d7 | 26 | /* *INDENT-OFF* */ |
705ececd MG |
27 | |
28 | enum { | |
705ececd MG |
29 | POD_SYSEX_SAVE = 0x24, |
30 | POD_SYSEX_SYSTEM = 0x56, | |
31 | POD_SYSEX_SYSTEMREQ = 0x57, | |
32 | /* POD_SYSEX_UPDATE = 0x6c, */ /* software update! */ | |
33 | POD_SYSEX_STORE = 0x71, | |
34 | POD_SYSEX_FINISH = 0x72, | |
35 | POD_SYSEX_DUMPMEM = 0x73, | |
36 | POD_SYSEX_DUMP = 0x74, | |
37 | POD_SYSEX_DUMPREQ = 0x75 | |
38 | /* POD_SYSEX_DUMPMEM2 = 0x76 */ /* dumps entire internal memory of PODxt Pro */ | |
39 | }; | |
40 | ||
41 | enum { | |
42 | POD_monitor_level = 0x04, | |
43 | POD_routing = 0x05, | |
44 | POD_tuner_mute = 0x13, | |
45 | POD_tuner_freq = 0x15, | |
46 | POD_tuner_note = 0x16, | |
47 | POD_tuner_pitch = 0x17, | |
1027f476 | 48 | POD_system_invalid = 0x10000 |
705ececd MG |
49 | }; |
50 | ||
e1a164d7 MG |
51 | /* *INDENT-ON* */ |
52 | ||
705ececd MG |
53 | enum { |
54 | POD_DUMP_MEMORY = 2 | |
55 | }; | |
56 | ||
57 | enum { | |
58 | POD_BUSY_READ, | |
59 | POD_BUSY_WRITE, | |
60 | POD_CHANNEL_DIRTY, | |
61 | POD_SAVE_PRESSED, | |
62 | POD_BUSY_MIDISEND | |
63 | }; | |
64 | ||
705ececd MG |
65 | static struct snd_ratden pod_ratden = { |
66 | .num_min = 78125, | |
67 | .num_max = 78125, | |
68 | .num_step = 1, | |
69 | .den = 2 | |
70 | }; | |
71 | ||
72 | static struct line6_pcm_properties pod_pcm_properties = { | |
1027f476 | 73 | .snd_line6_playback_hw = { |
e1a164d7 MG |
74 | .info = (SNDRV_PCM_INFO_MMAP | |
75 | SNDRV_PCM_INFO_INTERLEAVED | | |
76 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
77 | SNDRV_PCM_INFO_MMAP_VALID | | |
78 | SNDRV_PCM_INFO_PAUSE | | |
1027f476 | 79 | #ifdef CONFIG_PM |
e1a164d7 | 80 | SNDRV_PCM_INFO_RESUME | |
1027f476 | 81 | #endif |
e1a164d7 MG |
82 | SNDRV_PCM_INFO_SYNC_START), |
83 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | |
84 | .rates = SNDRV_PCM_RATE_KNOT, | |
85 | .rate_min = 39062, | |
86 | .rate_max = 39063, | |
87 | .channels_min = 2, | |
88 | .channels_max = 2, | |
89 | .buffer_bytes_max = 60000, | |
90 | .period_bytes_min = 64, | |
91 | .period_bytes_max = 8192, | |
92 | .periods_min = 1, | |
93 | .periods_max = 1024}, | |
1027f476 | 94 | .snd_line6_capture_hw = { |
e1a164d7 MG |
95 | .info = (SNDRV_PCM_INFO_MMAP | |
96 | SNDRV_PCM_INFO_INTERLEAVED | | |
97 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
98 | SNDRV_PCM_INFO_MMAP_VALID | | |
1027f476 | 99 | #ifdef CONFIG_PM |
e1a164d7 | 100 | SNDRV_PCM_INFO_RESUME | |
1027f476 | 101 | #endif |
e1a164d7 MG |
102 | SNDRV_PCM_INFO_SYNC_START), |
103 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | |
104 | .rates = SNDRV_PCM_RATE_KNOT, | |
105 | .rate_min = 39062, | |
106 | .rate_max = 39063, | |
107 | .channels_min = 2, | |
108 | .channels_max = 2, | |
109 | .buffer_bytes_max = 60000, | |
110 | .period_bytes_min = 64, | |
111 | .period_bytes_max = 8192, | |
112 | .periods_min = 1, | |
113 | .periods_max = 1024}, | |
705ececd | 114 | .snd_line6_rates = { |
e1a164d7 MG |
115 | .nrats = 1, |
116 | .rats = &pod_ratden}, | |
705ececd MG |
117 | .bytes_per_frame = POD_BYTES_PER_FRAME |
118 | }; | |
119 | ||
1027f476 MG |
120 | static const char pod_request_channel[] = { |
121 | 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7 | |
122 | }; | |
123 | ||
e1a164d7 | 124 | static const char pod_version_header[] = { |
1027f476 MG |
125 | 0xf2, 0x7e, 0x7f, 0x06, 0x02 |
126 | }; | |
127 | ||
1027f476 MG |
128 | /* forward declarations: */ |
129 | static void pod_startup2(unsigned long data); | |
130 | static void pod_startup3(struct usb_line6_pod *pod); | |
131 | static void pod_startup4(struct usb_line6_pod *pod); | |
705ececd | 132 | |
e1a164d7 MG |
133 | static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, |
134 | int size) | |
705ececd | 135 | { |
e1a164d7 MG |
136 | return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, |
137 | size); | |
705ececd MG |
138 | } |
139 | ||
705ececd | 140 | /* |
b772fe9e | 141 | Store parameter value in driver memory. |
705ececd MG |
142 | */ |
143 | static void pod_store_parameter(struct usb_line6_pod *pod, int param, int value) | |
144 | { | |
145 | pod->prog_data.control[param] = value; | |
705ececd MG |
146 | } |
147 | ||
148 | /* | |
1027f476 | 149 | Handle SAVE button. |
705ececd | 150 | */ |
e1a164d7 MG |
151 | static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, |
152 | int index) | |
705ececd | 153 | { |
705ececd MG |
154 | set_bit(POD_SAVE_PRESSED, &pod->atomic_flags); |
155 | } | |
156 | ||
157 | /* | |
158 | Process a completely received message. | |
159 | */ | |
1027f476 | 160 | void line6_pod_process_message(struct usb_line6_pod *pod) |
705ececd MG |
161 | { |
162 | const unsigned char *buf = pod->line6.buffer_message; | |
163 | ||
164 | /* filter messages by type */ | |
0fdef36a | 165 | switch (buf[0] & 0xf0) { |
705ececd MG |
166 | case LINE6_PARAM_CHANGE: |
167 | case LINE6_PROGRAM_CHANGE: | |
168 | case LINE6_SYSEX_BEGIN: | |
e1a164d7 | 169 | break; /* handle these further down */ |
705ececd MG |
170 | |
171 | default: | |
e1a164d7 | 172 | return; /* ignore all others */ |
705ececd MG |
173 | } |
174 | ||
175 | /* process all remaining messages */ | |
0fdef36a | 176 | switch (buf[0]) { |
705ececd MG |
177 | case LINE6_PARAM_CHANGE | LINE6_CHANNEL_DEVICE: |
178 | pod_store_parameter(pod, buf[1], buf[2]); | |
179 | /* intentionally no break here! */ | |
180 | ||
181 | case LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST: | |
0fdef36a GKH |
182 | if ((buf[1] == POD_amp_model_setup) || |
183 | (buf[1] == POD_effect_setup)) | |
184 | /* these also affect other settings */ | |
e1a164d7 MG |
185 | line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, |
186 | LINE6_DUMP_CURRENT); | |
705ececd MG |
187 | |
188 | break; | |
189 | ||
190 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE: | |
191 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST: | |
705ececd | 192 | set_bit(POD_CHANNEL_DIRTY, &pod->atomic_flags); |
e1a164d7 MG |
193 | line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, |
194 | LINE6_DUMP_CURRENT); | |
705ececd MG |
195 | break; |
196 | ||
197 | case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE: | |
198 | case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN: | |
0fdef36a GKH |
199 | if (memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) == 0) { |
200 | switch (buf[5]) { | |
705ececd | 201 | case POD_SYSEX_DUMP: |
e1a164d7 MG |
202 | if (pod->line6.message_length == |
203 | sizeof(pod->prog_data) + 7) { | |
0fdef36a | 204 | switch (pod->dumpreq.in_progress) { |
705ececd | 205 | case LINE6_DUMP_CURRENT: |
e1a164d7 MG |
206 | memcpy(&pod->prog_data, buf + 7, |
207 | sizeof(pod->prog_data)); | |
705ececd MG |
208 | break; |
209 | ||
210 | case POD_DUMP_MEMORY: | |
e1a164d7 MG |
211 | memcpy(&pod->prog_data_buf, |
212 | buf + 7, | |
213 | sizeof | |
214 | (pod->prog_data_buf)); | |
705ececd MG |
215 | break; |
216 | ||
217 | default: | |
e00d33cb SH |
218 | dev_dbg(pod->line6.ifcdev, |
219 | "unknown dump code %02X\n", | |
220 | pod->dumpreq.in_progress); | |
705ececd MG |
221 | } |
222 | ||
223 | line6_dump_finished(&pod->dumpreq); | |
1027f476 | 224 | pod_startup3(pod); |
0fdef36a | 225 | } else |
e00d33cb SH |
226 | dev_dbg(pod->line6.ifcdev, |
227 | "wrong size of channel dump message (%d instead of %d)\n", | |
228 | pod->line6.message_length, | |
229 | (int)sizeof(pod->prog_data) + | |
230 | 7); | |
705ececd MG |
231 | |
232 | break; | |
233 | ||
e1a164d7 MG |
234 | case POD_SYSEX_SYSTEM:{ |
235 | short value = | |
236 | ((int)buf[7] << 12) | ((int)buf[8] | |
237 | << 8) | | |
238 | ((int)buf[9] << 4) | (int)buf[10]; | |
705ececd MG |
239 | |
240 | #define PROCESS_SYSTEM_PARAM(x) \ | |
241 | case POD_ ## x: \ | |
242 | pod->x.value = value; \ | |
1027f476 | 243 | wake_up(&pod->x.wait); \ |
705ececd MG |
244 | break; |
245 | ||
e1a164d7 | 246 | switch (buf[6]) { |
410cefa7 SH |
247 | case POD_monitor_level: |
248 | pod->monitor_level.value = value; | |
249 | break; | |
250 | ||
e1a164d7 MG |
251 | PROCESS_SYSTEM_PARAM(routing); |
252 | PROCESS_SYSTEM_PARAM | |
253 | (tuner_mute); | |
254 | PROCESS_SYSTEM_PARAM | |
255 | (tuner_freq); | |
256 | PROCESS_SYSTEM_PARAM | |
257 | (tuner_note); | |
258 | PROCESS_SYSTEM_PARAM | |
259 | (tuner_pitch); | |
705ececd MG |
260 | |
261 | #undef PROCESS_SYSTEM_PARAM | |
262 | ||
e1a164d7 | 263 | default: |
e00d33cb SH |
264 | dev_dbg(pod->line6.ifcdev, |
265 | "unknown tuner/system response %02X\n", | |
266 | buf[6]); | |
e1a164d7 | 267 | } |
705ececd | 268 | |
e1a164d7 MG |
269 | break; |
270 | } | |
705ececd MG |
271 | |
272 | case POD_SYSEX_FINISH: | |
273 | /* do we need to respond to this? */ | |
274 | break; | |
275 | ||
276 | case POD_SYSEX_SAVE: | |
277 | pod_save_button_pressed(pod, buf[6], buf[7]); | |
278 | break; | |
279 | ||
705ececd | 280 | case POD_SYSEX_STORE: |
e00d33cb SH |
281 | dev_dbg(pod->line6.ifcdev, |
282 | "message %02X not yet implemented\n", | |
283 | buf[5]); | |
705ececd MG |
284 | break; |
285 | ||
286 | default: | |
e00d33cb SH |
287 | dev_dbg(pod->line6.ifcdev, |
288 | "unknown sysex message %02X\n", | |
289 | buf[5]); | |
705ececd | 290 | } |
e1a164d7 MG |
291 | } else |
292 | if (memcmp | |
293 | (buf, pod_version_header, | |
294 | sizeof(pod_version_header)) == 0) { | |
295 | pod->firmware_version = | |
296 | buf[13] * 100 + buf[14] * 10 + buf[15]; | |
297 | pod->device_id = | |
298 | ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int) | |
299 | buf[10]; | |
1027f476 | 300 | pod_startup4(pod); |
0fdef36a | 301 | } else |
e00d33cb | 302 | dev_dbg(pod->line6.ifcdev, "unknown sysex header\n"); |
705ececd MG |
303 | |
304 | break; | |
305 | ||
306 | case LINE6_SYSEX_END: | |
307 | break; | |
308 | ||
309 | default: | |
e00d33cb SH |
310 | dev_dbg(pod->line6.ifcdev, "POD: unknown message %02X\n", |
311 | buf[0]); | |
705ececd MG |
312 | } |
313 | } | |
314 | ||
315 | /* | |
316 | Detect some cases that require a channel dump after sending a command to the | |
317 | device. Important notes: | |
318 | *) The actual dump request can not be sent here since we are not allowed to | |
319 | wait for the completion of the first message in this context, and sending | |
320 | the dump request before completion of the previous message leaves the POD | |
321 | in an undefined state. The dump request will be sent when the echoed | |
322 | commands are received. | |
323 | *) This method fails if a param change message is "chopped" after the first | |
324 | byte. | |
325 | */ | |
e1a164d7 MG |
326 | void line6_pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, |
327 | int length) | |
705ececd MG |
328 | { |
329 | int i; | |
330 | ||
0fdef36a | 331 | if (!pod->midi_postprocess) |
705ececd MG |
332 | return; |
333 | ||
0fdef36a GKH |
334 | for (i = 0; i < length; ++i) { |
335 | if (data[i] == (LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST)) { | |
705ececd MG |
336 | line6_invalidate_current(&pod->dumpreq); |
337 | break; | |
e1a164d7 MG |
338 | } else |
339 | if ((data[i] == (LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST)) | |
340 | && (i < length - 1)) | |
341 | if ((data[i + 1] == POD_amp_model_setup) | |
342 | || (data[i + 1] == POD_effect_setup)) { | |
705ececd MG |
343 | line6_invalidate_current(&pod->dumpreq); |
344 | break; | |
345 | } | |
346 | } | |
347 | } | |
348 | ||
705ececd MG |
349 | /* |
350 | Transmit PODxt Pro control parameter. | |
351 | */ | |
e1a164d7 | 352 | void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, |
5b9bd2ad | 353 | u8 value) |
705ececd | 354 | { |
0fdef36a | 355 | if (line6_transmit_parameter(&pod->line6, param, value) == 0) |
705ececd MG |
356 | pod_store_parameter(pod, param, value); |
357 | ||
e1a164d7 | 358 | if ((param == POD_amp_model_setup) || (param == POD_effect_setup)) /* these also affect other settings */ |
705ececd MG |
359 | line6_invalidate_current(&pod->dumpreq); |
360 | } | |
361 | ||
362 | /* | |
363 | Resolve value to memory location. | |
364 | */ | |
e1a164d7 MG |
365 | static int pod_resolve(const char *buf, short block0, short block1, |
366 | unsigned char *location) | |
705ececd | 367 | { |
1d0e834d | 368 | u8 value; |
7e4d5c13 SB |
369 | short block; |
370 | int ret; | |
371 | ||
1d0e834d | 372 | ret = kstrtou8(buf, 10, &value); |
7e4d5c13 SB |
373 | if (ret) |
374 | return ret; | |
375 | ||
376 | block = (value < 0x40) ? block0 : block1; | |
705ececd MG |
377 | value &= 0x3f; |
378 | location[0] = block >> 7; | |
379 | location[1] = value | (block & 0x7f); | |
7e4d5c13 | 380 | return 0; |
705ececd MG |
381 | } |
382 | ||
383 | /* | |
384 | Send command to store channel/effects setup/amp setup to PODxt Pro. | |
385 | */ | |
e1a164d7 MG |
386 | static ssize_t pod_send_store_command(struct device *dev, const char *buf, |
387 | size_t count, short block0, short block1) | |
705ececd MG |
388 | { |
389 | struct usb_interface *interface = to_usb_interface(dev); | |
390 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
7e4d5c13 | 391 | int ret; |
705ececd MG |
392 | int size = 3 + sizeof(pod->prog_data_buf); |
393 | char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_STORE, size); | |
7e4d5c13 | 394 | |
0fdef36a GKH |
395 | if (!sysex) |
396 | return 0; | |
705ececd | 397 | |
980688f9 SH |
398 | /* Don't know what this is good for, but PODxt Pro transmits it, so we |
399 | * also do... */ | |
400 | sysex[SYSEX_DATA_OFS] = 5; | |
7e4d5c13 SB |
401 | ret = pod_resolve(buf, block0, block1, sysex + SYSEX_DATA_OFS + 1); |
402 | if (ret) { | |
403 | kfree(sysex); | |
404 | return ret; | |
405 | } | |
406 | ||
e1a164d7 MG |
407 | memcpy(sysex + SYSEX_DATA_OFS + 3, &pod->prog_data_buf, |
408 | sizeof(pod->prog_data_buf)); | |
705ececd MG |
409 | |
410 | line6_send_sysex_message(&pod->line6, sysex, size); | |
411 | kfree(sysex); | |
412 | /* needs some delay here on AMD64 platform */ | |
413 | return count; | |
414 | } | |
415 | ||
416 | /* | |
417 | Send command to retrieve channel/effects setup/amp setup to PODxt Pro. | |
418 | */ | |
e1a164d7 MG |
419 | static ssize_t pod_send_retrieve_command(struct device *dev, const char *buf, |
420 | size_t count, short block0, | |
421 | short block1) | |
705ececd MG |
422 | { |
423 | struct usb_interface *interface = to_usb_interface(dev); | |
424 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
7e4d5c13 | 425 | int ret; |
705ececd MG |
426 | int size = 4; |
427 | char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_DUMPMEM, size); | |
0fdef36a GKH |
428 | |
429 | if (!sysex) | |
430 | return 0; | |
705ececd | 431 | |
7e4d5c13 SB |
432 | ret = pod_resolve(buf, block0, block1, sysex + SYSEX_DATA_OFS); |
433 | if (ret) { | |
434 | kfree(sysex); | |
435 | return ret; | |
436 | } | |
705ececd MG |
437 | sysex[SYSEX_DATA_OFS + 2] = 0; |
438 | sysex[SYSEX_DATA_OFS + 3] = 0; | |
439 | line6_dump_started(&pod->dumpreq, POD_DUMP_MEMORY); | |
440 | ||
0fdef36a | 441 | if (line6_send_sysex_message(&pod->line6, sysex, size) < size) |
705ececd MG |
442 | line6_dump_finished(&pod->dumpreq); |
443 | ||
444 | kfree(sysex); | |
445 | /* needs some delay here on AMD64 platform */ | |
446 | return count; | |
447 | } | |
448 | ||
449 | /* | |
450 | Generic get name function. | |
451 | */ | |
e1a164d7 MG |
452 | static ssize_t get_name_generic(struct usb_line6_pod *pod, const char *str, |
453 | char *buf) | |
705ececd MG |
454 | { |
455 | int length = 0; | |
456 | const char *p1; | |
457 | char *p2; | |
458 | char *last_non_space = buf; | |
459 | ||
1027f476 | 460 | int retval = line6_dump_wait_interruptible(&pod->dumpreq); |
0fdef36a GKH |
461 | if (retval < 0) |
462 | return retval; | |
705ececd | 463 | |
0fdef36a | 464 | for (p1 = str, p2 = buf; *p1; ++p1, ++p2) { |
705ececd | 465 | *p2 = *p1; |
0fdef36a GKH |
466 | if (*p2 != ' ') |
467 | last_non_space = p2; | |
468 | if (++length == POD_NAME_LENGTH) | |
469 | break; | |
705ececd MG |
470 | } |
471 | ||
472 | *(last_non_space + 1) = '\n'; | |
473 | return last_non_space - buf + 2; | |
474 | } | |
475 | ||
705ececd MG |
476 | /* |
477 | "read" request on "name" special file. | |
478 | */ | |
77491e52 GKH |
479 | static ssize_t pod_get_name(struct device *dev, struct device_attribute *attr, |
480 | char *buf) | |
705ececd MG |
481 | { |
482 | struct usb_interface *interface = to_usb_interface(dev); | |
483 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
e1a164d7 MG |
484 | return get_name_generic(pod, pod->prog_data.header + POD_NAME_OFFSET, |
485 | buf); | |
705ececd MG |
486 | } |
487 | ||
488 | /* | |
489 | "read" request on "name" special file. | |
490 | */ | |
77491e52 GKH |
491 | static ssize_t pod_get_name_buf(struct device *dev, |
492 | struct device_attribute *attr, char *buf) | |
705ececd MG |
493 | { |
494 | struct usb_interface *interface = to_usb_interface(dev); | |
495 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
e1a164d7 MG |
496 | return get_name_generic(pod, |
497 | pod->prog_data_buf.header + POD_NAME_OFFSET, | |
498 | buf); | |
705ececd MG |
499 | } |
500 | ||
705ececd | 501 | /* |
1027f476 MG |
502 | Identify system parameters related to the tuner. |
503 | */ | |
504 | static bool pod_is_tuner(int code) | |
505 | { | |
506 | return | |
e1a164d7 MG |
507 | (code == POD_tuner_mute) || |
508 | (code == POD_tuner_freq) || | |
509 | (code == POD_tuner_note) || (code == POD_tuner_pitch); | |
1027f476 MG |
510 | } |
511 | ||
512 | /* | |
513 | Get system parameter (as integer). | |
705ececd MG |
514 | @param tuner non-zero, if code refers to a tuner parameter |
515 | */ | |
e1a164d7 MG |
516 | static int pod_get_system_param_int(struct usb_line6_pod *pod, int *value, |
517 | int code, struct ValueWait *param, int sign) | |
705ececd MG |
518 | { |
519 | char *sysex; | |
705ececd MG |
520 | static const int size = 1; |
521 | int retval = 0; | |
705ececd | 522 | |
e1a164d7 MG |
523 | if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) |
524 | && pod_is_tuner(code)) | |
705ececd MG |
525 | return -ENODEV; |
526 | ||
1027f476 | 527 | /* send value request to device: */ |
705ececd MG |
528 | param->value = POD_system_invalid; |
529 | sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEMREQ, size); | |
1027f476 | 530 | |
0fdef36a | 531 | if (!sysex) |
1027f476 MG |
532 | return -ENOMEM; |
533 | ||
705ececd MG |
534 | sysex[SYSEX_DATA_OFS] = code; |
535 | line6_send_sysex_message(&pod->line6, sysex, size); | |
536 | kfree(sysex); | |
537 | ||
1027f476 | 538 | /* wait for device to respond: */ |
e1a164d7 MG |
539 | retval = |
540 | wait_event_interruptible(param->wait, | |
541 | param->value != POD_system_invalid); | |
705ececd | 542 | |
1027f476 MG |
543 | if (retval < 0) |
544 | return retval; | |
705ececd | 545 | |
e1a164d7 MG |
546 | *value = sign ? (int)(signed short)param->value : (int)(unsigned short) |
547 | param->value; | |
705ececd | 548 | |
e1a164d7 MG |
549 | if (*value == POD_system_invalid) |
550 | *value = 0; /* don't report uninitialized values */ | |
1027f476 MG |
551 | |
552 | return 0; | |
553 | } | |
554 | ||
555 | /* | |
556 | Get system parameter (as string). | |
557 | @param tuner non-zero, if code refers to a tuner parameter | |
558 | */ | |
e1a164d7 MG |
559 | static ssize_t pod_get_system_param_string(struct usb_line6_pod *pod, char *buf, |
560 | int code, struct ValueWait *param, | |
561 | int sign) | |
1027f476 MG |
562 | { |
563 | int retval, value = 0; | |
564 | retval = pod_get_system_param_int(pod, &value, code, param, sign); | |
565 | ||
e1a164d7 | 566 | if (retval < 0) |
705ececd MG |
567 | return retval; |
568 | ||
705ececd MG |
569 | return sprintf(buf, "%d\n", value); |
570 | } | |
571 | ||
572 | /* | |
1027f476 | 573 | Send system parameter (from integer). |
705ececd MG |
574 | @param tuner non-zero, if code refers to a tuner parameter |
575 | */ | |
e1a164d7 MG |
576 | static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, |
577 | int code) | |
705ececd MG |
578 | { |
579 | char *sysex; | |
580 | static const int size = 5; | |
705ececd | 581 | |
e1a164d7 MG |
582 | if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) |
583 | && pod_is_tuner(code)) | |
705ececd MG |
584 | return -EINVAL; |
585 | ||
586 | /* send value to tuner: */ | |
587 | sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); | |
0fdef36a | 588 | if (!sysex) |
1027f476 | 589 | return -ENOMEM; |
705ececd MG |
590 | sysex[SYSEX_DATA_OFS] = code; |
591 | sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; | |
e1a164d7 MG |
592 | sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; |
593 | sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f; | |
594 | sysex[SYSEX_DATA_OFS + 4] = (value) & 0x0f; | |
705ececd MG |
595 | line6_send_sysex_message(&pod->line6, sysex, size); |
596 | kfree(sysex); | |
1027f476 MG |
597 | return 0; |
598 | } | |
599 | ||
600 | /* | |
601 | Send system parameter (from string). | |
602 | @param tuner non-zero, if code refers to a tuner parameter | |
603 | */ | |
e1a164d7 MG |
604 | static ssize_t pod_set_system_param_string(struct usb_line6_pod *pod, |
605 | const char *buf, int count, int code, | |
606 | unsigned short mask) | |
1027f476 MG |
607 | { |
608 | int retval; | |
609 | unsigned short value = simple_strtoul(buf, NULL, 10) & mask; | |
610 | retval = pod_set_system_param_int(pod, value, code); | |
611 | return (retval < 0) ? retval : count; | |
705ececd MG |
612 | } |
613 | ||
705ececd MG |
614 | /* |
615 | "write" request on "finish" special file. | |
616 | */ | |
77491e52 GKH |
617 | static ssize_t pod_set_finish(struct device *dev, |
618 | struct device_attribute *attr, | |
619 | const char *buf, size_t count) | |
705ececd MG |
620 | { |
621 | struct usb_interface *interface = to_usb_interface(dev); | |
622 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
623 | int size = 0; | |
624 | char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_FINISH, size); | |
0fdef36a GKH |
625 | if (!sysex) |
626 | return 0; | |
705ececd MG |
627 | line6_send_sysex_message(&pod->line6, sysex, size); |
628 | kfree(sysex); | |
629 | return count; | |
630 | } | |
631 | ||
632 | /* | |
633 | "write" request on "store_channel" special file. | |
634 | */ | |
77491e52 GKH |
635 | static ssize_t pod_set_store_channel(struct device *dev, |
636 | struct device_attribute *attr, | |
637 | const char *buf, size_t count) | |
705ececd MG |
638 | { |
639 | return pod_send_store_command(dev, buf, count, 0x0000, 0x00c0); | |
640 | } | |
641 | ||
642 | /* | |
643 | "write" request on "store_effects_setup" special file. | |
644 | */ | |
77491e52 GKH |
645 | static ssize_t pod_set_store_effects_setup(struct device *dev, |
646 | struct device_attribute *attr, | |
647 | const char *buf, size_t count) | |
705ececd MG |
648 | { |
649 | return pod_send_store_command(dev, buf, count, 0x0080, 0x0080); | |
650 | } | |
651 | ||
652 | /* | |
653 | "write" request on "store_amp_setup" special file. | |
654 | */ | |
77491e52 GKH |
655 | static ssize_t pod_set_store_amp_setup(struct device *dev, |
656 | struct device_attribute *attr, | |
657 | const char *buf, size_t count) | |
705ececd MG |
658 | { |
659 | return pod_send_store_command(dev, buf, count, 0x0040, 0x0100); | |
660 | } | |
661 | ||
662 | /* | |
663 | "write" request on "retrieve_channel" special file. | |
664 | */ | |
77491e52 GKH |
665 | static ssize_t pod_set_retrieve_channel(struct device *dev, |
666 | struct device_attribute *attr, | |
667 | const char *buf, size_t count) | |
705ececd MG |
668 | { |
669 | return pod_send_retrieve_command(dev, buf, count, 0x0000, 0x00c0); | |
670 | } | |
671 | ||
672 | /* | |
673 | "write" request on "retrieve_effects_setup" special file. | |
674 | */ | |
77491e52 GKH |
675 | static ssize_t pod_set_retrieve_effects_setup(struct device *dev, |
676 | struct device_attribute *attr, | |
677 | const char *buf, size_t count) | |
705ececd MG |
678 | { |
679 | return pod_send_retrieve_command(dev, buf, count, 0x0080, 0x0080); | |
680 | } | |
681 | ||
682 | /* | |
683 | "write" request on "retrieve_amp_setup" special file. | |
684 | */ | |
77491e52 GKH |
685 | static ssize_t pod_set_retrieve_amp_setup(struct device *dev, |
686 | struct device_attribute *attr, | |
687 | const char *buf, size_t count) | |
705ececd MG |
688 | { |
689 | return pod_send_retrieve_command(dev, buf, count, 0x0040, 0x0100); | |
690 | } | |
691 | ||
705ececd MG |
692 | /* |
693 | "read" request on "midi_postprocess" special file. | |
694 | */ | |
77491e52 GKH |
695 | static ssize_t pod_get_midi_postprocess(struct device *dev, |
696 | struct device_attribute *attr, | |
697 | char *buf) | |
705ececd MG |
698 | { |
699 | struct usb_interface *interface = to_usb_interface(dev); | |
700 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
701 | return sprintf(buf, "%d\n", pod->midi_postprocess); | |
702 | } | |
703 | ||
704 | /* | |
705 | "write" request on "midi_postprocess" special file. | |
706 | */ | |
77491e52 GKH |
707 | static ssize_t pod_set_midi_postprocess(struct device *dev, |
708 | struct device_attribute *attr, | |
709 | const char *buf, size_t count) | |
705ececd MG |
710 | { |
711 | struct usb_interface *interface = to_usb_interface(dev); | |
712 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
06501787 | 713 | u8 value; |
7e4d5c13 SB |
714 | int ret; |
715 | ||
06501787 | 716 | ret = kstrtou8(buf, 10, &value); |
7e4d5c13 SB |
717 | if (ret) |
718 | return ret; | |
719 | ||
705ececd MG |
720 | pod->midi_postprocess = value ? 1 : 0; |
721 | return count; | |
722 | } | |
723 | ||
724 | /* | |
725 | "read" request on "serial_number" special file. | |
726 | */ | |
77491e52 GKH |
727 | static ssize_t pod_get_serial_number(struct device *dev, |
728 | struct device_attribute *attr, char *buf) | |
705ececd MG |
729 | { |
730 | struct usb_interface *interface = to_usb_interface(dev); | |
731 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
732 | return sprintf(buf, "%d\n", pod->serial_number); | |
733 | } | |
734 | ||
735 | /* | |
736 | "read" request on "firmware_version" special file. | |
737 | */ | |
77491e52 GKH |
738 | static ssize_t pod_get_firmware_version(struct device *dev, |
739 | struct device_attribute *attr, | |
740 | char *buf) | |
705ececd MG |
741 | { |
742 | struct usb_interface *interface = to_usb_interface(dev); | |
743 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
0fdef36a GKH |
744 | return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100, |
745 | pod->firmware_version % 100); | |
705ececd MG |
746 | } |
747 | ||
748 | /* | |
749 | "read" request on "device_id" special file. | |
750 | */ | |
77491e52 GKH |
751 | static ssize_t pod_get_device_id(struct device *dev, |
752 | struct device_attribute *attr, char *buf) | |
705ececd MG |
753 | { |
754 | struct usb_interface *interface = to_usb_interface(dev); | |
755 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
756 | return sprintf(buf, "%d\n", pod->device_id); | |
757 | } | |
758 | ||
1027f476 MG |
759 | /* |
760 | POD startup procedure. | |
761 | This is a sequence of functions with special requirements (e.g., must | |
762 | not run immediately after initialization, must not run in interrupt | |
763 | context). After the last one has finished, the device is ready to use. | |
764 | */ | |
765 | ||
766 | static void pod_startup1(struct usb_line6_pod *pod) | |
767 | { | |
e1a164d7 | 768 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT); |
1027f476 MG |
769 | |
770 | /* delay startup procedure: */ | |
e1a164d7 MG |
771 | line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, |
772 | (unsigned long)pod); | |
1027f476 MG |
773 | } |
774 | ||
775 | static void pod_startup2(unsigned long data) | |
776 | { | |
777 | struct usb_line6_pod *pod = (struct usb_line6_pod *)data; | |
e1a164d7 MG |
778 | |
779 | /* schedule another startup procedure until startup is complete: */ | |
780 | if (pod->startup_progress >= POD_STARTUP_LAST) | |
781 | return; | |
782 | ||
783 | pod->startup_progress = POD_STARTUP_DUMPREQ; | |
784 | line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, | |
785 | (unsigned long)pod); | |
1027f476 MG |
786 | |
787 | /* current channel dump: */ | |
e1a164d7 MG |
788 | line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, |
789 | LINE6_DUMP_CURRENT); | |
1027f476 MG |
790 | } |
791 | ||
792 | static void pod_startup3(struct usb_line6_pod *pod) | |
793 | { | |
794 | struct usb_line6 *line6 = &pod->line6; | |
e1a164d7 | 795 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ); |
1027f476 MG |
796 | |
797 | /* request firmware version: */ | |
798 | line6_version_request_async(line6); | |
705ececd MG |
799 | } |
800 | ||
1027f476 MG |
801 | static void pod_startup4(struct usb_line6_pod *pod) |
802 | { | |
e1a164d7 | 803 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_WORKQUEUE); |
1027f476 MG |
804 | |
805 | /* schedule work for global work queue: */ | |
806 | schedule_work(&pod->startup_work); | |
807 | } | |
808 | ||
809 | static void pod_startup5(struct work_struct *work) | |
810 | { | |
e1a164d7 MG |
811 | struct usb_line6_pod *pod = |
812 | container_of(work, struct usb_line6_pod, startup_work); | |
1027f476 MG |
813 | struct usb_line6 *line6 = &pod->line6; |
814 | ||
e1a164d7 | 815 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_SETUP); |
1027f476 MG |
816 | |
817 | /* serial number: */ | |
818 | line6_read_serial_number(&pod->line6, &pod->serial_number); | |
819 | ||
820 | /* ALSA audio interface: */ | |
821 | line6_register_audio(line6); | |
822 | ||
823 | /* device files: */ | |
e1a164d7 MG |
824 | line6_pod_create_files(pod->firmware_version, |
825 | line6->properties->device_bit, line6->ifcdev); | |
1027f476 MG |
826 | } |
827 | ||
828 | #define POD_GET_SYSTEM_PARAM(code, sign) \ | |
77491e52 GKH |
829 | static ssize_t pod_get_ ## code(struct device *dev, \ |
830 | struct device_attribute *attr, char *buf) \ | |
705ececd MG |
831 | { \ |
832 | struct usb_interface *interface = to_usb_interface(dev); \ | |
833 | struct usb_line6_pod *pod = usb_get_intfdata(interface); \ | |
1027f476 MG |
834 | return pod_get_system_param_string(pod, buf, POD_ ## code, \ |
835 | &pod->code, sign); \ | |
705ececd MG |
836 | } |
837 | ||
1027f476 MG |
838 | #define POD_GET_SET_SYSTEM_PARAM(code, mask, sign) \ |
839 | POD_GET_SYSTEM_PARAM(code, sign) \ | |
77491e52 | 840 | static ssize_t pod_set_ ## code(struct device *dev, \ |
0fdef36a GKH |
841 | struct device_attribute *attr, \ |
842 | const char *buf, size_t count) \ | |
705ececd MG |
843 | { \ |
844 | struct usb_interface *interface = to_usb_interface(dev); \ | |
845 | struct usb_line6_pod *pod = usb_get_intfdata(interface); \ | |
1027f476 | 846 | return pod_set_system_param_string(pod, buf, count, POD_ ## code, mask); \ |
705ececd MG |
847 | } |
848 | ||
1027f476 MG |
849 | POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0); |
850 | POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 0); | |
851 | POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 0); | |
852 | POD_GET_SYSTEM_PARAM(tuner_note, 1); | |
853 | POD_GET_SYSTEM_PARAM(tuner_pitch, 1); | |
705ececd MG |
854 | |
855 | #undef GET_SET_SYSTEM_PARAM | |
856 | #undef GET_SYSTEM_PARAM | |
857 | ||
858 | /* POD special files: */ | |
705ececd | 859 | static DEVICE_ATTR(device_id, S_IRUGO, pod_get_device_id, line6_nop_write); |
a3a972a0 | 860 | static DEVICE_ATTR(finish, S_IWUSR, line6_nop_read, pod_set_finish); |
e1a164d7 MG |
861 | static DEVICE_ATTR(firmware_version, S_IRUGO, pod_get_firmware_version, |
862 | line6_nop_write); | |
a3a972a0 | 863 | static DEVICE_ATTR(midi_postprocess, S_IWUSR | S_IRUGO, |
e1a164d7 | 864 | pod_get_midi_postprocess, pod_set_midi_postprocess); |
705ececd MG |
865 | static DEVICE_ATTR(name, S_IRUGO, pod_get_name, line6_nop_write); |
866 | static DEVICE_ATTR(name_buf, S_IRUGO, pod_get_name_buf, line6_nop_write); | |
a3a972a0 | 867 | static DEVICE_ATTR(retrieve_amp_setup, S_IWUSR, line6_nop_read, |
e1a164d7 | 868 | pod_set_retrieve_amp_setup); |
a3a972a0 | 869 | static DEVICE_ATTR(retrieve_channel, S_IWUSR, line6_nop_read, |
e1a164d7 | 870 | pod_set_retrieve_channel); |
a3a972a0 | 871 | static DEVICE_ATTR(retrieve_effects_setup, S_IWUSR, line6_nop_read, |
e1a164d7 | 872 | pod_set_retrieve_effects_setup); |
a3a972a0 | 873 | static DEVICE_ATTR(routing, S_IWUSR | S_IRUGO, pod_get_routing, |
e1a164d7 MG |
874 | pod_set_routing); |
875 | static DEVICE_ATTR(serial_number, S_IRUGO, pod_get_serial_number, | |
876 | line6_nop_write); | |
a3a972a0 | 877 | static DEVICE_ATTR(store_amp_setup, S_IWUSR, line6_nop_read, |
e1a164d7 | 878 | pod_set_store_amp_setup); |
a3a972a0 | 879 | static DEVICE_ATTR(store_channel, S_IWUSR, line6_nop_read, |
e1a164d7 | 880 | pod_set_store_channel); |
a3a972a0 | 881 | static DEVICE_ATTR(store_effects_setup, S_IWUSR, line6_nop_read, |
e1a164d7 | 882 | pod_set_store_effects_setup); |
a3a972a0 | 883 | static DEVICE_ATTR(tuner_freq, S_IWUSR | S_IRUGO, pod_get_tuner_freq, |
e1a164d7 | 884 | pod_set_tuner_freq); |
a3a972a0 | 885 | static DEVICE_ATTR(tuner_mute, S_IWUSR | S_IRUGO, pod_get_tuner_mute, |
e1a164d7 | 886 | pod_set_tuner_mute); |
705ececd MG |
887 | static DEVICE_ATTR(tuner_note, S_IRUGO, pod_get_tuner_note, line6_nop_write); |
888 | static DEVICE_ATTR(tuner_pitch, S_IRUGO, pod_get_tuner_pitch, line6_nop_write); | |
889 | ||
1027f476 | 890 | #ifdef CONFIG_LINE6_USB_RAW |
a3a972a0 | 891 | static DEVICE_ATTR(raw, S_IWUSR, line6_nop_read, line6_set_raw); |
705ececd MG |
892 | #endif |
893 | ||
1027f476 MG |
894 | /* control info callback */ |
895 | static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol, | |
896 | struct snd_ctl_elem_info *uinfo) | |
897 | { | |
898 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
899 | uinfo->count = 1; | |
900 | uinfo->value.integer.min = 0; | |
901 | uinfo->value.integer.max = 65535; | |
902 | return 0; | |
903 | } | |
904 | ||
905 | /* control get callback */ | |
906 | static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol, | |
907 | struct snd_ctl_elem_value *ucontrol) | |
908 | { | |
909 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
910 | struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; | |
911 | ucontrol->value.integer.value[0] = pod->monitor_level.value; | |
912 | return 0; | |
913 | } | |
914 | ||
915 | /* control put callback */ | |
916 | static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol, | |
917 | struct snd_ctl_elem_value *ucontrol) | |
918 | { | |
919 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
920 | struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; | |
921 | ||
e1a164d7 | 922 | if (ucontrol->value.integer.value[0] == pod->monitor_level.value) |
1027f476 MG |
923 | return 0; |
924 | ||
925 | pod->monitor_level.value = ucontrol->value.integer.value[0]; | |
e1a164d7 MG |
926 | pod_set_system_param_int(pod, ucontrol->value.integer.value[0], |
927 | POD_monitor_level); | |
1027f476 MG |
928 | return 1; |
929 | } | |
930 | ||
931 | /* control definition */ | |
932 | static struct snd_kcontrol_new pod_control_monitor = { | |
933 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
934 | .name = "Monitor Playback Volume", | |
935 | .index = 0, | |
936 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
937 | .info = snd_pod_control_monitor_info, | |
938 | .get = snd_pod_control_monitor_get, | |
939 | .put = snd_pod_control_monitor_put | |
940 | }; | |
941 | ||
705ececd MG |
942 | /* |
943 | POD destructor. | |
944 | */ | |
945 | static void pod_destruct(struct usb_interface *interface) | |
946 | { | |
947 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
705ececd | 948 | |
0fdef36a GKH |
949 | if (pod == NULL) |
950 | return; | |
188e6645 | 951 | line6_cleanup_audio(&pod->line6); |
705ececd | 952 | |
e1a164d7 MG |
953 | del_timer(&pod->startup_timer); |
954 | cancel_work_sync(&pod->startup_work); | |
955 | ||
705ececd MG |
956 | /* free dump request data: */ |
957 | line6_dumpreq_destruct(&pod->dumpreq); | |
705ececd MG |
958 | } |
959 | ||
960 | /* | |
961 | Create sysfs entries. | |
962 | */ | |
b702ed25 | 963 | static int pod_create_files2(struct device *dev) |
705ececd MG |
964 | { |
965 | int err; | |
966 | ||
705ececd | 967 | CHECK_RETURN(device_create_file(dev, &dev_attr_device_id)); |
705ececd MG |
968 | CHECK_RETURN(device_create_file(dev, &dev_attr_finish)); |
969 | CHECK_RETURN(device_create_file(dev, &dev_attr_firmware_version)); | |
970 | CHECK_RETURN(device_create_file(dev, &dev_attr_midi_postprocess)); | |
705ececd MG |
971 | CHECK_RETURN(device_create_file(dev, &dev_attr_name)); |
972 | CHECK_RETURN(device_create_file(dev, &dev_attr_name_buf)); | |
973 | CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_amp_setup)); | |
974 | CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_channel)); | |
975 | CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_effects_setup)); | |
976 | CHECK_RETURN(device_create_file(dev, &dev_attr_routing)); | |
977 | CHECK_RETURN(device_create_file(dev, &dev_attr_serial_number)); | |
978 | CHECK_RETURN(device_create_file(dev, &dev_attr_store_amp_setup)); | |
979 | CHECK_RETURN(device_create_file(dev, &dev_attr_store_channel)); | |
980 | CHECK_RETURN(device_create_file(dev, &dev_attr_store_effects_setup)); | |
981 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_freq)); | |
982 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_mute)); | |
983 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_note)); | |
984 | CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_pitch)); | |
985 | ||
1027f476 | 986 | #ifdef CONFIG_LINE6_USB_RAW |
705ececd MG |
987 | CHECK_RETURN(device_create_file(dev, &dev_attr_raw)); |
988 | #endif | |
989 | ||
990 | return 0; | |
991 | } | |
992 | ||
993 | /* | |
1027f476 | 994 | Try to init POD device. |
705ececd | 995 | */ |
e1a164d7 MG |
996 | static int pod_try_init(struct usb_interface *interface, |
997 | struct usb_line6_pod *pod) | |
705ececd MG |
998 | { |
999 | int err; | |
1000 | struct usb_line6 *line6 = &pod->line6; | |
1001 | ||
e1a164d7 MG |
1002 | init_timer(&pod->startup_timer); |
1003 | INIT_WORK(&pod->startup_work, pod_startup5); | |
1004 | ||
0fdef36a GKH |
1005 | if ((interface == NULL) || (pod == NULL)) |
1006 | return -ENODEV; | |
705ececd | 1007 | |
705ececd | 1008 | /* initialize wait queues: */ |
705ececd MG |
1009 | init_waitqueue_head(&pod->routing.wait); |
1010 | init_waitqueue_head(&pod->tuner_mute.wait); | |
1011 | init_waitqueue_head(&pod->tuner_freq.wait); | |
1012 | init_waitqueue_head(&pod->tuner_note.wait); | |
1013 | init_waitqueue_head(&pod->tuner_pitch.wait); | |
705ececd | 1014 | |
705ececd | 1015 | /* initialize USB buffers: */ |
0fdef36a GKH |
1016 | err = line6_dumpreq_init(&pod->dumpreq, pod_request_channel, |
1017 | sizeof(pod_request_channel)); | |
1018 | if (err < 0) { | |
705ececd | 1019 | dev_err(&interface->dev, "Out of memory\n"); |
705ececd MG |
1020 | return -ENOMEM; |
1021 | } | |
1022 | ||
705ececd | 1023 | /* create sysfs entries: */ |
0fdef36a | 1024 | err = pod_create_files2(&interface->dev); |
027360c5 | 1025 | if (err < 0) |
705ececd | 1026 | return err; |
705ececd MG |
1027 | |
1028 | /* initialize audio system: */ | |
0fdef36a | 1029 | err = line6_init_audio(line6); |
027360c5 | 1030 | if (err < 0) |
705ececd | 1031 | return err; |
705ececd MG |
1032 | |
1033 | /* initialize MIDI subsystem: */ | |
0fdef36a | 1034 | err = line6_init_midi(line6); |
027360c5 | 1035 | if (err < 0) |
705ececd | 1036 | return err; |
705ececd MG |
1037 | |
1038 | /* initialize PCM subsystem: */ | |
0fdef36a | 1039 | err = line6_init_pcm(line6, &pod_pcm_properties); |
027360c5 | 1040 | if (err < 0) |
705ececd | 1041 | return err; |
705ececd | 1042 | |
1027f476 | 1043 | /* register monitor control: */ |
027360c5 GKH |
1044 | err = snd_ctl_add(line6->card, |
1045 | snd_ctl_new1(&pod_control_monitor, line6->line6pcm)); | |
1046 | if (err < 0) | |
705ececd | 1047 | return err; |
705ececd | 1048 | |
1027f476 | 1049 | /* |
e1a164d7 MG |
1050 | When the sound card is registered at this point, the PODxt Live |
1051 | displays "Invalid Code Error 07", so we do it later in the event | |
1052 | handler. | |
1053 | */ | |
1027f476 | 1054 | |
0fdef36a | 1055 | if (pod->line6.properties->capabilities & LINE6_BIT_CONTROL) { |
1027f476 MG |
1056 | pod->monitor_level.value = POD_system_invalid; |
1057 | ||
1058 | /* initiate startup procedure: */ | |
1059 | pod_startup1(pod); | |
705ececd MG |
1060 | } |
1061 | ||
1062 | return 0; | |
1063 | } | |
1064 | ||
1027f476 MG |
1065 | /* |
1066 | Init POD device (and clean up in case of failure). | |
1067 | */ | |
1068 | int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) | |
1069 | { | |
1070 | int err = pod_try_init(interface, pod); | |
1071 | ||
027360c5 | 1072 | if (err < 0) |
1027f476 | 1073 | pod_destruct(interface); |
1027f476 MG |
1074 | |
1075 | return err; | |
1076 | } | |
1077 | ||
705ececd MG |
1078 | /* |
1079 | POD device disconnected. | |
1080 | */ | |
1027f476 | 1081 | void line6_pod_disconnect(struct usb_interface *interface) |
705ececd MG |
1082 | { |
1083 | struct usb_line6_pod *pod; | |
1084 | ||
0fdef36a GKH |
1085 | if (interface == NULL) |
1086 | return; | |
705ececd MG |
1087 | pod = usb_get_intfdata(interface); |
1088 | ||
0fdef36a | 1089 | if (pod != NULL) { |
705ececd MG |
1090 | struct snd_line6_pcm *line6pcm = pod->line6.line6pcm; |
1091 | struct device *dev = &interface->dev; | |
1092 | ||
027360c5 | 1093 | if (line6pcm != NULL) |
1027f476 | 1094 | line6_pcm_disconnect(line6pcm); |
705ececd | 1095 | |
0fdef36a | 1096 | if (dev != NULL) { |
705ececd | 1097 | /* remove sysfs entries: */ |
e1a164d7 MG |
1098 | line6_pod_remove_files(pod->firmware_version, |
1099 | pod->line6. | |
1100 | properties->device_bit, dev); | |
705ececd | 1101 | |
705ececd | 1102 | device_remove_file(dev, &dev_attr_device_id); |
705ececd MG |
1103 | device_remove_file(dev, &dev_attr_finish); |
1104 | device_remove_file(dev, &dev_attr_firmware_version); | |
1105 | device_remove_file(dev, &dev_attr_midi_postprocess); | |
705ececd MG |
1106 | device_remove_file(dev, &dev_attr_name); |
1107 | device_remove_file(dev, &dev_attr_name_buf); | |
1108 | device_remove_file(dev, &dev_attr_retrieve_amp_setup); | |
1109 | device_remove_file(dev, &dev_attr_retrieve_channel); | |
e1a164d7 MG |
1110 | device_remove_file(dev, |
1111 | &dev_attr_retrieve_effects_setup); | |
705ececd MG |
1112 | device_remove_file(dev, &dev_attr_routing); |
1113 | device_remove_file(dev, &dev_attr_serial_number); | |
1114 | device_remove_file(dev, &dev_attr_store_amp_setup); | |
1115 | device_remove_file(dev, &dev_attr_store_channel); | |
1116 | device_remove_file(dev, &dev_attr_store_effects_setup); | |
1117 | device_remove_file(dev, &dev_attr_tuner_freq); | |
1118 | device_remove_file(dev, &dev_attr_tuner_mute); | |
1119 | device_remove_file(dev, &dev_attr_tuner_note); | |
1120 | device_remove_file(dev, &dev_attr_tuner_pitch); | |
1121 | ||
1027f476 | 1122 | #ifdef CONFIG_LINE6_USB_RAW |
705ececd MG |
1123 | device_remove_file(dev, &dev_attr_raw); |
1124 | #endif | |
1125 | } | |
1126 | } | |
1127 | ||
1128 | pod_destruct(interface); | |
1129 | } |