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" | |
1027f476 | 18 | #include "driver.h" |
705ececd MG |
19 | #include "playback.h" |
20 | #include "pod.h" | |
21 | ||
705ececd | 22 | #define POD_SYSEX_CODE 3 |
e1a164d7 | 23 | #define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ |
705ececd | 24 | |
e1a164d7 | 25 | /* *INDENT-OFF* */ |
705ececd MG |
26 | |
27 | enum { | |
705ececd MG |
28 | POD_SYSEX_SAVE = 0x24, |
29 | POD_SYSEX_SYSTEM = 0x56, | |
30 | POD_SYSEX_SYSTEMREQ = 0x57, | |
31 | /* POD_SYSEX_UPDATE = 0x6c, */ /* software update! */ | |
32 | POD_SYSEX_STORE = 0x71, | |
33 | POD_SYSEX_FINISH = 0x72, | |
34 | POD_SYSEX_DUMPMEM = 0x73, | |
35 | POD_SYSEX_DUMP = 0x74, | |
36 | POD_SYSEX_DUMPREQ = 0x75 | |
0a1eb4e8 SH |
37 | |
38 | /* dumps entire internal memory of PODxt Pro */ | |
39 | /* POD_SYSEX_DUMPMEM2 = 0x76 */ | |
705ececd MG |
40 | }; |
41 | ||
42 | enum { | |
7936095f SH |
43 | POD_MONITOR_LEVEL = 0x04, |
44 | POD_SYSTEM_INVALID = 0x10000 | |
705ececd MG |
45 | }; |
46 | ||
e1a164d7 MG |
47 | /* *INDENT-ON* */ |
48 | ||
705ececd MG |
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 | ||
705ececd MG |
61 | static struct snd_ratden pod_ratden = { |
62 | .num_min = 78125, | |
63 | .num_max = 78125, | |
64 | .num_step = 1, | |
65 | .den = 2 | |
66 | }; | |
67 | ||
68 | static struct line6_pcm_properties pod_pcm_properties = { | |
1027f476 | 69 | .snd_line6_playback_hw = { |
e1a164d7 MG |
70 | .info = (SNDRV_PCM_INFO_MMAP | |
71 | SNDRV_PCM_INFO_INTERLEAVED | | |
72 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
73 | SNDRV_PCM_INFO_MMAP_VALID | | |
74 | SNDRV_PCM_INFO_PAUSE | | |
1027f476 | 75 | #ifdef CONFIG_PM |
e1a164d7 | 76 | SNDRV_PCM_INFO_RESUME | |
1027f476 | 77 | #endif |
e1a164d7 MG |
78 | SNDRV_PCM_INFO_SYNC_START), |
79 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | |
80 | .rates = SNDRV_PCM_RATE_KNOT, | |
81 | .rate_min = 39062, | |
82 | .rate_max = 39063, | |
83 | .channels_min = 2, | |
84 | .channels_max = 2, | |
85 | .buffer_bytes_max = 60000, | |
86 | .period_bytes_min = 64, | |
87 | .period_bytes_max = 8192, | |
88 | .periods_min = 1, | |
89 | .periods_max = 1024}, | |
1027f476 | 90 | .snd_line6_capture_hw = { |
e1a164d7 MG |
91 | .info = (SNDRV_PCM_INFO_MMAP | |
92 | SNDRV_PCM_INFO_INTERLEAVED | | |
93 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
94 | SNDRV_PCM_INFO_MMAP_VALID | | |
1027f476 | 95 | #ifdef CONFIG_PM |
e1a164d7 | 96 | SNDRV_PCM_INFO_RESUME | |
1027f476 | 97 | #endif |
e1a164d7 MG |
98 | SNDRV_PCM_INFO_SYNC_START), |
99 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | |
100 | .rates = SNDRV_PCM_RATE_KNOT, | |
101 | .rate_min = 39062, | |
102 | .rate_max = 39063, | |
103 | .channels_min = 2, | |
104 | .channels_max = 2, | |
105 | .buffer_bytes_max = 60000, | |
106 | .period_bytes_min = 64, | |
107 | .period_bytes_max = 8192, | |
108 | .periods_min = 1, | |
109 | .periods_max = 1024}, | |
705ececd | 110 | .snd_line6_rates = { |
e1a164d7 MG |
111 | .nrats = 1, |
112 | .rats = &pod_ratden}, | |
705ececd MG |
113 | .bytes_per_frame = POD_BYTES_PER_FRAME |
114 | }; | |
115 | ||
e1a164d7 | 116 | static const char pod_version_header[] = { |
1027f476 MG |
117 | 0xf2, 0x7e, 0x7f, 0x06, 0x02 |
118 | }; | |
119 | ||
1027f476 MG |
120 | /* forward declarations: */ |
121 | static void pod_startup2(unsigned long data); | |
122 | static void pod_startup3(struct usb_line6_pod *pod); | |
705ececd | 123 | |
e1a164d7 MG |
124 | static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, |
125 | int size) | |
705ececd | 126 | { |
e1a164d7 MG |
127 | return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, |
128 | size); | |
705ececd MG |
129 | } |
130 | ||
705ececd MG |
131 | /* |
132 | Process a completely received message. | |
133 | */ | |
1027f476 | 134 | void line6_pod_process_message(struct usb_line6_pod *pod) |
705ececd MG |
135 | { |
136 | const unsigned char *buf = pod->line6.buffer_message; | |
137 | ||
4e6a8ffb SH |
138 | if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { |
139 | pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; | |
140 | pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | | |
141 | (int) buf[10]; | |
142 | pod_startup3(pod); | |
143 | return; | |
705ececd MG |
144 | } |
145 | ||
4e6a8ffb SH |
146 | /* Only look for sysex messages from this device */ |
147 | if (buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE) && | |
148 | buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN)) { | |
149 | return; | |
150 | } | |
c19e9461 | 151 | if (memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) != 0) |
4e6a8ffb | 152 | return; |
4e6a8ffb SH |
153 | |
154 | if (buf[5] == POD_SYSEX_SYSTEM && buf[6] == POD_MONITOR_LEVEL) { | |
155 | short value = ((int)buf[7] << 12) | ((int)buf[8] << 8) | | |
156 | ((int)buf[9] << 4) | (int)buf[10]; | |
157 | pod->monitor_level = value; | |
705ececd MG |
158 | } |
159 | } | |
160 | ||
705ececd MG |
161 | /* |
162 | Transmit PODxt Pro control parameter. | |
163 | */ | |
e1a164d7 | 164 | void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, |
5b9bd2ad | 165 | u8 value) |
705ececd | 166 | { |
79038f61 | 167 | line6_transmit_parameter(&pod->line6, param, value); |
705ececd MG |
168 | } |
169 | ||
705ececd | 170 | /* |
1027f476 | 171 | Send system parameter (from integer). |
705ececd | 172 | */ |
e1a164d7 MG |
173 | static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, |
174 | int code) | |
705ececd MG |
175 | { |
176 | char *sysex; | |
177 | static const int size = 5; | |
705ececd | 178 | |
705ececd | 179 | sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); |
0fdef36a | 180 | if (!sysex) |
1027f476 | 181 | return -ENOMEM; |
705ececd MG |
182 | sysex[SYSEX_DATA_OFS] = code; |
183 | sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; | |
e1a164d7 MG |
184 | sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; |
185 | sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f; | |
186 | sysex[SYSEX_DATA_OFS + 4] = (value) & 0x0f; | |
705ececd MG |
187 | line6_send_sysex_message(&pod->line6, sysex, size); |
188 | kfree(sysex); | |
1027f476 MG |
189 | return 0; |
190 | } | |
191 | ||
705ececd MG |
192 | /* |
193 | "read" request on "serial_number" special file. | |
194 | */ | |
77491e52 GKH |
195 | static ssize_t pod_get_serial_number(struct device *dev, |
196 | struct device_attribute *attr, char *buf) | |
705ececd MG |
197 | { |
198 | struct usb_interface *interface = to_usb_interface(dev); | |
199 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
200 | return sprintf(buf, "%d\n", pod->serial_number); | |
201 | } | |
202 | ||
203 | /* | |
204 | "read" request on "firmware_version" special file. | |
205 | */ | |
77491e52 GKH |
206 | static ssize_t pod_get_firmware_version(struct device *dev, |
207 | struct device_attribute *attr, | |
208 | char *buf) | |
705ececd MG |
209 | { |
210 | struct usb_interface *interface = to_usb_interface(dev); | |
211 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
0fdef36a GKH |
212 | return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100, |
213 | pod->firmware_version % 100); | |
705ececd MG |
214 | } |
215 | ||
216 | /* | |
217 | "read" request on "device_id" special file. | |
218 | */ | |
77491e52 GKH |
219 | static ssize_t pod_get_device_id(struct device *dev, |
220 | struct device_attribute *attr, char *buf) | |
705ececd MG |
221 | { |
222 | struct usb_interface *interface = to_usb_interface(dev); | |
223 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
224 | return sprintf(buf, "%d\n", pod->device_id); | |
225 | } | |
226 | ||
1027f476 MG |
227 | /* |
228 | POD startup procedure. | |
229 | This is a sequence of functions with special requirements (e.g., must | |
230 | not run immediately after initialization, must not run in interrupt | |
231 | context). After the last one has finished, the device is ready to use. | |
232 | */ | |
233 | ||
234 | static void pod_startup1(struct usb_line6_pod *pod) | |
235 | { | |
e1a164d7 | 236 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT); |
1027f476 MG |
237 | |
238 | /* delay startup procedure: */ | |
e1a164d7 MG |
239 | line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, |
240 | (unsigned long)pod); | |
1027f476 MG |
241 | } |
242 | ||
243 | static void pod_startup2(unsigned long data) | |
244 | { | |
245 | struct usb_line6_pod *pod = (struct usb_line6_pod *)data; | |
1027f476 | 246 | struct usb_line6 *line6 = &pod->line6; |
e1a164d7 | 247 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ); |
1027f476 MG |
248 | |
249 | /* request firmware version: */ | |
250 | line6_version_request_async(line6); | |
705ececd MG |
251 | } |
252 | ||
09fda10a | 253 | static void pod_startup3(struct usb_line6_pod *pod) |
1027f476 | 254 | { |
e1a164d7 | 255 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_WORKQUEUE); |
1027f476 MG |
256 | |
257 | /* schedule work for global work queue: */ | |
258 | schedule_work(&pod->startup_work); | |
259 | } | |
260 | ||
09fda10a | 261 | static void pod_startup4(struct work_struct *work) |
1027f476 | 262 | { |
e1a164d7 MG |
263 | struct usb_line6_pod *pod = |
264 | container_of(work, struct usb_line6_pod, startup_work); | |
1027f476 MG |
265 | struct usb_line6 *line6 = &pod->line6; |
266 | ||
e1a164d7 | 267 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_SETUP); |
1027f476 MG |
268 | |
269 | /* serial number: */ | |
270 | line6_read_serial_number(&pod->line6, &pod->serial_number); | |
271 | ||
272 | /* ALSA audio interface: */ | |
273 | line6_register_audio(line6); | |
1027f476 MG |
274 | } |
275 | ||
705ececd | 276 | /* POD special files: */ |
705ececd | 277 | static DEVICE_ATTR(device_id, S_IRUGO, pod_get_device_id, line6_nop_write); |
e1a164d7 MG |
278 | static DEVICE_ATTR(firmware_version, S_IRUGO, pod_get_firmware_version, |
279 | line6_nop_write); | |
e1a164d7 MG |
280 | static DEVICE_ATTR(serial_number, S_IRUGO, pod_get_serial_number, |
281 | line6_nop_write); | |
705ececd | 282 | |
1027f476 MG |
283 | /* control info callback */ |
284 | static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol, | |
285 | struct snd_ctl_elem_info *uinfo) | |
286 | { | |
287 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
288 | uinfo->count = 1; | |
289 | uinfo->value.integer.min = 0; | |
290 | uinfo->value.integer.max = 65535; | |
291 | return 0; | |
292 | } | |
293 | ||
294 | /* control get callback */ | |
295 | static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol, | |
296 | struct snd_ctl_elem_value *ucontrol) | |
297 | { | |
298 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
299 | struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; | |
2c35dc21 | 300 | ucontrol->value.integer.value[0] = pod->monitor_level; |
1027f476 MG |
301 | return 0; |
302 | } | |
303 | ||
304 | /* control put callback */ | |
305 | static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol, | |
306 | struct snd_ctl_elem_value *ucontrol) | |
307 | { | |
308 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
309 | struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; | |
310 | ||
2c35dc21 | 311 | if (ucontrol->value.integer.value[0] == pod->monitor_level) |
1027f476 MG |
312 | return 0; |
313 | ||
2c35dc21 | 314 | pod->monitor_level = ucontrol->value.integer.value[0]; |
e1a164d7 | 315 | pod_set_system_param_int(pod, ucontrol->value.integer.value[0], |
7936095f | 316 | POD_MONITOR_LEVEL); |
1027f476 MG |
317 | return 1; |
318 | } | |
319 | ||
320 | /* control definition */ | |
321 | static struct snd_kcontrol_new pod_control_monitor = { | |
322 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
323 | .name = "Monitor Playback Volume", | |
324 | .index = 0, | |
325 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
326 | .info = snd_pod_control_monitor_info, | |
327 | .get = snd_pod_control_monitor_get, | |
328 | .put = snd_pod_control_monitor_put | |
329 | }; | |
330 | ||
705ececd MG |
331 | /* |
332 | POD destructor. | |
333 | */ | |
334 | static void pod_destruct(struct usb_interface *interface) | |
335 | { | |
336 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | |
705ececd | 337 | |
0fdef36a GKH |
338 | if (pod == NULL) |
339 | return; | |
188e6645 | 340 | line6_cleanup_audio(&pod->line6); |
705ececd | 341 | |
e1a164d7 MG |
342 | del_timer(&pod->startup_timer); |
343 | cancel_work_sync(&pod->startup_work); | |
705ececd MG |
344 | } |
345 | ||
346 | /* | |
347 | Create sysfs entries. | |
348 | */ | |
b702ed25 | 349 | static int pod_create_files2(struct device *dev) |
705ececd MG |
350 | { |
351 | int err; | |
352 | ||
705ececd | 353 | CHECK_RETURN(device_create_file(dev, &dev_attr_device_id)); |
705ececd | 354 | CHECK_RETURN(device_create_file(dev, &dev_attr_firmware_version)); |
705ececd | 355 | CHECK_RETURN(device_create_file(dev, &dev_attr_serial_number)); |
705ececd MG |
356 | return 0; |
357 | } | |
358 | ||
359 | /* | |
1027f476 | 360 | Try to init POD device. |
705ececd | 361 | */ |
e1a164d7 MG |
362 | static int pod_try_init(struct usb_interface *interface, |
363 | struct usb_line6_pod *pod) | |
705ececd MG |
364 | { |
365 | int err; | |
366 | struct usb_line6 *line6 = &pod->line6; | |
367 | ||
e1a164d7 | 368 | init_timer(&pod->startup_timer); |
09fda10a | 369 | INIT_WORK(&pod->startup_work, pod_startup4); |
e1a164d7 | 370 | |
0fdef36a GKH |
371 | if ((interface == NULL) || (pod == NULL)) |
372 | return -ENODEV; | |
705ececd | 373 | |
705ececd | 374 | /* create sysfs entries: */ |
0fdef36a | 375 | err = pod_create_files2(&interface->dev); |
027360c5 | 376 | if (err < 0) |
705ececd | 377 | return err; |
705ececd MG |
378 | |
379 | /* initialize audio system: */ | |
0fdef36a | 380 | err = line6_init_audio(line6); |
027360c5 | 381 | if (err < 0) |
705ececd | 382 | return err; |
705ececd MG |
383 | |
384 | /* initialize MIDI subsystem: */ | |
0fdef36a | 385 | err = line6_init_midi(line6); |
027360c5 | 386 | if (err < 0) |
705ececd | 387 | return err; |
705ececd MG |
388 | |
389 | /* initialize PCM subsystem: */ | |
0fdef36a | 390 | err = line6_init_pcm(line6, &pod_pcm_properties); |
027360c5 | 391 | if (err < 0) |
705ececd | 392 | return err; |
705ececd | 393 | |
1027f476 | 394 | /* register monitor control: */ |
027360c5 GKH |
395 | err = snd_ctl_add(line6->card, |
396 | snd_ctl_new1(&pod_control_monitor, line6->line6pcm)); | |
397 | if (err < 0) | |
705ececd | 398 | return err; |
705ececd | 399 | |
1027f476 | 400 | /* |
e1a164d7 MG |
401 | When the sound card is registered at this point, the PODxt Live |
402 | displays "Invalid Code Error 07", so we do it later in the event | |
403 | handler. | |
404 | */ | |
1027f476 | 405 | |
0fdef36a | 406 | if (pod->line6.properties->capabilities & LINE6_BIT_CONTROL) { |
7936095f | 407 | pod->monitor_level = POD_SYSTEM_INVALID; |
1027f476 MG |
408 | |
409 | /* initiate startup procedure: */ | |
410 | pod_startup1(pod); | |
705ececd MG |
411 | } |
412 | ||
413 | return 0; | |
414 | } | |
415 | ||
1027f476 MG |
416 | /* |
417 | Init POD device (and clean up in case of failure). | |
418 | */ | |
419 | int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) | |
420 | { | |
421 | int err = pod_try_init(interface, pod); | |
422 | ||
027360c5 | 423 | if (err < 0) |
1027f476 | 424 | pod_destruct(interface); |
1027f476 MG |
425 | |
426 | return err; | |
427 | } | |
428 | ||
705ececd MG |
429 | /* |
430 | POD device disconnected. | |
431 | */ | |
1027f476 | 432 | void line6_pod_disconnect(struct usb_interface *interface) |
705ececd MG |
433 | { |
434 | struct usb_line6_pod *pod; | |
435 | ||
0fdef36a GKH |
436 | if (interface == NULL) |
437 | return; | |
705ececd MG |
438 | pod = usb_get_intfdata(interface); |
439 | ||
0fdef36a | 440 | if (pod != NULL) { |
705ececd MG |
441 | struct snd_line6_pcm *line6pcm = pod->line6.line6pcm; |
442 | struct device *dev = &interface->dev; | |
443 | ||
027360c5 | 444 | if (line6pcm != NULL) |
1027f476 | 445 | line6_pcm_disconnect(line6pcm); |
705ececd | 446 | |
0fdef36a | 447 | if (dev != NULL) { |
705ececd | 448 | /* remove sysfs entries: */ |
705ececd | 449 | device_remove_file(dev, &dev_attr_device_id); |
705ececd | 450 | device_remove_file(dev, &dev_attr_firmware_version); |
705ececd | 451 | device_remove_file(dev, &dev_attr_serial_number); |
705ececd MG |
452 | } |
453 | } | |
454 | ||
455 | pod_destruct(interface); | |
456 | } |