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, | |
705ececd MG |
43 | POD_tuner_mute = 0x13, |
44 | POD_tuner_freq = 0x15, | |
45 | POD_tuner_note = 0x16, | |
46 | POD_tuner_pitch = 0x17, | |
1027f476 | 47 | POD_system_invalid = 0x10000 |
705ececd MG |
48 | }; |
49 | ||
e1a164d7 MG |
50 | /* *INDENT-ON* */ |
51 | ||
705ececd MG |
52 | enum { |
53 | POD_DUMP_MEMORY = 2 | |
54 | }; | |
55 | ||
56 | enum { | |
57 | POD_BUSY_READ, | |
58 | POD_BUSY_WRITE, | |
59 | POD_CHANNEL_DIRTY, | |
60 | POD_SAVE_PRESSED, | |
61 | POD_BUSY_MIDISEND | |
62 | }; | |
63 | ||
705ececd MG |
64 | static struct snd_ratden pod_ratden = { |
65 | .num_min = 78125, | |
66 | .num_max = 78125, | |
67 | .num_step = 1, | |
68 | .den = 2 | |
69 | }; | |
70 | ||
71 | static struct line6_pcm_properties pod_pcm_properties = { | |
1027f476 | 72 | .snd_line6_playback_hw = { |
e1a164d7 MG |
73 | .info = (SNDRV_PCM_INFO_MMAP | |
74 | SNDRV_PCM_INFO_INTERLEAVED | | |
75 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
76 | SNDRV_PCM_INFO_MMAP_VALID | | |
77 | SNDRV_PCM_INFO_PAUSE | | |
1027f476 | 78 | #ifdef CONFIG_PM |
e1a164d7 | 79 | SNDRV_PCM_INFO_RESUME | |
1027f476 | 80 | #endif |
e1a164d7 MG |
81 | SNDRV_PCM_INFO_SYNC_START), |
82 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | |
83 | .rates = SNDRV_PCM_RATE_KNOT, | |
84 | .rate_min = 39062, | |
85 | .rate_max = 39063, | |
86 | .channels_min = 2, | |
87 | .channels_max = 2, | |
88 | .buffer_bytes_max = 60000, | |
89 | .period_bytes_min = 64, | |
90 | .period_bytes_max = 8192, | |
91 | .periods_min = 1, | |
92 | .periods_max = 1024}, | |
1027f476 | 93 | .snd_line6_capture_hw = { |
e1a164d7 MG |
94 | .info = (SNDRV_PCM_INFO_MMAP | |
95 | SNDRV_PCM_INFO_INTERLEAVED | | |
96 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
97 | SNDRV_PCM_INFO_MMAP_VALID | | |
1027f476 | 98 | #ifdef CONFIG_PM |
e1a164d7 | 99 | SNDRV_PCM_INFO_RESUME | |
1027f476 | 100 | #endif |
e1a164d7 MG |
101 | SNDRV_PCM_INFO_SYNC_START), |
102 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | |
103 | .rates = SNDRV_PCM_RATE_KNOT, | |
104 | .rate_min = 39062, | |
105 | .rate_max = 39063, | |
106 | .channels_min = 2, | |
107 | .channels_max = 2, | |
108 | .buffer_bytes_max = 60000, | |
109 | .period_bytes_min = 64, | |
110 | .period_bytes_max = 8192, | |
111 | .periods_min = 1, | |
112 | .periods_max = 1024}, | |
705ececd | 113 | .snd_line6_rates = { |
e1a164d7 MG |
114 | .nrats = 1, |
115 | .rats = &pod_ratden}, | |
705ececd MG |
116 | .bytes_per_frame = POD_BYTES_PER_FRAME |
117 | }; | |
118 | ||
1027f476 MG |
119 | static const char pod_request_channel[] = { |
120 | 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7 | |
121 | }; | |
122 | ||
e1a164d7 | 123 | static const char pod_version_header[] = { |
1027f476 MG |
124 | 0xf2, 0x7e, 0x7f, 0x06, 0x02 |
125 | }; | |
126 | ||
1027f476 MG |
127 | /* forward declarations: */ |
128 | static void pod_startup2(unsigned long data); | |
129 | static void pod_startup3(struct usb_line6_pod *pod); | |
130 | static void pod_startup4(struct usb_line6_pod *pod); | |
705ececd | 131 | |
e1a164d7 MG |
132 | static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, |
133 | int size) | |
705ececd | 134 | { |
e1a164d7 MG |
135 | return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, |
136 | size); | |
705ececd MG |
137 | } |
138 | ||
705ececd | 139 | /* |
b772fe9e | 140 | Store parameter value in driver memory. |
705ececd MG |
141 | */ |
142 | static void pod_store_parameter(struct usb_line6_pod *pod, int param, int value) | |
143 | { | |
144 | pod->prog_data.control[param] = value; | |
705ececd MG |
145 | } |
146 | ||
147 | /* | |
1027f476 | 148 | Handle SAVE button. |
705ececd | 149 | */ |
e1a164d7 MG |
150 | static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, |
151 | int index) | |
705ececd | 152 | { |
705ececd MG |
153 | set_bit(POD_SAVE_PRESSED, &pod->atomic_flags); |
154 | } | |
155 | ||
156 | /* | |
157 | Process a completely received message. | |
158 | */ | |
1027f476 | 159 | void line6_pod_process_message(struct usb_line6_pod *pod) |
705ececd MG |
160 | { |
161 | const unsigned char *buf = pod->line6.buffer_message; | |
162 | ||
163 | /* filter messages by type */ | |
0fdef36a | 164 | switch (buf[0] & 0xf0) { |
705ececd MG |
165 | case LINE6_PARAM_CHANGE: |
166 | case LINE6_PROGRAM_CHANGE: | |
167 | case LINE6_SYSEX_BEGIN: | |
e1a164d7 | 168 | break; /* handle these further down */ |
705ececd MG |
169 | |
170 | default: | |
e1a164d7 | 171 | return; /* ignore all others */ |
705ececd MG |
172 | } |
173 | ||
174 | /* process all remaining messages */ | |
0fdef36a | 175 | switch (buf[0]) { |
705ececd MG |
176 | case LINE6_PARAM_CHANGE | LINE6_CHANNEL_DEVICE: |
177 | pod_store_parameter(pod, buf[1], buf[2]); | |
178 | /* intentionally no break here! */ | |
179 | ||
180 | case LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST: | |
0fdef36a GKH |
181 | if ((buf[1] == POD_amp_model_setup) || |
182 | (buf[1] == POD_effect_setup)) | |
183 | /* these also affect other settings */ | |
e1a164d7 MG |
184 | line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, |
185 | LINE6_DUMP_CURRENT); | |
705ececd MG |
186 | |
187 | break; | |
188 | ||
189 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE: | |
190 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST: | |
705ececd | 191 | set_bit(POD_CHANNEL_DIRTY, &pod->atomic_flags); |
e1a164d7 MG |
192 | line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, |
193 | LINE6_DUMP_CURRENT); | |
705ececd MG |
194 | break; |
195 | ||
196 | case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE: | |
197 | case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN: | |
0fdef36a GKH |
198 | if (memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) == 0) { |
199 | switch (buf[5]) { | |
705ececd | 200 | case POD_SYSEX_DUMP: |
e1a164d7 MG |
201 | if (pod->line6.message_length == |
202 | sizeof(pod->prog_data) + 7) { | |
0fdef36a | 203 | switch (pod->dumpreq.in_progress) { |
705ececd | 204 | case LINE6_DUMP_CURRENT: |
e1a164d7 MG |
205 | memcpy(&pod->prog_data, buf + 7, |
206 | sizeof(pod->prog_data)); | |
705ececd MG |
207 | break; |
208 | ||
209 | case POD_DUMP_MEMORY: | |
e1a164d7 MG |
210 | memcpy(&pod->prog_data_buf, |
211 | buf + 7, | |
212 | sizeof | |
213 | (pod->prog_data_buf)); | |
705ececd MG |
214 | break; |
215 | ||
216 | default: | |
e00d33cb SH |
217 | dev_dbg(pod->line6.ifcdev, |
218 | "unknown dump code %02X\n", | |
219 | pod->dumpreq.in_progress); | |
705ececd MG |
220 | } |
221 | ||
222 | line6_dump_finished(&pod->dumpreq); | |
1027f476 | 223 | pod_startup3(pod); |
0fdef36a | 224 | } else |
e00d33cb SH |
225 | dev_dbg(pod->line6.ifcdev, |
226 | "wrong size of channel dump message (%d instead of %d)\n", | |
227 | pod->line6.message_length, | |
228 | (int)sizeof(pod->prog_data) + | |
229 | 7); | |
705ececd MG |
230 | |
231 | break; | |
232 | ||
e1a164d7 MG |
233 | case POD_SYSEX_SYSTEM:{ |
234 | short value = | |
235 | ((int)buf[7] << 12) | ((int)buf[8] | |
236 | << 8) | | |
237 | ((int)buf[9] << 4) | (int)buf[10]; | |
705ececd | 238 | |
99c54e98 | 239 | if (buf[6] == POD_monitor_level) |
2c35dc21 | 240 | pod->monitor_level = value; |
e1a164d7 MG |
241 | break; |
242 | } | |
705ececd MG |
243 | |
244 | case POD_SYSEX_FINISH: | |
245 | /* do we need to respond to this? */ | |
246 | break; | |
247 | ||
248 | case POD_SYSEX_SAVE: | |
249 | pod_save_button_pressed(pod, buf[6], buf[7]); | |
250 | break; | |
251 | ||
705ececd | 252 | case POD_SYSEX_STORE: |
e00d33cb SH |
253 | dev_dbg(pod->line6.ifcdev, |
254 | "message %02X not yet implemented\n", | |
255 | buf[5]); | |
705ececd MG |
256 | break; |
257 | ||
258 | default: | |
e00d33cb SH |
259 | dev_dbg(pod->line6.ifcdev, |
260 | "unknown sysex message %02X\n", | |
261 | buf[5]); | |
705ececd | 262 | } |
e1a164d7 MG |
263 | } else |
264 | if (memcmp | |
265 | (buf, pod_version_header, | |
266 | sizeof(pod_version_header)) == 0) { | |
267 | pod->firmware_version = | |
268 | buf[13] * 100 + buf[14] * 10 + buf[15]; | |
269 | pod->device_id = | |
270 | ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int) | |
271 | buf[10]; | |
1027f476 | 272 | pod_startup4(pod); |
0fdef36a | 273 | } else |
e00d33cb | 274 | dev_dbg(pod->line6.ifcdev, "unknown sysex header\n"); |
705ececd MG |
275 | |
276 | break; | |
277 | ||
278 | case LINE6_SYSEX_END: | |
279 | break; | |
280 | ||
281 | default: | |
e00d33cb SH |
282 | dev_dbg(pod->line6.ifcdev, "POD: unknown message %02X\n", |
283 | buf[0]); | |
705ececd MG |
284 | } |
285 | } | |
286 | ||
287 | /* | |
288 | Detect some cases that require a channel dump after sending a command to the | |
289 | device. Important notes: | |
290 | *) The actual dump request can not be sent here since we are not allowed to | |
291 | wait for the completion of the first message in this context, and sending | |
292 | the dump request before completion of the previous message leaves the POD | |
293 | in an undefined state. The dump request will be sent when the echoed | |
294 | commands are received. | |
295 | *) This method fails if a param change message is "chopped" after the first | |
296 | byte. | |
297 | */ | |
e1a164d7 MG |
298 | void line6_pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, |
299 | int length) | |
705ececd MG |
300 | { |
301 | int i; | |
302 | ||
0fdef36a | 303 | if (!pod->midi_postprocess) |
705ececd MG |
304 | return; |
305 | ||
0fdef36a GKH |
306 | for (i = 0; i < length; ++i) { |
307 | if (data[i] == (LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST)) { | |
705ececd MG |
308 | line6_invalidate_current(&pod->dumpreq); |
309 | break; | |
e1a164d7 MG |
310 | } else |
311 | if ((data[i] == (LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST)) | |
312 | && (i < length - 1)) | |
313 | if ((data[i + 1] == POD_amp_model_setup) | |
314 | || (data[i + 1] == POD_effect_setup)) { | |
705ececd MG |
315 | line6_invalidate_current(&pod->dumpreq); |
316 | break; | |
317 | } | |
318 | } | |
319 | } | |
320 | ||
705ececd MG |
321 | /* |
322 | Transmit PODxt Pro control parameter. | |
323 | */ | |
e1a164d7 | 324 | void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, |
5b9bd2ad | 325 | u8 value) |
705ececd | 326 | { |
0fdef36a | 327 | if (line6_transmit_parameter(&pod->line6, param, value) == 0) |
705ececd MG |
328 | pod_store_parameter(pod, param, value); |
329 | ||
e1a164d7 | 330 | if ((param == POD_amp_model_setup) || (param == POD_effect_setup)) /* these also affect other settings */ |
705ececd MG |
331 | line6_invalidate_current(&pod->dumpreq); |
332 | } | |
333 | ||
705ececd | 334 | /* |
1027f476 MG |
335 | Identify system parameters related to the tuner. |
336 | */ | |
337 | static bool pod_is_tuner(int code) | |
338 | { | |
339 | return | |
e1a164d7 MG |
340 | (code == POD_tuner_mute) || |
341 | (code == POD_tuner_freq) || | |
342 | (code == POD_tuner_note) || (code == POD_tuner_pitch); | |
1027f476 MG |
343 | } |
344 | ||
705ececd | 345 | /* |
1027f476 | 346 | Send system parameter (from integer). |
705ececd MG |
347 | @param tuner non-zero, if code refers to a tuner parameter |
348 | */ | |
e1a164d7 MG |
349 | static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, |
350 | int code) | |
705ececd MG |
351 | { |
352 | char *sysex; | |
353 | static const int size = 5; | |
705ececd | 354 | |
e1a164d7 MG |
355 | if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) |
356 | && pod_is_tuner(code)) | |
705ececd MG |
357 | return -EINVAL; |
358 | ||
359 | /* send value to tuner: */ | |
360 | sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); | |
0fdef36a | 361 | if (!sysex) |
1027f476 | 362 | return -ENOMEM; |
705ececd MG |
363 | sysex[SYSEX_DATA_OFS] = code; |
364 | sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; | |
e1a164d7 MG |
365 | sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; |
366 | sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f; | |
367 | sysex[SYSEX_DATA_OFS + 4] = (value) & 0x0f; | |
705ececd MG |
368 | line6_send_sysex_message(&pod->line6, sysex, size); |
369 | kfree(sysex); | |
1027f476 MG |
370 | return 0; |
371 | } | |
372 | ||
705ececd MG |
373 | /* |
374 | "read" request on "midi_postprocess" special file. | |
375 | */ | |
77491e52 GKH |
376 | static ssize_t pod_get_midi_postprocess(struct device *dev, |
377 | struct device_attribute *attr, | |
378 | char *buf) | |
705ececd MG |
379 | { |
380 | struct usb_interface *interface = to_usb_interface(dev); | |
381 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
382 | return sprintf(buf, "%d\n", pod->midi_postprocess); | |
383 | } | |
384 | ||
385 | /* | |
386 | "write" request on "midi_postprocess" special file. | |
387 | */ | |
77491e52 GKH |
388 | static ssize_t pod_set_midi_postprocess(struct device *dev, |
389 | struct device_attribute *attr, | |
390 | const char *buf, size_t count) | |
705ececd MG |
391 | { |
392 | struct usb_interface *interface = to_usb_interface(dev); | |
393 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
06501787 | 394 | u8 value; |
7e4d5c13 SB |
395 | int ret; |
396 | ||
06501787 | 397 | ret = kstrtou8(buf, 10, &value); |
7e4d5c13 SB |
398 | if (ret) |
399 | return ret; | |
400 | ||
705ececd MG |
401 | pod->midi_postprocess = value ? 1 : 0; |
402 | return count; | |
403 | } | |
404 | ||
405 | /* | |
406 | "read" request on "serial_number" special file. | |
407 | */ | |
77491e52 GKH |
408 | static ssize_t pod_get_serial_number(struct device *dev, |
409 | struct device_attribute *attr, char *buf) | |
705ececd MG |
410 | { |
411 | struct usb_interface *interface = to_usb_interface(dev); | |
412 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
413 | return sprintf(buf, "%d\n", pod->serial_number); | |
414 | } | |
415 | ||
416 | /* | |
417 | "read" request on "firmware_version" special file. | |
418 | */ | |
77491e52 GKH |
419 | static ssize_t pod_get_firmware_version(struct device *dev, |
420 | struct device_attribute *attr, | |
421 | char *buf) | |
705ececd MG |
422 | { |
423 | struct usb_interface *interface = to_usb_interface(dev); | |
424 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
0fdef36a GKH |
425 | return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100, |
426 | pod->firmware_version % 100); | |
705ececd MG |
427 | } |
428 | ||
429 | /* | |
430 | "read" request on "device_id" special file. | |
431 | */ | |
77491e52 GKH |
432 | static ssize_t pod_get_device_id(struct device *dev, |
433 | struct device_attribute *attr, char *buf) | |
705ececd MG |
434 | { |
435 | struct usb_interface *interface = to_usb_interface(dev); | |
436 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
437 | return sprintf(buf, "%d\n", pod->device_id); | |
438 | } | |
439 | ||
1027f476 MG |
440 | /* |
441 | POD startup procedure. | |
442 | This is a sequence of functions with special requirements (e.g., must | |
443 | not run immediately after initialization, must not run in interrupt | |
444 | context). After the last one has finished, the device is ready to use. | |
445 | */ | |
446 | ||
447 | static void pod_startup1(struct usb_line6_pod *pod) | |
448 | { | |
e1a164d7 | 449 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT); |
1027f476 MG |
450 | |
451 | /* delay startup procedure: */ | |
e1a164d7 MG |
452 | line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, |
453 | (unsigned long)pod); | |
1027f476 MG |
454 | } |
455 | ||
456 | static void pod_startup2(unsigned long data) | |
457 | { | |
458 | struct usb_line6_pod *pod = (struct usb_line6_pod *)data; | |
e1a164d7 MG |
459 | |
460 | /* schedule another startup procedure until startup is complete: */ | |
461 | if (pod->startup_progress >= POD_STARTUP_LAST) | |
462 | return; | |
463 | ||
464 | pod->startup_progress = POD_STARTUP_DUMPREQ; | |
465 | line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, | |
466 | (unsigned long)pod); | |
1027f476 MG |
467 | |
468 | /* current channel dump: */ | |
e1a164d7 MG |
469 | line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, |
470 | LINE6_DUMP_CURRENT); | |
1027f476 MG |
471 | } |
472 | ||
473 | static void pod_startup3(struct usb_line6_pod *pod) | |
474 | { | |
475 | struct usb_line6 *line6 = &pod->line6; | |
e1a164d7 | 476 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ); |
1027f476 MG |
477 | |
478 | /* request firmware version: */ | |
479 | line6_version_request_async(line6); | |
705ececd MG |
480 | } |
481 | ||
1027f476 MG |
482 | static void pod_startup4(struct usb_line6_pod *pod) |
483 | { | |
e1a164d7 | 484 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_WORKQUEUE); |
1027f476 MG |
485 | |
486 | /* schedule work for global work queue: */ | |
487 | schedule_work(&pod->startup_work); | |
488 | } | |
489 | ||
490 | static void pod_startup5(struct work_struct *work) | |
491 | { | |
e1a164d7 MG |
492 | struct usb_line6_pod *pod = |
493 | container_of(work, struct usb_line6_pod, startup_work); | |
1027f476 MG |
494 | struct usb_line6 *line6 = &pod->line6; |
495 | ||
e1a164d7 | 496 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_SETUP); |
1027f476 MG |
497 | |
498 | /* serial number: */ | |
499 | line6_read_serial_number(&pod->line6, &pod->serial_number); | |
500 | ||
501 | /* ALSA audio interface: */ | |
502 | line6_register_audio(line6); | |
503 | ||
504 | /* device files: */ | |
e1a164d7 MG |
505 | line6_pod_create_files(pod->firmware_version, |
506 | line6->properties->device_bit, line6->ifcdev); | |
1027f476 MG |
507 | } |
508 | ||
705ececd | 509 | /* POD special files: */ |
705ececd | 510 | static DEVICE_ATTR(device_id, S_IRUGO, pod_get_device_id, line6_nop_write); |
e1a164d7 MG |
511 | static DEVICE_ATTR(firmware_version, S_IRUGO, pod_get_firmware_version, |
512 | line6_nop_write); | |
a3a972a0 | 513 | static DEVICE_ATTR(midi_postprocess, S_IWUSR | S_IRUGO, |
e1a164d7 | 514 | pod_get_midi_postprocess, pod_set_midi_postprocess); |
e1a164d7 MG |
515 | static DEVICE_ATTR(serial_number, S_IRUGO, pod_get_serial_number, |
516 | line6_nop_write); | |
705ececd | 517 | |
1027f476 | 518 | #ifdef CONFIG_LINE6_USB_RAW |
a3a972a0 | 519 | static DEVICE_ATTR(raw, S_IWUSR, line6_nop_read, line6_set_raw); |
705ececd MG |
520 | #endif |
521 | ||
1027f476 MG |
522 | /* control info callback */ |
523 | static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol, | |
524 | struct snd_ctl_elem_info *uinfo) | |
525 | { | |
526 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
527 | uinfo->count = 1; | |
528 | uinfo->value.integer.min = 0; | |
529 | uinfo->value.integer.max = 65535; | |
530 | return 0; | |
531 | } | |
532 | ||
533 | /* control get callback */ | |
534 | static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol, | |
535 | struct snd_ctl_elem_value *ucontrol) | |
536 | { | |
537 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
538 | struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; | |
2c35dc21 | 539 | ucontrol->value.integer.value[0] = pod->monitor_level; |
1027f476 MG |
540 | return 0; |
541 | } | |
542 | ||
543 | /* control put callback */ | |
544 | static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol, | |
545 | struct snd_ctl_elem_value *ucontrol) | |
546 | { | |
547 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
548 | struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; | |
549 | ||
2c35dc21 | 550 | if (ucontrol->value.integer.value[0] == pod->monitor_level) |
1027f476 MG |
551 | return 0; |
552 | ||
2c35dc21 | 553 | pod->monitor_level = ucontrol->value.integer.value[0]; |
e1a164d7 MG |
554 | pod_set_system_param_int(pod, ucontrol->value.integer.value[0], |
555 | POD_monitor_level); | |
1027f476 MG |
556 | return 1; |
557 | } | |
558 | ||
559 | /* control definition */ | |
560 | static struct snd_kcontrol_new pod_control_monitor = { | |
561 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
562 | .name = "Monitor Playback Volume", | |
563 | .index = 0, | |
564 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
565 | .info = snd_pod_control_monitor_info, | |
566 | .get = snd_pod_control_monitor_get, | |
567 | .put = snd_pod_control_monitor_put | |
568 | }; | |
569 | ||
705ececd MG |
570 | /* |
571 | POD destructor. | |
572 | */ | |
573 | static void pod_destruct(struct usb_interface *interface) | |
574 | { | |
575 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
705ececd | 576 | |
0fdef36a GKH |
577 | if (pod == NULL) |
578 | return; | |
188e6645 | 579 | line6_cleanup_audio(&pod->line6); |
705ececd | 580 | |
e1a164d7 MG |
581 | del_timer(&pod->startup_timer); |
582 | cancel_work_sync(&pod->startup_work); | |
583 | ||
705ececd MG |
584 | /* free dump request data: */ |
585 | line6_dumpreq_destruct(&pod->dumpreq); | |
705ececd MG |
586 | } |
587 | ||
588 | /* | |
589 | Create sysfs entries. | |
590 | */ | |
b702ed25 | 591 | static int pod_create_files2(struct device *dev) |
705ececd MG |
592 | { |
593 | int err; | |
594 | ||
705ececd | 595 | CHECK_RETURN(device_create_file(dev, &dev_attr_device_id)); |
705ececd MG |
596 | CHECK_RETURN(device_create_file(dev, &dev_attr_firmware_version)); |
597 | CHECK_RETURN(device_create_file(dev, &dev_attr_midi_postprocess)); | |
705ececd | 598 | CHECK_RETURN(device_create_file(dev, &dev_attr_serial_number)); |
705ececd | 599 | |
1027f476 | 600 | #ifdef CONFIG_LINE6_USB_RAW |
705ececd MG |
601 | CHECK_RETURN(device_create_file(dev, &dev_attr_raw)); |
602 | #endif | |
603 | ||
604 | return 0; | |
605 | } | |
606 | ||
607 | /* | |
1027f476 | 608 | Try to init POD device. |
705ececd | 609 | */ |
e1a164d7 MG |
610 | static int pod_try_init(struct usb_interface *interface, |
611 | struct usb_line6_pod *pod) | |
705ececd MG |
612 | { |
613 | int err; | |
614 | struct usb_line6 *line6 = &pod->line6; | |
615 | ||
e1a164d7 MG |
616 | init_timer(&pod->startup_timer); |
617 | INIT_WORK(&pod->startup_work, pod_startup5); | |
618 | ||
0fdef36a GKH |
619 | if ((interface == NULL) || (pod == NULL)) |
620 | return -ENODEV; | |
705ececd | 621 | |
705ececd | 622 | /* initialize USB buffers: */ |
0fdef36a GKH |
623 | err = line6_dumpreq_init(&pod->dumpreq, pod_request_channel, |
624 | sizeof(pod_request_channel)); | |
625 | if (err < 0) { | |
705ececd | 626 | dev_err(&interface->dev, "Out of memory\n"); |
705ececd MG |
627 | return -ENOMEM; |
628 | } | |
629 | ||
705ececd | 630 | /* create sysfs entries: */ |
0fdef36a | 631 | err = pod_create_files2(&interface->dev); |
027360c5 | 632 | if (err < 0) |
705ececd | 633 | return err; |
705ececd MG |
634 | |
635 | /* initialize audio system: */ | |
0fdef36a | 636 | err = line6_init_audio(line6); |
027360c5 | 637 | if (err < 0) |
705ececd | 638 | return err; |
705ececd MG |
639 | |
640 | /* initialize MIDI subsystem: */ | |
0fdef36a | 641 | err = line6_init_midi(line6); |
027360c5 | 642 | if (err < 0) |
705ececd | 643 | return err; |
705ececd MG |
644 | |
645 | /* initialize PCM subsystem: */ | |
0fdef36a | 646 | err = line6_init_pcm(line6, &pod_pcm_properties); |
027360c5 | 647 | if (err < 0) |
705ececd | 648 | return err; |
705ececd | 649 | |
1027f476 | 650 | /* register monitor control: */ |
027360c5 GKH |
651 | err = snd_ctl_add(line6->card, |
652 | snd_ctl_new1(&pod_control_monitor, line6->line6pcm)); | |
653 | if (err < 0) | |
705ececd | 654 | return err; |
705ececd | 655 | |
1027f476 | 656 | /* |
e1a164d7 MG |
657 | When the sound card is registered at this point, the PODxt Live |
658 | displays "Invalid Code Error 07", so we do it later in the event | |
659 | handler. | |
660 | */ | |
1027f476 | 661 | |
0fdef36a | 662 | if (pod->line6.properties->capabilities & LINE6_BIT_CONTROL) { |
2c35dc21 | 663 | pod->monitor_level = POD_system_invalid; |
1027f476 MG |
664 | |
665 | /* initiate startup procedure: */ | |
666 | pod_startup1(pod); | |
705ececd MG |
667 | } |
668 | ||
669 | return 0; | |
670 | } | |
671 | ||
1027f476 MG |
672 | /* |
673 | Init POD device (and clean up in case of failure). | |
674 | */ | |
675 | int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) | |
676 | { | |
677 | int err = pod_try_init(interface, pod); | |
678 | ||
027360c5 | 679 | if (err < 0) |
1027f476 | 680 | pod_destruct(interface); |
1027f476 MG |
681 | |
682 | return err; | |
683 | } | |
684 | ||
705ececd MG |
685 | /* |
686 | POD device disconnected. | |
687 | */ | |
1027f476 | 688 | void line6_pod_disconnect(struct usb_interface *interface) |
705ececd MG |
689 | { |
690 | struct usb_line6_pod *pod; | |
691 | ||
0fdef36a GKH |
692 | if (interface == NULL) |
693 | return; | |
705ececd MG |
694 | pod = usb_get_intfdata(interface); |
695 | ||
0fdef36a | 696 | if (pod != NULL) { |
705ececd MG |
697 | struct snd_line6_pcm *line6pcm = pod->line6.line6pcm; |
698 | struct device *dev = &interface->dev; | |
699 | ||
027360c5 | 700 | if (line6pcm != NULL) |
1027f476 | 701 | line6_pcm_disconnect(line6pcm); |
705ececd | 702 | |
0fdef36a | 703 | if (dev != NULL) { |
705ececd | 704 | /* remove sysfs entries: */ |
e1a164d7 MG |
705 | line6_pod_remove_files(pod->firmware_version, |
706 | pod->line6. | |
707 | properties->device_bit, dev); | |
705ececd | 708 | |
705ececd | 709 | device_remove_file(dev, &dev_attr_device_id); |
705ececd MG |
710 | device_remove_file(dev, &dev_attr_firmware_version); |
711 | device_remove_file(dev, &dev_attr_midi_postprocess); | |
705ececd | 712 | device_remove_file(dev, &dev_attr_serial_number); |
705ececd | 713 | |
1027f476 | 714 | #ifdef CONFIG_LINE6_USB_RAW |
705ececd MG |
715 | device_remove_file(dev, &dev_attr_raw); |
716 | #endif | |
717 | } | |
718 | } | |
719 | ||
720 | pod_destruct(interface); | |
721 | } |