diff options
| author | Takashi Iwai <tiwai@suse.de> | 2015-01-12 16:29:57 -0500 |
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2015-01-12 16:29:57 -0500 |
| commit | 61864d844c296933d40c02683252bbea5193b101 (patch) | |
| tree | af106a2061e9b33dfbffdf761406cf5ff890ba2d /sound/usb/line6 | |
| parent | d29b854fe9036a505af373ac485b2110ebae6ccd (diff) | |
ALSA: move line6 usb driver into sound/usb
Promote line6 driver from staging to sound/usb/line6 directory, and
maintain through sound subsystem tree.
This commit just moves the code and adapts Makefile / Kconfig.
The further renames and misc cleanups will follow.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/line6')
| -rw-r--r-- | sound/usb/line6/Kconfig | 38 | ||||
| -rw-r--r-- | sound/usb/line6/Makefile | 14 | ||||
| -rw-r--r-- | sound/usb/line6/audio.c | 71 | ||||
| -rw-r--r-- | sound/usb/line6/audio.h | 21 | ||||
| -rw-r--r-- | sound/usb/line6/capture.c | 433 | ||||
| -rw-r--r-- | sound/usb/line6/capture.h | 35 | ||||
| -rw-r--r-- | sound/usb/line6/driver.c | 1114 | ||||
| -rw-r--r-- | sound/usb/line6/driver.h | 228 | ||||
| -rw-r--r-- | sound/usb/line6/midi.c | 321 | ||||
| -rw-r--r-- | sound/usb/line6/midi.h | 72 | ||||
| -rw-r--r-- | sound/usb/line6/midibuf.c | 270 | ||||
| -rw-r--r-- | sound/usb/line6/midibuf.h | 38 | ||||
| -rw-r--r-- | sound/usb/line6/pcm.c | 527 | ||||
| -rw-r--r-- | sound/usb/line6/pcm.h | 374 | ||||
| -rw-r--r-- | sound/usb/line6/playback.c | 593 | ||||
| -rw-r--r-- | sound/usb/line6/playback.h | 41 | ||||
| -rw-r--r-- | sound/usb/line6/pod.c | 453 | ||||
| -rw-r--r-- | sound/usb/line6/pod.h | 92 | ||||
| -rw-r--r-- | sound/usb/line6/podhd.c | 156 | ||||
| -rw-r--r-- | sound/usb/line6/podhd.h | 29 | ||||
| -rw-r--r-- | sound/usb/line6/revision.h | 4 | ||||
| -rw-r--r-- | sound/usb/line6/toneport.c | 465 | ||||
| -rw-r--r-- | sound/usb/line6/toneport.h | 51 | ||||
| -rw-r--r-- | sound/usb/line6/usbdefs.h | 27 | ||||
| -rw-r--r-- | sound/usb/line6/variax.c | 239 | ||||
| -rw-r--r-- | sound/usb/line6/variax.h | 70 |
26 files changed, 5776 insertions, 0 deletions
diff --git a/sound/usb/line6/Kconfig b/sound/usb/line6/Kconfig new file mode 100644 index 000000000000..4f1219b4c692 --- /dev/null +++ b/sound/usb/line6/Kconfig | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | menuconfig LINE6_USB | ||
| 2 | tristate "Line6 USB support" | ||
| 3 | depends on USB && SND | ||
| 4 | select SND_RAWMIDI | ||
| 5 | select SND_PCM | ||
| 6 | help | ||
| 7 | This is a driver for the guitar amp, cab, and effects modeller | ||
| 8 | PODxt Pro by Line6 (and similar devices), supporting the | ||
| 9 | following features: | ||
| 10 | * Reading/writing individual parameters | ||
| 11 | * Reading/writing complete channel, effects setup, and amp | ||
| 12 | setup data | ||
| 13 | * Channel switching | ||
| 14 | * Virtual MIDI interface | ||
| 15 | * Tuner access | ||
| 16 | * Playback/capture/mixer device for any ALSA-compatible PCM | ||
| 17 | audio application | ||
| 18 | * Signal routing (record clean/processed guitar signal, | ||
| 19 | re-amping) | ||
| 20 | |||
| 21 | Preliminary support for the Variax Workbench and TonePort | ||
| 22 | devices is included. | ||
| 23 | |||
| 24 | if LINE6_USB | ||
| 25 | |||
| 26 | config LINE6_USB_IMPULSE_RESPONSE | ||
| 27 | bool "measure impulse response" | ||
| 28 | default n | ||
| 29 | help | ||
| 30 | Say Y here to add code to measure the impulse response of a Line6 | ||
| 31 | device. This is more accurate than user-space methods since it | ||
| 32 | bypasses any PCM data buffering (e.g., by ALSA or jack). This is | ||
| 33 | useful for assessing the performance of new devices, but is not | ||
| 34 | required for normal operation. | ||
| 35 | |||
| 36 | If unsure, say N. | ||
| 37 | |||
| 38 | endif # LINE6_USB | ||
diff --git a/sound/usb/line6/Makefile b/sound/usb/line6/Makefile new file mode 100644 index 000000000000..ae5c374b0f87 --- /dev/null +++ b/sound/usb/line6/Makefile | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | obj-$(CONFIG_LINE6_USB) += line6usb.o | ||
| 2 | |||
| 3 | line6usb-y := \ | ||
| 4 | audio.o \ | ||
| 5 | capture.o \ | ||
| 6 | driver.o \ | ||
| 7 | midi.o \ | ||
| 8 | midibuf.o \ | ||
| 9 | pcm.o \ | ||
| 10 | playback.o \ | ||
| 11 | pod.o \ | ||
| 12 | toneport.o \ | ||
| 13 | variax.o \ | ||
| 14 | podhd.o | ||
diff --git a/sound/usb/line6/audio.c b/sound/usb/line6/audio.c new file mode 100644 index 000000000000..171d80c1b020 --- /dev/null +++ b/sound/usb/line6/audio.c | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 <sound/core.h> | ||
| 13 | #include <sound/initval.h> | ||
| 14 | #include <linux/export.h> | ||
| 15 | |||
| 16 | #include "driver.h" | ||
| 17 | #include "audio.h" | ||
| 18 | |||
| 19 | /* | ||
| 20 | Initialize the Line6 USB audio system. | ||
| 21 | */ | ||
| 22 | int line6_init_audio(struct usb_line6 *line6) | ||
| 23 | { | ||
| 24 | struct snd_card *card; | ||
| 25 | int err; | ||
| 26 | |||
| 27 | err = snd_card_new(line6->ifcdev, | ||
| 28 | SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, | ||
| 29 | THIS_MODULE, 0, &card); | ||
| 30 | if (err < 0) | ||
| 31 | return err; | ||
| 32 | |||
| 33 | line6->card = card; | ||
| 34 | |||
| 35 | strcpy(card->id, line6->properties->id); | ||
| 36 | strcpy(card->driver, DRIVER_NAME); | ||
| 37 | strcpy(card->shortname, line6->properties->name); | ||
| 38 | /* longname is 80 chars - see asound.h */ | ||
| 39 | sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name, | ||
| 40 | dev_name(line6->ifcdev)); | ||
| 41 | return 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | /* | ||
| 45 | Register the Line6 USB audio system. | ||
| 46 | */ | ||
| 47 | int line6_register_audio(struct usb_line6 *line6) | ||
| 48 | { | ||
| 49 | int err; | ||
| 50 | |||
| 51 | err = snd_card_register(line6->card); | ||
| 52 | if (err < 0) | ||
| 53 | return err; | ||
| 54 | |||
| 55 | return 0; | ||
| 56 | } | ||
| 57 | |||
| 58 | /* | ||
| 59 | Cleanup the Line6 USB audio system. | ||
| 60 | */ | ||
| 61 | void line6_cleanup_audio(struct usb_line6 *line6) | ||
| 62 | { | ||
| 63 | struct snd_card *card = line6->card; | ||
| 64 | |||
| 65 | if (card == NULL) | ||
| 66 | return; | ||
| 67 | |||
| 68 | snd_card_disconnect(card); | ||
| 69 | snd_card_free(card); | ||
| 70 | line6->card = NULL; | ||
| 71 | } | ||
diff --git a/sound/usb/line6/audio.h b/sound/usb/line6/audio.h new file mode 100644 index 000000000000..5f8a09a0fa95 --- /dev/null +++ b/sound/usb/line6/audio.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 | #include "driver.h" | ||
| 16 | |||
| 17 | extern void line6_cleanup_audio(struct usb_line6 *); | ||
| 18 | extern int line6_init_audio(struct usb_line6 *); | ||
| 19 | extern int line6_register_audio(struct usb_line6 *); | ||
| 20 | |||
| 21 | #endif | ||
diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c new file mode 100644 index 000000000000..f24c7c5e0a3e --- /dev/null +++ b/sound/usb/line6/capture.c | |||
| @@ -0,0 +1,433 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 <linux/slab.h> | ||
| 13 | #include <sound/core.h> | ||
| 14 | #include <sound/pcm.h> | ||
| 15 | #include <sound/pcm_params.h> | ||
| 16 | |||
| 17 | #include "audio.h" | ||
| 18 | #include "capture.h" | ||
| 19 | #include "driver.h" | ||
| 20 | #include "pcm.h" | ||
| 21 | #include "pod.h" | ||
| 22 | |||
| 23 | /* | ||
| 24 | Find a free URB and submit it. | ||
| 25 | */ | ||
| 26 | static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) | ||
| 27 | { | ||
| 28 | int index; | ||
| 29 | unsigned long flags; | ||
| 30 | int i, urb_size; | ||
| 31 | int ret; | ||
| 32 | struct urb *urb_in; | ||
| 33 | |||
| 34 | spin_lock_irqsave(&line6pcm->lock_audio_in, flags); | ||
| 35 | index = | ||
| 36 | find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); | ||
| 37 | |||
| 38 | if (index < 0 || index >= LINE6_ISO_BUFFERS) { | ||
| 39 | spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); | ||
| 40 | dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); | ||
| 41 | return -EINVAL; | ||
| 42 | } | ||
| 43 | |||
| 44 | urb_in = line6pcm->urb_audio_in[index]; | ||
| 45 | urb_size = 0; | ||
| 46 | |||
| 47 | for (i = 0; i < LINE6_ISO_PACKETS; ++i) { | ||
| 48 | struct usb_iso_packet_descriptor *fin = | ||
| 49 | &urb_in->iso_frame_desc[i]; | ||
| 50 | fin->offset = urb_size; | ||
| 51 | fin->length = line6pcm->max_packet_size; | ||
| 52 | urb_size += line6pcm->max_packet_size; | ||
| 53 | } | ||
| 54 | |||
| 55 | urb_in->transfer_buffer = | ||
| 56 | line6pcm->buffer_in + | ||
| 57 | index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; | ||
| 58 | urb_in->transfer_buffer_length = urb_size; | ||
| 59 | urb_in->context = line6pcm; | ||
| 60 | |||
| 61 | ret = usb_submit_urb(urb_in, GFP_ATOMIC); | ||
| 62 | |||
| 63 | if (ret == 0) | ||
| 64 | set_bit(index, &line6pcm->active_urb_in); | ||
| 65 | else | ||
| 66 | dev_err(line6pcm->line6->ifcdev, | ||
| 67 | "URB in #%d submission failed (%d)\n", index, ret); | ||
| 68 | |||
| 69 | spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); | ||
| 70 | return 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | /* | ||
| 74 | Submit all currently available capture URBs. | ||
| 75 | */ | ||
| 76 | int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm) | ||
| 77 | { | ||
| 78 | int ret, i; | ||
| 79 | |||
| 80 | for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { | ||
| 81 | ret = submit_audio_in_urb(line6pcm); | ||
| 82 | if (ret < 0) | ||
| 83 | return ret; | ||
| 84 | } | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | /* | ||
| 90 | Unlink all currently active capture URBs. | ||
| 91 | */ | ||
| 92 | void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) | ||
| 93 | { | ||
| 94 | unsigned int i; | ||
| 95 | |||
| 96 | for (i = LINE6_ISO_BUFFERS; i--;) { | ||
| 97 | if (test_bit(i, &line6pcm->active_urb_in)) { | ||
| 98 | if (!test_and_set_bit(i, &line6pcm->unlink_urb_in)) { | ||
| 99 | struct urb *u = line6pcm->urb_audio_in[i]; | ||
| 100 | |||
| 101 | usb_unlink_urb(u); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | /* | ||
| 108 | Wait until unlinking of all currently active capture URBs has been | ||
| 109 | finished. | ||
| 110 | */ | ||
| 111 | void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) | ||
| 112 | { | ||
| 113 | int timeout = HZ; | ||
| 114 | unsigned int i; | ||
| 115 | int alive; | ||
| 116 | |||
| 117 | do { | ||
| 118 | alive = 0; | ||
| 119 | for (i = LINE6_ISO_BUFFERS; i--;) { | ||
| 120 | if (test_bit(i, &line6pcm->active_urb_in)) | ||
| 121 | alive++; | ||
| 122 | } | ||
| 123 | if (!alive) | ||
| 124 | break; | ||
| 125 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
| 126 | schedule_timeout(1); | ||
| 127 | } while (--timeout > 0); | ||
| 128 | if (alive) | ||
| 129 | snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); | ||
| 130 | } | ||
| 131 | |||
| 132 | /* | ||
| 133 | Unlink all currently active capture URBs, and wait for finishing. | ||
| 134 | */ | ||
| 135 | void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) | ||
| 136 | { | ||
| 137 | line6_unlink_audio_in_urbs(line6pcm); | ||
| 138 | line6_wait_clear_audio_in_urbs(line6pcm); | ||
| 139 | } | ||
| 140 | |||
| 141 | /* | ||
| 142 | Copy data into ALSA capture buffer. | ||
| 143 | */ | ||
| 144 | void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize) | ||
| 145 | { | ||
| 146 | struct snd_pcm_substream *substream = | ||
| 147 | get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); | ||
| 148 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 149 | const int bytes_per_frame = line6pcm->properties->bytes_per_frame; | ||
| 150 | int frames = fsize / bytes_per_frame; | ||
| 151 | |||
| 152 | if (runtime == NULL) | ||
| 153 | return; | ||
| 154 | |||
| 155 | if (line6pcm->pos_in_done + frames > runtime->buffer_size) { | ||
| 156 | /* | ||
| 157 | The transferred area goes over buffer boundary, | ||
| 158 | copy two separate chunks. | ||
| 159 | */ | ||
| 160 | int len; | ||
| 161 | |||
| 162 | len = runtime->buffer_size - line6pcm->pos_in_done; | ||
| 163 | |||
| 164 | if (len > 0) { | ||
| 165 | memcpy(runtime->dma_area + | ||
| 166 | line6pcm->pos_in_done * bytes_per_frame, fbuf, | ||
| 167 | len * bytes_per_frame); | ||
| 168 | memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, | ||
| 169 | (frames - len) * bytes_per_frame); | ||
| 170 | } else { | ||
| 171 | /* this is somewhat paranoid */ | ||
| 172 | dev_err(line6pcm->line6->ifcdev, | ||
| 173 | "driver bug: len = %d\n", len); | ||
| 174 | } | ||
| 175 | } else { | ||
| 176 | /* copy single chunk */ | ||
| 177 | memcpy(runtime->dma_area + | ||
| 178 | line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize); | ||
| 179 | } | ||
| 180 | |||
| 181 | line6pcm->pos_in_done += frames; | ||
| 182 | if (line6pcm->pos_in_done >= runtime->buffer_size) | ||
| 183 | line6pcm->pos_in_done -= runtime->buffer_size; | ||
| 184 | } | ||
| 185 | |||
| 186 | void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) | ||
| 187 | { | ||
| 188 | struct snd_pcm_substream *substream = | ||
| 189 | get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); | ||
| 190 | |||
| 191 | line6pcm->bytes_in += length; | ||
| 192 | if (line6pcm->bytes_in >= line6pcm->period_in) { | ||
| 193 | line6pcm->bytes_in %= line6pcm->period_in; | ||
| 194 | snd_pcm_period_elapsed(substream); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm) | ||
| 199 | { | ||
| 200 | kfree(line6pcm->buffer_in); | ||
| 201 | line6pcm->buffer_in = NULL; | ||
| 202 | } | ||
| 203 | |||
| 204 | /* | ||
| 205 | * Callback for completed capture URB. | ||
| 206 | */ | ||
| 207 | static void audio_in_callback(struct urb *urb) | ||
| 208 | { | ||
| 209 | int i, index, length = 0, shutdown = 0; | ||
| 210 | unsigned long flags; | ||
| 211 | |||
| 212 | struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; | ||
| 213 | |||
| 214 | line6pcm->last_frame_in = urb->start_frame; | ||
| 215 | |||
| 216 | /* find index of URB */ | ||
| 217 | for (index = 0; index < LINE6_ISO_BUFFERS; ++index) | ||
| 218 | if (urb == line6pcm->urb_audio_in[index]) | ||
| 219 | break; | ||
| 220 | |||
| 221 | spin_lock_irqsave(&line6pcm->lock_audio_in, flags); | ||
| 222 | |||
| 223 | for (i = 0; i < LINE6_ISO_PACKETS; ++i) { | ||
| 224 | char *fbuf; | ||
| 225 | int fsize; | ||
| 226 | struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i]; | ||
| 227 | |||
| 228 | if (fin->status == -EXDEV) { | ||
| 229 | shutdown = 1; | ||
| 230 | break; | ||
| 231 | } | ||
| 232 | |||
| 233 | fbuf = urb->transfer_buffer + fin->offset; | ||
| 234 | fsize = fin->actual_length; | ||
| 235 | |||
| 236 | if (fsize > line6pcm->max_packet_size) { | ||
| 237 | dev_err(line6pcm->line6->ifcdev, | ||
| 238 | "driver and/or device bug: packet too large (%d > %d)\n", | ||
| 239 | fsize, line6pcm->max_packet_size); | ||
| 240 | } | ||
| 241 | |||
| 242 | length += fsize; | ||
| 243 | |||
| 244 | /* the following assumes LINE6_ISO_PACKETS == 1: */ | ||
| 245 | line6pcm->prev_fbuf = fbuf; | ||
| 246 | line6pcm->prev_fsize = fsize; | ||
| 247 | |||
| 248 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 249 | if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) | ||
| 250 | #endif | ||
| 251 | if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, | ||
| 252 | &line6pcm->flags) && (fsize > 0)) | ||
| 253 | line6_capture_copy(line6pcm, fbuf, fsize); | ||
| 254 | } | ||
| 255 | |||
| 256 | clear_bit(index, &line6pcm->active_urb_in); | ||
| 257 | |||
| 258 | if (test_and_clear_bit(index, &line6pcm->unlink_urb_in)) | ||
| 259 | shutdown = 1; | ||
| 260 | |||
| 261 | spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); | ||
| 262 | |||
| 263 | if (!shutdown) { | ||
| 264 | submit_audio_in_urb(line6pcm); | ||
| 265 | |||
| 266 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 267 | if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) | ||
| 268 | #endif | ||
| 269 | if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, | ||
| 270 | &line6pcm->flags)) | ||
| 271 | line6_capture_check_period(line6pcm, length); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | /* open capture callback */ | ||
| 276 | static int snd_line6_capture_open(struct snd_pcm_substream *substream) | ||
| 277 | { | ||
| 278 | int err; | ||
| 279 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 280 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
| 281 | |||
| 282 | err = snd_pcm_hw_constraint_ratdens(runtime, 0, | ||
| 283 | SNDRV_PCM_HW_PARAM_RATE, | ||
| 284 | (&line6pcm-> | ||
| 285 | properties->snd_line6_rates)); | ||
| 286 | if (err < 0) | ||
| 287 | return err; | ||
| 288 | |||
| 289 | runtime->hw = line6pcm->properties->snd_line6_capture_hw; | ||
| 290 | return 0; | ||
| 291 | } | ||
| 292 | |||
| 293 | /* close capture callback */ | ||
| 294 | static int snd_line6_capture_close(struct snd_pcm_substream *substream) | ||
| 295 | { | ||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | |||
| 299 | /* hw_params capture callback */ | ||
| 300 | static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, | ||
| 301 | struct snd_pcm_hw_params *hw_params) | ||
| 302 | { | ||
| 303 | int ret; | ||
| 304 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
| 305 | |||
| 306 | /* -- Florian Demski [FD] */ | ||
| 307 | /* don't ask me why, but this fixes the bug on my machine */ | ||
| 308 | if (line6pcm == NULL) { | ||
| 309 | if (substream->pcm == NULL) | ||
| 310 | return -ENOMEM; | ||
| 311 | if (substream->pcm->private_data == NULL) | ||
| 312 | return -ENOMEM; | ||
| 313 | substream->private_data = substream->pcm->private_data; | ||
| 314 | line6pcm = snd_pcm_substream_chip(substream); | ||
| 315 | } | ||
| 316 | /* -- [FD] end */ | ||
| 317 | |||
| 318 | ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); | ||
| 319 | |||
| 320 | if (ret < 0) | ||
| 321 | return ret; | ||
| 322 | |||
| 323 | ret = snd_pcm_lib_malloc_pages(substream, | ||
| 324 | params_buffer_bytes(hw_params)); | ||
| 325 | if (ret < 0) { | ||
| 326 | line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); | ||
| 327 | return ret; | ||
| 328 | } | ||
| 329 | |||
| 330 | line6pcm->period_in = params_period_bytes(hw_params); | ||
| 331 | return 0; | ||
| 332 | } | ||
| 333 | |||
| 334 | /* hw_free capture callback */ | ||
| 335 | static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) | ||
| 336 | { | ||
| 337 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
| 338 | |||
| 339 | line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); | ||
| 340 | return snd_pcm_lib_free_pages(substream); | ||
| 341 | } | ||
| 342 | |||
| 343 | /* trigger callback */ | ||
| 344 | int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd) | ||
| 345 | { | ||
| 346 | int err; | ||
| 347 | |||
| 348 | switch (cmd) { | ||
| 349 | case SNDRV_PCM_TRIGGER_START: | ||
| 350 | #ifdef CONFIG_PM | ||
| 351 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 352 | #endif | ||
| 353 | err = line6_pcm_acquire(line6pcm, | ||
| 354 | LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); | ||
| 355 | |||
| 356 | if (err < 0) | ||
| 357 | return err; | ||
| 358 | |||
| 359 | break; | ||
| 360 | |||
| 361 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 362 | #ifdef CONFIG_PM | ||
| 363 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 364 | #endif | ||
| 365 | err = line6_pcm_release(line6pcm, | ||
| 366 | LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); | ||
| 367 | |||
| 368 | if (err < 0) | ||
| 369 | return err; | ||
| 370 | |||
| 371 | break; | ||
| 372 | |||
| 373 | default: | ||
| 374 | return -EINVAL; | ||
| 375 | } | ||
| 376 | |||
| 377 | return 0; | ||
| 378 | } | ||
| 379 | |||
| 380 | /* capture pointer callback */ | ||
| 381 | static snd_pcm_uframes_t | ||
| 382 | snd_line6_capture_pointer(struct snd_pcm_substream *substream) | ||
| 383 | { | ||
| 384 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
| 385 | |||
| 386 | return line6pcm->pos_in_done; | ||
| 387 | } | ||
| 388 | |||
| 389 | /* capture operators */ | ||
| 390 | struct snd_pcm_ops snd_line6_capture_ops = { | ||
| 391 | .open = snd_line6_capture_open, | ||
| 392 | .close = snd_line6_capture_close, | ||
| 393 | .ioctl = snd_pcm_lib_ioctl, | ||
| 394 | .hw_params = snd_line6_capture_hw_params, | ||
| 395 | .hw_free = snd_line6_capture_hw_free, | ||
| 396 | .prepare = snd_line6_prepare, | ||
| 397 | .trigger = snd_line6_trigger, | ||
| 398 | .pointer = snd_line6_capture_pointer, | ||
| 399 | }; | ||
| 400 | |||
| 401 | int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) | ||
| 402 | { | ||
| 403 | struct usb_line6 *line6 = line6pcm->line6; | ||
| 404 | int i; | ||
| 405 | |||
| 406 | /* create audio URBs and fill in constant values: */ | ||
| 407 | for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { | ||
| 408 | struct urb *urb; | ||
| 409 | |||
| 410 | /* URB for audio in: */ | ||
| 411 | urb = line6pcm->urb_audio_in[i] = | ||
| 412 | usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); | ||
| 413 | |||
| 414 | if (urb == NULL) { | ||
| 415 | dev_err(line6->ifcdev, "Out of memory\n"); | ||
| 416 | return -ENOMEM; | ||
| 417 | } | ||
| 418 | |||
| 419 | urb->dev = line6->usbdev; | ||
| 420 | urb->pipe = | ||
| 421 | usb_rcvisocpipe(line6->usbdev, | ||
| 422 | line6->properties->ep_audio_r & | ||
| 423 | USB_ENDPOINT_NUMBER_MASK); | ||
| 424 | urb->transfer_flags = URB_ISO_ASAP; | ||
| 425 | urb->start_frame = -1; | ||
| 426 | urb->number_of_packets = LINE6_ISO_PACKETS; | ||
| 427 | urb->interval = LINE6_ISO_INTERVAL; | ||
| 428 | urb->error_count = 0; | ||
| 429 | urb->complete = audio_in_callback; | ||
| 430 | } | ||
| 431 | |||
| 432 | return 0; | ||
| 433 | } | ||
diff --git a/sound/usb/line6/capture.h b/sound/usb/line6/capture.h new file mode 100644 index 000000000000..4157bcb598a9 --- /dev/null +++ b/sound/usb/line6/capture.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 | #include <sound/pcm.h> | ||
| 16 | |||
| 17 | #include "driver.h" | ||
| 18 | #include "pcm.h" | ||
| 19 | |||
| 20 | extern struct snd_pcm_ops snd_line6_capture_ops; | ||
| 21 | |||
| 22 | extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, | ||
| 23 | int fsize); | ||
| 24 | extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm, | ||
| 25 | int length); | ||
| 26 | extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); | ||
| 27 | extern void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm); | ||
| 28 | extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm); | ||
| 29 | extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm); | ||
| 30 | extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm | ||
| 31 | *line6pcm); | ||
| 32 | extern void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm); | ||
| 33 | extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd); | ||
| 34 | |||
| 35 | #endif | ||
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c new file mode 100644 index 000000000000..fc852f6ab8bc --- /dev/null +++ b/sound/usb/line6/driver.c | |||
| @@ -0,0 +1,1114 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 <linux/kernel.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/slab.h> | ||
| 15 | #include <linux/usb.h> | ||
| 16 | |||
| 17 | #include "audio.h" | ||
| 18 | #include "capture.h" | ||
| 19 | #include "driver.h" | ||
| 20 | #include "midi.h" | ||
| 21 | #include "playback.h" | ||
| 22 | #include "pod.h" | ||
| 23 | #include "podhd.h" | ||
| 24 | #include "revision.h" | ||
| 25 | #include "toneport.h" | ||
| 26 | #include "usbdefs.h" | ||
| 27 | #include "variax.h" | ||
| 28 | |||
| 29 | #define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>" | ||
| 30 | #define DRIVER_DESC "Line6 USB Driver" | ||
| 31 | #define DRIVER_VERSION "0.9.1beta" DRIVER_REVISION | ||
| 32 | |||
| 33 | #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) | ||
| 34 | #define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) | ||
| 35 | |||
| 36 | /* table of devices that work with this driver */ | ||
| 37 | static const struct usb_device_id line6_id_table[] = { | ||
| 38 | { LINE6_DEVICE(0x4250), .driver_info = LINE6_BASSPODXT }, | ||
| 39 | { LINE6_DEVICE(0x4642), .driver_info = LINE6_BASSPODXTLIVE }, | ||
| 40 | { LINE6_DEVICE(0x4252), .driver_info = LINE6_BASSPODXTPRO }, | ||
| 41 | { LINE6_DEVICE(0x4750), .driver_info = LINE6_GUITARPORT }, | ||
| 42 | { LINE6_IF_NUM(0x5051, 1), .driver_info = LINE6_POCKETPOD }, | ||
| 43 | { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, | ||
| 44 | { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, | ||
| 45 | { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 }, | ||
| 46 | { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 }, | ||
| 47 | { LINE6_DEVICE(0x4153), .driver_info = LINE6_PODSTUDIO_GX }, | ||
| 48 | { LINE6_DEVICE(0x4150), .driver_info = LINE6_PODSTUDIO_UX1 }, | ||
| 49 | { LINE6_IF_NUM(0x4151, 0), .driver_info = LINE6_PODSTUDIO_UX2 }, | ||
| 50 | { LINE6_DEVICE(0x5044), .driver_info = LINE6_PODXT }, | ||
| 51 | { LINE6_IF_NUM(0x4650, 0), .driver_info = LINE6_PODXTLIVE_POD }, | ||
| 52 | { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX }, | ||
| 53 | { LINE6_DEVICE(0x5050), .driver_info = LINE6_PODXTPRO }, | ||
| 54 | { LINE6_DEVICE(0x4147), .driver_info = LINE6_TONEPORT_GX }, | ||
| 55 | { LINE6_DEVICE(0x4141), .driver_info = LINE6_TONEPORT_UX1 }, | ||
| 56 | { LINE6_IF_NUM(0x4142, 0), .driver_info = LINE6_TONEPORT_UX2 }, | ||
| 57 | { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX }, | ||
| 58 | {} | ||
| 59 | }; | ||
| 60 | |||
| 61 | MODULE_DEVICE_TABLE(usb, line6_id_table); | ||
| 62 | |||
| 63 | static const struct line6_properties line6_properties_table[] = { | ||
| 64 | [LINE6_BASSPODXT] = { | ||
| 65 | .id = "BassPODxt", | ||
| 66 | .name = "BassPODxt", | ||
| 67 | .capabilities = LINE6_CAP_CONTROL | ||
| 68 | | LINE6_CAP_PCM | ||
| 69 | | LINE6_CAP_HWMON, | ||
| 70 | .altsetting = 5, | ||
| 71 | .ep_ctrl_r = 0x84, | ||
| 72 | .ep_ctrl_w = 0x03, | ||
| 73 | .ep_audio_r = 0x82, | ||
| 74 | .ep_audio_w = 0x01, | ||
| 75 | }, | ||
| 76 | [LINE6_BASSPODXTLIVE] = { | ||
| 77 | .id = "BassPODxtLive", | ||
| 78 | .name = "BassPODxt Live", | ||
| 79 | .capabilities = LINE6_CAP_CONTROL | ||
| 80 | | LINE6_CAP_PCM | ||
| 81 | | LINE6_CAP_HWMON, | ||
| 82 | .altsetting = 1, | ||
| 83 | .ep_ctrl_r = 0x84, | ||
| 84 | .ep_ctrl_w = 0x03, | ||
| 85 | .ep_audio_r = 0x82, | ||
| 86 | .ep_audio_w = 0x01, | ||
| 87 | }, | ||
| 88 | [LINE6_BASSPODXTPRO] = { | ||
| 89 | .id = "BassPODxtPro", | ||
| 90 | .name = "BassPODxt Pro", | ||
| 91 | .capabilities = LINE6_CAP_CONTROL | ||
| 92 | | LINE6_CAP_PCM | ||
| 93 | | LINE6_CAP_HWMON, | ||
| 94 | .altsetting = 5, | ||
| 95 | .ep_ctrl_r = 0x84, | ||
| 96 | .ep_ctrl_w = 0x03, | ||
| 97 | .ep_audio_r = 0x82, | ||
| 98 | .ep_audio_w = 0x01, | ||
| 99 | }, | ||
| 100 | [LINE6_GUITARPORT] = { | ||
| 101 | .id = "GuitarPort", | ||
| 102 | .name = "GuitarPort", | ||
| 103 | .capabilities = LINE6_CAP_PCM, | ||
| 104 | .altsetting = 2, /* 1..4 seem to be ok */ | ||
| 105 | /* no control channel */ | ||
| 106 | .ep_audio_r = 0x82, | ||
| 107 | .ep_audio_w = 0x01, | ||
| 108 | }, | ||
| 109 | [LINE6_POCKETPOD] = { | ||
| 110 | .id = "PocketPOD", | ||
| 111 | .name = "Pocket POD", | ||
| 112 | .capabilities = LINE6_CAP_CONTROL, | ||
| 113 | .altsetting = 0, | ||
| 114 | .ep_ctrl_r = 0x82, | ||
| 115 | .ep_ctrl_w = 0x02, | ||
| 116 | /* no audio channel */ | ||
| 117 | }, | ||
| 118 | [LINE6_PODHD300] = { | ||
| 119 | .id = "PODHD300", | ||
| 120 | .name = "POD HD300", | ||
| 121 | .capabilities = LINE6_CAP_CONTROL | ||
| 122 | | LINE6_CAP_PCM | ||
| 123 | | LINE6_CAP_HWMON, | ||
| 124 | .altsetting = 5, | ||
| 125 | .ep_ctrl_r = 0x84, | ||
| 126 | .ep_ctrl_w = 0x03, | ||
| 127 | .ep_audio_r = 0x82, | ||
| 128 | .ep_audio_w = 0x01, | ||
| 129 | }, | ||
| 130 | [LINE6_PODHD400] = { | ||
| 131 | .id = "PODHD400", | ||
| 132 | .name = "POD HD400", | ||
| 133 | .capabilities = LINE6_CAP_CONTROL | ||
| 134 | | LINE6_CAP_PCM | ||
| 135 | | LINE6_CAP_HWMON, | ||
| 136 | .altsetting = 5, | ||
| 137 | .ep_ctrl_r = 0x84, | ||
| 138 | .ep_ctrl_w = 0x03, | ||
| 139 | .ep_audio_r = 0x82, | ||
| 140 | .ep_audio_w = 0x01, | ||
| 141 | }, | ||
| 142 | [LINE6_PODHD500_0] = { | ||
| 143 | .id = "PODHD500", | ||
| 144 | .name = "POD HD500", | ||
| 145 | .capabilities = LINE6_CAP_CONTROL | ||
| 146 | | LINE6_CAP_PCM | ||
| 147 | | LINE6_CAP_HWMON, | ||
| 148 | .altsetting = 1, | ||
| 149 | .ep_ctrl_r = 0x81, | ||
| 150 | .ep_ctrl_w = 0x01, | ||
| 151 | .ep_audio_r = 0x86, | ||
| 152 | .ep_audio_w = 0x02, | ||
| 153 | }, | ||
| 154 | [LINE6_PODHD500_1] = { | ||
| 155 | .id = "PODHD500", | ||
| 156 | .name = "POD HD500", | ||
| 157 | .capabilities = LINE6_CAP_CONTROL | ||
| 158 | | LINE6_CAP_PCM | ||
| 159 | | LINE6_CAP_HWMON, | ||
| 160 | .altsetting = 1, | ||
| 161 | .ep_ctrl_r = 0x81, | ||
| 162 | .ep_ctrl_w = 0x01, | ||
| 163 | .ep_audio_r = 0x86, | ||
| 164 | .ep_audio_w = 0x02, | ||
| 165 | }, | ||
| 166 | [LINE6_PODSTUDIO_GX] = { | ||
| 167 | .id = "PODStudioGX", | ||
| 168 | .name = "POD Studio GX", | ||
| 169 | .capabilities = LINE6_CAP_PCM, | ||
| 170 | .altsetting = 2, /* 1..4 seem to be ok */ | ||
| 171 | /* no control channel */ | ||
| 172 | .ep_audio_r = 0x82, | ||
| 173 | .ep_audio_w = 0x01, | ||
| 174 | }, | ||
| 175 | [LINE6_PODSTUDIO_UX1] = { | ||
| 176 | .id = "PODStudioUX1", | ||
| 177 | .name = "POD Studio UX1", | ||
| 178 | .capabilities = LINE6_CAP_PCM, | ||
| 179 | .altsetting = 2, /* 1..4 seem to be ok */ | ||
| 180 | /* no control channel */ | ||
| 181 | .ep_audio_r = 0x82, | ||
| 182 | .ep_audio_w = 0x01, | ||
| 183 | }, | ||
| 184 | [LINE6_PODSTUDIO_UX2] = { | ||
| 185 | .id = "PODStudioUX2", | ||
| 186 | .name = "POD Studio UX2", | ||
| 187 | .capabilities = LINE6_CAP_PCM, | ||
| 188 | .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ | ||
| 189 | /* no control channel */ | ||
| 190 | .ep_audio_r = 0x82, | ||
| 191 | .ep_audio_w = 0x01, | ||
| 192 | }, | ||
| 193 | [LINE6_PODXT] = { | ||
| 194 | .id = "PODxt", | ||
| 195 | .name = "PODxt", | ||
| 196 | .capabilities = LINE6_CAP_CONTROL | ||
| 197 | | LINE6_CAP_PCM | ||
| 198 | | LINE6_CAP_HWMON, | ||
| 199 | .altsetting = 5, | ||
| 200 | .ep_ctrl_r = 0x84, | ||
| 201 | .ep_ctrl_w = 0x03, | ||
| 202 | .ep_audio_r = 0x82, | ||
| 203 | .ep_audio_w = 0x01, | ||
| 204 | }, | ||
| 205 | [LINE6_PODXTLIVE_POD] = { | ||
| 206 | .id = "PODxtLive", | ||
| 207 | .name = "PODxt Live", | ||
| 208 | .capabilities = LINE6_CAP_CONTROL | ||
| 209 | | LINE6_CAP_PCM | ||
| 210 | | LINE6_CAP_HWMON, | ||
| 211 | .altsetting = 1, | ||
| 212 | .ep_ctrl_r = 0x84, | ||
| 213 | .ep_ctrl_w = 0x03, | ||
| 214 | .ep_audio_r = 0x82, | ||
| 215 | .ep_audio_w = 0x01, | ||
| 216 | }, | ||
| 217 | [LINE6_PODXTLIVE_VARIAX] = { | ||
| 218 | .id = "PODxtLive", | ||
| 219 | .name = "PODxt Live", | ||
| 220 | .capabilities = LINE6_CAP_CONTROL | ||
| 221 | | LINE6_CAP_PCM | ||
| 222 | | LINE6_CAP_HWMON, | ||
| 223 | .altsetting = 1, | ||
| 224 | .ep_ctrl_r = 0x86, | ||
| 225 | .ep_ctrl_w = 0x05, | ||
| 226 | .ep_audio_r = 0x82, | ||
| 227 | .ep_audio_w = 0x01, | ||
| 228 | }, | ||
| 229 | [LINE6_PODXTPRO] = { | ||
| 230 | .id = "PODxtPro", | ||
| 231 | .name = "PODxt Pro", | ||
| 232 | .capabilities = LINE6_CAP_CONTROL | ||
| 233 | | LINE6_CAP_PCM | ||
| 234 | | LINE6_CAP_HWMON, | ||
| 235 | .altsetting = 5, | ||
| 236 | .ep_ctrl_r = 0x84, | ||
| 237 | .ep_ctrl_w = 0x03, | ||
| 238 | .ep_audio_r = 0x82, | ||
| 239 | .ep_audio_w = 0x01, | ||
| 240 | }, | ||
| 241 | [LINE6_TONEPORT_GX] = { | ||
| 242 | .id = "TonePortGX", | ||
| 243 | .name = "TonePort GX", | ||
| 244 | .capabilities = LINE6_CAP_PCM, | ||
| 245 | .altsetting = 2, /* 1..4 seem to be ok */ | ||
| 246 | /* no control channel */ | ||
| 247 | .ep_audio_r = 0x82, | ||
| 248 | .ep_audio_w = 0x01, | ||
| 249 | }, | ||
| 250 | [LINE6_TONEPORT_UX1] = { | ||
| 251 | .id = "TonePortUX1", | ||
| 252 | .name = "TonePort UX1", | ||
| 253 | .capabilities = LINE6_CAP_PCM, | ||
| 254 | .altsetting = 2, /* 1..4 seem to be ok */ | ||
| 255 | /* no control channel */ | ||
| 256 | .ep_audio_r = 0x82, | ||
| 257 | .ep_audio_w = 0x01, | ||
| 258 | }, | ||
| 259 | [LINE6_TONEPORT_UX2] = { | ||
| 260 | .id = "TonePortUX2", | ||
| 261 | .name = "TonePort UX2", | ||
| 262 | .capabilities = LINE6_CAP_PCM, | ||
| 263 | .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ | ||
| 264 | /* no control channel */ | ||
| 265 | .ep_audio_r = 0x82, | ||
| 266 | .ep_audio_w = 0x01, | ||
| 267 | }, | ||
| 268 | [LINE6_VARIAX] = { | ||
| 269 | .id = "Variax", | ||
| 270 | .name = "Variax Workbench", | ||
| 271 | .capabilities = LINE6_CAP_CONTROL, | ||
| 272 | .altsetting = 1, | ||
| 273 | .ep_ctrl_r = 0x82, | ||
| 274 | .ep_ctrl_w = 0x01, | ||
| 275 | /* no audio channel */ | ||
| 276 | } | ||
| 277 | }; | ||
| 278 | |||
| 279 | /* | ||
| 280 | This is Line6's MIDI manufacturer ID. | ||
| 281 | */ | ||
| 282 | const unsigned char line6_midi_id[] = { | ||
| 283 | 0x00, 0x01, 0x0c | ||
| 284 | }; | ||
| 285 | |||
| 286 | /* | ||
| 287 | Code to request version of POD, Variax interface | ||
| 288 | (and maybe other devices). | ||
| 289 | */ | ||
| 290 | static const char line6_request_version[] = { | ||
| 291 | 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 | ||
| 292 | }; | ||
| 293 | |||
| 294 | /** | ||
| 295 | Class for asynchronous messages. | ||
| 296 | */ | ||
| 297 | struct message { | ||
| 298 | struct usb_line6 *line6; | ||
| 299 | const char *buffer; | ||
| 300 | int size; | ||
| 301 | int done; | ||
| 302 | }; | ||
| 303 | |||
| 304 | /* | ||
| 305 | Forward declarations. | ||
| 306 | */ | ||
| 307 | static void line6_data_received(struct urb *urb); | ||
| 308 | static int line6_send_raw_message_async_part(struct message *msg, | ||
| 309 | struct urb *urb); | ||
| 310 | |||
| 311 | /* | ||
| 312 | Start to listen on endpoint. | ||
| 313 | */ | ||
| 314 | static int line6_start_listen(struct usb_line6 *line6) | ||
| 315 | { | ||
| 316 | int err; | ||
| 317 | |||
| 318 | usb_fill_int_urb(line6->urb_listen, line6->usbdev, | ||
| 319 | usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r), | ||
| 320 | line6->buffer_listen, LINE6_BUFSIZE_LISTEN, | ||
| 321 | line6_data_received, line6, line6->interval); | ||
| 322 | line6->urb_listen->actual_length = 0; | ||
| 323 | err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC); | ||
| 324 | return err; | ||
| 325 | } | ||
| 326 | |||
| 327 | /* | ||
| 328 | Stop listening on endpoint. | ||
| 329 | */ | ||
| 330 | static void line6_stop_listen(struct usb_line6 *line6) | ||
| 331 | { | ||
| 332 | usb_kill_urb(line6->urb_listen); | ||
| 333 | } | ||
| 334 | |||
| 335 | /* | ||
| 336 | Send raw message in pieces of wMaxPacketSize bytes. | ||
| 337 | */ | ||
| 338 | int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, | ||
| 339 | int size) | ||
| 340 | { | ||
| 341 | int i, done = 0; | ||
| 342 | |||
| 343 | for (i = 0; i < size; i += line6->max_packet_size) { | ||
| 344 | int partial; | ||
| 345 | const char *frag_buf = buffer + i; | ||
| 346 | int frag_size = min(line6->max_packet_size, size - i); | ||
| 347 | int retval; | ||
| 348 | |||
| 349 | retval = usb_interrupt_msg(line6->usbdev, | ||
| 350 | usb_sndintpipe(line6->usbdev, | ||
| 351 | line6->properties->ep_ctrl_w), | ||
| 352 | (char *)frag_buf, frag_size, | ||
| 353 | &partial, LINE6_TIMEOUT * HZ); | ||
| 354 | |||
| 355 | if (retval) { | ||
| 356 | dev_err(line6->ifcdev, | ||
| 357 | "usb_interrupt_msg failed (%d)\n", retval); | ||
| 358 | break; | ||
| 359 | } | ||
| 360 | |||
| 361 | done += frag_size; | ||
| 362 | } | ||
| 363 | |||
| 364 | return done; | ||
| 365 | } | ||
| 366 | |||
| 367 | /* | ||
| 368 | Notification of completion of asynchronous request transmission. | ||
| 369 | */ | ||
| 370 | static void line6_async_request_sent(struct urb *urb) | ||
| 371 | { | ||
| 372 | struct message *msg = (struct message *)urb->context; | ||
| 373 | |||
| 374 | if (msg->done >= msg->size) { | ||
| 375 | usb_free_urb(urb); | ||
| 376 | kfree(msg); | ||
| 377 | } else | ||
| 378 | line6_send_raw_message_async_part(msg, urb); | ||
| 379 | } | ||
| 380 | |||
| 381 | /* | ||
| 382 | Asynchronously send part of a raw message. | ||
| 383 | */ | ||
| 384 | static int line6_send_raw_message_async_part(struct message *msg, | ||
| 385 | struct urb *urb) | ||
| 386 | { | ||
| 387 | int retval; | ||
| 388 | struct usb_line6 *line6 = msg->line6; | ||
| 389 | int done = msg->done; | ||
| 390 | int bytes = min(msg->size - done, line6->max_packet_size); | ||
| 391 | |||
| 392 | usb_fill_int_urb(urb, line6->usbdev, | ||
| 393 | usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w), | ||
| 394 | (char *)msg->buffer + done, bytes, | ||
| 395 | line6_async_request_sent, msg, line6->interval); | ||
| 396 | |||
| 397 | msg->done += bytes; | ||
| 398 | retval = usb_submit_urb(urb, GFP_ATOMIC); | ||
| 399 | |||
| 400 | if (retval < 0) { | ||
| 401 | dev_err(line6->ifcdev, "%s: usb_submit_urb failed (%d)\n", | ||
| 402 | __func__, retval); | ||
| 403 | usb_free_urb(urb); | ||
| 404 | kfree(msg); | ||
| 405 | return retval; | ||
| 406 | } | ||
| 407 | |||
| 408 | return 0; | ||
| 409 | } | ||
| 410 | |||
| 411 | /* | ||
| 412 | Setup and start timer. | ||
| 413 | */ | ||
| 414 | void line6_start_timer(struct timer_list *timer, unsigned int msecs, | ||
| 415 | void (*function)(unsigned long), unsigned long data) | ||
| 416 | { | ||
| 417 | setup_timer(timer, function, data); | ||
| 418 | timer->expires = jiffies + msecs * HZ / 1000; | ||
| 419 | add_timer(timer); | ||
| 420 | } | ||
| 421 | |||
| 422 | /* | ||
| 423 | Asynchronously send raw message. | ||
| 424 | */ | ||
| 425 | int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, | ||
| 426 | int size) | ||
| 427 | { | ||
| 428 | struct message *msg; | ||
| 429 | struct urb *urb; | ||
| 430 | |||
| 431 | /* create message: */ | ||
| 432 | msg = kmalloc(sizeof(struct message), GFP_ATOMIC); | ||
| 433 | if (msg == NULL) | ||
| 434 | return -ENOMEM; | ||
| 435 | |||
| 436 | /* create URB: */ | ||
| 437 | urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
| 438 | |||
| 439 | if (urb == NULL) { | ||
| 440 | kfree(msg); | ||
| 441 | dev_err(line6->ifcdev, "Out of memory\n"); | ||
| 442 | return -ENOMEM; | ||
| 443 | } | ||
| 444 | |||
| 445 | /* set message data: */ | ||
| 446 | msg->line6 = line6; | ||
| 447 | msg->buffer = buffer; | ||
| 448 | msg->size = size; | ||
| 449 | msg->done = 0; | ||
| 450 | |||
| 451 | /* start sending: */ | ||
| 452 | return line6_send_raw_message_async_part(msg, urb); | ||
| 453 | } | ||
| 454 | |||
| 455 | /* | ||
| 456 | Send asynchronous device version request. | ||
| 457 | */ | ||
| 458 | int line6_version_request_async(struct usb_line6 *line6) | ||
| 459 | { | ||
| 460 | char *buffer; | ||
| 461 | int retval; | ||
| 462 | |||
| 463 | buffer = kmemdup(line6_request_version, | ||
| 464 | sizeof(line6_request_version), GFP_ATOMIC); | ||
| 465 | if (buffer == NULL) { | ||
| 466 | dev_err(line6->ifcdev, "Out of memory"); | ||
| 467 | return -ENOMEM; | ||
| 468 | } | ||
| 469 | |||
| 470 | retval = line6_send_raw_message_async(line6, buffer, | ||
| 471 | sizeof(line6_request_version)); | ||
| 472 | kfree(buffer); | ||
| 473 | return retval; | ||
| 474 | } | ||
| 475 | |||
| 476 | /* | ||
| 477 | Send sysex message in pieces of wMaxPacketSize bytes. | ||
| 478 | */ | ||
| 479 | int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, | ||
| 480 | int size) | ||
| 481 | { | ||
| 482 | return line6_send_raw_message(line6, buffer, | ||
| 483 | size + SYSEX_EXTRA_SIZE) - | ||
| 484 | SYSEX_EXTRA_SIZE; | ||
| 485 | } | ||
| 486 | |||
| 487 | /* | ||
| 488 | Allocate buffer for sysex message and prepare header. | ||
| 489 | @param code sysex message code | ||
| 490 | @param size number of bytes between code and sysex end | ||
| 491 | */ | ||
| 492 | char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, | ||
| 493 | int size) | ||
| 494 | { | ||
| 495 | char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC); | ||
| 496 | |||
| 497 | if (!buffer) | ||
| 498 | return NULL; | ||
| 499 | |||
| 500 | buffer[0] = LINE6_SYSEX_BEGIN; | ||
| 501 | memcpy(buffer + 1, line6_midi_id, sizeof(line6_midi_id)); | ||
| 502 | buffer[sizeof(line6_midi_id) + 1] = code1; | ||
| 503 | buffer[sizeof(line6_midi_id) + 2] = code2; | ||
| 504 | buffer[sizeof(line6_midi_id) + 3 + size] = LINE6_SYSEX_END; | ||
| 505 | return buffer; | ||
| 506 | } | ||
| 507 | |||
| 508 | /* | ||
| 509 | Notification of data received from the Line6 device. | ||
| 510 | */ | ||
| 511 | static void line6_data_received(struct urb *urb) | ||
| 512 | { | ||
| 513 | struct usb_line6 *line6 = (struct usb_line6 *)urb->context; | ||
| 514 | struct midi_buffer *mb = &line6->line6midi->midibuf_in; | ||
| 515 | int done; | ||
| 516 | |||
| 517 | if (urb->status == -ESHUTDOWN) | ||
| 518 | return; | ||
| 519 | |||
| 520 | done = | ||
| 521 | line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length); | ||
| 522 | |||
| 523 | if (done < urb->actual_length) { | ||
| 524 | line6_midibuf_ignore(mb, done); | ||
| 525 | dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n", | ||
| 526 | done, urb->actual_length); | ||
| 527 | } | ||
| 528 | |||
| 529 | for (;;) { | ||
| 530 | done = | ||
| 531 | line6_midibuf_read(mb, line6->buffer_message, | ||
| 532 | LINE6_MESSAGE_MAXLEN); | ||
| 533 | |||
| 534 | if (done == 0) | ||
| 535 | break; | ||
| 536 | |||
| 537 | line6->message_length = done; | ||
| 538 | line6_midi_receive(line6, line6->buffer_message, done); | ||
| 539 | |||
| 540 | if (line6->process_message) | ||
| 541 | line6->process_message(line6); | ||
| 542 | } | ||
| 543 | |||
| 544 | line6_start_listen(line6); | ||
| 545 | } | ||
| 546 | |||
| 547 | /* | ||
| 548 | Send channel number (i.e., switch to a different sound). | ||
| 549 | */ | ||
| 550 | int line6_send_program(struct usb_line6 *line6, u8 value) | ||
| 551 | { | ||
| 552 | int retval; | ||
| 553 | unsigned char *buffer; | ||
| 554 | int partial; | ||
| 555 | |||
| 556 | buffer = kmalloc(2, GFP_KERNEL); | ||
| 557 | if (!buffer) | ||
| 558 | return -ENOMEM; | ||
| 559 | |||
| 560 | buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST; | ||
| 561 | buffer[1] = value; | ||
| 562 | |||
| 563 | retval = usb_interrupt_msg(line6->usbdev, | ||
| 564 | usb_sndintpipe(line6->usbdev, | ||
| 565 | line6->properties->ep_ctrl_w), | ||
| 566 | buffer, 2, &partial, LINE6_TIMEOUT * HZ); | ||
| 567 | |||
| 568 | if (retval) | ||
| 569 | dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", | ||
| 570 | retval); | ||
| 571 | |||
| 572 | kfree(buffer); | ||
| 573 | return retval; | ||
| 574 | } | ||
| 575 | |||
| 576 | /* | ||
| 577 | Transmit Line6 control parameter. | ||
| 578 | */ | ||
| 579 | int line6_transmit_parameter(struct usb_line6 *line6, int param, u8 value) | ||
| 580 | { | ||
| 581 | int retval; | ||
| 582 | unsigned char *buffer; | ||
| 583 | int partial; | ||
| 584 | |||
| 585 | buffer = kmalloc(3, GFP_KERNEL); | ||
| 586 | if (!buffer) | ||
| 587 | return -ENOMEM; | ||
| 588 | |||
| 589 | buffer[0] = LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST; | ||
| 590 | buffer[1] = param; | ||
| 591 | buffer[2] = value; | ||
| 592 | |||
| 593 | retval = usb_interrupt_msg(line6->usbdev, | ||
| 594 | usb_sndintpipe(line6->usbdev, | ||
| 595 | line6->properties->ep_ctrl_w), | ||
| 596 | buffer, 3, &partial, LINE6_TIMEOUT * HZ); | ||
| 597 | |||
| 598 | if (retval) | ||
| 599 | dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", | ||
| 600 | retval); | ||
| 601 | |||
| 602 | kfree(buffer); | ||
| 603 | return retval; | ||
| 604 | } | ||
| 605 | |||
| 606 | /* | ||
| 607 | Read data from device. | ||
| 608 | */ | ||
| 609 | int line6_read_data(struct usb_line6 *line6, int address, void *data, | ||
| 610 | size_t datalen) | ||
| 611 | { | ||
| 612 | struct usb_device *usbdev = line6->usbdev; | ||
| 613 | int ret; | ||
| 614 | unsigned char len; | ||
| 615 | |||
| 616 | /* query the serial number: */ | ||
| 617 | ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, | ||
| 618 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, | ||
| 619 | (datalen << 8) | 0x21, address, | ||
| 620 | NULL, 0, LINE6_TIMEOUT * HZ); | ||
| 621 | |||
| 622 | if (ret < 0) { | ||
| 623 | dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); | ||
| 624 | return ret; | ||
| 625 | } | ||
| 626 | |||
| 627 | /* Wait for data length. We'll get 0xff until length arrives. */ | ||
| 628 | do { | ||
| 629 | ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, | ||
| 630 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | | ||
| 631 | USB_DIR_IN, | ||
| 632 | 0x0012, 0x0000, &len, 1, | ||
| 633 | LINE6_TIMEOUT * HZ); | ||
| 634 | if (ret < 0) { | ||
| 635 | dev_err(line6->ifcdev, | ||
| 636 | "receive length failed (error %d)\n", ret); | ||
| 637 | return ret; | ||
| 638 | } | ||
| 639 | } while (len == 0xff); | ||
| 640 | |||
| 641 | if (len != datalen) { | ||
| 642 | /* should be equal or something went wrong */ | ||
| 643 | dev_err(line6->ifcdev, | ||
| 644 | "length mismatch (expected %d, got %d)\n", | ||
| 645 | (int)datalen, (int)len); | ||
| 646 | return -EINVAL; | ||
| 647 | } | ||
| 648 | |||
| 649 | /* receive the result: */ | ||
| 650 | ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, | ||
| 651 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | ||
| 652 | 0x0013, 0x0000, data, datalen, | ||
| 653 | LINE6_TIMEOUT * HZ); | ||
| 654 | |||
| 655 | if (ret < 0) { | ||
| 656 | dev_err(line6->ifcdev, "read failed (error %d)\n", ret); | ||
| 657 | return ret; | ||
| 658 | } | ||
| 659 | |||
| 660 | return 0; | ||
| 661 | } | ||
| 662 | |||
| 663 | /* | ||
| 664 | Write data to device. | ||
| 665 | */ | ||
| 666 | int line6_write_data(struct usb_line6 *line6, int address, void *data, | ||
| 667 | size_t datalen) | ||
| 668 | { | ||
| 669 | struct usb_device *usbdev = line6->usbdev; | ||
| 670 | int ret; | ||
| 671 | unsigned char status; | ||
| 672 | |||
| 673 | ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, | ||
| 674 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, | ||
| 675 | 0x0022, address, data, datalen, | ||
| 676 | LINE6_TIMEOUT * HZ); | ||
| 677 | |||
| 678 | if (ret < 0) { | ||
| 679 | dev_err(line6->ifcdev, | ||
| 680 | "write request failed (error %d)\n", ret); | ||
| 681 | return ret; | ||
| 682 | } | ||
| 683 | |||
| 684 | do { | ||
| 685 | ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), | ||
| 686 | 0x67, | ||
| 687 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | | ||
| 688 | USB_DIR_IN, | ||
| 689 | 0x0012, 0x0000, | ||
| 690 | &status, 1, LINE6_TIMEOUT * HZ); | ||
| 691 | |||
| 692 | if (ret < 0) { | ||
| 693 | dev_err(line6->ifcdev, | ||
| 694 | "receiving status failed (error %d)\n", ret); | ||
| 695 | return ret; | ||
| 696 | } | ||
| 697 | } while (status == 0xff); | ||
| 698 | |||
| 699 | if (status != 0) { | ||
| 700 | dev_err(line6->ifcdev, "write failed (error %d)\n", ret); | ||
| 701 | return -EINVAL; | ||
| 702 | } | ||
| 703 | |||
| 704 | return 0; | ||
| 705 | } | ||
| 706 | |||
| 707 | /* | ||
| 708 | Read Line6 device serial number. | ||
| 709 | (POD, TonePort, GuitarPort) | ||
| 710 | */ | ||
| 711 | int line6_read_serial_number(struct usb_line6 *line6, int *serial_number) | ||
| 712 | { | ||
| 713 | return line6_read_data(line6, 0x80d0, serial_number, | ||
| 714 | sizeof(*serial_number)); | ||
| 715 | } | ||
| 716 | |||
| 717 | /* | ||
| 718 | No operation (i.e., unsupported). | ||
| 719 | */ | ||
| 720 | ssize_t line6_nop_read(struct device *dev, struct device_attribute *attr, | ||
| 721 | char *buf) | ||
| 722 | { | ||
| 723 | return 0; | ||
| 724 | } | ||
| 725 | |||
| 726 | /* | ||
| 727 | Generic destructor. | ||
| 728 | */ | ||
| 729 | static void line6_destruct(struct usb_interface *interface) | ||
| 730 | { | ||
| 731 | struct usb_line6 *line6; | ||
| 732 | |||
| 733 | if (interface == NULL) | ||
| 734 | return; | ||
| 735 | line6 = usb_get_intfdata(interface); | ||
| 736 | if (line6 == NULL) | ||
| 737 | return; | ||
| 738 | |||
| 739 | /* free buffer memory first: */ | ||
| 740 | kfree(line6->buffer_message); | ||
| 741 | kfree(line6->buffer_listen); | ||
| 742 | |||
| 743 | /* then free URBs: */ | ||
| 744 | usb_free_urb(line6->urb_listen); | ||
| 745 | |||
| 746 | /* make sure the device isn't destructed twice: */ | ||
| 747 | usb_set_intfdata(interface, NULL); | ||
| 748 | |||
| 749 | /* free interface data: */ | ||
| 750 | kfree(line6); | ||
| 751 | } | ||
| 752 | |||
| 753 | /* | ||
| 754 | Probe USB device. | ||
| 755 | */ | ||
| 756 | static int line6_probe(struct usb_interface *interface, | ||
| 757 | const struct usb_device_id *id) | ||
| 758 | { | ||
| 759 | enum line6_device_type devtype; | ||
| 760 | struct usb_device *usbdev; | ||
| 761 | struct usb_line6 *line6; | ||
| 762 | const struct line6_properties *properties; | ||
| 763 | int interface_number; | ||
| 764 | int size = 0; | ||
| 765 | int ret; | ||
| 766 | |||
| 767 | if (interface == NULL) | ||
| 768 | return -ENODEV; | ||
| 769 | usbdev = interface_to_usbdev(interface); | ||
| 770 | if (usbdev == NULL) | ||
| 771 | return -ENODEV; | ||
| 772 | |||
| 773 | /* we don't handle multiple configurations */ | ||
| 774 | if (usbdev->descriptor.bNumConfigurations != 1) { | ||
| 775 | ret = -ENODEV; | ||
| 776 | goto err_put; | ||
| 777 | } | ||
| 778 | |||
| 779 | devtype = id->driver_info; | ||
| 780 | |||
| 781 | /* initialize device info: */ | ||
| 782 | properties = &line6_properties_table[devtype]; | ||
| 783 | dev_info(&interface->dev, "Line6 %s found\n", properties->name); | ||
| 784 | |||
| 785 | /* query interface number */ | ||
| 786 | interface_number = interface->cur_altsetting->desc.bInterfaceNumber; | ||
| 787 | |||
| 788 | ret = usb_set_interface(usbdev, interface_number, | ||
| 789 | properties->altsetting); | ||
| 790 | if (ret < 0) { | ||
| 791 | dev_err(&interface->dev, "set_interface failed\n"); | ||
| 792 | goto err_put; | ||
| 793 | } | ||
| 794 | |||
| 795 | /* initialize device data based on device: */ | ||
| 796 | switch (devtype) { | ||
| 797 | case LINE6_BASSPODXT: | ||
| 798 | case LINE6_BASSPODXTLIVE: | ||
| 799 | case LINE6_BASSPODXTPRO: | ||
| 800 | case LINE6_PODXT: | ||
| 801 | case LINE6_PODXTPRO: | ||
| 802 | size = sizeof(struct usb_line6_pod); | ||
| 803 | break; | ||
| 804 | |||
| 805 | case LINE6_PODHD300: | ||
| 806 | case LINE6_PODHD400: | ||
| 807 | size = sizeof(struct usb_line6_podhd); | ||
| 808 | break; | ||
| 809 | |||
| 810 | case LINE6_PODHD500_0: | ||
| 811 | case LINE6_PODHD500_1: | ||
| 812 | size = sizeof(struct usb_line6_podhd); | ||
| 813 | break; | ||
| 814 | |||
| 815 | case LINE6_POCKETPOD: | ||
| 816 | size = sizeof(struct usb_line6_pod); | ||
| 817 | break; | ||
| 818 | |||
| 819 | case LINE6_PODSTUDIO_GX: | ||
| 820 | case LINE6_PODSTUDIO_UX1: | ||
| 821 | case LINE6_PODSTUDIO_UX2: | ||
| 822 | case LINE6_TONEPORT_GX: | ||
| 823 | case LINE6_TONEPORT_UX1: | ||
| 824 | case LINE6_TONEPORT_UX2: | ||
| 825 | case LINE6_GUITARPORT: | ||
| 826 | size = sizeof(struct usb_line6_toneport); | ||
| 827 | break; | ||
| 828 | |||
| 829 | case LINE6_PODXTLIVE_POD: | ||
| 830 | size = sizeof(struct usb_line6_pod); | ||
| 831 | break; | ||
| 832 | |||
| 833 | case LINE6_PODXTLIVE_VARIAX: | ||
| 834 | size = sizeof(struct usb_line6_variax); | ||
| 835 | break; | ||
| 836 | |||
| 837 | case LINE6_VARIAX: | ||
| 838 | size = sizeof(struct usb_line6_variax); | ||
| 839 | break; | ||
| 840 | |||
| 841 | default: | ||
| 842 | MISSING_CASE; | ||
| 843 | ret = -ENODEV; | ||
| 844 | goto err_put; | ||
| 845 | } | ||
| 846 | |||
| 847 | if (size == 0) { | ||
| 848 | dev_err(&interface->dev, | ||
| 849 | "driver bug: interface data size not set\n"); | ||
| 850 | ret = -ENODEV; | ||
| 851 | goto err_put; | ||
| 852 | } | ||
| 853 | |||
| 854 | line6 = kzalloc(size, GFP_KERNEL); | ||
| 855 | if (line6 == NULL) { | ||
| 856 | ret = -ENODEV; | ||
| 857 | goto err_put; | ||
| 858 | } | ||
| 859 | |||
| 860 | /* store basic data: */ | ||
| 861 | line6->properties = properties; | ||
| 862 | line6->usbdev = usbdev; | ||
| 863 | line6->ifcdev = &interface->dev; | ||
| 864 | line6->type = devtype; | ||
| 865 | |||
| 866 | /* get data from endpoint descriptor (see usb_maxpacket): */ | ||
| 867 | { | ||
| 868 | struct usb_host_endpoint *ep; | ||
| 869 | unsigned pipe = usb_rcvintpipe(usbdev, properties->ep_ctrl_r); | ||
| 870 | unsigned epnum = usb_pipeendpoint(pipe); | ||
| 871 | ep = usbdev->ep_in[epnum]; | ||
| 872 | |||
| 873 | if (ep != NULL) { | ||
| 874 | line6->interval = ep->desc.bInterval; | ||
| 875 | line6->max_packet_size = | ||
| 876 | le16_to_cpu(ep->desc.wMaxPacketSize); | ||
| 877 | } else { | ||
| 878 | line6->interval = LINE6_FALLBACK_INTERVAL; | ||
| 879 | line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE; | ||
| 880 | dev_err(line6->ifcdev, | ||
| 881 | "endpoint not available, using fallback values"); | ||
| 882 | } | ||
| 883 | } | ||
| 884 | |||
| 885 | usb_set_intfdata(interface, line6); | ||
| 886 | |||
| 887 | if (properties->capabilities & LINE6_CAP_CONTROL) { | ||
| 888 | /* initialize USB buffers: */ | ||
| 889 | line6->buffer_listen = | ||
| 890 | kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); | ||
| 891 | if (line6->buffer_listen == NULL) { | ||
| 892 | ret = -ENOMEM; | ||
| 893 | goto err_destruct; | ||
| 894 | } | ||
| 895 | |||
| 896 | line6->buffer_message = | ||
| 897 | kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL); | ||
| 898 | if (line6->buffer_message == NULL) { | ||
| 899 | ret = -ENOMEM; | ||
| 900 | goto err_destruct; | ||
| 901 | } | ||
| 902 | |||
| 903 | line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL); | ||
| 904 | |||
| 905 | if (line6->urb_listen == NULL) { | ||
| 906 | dev_err(&interface->dev, "Out of memory\n"); | ||
| 907 | line6_destruct(interface); | ||
| 908 | ret = -ENOMEM; | ||
| 909 | goto err_destruct; | ||
| 910 | } | ||
| 911 | |||
| 912 | ret = line6_start_listen(line6); | ||
| 913 | if (ret < 0) { | ||
| 914 | dev_err(&interface->dev, "%s: usb_submit_urb failed\n", | ||
| 915 | __func__); | ||
| 916 | goto err_destruct; | ||
| 917 | } | ||
| 918 | } | ||
| 919 | |||
| 920 | /* initialize device data based on device: */ | ||
| 921 | switch (devtype) { | ||
| 922 | case LINE6_BASSPODXT: | ||
| 923 | case LINE6_BASSPODXTLIVE: | ||
| 924 | case LINE6_BASSPODXTPRO: | ||
| 925 | case LINE6_POCKETPOD: | ||
| 926 | case LINE6_PODXT: | ||
| 927 | case LINE6_PODXTPRO: | ||
| 928 | ret = line6_pod_init(interface, line6); | ||
| 929 | break; | ||
| 930 | |||
| 931 | case LINE6_PODHD300: | ||
| 932 | case LINE6_PODHD400: | ||
| 933 | case LINE6_PODHD500_0: | ||
| 934 | case LINE6_PODHD500_1: | ||
| 935 | ret = line6_podhd_init(interface, line6); | ||
| 936 | break; | ||
| 937 | |||
| 938 | case LINE6_PODXTLIVE_POD: | ||
| 939 | ret = line6_pod_init(interface, line6); | ||
| 940 | break; | ||
| 941 | |||
| 942 | case LINE6_PODXTLIVE_VARIAX: | ||
| 943 | ret = line6_variax_init(interface, line6); | ||
| 944 | break; | ||
| 945 | |||
| 946 | case LINE6_VARIAX: | ||
| 947 | ret = line6_variax_init(interface, line6); | ||
| 948 | break; | ||
| 949 | |||
| 950 | case LINE6_PODSTUDIO_GX: | ||
| 951 | case LINE6_PODSTUDIO_UX1: | ||
| 952 | case LINE6_PODSTUDIO_UX2: | ||
| 953 | case LINE6_TONEPORT_GX: | ||
| 954 | case LINE6_TONEPORT_UX1: | ||
| 955 | case LINE6_TONEPORT_UX2: | ||
| 956 | case LINE6_GUITARPORT: | ||
| 957 | ret = line6_toneport_init(interface, line6); | ||
| 958 | break; | ||
| 959 | |||
| 960 | default: | ||
| 961 | MISSING_CASE; | ||
| 962 | ret = -ENODEV; | ||
| 963 | } | ||
| 964 | |||
| 965 | if (ret < 0) | ||
| 966 | goto err_destruct; | ||
| 967 | |||
| 968 | ret = sysfs_create_link(&interface->dev.kobj, &usbdev->dev.kobj, | ||
| 969 | "usb_device"); | ||
| 970 | if (ret < 0) | ||
| 971 | goto err_destruct; | ||
| 972 | |||
| 973 | /* creation of additional special files should go here */ | ||
| 974 | |||
| 975 | dev_info(&interface->dev, "Line6 %s now attached\n", | ||
| 976 | line6->properties->name); | ||
| 977 | |||
| 978 | /* increment reference counters: */ | ||
| 979 | usb_get_intf(interface); | ||
| 980 | usb_get_dev(usbdev); | ||
| 981 | |||
| 982 | return 0; | ||
| 983 | |||
| 984 | err_destruct: | ||
| 985 | line6_destruct(interface); | ||
| 986 | err_put: | ||
| 987 | return ret; | ||
| 988 | } | ||
| 989 | |||
| 990 | /* | ||
| 991 | Line6 device disconnected. | ||
| 992 | */ | ||
| 993 | static void line6_disconnect(struct usb_interface *interface) | ||
| 994 | { | ||
| 995 | struct usb_line6 *line6; | ||
| 996 | struct usb_device *usbdev; | ||
| 997 | int interface_number; | ||
| 998 | |||
| 999 | if (interface == NULL) | ||
| 1000 | return; | ||
| 1001 | usbdev = interface_to_usbdev(interface); | ||
| 1002 | if (usbdev == NULL) | ||
| 1003 | return; | ||
| 1004 | |||
| 1005 | /* removal of additional special files should go here */ | ||
| 1006 | |||
| 1007 | sysfs_remove_link(&interface->dev.kobj, "usb_device"); | ||
| 1008 | |||
| 1009 | interface_number = interface->cur_altsetting->desc.bInterfaceNumber; | ||
| 1010 | line6 = usb_get_intfdata(interface); | ||
| 1011 | |||
| 1012 | if (line6 != NULL) { | ||
| 1013 | if (line6->urb_listen != NULL) | ||
| 1014 | line6_stop_listen(line6); | ||
| 1015 | |||
| 1016 | if (usbdev != line6->usbdev) | ||
| 1017 | dev_err(line6->ifcdev, | ||
| 1018 | "driver bug: inconsistent usb device\n"); | ||
| 1019 | |||
| 1020 | line6->disconnect(interface); | ||
| 1021 | |||
| 1022 | dev_info(&interface->dev, "Line6 %s now disconnected\n", | ||
| 1023 | line6->properties->name); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | line6_destruct(interface); | ||
| 1027 | |||
| 1028 | /* decrement reference counters: */ | ||
| 1029 | usb_put_intf(interface); | ||
| 1030 | usb_put_dev(usbdev); | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | #ifdef CONFIG_PM | ||
| 1034 | |||
| 1035 | /* | ||
| 1036 | Suspend Line6 device. | ||
| 1037 | */ | ||
| 1038 | static int line6_suspend(struct usb_interface *interface, pm_message_t message) | ||
| 1039 | { | ||
| 1040 | struct usb_line6 *line6 = usb_get_intfdata(interface); | ||
| 1041 | struct snd_line6_pcm *line6pcm = line6->line6pcm; | ||
| 1042 | |||
| 1043 | snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot); | ||
| 1044 | |||
| 1045 | if (line6->properties->capabilities & LINE6_CAP_CONTROL) | ||
| 1046 | line6_stop_listen(line6); | ||
| 1047 | |||
| 1048 | if (line6pcm != NULL) { | ||
| 1049 | snd_pcm_suspend_all(line6pcm->pcm); | ||
| 1050 | line6_pcm_disconnect(line6pcm); | ||
| 1051 | line6pcm->flags = 0; | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | return 0; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | /* | ||
| 1058 | Resume Line6 device. | ||
| 1059 | */ | ||
| 1060 | static int line6_resume(struct usb_interface *interface) | ||
| 1061 | { | ||
| 1062 | struct usb_line6 *line6 = usb_get_intfdata(interface); | ||
| 1063 | |||
| 1064 | if (line6->properties->capabilities & LINE6_CAP_CONTROL) | ||
| 1065 | line6_start_listen(line6); | ||
| 1066 | |||
| 1067 | snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0); | ||
| 1068 | return 0; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | /* | ||
| 1072 | Resume Line6 device after reset. | ||
| 1073 | */ | ||
| 1074 | static int line6_reset_resume(struct usb_interface *interface) | ||
| 1075 | { | ||
| 1076 | struct usb_line6 *line6 = usb_get_intfdata(interface); | ||
| 1077 | |||
| 1078 | switch (line6->type) { | ||
| 1079 | case LINE6_PODSTUDIO_GX: | ||
| 1080 | case LINE6_PODSTUDIO_UX1: | ||
| 1081 | case LINE6_PODSTUDIO_UX2: | ||
| 1082 | case LINE6_TONEPORT_GX: | ||
| 1083 | case LINE6_TONEPORT_UX1: | ||
| 1084 | case LINE6_TONEPORT_UX2: | ||
| 1085 | case LINE6_GUITARPORT: | ||
| 1086 | line6_toneport_reset_resume((struct usb_line6_toneport *)line6); | ||
| 1087 | |||
| 1088 | default: | ||
| 1089 | break; | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | return line6_resume(interface); | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | #endif /* CONFIG_PM */ | ||
| 1096 | |||
| 1097 | static struct usb_driver line6_driver = { | ||
| 1098 | .name = DRIVER_NAME, | ||
| 1099 | .probe = line6_probe, | ||
| 1100 | .disconnect = line6_disconnect, | ||
| 1101 | #ifdef CONFIG_PM | ||
| 1102 | .suspend = line6_suspend, | ||
| 1103 | .resume = line6_resume, | ||
| 1104 | .reset_resume = line6_reset_resume, | ||
| 1105 | #endif | ||
| 1106 | .id_table = line6_id_table, | ||
| 1107 | }; | ||
| 1108 | |||
| 1109 | module_usb_driver(line6_driver); | ||
| 1110 | |||
| 1111 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
| 1112 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
| 1113 | MODULE_LICENSE("GPL"); | ||
| 1114 | MODULE_VERSION(DRIVER_VERSION); | ||
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h new file mode 100644 index 000000000000..ad203f197e80 --- /dev/null +++ b/sound/usb/line6/driver.h | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 | #include <linux/spinlock.h> | ||
| 16 | #include <linux/usb.h> | ||
| 17 | #include <sound/core.h> | ||
| 18 | |||
| 19 | #include "midi.h" | ||
| 20 | |||
| 21 | #define DRIVER_NAME "line6usb" | ||
| 22 | |||
| 23 | enum line6_device_type { | ||
| 24 | LINE6_BASSPODXT, | ||
| 25 | LINE6_BASSPODXTLIVE, | ||
| 26 | LINE6_BASSPODXTPRO, | ||
| 27 | LINE6_GUITARPORT, | ||
| 28 | LINE6_POCKETPOD, | ||
| 29 | LINE6_PODHD300, | ||
| 30 | LINE6_PODHD400, | ||
| 31 | LINE6_PODHD500_0, | ||
| 32 | LINE6_PODHD500_1, | ||
| 33 | LINE6_PODSTUDIO_GX, | ||
| 34 | LINE6_PODSTUDIO_UX1, | ||
| 35 | LINE6_PODSTUDIO_UX2, | ||
| 36 | LINE6_PODXT, | ||
| 37 | LINE6_PODXTLIVE_POD, | ||
| 38 | LINE6_PODXTLIVE_VARIAX, | ||
| 39 | LINE6_PODXTPRO, | ||
| 40 | LINE6_TONEPORT_GX, | ||
| 41 | LINE6_TONEPORT_UX1, | ||
| 42 | LINE6_TONEPORT_UX2, | ||
| 43 | LINE6_VARIAX | ||
| 44 | }; | ||
| 45 | |||
| 46 | #define LINE6_TIMEOUT 1 | ||
| 47 | #define LINE6_BUFSIZE_LISTEN 32 | ||
| 48 | #define LINE6_MESSAGE_MAXLEN 256 | ||
| 49 | |||
| 50 | /* | ||
| 51 | Line6 MIDI control commands | ||
| 52 | */ | ||
| 53 | #define LINE6_PARAM_CHANGE 0xb0 | ||
| 54 | #define LINE6_PROGRAM_CHANGE 0xc0 | ||
| 55 | #define LINE6_SYSEX_BEGIN 0xf0 | ||
| 56 | #define LINE6_SYSEX_END 0xf7 | ||
| 57 | #define LINE6_RESET 0xff | ||
| 58 | |||
| 59 | /* | ||
| 60 | MIDI channel for messages initiated by the host | ||
| 61 | (and eventually echoed back by the device) | ||
| 62 | */ | ||
| 63 | #define LINE6_CHANNEL_HOST 0x00 | ||
| 64 | |||
| 65 | /* | ||
| 66 | MIDI channel for messages initiated by the device | ||
| 67 | */ | ||
| 68 | #define LINE6_CHANNEL_DEVICE 0x02 | ||
| 69 | |||
| 70 | #define LINE6_CHANNEL_UNKNOWN 5 /* don't know yet what this is good for */ | ||
| 71 | |||
| 72 | #define LINE6_CHANNEL_MASK 0x0f | ||
| 73 | |||
| 74 | #define MISSING_CASE \ | ||
| 75 | pr_err("line6usb driver bug: missing case in %s:%d\n", \ | ||
| 76 | __FILE__, __LINE__) | ||
| 77 | |||
| 78 | #define CHECK_RETURN(x) \ | ||
| 79 | do { \ | ||
| 80 | err = x; \ | ||
| 81 | if (err < 0) \ | ||
| 82 | return err; \ | ||
| 83 | } while (0) | ||
| 84 | |||
| 85 | #define CHECK_STARTUP_PROGRESS(x, n) \ | ||
| 86 | do { \ | ||
| 87 | if ((x) >= (n)) \ | ||
| 88 | return; \ | ||
| 89 | x = (n); \ | ||
| 90 | } while (0) | ||
| 91 | |||
| 92 | extern const unsigned char line6_midi_id[3]; | ||
| 93 | |||
| 94 | static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3; | ||
| 95 | static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4; | ||
| 96 | |||
| 97 | /** | ||
| 98 | Common properties of Line6 devices. | ||
| 99 | */ | ||
| 100 | struct line6_properties { | ||
| 101 | /** | ||
| 102 | Card id string (maximum 16 characters). | ||
| 103 | This can be used to address the device in ALSA programs as | ||
| 104 | "default:CARD=<id>" | ||
| 105 | */ | ||
| 106 | const char *id; | ||
| 107 | |||
| 108 | /** | ||
| 109 | Card short name (maximum 32 characters). | ||
| 110 | */ | ||
| 111 | const char *name; | ||
| 112 | |||
| 113 | /** | ||
| 114 | Bit vector defining this device's capabilities in the | ||
| 115 | line6usb driver. | ||
| 116 | */ | ||
| 117 | int capabilities; | ||
| 118 | |||
| 119 | int altsetting; | ||
| 120 | |||
| 121 | unsigned ep_ctrl_r; | ||
| 122 | unsigned ep_ctrl_w; | ||
| 123 | unsigned ep_audio_r; | ||
| 124 | unsigned ep_audio_w; | ||
| 125 | }; | ||
| 126 | |||
| 127 | /** | ||
| 128 | Common data shared by all Line6 devices. | ||
| 129 | Corresponds to a pair of USB endpoints. | ||
| 130 | */ | ||
| 131 | struct usb_line6 { | ||
| 132 | /** | ||
| 133 | USB device. | ||
| 134 | */ | ||
| 135 | struct usb_device *usbdev; | ||
| 136 | |||
| 137 | /** | ||
| 138 | Device type. | ||
| 139 | */ | ||
| 140 | enum line6_device_type type; | ||
| 141 | |||
| 142 | /** | ||
| 143 | Properties. | ||
| 144 | */ | ||
| 145 | const struct line6_properties *properties; | ||
| 146 | |||
| 147 | /** | ||
| 148 | Interval (ms). | ||
| 149 | */ | ||
| 150 | int interval; | ||
| 151 | |||
| 152 | /** | ||
| 153 | Maximum size of USB packet. | ||
| 154 | */ | ||
| 155 | int max_packet_size; | ||
| 156 | |||
| 157 | /** | ||
| 158 | Device representing the USB interface. | ||
| 159 | */ | ||
| 160 | struct device *ifcdev; | ||
| 161 | |||
| 162 | /** | ||
| 163 | Line6 sound card data structure. | ||
| 164 | Each device has at least MIDI or PCM. | ||
| 165 | */ | ||
| 166 | struct snd_card *card; | ||
| 167 | |||
| 168 | /** | ||
| 169 | Line6 PCM device data structure. | ||
| 170 | */ | ||
| 171 | struct snd_line6_pcm *line6pcm; | ||
| 172 | |||
| 173 | /** | ||
| 174 | Line6 MIDI device data structure. | ||
| 175 | */ | ||
| 176 | struct snd_line6_midi *line6midi; | ||
| 177 | |||
| 178 | /** | ||
| 179 | URB for listening to PODxt Pro control endpoint. | ||
| 180 | */ | ||
| 181 | struct urb *urb_listen; | ||
| 182 | |||
| 183 | /** | ||
| 184 | Buffer for listening to PODxt Pro control endpoint. | ||
| 185 | */ | ||
| 186 | unsigned char *buffer_listen; | ||
| 187 | |||
| 188 | /** | ||
| 189 | Buffer for message to be processed. | ||
| 190 | */ | ||
| 191 | unsigned char *buffer_message; | ||
| 192 | |||
| 193 | /** | ||
| 194 | Length of message to be processed. | ||
| 195 | */ | ||
| 196 | int message_length; | ||
| 197 | |||
| 198 | void (*process_message)(struct usb_line6 *); | ||
| 199 | void (*disconnect)(struct usb_interface *); | ||
| 200 | }; | ||
| 201 | |||
| 202 | extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, | ||
| 203 | int code2, int size); | ||
| 204 | extern ssize_t line6_nop_read(struct device *dev, | ||
| 205 | struct device_attribute *attr, char *buf); | ||
| 206 | extern int line6_read_data(struct usb_line6 *line6, int address, void *data, | ||
| 207 | size_t datalen); | ||
| 208 | extern int line6_read_serial_number(struct usb_line6 *line6, | ||
| 209 | int *serial_number); | ||
| 210 | extern int line6_send_program(struct usb_line6 *line6, u8 value); | ||
| 211 | extern int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, | ||
| 212 | int size); | ||
| 213 | extern int line6_send_raw_message_async(struct usb_line6 *line6, | ||
| 214 | const char *buffer, int size); | ||
| 215 | extern int line6_send_sysex_message(struct usb_line6 *line6, | ||
| 216 | const char *buffer, int size); | ||
| 217 | extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr, | ||
| 218 | const char *buf, size_t count); | ||
| 219 | extern void line6_start_timer(struct timer_list *timer, unsigned int msecs, | ||
| 220 | void (*function)(unsigned long), | ||
| 221 | unsigned long data); | ||
| 222 | extern int line6_transmit_parameter(struct usb_line6 *line6, int param, | ||
| 223 | u8 value); | ||
| 224 | extern int line6_version_request_async(struct usb_line6 *line6); | ||
| 225 | extern int line6_write_data(struct usb_line6 *line6, int address, void *data, | ||
| 226 | size_t datalen); | ||
| 227 | |||
| 228 | #endif | ||
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c new file mode 100644 index 000000000000..c9d725ae85a0 --- /dev/null +++ b/sound/usb/line6/midi.c | |||
| @@ -0,0 +1,321 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 <linux/slab.h> | ||
| 13 | #include <linux/usb.h> | ||
| 14 | #include <sound/core.h> | ||
| 15 | #include <sound/rawmidi.h> | ||
| 16 | |||
| 17 | #include "audio.h" | ||
| 18 | #include "driver.h" | ||
| 19 | #include "midi.h" | ||
| 20 | #include "pod.h" | ||
| 21 | #include "usbdefs.h" | ||
| 22 | |||
| 23 | #define line6_rawmidi_substream_midi(substream) \ | ||
| 24 | ((struct snd_line6_midi *)((substream)->rmidi->private_data)) | ||
| 25 | |||
| 26 | static int send_midi_async(struct usb_line6 *line6, unsigned char *data, | ||
| 27 | int length); | ||
| 28 | |||
| 29 | /* | ||
| 30 | Pass data received via USB to MIDI. | ||
| 31 | */ | ||
| 32 | void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, | ||
| 33 | int length) | ||
| 34 | { | ||
| 35 | if (line6->line6midi->substream_receive) | ||
| 36 | snd_rawmidi_receive(line6->line6midi->substream_receive, | ||
| 37 | data, length); | ||
| 38 | } | ||
| 39 | |||
| 40 | /* | ||
| 41 | Read data from MIDI buffer and transmit them via USB. | ||
| 42 | */ | ||
| 43 | static void line6_midi_transmit(struct snd_rawmidi_substream *substream) | ||
| 44 | { | ||
| 45 | struct usb_line6 *line6 = | ||
| 46 | line6_rawmidi_substream_midi(substream)->line6; | ||
| 47 | struct snd_line6_midi *line6midi = line6->line6midi; | ||
| 48 | struct midi_buffer *mb = &line6midi->midibuf_out; | ||
| 49 | unsigned long flags; | ||
| 50 | unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE]; | ||
| 51 | int req, done; | ||
| 52 | |||
| 53 | spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags); | ||
| 54 | |||
| 55 | for (;;) { | ||
| 56 | req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size); | ||
| 57 | done = snd_rawmidi_transmit_peek(substream, chunk, req); | ||
| 58 | |||
| 59 | if (done == 0) | ||
| 60 | break; | ||
| 61 | |||
| 62 | line6_midibuf_write(mb, chunk, done); | ||
| 63 | snd_rawmidi_transmit_ack(substream, done); | ||
| 64 | } | ||
| 65 | |||
| 66 | for (;;) { | ||
| 67 | done = line6_midibuf_read(mb, chunk, | ||
| 68 | LINE6_FALLBACK_MAXPACKETSIZE); | ||
| 69 | |||
| 70 | if (done == 0) | ||
| 71 | break; | ||
| 72 | |||
| 73 | send_midi_async(line6, chunk, done); | ||
| 74 | } | ||
| 75 | |||
| 76 | spin_unlock_irqrestore(&line6->line6midi->midi_transmit_lock, flags); | ||
| 77 | } | ||
| 78 | |||
| 79 | /* | ||
| 80 | Notification of completion of MIDI transmission. | ||
| 81 | */ | ||
| 82 | static void midi_sent(struct urb *urb) | ||
| 83 | { | ||
| 84 | unsigned long flags; | ||
| 85 | int status; | ||
| 86 | int num; | ||
| 87 | struct usb_line6 *line6 = (struct usb_line6 *)urb->context; | ||
| 88 | |||
| 89 | status = urb->status; | ||
| 90 | kfree(urb->transfer_buffer); | ||
| 91 | usb_free_urb(urb); | ||
| 92 | |||
| 93 | if (status == -ESHUTDOWN) | ||
| 94 | return; | ||
| 95 | |||
| 96 | spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); | ||
| 97 | num = --line6->line6midi->num_active_send_urbs; | ||
| 98 | |||
| 99 | if (num == 0) { | ||
| 100 | line6_midi_transmit(line6->line6midi->substream_transmit); | ||
| 101 | num = line6->line6midi->num_active_send_urbs; | ||
| 102 | } | ||
| 103 | |||
| 104 | if (num == 0) | ||
| 105 | wake_up(&line6->line6midi->send_wait); | ||
| 106 | |||
| 107 | spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); | ||
| 108 | } | ||
| 109 | |||
| 110 | /* | ||
| 111 | Send an asynchronous MIDI message. | ||
| 112 | Assumes that line6->line6midi->send_urb_lock is held | ||
| 113 | (i.e., this function is serialized). | ||
| 114 | */ | ||
| 115 | static int send_midi_async(struct usb_line6 *line6, unsigned char *data, | ||
| 116 | int length) | ||
| 117 | { | ||
| 118 | struct urb *urb; | ||
| 119 | int retval; | ||
| 120 | unsigned char *transfer_buffer; | ||
| 121 | |||
| 122 | urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
| 123 | |||
| 124 | if (urb == NULL) { | ||
| 125 | dev_err(line6->ifcdev, "Out of memory\n"); | ||
| 126 | return -ENOMEM; | ||
| 127 | } | ||
| 128 | |||
| 129 | transfer_buffer = kmemdup(data, length, GFP_ATOMIC); | ||
| 130 | |||
| 131 | if (transfer_buffer == NULL) { | ||
| 132 | usb_free_urb(urb); | ||
| 133 | dev_err(line6->ifcdev, "Out of memory\n"); | ||
| 134 | return -ENOMEM; | ||
| 135 | } | ||
| 136 | |||
| 137 | usb_fill_int_urb(urb, line6->usbdev, | ||
| 138 | usb_sndbulkpipe(line6->usbdev, | ||
| 139 | line6->properties->ep_ctrl_w), | ||
| 140 | transfer_buffer, length, midi_sent, line6, | ||
| 141 | line6->interval); | ||
| 142 | urb->actual_length = 0; | ||
| 143 | retval = usb_submit_urb(urb, GFP_ATOMIC); | ||
| 144 | |||
| 145 | if (retval < 0) { | ||
| 146 | dev_err(line6->ifcdev, "usb_submit_urb failed\n"); | ||
| 147 | usb_free_urb(urb); | ||
| 148 | return retval; | ||
| 149 | } | ||
| 150 | |||
| 151 | ++line6->line6midi->num_active_send_urbs; | ||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | static int line6_midi_output_open(struct snd_rawmidi_substream *substream) | ||
| 156 | { | ||
| 157 | return 0; | ||
| 158 | } | ||
| 159 | |||
| 160 | static int line6_midi_output_close(struct snd_rawmidi_substream *substream) | ||
| 161 | { | ||
| 162 | return 0; | ||
| 163 | } | ||
| 164 | |||
| 165 | static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, | ||
| 166 | int up) | ||
| 167 | { | ||
| 168 | unsigned long flags; | ||
| 169 | struct usb_line6 *line6 = | ||
| 170 | line6_rawmidi_substream_midi(substream)->line6; | ||
| 171 | |||
| 172 | line6->line6midi->substream_transmit = substream; | ||
| 173 | spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); | ||
| 174 | |||
| 175 | if (line6->line6midi->num_active_send_urbs == 0) | ||
| 176 | line6_midi_transmit(substream); | ||
| 177 | |||
| 178 | spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); | ||
| 179 | } | ||
| 180 | |||
| 181 | static void line6_midi_output_drain(struct snd_rawmidi_substream *substream) | ||
| 182 | { | ||
| 183 | struct usb_line6 *line6 = | ||
| 184 | line6_rawmidi_substream_midi(substream)->line6; | ||
| 185 | struct snd_line6_midi *midi = line6->line6midi; | ||
| 186 | |||
| 187 | wait_event_interruptible(midi->send_wait, | ||
| 188 | midi->num_active_send_urbs == 0); | ||
| 189 | } | ||
| 190 | |||
| 191 | static int line6_midi_input_open(struct snd_rawmidi_substream *substream) | ||
| 192 | { | ||
| 193 | return 0; | ||
| 194 | } | ||
| 195 | |||
| 196 | static int line6_midi_input_close(struct snd_rawmidi_substream *substream) | ||
| 197 | { | ||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, | ||
| 202 | int up) | ||
| 203 | { | ||
| 204 | struct usb_line6 *line6 = | ||
| 205 | line6_rawmidi_substream_midi(substream)->line6; | ||
| 206 | |||
| 207 | if (up) | ||
| 208 | line6->line6midi->substream_receive = substream; | ||
| 209 | else | ||
| 210 | line6->line6midi->substream_receive = NULL; | ||
| 211 | } | ||
| 212 | |||
| 213 | static struct snd_rawmidi_ops line6_midi_output_ops = { | ||
| 214 | .open = line6_midi_output_open, | ||
| 215 | .close = line6_midi_output_close, | ||
| 216 | .trigger = line6_midi_output_trigger, | ||
| 217 | .drain = line6_midi_output_drain, | ||
| 218 | }; | ||
| 219 | |||
| 220 | static struct snd_rawmidi_ops line6_midi_input_ops = { | ||
| 221 | .open = line6_midi_input_open, | ||
| 222 | .close = line6_midi_input_close, | ||
| 223 | .trigger = line6_midi_input_trigger, | ||
| 224 | }; | ||
| 225 | |||
| 226 | /* | ||
| 227 | Cleanup the Line6 MIDI device. | ||
| 228 | */ | ||
| 229 | static void line6_cleanup_midi(struct snd_rawmidi *rmidi) | ||
| 230 | { | ||
| 231 | } | ||
| 232 | |||
| 233 | /* Create a MIDI device */ | ||
| 234 | static int snd_line6_new_midi(struct snd_line6_midi *line6midi) | ||
| 235 | { | ||
| 236 | struct snd_rawmidi *rmidi; | ||
| 237 | int err; | ||
| 238 | |||
| 239 | err = snd_rawmidi_new(line6midi->line6->card, "Line6 MIDI", 0, 1, 1, | ||
| 240 | &rmidi); | ||
| 241 | if (err < 0) | ||
| 242 | return err; | ||
| 243 | |||
| 244 | rmidi->private_data = line6midi; | ||
| 245 | rmidi->private_free = line6_cleanup_midi; | ||
| 246 | strcpy(rmidi->id, line6midi->line6->properties->id); | ||
| 247 | strcpy(rmidi->name, line6midi->line6->properties->name); | ||
| 248 | |||
| 249 | rmidi->info_flags = | ||
| 250 | SNDRV_RAWMIDI_INFO_OUTPUT | | ||
| 251 | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; | ||
| 252 | |||
| 253 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, | ||
| 254 | &line6_midi_output_ops); | ||
| 255 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, | ||
| 256 | &line6_midi_input_ops); | ||
| 257 | return 0; | ||
| 258 | } | ||
| 259 | |||
| 260 | /* MIDI device destructor */ | ||
| 261 | static int snd_line6_midi_free(struct snd_device *device) | ||
| 262 | { | ||
| 263 | struct snd_line6_midi *line6midi = device->device_data; | ||
| 264 | |||
| 265 | line6_midibuf_destroy(&line6midi->midibuf_in); | ||
| 266 | line6_midibuf_destroy(&line6midi->midibuf_out); | ||
| 267 | return 0; | ||
| 268 | } | ||
| 269 | |||
| 270 | /* | ||
| 271 | Initialize the Line6 MIDI subsystem. | ||
| 272 | */ | ||
| 273 | int line6_init_midi(struct usb_line6 *line6) | ||
| 274 | { | ||
| 275 | static struct snd_device_ops midi_ops = { | ||
| 276 | .dev_free = snd_line6_midi_free, | ||
| 277 | }; | ||
| 278 | |||
| 279 | int err; | ||
| 280 | struct snd_line6_midi *line6midi; | ||
| 281 | |||
| 282 | if (!(line6->properties->capabilities & LINE6_CAP_CONTROL)) { | ||
| 283 | /* skip MIDI initialization and report success */ | ||
| 284 | return 0; | ||
| 285 | } | ||
| 286 | |||
| 287 | line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL); | ||
| 288 | |||
| 289 | if (line6midi == NULL) | ||
| 290 | return -ENOMEM; | ||
| 291 | |||
| 292 | err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); | ||
| 293 | if (err < 0) { | ||
| 294 | kfree(line6midi); | ||
| 295 | return err; | ||
| 296 | } | ||
| 297 | |||
| 298 | err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); | ||
| 299 | if (err < 0) { | ||
| 300 | kfree(line6midi->midibuf_in.buf); | ||
| 301 | kfree(line6midi); | ||
| 302 | return err; | ||
| 303 | } | ||
| 304 | |||
| 305 | line6midi->line6 = line6; | ||
| 306 | line6->line6midi = line6midi; | ||
| 307 | |||
| 308 | err = snd_device_new(line6->card, SNDRV_DEV_RAWMIDI, line6midi, | ||
| 309 | &midi_ops); | ||
| 310 | if (err < 0) | ||
| 311 | return err; | ||
| 312 | |||
| 313 | err = snd_line6_new_midi(line6midi); | ||
| 314 | if (err < 0) | ||
| 315 | return err; | ||
| 316 | |||
| 317 | init_waitqueue_head(&line6midi->send_wait); | ||
| 318 | spin_lock_init(&line6midi->send_urb_lock); | ||
| 319 | spin_lock_init(&line6midi->midi_transmit_lock); | ||
| 320 | return 0; | ||
| 321 | } | ||
diff --git a/sound/usb/line6/midi.h b/sound/usb/line6/midi.h new file mode 100644 index 000000000000..78f903fb4d41 --- /dev/null +++ b/sound/usb/line6/midi.h | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 | #include <sound/rawmidi.h> | ||
| 16 | |||
| 17 | #include "midibuf.h" | ||
| 18 | |||
| 19 | #define MIDI_BUFFER_SIZE 1024 | ||
| 20 | |||
| 21 | struct snd_line6_midi { | ||
| 22 | /** | ||
| 23 | Pointer back to the Line6 driver data structure. | ||
| 24 | */ | ||
| 25 | struct usb_line6 *line6; | ||
| 26 | |||
| 27 | /** | ||
| 28 | MIDI substream for receiving (or NULL if not active). | ||
| 29 | */ | ||
| 30 | struct snd_rawmidi_substream *substream_receive; | ||
| 31 | |||
| 32 | /** | ||
| 33 | MIDI substream for transmitting (or NULL if not active). | ||
| 34 | */ | ||
| 35 | struct snd_rawmidi_substream *substream_transmit; | ||
| 36 | |||
| 37 | /** | ||
| 38 | Number of currently active MIDI send URBs. | ||
| 39 | */ | ||
| 40 | int num_active_send_urbs; | ||
| 41 | |||
| 42 | /** | ||
| 43 | Spin lock to protect updates of send_urb. | ||
| 44 | */ | ||
| 45 | spinlock_t send_urb_lock; | ||
| 46 | |||
| 47 | /** | ||
| 48 | Spin lock to protect MIDI buffer handling. | ||
| 49 | */ | ||
| 50 | spinlock_t midi_transmit_lock; | ||
| 51 | |||
| 52 | /** | ||
| 53 | Wait queue for MIDI transmission. | ||
| 54 | */ | ||
| 55 | wait_queue_head_t send_wait; | ||
| 56 | |||
| 57 | /** | ||
| 58 | Buffer for incoming MIDI stream. | ||
| 59 | */ | ||
| 60 | struct midi_buffer midibuf_in; | ||
| 61 | |||
| 62 | /** | ||
| 63 | Buffer for outgoing MIDI stream. | ||
| 64 | */ | ||
| 65 | struct midi_buffer midibuf_out; | ||
| 66 | }; | ||
| 67 | |||
| 68 | extern int line6_init_midi(struct usb_line6 *line6); | ||
| 69 | extern void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, | ||
| 70 | int length); | ||
| 71 | |||
| 72 | #endif | ||
diff --git a/sound/usb/line6/midibuf.c b/sound/usb/line6/midibuf.c new file mode 100644 index 000000000000..1ff856989fd6 --- /dev/null +++ b/sound/usb/line6/midibuf.c | |||
| @@ -0,0 +1,270 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 <linux/slab.h> | ||
| 13 | |||
| 14 | #include "midibuf.h" | ||
| 15 | |||
| 16 | static int midibuf_message_length(unsigned char code) | ||
| 17 | { | ||
| 18 | int message_length; | ||
| 19 | |||
| 20 | if (code < 0x80) | ||
| 21 | message_length = -1; | ||
| 22 | else if (code < 0xf0) { | ||
| 23 | static const int length[] = { 3, 3, 3, 3, 2, 2, 3 }; | ||
| 24 | |||
| 25 | message_length = length[(code >> 4) - 8]; | ||
| 26 | } else { | ||
| 27 | /* | ||
| 28 | Note that according to the MIDI specification 0xf2 is | ||
| 29 | the "Song Position Pointer", but this is used by Line6 | ||
| 30 | to send sysex messages to the host. | ||
| 31 | */ | ||
| 32 | static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1, | ||
| 33 | 1, 1, 1, -1, 1, 1 | ||
| 34 | }; | ||
| 35 | message_length = length[code & 0x0f]; | ||
| 36 | } | ||
| 37 | |||
| 38 | return message_length; | ||
| 39 | } | ||
| 40 | |||
| 41 | static int midibuf_is_empty(struct midi_buffer *this) | ||
| 42 | { | ||
| 43 | return (this->pos_read == this->pos_write) && !this->full; | ||
| 44 | } | ||
| 45 | |||
| 46 | static int midibuf_is_full(struct midi_buffer *this) | ||
| 47 | { | ||
| 48 | return this->full; | ||
| 49 | } | ||
| 50 | |||
| 51 | void line6_midibuf_reset(struct midi_buffer *this) | ||
| 52 | { | ||
| 53 | this->pos_read = this->pos_write = this->full = 0; | ||
| 54 | this->command_prev = -1; | ||
| 55 | } | ||
| 56 | |||
| 57 | int line6_midibuf_init(struct midi_buffer *this, int size, int split) | ||
| 58 | { | ||
| 59 | this->buf = kmalloc(size, GFP_KERNEL); | ||
| 60 | |||
| 61 | if (this->buf == NULL) | ||
| 62 | return -ENOMEM; | ||
| 63 | |||
| 64 | this->size = size; | ||
| 65 | this->split = split; | ||
| 66 | line6_midibuf_reset(this); | ||
| 67 | return 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | void line6_midibuf_status(struct midi_buffer *this) | ||
| 71 | { | ||
| 72 | pr_debug("midibuf size=%d split=%d pos_read=%d pos_write=%d full=%d command_prev=%02x\n", | ||
| 73 | this->size, this->split, this->pos_read, this->pos_write, | ||
| 74 | this->full, this->command_prev); | ||
| 75 | } | ||
| 76 | |||
| 77 | int line6_midibuf_bytes_free(struct midi_buffer *this) | ||
| 78 | { | ||
| 79 | return | ||
| 80 | midibuf_is_full(this) ? | ||
| 81 | 0 : | ||
| 82 | (this->pos_read - this->pos_write + this->size - 1) % this->size + | ||
| 83 | 1; | ||
| 84 | } | ||
| 85 | |||
| 86 | int line6_midibuf_bytes_used(struct midi_buffer *this) | ||
| 87 | { | ||
| 88 | return | ||
| 89 | midibuf_is_empty(this) ? | ||
| 90 | 0 : | ||
| 91 | (this->pos_write - this->pos_read + this->size - 1) % this->size + | ||
| 92 | 1; | ||
| 93 | } | ||
| 94 | |||
| 95 | int line6_midibuf_write(struct midi_buffer *this, unsigned char *data, | ||
| 96 | int length) | ||
| 97 | { | ||
| 98 | int bytes_free; | ||
| 99 | int length1, length2; | ||
| 100 | int skip_active_sense = 0; | ||
| 101 | |||
| 102 | if (midibuf_is_full(this) || (length <= 0)) | ||
| 103 | return 0; | ||
| 104 | |||
| 105 | /* skip trailing active sense */ | ||
| 106 | if (data[length - 1] == 0xfe) { | ||
| 107 | --length; | ||
| 108 | skip_active_sense = 1; | ||
| 109 | } | ||
| 110 | |||
| 111 | bytes_free = line6_midibuf_bytes_free(this); | ||
| 112 | |||
| 113 | if (length > bytes_free) | ||
| 114 | length = bytes_free; | ||
| 115 | |||
| 116 | if (length > 0) { | ||
| 117 | length1 = this->size - this->pos_write; | ||
| 118 | |||
| 119 | if (length < length1) { | ||
| 120 | /* no buffer wraparound */ | ||
| 121 | memcpy(this->buf + this->pos_write, data, length); | ||
| 122 | this->pos_write += length; | ||
| 123 | } else { | ||
| 124 | /* buffer wraparound */ | ||
| 125 | length2 = length - length1; | ||
| 126 | memcpy(this->buf + this->pos_write, data, length1); | ||
| 127 | memcpy(this->buf, data + length1, length2); | ||
| 128 | this->pos_write = length2; | ||
| 129 | } | ||
| 130 | |||
| 131 | if (this->pos_write == this->pos_read) | ||
| 132 | this->full = 1; | ||
| 133 | } | ||
| 134 | |||
| 135 | return length + skip_active_sense; | ||
| 136 | } | ||
| 137 | |||
| 138 | int line6_midibuf_read(struct midi_buffer *this, unsigned char *data, | ||
| 139 | int length) | ||
| 140 | { | ||
| 141 | int bytes_used; | ||
| 142 | int length1, length2; | ||
| 143 | int command; | ||
| 144 | int midi_length; | ||
| 145 | int repeat = 0; | ||
| 146 | int i; | ||
| 147 | |||
| 148 | /* we need to be able to store at least a 3 byte MIDI message */ | ||
| 149 | if (length < 3) | ||
| 150 | return -EINVAL; | ||
| 151 | |||
| 152 | if (midibuf_is_empty(this)) | ||
| 153 | return 0; | ||
| 154 | |||
| 155 | bytes_used = line6_midibuf_bytes_used(this); | ||
| 156 | |||
| 157 | if (length > bytes_used) | ||
| 158 | length = bytes_used; | ||
| 159 | |||
| 160 | length1 = this->size - this->pos_read; | ||
| 161 | |||
| 162 | /* check MIDI command length */ | ||
| 163 | command = this->buf[this->pos_read]; | ||
| 164 | |||
| 165 | if (command & 0x80) { | ||
| 166 | midi_length = midibuf_message_length(command); | ||
| 167 | this->command_prev = command; | ||
| 168 | } else { | ||
| 169 | if (this->command_prev > 0) { | ||
| 170 | int midi_length_prev = | ||
| 171 | midibuf_message_length(this->command_prev); | ||
| 172 | |||
| 173 | if (midi_length_prev > 0) { | ||
| 174 | midi_length = midi_length_prev - 1; | ||
| 175 | repeat = 1; | ||
| 176 | } else | ||
| 177 | midi_length = -1; | ||
| 178 | } else | ||
| 179 | midi_length = -1; | ||
| 180 | } | ||
| 181 | |||
| 182 | if (midi_length < 0) { | ||
| 183 | /* search for end of message */ | ||
| 184 | if (length < length1) { | ||
| 185 | /* no buffer wraparound */ | ||
| 186 | for (i = 1; i < length; ++i) | ||
| 187 | if (this->buf[this->pos_read + i] & 0x80) | ||
| 188 | break; | ||
| 189 | |||
| 190 | midi_length = i; | ||
| 191 | } else { | ||
| 192 | /* buffer wraparound */ | ||
| 193 | length2 = length - length1; | ||
| 194 | |||
| 195 | for (i = 1; i < length1; ++i) | ||
| 196 | if (this->buf[this->pos_read + i] & 0x80) | ||
| 197 | break; | ||
| 198 | |||
| 199 | if (i < length1) | ||
| 200 | midi_length = i; | ||
| 201 | else { | ||
| 202 | for (i = 0; i < length2; ++i) | ||
| 203 | if (this->buf[i] & 0x80) | ||
| 204 | break; | ||
| 205 | |||
| 206 | midi_length = length1 + i; | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | if (midi_length == length) | ||
| 211 | midi_length = -1; /* end of message not found */ | ||
| 212 | } | ||
| 213 | |||
| 214 | if (midi_length < 0) { | ||
| 215 | if (!this->split) | ||
| 216 | return 0; /* command is not yet complete */ | ||
| 217 | } else { | ||
| 218 | if (length < midi_length) | ||
| 219 | return 0; /* command is not yet complete */ | ||
| 220 | |||
| 221 | length = midi_length; | ||
| 222 | } | ||
| 223 | |||
| 224 | if (length < length1) { | ||
| 225 | /* no buffer wraparound */ | ||
| 226 | memcpy(data + repeat, this->buf + this->pos_read, length); | ||
| 227 | this->pos_read += length; | ||
| 228 | } else { | ||
| 229 | /* buffer wraparound */ | ||
| 230 | length2 = length - length1; | ||
| 231 | memcpy(data + repeat, this->buf + this->pos_read, length1); | ||
| 232 | memcpy(data + repeat + length1, this->buf, length2); | ||
| 233 | this->pos_read = length2; | ||
| 234 | } | ||
| 235 | |||
| 236 | if (repeat) | ||
| 237 | data[0] = this->command_prev; | ||
| 238 | |||
| 239 | this->full = 0; | ||
| 240 | return length + repeat; | ||
| 241 | } | ||
| 242 | |||
| 243 | int line6_midibuf_ignore(struct midi_buffer *this, int length) | ||
| 244 | { | ||
| 245 | int bytes_used = line6_midibuf_bytes_used(this); | ||
| 246 | |||
| 247 | if (length > bytes_used) | ||
| 248 | length = bytes_used; | ||
| 249 | |||
| 250 | this->pos_read = (this->pos_read + length) % this->size; | ||
| 251 | this->full = 0; | ||
| 252 | return length; | ||
| 253 | } | ||
| 254 | |||
| 255 | int line6_midibuf_skip_message(struct midi_buffer *this, unsigned short mask) | ||
| 256 | { | ||
| 257 | int cmd = this->command_prev; | ||
| 258 | |||
| 259 | if ((cmd >= 0x80) && (cmd < 0xf0)) | ||
| 260 | if ((mask & (1 << (cmd & 0x0f))) == 0) | ||
| 261 | return 1; | ||
| 262 | |||
| 263 | return 0; | ||
| 264 | } | ||
| 265 | |||
| 266 | void line6_midibuf_destroy(struct midi_buffer *this) | ||
| 267 | { | ||
| 268 | kfree(this->buf); | ||
| 269 | this->buf = NULL; | ||
| 270 | } | ||
diff --git a/sound/usb/line6/midibuf.h b/sound/usb/line6/midibuf.h new file mode 100644 index 000000000000..707482b940e4 --- /dev/null +++ b/sound/usb/line6/midibuf.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 | struct midi_buffer { | ||
| 16 | unsigned char *buf; | ||
| 17 | int size; | ||
| 18 | int split; | ||
| 19 | int pos_read, pos_write; | ||
| 20 | int full; | ||
| 21 | int command_prev; | ||
| 22 | }; | ||
| 23 | |||
| 24 | extern int line6_midibuf_bytes_used(struct midi_buffer *mb); | ||
| 25 | extern int line6_midibuf_bytes_free(struct midi_buffer *mb); | ||
| 26 | extern void line6_midibuf_destroy(struct midi_buffer *mb); | ||
| 27 | extern int line6_midibuf_ignore(struct midi_buffer *mb, int length); | ||
| 28 | extern int line6_midibuf_init(struct midi_buffer *mb, int size, int split); | ||
| 29 | extern int line6_midibuf_read(struct midi_buffer *mb, unsigned char *data, | ||
| 30 | int length); | ||
| 31 | extern void line6_midibuf_reset(struct midi_buffer *mb); | ||
| 32 | extern int line6_midibuf_skip_message(struct midi_buffer *mb, | ||
| 33 | unsigned short mask); | ||
| 34 | extern void line6_midibuf_status(struct midi_buffer *mb); | ||
| 35 | extern int line6_midibuf_write(struct midi_buffer *mb, unsigned char *data, | ||
| 36 | int length); | ||
| 37 | |||
| 38 | #endif | ||
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c new file mode 100644 index 000000000000..6d4e5cd0482c --- /dev/null +++ b/sound/usb/line6/pcm.c | |||
| @@ -0,0 +1,527 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 <linux/slab.h> | ||
| 13 | #include <sound/core.h> | ||
| 14 | #include <sound/control.h> | ||
| 15 | #include <sound/pcm.h> | ||
| 16 | #include <sound/pcm_params.h> | ||
| 17 | |||
| 18 | #include "audio.h" | ||
| 19 | #include "capture.h" | ||
| 20 | #include "driver.h" | ||
| 21 | #include "playback.h" | ||
| 22 | #include "pod.h" | ||
| 23 | |||
| 24 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 25 | |||
| 26 | static struct snd_line6_pcm *dev2pcm(struct device *dev) | ||
| 27 | { | ||
| 28 | struct usb_interface *interface = to_usb_interface(dev); | ||
| 29 | struct usb_line6 *line6 = usb_get_intfdata(interface); | ||
| 30 | struct snd_line6_pcm *line6pcm = line6->line6pcm; | ||
| 31 | return line6pcm; | ||
| 32 | } | ||
| 33 | |||
| 34 | /* | ||
| 35 | "read" request on "impulse_volume" special file. | ||
| 36 | */ | ||
| 37 | static ssize_t impulse_volume_show(struct device *dev, | ||
| 38 | struct device_attribute *attr, char *buf) | ||
| 39 | { | ||
| 40 | return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_volume); | ||
| 41 | } | ||
| 42 | |||
| 43 | /* | ||
| 44 | "write" request on "impulse_volume" special file. | ||
| 45 | */ | ||
| 46 | static ssize_t impulse_volume_store(struct device *dev, | ||
| 47 | struct device_attribute *attr, | ||
| 48 | const char *buf, size_t count) | ||
| 49 | { | ||
| 50 | struct snd_line6_pcm *line6pcm = dev2pcm(dev); | ||
| 51 | int value; | ||
| 52 | int ret; | ||
| 53 | |||
| 54 | ret = kstrtoint(buf, 10, &value); | ||
| 55 | if (ret < 0) | ||
| 56 | return ret; | ||
| 57 | |||
| 58 | line6pcm->impulse_volume = value; | ||
| 59 | |||
| 60 | if (value > 0) | ||
| 61 | line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_IMPULSE); | ||
| 62 | else | ||
| 63 | line6_pcm_release(line6pcm, LINE6_BITS_PCM_IMPULSE); | ||
| 64 | |||
| 65 | return count; | ||
| 66 | } | ||
| 67 | static DEVICE_ATTR_RW(impulse_volume); | ||
| 68 | |||
| 69 | /* | ||
| 70 | "read" request on "impulse_period" special file. | ||
| 71 | */ | ||
| 72 | static ssize_t impulse_period_show(struct device *dev, | ||
| 73 | struct device_attribute *attr, char *buf) | ||
| 74 | { | ||
| 75 | return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_period); | ||
| 76 | } | ||
| 77 | |||
| 78 | /* | ||
| 79 | "write" request on "impulse_period" special file. | ||
| 80 | */ | ||
| 81 | static ssize_t impulse_period_store(struct device *dev, | ||
| 82 | struct device_attribute *attr, | ||
| 83 | const char *buf, size_t count) | ||
| 84 | { | ||
| 85 | int value; | ||
| 86 | int ret; | ||
| 87 | |||
| 88 | ret = kstrtoint(buf, 10, &value); | ||
| 89 | if (ret < 0) | ||
| 90 | return ret; | ||
| 91 | |||
| 92 | dev2pcm(dev)->impulse_period = value; | ||
| 93 | return count; | ||
| 94 | } | ||
| 95 | static DEVICE_ATTR_RW(impulse_period); | ||
| 96 | |||
| 97 | #endif | ||
| 98 | |||
| 99 | static bool test_flags(unsigned long flags0, unsigned long flags1, | ||
| 100 | unsigned long mask) | ||
| 101 | { | ||
| 102 | return ((flags0 & mask) == 0) && ((flags1 & mask) != 0); | ||
| 103 | } | ||
| 104 | |||
| 105 | int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels) | ||
| 106 | { | ||
| 107 | unsigned long flags_old, flags_new, flags_final; | ||
| 108 | int err; | ||
| 109 | |||
| 110 | do { | ||
| 111 | flags_old = ACCESS_ONCE(line6pcm->flags); | ||
| 112 | flags_new = flags_old | channels; | ||
| 113 | } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); | ||
| 114 | |||
| 115 | flags_final = flags_old; | ||
| 116 | |||
| 117 | line6pcm->prev_fbuf = NULL; | ||
| 118 | |||
| 119 | if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_BUFFER)) { | ||
| 120 | /* Invoked multiple times in a row so allocate once only */ | ||
| 121 | if (!line6pcm->buffer_in) { | ||
| 122 | line6pcm->buffer_in = | ||
| 123 | kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * | ||
| 124 | line6pcm->max_packet_size, GFP_KERNEL); | ||
| 125 | if (!line6pcm->buffer_in) { | ||
| 126 | err = -ENOMEM; | ||
| 127 | goto pcm_acquire_error; | ||
| 128 | } | ||
| 129 | |||
| 130 | flags_final |= channels & LINE6_BITS_CAPTURE_BUFFER; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_STREAM)) { | ||
| 135 | /* | ||
| 136 | Waiting for completion of active URBs in the stop handler is | ||
| 137 | a bug, we therefore report an error if capturing is restarted | ||
| 138 | too soon. | ||
| 139 | */ | ||
| 140 | if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) { | ||
| 141 | dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); | ||
| 142 | return -EBUSY; | ||
| 143 | } | ||
| 144 | |||
| 145 | line6pcm->count_in = 0; | ||
| 146 | line6pcm->prev_fsize = 0; | ||
| 147 | err = line6_submit_audio_in_all_urbs(line6pcm); | ||
| 148 | |||
| 149 | if (err < 0) | ||
| 150 | goto pcm_acquire_error; | ||
| 151 | |||
| 152 | flags_final |= channels & LINE6_BITS_CAPTURE_STREAM; | ||
| 153 | } | ||
| 154 | |||
| 155 | if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_BUFFER)) { | ||
| 156 | /* Invoked multiple times in a row so allocate once only */ | ||
| 157 | if (!line6pcm->buffer_out) { | ||
| 158 | line6pcm->buffer_out = | ||
| 159 | kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * | ||
| 160 | line6pcm->max_packet_size, GFP_KERNEL); | ||
| 161 | if (!line6pcm->buffer_out) { | ||
| 162 | err = -ENOMEM; | ||
| 163 | goto pcm_acquire_error; | ||
| 164 | } | ||
| 165 | |||
| 166 | flags_final |= channels & LINE6_BITS_PLAYBACK_BUFFER; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_STREAM)) { | ||
| 171 | /* | ||
| 172 | See comment above regarding PCM restart. | ||
| 173 | */ | ||
| 174 | if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) { | ||
| 175 | dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); | ||
| 176 | return -EBUSY; | ||
| 177 | } | ||
| 178 | |||
| 179 | line6pcm->count_out = 0; | ||
| 180 | err = line6_submit_audio_out_all_urbs(line6pcm); | ||
| 181 | |||
| 182 | if (err < 0) | ||
| 183 | goto pcm_acquire_error; | ||
| 184 | |||
| 185 | flags_final |= channels & LINE6_BITS_PLAYBACK_STREAM; | ||
| 186 | } | ||
| 187 | |||
| 188 | return 0; | ||
| 189 | |||
| 190 | pcm_acquire_error: | ||
| 191 | /* | ||
| 192 | If not all requested resources/streams could be obtained, release | ||
| 193 | those which were successfully obtained (if any). | ||
| 194 | */ | ||
| 195 | line6_pcm_release(line6pcm, flags_final & channels); | ||
| 196 | return err; | ||
| 197 | } | ||
| 198 | |||
| 199 | int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels) | ||
| 200 | { | ||
| 201 | unsigned long flags_old, flags_new; | ||
| 202 | |||
| 203 | do { | ||
| 204 | flags_old = ACCESS_ONCE(line6pcm->flags); | ||
| 205 | flags_new = flags_old & ~channels; | ||
| 206 | } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); | ||
| 207 | |||
| 208 | if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_STREAM)) | ||
| 209 | line6_unlink_audio_in_urbs(line6pcm); | ||
| 210 | |||
| 211 | if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_BUFFER)) { | ||
| 212 | line6_wait_clear_audio_in_urbs(line6pcm); | ||
| 213 | line6_free_capture_buffer(line6pcm); | ||
| 214 | } | ||
| 215 | |||
| 216 | if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_STREAM)) | ||
| 217 | line6_unlink_audio_out_urbs(line6pcm); | ||
| 218 | |||
| 219 | if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_BUFFER)) { | ||
| 220 | line6_wait_clear_audio_out_urbs(line6pcm); | ||
| 221 | line6_free_playback_buffer(line6pcm); | ||
| 222 | } | ||
| 223 | |||
| 224 | return 0; | ||
| 225 | } | ||
| 226 | |||
| 227 | /* trigger callback */ | ||
| 228 | int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) | ||
| 229 | { | ||
| 230 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
| 231 | struct snd_pcm_substream *s; | ||
| 232 | int err; | ||
| 233 | unsigned long flags; | ||
| 234 | |||
| 235 | spin_lock_irqsave(&line6pcm->lock_trigger, flags); | ||
| 236 | clear_bit(LINE6_INDEX_PREPARED, &line6pcm->flags); | ||
| 237 | |||
| 238 | snd_pcm_group_for_each_entry(s, substream) { | ||
| 239 | switch (s->stream) { | ||
| 240 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
| 241 | err = snd_line6_playback_trigger(line6pcm, cmd); | ||
| 242 | |||
| 243 | if (err < 0) { | ||
| 244 | spin_unlock_irqrestore(&line6pcm->lock_trigger, | ||
| 245 | flags); | ||
| 246 | return err; | ||
| 247 | } | ||
| 248 | |||
| 249 | break; | ||
| 250 | |||
| 251 | case SNDRV_PCM_STREAM_CAPTURE: | ||
| 252 | err = snd_line6_capture_trigger(line6pcm, cmd); | ||
| 253 | |||
| 254 | if (err < 0) { | ||
| 255 | spin_unlock_irqrestore(&line6pcm->lock_trigger, | ||
| 256 | flags); | ||
| 257 | return err; | ||
| 258 | } | ||
| 259 | |||
| 260 | break; | ||
| 261 | |||
| 262 | default: | ||
| 263 | dev_err(line6pcm->line6->ifcdev, | ||
| 264 | "Unknown stream direction %d\n", s->stream); | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | spin_unlock_irqrestore(&line6pcm->lock_trigger, flags); | ||
| 269 | return 0; | ||
| 270 | } | ||
| 271 | |||
| 272 | /* control info callback */ | ||
| 273 | static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, | ||
| 274 | struct snd_ctl_elem_info *uinfo) | ||
| 275 | { | ||
| 276 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
| 277 | uinfo->count = 2; | ||
| 278 | uinfo->value.integer.min = 0; | ||
| 279 | uinfo->value.integer.max = 256; | ||
| 280 | return 0; | ||
| 281 | } | ||
| 282 | |||
| 283 | /* control get callback */ | ||
| 284 | static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, | ||
| 285 | struct snd_ctl_elem_value *ucontrol) | ||
| 286 | { | ||
| 287 | int i; | ||
| 288 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
| 289 | |||
| 290 | for (i = 2; i--;) | ||
| 291 | ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; | ||
| 292 | |||
| 293 | return 0; | ||
| 294 | } | ||
| 295 | |||
| 296 | /* control put callback */ | ||
| 297 | static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, | ||
| 298 | struct snd_ctl_elem_value *ucontrol) | ||
| 299 | { | ||
| 300 | int i, changed = 0; | ||
| 301 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
| 302 | |||
| 303 | for (i = 2; i--;) | ||
| 304 | if (line6pcm->volume_playback[i] != | ||
| 305 | ucontrol->value.integer.value[i]) { | ||
| 306 | line6pcm->volume_playback[i] = | ||
| 307 | ucontrol->value.integer.value[i]; | ||
| 308 | changed = 1; | ||
| 309 | } | ||
| 310 | |||
| 311 | return changed; | ||
| 312 | } | ||
| 313 | |||
| 314 | /* control definition */ | ||
| 315 | static struct snd_kcontrol_new line6_control_playback = { | ||
| 316 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 317 | .name = "PCM Playback Volume", | ||
| 318 | .index = 0, | ||
| 319 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 320 | .info = snd_line6_control_playback_info, | ||
| 321 | .get = snd_line6_control_playback_get, | ||
| 322 | .put = snd_line6_control_playback_put | ||
| 323 | }; | ||
| 324 | |||
| 325 | /* | ||
| 326 | Cleanup the PCM device. | ||
| 327 | */ | ||
| 328 | static void line6_cleanup_pcm(struct snd_pcm *pcm) | ||
| 329 | { | ||
| 330 | int i; | ||
| 331 | struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); | ||
| 332 | |||
| 333 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 334 | device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_volume); | ||
| 335 | device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_period); | ||
| 336 | #endif | ||
| 337 | |||
| 338 | for (i = LINE6_ISO_BUFFERS; i--;) { | ||
| 339 | if (line6pcm->urb_audio_out[i]) { | ||
| 340 | usb_kill_urb(line6pcm->urb_audio_out[i]); | ||
| 341 | usb_free_urb(line6pcm->urb_audio_out[i]); | ||
| 342 | } | ||
| 343 | if (line6pcm->urb_audio_in[i]) { | ||
| 344 | usb_kill_urb(line6pcm->urb_audio_in[i]); | ||
| 345 | usb_free_urb(line6pcm->urb_audio_in[i]); | ||
| 346 | } | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | /* create a PCM device */ | ||
| 351 | static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm) | ||
| 352 | { | ||
| 353 | struct snd_pcm *pcm; | ||
| 354 | int err; | ||
| 355 | |||
| 356 | err = snd_pcm_new(line6pcm->line6->card, | ||
| 357 | (char *)line6pcm->line6->properties->name, | ||
| 358 | 0, 1, 1, &pcm); | ||
| 359 | if (err < 0) | ||
| 360 | return err; | ||
| 361 | |||
| 362 | pcm->private_data = line6pcm; | ||
| 363 | pcm->private_free = line6_cleanup_pcm; | ||
| 364 | line6pcm->pcm = pcm; | ||
| 365 | strcpy(pcm->name, line6pcm->line6->properties->name); | ||
| 366 | |||
| 367 | /* set operators */ | ||
| 368 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, | ||
| 369 | &snd_line6_playback_ops); | ||
| 370 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); | ||
| 371 | |||
| 372 | /* pre-allocation of buffers */ | ||
| 373 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, | ||
| 374 | snd_dma_continuous_data | ||
| 375 | (GFP_KERNEL), 64 * 1024, | ||
| 376 | 128 * 1024); | ||
| 377 | |||
| 378 | return 0; | ||
| 379 | } | ||
| 380 | |||
| 381 | /* PCM device destructor */ | ||
| 382 | static int snd_line6_pcm_free(struct snd_device *device) | ||
| 383 | { | ||
| 384 | return 0; | ||
| 385 | } | ||
| 386 | |||
| 387 | /* | ||
| 388 | Stop substream if still running. | ||
| 389 | */ | ||
| 390 | static void pcm_disconnect_substream(struct snd_pcm_substream *substream) | ||
| 391 | { | ||
| 392 | if (substream->runtime && snd_pcm_running(substream)) { | ||
| 393 | snd_pcm_stream_lock_irq(substream); | ||
| 394 | snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); | ||
| 395 | snd_pcm_stream_unlock_irq(substream); | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 399 | /* | ||
| 400 | Stop PCM stream. | ||
| 401 | */ | ||
| 402 | void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) | ||
| 403 | { | ||
| 404 | pcm_disconnect_substream(get_substream | ||
| 405 | (line6pcm, SNDRV_PCM_STREAM_CAPTURE)); | ||
| 406 | pcm_disconnect_substream(get_substream | ||
| 407 | (line6pcm, SNDRV_PCM_STREAM_PLAYBACK)); | ||
| 408 | line6_unlink_wait_clear_audio_out_urbs(line6pcm); | ||
| 409 | line6_unlink_wait_clear_audio_in_urbs(line6pcm); | ||
| 410 | } | ||
| 411 | |||
| 412 | /* | ||
| 413 | Create and register the PCM device and mixer entries. | ||
| 414 | Create URBs for playback and capture. | ||
| 415 | */ | ||
| 416 | int line6_init_pcm(struct usb_line6 *line6, | ||
| 417 | struct line6_pcm_properties *properties) | ||
| 418 | { | ||
| 419 | static struct snd_device_ops pcm_ops = { | ||
| 420 | .dev_free = snd_line6_pcm_free, | ||
| 421 | }; | ||
| 422 | |||
| 423 | int err; | ||
| 424 | unsigned ep_read = line6->properties->ep_audio_r; | ||
| 425 | unsigned ep_write = line6->properties->ep_audio_w; | ||
| 426 | struct snd_line6_pcm *line6pcm; | ||
| 427 | |||
| 428 | if (!(line6->properties->capabilities & LINE6_CAP_PCM)) | ||
| 429 | return 0; /* skip PCM initialization and report success */ | ||
| 430 | |||
| 431 | line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL); | ||
| 432 | |||
| 433 | if (line6pcm == NULL) | ||
| 434 | return -ENOMEM; | ||
| 435 | |||
| 436 | line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; | ||
| 437 | line6pcm->volume_monitor = 255; | ||
| 438 | line6pcm->line6 = line6; | ||
| 439 | |||
| 440 | /* Read and write buffers are sized identically, so choose minimum */ | ||
| 441 | line6pcm->max_packet_size = min( | ||
| 442 | usb_maxpacket(line6->usbdev, | ||
| 443 | usb_rcvisocpipe(line6->usbdev, ep_read), 0), | ||
| 444 | usb_maxpacket(line6->usbdev, | ||
| 445 | usb_sndisocpipe(line6->usbdev, ep_write), 1)); | ||
| 446 | |||
| 447 | line6pcm->properties = properties; | ||
| 448 | line6->line6pcm = line6pcm; | ||
| 449 | |||
| 450 | /* PCM device: */ | ||
| 451 | err = snd_device_new(line6->card, SNDRV_DEV_PCM, line6, &pcm_ops); | ||
| 452 | if (err < 0) | ||
| 453 | return err; | ||
| 454 | |||
| 455 | err = snd_line6_new_pcm(line6pcm); | ||
| 456 | if (err < 0) | ||
| 457 | return err; | ||
| 458 | |||
| 459 | spin_lock_init(&line6pcm->lock_audio_out); | ||
| 460 | spin_lock_init(&line6pcm->lock_audio_in); | ||
| 461 | spin_lock_init(&line6pcm->lock_trigger); | ||
| 462 | |||
| 463 | err = line6_create_audio_out_urbs(line6pcm); | ||
| 464 | if (err < 0) | ||
| 465 | return err; | ||
| 466 | |||
| 467 | err = line6_create_audio_in_urbs(line6pcm); | ||
| 468 | if (err < 0) | ||
| 469 | return err; | ||
| 470 | |||
| 471 | /* mixer: */ | ||
| 472 | err = | ||
| 473 | snd_ctl_add(line6->card, | ||
| 474 | snd_ctl_new1(&line6_control_playback, line6pcm)); | ||
| 475 | if (err < 0) | ||
| 476 | return err; | ||
| 477 | |||
| 478 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 479 | /* impulse response test: */ | ||
| 480 | err = device_create_file(line6->ifcdev, &dev_attr_impulse_volume); | ||
| 481 | if (err < 0) | ||
| 482 | return err; | ||
| 483 | |||
| 484 | err = device_create_file(line6->ifcdev, &dev_attr_impulse_period); | ||
| 485 | if (err < 0) | ||
| 486 | return err; | ||
| 487 | |||
| 488 | line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; | ||
| 489 | #endif | ||
| 490 | |||
| 491 | return 0; | ||
| 492 | } | ||
| 493 | |||
| 494 | /* prepare pcm callback */ | ||
| 495 | int snd_line6_prepare(struct snd_pcm_substream *substream) | ||
| 496 | { | ||
| 497 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
| 498 | |||
| 499 | switch (substream->stream) { | ||
| 500 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
| 501 | if ((line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) == 0) | ||
| 502 | line6_unlink_wait_clear_audio_out_urbs(line6pcm); | ||
| 503 | |||
| 504 | break; | ||
| 505 | |||
| 506 | case SNDRV_PCM_STREAM_CAPTURE: | ||
| 507 | if ((line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) == 0) | ||
| 508 | line6_unlink_wait_clear_audio_in_urbs(line6pcm); | ||
| 509 | |||
| 510 | break; | ||
| 511 | |||
| 512 | default: | ||
| 513 | MISSING_CASE; | ||
| 514 | } | ||
| 515 | |||
| 516 | if (!test_and_set_bit(LINE6_INDEX_PREPARED, &line6pcm->flags)) { | ||
| 517 | line6pcm->count_out = 0; | ||
| 518 | line6pcm->pos_out = 0; | ||
| 519 | line6pcm->pos_out_done = 0; | ||
| 520 | line6pcm->bytes_out = 0; | ||
| 521 | line6pcm->count_in = 0; | ||
| 522 | line6pcm->pos_in_done = 0; | ||
| 523 | line6pcm->bytes_in = 0; | ||
| 524 | } | ||
| 525 | |||
| 526 | return 0; | ||
| 527 | } | ||
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h new file mode 100644 index 000000000000..7315e8131184 --- /dev/null +++ b/sound/usb/line6/pcm.h | |||
| @@ -0,0 +1,374 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 | #include <sound/pcm.h> | ||
| 20 | |||
| 21 | #include "driver.h" | ||
| 22 | #include "usbdefs.h" | ||
| 23 | |||
| 24 | /* number of URBs */ | ||
| 25 | #define LINE6_ISO_BUFFERS 2 | ||
| 26 | |||
| 27 | /* | ||
| 28 | number of USB frames per URB | ||
| 29 | The Line6 Windows driver always transmits two frames per packet, but | ||
| 30 | the Linux driver performs significantly better (i.e., lower latency) | ||
| 31 | with only one frame per packet. | ||
| 32 | */ | ||
| 33 | #define LINE6_ISO_PACKETS 1 | ||
| 34 | |||
| 35 | /* in a "full speed" device (such as the PODxt Pro) this means 1ms */ | ||
| 36 | #define LINE6_ISO_INTERVAL 1 | ||
| 37 | |||
| 38 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 39 | #define LINE6_IMPULSE_DEFAULT_PERIOD 100 | ||
| 40 | #endif | ||
| 41 | |||
| 42 | /* | ||
| 43 | Get substream from Line6 PCM data structure | ||
| 44 | */ | ||
| 45 | #define get_substream(line6pcm, stream) \ | ||
| 46 | (line6pcm->pcm->streams[stream].substream) | ||
| 47 | |||
| 48 | /* | ||
| 49 | PCM mode bits. | ||
| 50 | |||
| 51 | There are several features of the Line6 USB driver which require PCM | ||
| 52 | data to be exchanged with the device: | ||
| 53 | *) PCM playback and capture via ALSA | ||
| 54 | *) software monitoring (for devices without hardware monitoring) | ||
| 55 | *) optional impulse response measurement | ||
| 56 | However, from the device's point of view, there is just a single | ||
| 57 | capture and playback stream, which must be shared between these | ||
| 58 | subsystems. It is therefore necessary to maintain the state of the | ||
| 59 | subsystems with respect to PCM usage. We define several constants of | ||
| 60 | the form LINE6_BIT_PCM_<subsystem>_<direction>_<resource> with the | ||
| 61 | following meanings: | ||
| 62 | *) <subsystem> is one of | ||
| 63 | -) ALSA: PCM playback and capture via ALSA | ||
| 64 | -) MONITOR: software monitoring | ||
| 65 | -) IMPULSE: optional impulse response measurement | ||
| 66 | *) <direction> is one of | ||
| 67 | -) PLAYBACK: audio output (from host to device) | ||
| 68 | -) CAPTURE: audio input (from device to host) | ||
| 69 | *) <resource> is one of | ||
| 70 | -) BUFFER: buffer required by PCM data stream | ||
| 71 | -) STREAM: actual PCM data stream | ||
| 72 | |||
| 73 | The subsystems call line6_pcm_acquire() to acquire the (shared) | ||
| 74 | resources needed for a particular operation (e.g., allocate the buffer | ||
| 75 | for ALSA playback or start the capture stream for software monitoring). | ||
| 76 | When a resource is no longer needed, it is released by calling | ||
| 77 | line6_pcm_release(). Buffer allocation and stream startup are handled | ||
| 78 | separately to allow the ALSA kernel driver to perform them at | ||
| 79 | appropriate places (since the callback which starts a PCM stream is not | ||
| 80 | allowed to sleep). | ||
| 81 | */ | ||
| 82 | enum { | ||
| 83 | /* individual bit indices: */ | ||
| 84 | LINE6_INDEX_PCM_ALSA_PLAYBACK_BUFFER, | ||
| 85 | LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, | ||
| 86 | LINE6_INDEX_PCM_ALSA_CAPTURE_BUFFER, | ||
| 87 | LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, | ||
| 88 | LINE6_INDEX_PCM_MONITOR_PLAYBACK_BUFFER, | ||
| 89 | LINE6_INDEX_PCM_MONITOR_PLAYBACK_STREAM, | ||
| 90 | LINE6_INDEX_PCM_MONITOR_CAPTURE_BUFFER, | ||
| 91 | LINE6_INDEX_PCM_MONITOR_CAPTURE_STREAM, | ||
| 92 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 93 | LINE6_INDEX_PCM_IMPULSE_PLAYBACK_BUFFER, | ||
| 94 | LINE6_INDEX_PCM_IMPULSE_PLAYBACK_STREAM, | ||
| 95 | LINE6_INDEX_PCM_IMPULSE_CAPTURE_BUFFER, | ||
| 96 | LINE6_INDEX_PCM_IMPULSE_CAPTURE_STREAM, | ||
| 97 | #endif | ||
| 98 | LINE6_INDEX_PAUSE_PLAYBACK, | ||
| 99 | LINE6_INDEX_PREPARED, | ||
| 100 | |||
| 101 | #define LINE6_BIT(x) LINE6_BIT_ ## x = 1 << LINE6_INDEX_ ## x | ||
| 102 | |||
| 103 | /* individual bit masks: */ | ||
| 104 | LINE6_BIT(PCM_ALSA_PLAYBACK_BUFFER), | ||
| 105 | LINE6_BIT(PCM_ALSA_PLAYBACK_STREAM), | ||
| 106 | LINE6_BIT(PCM_ALSA_CAPTURE_BUFFER), | ||
| 107 | LINE6_BIT(PCM_ALSA_CAPTURE_STREAM), | ||
| 108 | LINE6_BIT(PCM_MONITOR_PLAYBACK_BUFFER), | ||
| 109 | LINE6_BIT(PCM_MONITOR_PLAYBACK_STREAM), | ||
| 110 | LINE6_BIT(PCM_MONITOR_CAPTURE_BUFFER), | ||
| 111 | LINE6_BIT(PCM_MONITOR_CAPTURE_STREAM), | ||
| 112 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 113 | LINE6_BIT(PCM_IMPULSE_PLAYBACK_BUFFER), | ||
| 114 | LINE6_BIT(PCM_IMPULSE_PLAYBACK_STREAM), | ||
| 115 | LINE6_BIT(PCM_IMPULSE_CAPTURE_BUFFER), | ||
| 116 | LINE6_BIT(PCM_IMPULSE_CAPTURE_STREAM), | ||
| 117 | #endif | ||
| 118 | LINE6_BIT(PAUSE_PLAYBACK), | ||
| 119 | LINE6_BIT(PREPARED), | ||
| 120 | |||
| 121 | /* combined bit masks (by operation): */ | ||
| 122 | LINE6_BITS_PCM_ALSA_BUFFER = | ||
| 123 | LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | | ||
| 124 | LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER, | ||
| 125 | |||
| 126 | LINE6_BITS_PCM_ALSA_STREAM = | ||
| 127 | LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | | ||
| 128 | LINE6_BIT_PCM_ALSA_CAPTURE_STREAM, | ||
| 129 | |||
| 130 | LINE6_BITS_PCM_MONITOR = | ||
| 131 | LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER | | ||
| 132 | LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM | | ||
| 133 | LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER | | ||
| 134 | LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, | ||
| 135 | |||
| 136 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 137 | LINE6_BITS_PCM_IMPULSE = | ||
| 138 | LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | | ||
| 139 | LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | | ||
| 140 | LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | | ||
| 141 | LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM, | ||
| 142 | #endif | ||
| 143 | |||
| 144 | /* combined bit masks (by direction): */ | ||
| 145 | LINE6_BITS_PLAYBACK_BUFFER = | ||
| 146 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 147 | LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | | ||
| 148 | #endif | ||
| 149 | LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | | ||
| 150 | LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER, | ||
| 151 | |||
| 152 | LINE6_BITS_PLAYBACK_STREAM = | ||
| 153 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 154 | LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | | ||
| 155 | #endif | ||
| 156 | LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | | ||
| 157 | LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM, | ||
| 158 | |||
| 159 | LINE6_BITS_CAPTURE_BUFFER = | ||
| 160 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 161 | LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | | ||
| 162 | #endif | ||
| 163 | LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER | | ||
| 164 | LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER, | ||
| 165 | |||
| 166 | LINE6_BITS_CAPTURE_STREAM = | ||
| 167 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 168 | LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM | | ||
| 169 | #endif | ||
| 170 | LINE6_BIT_PCM_ALSA_CAPTURE_STREAM | | ||
| 171 | LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, | ||
| 172 | |||
| 173 | LINE6_BITS_STREAM = | ||
| 174 | LINE6_BITS_PLAYBACK_STREAM | | ||
| 175 | LINE6_BITS_CAPTURE_STREAM | ||
| 176 | }; | ||
| 177 | |||
| 178 | struct line6_pcm_properties { | ||
| 179 | struct snd_pcm_hardware snd_line6_playback_hw, snd_line6_capture_hw; | ||
| 180 | struct snd_pcm_hw_constraint_ratdens snd_line6_rates; | ||
| 181 | int bytes_per_frame; | ||
| 182 | }; | ||
| 183 | |||
| 184 | struct snd_line6_pcm { | ||
| 185 | /** | ||
| 186 | Pointer back to the Line6 driver data structure. | ||
| 187 | */ | ||
| 188 | struct usb_line6 *line6; | ||
| 189 | |||
| 190 | /** | ||
| 191 | Properties. | ||
| 192 | */ | ||
| 193 | struct line6_pcm_properties *properties; | ||
| 194 | |||
| 195 | /** | ||
| 196 | ALSA pcm stream | ||
| 197 | */ | ||
| 198 | struct snd_pcm *pcm; | ||
| 199 | |||
| 200 | /** | ||
| 201 | URBs for audio playback. | ||
| 202 | */ | ||
| 203 | struct urb *urb_audio_out[LINE6_ISO_BUFFERS]; | ||
| 204 | |||
| 205 | /** | ||
| 206 | URBs for audio capture. | ||
| 207 | */ | ||
| 208 | struct urb *urb_audio_in[LINE6_ISO_BUFFERS]; | ||
| 209 | |||
| 210 | /** | ||
| 211 | Temporary buffer for playback. | ||
| 212 | Since the packet size is not known in advance, this buffer is | ||
| 213 | large enough to store maximum size packets. | ||
| 214 | */ | ||
| 215 | unsigned char *buffer_out; | ||
| 216 | |||
| 217 | /** | ||
| 218 | Temporary buffer for capture. | ||
| 219 | Since the packet size is not known in advance, this buffer is | ||
| 220 | large enough to store maximum size packets. | ||
| 221 | */ | ||
| 222 | unsigned char *buffer_in; | ||
| 223 | |||
| 224 | /** | ||
| 225 | Previously captured frame (for software monitoring). | ||
| 226 | */ | ||
| 227 | unsigned char *prev_fbuf; | ||
| 228 | |||
| 229 | /** | ||
| 230 | Size of previously captured frame (for software monitoring). | ||
| 231 | */ | ||
| 232 | int prev_fsize; | ||
| 233 | |||
| 234 | /** | ||
| 235 | Free frame position in the playback buffer. | ||
| 236 | */ | ||
| 237 | snd_pcm_uframes_t pos_out; | ||
| 238 | |||
| 239 | /** | ||
| 240 | Count processed bytes for playback. | ||
| 241 | This is modulo period size (to determine when a period is | ||
| 242 | finished). | ||
| 243 | */ | ||
| 244 | unsigned bytes_out; | ||
| 245 | |||
| 246 | /** | ||
| 247 | Counter to create desired playback sample rate. | ||
| 248 | */ | ||
| 249 | unsigned count_out; | ||
| 250 | |||
| 251 | /** | ||
| 252 | Playback period size in bytes | ||
| 253 | */ | ||
| 254 | unsigned period_out; | ||
| 255 | |||
| 256 | /** | ||
| 257 | Processed frame position in the playback buffer. | ||
| 258 | The contents of the output ring buffer have been consumed by | ||
| 259 | the USB subsystem (i.e., sent to the USB device) up to this | ||
| 260 | position. | ||
| 261 | */ | ||
| 262 | snd_pcm_uframes_t pos_out_done; | ||
| 263 | |||
| 264 | /** | ||
| 265 | Count processed bytes for capture. | ||
| 266 | This is modulo period size (to determine when a period is | ||
| 267 | finished). | ||
| 268 | */ | ||
| 269 | unsigned bytes_in; | ||
| 270 | |||
| 271 | /** | ||
| 272 | Counter to create desired capture sample rate. | ||
| 273 | */ | ||
| 274 | unsigned count_in; | ||
| 275 | |||
| 276 | /** | ||
| 277 | Capture period size in bytes | ||
| 278 | */ | ||
| 279 | unsigned period_in; | ||
| 280 | |||
| 281 | /** | ||
| 282 | Processed frame position in the capture buffer. | ||
| 283 | The contents of the output ring buffer have been consumed by | ||
| 284 | the USB subsystem (i.e., sent to the USB device) up to this | ||
| 285 | position. | ||
| 286 | */ | ||
| 287 | snd_pcm_uframes_t pos_in_done; | ||
| 288 | |||
| 289 | /** | ||
| 290 | Bit mask of active playback URBs. | ||
| 291 | */ | ||
| 292 | unsigned long active_urb_out; | ||
| 293 | |||
| 294 | /** | ||
| 295 | Maximum size of USB packet. | ||
| 296 | */ | ||
| 297 | int max_packet_size; | ||
| 298 | |||
| 299 | /** | ||
| 300 | Bit mask of active capture URBs. | ||
| 301 | */ | ||
| 302 | unsigned long active_urb_in; | ||
| 303 | |||
| 304 | /** | ||
| 305 | Bit mask of playback URBs currently being unlinked. | ||
| 306 | */ | ||
| 307 | unsigned long unlink_urb_out; | ||
| 308 | |||
| 309 | /** | ||
| 310 | Bit mask of capture URBs currently being unlinked. | ||
| 311 | */ | ||
| 312 | unsigned long unlink_urb_in; | ||
| 313 | |||
| 314 | /** | ||
| 315 | Spin lock to protect updates of the playback buffer positions (not | ||
| 316 | contents!) | ||
| 317 | */ | ||
| 318 | spinlock_t lock_audio_out; | ||
| 319 | |||
| 320 | /** | ||
| 321 | Spin lock to protect updates of the capture buffer positions (not | ||
| 322 | contents!) | ||
| 323 | */ | ||
| 324 | spinlock_t lock_audio_in; | ||
| 325 | |||
| 326 | /** | ||
| 327 | Spin lock to protect trigger. | ||
| 328 | */ | ||
| 329 | spinlock_t lock_trigger; | ||
| 330 | |||
| 331 | /** | ||
| 332 | PCM playback volume (left and right). | ||
| 333 | */ | ||
| 334 | int volume_playback[2]; | ||
| 335 | |||
| 336 | /** | ||
| 337 | PCM monitor volume. | ||
| 338 | */ | ||
| 339 | int volume_monitor; | ||
| 340 | |||
| 341 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 342 | /** | ||
| 343 | Volume of impulse response test signal (if zero, test is disabled). | ||
| 344 | */ | ||
| 345 | int impulse_volume; | ||
| 346 | |||
| 347 | /** | ||
| 348 | Period of impulse response test signal. | ||
| 349 | */ | ||
| 350 | int impulse_period; | ||
| 351 | |||
| 352 | /** | ||
| 353 | Counter for impulse response test signal. | ||
| 354 | */ | ||
| 355 | int impulse_count; | ||
| 356 | #endif | ||
| 357 | |||
| 358 | /** | ||
| 359 | Several status bits (see LINE6_BIT_*). | ||
| 360 | */ | ||
| 361 | unsigned long flags; | ||
| 362 | |||
| 363 | int last_frame_in, last_frame_out; | ||
| 364 | }; | ||
| 365 | |||
| 366 | extern int line6_init_pcm(struct usb_line6 *line6, | ||
| 367 | struct line6_pcm_properties *properties); | ||
| 368 | extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd); | ||
| 369 | extern int snd_line6_prepare(struct snd_pcm_substream *substream); | ||
| 370 | extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm); | ||
| 371 | extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels); | ||
| 372 | extern int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels); | ||
| 373 | |||
| 374 | #endif | ||
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c new file mode 100644 index 000000000000..da2e3b8876b8 --- /dev/null +++ b/sound/usb/line6/playback.c | |||
| @@ -0,0 +1,593 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 <linux/slab.h> | ||
| 13 | #include <sound/core.h> | ||
| 14 | #include <sound/pcm.h> | ||
| 15 | #include <sound/pcm_params.h> | ||
| 16 | |||
| 17 | #include "audio.h" | ||
| 18 | #include "capture.h" | ||
| 19 | #include "driver.h" | ||
| 20 | #include "pcm.h" | ||
| 21 | #include "pod.h" | ||
| 22 | #include "playback.h" | ||
| 23 | |||
| 24 | /* | ||
| 25 | Software stereo volume control. | ||
| 26 | */ | ||
| 27 | static void change_volume(struct urb *urb_out, int volume[], | ||
| 28 | int bytes_per_frame) | ||
| 29 | { | ||
| 30 | int chn = 0; | ||
| 31 | |||
| 32 | if (volume[0] == 256 && volume[1] == 256) | ||
| 33 | return; /* maximum volume - no change */ | ||
| 34 | |||
| 35 | if (bytes_per_frame == 4) { | ||
| 36 | short *p, *buf_end; | ||
| 37 | |||
| 38 | p = (short *)urb_out->transfer_buffer; | ||
| 39 | buf_end = p + urb_out->transfer_buffer_length / sizeof(*p); | ||
| 40 | |||
| 41 | for (; p < buf_end; ++p) { | ||
| 42 | *p = (*p * volume[chn & 1]) >> 8; | ||
| 43 | ++chn; | ||
| 44 | } | ||
| 45 | } else if (bytes_per_frame == 6) { | ||
| 46 | unsigned char *p, *buf_end; | ||
| 47 | |||
| 48 | p = (unsigned char *)urb_out->transfer_buffer; | ||
| 49 | buf_end = p + urb_out->transfer_buffer_length; | ||
| 50 | |||
| 51 | for (; p < buf_end; p += 3) { | ||
| 52 | int val; | ||
| 53 | |||
| 54 | val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16); | ||
| 55 | val = (val * volume[chn & 1]) >> 8; | ||
| 56 | p[0] = val; | ||
| 57 | p[1] = val >> 8; | ||
| 58 | p[2] = val >> 16; | ||
| 59 | ++chn; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 65 | |||
| 66 | /* | ||
| 67 | Create signal for impulse response test. | ||
| 68 | */ | ||
| 69 | static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm, | ||
| 70 | struct urb *urb_out, int bytes_per_frame) | ||
| 71 | { | ||
| 72 | int frames = urb_out->transfer_buffer_length / bytes_per_frame; | ||
| 73 | |||
| 74 | if (bytes_per_frame == 4) { | ||
| 75 | int i; | ||
| 76 | short *pi = (short *)line6pcm->prev_fbuf; | ||
| 77 | short *po = (short *)urb_out->transfer_buffer; | ||
| 78 | |||
| 79 | for (i = 0; i < frames; ++i) { | ||
| 80 | po[0] = pi[0]; | ||
| 81 | po[1] = 0; | ||
| 82 | pi += 2; | ||
| 83 | po += 2; | ||
| 84 | } | ||
| 85 | } else if (bytes_per_frame == 6) { | ||
| 86 | int i, j; | ||
| 87 | unsigned char *pi = line6pcm->prev_fbuf; | ||
| 88 | unsigned char *po = urb_out->transfer_buffer; | ||
| 89 | |||
| 90 | for (i = 0; i < frames; ++i) { | ||
| 91 | for (j = 0; j < bytes_per_frame / 2; ++j) | ||
| 92 | po[j] = pi[j]; | ||
| 93 | |||
| 94 | for (; j < bytes_per_frame; ++j) | ||
| 95 | po[j] = 0; | ||
| 96 | |||
| 97 | pi += bytes_per_frame; | ||
| 98 | po += bytes_per_frame; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | if (--line6pcm->impulse_count <= 0) { | ||
| 102 | ((unsigned char *)(urb_out->transfer_buffer))[bytes_per_frame - | ||
| 103 | 1] = | ||
| 104 | line6pcm->impulse_volume; | ||
| 105 | line6pcm->impulse_count = line6pcm->impulse_period; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | #endif | ||
| 110 | |||
| 111 | /* | ||
| 112 | Add signal to buffer for software monitoring. | ||
| 113 | */ | ||
| 114 | static void add_monitor_signal(struct urb *urb_out, unsigned char *signal, | ||
| 115 | int volume, int bytes_per_frame) | ||
| 116 | { | ||
| 117 | if (volume == 0) | ||
| 118 | return; /* zero volume - no change */ | ||
| 119 | |||
| 120 | if (bytes_per_frame == 4) { | ||
| 121 | short *pi, *po, *buf_end; | ||
| 122 | |||
| 123 | pi = (short *)signal; | ||
| 124 | po = (short *)urb_out->transfer_buffer; | ||
| 125 | buf_end = po + urb_out->transfer_buffer_length / sizeof(*po); | ||
| 126 | |||
| 127 | for (; po < buf_end; ++pi, ++po) | ||
| 128 | *po += (*pi * volume) >> 8; | ||
| 129 | } | ||
| 130 | |||
| 131 | /* | ||
| 132 | We don't need to handle devices with 6 bytes per frame here | ||
| 133 | since they all support hardware monitoring. | ||
| 134 | */ | ||
| 135 | } | ||
| 136 | |||
| 137 | /* | ||
| 138 | Find a free URB, prepare audio data, and submit URB. | ||
| 139 | */ | ||
| 140 | static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) | ||
| 141 | { | ||
| 142 | int index; | ||
| 143 | unsigned long flags; | ||
| 144 | int i, urb_size, urb_frames; | ||
| 145 | int ret; | ||
| 146 | const int bytes_per_frame = line6pcm->properties->bytes_per_frame; | ||
| 147 | const int frame_increment = | ||
| 148 | line6pcm->properties->snd_line6_rates.rats[0].num_min; | ||
| 149 | const int frame_factor = | ||
| 150 | line6pcm->properties->snd_line6_rates.rats[0].den * | ||
| 151 | (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL); | ||
| 152 | struct urb *urb_out; | ||
| 153 | |||
| 154 | spin_lock_irqsave(&line6pcm->lock_audio_out, flags); | ||
| 155 | index = | ||
| 156 | find_first_zero_bit(&line6pcm->active_urb_out, LINE6_ISO_BUFFERS); | ||
| 157 | |||
| 158 | if (index < 0 || index >= LINE6_ISO_BUFFERS) { | ||
| 159 | spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); | ||
| 160 | dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); | ||
| 161 | return -EINVAL; | ||
| 162 | } | ||
| 163 | |||
| 164 | urb_out = line6pcm->urb_audio_out[index]; | ||
| 165 | urb_size = 0; | ||
| 166 | |||
| 167 | for (i = 0; i < LINE6_ISO_PACKETS; ++i) { | ||
| 168 | /* compute frame size for given sampling rate */ | ||
| 169 | int fsize = 0; | ||
| 170 | struct usb_iso_packet_descriptor *fout = | ||
| 171 | &urb_out->iso_frame_desc[i]; | ||
| 172 | |||
| 173 | if (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) | ||
| 174 | fsize = line6pcm->prev_fsize; | ||
| 175 | |||
| 176 | if (fsize == 0) { | ||
| 177 | int n; | ||
| 178 | |||
| 179 | line6pcm->count_out += frame_increment; | ||
| 180 | n = line6pcm->count_out / frame_factor; | ||
| 181 | line6pcm->count_out -= n * frame_factor; | ||
| 182 | fsize = n * bytes_per_frame; | ||
| 183 | } | ||
| 184 | |||
| 185 | fout->offset = urb_size; | ||
| 186 | fout->length = fsize; | ||
| 187 | urb_size += fsize; | ||
| 188 | } | ||
| 189 | |||
| 190 | if (urb_size == 0) { | ||
| 191 | /* can't determine URB size */ | ||
| 192 | spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); | ||
| 193 | dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); | ||
| 194 | return -EINVAL; | ||
| 195 | } | ||
| 196 | |||
| 197 | urb_frames = urb_size / bytes_per_frame; | ||
| 198 | urb_out->transfer_buffer = | ||
| 199 | line6pcm->buffer_out + | ||
| 200 | index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; | ||
| 201 | urb_out->transfer_buffer_length = urb_size; | ||
| 202 | urb_out->context = line6pcm; | ||
| 203 | |||
| 204 | if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags) && | ||
| 205 | !test_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags)) { | ||
| 206 | struct snd_pcm_runtime *runtime = | ||
| 207 | get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime; | ||
| 208 | |||
| 209 | if (line6pcm->pos_out + urb_frames > runtime->buffer_size) { | ||
| 210 | /* | ||
| 211 | The transferred area goes over buffer boundary, | ||
| 212 | copy the data to the temp buffer. | ||
| 213 | */ | ||
| 214 | int len; | ||
| 215 | |||
| 216 | len = runtime->buffer_size - line6pcm->pos_out; | ||
| 217 | |||
| 218 | if (len > 0) { | ||
| 219 | memcpy(urb_out->transfer_buffer, | ||
| 220 | runtime->dma_area + | ||
| 221 | line6pcm->pos_out * bytes_per_frame, | ||
| 222 | len * bytes_per_frame); | ||
| 223 | memcpy(urb_out->transfer_buffer + | ||
| 224 | len * bytes_per_frame, runtime->dma_area, | ||
| 225 | (urb_frames - len) * bytes_per_frame); | ||
| 226 | } else | ||
| 227 | dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", | ||
| 228 | len); | ||
| 229 | } else { | ||
| 230 | memcpy(urb_out->transfer_buffer, | ||
| 231 | runtime->dma_area + | ||
| 232 | line6pcm->pos_out * bytes_per_frame, | ||
| 233 | urb_out->transfer_buffer_length); | ||
| 234 | } | ||
| 235 | |||
| 236 | line6pcm->pos_out += urb_frames; | ||
| 237 | if (line6pcm->pos_out >= runtime->buffer_size) | ||
| 238 | line6pcm->pos_out -= runtime->buffer_size; | ||
| 239 | } else { | ||
| 240 | memset(urb_out->transfer_buffer, 0, | ||
| 241 | urb_out->transfer_buffer_length); | ||
| 242 | } | ||
| 243 | |||
| 244 | change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame); | ||
| 245 | |||
| 246 | if (line6pcm->prev_fbuf != NULL) { | ||
| 247 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 248 | if (line6pcm->flags & LINE6_BITS_PCM_IMPULSE) { | ||
| 249 | create_impulse_test_signal(line6pcm, urb_out, | ||
| 250 | bytes_per_frame); | ||
| 251 | if (line6pcm->flags & | ||
| 252 | LINE6_BIT_PCM_ALSA_CAPTURE_STREAM) { | ||
| 253 | line6_capture_copy(line6pcm, | ||
| 254 | urb_out->transfer_buffer, | ||
| 255 | urb_out-> | ||
| 256 | transfer_buffer_length); | ||
| 257 | line6_capture_check_period(line6pcm, | ||
| 258 | urb_out->transfer_buffer_length); | ||
| 259 | } | ||
| 260 | } else { | ||
| 261 | #endif | ||
| 262 | if (! | ||
| 263 | (line6pcm->line6-> | ||
| 264 | properties->capabilities & LINE6_CAP_HWMON) | ||
| 265 | && (line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) | ||
| 266 | && (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM)) | ||
| 267 | add_monitor_signal(urb_out, line6pcm->prev_fbuf, | ||
| 268 | line6pcm->volume_monitor, | ||
| 269 | bytes_per_frame); | ||
| 270 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | ||
| 271 | } | ||
| 272 | #endif | ||
| 273 | } | ||
| 274 | |||
| 275 | ret = usb_submit_urb(urb_out, GFP_ATOMIC); | ||
| 276 | |||
| 277 | if (ret == 0) | ||
| 278 | set_bit(index, &line6pcm->active_urb_out); | ||
| 279 | else | ||
| 280 | dev_err(line6pcm->line6->ifcdev, | ||
| 281 | "URB out #%d submission failed (%d)\n", index, ret); | ||
| 282 | |||
| 283 | spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); | ||
| 284 | return 0; | ||
| 285 | } | ||
| 286 | |||
| 287 | /* | ||
| 288 | Submit all currently available playback URBs. | ||
| 289 | */ | ||
| 290 | int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm) | ||
| 291 | { | ||
| 292 | int ret, i; | ||
| 293 | |||
| 294 | for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { | ||
| 295 | ret = submit_audio_out_urb(line6pcm); | ||
| 296 | if (ret < 0) | ||
| 297 | return ret; | ||
| 298 | } | ||
| 299 | |||
| 300 | return 0; | ||
| 301 | } | ||
| 302 | |||
| 303 | /* | ||
| 304 | Unlink all currently active playback URBs. | ||
| 305 | */ | ||
| 306 | void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) | ||
| 307 | { | ||
| 308 | unsigned int i; | ||
| 309 | |||
| 310 | for (i = LINE6_ISO_BUFFERS; i--;) { | ||
| 311 | if (test_bit(i, &line6pcm->active_urb_out)) { | ||
| 312 | if (!test_and_set_bit(i, &line6pcm->unlink_urb_out)) { | ||
| 313 | struct urb *u = line6pcm->urb_audio_out[i]; | ||
| 314 | |||
| 315 | usb_unlink_urb(u); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | /* | ||
| 322 | Wait until unlinking of all currently active playback URBs has been | ||
| 323 | finished. | ||
| 324 | */ | ||
| 325 | void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) | ||
| 326 | { | ||
| 327 | int timeout = HZ; | ||
| 328 | unsigned int i; | ||
| 329 | int alive; | ||
| 330 | |||
| 331 | do { | ||
| 332 | alive = 0; | ||
| 333 | for (i = LINE6_ISO_BUFFERS; i--;) { | ||
| 334 | if (test_bit(i, &line6pcm->active_urb_out)) | ||
| 335 | alive++; | ||
| 336 | } | ||
| 337 | if (!alive) | ||
| 338 | break; | ||
| 339 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
| 340 | schedule_timeout(1); | ||
| 341 | } while (--timeout > 0); | ||
| 342 | if (alive) | ||
| 343 | snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); | ||
| 344 | } | ||
| 345 | |||
| 346 | /* | ||
| 347 | Unlink all currently active playback URBs, and wait for finishing. | ||
| 348 | */ | ||
| 349 | void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) | ||
| 350 | { | ||
| 351 | line6_unlink_audio_out_urbs(line6pcm); | ||
| 352 | line6_wait_clear_audio_out_urbs(line6pcm); | ||
| 353 | } | ||
| 354 | |||
| 355 | void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm) | ||
| 356 | { | ||
| 357 | kfree(line6pcm->buffer_out); | ||
| 358 | line6pcm->buffer_out = NULL; | ||
| 359 | } | ||
| 360 | |||
| 361 | /* | ||
| 362 | Callback for completed playback URB. | ||
| 363 | */ | ||
| 364 | static void audio_out_callback(struct urb *urb) | ||
| 365 | { | ||
| 366 | int i, index, length = 0, shutdown = 0; | ||
| 367 | unsigned long flags; | ||
| 368 | struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; | ||
| 369 | struct snd_pcm_substream *substream = | ||
| 370 | get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK); | ||
| 371 | |||
| 372 | #if USE_CLEAR_BUFFER_WORKAROUND | ||
| 373 | memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); | ||
| 374 | #endif | ||
| 375 | |||
| 376 | line6pcm->last_frame_out = urb->start_frame; | ||
| 377 | |||
| 378 | /* find index of URB */ | ||
| 379 | for (index = LINE6_ISO_BUFFERS; index--;) | ||
| 380 | if (urb == line6pcm->urb_audio_out[index]) | ||
| 381 | break; | ||
| 382 | |||
| 383 | if (index < 0) | ||
| 384 | return; /* URB has been unlinked asynchronously */ | ||
| 385 | |||
| 386 | for (i = LINE6_ISO_PACKETS; i--;) | ||
| 387 | length += urb->iso_frame_desc[i].length; | ||
| 388 | |||
| 389 | spin_lock_irqsave(&line6pcm->lock_audio_out, flags); | ||
| 390 | |||
| 391 | if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags)) { | ||
| 392 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 393 | |||
| 394 | line6pcm->pos_out_done += | ||
| 395 | length / line6pcm->properties->bytes_per_frame; | ||
| 396 | |||
| 397 | if (line6pcm->pos_out_done >= runtime->buffer_size) | ||
| 398 | line6pcm->pos_out_done -= runtime->buffer_size; | ||
| 399 | } | ||
| 400 | |||
| 401 | clear_bit(index, &line6pcm->active_urb_out); | ||
| 402 | |||
| 403 | for (i = LINE6_ISO_PACKETS; i--;) | ||
| 404 | if (urb->iso_frame_desc[i].status == -EXDEV) { | ||
| 405 | shutdown = 1; | ||
| 406 | break; | ||
| 407 | } | ||
| 408 | |||
| 409 | if (test_and_clear_bit(index, &line6pcm->unlink_urb_out)) | ||
| 410 | shutdown = 1; | ||
| 411 | |||
| 412 | spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); | ||
| 413 | |||
| 414 | if (!shutdown) { | ||
| 415 | submit_audio_out_urb(line6pcm); | ||
| 416 | |||
| 417 | if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, | ||
| 418 | &line6pcm->flags)) { | ||
| 419 | line6pcm->bytes_out += length; | ||
| 420 | if (line6pcm->bytes_out >= line6pcm->period_out) { | ||
| 421 | line6pcm->bytes_out %= line6pcm->period_out; | ||
| 422 | snd_pcm_period_elapsed(substream); | ||
| 423 | } | ||
| 424 | } | ||
| 425 | } | ||
| 426 | } | ||
| 427 | |||
| 428 | /* open playback callback */ | ||
| 429 | static int snd_line6_playback_open(struct snd_pcm_substream *substream) | ||
| 430 | { | ||
| 431 | int err; | ||
| 432 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 433 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
| 434 | |||
| 435 | err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
| 436 | (&line6pcm-> | ||
| 437 | properties->snd_line6_rates)); | ||
| 438 | if (err < 0) | ||
| 439 | return err; | ||
| 440 | |||
| 441 | runtime->hw = line6pcm->properties->snd_line6_playback_hw; | ||
| 442 | return 0; | ||
| 443 | } | ||
| 444 | |||
| 445 | /* close playback callback */ | ||
| 446 | static int snd_line6_playback_close(struct snd_pcm_substream *substream) | ||
| 447 | { | ||
| 448 | return 0; | ||
| 449 | } | ||
| 450 | |||
| 451 | /* hw_params playback callback */ | ||
| 452 | static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, | ||
| 453 | struct snd_pcm_hw_params *hw_params) | ||
| 454 | { | ||
| 455 | int ret; | ||
| 456 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
| 457 | |||
| 458 | /* -- Florian Demski [FD] */ | ||
| 459 | /* don't ask me why, but this fixes the bug on my machine */ | ||
| 460 | if (line6pcm == NULL) { | ||
| 461 | if (substream->pcm == NULL) | ||
| 462 | return -ENOMEM; | ||
| 463 | if (substream->pcm->private_data == NULL) | ||
| 464 | return -ENOMEM; | ||
| 465 | substream->private_data = substream->pcm->private_data; | ||
| 466 | line6pcm = snd_pcm_substream_chip(substream); | ||
| 467 | } | ||
| 468 | /* -- [FD] end */ | ||
| 469 | |||
| 470 | ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); | ||
| 471 | |||
| 472 | if (ret < 0) | ||
| 473 | return ret; | ||
| 474 | |||
| 475 | ret = snd_pcm_lib_malloc_pages(substream, | ||
| 476 | params_buffer_bytes(hw_params)); | ||
| 477 | if (ret < 0) { | ||
| 478 | line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); | ||
| 479 | return ret; | ||
| 480 | } | ||
| 481 | |||
| 482 | line6pcm->period_out = params_period_bytes(hw_params); | ||
| 483 | return 0; | ||
| 484 | } | ||
| 485 | |||
| 486 | /* hw_free playback callback */ | ||
| 487 | static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) | ||
| 488 | { | ||
| 489 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
| 490 | |||
| 491 | line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); | ||
| 492 | return snd_pcm_lib_free_pages(substream); | ||
| 493 | } | ||
| 494 | |||
| 495 | /* trigger playback callback */ | ||
| 496 | int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd) | ||
| 497 | { | ||
| 498 | int err; | ||
| 499 | |||
| 500 | switch (cmd) { | ||
| 501 | case SNDRV_PCM_TRIGGER_START: | ||
| 502 | #ifdef CONFIG_PM | ||
| 503 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 504 | #endif | ||
| 505 | err = line6_pcm_acquire(line6pcm, | ||
| 506 | LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); | ||
| 507 | |||
| 508 | if (err < 0) | ||
| 509 | return err; | ||
| 510 | |||
| 511 | break; | ||
| 512 | |||
| 513 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 514 | #ifdef CONFIG_PM | ||
| 515 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 516 | #endif | ||
| 517 | err = line6_pcm_release(line6pcm, | ||
| 518 | LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); | ||
| 519 | |||
| 520 | if (err < 0) | ||
| 521 | return err; | ||
| 522 | |||
| 523 | break; | ||
| 524 | |||
| 525 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 526 | set_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); | ||
| 527 | break; | ||
| 528 | |||
| 529 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 530 | clear_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); | ||
| 531 | break; | ||
| 532 | |||
| 533 | default: | ||
| 534 | return -EINVAL; | ||
| 535 | } | ||
| 536 | |||
| 537 | return 0; | ||
| 538 | } | ||
| 539 | |||
| 540 | /* playback pointer callback */ | ||
| 541 | static snd_pcm_uframes_t | ||
| 542 | snd_line6_playback_pointer(struct snd_pcm_substream *substream) | ||
| 543 | { | ||
| 544 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
| 545 | |||
| 546 | return line6pcm->pos_out_done; | ||
| 547 | } | ||
| 548 | |||
| 549 | /* playback operators */ | ||
| 550 | struct snd_pcm_ops snd_line6_playback_ops = { | ||
| 551 | .open = snd_line6_playback_open, | ||
| 552 | .close = snd_line6_playback_close, | ||
| 553 | .ioctl = snd_pcm_lib_ioctl, | ||
| 554 | .hw_params = snd_line6_playback_hw_params, | ||
| 555 | .hw_free = snd_line6_playback_hw_free, | ||
| 556 | .prepare = snd_line6_prepare, | ||
| 557 | .trigger = snd_line6_trigger, | ||
| 558 | .pointer = snd_line6_playback_pointer, | ||
| 559 | }; | ||
| 560 | |||
| 561 | int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) | ||
| 562 | { | ||
| 563 | struct usb_line6 *line6 = line6pcm->line6; | ||
| 564 | int i; | ||
| 565 | |||
| 566 | /* create audio URBs and fill in constant values: */ | ||
| 567 | for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { | ||
| 568 | struct urb *urb; | ||
| 569 | |||
| 570 | /* URB for audio out: */ | ||
| 571 | urb = line6pcm->urb_audio_out[i] = | ||
| 572 | usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); | ||
| 573 | |||
| 574 | if (urb == NULL) { | ||
| 575 | dev_err(line6->ifcdev, "Out of memory\n"); | ||
| 576 | return -ENOMEM; | ||
| 577 | } | ||
| 578 | |||
| 579 | urb->dev = line6->usbdev; | ||
| 580 | urb->pipe = | ||
| 581 | usb_sndisocpipe(line6->usbdev, | ||
| 582 | line6->properties->ep_audio_w & | ||
| 583 | USB_ENDPOINT_NUMBER_MASK); | ||
| 584 | urb->transfer_flags = URB_ISO_ASAP; | ||
| 585 | urb->start_frame = -1; | ||
| 586 | urb->number_of_packets = LINE6_ISO_PACKETS; | ||
| 587 | urb->interval = LINE6_ISO_INTERVAL; | ||
| 588 | urb->error_count = 0; | ||
| 589 | urb->complete = audio_out_callback; | ||
| 590 | } | ||
| 591 | |||
| 592 | return 0; | ||
| 593 | } | ||
diff --git a/sound/usb/line6/playback.h b/sound/usb/line6/playback.h new file mode 100644 index 000000000000..743bd6f74c57 --- /dev/null +++ b/sound/usb/line6/playback.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 | #include <sound/pcm.h> | ||
| 16 | |||
| 17 | #include "driver.h" | ||
| 18 | |||
| 19 | /* | ||
| 20 | * When the TonePort is used with jack in full duplex mode and the outputs are | ||
| 21 | * not connected, the software monitor produces an ugly noise since everything | ||
| 22 | * written to the output buffer (i.e., the input signal) will be repeated in | ||
| 23 | * the next period (sounds like a delay effect). As a workaround, the output | ||
| 24 | * buffer is cleared after the data have been read, but there must be a better | ||
| 25 | * solution. Until one is found, this workaround can be used to fix the | ||
| 26 | * problem. | ||
| 27 | */ | ||
| 28 | #define USE_CLEAR_BUFFER_WORKAROUND 1 | ||
| 29 | |||
| 30 | extern struct snd_pcm_ops snd_line6_playback_ops; | ||
| 31 | |||
| 32 | extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm); | ||
| 33 | extern void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm); | ||
| 34 | extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm); | ||
| 35 | extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm); | ||
| 36 | extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm | ||
| 37 | *line6pcm); | ||
| 38 | extern void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm); | ||
| 39 | extern int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd); | ||
| 40 | |||
| 41 | #endif | ||
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c new file mode 100644 index 000000000000..85a43631996e --- /dev/null +++ b/sound/usb/line6/pod.c | |||
| @@ -0,0 +1,453 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 <linux/slab.h> | ||
| 13 | #include <linux/wait.h> | ||
| 14 | #include <sound/control.h> | ||
| 15 | |||
| 16 | #include "audio.h" | ||
| 17 | #include "capture.h" | ||
| 18 | #include "driver.h" | ||
| 19 | #include "playback.h" | ||
| 20 | #include "pod.h" | ||
| 21 | |||
| 22 | #define POD_SYSEX_CODE 3 | ||
| 23 | #define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ | ||
| 24 | |||
| 25 | /* *INDENT-OFF* */ | ||
| 26 | |||
| 27 | enum { | ||
| 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 | ||
| 37 | |||
| 38 | /* dumps entire internal memory of PODxt Pro */ | ||
| 39 | /* POD_SYSEX_DUMPMEM2 = 0x76 */ | ||
| 40 | }; | ||
| 41 | |||
| 42 | enum { | ||
| 43 | POD_MONITOR_LEVEL = 0x04, | ||
| 44 | POD_SYSTEM_INVALID = 0x10000 | ||
| 45 | }; | ||
| 46 | |||
| 47 | /* *INDENT-ON* */ | ||
| 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 | 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 = { | ||
| 69 | .snd_line6_playback_hw = { | ||
| 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 | | ||
| 75 | #ifdef CONFIG_PM | ||
| 76 | SNDRV_PCM_INFO_RESUME | | ||
| 77 | #endif | ||
| 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}, | ||
| 90 | .snd_line6_capture_hw = { | ||
| 91 | .info = (SNDRV_PCM_INFO_MMAP | | ||
| 92 | SNDRV_PCM_INFO_INTERLEAVED | | ||
| 93 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
| 94 | SNDRV_PCM_INFO_MMAP_VALID | | ||
| 95 | #ifdef CONFIG_PM | ||
| 96 | SNDRV_PCM_INFO_RESUME | | ||
| 97 | #endif | ||
| 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}, | ||
| 110 | .snd_line6_rates = { | ||
| 111 | .nrats = 1, | ||
| 112 | .rats = &pod_ratden}, | ||
| 113 | .bytes_per_frame = POD_BYTES_PER_FRAME | ||
| 114 | }; | ||
| 115 | |||
| 116 | static const char pod_version_header[] = { | ||
| 117 | 0xf2, 0x7e, 0x7f, 0x06, 0x02 | ||
| 118 | }; | ||
| 119 | |||
| 120 | /* forward declarations: */ | ||
| 121 | static void pod_startup2(unsigned long data); | ||
| 122 | static void pod_startup3(struct usb_line6_pod *pod); | ||
| 123 | |||
| 124 | static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, | ||
| 125 | int size) | ||
| 126 | { | ||
| 127 | return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, | ||
| 128 | size); | ||
| 129 | } | ||
| 130 | |||
| 131 | /* | ||
| 132 | Process a completely received message. | ||
| 133 | */ | ||
| 134 | static void line6_pod_process_message(struct usb_line6 *line6) | ||
| 135 | { | ||
| 136 | struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; | ||
| 137 | const unsigned char *buf = pod->line6.buffer_message; | ||
| 138 | |||
| 139 | if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { | ||
| 140 | pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; | ||
| 141 | pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | | ||
| 142 | (int) buf[10]; | ||
| 143 | pod_startup3(pod); | ||
| 144 | return; | ||
| 145 | } | ||
| 146 | |||
| 147 | /* Only look for sysex messages from this device */ | ||
| 148 | if (buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE) && | ||
| 149 | buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN)) { | ||
| 150 | return; | ||
| 151 | } | ||
| 152 | if (memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) != 0) | ||
| 153 | return; | ||
| 154 | |||
| 155 | if (buf[5] == POD_SYSEX_SYSTEM && buf[6] == POD_MONITOR_LEVEL) { | ||
| 156 | short value = ((int)buf[7] << 12) | ((int)buf[8] << 8) | | ||
| 157 | ((int)buf[9] << 4) | (int)buf[10]; | ||
| 158 | pod->monitor_level = value; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | /* | ||
| 163 | Send system parameter (from integer). | ||
| 164 | */ | ||
| 165 | static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, | ||
| 166 | int code) | ||
| 167 | { | ||
| 168 | char *sysex; | ||
| 169 | static const int size = 5; | ||
| 170 | |||
| 171 | sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); | ||
| 172 | if (!sysex) | ||
| 173 | return -ENOMEM; | ||
| 174 | sysex[SYSEX_DATA_OFS] = code; | ||
| 175 | sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; | ||
| 176 | sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; | ||
| 177 | sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f; | ||
| 178 | sysex[SYSEX_DATA_OFS + 4] = (value) & 0x0f; | ||
| 179 | line6_send_sysex_message(&pod->line6, sysex, size); | ||
| 180 | kfree(sysex); | ||
| 181 | return 0; | ||
| 182 | } | ||
| 183 | |||
| 184 | /* | ||
| 185 | "read" request on "serial_number" special file. | ||
| 186 | */ | ||
| 187 | static ssize_t serial_number_show(struct device *dev, | ||
| 188 | struct device_attribute *attr, char *buf) | ||
| 189 | { | ||
| 190 | struct usb_interface *interface = to_usb_interface(dev); | ||
| 191 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
| 192 | |||
| 193 | return sprintf(buf, "%d\n", pod->serial_number); | ||
| 194 | } | ||
| 195 | |||
| 196 | /* | ||
| 197 | "read" request on "firmware_version" special file. | ||
| 198 | */ | ||
| 199 | static ssize_t firmware_version_show(struct device *dev, | ||
| 200 | struct device_attribute *attr, char *buf) | ||
| 201 | { | ||
| 202 | struct usb_interface *interface = to_usb_interface(dev); | ||
| 203 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
| 204 | |||
| 205 | return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100, | ||
| 206 | pod->firmware_version % 100); | ||
| 207 | } | ||
| 208 | |||
| 209 | /* | ||
| 210 | "read" request on "device_id" special file. | ||
| 211 | */ | ||
| 212 | static ssize_t device_id_show(struct device *dev, | ||
| 213 | struct device_attribute *attr, char *buf) | ||
| 214 | { | ||
| 215 | struct usb_interface *interface = to_usb_interface(dev); | ||
| 216 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
| 217 | |||
| 218 | return sprintf(buf, "%d\n", pod->device_id); | ||
| 219 | } | ||
| 220 | |||
| 221 | /* | ||
| 222 | POD startup procedure. | ||
| 223 | This is a sequence of functions with special requirements (e.g., must | ||
| 224 | not run immediately after initialization, must not run in interrupt | ||
| 225 | context). After the last one has finished, the device is ready to use. | ||
| 226 | */ | ||
| 227 | |||
| 228 | static void pod_startup1(struct usb_line6_pod *pod) | ||
| 229 | { | ||
| 230 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT); | ||
| 231 | |||
| 232 | /* delay startup procedure: */ | ||
| 233 | line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, | ||
| 234 | (unsigned long)pod); | ||
| 235 | } | ||
| 236 | |||
| 237 | static void pod_startup2(unsigned long data) | ||
| 238 | { | ||
| 239 | struct usb_line6_pod *pod = (struct usb_line6_pod *)data; | ||
| 240 | struct usb_line6 *line6 = &pod->line6; | ||
| 241 | |||
| 242 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ); | ||
| 243 | |||
| 244 | /* request firmware version: */ | ||
| 245 | line6_version_request_async(line6); | ||
| 246 | } | ||
| 247 | |||
| 248 | static void pod_startup3(struct usb_line6_pod *pod) | ||
| 249 | { | ||
| 250 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_WORKQUEUE); | ||
| 251 | |||
| 252 | /* schedule work for global work queue: */ | ||
| 253 | schedule_work(&pod->startup_work); | ||
| 254 | } | ||
| 255 | |||
| 256 | static void pod_startup4(struct work_struct *work) | ||
| 257 | { | ||
| 258 | struct usb_line6_pod *pod = | ||
| 259 | container_of(work, struct usb_line6_pod, startup_work); | ||
| 260 | struct usb_line6 *line6 = &pod->line6; | ||
| 261 | |||
| 262 | CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_SETUP); | ||
| 263 | |||
| 264 | /* serial number: */ | ||
| 265 | line6_read_serial_number(&pod->line6, &pod->serial_number); | ||
| 266 | |||
| 267 | /* ALSA audio interface: */ | ||
| 268 | line6_register_audio(line6); | ||
| 269 | } | ||
| 270 | |||
| 271 | /* POD special files: */ | ||
| 272 | static DEVICE_ATTR_RO(device_id); | ||
| 273 | static DEVICE_ATTR_RO(firmware_version); | ||
| 274 | static DEVICE_ATTR_RO(serial_number); | ||
| 275 | |||
| 276 | /* control info callback */ | ||
| 277 | static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol, | ||
| 278 | struct snd_ctl_elem_info *uinfo) | ||
| 279 | { | ||
| 280 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
| 281 | uinfo->count = 1; | ||
| 282 | uinfo->value.integer.min = 0; | ||
| 283 | uinfo->value.integer.max = 65535; | ||
| 284 | return 0; | ||
| 285 | } | ||
| 286 | |||
| 287 | /* control get callback */ | ||
| 288 | static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol, | ||
| 289 | struct snd_ctl_elem_value *ucontrol) | ||
| 290 | { | ||
| 291 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
| 292 | struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; | ||
| 293 | |||
| 294 | ucontrol->value.integer.value[0] = pod->monitor_level; | ||
| 295 | return 0; | ||
| 296 | } | ||
| 297 | |||
| 298 | /* control put callback */ | ||
| 299 | static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol, | ||
| 300 | struct snd_ctl_elem_value *ucontrol) | ||
| 301 | { | ||
| 302 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
| 303 | struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; | ||
| 304 | |||
| 305 | if (ucontrol->value.integer.value[0] == pod->monitor_level) | ||
| 306 | return 0; | ||
| 307 | |||
| 308 | pod->monitor_level = ucontrol->value.integer.value[0]; | ||
| 309 | pod_set_system_param_int(pod, ucontrol->value.integer.value[0], | ||
| 310 | POD_MONITOR_LEVEL); | ||
| 311 | return 1; | ||
| 312 | } | ||
| 313 | |||
| 314 | /* control definition */ | ||
| 315 | static struct snd_kcontrol_new pod_control_monitor = { | ||
| 316 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 317 | .name = "Monitor Playback Volume", | ||
| 318 | .index = 0, | ||
| 319 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 320 | .info = snd_pod_control_monitor_info, | ||
| 321 | .get = snd_pod_control_monitor_get, | ||
| 322 | .put = snd_pod_control_monitor_put | ||
| 323 | }; | ||
| 324 | |||
| 325 | /* | ||
| 326 | POD destructor. | ||
| 327 | */ | ||
| 328 | static void pod_destruct(struct usb_interface *interface) | ||
| 329 | { | ||
| 330 | struct usb_line6_pod *pod = usb_get_intfdata(interface); | ||
| 331 | |||
| 332 | if (pod == NULL) | ||
| 333 | return; | ||
| 334 | line6_cleanup_audio(&pod->line6); | ||
| 335 | |||
| 336 | del_timer(&pod->startup_timer); | ||
| 337 | cancel_work_sync(&pod->startup_work); | ||
| 338 | } | ||
| 339 | |||
| 340 | /* | ||
| 341 | POD device disconnected. | ||
| 342 | */ | ||
| 343 | static void line6_pod_disconnect(struct usb_interface *interface) | ||
| 344 | { | ||
| 345 | struct usb_line6_pod *pod; | ||
| 346 | |||
| 347 | if (interface == NULL) | ||
| 348 | return; | ||
| 349 | pod = usb_get_intfdata(interface); | ||
| 350 | |||
| 351 | if (pod != NULL) { | ||
| 352 | struct snd_line6_pcm *line6pcm = pod->line6.line6pcm; | ||
| 353 | struct device *dev = &interface->dev; | ||
| 354 | |||
| 355 | if (line6pcm != NULL) | ||
| 356 | line6_pcm_disconnect(line6pcm); | ||
| 357 | |||
| 358 | if (dev != NULL) { | ||
| 359 | /* remove sysfs entries: */ | ||
| 360 | device_remove_file(dev, &dev_attr_device_id); | ||
| 361 | device_remove_file(dev, &dev_attr_firmware_version); | ||
| 362 | device_remove_file(dev, &dev_attr_serial_number); | ||
| 363 | } | ||
| 364 | } | ||
| 365 | |||
| 366 | pod_destruct(interface); | ||
| 367 | } | ||
| 368 | |||
| 369 | /* | ||
| 370 | Create sysfs entries. | ||
| 371 | */ | ||
| 372 | static int pod_create_files2(struct device *dev) | ||
| 373 | { | ||
| 374 | int err; | ||
| 375 | |||
| 376 | CHECK_RETURN(device_create_file(dev, &dev_attr_device_id)); | ||
| 377 | CHECK_RETURN(device_create_file(dev, &dev_attr_firmware_version)); | ||
| 378 | CHECK_RETURN(device_create_file(dev, &dev_attr_serial_number)); | ||
| 379 | return 0; | ||
| 380 | } | ||
| 381 | |||
| 382 | /* | ||
| 383 | Try to init POD device. | ||
| 384 | */ | ||
| 385 | static int pod_try_init(struct usb_interface *interface, | ||
| 386 | struct usb_line6 *line6) | ||
| 387 | { | ||
| 388 | int err; | ||
| 389 | struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; | ||
| 390 | |||
| 391 | line6->process_message = line6_pod_process_message; | ||
| 392 | line6->disconnect = line6_pod_disconnect; | ||
| 393 | |||
| 394 | init_timer(&pod->startup_timer); | ||
| 395 | INIT_WORK(&pod->startup_work, pod_startup4); | ||
| 396 | |||
| 397 | if ((interface == NULL) || (pod == NULL)) | ||
| 398 | return -ENODEV; | ||
| 399 | |||
| 400 | /* create sysfs entries: */ | ||
| 401 | err = pod_create_files2(&interface->dev); | ||
| 402 | if (err < 0) | ||
| 403 | return err; | ||
| 404 | |||
| 405 | /* initialize audio system: */ | ||
| 406 | err = line6_init_audio(line6); | ||
| 407 | if (err < 0) | ||
| 408 | return err; | ||
| 409 | |||
| 410 | /* initialize MIDI subsystem: */ | ||
| 411 | err = line6_init_midi(line6); | ||
| 412 | if (err < 0) | ||
| 413 | return err; | ||
| 414 | |||
| 415 | /* initialize PCM subsystem: */ | ||
| 416 | err = line6_init_pcm(line6, &pod_pcm_properties); | ||
| 417 | if (err < 0) | ||
| 418 | return err; | ||
| 419 | |||
| 420 | /* register monitor control: */ | ||
| 421 | err = snd_ctl_add(line6->card, | ||
| 422 | snd_ctl_new1(&pod_control_monitor, line6->line6pcm)); | ||
| 423 | if (err < 0) | ||
| 424 | return err; | ||
| 425 | |||
| 426 | /* | ||
| 427 | When the sound card is registered at this point, the PODxt Live | ||
| 428 | displays "Invalid Code Error 07", so we do it later in the event | ||
| 429 | handler. | ||
| 430 | */ | ||
| 431 | |||
| 432 | if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { | ||
| 433 | pod->monitor_level = POD_SYSTEM_INVALID; | ||
| 434 | |||
| 435 | /* initiate startup procedure: */ | ||
| 436 | pod_startup1(pod); | ||
| 437 | } | ||
| 438 | |||
| 439 | return 0; | ||
| 440 | } | ||
| 441 | |||
| 442 | /* | ||
| 443 | Init POD device (and clean up in case of failure). | ||
| 444 | */ | ||
| 445 | int line6_pod_init(struct usb_interface *interface, struct usb_line6 *line6) | ||
| 446 | { | ||
| 447 | int err = pod_try_init(interface, line6); | ||
| 448 | |||
| 449 | if (err < 0) | ||
| 450 | pod_destruct(interface); | ||
| 451 | |||
| 452 | return err; | ||
| 453 | } | ||
diff --git a/sound/usb/line6/pod.h b/sound/usb/line6/pod.h new file mode 100644 index 000000000000..87a8f0fa9cba --- /dev/null +++ b/sound/usb/line6/pod.h | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 | #include <linux/interrupt.h> | ||
| 16 | #include <linux/spinlock.h> | ||
| 17 | #include <linux/usb.h> | ||
| 18 | |||
| 19 | #include <sound/core.h> | ||
| 20 | |||
| 21 | #include "driver.h" | ||
| 22 | |||
| 23 | /* | ||
| 24 | Locate name in binary program dump | ||
| 25 | */ | ||
| 26 | #define POD_NAME_OFFSET 0 | ||
| 27 | #define POD_NAME_LENGTH 16 | ||
| 28 | |||
| 29 | /* | ||
| 30 | Other constants | ||
| 31 | */ | ||
| 32 | #define POD_CONTROL_SIZE 0x80 | ||
| 33 | #define POD_BUFSIZE_DUMPREQ 7 | ||
| 34 | #define POD_STARTUP_DELAY 1000 | ||
| 35 | |||
| 36 | /* | ||
| 37 | Stages of POD startup procedure | ||
| 38 | */ | ||
| 39 | enum { | ||
| 40 | POD_STARTUP_INIT = 1, | ||
| 41 | POD_STARTUP_VERSIONREQ, | ||
| 42 | POD_STARTUP_WORKQUEUE, | ||
| 43 | POD_STARTUP_SETUP, | ||
| 44 | POD_STARTUP_LAST = POD_STARTUP_SETUP - 1 | ||
| 45 | }; | ||
| 46 | |||
| 47 | struct usb_line6_pod { | ||
| 48 | /** | ||
| 49 | Generic Line6 USB data. | ||
| 50 | */ | ||
| 51 | struct usb_line6 line6; | ||
| 52 | |||
| 53 | /** | ||
| 54 | Instrument monitor level. | ||
| 55 | */ | ||
| 56 | int monitor_level; | ||
| 57 | |||
| 58 | /** | ||
| 59 | Timer for device initializaton. | ||
| 60 | */ | ||
| 61 | struct timer_list startup_timer; | ||
| 62 | |||
| 63 | /** | ||
| 64 | Work handler for device initializaton. | ||
| 65 | */ | ||
| 66 | struct work_struct startup_work; | ||
| 67 | |||
| 68 | /** | ||
| 69 | Current progress in startup procedure. | ||
| 70 | */ | ||
| 71 | int startup_progress; | ||
| 72 | |||
| 73 | /** | ||
| 74 | Serial number of device. | ||
| 75 | */ | ||
| 76 | int serial_number; | ||
| 77 | |||
| 78 | /** | ||
| 79 | Firmware version (x 100). | ||
| 80 | */ | ||
| 81 | int firmware_version; | ||
| 82 | |||
| 83 | /** | ||
| 84 | Device ID. | ||
| 85 | */ | ||
| 86 | int device_id; | ||
| 87 | }; | ||
| 88 | |||
| 89 | extern int line6_pod_init(struct usb_interface *interface, | ||
| 90 | struct usb_line6 *line6); | ||
| 91 | |||
| 92 | #endif | ||
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c new file mode 100644 index 000000000000..27c5402cece8 --- /dev/null +++ b/sound/usb/line6/podhd.c | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Pod HD | ||
| 3 | * | ||
| 4 | * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com> | ||
| 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 <sound/core.h> | ||
| 13 | #include <sound/pcm.h> | ||
| 14 | |||
| 15 | #include "audio.h" | ||
| 16 | #include "driver.h" | ||
| 17 | #include "pcm.h" | ||
| 18 | #include "podhd.h" | ||
| 19 | |||
| 20 | #define PODHD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ | ||
| 21 | |||
| 22 | static struct snd_ratden podhd_ratden = { | ||
| 23 | .num_min = 48000, | ||
| 24 | .num_max = 48000, | ||
| 25 | .num_step = 1, | ||
| 26 | .den = 1, | ||
| 27 | }; | ||
| 28 | |||
| 29 | static struct line6_pcm_properties podhd_pcm_properties = { | ||
| 30 | .snd_line6_playback_hw = { | ||
| 31 | .info = (SNDRV_PCM_INFO_MMAP | | ||
| 32 | SNDRV_PCM_INFO_INTERLEAVED | | ||
| 33 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
| 34 | SNDRV_PCM_INFO_MMAP_VALID | | ||
| 35 | SNDRV_PCM_INFO_PAUSE | | ||
| 36 | #ifdef CONFIG_PM | ||
| 37 | SNDRV_PCM_INFO_RESUME | | ||
| 38 | #endif | ||
| 39 | SNDRV_PCM_INFO_SYNC_START), | ||
| 40 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | ||
| 41 | .rates = SNDRV_PCM_RATE_48000, | ||
| 42 | .rate_min = 48000, | ||
| 43 | .rate_max = 48000, | ||
| 44 | .channels_min = 2, | ||
| 45 | .channels_max = 2, | ||
| 46 | .buffer_bytes_max = 60000, | ||
| 47 | .period_bytes_min = 64, | ||
| 48 | .period_bytes_max = 8192, | ||
| 49 | .periods_min = 1, | ||
| 50 | .periods_max = 1024}, | ||
| 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 | #ifdef CONFIG_PM | ||
| 57 | SNDRV_PCM_INFO_RESUME | | ||
| 58 | #endif | ||
| 59 | SNDRV_PCM_INFO_SYNC_START), | ||
| 60 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | ||
| 61 | .rates = SNDRV_PCM_RATE_48000, | ||
| 62 | .rate_min = 48000, | ||
| 63 | .rate_max = 48000, | ||
| 64 | .channels_min = 2, | ||
| 65 | .channels_max = 2, | ||
| 66 | .buffer_bytes_max = 60000, | ||
| 67 | .period_bytes_min = 64, | ||
| 68 | .period_bytes_max = 8192, | ||
| 69 | .periods_min = 1, | ||
| 70 | .periods_max = 1024}, | ||
| 71 | .snd_line6_rates = { | ||
| 72 | .nrats = 1, | ||
| 73 | .rats = &podhd_ratden}, | ||
| 74 | .bytes_per_frame = PODHD_BYTES_PER_FRAME | ||
| 75 | }; | ||
| 76 | |||
| 77 | /* | ||
| 78 | POD HD destructor. | ||
| 79 | */ | ||
| 80 | static void podhd_destruct(struct usb_interface *interface) | ||
| 81 | { | ||
| 82 | struct usb_line6_podhd *podhd = usb_get_intfdata(interface); | ||
| 83 | |||
| 84 | if (podhd == NULL) | ||
| 85 | return; | ||
| 86 | line6_cleanup_audio(&podhd->line6); | ||
| 87 | } | ||
| 88 | |||
| 89 | /* | ||
| 90 | POD HD device disconnected. | ||
| 91 | */ | ||
| 92 | static void line6_podhd_disconnect(struct usb_interface *interface) | ||
| 93 | { | ||
| 94 | struct usb_line6_podhd *podhd; | ||
| 95 | |||
| 96 | if (interface == NULL) | ||
| 97 | return; | ||
| 98 | podhd = usb_get_intfdata(interface); | ||
| 99 | |||
| 100 | if (podhd != NULL) { | ||
| 101 | struct snd_line6_pcm *line6pcm = podhd->line6.line6pcm; | ||
| 102 | |||
| 103 | if (line6pcm != NULL) | ||
| 104 | line6_pcm_disconnect(line6pcm); | ||
| 105 | } | ||
| 106 | |||
| 107 | podhd_destruct(interface); | ||
| 108 | } | ||
| 109 | |||
| 110 | /* | ||
| 111 | Try to init POD HD device. | ||
| 112 | */ | ||
| 113 | static int podhd_try_init(struct usb_interface *interface, | ||
| 114 | struct usb_line6_podhd *podhd) | ||
| 115 | { | ||
| 116 | int err; | ||
| 117 | struct usb_line6 *line6 = &podhd->line6; | ||
| 118 | |||
| 119 | if ((interface == NULL) || (podhd == NULL)) | ||
| 120 | return -ENODEV; | ||
| 121 | |||
| 122 | line6->disconnect = line6_podhd_disconnect; | ||
| 123 | |||
| 124 | /* initialize audio system: */ | ||
| 125 | err = line6_init_audio(line6); | ||
| 126 | if (err < 0) | ||
| 127 | return err; | ||
| 128 | |||
| 129 | /* initialize MIDI subsystem: */ | ||
| 130 | err = line6_init_midi(line6); | ||
| 131 | if (err < 0) | ||
| 132 | return err; | ||
| 133 | |||
| 134 | /* initialize PCM subsystem: */ | ||
| 135 | err = line6_init_pcm(line6, &podhd_pcm_properties); | ||
| 136 | if (err < 0) | ||
| 137 | return err; | ||
| 138 | |||
| 139 | /* register USB audio system: */ | ||
| 140 | err = line6_register_audio(line6); | ||
| 141 | return err; | ||
| 142 | } | ||
| 143 | |||
| 144 | /* | ||
| 145 | Init POD HD device (and clean up in case of failure). | ||
| 146 | */ | ||
| 147 | int line6_podhd_init(struct usb_interface *interface, struct usb_line6 *line6) | ||
| 148 | { | ||
| 149 | struct usb_line6_podhd *podhd = (struct usb_line6_podhd *) line6; | ||
| 150 | int err = podhd_try_init(interface, podhd); | ||
| 151 | |||
| 152 | if (err < 0) | ||
| 153 | podhd_destruct(interface); | ||
| 154 | |||
| 155 | return err; | ||
| 156 | } | ||
diff --git a/sound/usb/line6/podhd.h b/sound/usb/line6/podhd.h new file mode 100644 index 000000000000..a14f711f9725 --- /dev/null +++ b/sound/usb/line6/podhd.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Pod HD | ||
| 3 | * | ||
| 4 | * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com> | ||
| 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 PODHD_H | ||
| 13 | #define PODHD_H | ||
| 14 | |||
| 15 | #include <linux/usb.h> | ||
| 16 | |||
| 17 | #include "driver.h" | ||
| 18 | |||
| 19 | struct usb_line6_podhd { | ||
| 20 | /** | ||
| 21 | Generic Line6 USB data. | ||
| 22 | */ | ||
| 23 | struct usb_line6 line6; | ||
| 24 | }; | ||
| 25 | |||
| 26 | extern int line6_podhd_init(struct usb_interface *interface, | ||
| 27 | struct usb_line6 *line6); | ||
| 28 | |||
| 29 | #endif /* PODHD_H */ | ||
diff --git a/sound/usb/line6/revision.h b/sound/usb/line6/revision.h new file mode 100644 index 000000000000..b4eee2b73831 --- /dev/null +++ b/sound/usb/line6/revision.h | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | #ifndef DRIVER_REVISION | ||
| 2 | /* current subversion revision */ | ||
| 3 | #define DRIVER_REVISION " (904)" | ||
| 4 | #endif | ||
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c new file mode 100644 index 000000000000..aae78d8a82d9 --- /dev/null +++ b/sound/usb/line6/toneport.c | |||
| @@ -0,0 +1,465 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 <linux/wait.h> | ||
| 14 | #include <sound/control.h> | ||
| 15 | |||
| 16 | #include "audio.h" | ||
| 17 | #include "capture.h" | ||
| 18 | #include "driver.h" | ||
| 19 | #include "playback.h" | ||
| 20 | #include "toneport.h" | ||
| 21 | |||
| 22 | static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); | ||
| 23 | |||
| 24 | #define TONEPORT_PCM_DELAY 1 | ||
| 25 | |||
| 26 | static struct snd_ratden toneport_ratden = { | ||
| 27 | .num_min = 44100, | ||
| 28 | .num_max = 44100, | ||
| 29 | .num_step = 1, | ||
| 30 | .den = 1 | ||
| 31 | }; | ||
| 32 | |||
| 33 | static struct line6_pcm_properties toneport_pcm_properties = { | ||
| 34 | .snd_line6_playback_hw = { | ||
| 35 | .info = (SNDRV_PCM_INFO_MMAP | | ||
| 36 | SNDRV_PCM_INFO_INTERLEAVED | | ||
| 37 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
| 38 | SNDRV_PCM_INFO_MMAP_VALID | | ||
| 39 | SNDRV_PCM_INFO_PAUSE | | ||
| 40 | #ifdef CONFIG_PM | ||
| 41 | SNDRV_PCM_INFO_RESUME | | ||
| 42 | #endif | ||
| 43 | SNDRV_PCM_INFO_SYNC_START), | ||
| 44 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
| 45 | .rates = SNDRV_PCM_RATE_KNOT, | ||
| 46 | .rate_min = 44100, | ||
| 47 | .rate_max = 44100, | ||
| 48 | .channels_min = 2, | ||
| 49 | .channels_max = 2, | ||
| 50 | .buffer_bytes_max = 60000, | ||
| 51 | .period_bytes_min = 64, | ||
| 52 | .period_bytes_max = 8192, | ||
| 53 | .periods_min = 1, | ||
| 54 | .periods_max = 1024}, | ||
| 55 | .snd_line6_capture_hw = { | ||
| 56 | .info = (SNDRV_PCM_INFO_MMAP | | ||
| 57 | SNDRV_PCM_INFO_INTERLEAVED | | ||
| 58 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
| 59 | SNDRV_PCM_INFO_MMAP_VALID | | ||
| 60 | #ifdef CONFIG_PM | ||
| 61 | SNDRV_PCM_INFO_RESUME | | ||
| 62 | #endif | ||
| 63 | SNDRV_PCM_INFO_SYNC_START), | ||
| 64 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
| 65 | .rates = SNDRV_PCM_RATE_KNOT, | ||
| 66 | .rate_min = 44100, | ||
| 67 | .rate_max = 44100, | ||
| 68 | .channels_min = 2, | ||
| 69 | .channels_max = 2, | ||
| 70 | .buffer_bytes_max = 60000, | ||
| 71 | .period_bytes_min = 64, | ||
| 72 | .period_bytes_max = 8192, | ||
| 73 | .periods_min = 1, | ||
| 74 | .periods_max = 1024}, | ||
| 75 | .snd_line6_rates = { | ||
| 76 | .nrats = 1, | ||
| 77 | .rats = &toneport_ratden}, | ||
| 78 | .bytes_per_frame = 4 | ||
| 79 | }; | ||
| 80 | |||
| 81 | /* | ||
| 82 | For the led on Guitarport. | ||
| 83 | Brightness goes from 0x00 to 0x26. Set a value above this to have led | ||
| 84 | blink. | ||
| 85 | (void cmd_0x02(byte red, byte green) | ||
| 86 | */ | ||
| 87 | static int led_red = 0x00; | ||
| 88 | static int led_green = 0x26; | ||
| 89 | |||
| 90 | static const struct { | ||
| 91 | const char *name; | ||
| 92 | int code; | ||
| 93 | } toneport_source_info[] = { | ||
| 94 | {"Microphone", 0x0a01}, | ||
| 95 | {"Line", 0x0801}, | ||
| 96 | {"Instrument", 0x0b01}, | ||
| 97 | {"Inst & Mic", 0x0901} | ||
| 98 | }; | ||
| 99 | |||
| 100 | static bool toneport_has_led(enum line6_device_type type) | ||
| 101 | { | ||
| 102 | return | ||
| 103 | (type == LINE6_GUITARPORT) || | ||
| 104 | (type == LINE6_TONEPORT_GX); | ||
| 105 | /* add your device here if you are missing support for the LEDs */ | ||
| 106 | } | ||
| 107 | |||
| 108 | static void toneport_update_led(struct device *dev) | ||
| 109 | { | ||
| 110 | struct usb_interface *interface = to_usb_interface(dev); | ||
| 111 | struct usb_line6_toneport *tp = usb_get_intfdata(interface); | ||
| 112 | struct usb_line6 *line6; | ||
| 113 | |||
| 114 | if (!tp) | ||
| 115 | return; | ||
| 116 | |||
| 117 | line6 = &tp->line6; | ||
| 118 | if (line6) | ||
| 119 | toneport_send_cmd(line6->usbdev, (led_red << 8) | 0x0002, | ||
| 120 | led_green); | ||
| 121 | } | ||
| 122 | |||
| 123 | static ssize_t toneport_set_led_red(struct device *dev, | ||
| 124 | struct device_attribute *attr, | ||
| 125 | const char *buf, size_t count) | ||
| 126 | { | ||
| 127 | int retval; | ||
| 128 | |||
| 129 | retval = kstrtoint(buf, 10, &led_red); | ||
| 130 | if (retval) | ||
| 131 | return retval; | ||
| 132 | |||
| 133 | toneport_update_led(dev); | ||
| 134 | return count; | ||
| 135 | } | ||
| 136 | |||
| 137 | static ssize_t toneport_set_led_green(struct device *dev, | ||
| 138 | struct device_attribute *attr, | ||
| 139 | const char *buf, size_t count) | ||
| 140 | { | ||
| 141 | int retval; | ||
| 142 | |||
| 143 | retval = kstrtoint(buf, 10, &led_green); | ||
| 144 | if (retval) | ||
| 145 | return retval; | ||
| 146 | |||
| 147 | toneport_update_led(dev); | ||
| 148 | return count; | ||
| 149 | } | ||
| 150 | |||
| 151 | static DEVICE_ATTR(led_red, S_IWUSR | S_IRUGO, line6_nop_read, | ||
| 152 | toneport_set_led_red); | ||
| 153 | static DEVICE_ATTR(led_green, S_IWUSR | S_IRUGO, line6_nop_read, | ||
| 154 | toneport_set_led_green); | ||
| 155 | |||
| 156 | static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) | ||
| 157 | { | ||
| 158 | int ret; | ||
| 159 | |||
| 160 | ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, | ||
| 161 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, | ||
| 162 | cmd1, cmd2, NULL, 0, LINE6_TIMEOUT * HZ); | ||
| 163 | |||
| 164 | if (ret < 0) { | ||
| 165 | dev_err(&usbdev->dev, "send failed (error %d)\n", ret); | ||
| 166 | return ret; | ||
| 167 | } | ||
| 168 | |||
| 169 | return 0; | ||
| 170 | } | ||
| 171 | |||
| 172 | /* monitor info callback */ | ||
| 173 | static int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol, | ||
| 174 | struct snd_ctl_elem_info *uinfo) | ||
| 175 | { | ||
| 176 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
| 177 | uinfo->count = 1; | ||
| 178 | uinfo->value.integer.min = 0; | ||
| 179 | uinfo->value.integer.max = 256; | ||
| 180 | return 0; | ||
| 181 | } | ||
| 182 | |||
| 183 | /* monitor get callback */ | ||
| 184 | static int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol, | ||
| 185 | struct snd_ctl_elem_value *ucontrol) | ||
| 186 | { | ||
| 187 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
| 188 | |||
| 189 | ucontrol->value.integer.value[0] = line6pcm->volume_monitor; | ||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | /* monitor put callback */ | ||
| 194 | static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol, | ||
| 195 | struct snd_ctl_elem_value *ucontrol) | ||
| 196 | { | ||
| 197 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
| 198 | |||
| 199 | if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor) | ||
| 200 | return 0; | ||
| 201 | |||
| 202 | line6pcm->volume_monitor = ucontrol->value.integer.value[0]; | ||
| 203 | |||
| 204 | if (line6pcm->volume_monitor > 0) | ||
| 205 | line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_MONITOR); | ||
| 206 | else | ||
| 207 | line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); | ||
| 208 | |||
| 209 | return 1; | ||
| 210 | } | ||
| 211 | |||
| 212 | /* source info callback */ | ||
| 213 | static int snd_toneport_source_info(struct snd_kcontrol *kcontrol, | ||
| 214 | struct snd_ctl_elem_info *uinfo) | ||
| 215 | { | ||
| 216 | const int size = ARRAY_SIZE(toneport_source_info); | ||
| 217 | |||
| 218 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
| 219 | uinfo->count = 1; | ||
| 220 | uinfo->value.enumerated.items = size; | ||
| 221 | |||
| 222 | if (uinfo->value.enumerated.item >= size) | ||
| 223 | uinfo->value.enumerated.item = size - 1; | ||
| 224 | |||
| 225 | strcpy(uinfo->value.enumerated.name, | ||
| 226 | toneport_source_info[uinfo->value.enumerated.item].name); | ||
| 227 | |||
| 228 | return 0; | ||
| 229 | } | ||
| 230 | |||
| 231 | /* source get callback */ | ||
| 232 | static int snd_toneport_source_get(struct snd_kcontrol *kcontrol, | ||
| 233 | struct snd_ctl_elem_value *ucontrol) | ||
| 234 | { | ||
| 235 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
| 236 | struct usb_line6_toneport *toneport = | ||
| 237 | (struct usb_line6_toneport *)line6pcm->line6; | ||
| 238 | ucontrol->value.enumerated.item[0] = toneport->source; | ||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | |||
| 242 | /* source put callback */ | ||
| 243 | static int snd_toneport_source_put(struct snd_kcontrol *kcontrol, | ||
| 244 | struct snd_ctl_elem_value *ucontrol) | ||
| 245 | { | ||
| 246 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
| 247 | struct usb_line6_toneport *toneport = | ||
| 248 | (struct usb_line6_toneport *)line6pcm->line6; | ||
| 249 | unsigned int source; | ||
| 250 | |||
| 251 | source = ucontrol->value.enumerated.item[0]; | ||
| 252 | if (source >= ARRAY_SIZE(toneport_source_info)) | ||
| 253 | return -EINVAL; | ||
| 254 | if (source == toneport->source) | ||
| 255 | return 0; | ||
| 256 | |||
| 257 | toneport->source = source; | ||
| 258 | toneport_send_cmd(toneport->line6.usbdev, | ||
| 259 | toneport_source_info[source].code, 0x0000); | ||
| 260 | return 1; | ||
| 261 | } | ||
| 262 | |||
| 263 | static void toneport_start_pcm(unsigned long arg) | ||
| 264 | { | ||
| 265 | struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg; | ||
| 266 | struct usb_line6 *line6 = &toneport->line6; | ||
| 267 | |||
| 268 | line6_pcm_acquire(line6->line6pcm, LINE6_BITS_PCM_MONITOR); | ||
| 269 | } | ||
| 270 | |||
| 271 | /* control definition */ | ||
| 272 | static struct snd_kcontrol_new toneport_control_monitor = { | ||
| 273 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 274 | .name = "Monitor Playback Volume", | ||
| 275 | .index = 0, | ||
| 276 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 277 | .info = snd_toneport_monitor_info, | ||
| 278 | .get = snd_toneport_monitor_get, | ||
| 279 | .put = snd_toneport_monitor_put | ||
| 280 | }; | ||
| 281 | |||
| 282 | /* source selector definition */ | ||
| 283 | static struct snd_kcontrol_new toneport_control_source = { | ||
| 284 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 285 | .name = "PCM Capture Source", | ||
| 286 | .index = 0, | ||
| 287 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 288 | .info = snd_toneport_source_info, | ||
| 289 | .get = snd_toneport_source_get, | ||
| 290 | .put = snd_toneport_source_put | ||
| 291 | }; | ||
| 292 | |||
| 293 | /* | ||
| 294 | Toneport destructor. | ||
| 295 | */ | ||
| 296 | static void toneport_destruct(struct usb_interface *interface) | ||
| 297 | { | ||
| 298 | struct usb_line6_toneport *toneport = usb_get_intfdata(interface); | ||
| 299 | |||
| 300 | if (toneport == NULL) | ||
| 301 | return; | ||
| 302 | line6_cleanup_audio(&toneport->line6); | ||
| 303 | } | ||
| 304 | |||
| 305 | /* | ||
| 306 | Setup Toneport device. | ||
| 307 | */ | ||
| 308 | static void toneport_setup(struct usb_line6_toneport *toneport) | ||
| 309 | { | ||
| 310 | int ticks; | ||
| 311 | struct usb_line6 *line6 = &toneport->line6; | ||
| 312 | struct usb_device *usbdev = line6->usbdev; | ||
| 313 | |||
| 314 | /* sync time on device with host: */ | ||
| 315 | ticks = (int)get_seconds(); | ||
| 316 | line6_write_data(line6, 0x80c6, &ticks, 4); | ||
| 317 | |||
| 318 | /* enable device: */ | ||
| 319 | toneport_send_cmd(usbdev, 0x0301, 0x0000); | ||
| 320 | |||
| 321 | /* initialize source select: */ | ||
| 322 | switch (line6->type) { | ||
| 323 | case LINE6_TONEPORT_UX1: | ||
| 324 | case LINE6_TONEPORT_UX2: | ||
| 325 | case LINE6_PODSTUDIO_UX1: | ||
| 326 | case LINE6_PODSTUDIO_UX2: | ||
| 327 | toneport_send_cmd(usbdev, | ||
| 328 | toneport_source_info[toneport->source].code, | ||
| 329 | 0x0000); | ||
| 330 | default: | ||
| 331 | break; | ||
| 332 | } | ||
| 333 | |||
| 334 | if (toneport_has_led(line6->type)) | ||
| 335 | toneport_update_led(&usbdev->dev); | ||
| 336 | } | ||
| 337 | |||
| 338 | /* | ||
| 339 | Toneport device disconnected. | ||
| 340 | */ | ||
| 341 | static void line6_toneport_disconnect(struct usb_interface *interface) | ||
| 342 | { | ||
| 343 | struct usb_line6_toneport *toneport; | ||
| 344 | u16 idProduct; | ||
| 345 | |||
| 346 | if (interface == NULL) | ||
| 347 | return; | ||
| 348 | |||
| 349 | toneport = usb_get_intfdata(interface); | ||
| 350 | del_timer_sync(&toneport->timer); | ||
| 351 | idProduct = le16_to_cpu(toneport->line6.usbdev->descriptor.idProduct); | ||
| 352 | |||
| 353 | if (toneport_has_led(idProduct)) { | ||
| 354 | device_remove_file(&interface->dev, &dev_attr_led_red); | ||
| 355 | device_remove_file(&interface->dev, &dev_attr_led_green); | ||
| 356 | } | ||
| 357 | |||
| 358 | if (toneport != NULL) { | ||
| 359 | struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; | ||
| 360 | |||
| 361 | if (line6pcm != NULL) { | ||
| 362 | line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); | ||
| 363 | line6_pcm_disconnect(line6pcm); | ||
| 364 | } | ||
| 365 | } | ||
| 366 | |||
| 367 | toneport_destruct(interface); | ||
| 368 | } | ||
| 369 | |||
| 370 | |||
| 371 | /* | ||
| 372 | Try to init Toneport device. | ||
| 373 | */ | ||
| 374 | static int toneport_try_init(struct usb_interface *interface, | ||
| 375 | struct usb_line6 *line6) | ||
| 376 | { | ||
| 377 | int err; | ||
| 378 | struct usb_line6_toneport *toneport = (struct usb_line6_toneport *) line6; | ||
| 379 | |||
| 380 | if ((interface == NULL) || (toneport == NULL)) | ||
| 381 | return -ENODEV; | ||
| 382 | |||
| 383 | line6->disconnect = line6_toneport_disconnect; | ||
| 384 | |||
| 385 | /* initialize audio system: */ | ||
| 386 | err = line6_init_audio(line6); | ||
| 387 | if (err < 0) | ||
| 388 | return err; | ||
| 389 | |||
| 390 | /* initialize PCM subsystem: */ | ||
| 391 | err = line6_init_pcm(line6, &toneport_pcm_properties); | ||
| 392 | if (err < 0) | ||
| 393 | return err; | ||
| 394 | |||
| 395 | /* register monitor control: */ | ||
| 396 | err = snd_ctl_add(line6->card, | ||
| 397 | snd_ctl_new1(&toneport_control_monitor, | ||
| 398 | line6->line6pcm)); | ||
| 399 | if (err < 0) | ||
| 400 | return err; | ||
| 401 | |||
| 402 | /* register source select control: */ | ||
| 403 | switch (line6->type) { | ||
| 404 | case LINE6_TONEPORT_UX1: | ||
| 405 | case LINE6_TONEPORT_UX2: | ||
| 406 | case LINE6_PODSTUDIO_UX1: | ||
| 407 | case LINE6_PODSTUDIO_UX2: | ||
| 408 | err = | ||
| 409 | snd_ctl_add(line6->card, | ||
| 410 | snd_ctl_new1(&toneport_control_source, | ||
| 411 | line6->line6pcm)); | ||
| 412 | if (err < 0) | ||
| 413 | return err; | ||
| 414 | |||
| 415 | default: | ||
| 416 | break; | ||
| 417 | } | ||
| 418 | |||
| 419 | /* register audio system: */ | ||
| 420 | err = line6_register_audio(line6); | ||
| 421 | if (err < 0) | ||
| 422 | return err; | ||
| 423 | |||
| 424 | line6_read_serial_number(line6, &toneport->serial_number); | ||
| 425 | line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); | ||
| 426 | |||
| 427 | if (toneport_has_led(line6->type)) { | ||
| 428 | CHECK_RETURN(device_create_file | ||
| 429 | (&interface->dev, &dev_attr_led_red)); | ||
| 430 | CHECK_RETURN(device_create_file | ||
| 431 | (&interface->dev, &dev_attr_led_green)); | ||
| 432 | } | ||
| 433 | |||
| 434 | toneport_setup(toneport); | ||
| 435 | |||
| 436 | init_timer(&toneport->timer); | ||
| 437 | toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ; | ||
| 438 | toneport->timer.function = toneport_start_pcm; | ||
| 439 | toneport->timer.data = (unsigned long)toneport; | ||
| 440 | add_timer(&toneport->timer); | ||
| 441 | |||
| 442 | return 0; | ||
| 443 | } | ||
| 444 | |||
| 445 | /* | ||
| 446 | Init Toneport device (and clean up in case of failure). | ||
| 447 | */ | ||
| 448 | int line6_toneport_init(struct usb_interface *interface, | ||
| 449 | struct usb_line6 *line6) | ||
| 450 | { | ||
| 451 | int err = toneport_try_init(interface, line6); | ||
| 452 | |||
| 453 | if (err < 0) | ||
| 454 | toneport_destruct(interface); | ||
| 455 | |||
| 456 | return err; | ||
| 457 | } | ||
| 458 | |||
| 459 | /* | ||
| 460 | Resume Toneport device after reset. | ||
| 461 | */ | ||
| 462 | void line6_toneport_reset_resume(struct usb_line6_toneport *toneport) | ||
| 463 | { | ||
| 464 | toneport_setup(toneport); | ||
| 465 | } | ||
diff --git a/sound/usb/line6/toneport.h b/sound/usb/line6/toneport.h new file mode 100644 index 000000000000..8cb14426f6ae --- /dev/null +++ b/sound/usb/line6/toneport.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 | #include <linux/usb.h> | ||
| 16 | #include <sound/core.h> | ||
| 17 | |||
| 18 | #include "driver.h" | ||
| 19 | |||
| 20 | struct usb_line6_toneport { | ||
| 21 | /** | ||
| 22 | Generic Line6 USB data. | ||
| 23 | */ | ||
| 24 | struct usb_line6 line6; | ||
| 25 | |||
| 26 | /** | ||
| 27 | Source selector. | ||
| 28 | */ | ||
| 29 | int source; | ||
| 30 | |||
| 31 | /** | ||
| 32 | Serial number of device. | ||
| 33 | */ | ||
| 34 | int serial_number; | ||
| 35 | |||
| 36 | /** | ||
| 37 | Firmware version (x 100). | ||
| 38 | */ | ||
| 39 | int firmware_version; | ||
| 40 | |||
| 41 | /** | ||
| 42 | Timer for delayed PCM startup. | ||
| 43 | */ | ||
| 44 | struct timer_list timer; | ||
| 45 | }; | ||
| 46 | |||
| 47 | extern int line6_toneport_init(struct usb_interface *interface, | ||
| 48 | struct usb_line6 *line6); | ||
| 49 | extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport); | ||
| 50 | |||
| 51 | #endif | ||
diff --git a/sound/usb/line6/usbdefs.h b/sound/usb/line6/usbdefs.h new file mode 100644 index 000000000000..f4d080e69abc --- /dev/null +++ b/sound/usb/line6/usbdefs.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 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 | #define USB_INTERVALS_PER_SECOND 1000 | ||
| 16 | |||
| 17 | /* device supports settings parameter via USB */ | ||
| 18 | #define LINE6_CAP_CONTROL (1 << 0) | ||
| 19 | /* device supports PCM input/output via USB */ | ||
| 20 | #define LINE6_CAP_PCM (1 << 1) | ||
| 21 | /* device support hardware monitoring */ | ||
| 22 | #define LINE6_CAP_HWMON (1 << 2) | ||
| 23 | |||
| 24 | #define LINE6_FALLBACK_INTERVAL 10 | ||
| 25 | #define LINE6_FALLBACK_MAXPACKETSIZE 16 | ||
| 26 | |||
| 27 | #endif | ||
diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c new file mode 100644 index 000000000000..b4a41b0ad0ea --- /dev/null +++ b/sound/usb/line6/variax.c | |||
| @@ -0,0 +1,239 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 <linux/slab.h> | ||
| 13 | |||
| 14 | #include "audio.h" | ||
| 15 | #include "driver.h" | ||
| 16 | #include "variax.h" | ||
| 17 | |||
| 18 | #define VARIAX_OFFSET_ACTIVATE 7 | ||
| 19 | |||
| 20 | /* | ||
| 21 | This message is sent by the device during initialization and identifies | ||
| 22 | the connected guitar version. | ||
| 23 | */ | ||
| 24 | static const char variax_init_version[] = { | ||
| 25 | 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c, | ||
| 26 | 0x07, 0x00, 0x00, 0x00 | ||
| 27 | }; | ||
| 28 | |||
| 29 | /* | ||
| 30 | This message is the last one sent by the device during initialization. | ||
| 31 | */ | ||
| 32 | static const char variax_init_done[] = { | ||
| 33 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b | ||
| 34 | }; | ||
| 35 | |||
| 36 | static const char variax_activate[] = { | ||
| 37 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, | ||
| 38 | 0xf7 | ||
| 39 | }; | ||
| 40 | |||
| 41 | /* forward declarations: */ | ||
| 42 | static void variax_startup2(unsigned long data); | ||
| 43 | static void variax_startup4(unsigned long data); | ||
| 44 | static void variax_startup5(unsigned long data); | ||
| 45 | |||
| 46 | static void variax_activate_async(struct usb_line6_variax *variax, int a) | ||
| 47 | { | ||
| 48 | variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; | ||
| 49 | line6_send_raw_message_async(&variax->line6, variax->buffer_activate, | ||
| 50 | sizeof(variax_activate)); | ||
| 51 | } | ||
| 52 | |||
| 53 | /* | ||
| 54 | Variax startup procedure. | ||
| 55 | This is a sequence of functions with special requirements (e.g., must | ||
| 56 | not run immediately after initialization, must not run in interrupt | ||
| 57 | context). After the last one has finished, the device is ready to use. | ||
| 58 | */ | ||
| 59 | |||
| 60 | static void variax_startup1(struct usb_line6_variax *variax) | ||
| 61 | { | ||
| 62 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT); | ||
| 63 | |||
| 64 | /* delay startup procedure: */ | ||
| 65 | line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, | ||
| 66 | variax_startup2, (unsigned long)variax); | ||
| 67 | } | ||
| 68 | |||
| 69 | static void variax_startup2(unsigned long data) | ||
| 70 | { | ||
| 71 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; | ||
| 72 | struct usb_line6 *line6 = &variax->line6; | ||
| 73 | |||
| 74 | /* schedule another startup procedure until startup is complete: */ | ||
| 75 | if (variax->startup_progress >= VARIAX_STARTUP_LAST) | ||
| 76 | return; | ||
| 77 | |||
| 78 | variax->startup_progress = VARIAX_STARTUP_VERSIONREQ; | ||
| 79 | line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, | ||
| 80 | variax_startup2, (unsigned long)variax); | ||
| 81 | |||
| 82 | /* request firmware version: */ | ||
| 83 | line6_version_request_async(line6); | ||
| 84 | } | ||
| 85 | |||
| 86 | static void variax_startup3(struct usb_line6_variax *variax) | ||
| 87 | { | ||
| 88 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT); | ||
| 89 | |||
| 90 | /* delay startup procedure: */ | ||
| 91 | line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3, | ||
| 92 | variax_startup4, (unsigned long)variax); | ||
| 93 | } | ||
| 94 | |||
| 95 | static void variax_startup4(unsigned long data) | ||
| 96 | { | ||
| 97 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; | ||
| 98 | |||
| 99 | CHECK_STARTUP_PROGRESS(variax->startup_progress, | ||
| 100 | VARIAX_STARTUP_ACTIVATE); | ||
| 101 | |||
| 102 | /* activate device: */ | ||
| 103 | variax_activate_async(variax, 1); | ||
| 104 | line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4, | ||
| 105 | variax_startup5, (unsigned long)variax); | ||
| 106 | } | ||
| 107 | |||
| 108 | static void variax_startup5(unsigned long data) | ||
| 109 | { | ||
| 110 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; | ||
| 111 | |||
| 112 | CHECK_STARTUP_PROGRESS(variax->startup_progress, | ||
| 113 | VARIAX_STARTUP_WORKQUEUE); | ||
| 114 | |||
| 115 | /* schedule work for global work queue: */ | ||
| 116 | schedule_work(&variax->startup_work); | ||
| 117 | } | ||
| 118 | |||
| 119 | static void variax_startup6(struct work_struct *work) | ||
| 120 | { | ||
| 121 | struct usb_line6_variax *variax = | ||
| 122 | container_of(work, struct usb_line6_variax, startup_work); | ||
| 123 | |||
| 124 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP); | ||
| 125 | |||
| 126 | /* ALSA audio interface: */ | ||
| 127 | line6_register_audio(&variax->line6); | ||
| 128 | } | ||
| 129 | |||
| 130 | /* | ||
| 131 | Process a completely received message. | ||
| 132 | */ | ||
| 133 | static void line6_variax_process_message(struct usb_line6 *line6) | ||
| 134 | { | ||
| 135 | struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; | ||
| 136 | const unsigned char *buf = variax->line6.buffer_message; | ||
| 137 | |||
| 138 | switch (buf[0]) { | ||
| 139 | case LINE6_RESET: | ||
| 140 | dev_info(variax->line6.ifcdev, "VARIAX reset\n"); | ||
| 141 | break; | ||
| 142 | |||
| 143 | case LINE6_SYSEX_BEGIN: | ||
| 144 | if (memcmp(buf + 1, variax_init_version + 1, | ||
| 145 | sizeof(variax_init_version) - 1) == 0) { | ||
| 146 | variax_startup3(variax); | ||
| 147 | } else if (memcmp(buf + 1, variax_init_done + 1, | ||
| 148 | sizeof(variax_init_done) - 1) == 0) { | ||
| 149 | /* notify of complete initialization: */ | ||
| 150 | variax_startup4((unsigned long)variax); | ||
| 151 | } | ||
| 152 | break; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | /* | ||
| 157 | Variax destructor. | ||
| 158 | */ | ||
| 159 | static void variax_destruct(struct usb_interface *interface) | ||
| 160 | { | ||
| 161 | struct usb_line6_variax *variax = usb_get_intfdata(interface); | ||
| 162 | |||
| 163 | if (variax == NULL) | ||
| 164 | return; | ||
| 165 | line6_cleanup_audio(&variax->line6); | ||
| 166 | |||
| 167 | del_timer(&variax->startup_timer1); | ||
| 168 | del_timer(&variax->startup_timer2); | ||
| 169 | cancel_work_sync(&variax->startup_work); | ||
| 170 | |||
| 171 | kfree(variax->buffer_activate); | ||
| 172 | } | ||
| 173 | |||
| 174 | /* | ||
| 175 | Workbench device disconnected. | ||
| 176 | */ | ||
| 177 | static void line6_variax_disconnect(struct usb_interface *interface) | ||
| 178 | { | ||
| 179 | if (interface == NULL) | ||
| 180 | return; | ||
| 181 | |||
| 182 | variax_destruct(interface); | ||
| 183 | } | ||
| 184 | |||
| 185 | /* | ||
| 186 | Try to init workbench device. | ||
| 187 | */ | ||
| 188 | static int variax_try_init(struct usb_interface *interface, | ||
| 189 | struct usb_line6 *line6) | ||
| 190 | { | ||
| 191 | struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; | ||
| 192 | int err; | ||
| 193 | |||
| 194 | line6->process_message = line6_variax_process_message; | ||
| 195 | line6->disconnect = line6_variax_disconnect; | ||
| 196 | |||
| 197 | init_timer(&variax->startup_timer1); | ||
| 198 | init_timer(&variax->startup_timer2); | ||
| 199 | INIT_WORK(&variax->startup_work, variax_startup6); | ||
| 200 | |||
| 201 | if ((interface == NULL) || (variax == NULL)) | ||
| 202 | return -ENODEV; | ||
| 203 | |||
| 204 | /* initialize USB buffers: */ | ||
| 205 | variax->buffer_activate = kmemdup(variax_activate, | ||
| 206 | sizeof(variax_activate), GFP_KERNEL); | ||
| 207 | |||
| 208 | if (variax->buffer_activate == NULL) { | ||
| 209 | dev_err(&interface->dev, "Out of memory\n"); | ||
| 210 | return -ENOMEM; | ||
| 211 | } | ||
| 212 | |||
| 213 | /* initialize audio system: */ | ||
| 214 | err = line6_init_audio(&variax->line6); | ||
| 215 | if (err < 0) | ||
| 216 | return err; | ||
| 217 | |||
| 218 | /* initialize MIDI subsystem: */ | ||
| 219 | err = line6_init_midi(&variax->line6); | ||
| 220 | if (err < 0) | ||
| 221 | return err; | ||
| 222 | |||
| 223 | /* initiate startup procedure: */ | ||
| 224 | variax_startup1(variax); | ||
| 225 | return 0; | ||
| 226 | } | ||
| 227 | |||
| 228 | /* | ||
| 229 | Init workbench device (and clean up in case of failure). | ||
| 230 | */ | ||
| 231 | int line6_variax_init(struct usb_interface *interface, struct usb_line6 *line6) | ||
| 232 | { | ||
| 233 | int err = variax_try_init(interface, line6); | ||
| 234 | |||
| 235 | if (err < 0) | ||
| 236 | variax_destruct(interface); | ||
| 237 | |||
| 238 | return err; | ||
| 239 | } | ||
diff --git a/sound/usb/line6/variax.h b/sound/usb/line6/variax.h new file mode 100644 index 000000000000..dfb94e574cc4 --- /dev/null +++ b/sound/usb/line6/variax.h | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | /* | ||
| 2 | * Line6 Linux USB driver - 0.9.1beta | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004-2010 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 | #include <linux/spinlock.h> | ||
| 16 | #include <linux/usb.h> | ||
| 17 | #include <linux/wait.h> | ||
| 18 | #include <sound/core.h> | ||
| 19 | |||
| 20 | #include "driver.h" | ||
| 21 | |||
| 22 | #define VARIAX_STARTUP_DELAY1 1000 | ||
| 23 | #define VARIAX_STARTUP_DELAY3 100 | ||
| 24 | #define VARIAX_STARTUP_DELAY4 100 | ||
| 25 | |||
| 26 | /* | ||
| 27 | Stages of Variax startup procedure | ||
| 28 | */ | ||
| 29 | enum { | ||
| 30 | VARIAX_STARTUP_INIT = 1, | ||
| 31 | VARIAX_STARTUP_VERSIONREQ, | ||
| 32 | VARIAX_STARTUP_WAIT, | ||
| 33 | VARIAX_STARTUP_ACTIVATE, | ||
| 34 | VARIAX_STARTUP_WORKQUEUE, | ||
| 35 | VARIAX_STARTUP_SETUP, | ||
| 36 | VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1 | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct usb_line6_variax { | ||
| 40 | /** | ||
| 41 | Generic Line6 USB data. | ||
| 42 | */ | ||
| 43 | struct usb_line6 line6; | ||
| 44 | |||
| 45 | /** | ||
| 46 | Buffer for activation code. | ||
| 47 | */ | ||
| 48 | unsigned char *buffer_activate; | ||
| 49 | |||
| 50 | /** | ||
| 51 | Handler for device initializaton. | ||
| 52 | */ | ||
| 53 | struct work_struct startup_work; | ||
| 54 | |||
| 55 | /** | ||
| 56 | Timers for device initializaton. | ||
| 57 | */ | ||
| 58 | struct timer_list startup_timer1; | ||
| 59 | struct timer_list startup_timer2; | ||
| 60 | |||
| 61 | /** | ||
| 62 | Current progress in startup procedure. | ||
| 63 | */ | ||
| 64 | int startup_progress; | ||
| 65 | }; | ||
| 66 | |||
| 67 | extern int line6_variax_init(struct usb_interface *interface, | ||
| 68 | struct usb_line6 *line6); | ||
| 69 | |||
| 70 | #endif | ||
