summaryrefslogtreecommitdiff
path: root/drivers/staging/line6
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/line6')
-rw-r--r--drivers/staging/line6/Kconfig85
-rw-r--r--drivers/staging/line6/Makefile16
-rw-r--r--drivers/staging/line6/audio.c74
-rw-r--r--drivers/staging/line6/audio.h21
-rw-r--r--drivers/staging/line6/capture.c435
-rw-r--r--drivers/staging/line6/capture.h35
-rw-r--r--drivers/staging/line6/config.h48
-rw-r--r--drivers/staging/line6/control.c995
-rw-r--r--drivers/staging/line6/control.h195
-rw-r--r--drivers/staging/line6/driver.c1362
-rw-r--r--drivers/staging/line6/driver.h237
-rw-r--r--drivers/staging/line6/dumprequest.c135
-rw-r--r--drivers/staging/line6/dumprequest.h76
-rw-r--r--drivers/staging/line6/midi.c446
-rw-r--r--drivers/staging/line6/midi.h82
-rw-r--r--drivers/staging/line6/midibuf.c264
-rw-r--r--drivers/staging/line6/midibuf.h38
-rw-r--r--drivers/staging/line6/pcm.c564
-rw-r--r--drivers/staging/line6/pcm.h382
-rw-r--r--drivers/staging/line6/playback.c586
-rw-r--r--drivers/staging/line6/playback.h41
-rw-r--r--drivers/staging/line6/pod.c1351
-rw-r--r--drivers/staging/line6/pod.h205
-rw-r--r--drivers/staging/line6/podhd.c154
-rw-r--r--drivers/staging/line6/podhd.h30
-rw-r--r--drivers/staging/line6/revision.h4
-rw-r--r--drivers/staging/line6/toneport.c455
-rw-r--r--drivers/staging/line6/toneport.h52
-rw-r--r--drivers/staging/line6/usbdefs.h107
-rw-r--r--drivers/staging/line6/variax.c719
-rw-r--r--drivers/staging/line6/variax.h132
31 files changed, 9326 insertions, 0 deletions
diff --git a/drivers/staging/line6/Kconfig b/drivers/staging/line6/Kconfig
new file mode 100644
index 00000000..43120ff2
--- /dev/null
+++ b/drivers/staging/line6/Kconfig
@@ -0,0 +1,85 @@
+menuconfig LINE6_USB
+ tristate "Line6 USB support"
+ depends on USB && SND
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ This is a driver for the guitar amp, cab, and effects modeller
+ PODxt Pro by Line6 (and similar devices), supporting the
+ following features:
+ * Reading/writing individual parameters
+ * Reading/writing complete channel, effects setup, and amp
+ setup data
+ * Channel switching
+ * Virtual MIDI interface
+ * Tuner access
+ * Playback/capture/mixer device for any ALSA-compatible PCM
+ audio application
+ * Signal routing (record clean/processed guitar signal,
+ re-amping)
+
+ Preliminary support for the Variax Workbench and TonePort
+ devices is included.
+
+if LINE6_USB
+
+config LINE6_USB_DEBUG
+ bool "print debug messages"
+ default n
+ help
+ Say Y here to write debug messages to the syslog.
+
+ If unsure, say N.
+
+config LINE6_USB_DUMP_CTRL
+ bool "dump control messages"
+ default n
+ help
+ Say Y here to write control messages sent to and received from
+ Line6 devices to the syslog.
+
+ If unsure, say N.
+
+config LINE6_USB_DUMP_MIDI
+ bool "dump MIDI messages"
+ default n
+ help
+ Say Y here to write MIDI messages sent to and received from
+ Line6 devices to the syslog.
+
+ If unsure, say N.
+
+config LINE6_USB_DUMP_PCM
+ bool "dump PCM data"
+ default n
+ help
+ Say Y here to write PCM data sent to and received from Line6
+ devices to the syslog. This will produce a huge amount of
+ syslog data during playback and capture.
+
+ If unsure, say N.
+
+config LINE6_USB_RAW
+ bool "raw data communication"
+ default n
+ help
+ Say Y here to create special files which allow to send raw data
+ to the device. This bypasses any sanity checks, so if you discover
+ the code to erase the firmware, feel free to render your device
+ useless, but only after reading the GPL section "NO WARRANTY".
+
+ If unsure, say N.
+
+config LINE6_USB_IMPULSE_RESPONSE
+ bool "measure impulse response"
+ default n
+ help
+ Say Y here to add code to measure the impulse response of a Line6
+ device. This is more accurate than user-space methods since it
+ bypasses any PCM data buffering (e.g., by ALSA or jack). This is
+ useful for assessing the performance of new devices, but is not
+ required for normal operation.
+
+ If unsure, say N.
+
+endif # LINE6_USB
diff --git a/drivers/staging/line6/Makefile b/drivers/staging/line6/Makefile
new file mode 100644
index 00000000..34a2ddac
--- /dev/null
+++ b/drivers/staging/line6/Makefile
@@ -0,0 +1,16 @@
+obj-$(CONFIG_LINE6_USB) += line6usb.o
+
+line6usb-y := \
+ audio.o \
+ capture.o \
+ control.o \
+ driver.o \
+ dumprequest.o \
+ midi.o \
+ midibuf.o \
+ pcm.o \
+ playback.o \
+ pod.o \
+ toneport.o \
+ variax.o \
+ podhd.o
diff --git a/drivers/staging/line6/audio.c b/drivers/staging/line6/audio.c
new file mode 100644
index 00000000..8e739839
--- /dev/null
+++ b/drivers/staging/line6/audio.c
@@ -0,0 +1,74 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <linux/export.h>
+
+#include "driver.h"
+#include "audio.h"
+
+static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+
+/*
+ Initialize the Line6 USB audio system.
+*/
+int line6_init_audio(struct usb_line6 *line6)
+{
+ static int dev;
+ struct snd_card *card;
+ int err;
+
+ err = snd_card_create(line6_index[dev], line6_id[dev], THIS_MODULE, 0,
+ &card);
+ if (err < 0)
+ return err;
+
+ line6->card = card;
+
+ strcpy(card->id, line6->properties->id);
+ strcpy(card->driver, DRIVER_NAME);
+ strcpy(card->shortname, line6->properties->name);
+ /* longname is 80 chars - see asound.h */
+ sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name,
+ dev_name(line6->ifcdev));
+ return 0;
+}
+
+/*
+ Register the Line6 USB audio system.
+*/
+int line6_register_audio(struct usb_line6 *line6)
+{
+ int err;
+
+ err = snd_card_register(line6->card);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ Cleanup the Line6 USB audio system.
+*/
+void line6_cleanup_audio(struct usb_line6 *line6)
+{
+ struct snd_card *card = line6->card;
+
+ if (card == NULL)
+ return;
+
+ snd_card_disconnect(card);
+ snd_card_free(card);
+ line6->card = NULL;
+}
diff --git a/drivers/staging/line6/audio.h b/drivers/staging/line6/audio.h
new file mode 100644
index 00000000..5f8a09a0
--- /dev/null
+++ b/drivers/staging/line6/audio.h
@@ -0,0 +1,21 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef AUDIO_H
+#define AUDIO_H
+
+#include "driver.h"
+
+extern void line6_cleanup_audio(struct usb_line6 *);
+extern int line6_init_audio(struct usb_line6 *);
+extern int line6_register_audio(struct usb_line6 *);
+
+#endif
diff --git a/drivers/staging/line6/capture.c b/drivers/staging/line6/capture.c
new file mode 100644
index 00000000..c85c5b6b
--- /dev/null
+++ b/drivers/staging/line6/capture.c
@@ -0,0 +1,435 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "audio.h"
+#include "capture.h"
+#include "driver.h"
+#include "pcm.h"
+#include "pod.h"
+
+/*
+ Find a free URB and submit it.
+*/
+static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
+{
+ int index;
+ unsigned long flags;
+ int i, urb_size;
+ int ret;
+ struct urb *urb_in;
+
+ spin_lock_irqsave(&line6pcm->lock_audio_in, flags);
+ index =
+ find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS);
+
+ if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+ spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
+ dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
+ return -EINVAL;
+ }
+
+ urb_in = line6pcm->urb_audio_in[index];
+ urb_size = 0;
+
+ for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
+ struct usb_iso_packet_descriptor *fin =
+ &urb_in->iso_frame_desc[i];
+ fin->offset = urb_size;
+ fin->length = line6pcm->max_packet_size;
+ urb_size += line6pcm->max_packet_size;
+ }
+
+ urb_in->transfer_buffer =
+ line6pcm->buffer_in +
+ index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+ urb_in->transfer_buffer_length = urb_size;
+ urb_in->context = line6pcm;
+
+ ret = usb_submit_urb(urb_in, GFP_ATOMIC);
+
+ if (ret == 0)
+ set_bit(index, &line6pcm->active_urb_in);
+ else
+ dev_err(line6pcm->line6->ifcdev,
+ "URB in #%d submission failed (%d)\n", index, ret);
+
+ spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
+ return 0;
+}
+
+/*
+ Submit all currently available capture URBs.
+*/
+int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
+{
+ int ret, i;
+
+ for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+ ret = submit_audio_in_urb(line6pcm);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ Unlink all currently active capture URBs.
+*/
+void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm)
+{
+ unsigned int i;
+
+ for (i = LINE6_ISO_BUFFERS; i--;) {
+ if (test_bit(i, &line6pcm->active_urb_in)) {
+ if (!test_and_set_bit(i, &line6pcm->unlink_urb_in)) {
+ struct urb *u = line6pcm->urb_audio_in[i];
+ usb_unlink_urb(u);
+ }
+ }
+ }
+}
+
+/*
+ Wait until unlinking of all currently active capture URBs has been
+ finished.
+*/
+void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
+{
+ int timeout = HZ;
+ unsigned int i;
+ int alive;
+
+ do {
+ alive = 0;
+ for (i = LINE6_ISO_BUFFERS; i--;) {
+ if (test_bit(i, &line6pcm->active_urb_in))
+ alive++;
+ }
+ if (!alive)
+ break;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (--timeout > 0);
+ if (alive)
+ snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
+}
+
+/*
+ Unlink all currently active capture URBs, and wait for finishing.
+*/
+void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
+{
+ line6_unlink_audio_in_urbs(line6pcm);
+ line6_wait_clear_audio_in_urbs(line6pcm);
+}
+
+/*
+ Copy data into ALSA capture buffer.
+*/
+void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
+{
+ struct snd_pcm_substream *substream =
+ get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+ int frames = fsize / bytes_per_frame;
+
+ if (runtime == NULL)
+ return;
+
+ if (line6pcm->pos_in_done + frames > runtime->buffer_size) {
+ /*
+ The transferred area goes over buffer boundary,
+ copy two separate chunks.
+ */
+ int len;
+ len = runtime->buffer_size - line6pcm->pos_in_done;
+
+ if (len > 0) {
+ memcpy(runtime->dma_area +
+ line6pcm->pos_in_done * bytes_per_frame, fbuf,
+ len * bytes_per_frame);
+ memcpy(runtime->dma_area, fbuf + len * bytes_per_frame,
+ (frames - len) * bytes_per_frame);
+ } else {
+ /* this is somewhat paranoid */
+ dev_err(line6pcm->line6->ifcdev,
+ "driver bug: len = %d\n", len);
+ }
+ } else {
+ /* copy single chunk */
+ memcpy(runtime->dma_area +
+ line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize);
+ }
+
+ line6pcm->pos_in_done += frames;
+ if (line6pcm->pos_in_done >= runtime->buffer_size)
+ line6pcm->pos_in_done -= runtime->buffer_size;
+}
+
+void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
+{
+ struct snd_pcm_substream *substream =
+ get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
+
+ line6pcm->bytes_in += length;
+ if (line6pcm->bytes_in >= line6pcm->period_in) {
+ line6pcm->bytes_in %= line6pcm->period_in;
+ snd_pcm_period_elapsed(substream);
+ }
+}
+
+void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm)
+{
+ kfree(line6pcm->buffer_in);
+ line6pcm->buffer_in = NULL;
+}
+
+/*
+ * Callback for completed capture URB.
+ */
+static void audio_in_callback(struct urb *urb)
+{
+ int i, index, length = 0, shutdown = 0;
+ unsigned long flags;
+
+ struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
+
+ line6pcm->last_frame_in = urb->start_frame;
+
+ /* find index of URB */
+ for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
+ if (urb == line6pcm->urb_audio_in[index])
+ break;
+
+#ifdef CONFIG_LINE6_USB_DUMP_PCM
+ for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
+ struct usb_iso_packet_descriptor *fout =
+ &urb->iso_frame_desc[i];
+ line6_write_hexdump(line6pcm->line6, 'C',
+ urb->transfer_buffer + fout->offset,
+ fout->length);
+ }
+#endif
+
+ spin_lock_irqsave(&line6pcm->lock_audio_in, flags);
+
+ for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
+ char *fbuf;
+ int fsize;
+ struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i];
+
+ if (fin->status == -EXDEV) {
+ shutdown = 1;
+ break;
+ }
+
+ fbuf = urb->transfer_buffer + fin->offset;
+ fsize = fin->actual_length;
+
+ if (fsize > line6pcm->max_packet_size) {
+ dev_err(line6pcm->line6->ifcdev,
+ "driver and/or device bug: packet too large (%d > %d)\n",
+ fsize, line6pcm->max_packet_size);
+ }
+
+ length += fsize;
+
+ /* the following assumes LINE6_ISO_PACKETS == 1: */
+ line6pcm->prev_fbuf = fbuf;
+ line6pcm->prev_fsize = fsize;
+
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE))
+#endif
+ if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, &line6pcm->flags)
+ && (fsize > 0))
+ line6_capture_copy(line6pcm, fbuf, fsize);
+ }
+
+ clear_bit(index, &line6pcm->active_urb_in);
+
+ if (test_and_clear_bit(index, &line6pcm->unlink_urb_in))
+ shutdown = 1;
+
+ spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
+
+ if (!shutdown) {
+ submit_audio_in_urb(line6pcm);
+
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE))
+#endif
+ if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, &line6pcm->flags))
+ line6_capture_check_period(line6pcm, length);
+ }
+}
+
+/* open capture callback */
+static int snd_line6_capture_open(struct snd_pcm_substream *substream)
+{
+ int err;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+
+ err = snd_pcm_hw_constraint_ratdens(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ (&line6pcm->
+ properties->snd_line6_rates));
+ if (err < 0)
+ return err;
+
+ runtime->hw = line6pcm->properties->snd_line6_capture_hw;
+ return 0;
+}
+
+/* close capture callback */
+static int snd_line6_capture_close(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/* hw_params capture callback */
+static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int ret;
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+
+ /* -- Florian Demski [FD] */
+ /* don't ask me why, but this fixes the bug on my machine */
+ if (line6pcm == NULL) {
+ if (substream->pcm == NULL)
+ return -ENOMEM;
+ if (substream->pcm->private_data == NULL)
+ return -ENOMEM;
+ substream->private_data = substream->pcm->private_data;
+ line6pcm = snd_pcm_substream_chip(substream);
+ }
+ /* -- [FD] end */
+
+ ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER);
+
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (ret < 0) {
+ line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER);
+ return ret;
+ }
+
+ line6pcm->period_in = params_period_bytes(hw_params);
+ return 0;
+}
+
+/* hw_free capture callback */
+static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+ line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* trigger callback */
+int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd)
+{
+ int err;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+#ifdef CONFIG_PM
+ case SNDRV_PCM_TRIGGER_RESUME:
+#endif
+ err = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_STREAM);
+
+ if (err < 0)
+ return err;
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+#ifdef CONFIG_PM
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+#endif
+ err = line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_STREAM);
+
+ if (err < 0)
+ return err;
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* capture pointer callback */
+static snd_pcm_uframes_t
+snd_line6_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+ return line6pcm->pos_in_done;
+}
+
+/* capture operators */
+struct snd_pcm_ops snd_line6_capture_ops = {
+ .open = snd_line6_capture_open,
+ .close = snd_line6_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_line6_capture_hw_params,
+ .hw_free = snd_line6_capture_hw_free,
+ .prepare = snd_line6_prepare,
+ .trigger = snd_line6_trigger,
+ .pointer = snd_line6_capture_pointer,
+};
+
+int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
+{
+ int i;
+
+ /* create audio URBs and fill in constant values: */
+ for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+ struct urb *urb;
+
+ /* URB for audio in: */
+ urb = line6pcm->urb_audio_in[i] =
+ usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);
+
+ if (urb == NULL) {
+ dev_err(line6pcm->line6->ifcdev, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ urb->dev = line6pcm->line6->usbdev;
+ urb->pipe =
+ usb_rcvisocpipe(line6pcm->line6->usbdev,
+ line6pcm->ep_audio_read &
+ USB_ENDPOINT_NUMBER_MASK);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->start_frame = -1;
+ urb->number_of_packets = LINE6_ISO_PACKETS;
+ urb->interval = LINE6_ISO_INTERVAL;
+ urb->error_count = 0;
+ urb->complete = audio_in_callback;
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/line6/capture.h b/drivers/staging/line6/capture.h
new file mode 100644
index 00000000..4157bcb5
--- /dev/null
+++ b/drivers/staging/line6/capture.h
@@ -0,0 +1,35 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef CAPTURE_H
+#define CAPTURE_H
+
+#include <sound/pcm.h>
+
+#include "driver.h"
+#include "pcm.h"
+
+extern struct snd_pcm_ops snd_line6_capture_ops;
+
+extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf,
+ int fsize);
+extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm,
+ int length);
+extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm);
+extern void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm);
+extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm);
+extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm);
+extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm
+ *line6pcm);
+extern void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm);
+extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd);
+
+#endif
diff --git a/drivers/staging/line6/config.h b/drivers/staging/line6/config.h
new file mode 100644
index 00000000..f8a5149e
--- /dev/null
+++ b/drivers/staging/line6/config.h
@@ -0,0 +1,48 @@
+/*
+ * Line6 Linux USB driver - 0.8.0
+ *
+ * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG 1
+#endif
+
+
+/*
+ * Development tools.
+ */
+#define DO_DEBUG_MESSAGES 0
+#define DO_DUMP_URB_SEND DO_DEBUG_MESSAGES
+#define DO_DUMP_URB_RECEIVE DO_DEBUG_MESSAGES
+#define DO_DUMP_PCM_SEND 0
+#define DO_DUMP_PCM_RECEIVE 0
+#define DO_DUMP_MIDI_SEND DO_DEBUG_MESSAGES
+#define DO_DUMP_MIDI_RECEIVE DO_DEBUG_MESSAGES
+#define DO_DUMP_ANY (DO_DUMP_URB_SEND || DO_DUMP_URB_RECEIVE || \
+ DO_DUMP_PCM_SEND || DO_DUMP_PCM_RECEIVE || \
+ DO_DUMP_MIDI_SEND || DO_DUMP_MIDI_RECEIVE)
+#define CREATE_RAW_FILE 0
+
+#if DO_DEBUG_MESSAGES
+#define CHECKPOINT printk(KERN_INFO "line6usb: %s (%s:%d)\n", \
+ __func__, __FILE__, __LINE__)
+#endif
+
+#if DO_DEBUG_MESSAGES
+#define DEBUG_MESSAGES(x) (x)
+#else
+#define DEBUG_MESSAGES(x)
+#endif
+
+
+#endif
diff --git a/drivers/staging/line6/control.c b/drivers/staging/line6/control.c
new file mode 100644
index 00000000..67e23b6e
--- /dev/null
+++ b/drivers/staging/line6/control.c
@@ -0,0 +1,995 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/usb.h>
+
+#include "control.h"
+#include "driver.h"
+#include "pod.h"
+#include "usbdefs.h"
+#include "variax.h"
+
+#define DEVICE_ATTR2(_name1, _name2, _mode, _show, _store) \
+struct device_attribute dev_attr_##_name1 = __ATTR(_name2, _mode, _show, _store)
+
+#define LINE6_PARAM_R(PREFIX, prefix, type, param) \
+static ssize_t prefix##_get_##param(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return prefix##_get_param_##type(dev, buf, PREFIX##_##param); \
+}
+
+#define LINE6_PARAM_RW(PREFIX, prefix, type, param) \
+LINE6_PARAM_R(PREFIX, prefix, type, param); \
+static ssize_t prefix##_set_##param(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ return prefix##_set_param_##type(dev, buf, count, PREFIX##_##param); \
+}
+
+#define POD_PARAM_R(type, param) LINE6_PARAM_R(POD, pod, type, param)
+#define POD_PARAM_RW(type, param) LINE6_PARAM_RW(POD, pod, type, param)
+#define VARIAX_PARAM_R(type, param) LINE6_PARAM_R(VARIAX, variax, type, param)
+#define VARIAX_PARAM_RW(type, param) LINE6_PARAM_RW(VARIAX, variax, type, param)
+
+static ssize_t pod_get_param_int(struct device *dev, char *buf, int param)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ int retval = line6_dump_wait_interruptible(&pod->dumpreq);
+ if (retval < 0)
+ return retval;
+ return sprintf(buf, "%d\n", pod->prog_data.control[param]);
+}
+
+static ssize_t pod_set_param_int(struct device *dev, const char *buf,
+ size_t count, int param)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ unsigned long value;
+ int retval;
+
+ retval = strict_strtoul(buf, 10, &value);
+ if (retval)
+ return retval;
+
+ line6_pod_transmit_parameter(pod, param, value);
+ return count;
+}
+
+static ssize_t variax_get_param_int(struct device *dev, char *buf, int param)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_variax *variax = usb_get_intfdata(interface);
+ int retval = line6_dump_wait_interruptible(&variax->dumpreq);
+ if (retval < 0)
+ return retval;
+ return sprintf(buf, "%d\n", variax->model_data.control[param]);
+}
+
+static ssize_t variax_get_param_float(struct device *dev, char *buf, int param)
+{
+ /*
+ We do our own floating point handling here since at the time
+ this code was written (Jan 2006) it was highly discouraged to
+ use floating point arithmetic in the kernel. If you think that
+ this no longer applies, feel free to replace this by generic
+ floating point code.
+ */
+
+ static const int BIAS = 0x7f;
+ static const int OFFSET = 0xf;
+ static const int PRECISION = 1000;
+
+ int len = 0;
+ unsigned part_int, part_frac;
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_variax *variax = usb_get_intfdata(interface);
+ const unsigned char *p = variax->model_data.control + param;
+ int retval = line6_dump_wait_interruptible(&variax->dumpreq);
+ if (retval < 0)
+ return retval;
+
+ if ((p[0] == 0) && (p[1] == 0) && (p[2] == 0))
+ part_int = part_frac = 0;
+ else {
+ int exponent = (((p[0] & 0x7f) << 1) | (p[1] >> 7)) - BIAS;
+ unsigned mantissa = (p[1] << 8) | p[2] | 0x8000;
+ exponent -= OFFSET;
+
+ if (exponent >= 0) {
+ part_int = mantissa << exponent;
+ part_frac = 0;
+ } else {
+ part_int = mantissa >> -exponent;
+ part_frac = (mantissa << (32 + exponent)) & 0xffffffff;
+ }
+
+ part_frac =
+ (part_frac / ((1UL << 31) / (PRECISION / 2 * 10)) + 5) / 10;
+ }
+
+ len +=
+ sprintf(buf + len, "%s%d.%03d\n", ((p[0] & 0x80) ? "-" : ""),
+ part_int, part_frac);
+ return len;
+}
+
+POD_PARAM_RW(int, tweak);
+POD_PARAM_RW(int, wah_position);
+POD_PARAM_RW(int, compression_gain);
+POD_PARAM_RW(int, vol_pedal_position);
+POD_PARAM_RW(int, compression_threshold);
+POD_PARAM_RW(int, pan);
+POD_PARAM_RW(int, amp_model_setup);
+POD_PARAM_RW(int, amp_model);
+POD_PARAM_RW(int, drive);
+POD_PARAM_RW(int, bass);
+POD_PARAM_RW(int, mid);
+POD_PARAM_RW(int, lowmid);
+POD_PARAM_RW(int, treble);
+POD_PARAM_RW(int, highmid);
+POD_PARAM_RW(int, chan_vol);
+POD_PARAM_RW(int, reverb_mix);
+POD_PARAM_RW(int, effect_setup);
+POD_PARAM_RW(int, band_1_frequency);
+POD_PARAM_RW(int, presence);
+POD_PARAM_RW(int, treble__bass);
+POD_PARAM_RW(int, noise_gate_enable);
+POD_PARAM_RW(int, gate_threshold);
+POD_PARAM_RW(int, gate_decay_time);
+POD_PARAM_RW(int, stomp_enable);
+POD_PARAM_RW(int, comp_enable);
+POD_PARAM_RW(int, stomp_time);
+POD_PARAM_RW(int, delay_enable);
+POD_PARAM_RW(int, mod_param_1);
+POD_PARAM_RW(int, delay_param_1);
+POD_PARAM_RW(int, delay_param_1_note_value);
+POD_PARAM_RW(int, band_2_frequency__bass);
+POD_PARAM_RW(int, delay_param_2);
+POD_PARAM_RW(int, delay_volume_mix);
+POD_PARAM_RW(int, delay_param_3);
+POD_PARAM_RW(int, reverb_enable);
+POD_PARAM_RW(int, reverb_type);
+POD_PARAM_RW(int, reverb_decay);
+POD_PARAM_RW(int, reverb_tone);
+POD_PARAM_RW(int, reverb_pre_delay);
+POD_PARAM_RW(int, reverb_pre_post);
+POD_PARAM_RW(int, band_2_frequency);
+POD_PARAM_RW(int, band_3_frequency__bass);
+POD_PARAM_RW(int, wah_enable);
+POD_PARAM_RW(int, modulation_lo_cut);
+POD_PARAM_RW(int, delay_reverb_lo_cut);
+POD_PARAM_RW(int, volume_pedal_minimum);
+POD_PARAM_RW(int, eq_pre_post);
+POD_PARAM_RW(int, volume_pre_post);
+POD_PARAM_RW(int, di_model);
+POD_PARAM_RW(int, di_delay);
+POD_PARAM_RW(int, mod_enable);
+POD_PARAM_RW(int, mod_param_1_note_value);
+POD_PARAM_RW(int, mod_param_2);
+POD_PARAM_RW(int, mod_param_3);
+POD_PARAM_RW(int, mod_param_4);
+POD_PARAM_RW(int, mod_param_5);
+POD_PARAM_RW(int, mod_volume_mix);
+POD_PARAM_RW(int, mod_pre_post);
+POD_PARAM_RW(int, modulation_model);
+POD_PARAM_RW(int, band_3_frequency);
+POD_PARAM_RW(int, band_4_frequency__bass);
+POD_PARAM_RW(int, mod_param_1_double_precision);
+POD_PARAM_RW(int, delay_param_1_double_precision);
+POD_PARAM_RW(int, eq_enable);
+POD_PARAM_RW(int, tap);
+POD_PARAM_RW(int, volume_tweak_pedal_assign);
+POD_PARAM_RW(int, band_5_frequency);
+POD_PARAM_RW(int, tuner);
+POD_PARAM_RW(int, mic_selection);
+POD_PARAM_RW(int, cabinet_model);
+POD_PARAM_RW(int, stomp_model);
+POD_PARAM_RW(int, roomlevel);
+POD_PARAM_RW(int, band_4_frequency);
+POD_PARAM_RW(int, band_6_frequency);
+POD_PARAM_RW(int, stomp_param_1_note_value);
+POD_PARAM_RW(int, stomp_param_2);
+POD_PARAM_RW(int, stomp_param_3);
+POD_PARAM_RW(int, stomp_param_4);
+POD_PARAM_RW(int, stomp_param_5);
+POD_PARAM_RW(int, stomp_param_6);
+POD_PARAM_RW(int, amp_switch_select);
+POD_PARAM_RW(int, delay_param_4);
+POD_PARAM_RW(int, delay_param_5);
+POD_PARAM_RW(int, delay_pre_post);
+POD_PARAM_RW(int, delay_model);
+POD_PARAM_RW(int, delay_verb_model);
+POD_PARAM_RW(int, tempo_msb);
+POD_PARAM_RW(int, tempo_lsb);
+POD_PARAM_RW(int, wah_model);
+POD_PARAM_RW(int, bypass_volume);
+POD_PARAM_RW(int, fx_loop_on_off);
+POD_PARAM_RW(int, tweak_param_select);
+POD_PARAM_RW(int, amp1_engage);
+POD_PARAM_RW(int, band_1_gain);
+POD_PARAM_RW(int, band_2_gain__bass);
+POD_PARAM_RW(int, band_2_gain);
+POD_PARAM_RW(int, band_3_gain__bass);
+POD_PARAM_RW(int, band_3_gain);
+POD_PARAM_RW(int, band_4_gain__bass);
+POD_PARAM_RW(int, band_5_gain__bass);
+POD_PARAM_RW(int, band_4_gain);
+POD_PARAM_RW(int, band_6_gain__bass);
+VARIAX_PARAM_R(int, body);
+VARIAX_PARAM_R(int, pickup1_enable);
+VARIAX_PARAM_R(int, pickup1_type);
+VARIAX_PARAM_R(float, pickup1_position);
+VARIAX_PARAM_R(float, pickup1_angle);
+VARIAX_PARAM_R(float, pickup1_level);
+VARIAX_PARAM_R(int, pickup2_enable);
+VARIAX_PARAM_R(int, pickup2_type);
+VARIAX_PARAM_R(float, pickup2_position);
+VARIAX_PARAM_R(float, pickup2_angle);
+VARIAX_PARAM_R(float, pickup2_level);
+VARIAX_PARAM_R(int, pickup_phase);
+VARIAX_PARAM_R(float, capacitance);
+VARIAX_PARAM_R(float, tone_resistance);
+VARIAX_PARAM_R(float, volume_resistance);
+VARIAX_PARAM_R(int, taper);
+VARIAX_PARAM_R(float, tone_dump);
+VARIAX_PARAM_R(int, save_tone);
+VARIAX_PARAM_R(float, volume_dump);
+VARIAX_PARAM_R(int, tuning_enable);
+VARIAX_PARAM_R(int, tuning6);
+VARIAX_PARAM_R(int, tuning5);
+VARIAX_PARAM_R(int, tuning4);
+VARIAX_PARAM_R(int, tuning3);
+VARIAX_PARAM_R(int, tuning2);
+VARIAX_PARAM_R(int, tuning1);
+VARIAX_PARAM_R(float, detune6);
+VARIAX_PARAM_R(float, detune5);
+VARIAX_PARAM_R(float, detune4);
+VARIAX_PARAM_R(float, detune3);
+VARIAX_PARAM_R(float, detune2);
+VARIAX_PARAM_R(float, detune1);
+VARIAX_PARAM_R(float, mix6);
+VARIAX_PARAM_R(float, mix5);
+VARIAX_PARAM_R(float, mix4);
+VARIAX_PARAM_R(float, mix3);
+VARIAX_PARAM_R(float, mix2);
+VARIAX_PARAM_R(float, mix1);
+VARIAX_PARAM_R(int, pickup_wiring);
+
+static DEVICE_ATTR(tweak, S_IWUSR | S_IRUGO, pod_get_tweak, pod_set_tweak);
+static DEVICE_ATTR(wah_position, S_IWUSR | S_IRUGO, pod_get_wah_position,
+ pod_set_wah_position);
+static DEVICE_ATTR(compression_gain, S_IWUSR | S_IRUGO,
+ pod_get_compression_gain, pod_set_compression_gain);
+static DEVICE_ATTR(vol_pedal_position, S_IWUSR | S_IRUGO,
+ pod_get_vol_pedal_position, pod_set_vol_pedal_position);
+static DEVICE_ATTR(compression_threshold, S_IWUSR | S_IRUGO,
+ pod_get_compression_threshold,
+ pod_set_compression_threshold);
+static DEVICE_ATTR(pan, S_IWUSR | S_IRUGO, pod_get_pan, pod_set_pan);
+static DEVICE_ATTR(amp_model_setup, S_IWUSR | S_IRUGO, pod_get_amp_model_setup,
+ pod_set_amp_model_setup);
+static DEVICE_ATTR(amp_model, S_IWUSR | S_IRUGO, pod_get_amp_model,
+ pod_set_amp_model);
+static DEVICE_ATTR(drive, S_IWUSR | S_IRUGO, pod_get_drive, pod_set_drive);
+static DEVICE_ATTR(bass, S_IWUSR | S_IRUGO, pod_get_bass, pod_set_bass);
+static DEVICE_ATTR(mid, S_IWUSR | S_IRUGO, pod_get_mid, pod_set_mid);
+static DEVICE_ATTR(lowmid, S_IWUSR | S_IRUGO, pod_get_lowmid, pod_set_lowmid);
+static DEVICE_ATTR(treble, S_IWUSR | S_IRUGO, pod_get_treble, pod_set_treble);
+static DEVICE_ATTR(highmid, S_IWUSR | S_IRUGO, pod_get_highmid,
+ pod_set_highmid);
+static DEVICE_ATTR(chan_vol, S_IWUSR | S_IRUGO, pod_get_chan_vol,
+ pod_set_chan_vol);
+static DEVICE_ATTR(reverb_mix, S_IWUSR | S_IRUGO, pod_get_reverb_mix,
+ pod_set_reverb_mix);
+static DEVICE_ATTR(effect_setup, S_IWUSR | S_IRUGO, pod_get_effect_setup,
+ pod_set_effect_setup);
+static DEVICE_ATTR(band_1_frequency, S_IWUSR | S_IRUGO,
+ pod_get_band_1_frequency, pod_set_band_1_frequency);
+static DEVICE_ATTR(presence, S_IWUSR | S_IRUGO, pod_get_presence,
+ pod_set_presence);
+static DEVICE_ATTR2(treble__bass, treble, S_IWUSR | S_IRUGO,
+ pod_get_treble__bass, pod_set_treble__bass);
+static DEVICE_ATTR(noise_gate_enable, S_IWUSR | S_IRUGO,
+ pod_get_noise_gate_enable, pod_set_noise_gate_enable);
+static DEVICE_ATTR(gate_threshold, S_IWUSR | S_IRUGO, pod_get_gate_threshold,
+ pod_set_gate_threshold);
+static DEVICE_ATTR(gate_decay_time, S_IWUSR | S_IRUGO, pod_get_gate_decay_time,
+ pod_set_gate_decay_time);
+static DEVICE_ATTR(stomp_enable, S_IWUSR | S_IRUGO, pod_get_stomp_enable,
+ pod_set_stomp_enable);
+static DEVICE_ATTR(comp_enable, S_IWUSR | S_IRUGO, pod_get_comp_enable,
+ pod_set_comp_enable);
+static DEVICE_ATTR(stomp_time, S_IWUSR | S_IRUGO, pod_get_stomp_time,
+ pod_set_stomp_time);
+static DEVICE_ATTR(delay_enable, S_IWUSR | S_IRUGO, pod_get_delay_enable,
+ pod_set_delay_enable);
+static DEVICE_ATTR(mod_param_1, S_IWUSR | S_IRUGO, pod_get_mod_param_1,
+ pod_set_mod_param_1);
+static DEVICE_ATTR(delay_param_1, S_IWUSR | S_IRUGO, pod_get_delay_param_1,
+ pod_set_delay_param_1);
+static DEVICE_ATTR(delay_param_1_note_value, S_IWUSR | S_IRUGO,
+ pod_get_delay_param_1_note_value,
+ pod_set_delay_param_1_note_value);
+static DEVICE_ATTR2(band_2_frequency__bass, band_2_frequency, S_IWUSR | S_IRUGO,
+ pod_get_band_2_frequency__bass,
+ pod_set_band_2_frequency__bass);
+static DEVICE_ATTR(delay_param_2, S_IWUSR | S_IRUGO, pod_get_delay_param_2,
+ pod_set_delay_param_2);
+static DEVICE_ATTR(delay_volume_mix, S_IWUSR | S_IRUGO,
+ pod_get_delay_volume_mix, pod_set_delay_volume_mix);
+static DEVICE_ATTR(delay_param_3, S_IWUSR | S_IRUGO, pod_get_delay_param_3,
+ pod_set_delay_param_3);
+static DEVICE_ATTR(reverb_enable, S_IWUSR | S_IRUGO, pod_get_reverb_enable,
+ pod_set_reverb_enable);
+static DEVICE_ATTR(reverb_type, S_IWUSR | S_IRUGO, pod_get_reverb_type,
+ pod_set_reverb_type);
+static DEVICE_ATTR(reverb_decay, S_IWUSR | S_IRUGO, pod_get_reverb_decay,
+ pod_set_reverb_decay);
+static DEVICE_ATTR(reverb_tone, S_IWUSR | S_IRUGO, pod_get_reverb_tone,
+ pod_set_reverb_tone);
+static DEVICE_ATTR(reverb_pre_delay, S_IWUSR | S_IRUGO,
+ pod_get_reverb_pre_delay, pod_set_reverb_pre_delay);
+static DEVICE_ATTR(reverb_pre_post, S_IWUSR | S_IRUGO, pod_get_reverb_pre_post,
+ pod_set_reverb_pre_post);
+static DEVICE_ATTR(band_2_frequency, S_IWUSR | S_IRUGO,
+ pod_get_band_2_frequency, pod_set_band_2_frequency);
+static DEVICE_ATTR2(band_3_frequency__bass, band_3_frequency, S_IWUSR | S_IRUGO,
+ pod_get_band_3_frequency__bass,
+ pod_set_band_3_frequency__bass);
+static DEVICE_ATTR(wah_enable, S_IWUSR | S_IRUGO, pod_get_wah_enable,
+ pod_set_wah_enable);
+static DEVICE_ATTR(modulation_lo_cut, S_IWUSR | S_IRUGO,
+ pod_get_modulation_lo_cut, pod_set_modulation_lo_cut);
+static DEVICE_ATTR(delay_reverb_lo_cut, S_IWUSR | S_IRUGO,
+ pod_get_delay_reverb_lo_cut, pod_set_delay_reverb_lo_cut);
+static DEVICE_ATTR(volume_pedal_minimum, S_IWUSR | S_IRUGO,
+ pod_get_volume_pedal_minimum, pod_set_volume_pedal_minimum);
+static DEVICE_ATTR(eq_pre_post, S_IWUSR | S_IRUGO, pod_get_eq_pre_post,
+ pod_set_eq_pre_post);
+static DEVICE_ATTR(volume_pre_post, S_IWUSR | S_IRUGO, pod_get_volume_pre_post,
+ pod_set_volume_pre_post);
+static DEVICE_ATTR(di_model, S_IWUSR | S_IRUGO, pod_get_di_model,
+ pod_set_di_model);
+static DEVICE_ATTR(di_delay, S_IWUSR | S_IRUGO, pod_get_di_delay,
+ pod_set_di_delay);
+static DEVICE_ATTR(mod_enable, S_IWUSR | S_IRUGO, pod_get_mod_enable,
+ pod_set_mod_enable);
+static DEVICE_ATTR(mod_param_1_note_value, S_IWUSR | S_IRUGO,
+ pod_get_mod_param_1_note_value,
+ pod_set_mod_param_1_note_value);
+static DEVICE_ATTR(mod_param_2, S_IWUSR | S_IRUGO, pod_get_mod_param_2,
+ pod_set_mod_param_2);
+static DEVICE_ATTR(mod_param_3, S_IWUSR | S_IRUGO, pod_get_mod_param_3,
+ pod_set_mod_param_3);
+static DEVICE_ATTR(mod_param_4, S_IWUSR | S_IRUGO, pod_get_mod_param_4,
+ pod_set_mod_param_4);
+static DEVICE_ATTR(mod_param_5, S_IWUSR | S_IRUGO, pod_get_mod_param_5,
+ pod_set_mod_param_5);
+static DEVICE_ATTR(mod_volume_mix, S_IWUSR | S_IRUGO, pod_get_mod_volume_mix,
+ pod_set_mod_volume_mix);
+static DEVICE_ATTR(mod_pre_post, S_IWUSR | S_IRUGO, pod_get_mod_pre_post,
+ pod_set_mod_pre_post);
+static DEVICE_ATTR(modulation_model, S_IWUSR | S_IRUGO,
+ pod_get_modulation_model, pod_set_modulation_model);
+static DEVICE_ATTR(band_3_frequency, S_IWUSR | S_IRUGO,
+ pod_get_band_3_frequency, pod_set_band_3_frequency);
+static DEVICE_ATTR2(band_4_frequency__bass, band_4_frequency, S_IWUSR | S_IRUGO,
+ pod_get_band_4_frequency__bass,
+ pod_set_band_4_frequency__bass);
+static DEVICE_ATTR(mod_param_1_double_precision, S_IWUSR | S_IRUGO,
+ pod_get_mod_param_1_double_precision,
+ pod_set_mod_param_1_double_precision);
+static DEVICE_ATTR(delay_param_1_double_precision, S_IWUSR | S_IRUGO,
+ pod_get_delay_param_1_double_precision,
+ pod_set_delay_param_1_double_precision);
+static DEVICE_ATTR(eq_enable, S_IWUSR | S_IRUGO, pod_get_eq_enable,
+ pod_set_eq_enable);
+static DEVICE_ATTR(tap, S_IWUSR | S_IRUGO, pod_get_tap, pod_set_tap);
+static DEVICE_ATTR(volume_tweak_pedal_assign, S_IWUSR | S_IRUGO,
+ pod_get_volume_tweak_pedal_assign,
+ pod_set_volume_tweak_pedal_assign);
+static DEVICE_ATTR(band_5_frequency, S_IWUSR | S_IRUGO,
+ pod_get_band_5_frequency, pod_set_band_5_frequency);
+static DEVICE_ATTR(tuner, S_IWUSR | S_IRUGO, pod_get_tuner, pod_set_tuner);
+static DEVICE_ATTR(mic_selection, S_IWUSR | S_IRUGO, pod_get_mic_selection,
+ pod_set_mic_selection);
+static DEVICE_ATTR(cabinet_model, S_IWUSR | S_IRUGO, pod_get_cabinet_model,
+ pod_set_cabinet_model);
+static DEVICE_ATTR(stomp_model, S_IWUSR | S_IRUGO, pod_get_stomp_model,
+ pod_set_stomp_model);
+static DEVICE_ATTR(roomlevel, S_IWUSR | S_IRUGO, pod_get_roomlevel,
+ pod_set_roomlevel);
+static DEVICE_ATTR(band_4_frequency, S_IWUSR | S_IRUGO,
+ pod_get_band_4_frequency, pod_set_band_4_frequency);
+static DEVICE_ATTR(band_6_frequency, S_IWUSR | S_IRUGO,
+ pod_get_band_6_frequency, pod_set_band_6_frequency);
+static DEVICE_ATTR(stomp_param_1_note_value, S_IWUSR | S_IRUGO,
+ pod_get_stomp_param_1_note_value,
+ pod_set_stomp_param_1_note_value);
+static DEVICE_ATTR(stomp_param_2, S_IWUSR | S_IRUGO, pod_get_stomp_param_2,
+ pod_set_stomp_param_2);
+static DEVICE_ATTR(stomp_param_3, S_IWUSR | S_IRUGO, pod_get_stomp_param_3,
+ pod_set_stomp_param_3);
+static DEVICE_ATTR(stomp_param_4, S_IWUSR | S_IRUGO, pod_get_stomp_param_4,
+ pod_set_stomp_param_4);
+static DEVICE_ATTR(stomp_param_5, S_IWUSR | S_IRUGO, pod_get_stomp_param_5,
+ pod_set_stomp_param_5);
+static DEVICE_ATTR(stomp_param_6, S_IWUSR | S_IRUGO, pod_get_stomp_param_6,
+ pod_set_stomp_param_6);
+static DEVICE_ATTR(amp_switch_select, S_IWUSR | S_IRUGO,
+ pod_get_amp_switch_select, pod_set_amp_switch_select);
+static DEVICE_ATTR(delay_param_4, S_IWUSR | S_IRUGO, pod_get_delay_param_4,
+ pod_set_delay_param_4);
+static DEVICE_ATTR(delay_param_5, S_IWUSR | S_IRUGO, pod_get_delay_param_5,
+ pod_set_delay_param_5);
+static DEVICE_ATTR(delay_pre_post, S_IWUSR | S_IRUGO, pod_get_delay_pre_post,
+ pod_set_delay_pre_post);
+static DEVICE_ATTR(delay_model, S_IWUSR | S_IRUGO, pod_get_delay_model,
+ pod_set_delay_model);
+static DEVICE_ATTR(delay_verb_model, S_IWUSR | S_IRUGO,
+ pod_get_delay_verb_model, pod_set_delay_verb_model);
+static DEVICE_ATTR(tempo_msb, S_IWUSR | S_IRUGO, pod_get_tempo_msb,
+ pod_set_tempo_msb);
+static DEVICE_ATTR(tempo_lsb, S_IWUSR | S_IRUGO, pod_get_tempo_lsb,
+ pod_set_tempo_lsb);
+static DEVICE_ATTR(wah_model, S_IWUSR | S_IRUGO, pod_get_wah_model,
+ pod_set_wah_model);
+static DEVICE_ATTR(bypass_volume, S_IWUSR | S_IRUGO, pod_get_bypass_volume,
+ pod_set_bypass_volume);
+static DEVICE_ATTR(fx_loop_on_off, S_IWUSR | S_IRUGO, pod_get_fx_loop_on_off,
+ pod_set_fx_loop_on_off);
+static DEVICE_ATTR(tweak_param_select, S_IWUSR | S_IRUGO,
+ pod_get_tweak_param_select, pod_set_tweak_param_select);
+static DEVICE_ATTR(amp1_engage, S_IWUSR | S_IRUGO, pod_get_amp1_engage,
+ pod_set_amp1_engage);
+static DEVICE_ATTR(band_1_gain, S_IWUSR | S_IRUGO, pod_get_band_1_gain,
+ pod_set_band_1_gain);
+static DEVICE_ATTR2(band_2_gain__bass, band_2_gain, S_IWUSR | S_IRUGO,
+ pod_get_band_2_gain__bass, pod_set_band_2_gain__bass);
+static DEVICE_ATTR(band_2_gain, S_IWUSR | S_IRUGO, pod_get_band_2_gain,
+ pod_set_band_2_gain);
+static DEVICE_ATTR2(band_3_gain__bass, band_3_gain, S_IWUSR | S_IRUGO,
+ pod_get_band_3_gain__bass, pod_set_band_3_gain__bass);
+static DEVICE_ATTR(band_3_gain, S_IWUSR | S_IRUGO, pod_get_band_3_gain,
+ pod_set_band_3_gain);
+static DEVICE_ATTR2(band_4_gain__bass, band_4_gain, S_IWUSR | S_IRUGO,
+ pod_get_band_4_gain__bass, pod_set_band_4_gain__bass);
+static DEVICE_ATTR2(band_5_gain__bass, band_5_gain, S_IWUSR | S_IRUGO,
+ pod_get_band_5_gain__bass, pod_set_band_5_gain__bass);
+static DEVICE_ATTR(band_4_gain, S_IWUSR | S_IRUGO, pod_get_band_4_gain,
+ pod_set_band_4_gain);
+static DEVICE_ATTR2(band_6_gain__bass, band_6_gain, S_IWUSR | S_IRUGO,
+ pod_get_band_6_gain__bass, pod_set_band_6_gain__bass);
+static DEVICE_ATTR(body, S_IRUGO, variax_get_body, line6_nop_write);
+static DEVICE_ATTR(pickup1_enable, S_IRUGO, variax_get_pickup1_enable,
+ line6_nop_write);
+static DEVICE_ATTR(pickup1_type, S_IRUGO, variax_get_pickup1_type,
+ line6_nop_write);
+static DEVICE_ATTR(pickup1_position, S_IRUGO, variax_get_pickup1_position,
+ line6_nop_write);
+static DEVICE_ATTR(pickup1_angle, S_IRUGO, variax_get_pickup1_angle,
+ line6_nop_write);
+static DEVICE_ATTR(pickup1_level, S_IRUGO, variax_get_pickup1_level,
+ line6_nop_write);
+static DEVICE_ATTR(pickup2_enable, S_IRUGO, variax_get_pickup2_enable,
+ line6_nop_write);
+static DEVICE_ATTR(pickup2_type, S_IRUGO, variax_get_pickup2_type,
+ line6_nop_write);
+static DEVICE_ATTR(pickup2_position, S_IRUGO, variax_get_pickup2_position,
+ line6_nop_write);
+static DEVICE_ATTR(pickup2_angle, S_IRUGO, variax_get_pickup2_angle,
+ line6_nop_write);
+static DEVICE_ATTR(pickup2_level, S_IRUGO, variax_get_pickup2_level,
+ line6_nop_write);
+static DEVICE_ATTR(pickup_phase, S_IRUGO, variax_get_pickup_phase,
+ line6_nop_write);
+static DEVICE_ATTR(capacitance, S_IRUGO, variax_get_capacitance,
+ line6_nop_write);
+static DEVICE_ATTR(tone_resistance, S_IRUGO, variax_get_tone_resistance,
+ line6_nop_write);
+static DEVICE_ATTR(volume_resistance, S_IRUGO, variax_get_volume_resistance,
+ line6_nop_write);
+static DEVICE_ATTR(taper, S_IRUGO, variax_get_taper, line6_nop_write);
+static DEVICE_ATTR(tone_dump, S_IRUGO, variax_get_tone_dump, line6_nop_write);
+static DEVICE_ATTR(save_tone, S_IRUGO, variax_get_save_tone, line6_nop_write);
+static DEVICE_ATTR(volume_dump, S_IRUGO, variax_get_volume_dump,
+ line6_nop_write);
+static DEVICE_ATTR(tuning_enable, S_IRUGO, variax_get_tuning_enable,
+ line6_nop_write);
+static DEVICE_ATTR(tuning6, S_IRUGO, variax_get_tuning6, line6_nop_write);
+static DEVICE_ATTR(tuning5, S_IRUGO, variax_get_tuning5, line6_nop_write);
+static DEVICE_ATTR(tuning4, S_IRUGO, variax_get_tuning4, line6_nop_write);
+static DEVICE_ATTR(tuning3, S_IRUGO, variax_get_tuning3, line6_nop_write);
+static DEVICE_ATTR(tuning2, S_IRUGO, variax_get_tuning2, line6_nop_write);
+static DEVICE_ATTR(tuning1, S_IRUGO, variax_get_tuning1, line6_nop_write);
+static DEVICE_ATTR(detune6, S_IRUGO, variax_get_detune6, line6_nop_write);
+static DEVICE_ATTR(detune5, S_IRUGO, variax_get_detune5, line6_nop_write);
+static DEVICE_ATTR(detune4, S_IRUGO, variax_get_detune4, line6_nop_write);
+static DEVICE_ATTR(detune3, S_IRUGO, variax_get_detune3, line6_nop_write);
+static DEVICE_ATTR(detune2, S_IRUGO, variax_get_detune2, line6_nop_write);
+static DEVICE_ATTR(detune1, S_IRUGO, variax_get_detune1, line6_nop_write);
+static DEVICE_ATTR(mix6, S_IRUGO, variax_get_mix6, line6_nop_write);
+static DEVICE_ATTR(mix5, S_IRUGO, variax_get_mix5, line6_nop_write);
+static DEVICE_ATTR(mix4, S_IRUGO, variax_get_mix4, line6_nop_write);
+static DEVICE_ATTR(mix3, S_IRUGO, variax_get_mix3, line6_nop_write);
+static DEVICE_ATTR(mix2, S_IRUGO, variax_get_mix2, line6_nop_write);
+static DEVICE_ATTR(mix1, S_IRUGO, variax_get_mix1, line6_nop_write);
+static DEVICE_ATTR(pickup_wiring, S_IRUGO, variax_get_pickup_wiring,
+ line6_nop_write);
+
+int line6_pod_create_files(int firmware, int type, struct device *dev)
+{
+ int err;
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tweak));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_wah_position));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_compression_gain));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_vol_pedal_position));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_compression_threshold));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pan));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_amp_model_setup));
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_amp_model));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_drive));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_bass));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mid));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_lowmid));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_treble));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_highmid));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_chan_vol));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_mix));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_effect_setup));
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_1_frequency));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_presence));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_treble__bass));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_noise_gate_enable));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_gate_threshold));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_gate_decay_time));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_enable));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_comp_enable));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_time));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_delay_enable));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_1));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_1));
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_delay_param_1_note_value));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_2_frequency__bass));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_2));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_delay_volume_mix));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_3));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_enable));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_type));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_decay));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_tone));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_reverb_pre_delay));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_reverb_pre_post));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_2_frequency));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_3_frequency__bass));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_wah_enable));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_modulation_lo_cut));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_delay_reverb_lo_cut));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_volume_pedal_minimum));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_eq_pre_post));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_volume_pre_post));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_di_model));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_di_delay));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mod_enable));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_1_note_value));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_2));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_3));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_4));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_5));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mod_volume_mix));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mod_pre_post));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_modulation_model));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_3_frequency));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_4_frequency__bass));
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_mod_param_1_double_precision));
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_delay_param_1_double_precision));
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_eq_enable));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tap));
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_volume_tweak_pedal_assign));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_5_frequency));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuner));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mic_selection));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_cabinet_model));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_model));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_roomlevel));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_4_frequency));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_6_frequency));
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_stomp_param_1_note_value));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_2));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_3));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_4));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_5));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_6));
+ if ((type & (LINE6_BITS_LIVE)) != 0)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_amp_switch_select));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_4));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_5));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_delay_pre_post));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_delay_model));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_delay_verb_model));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tempo_msb));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tempo_lsb));
+ if (firmware >= 300)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_wah_model));
+ if (firmware >= 214)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_bypass_volume));
+ if ((type & (LINE6_BITS_PRO)) != 0)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_fx_loop_on_off));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tweak_param_select));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_amp1_engage));
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file(dev, &dev_attr_band_1_gain));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_2_gain__bass));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_2_gain));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_3_gain__bass));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_3_gain));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_4_gain__bass));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_5_gain__bass));
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_4_gain));
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ CHECK_RETURN(device_create_file
+ (dev, &dev_attr_band_6_gain__bass));
+ return 0;
+}
+
+void line6_pod_remove_files(int firmware, int type, struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_tweak);
+ device_remove_file(dev, &dev_attr_wah_position);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_compression_gain);
+ device_remove_file(dev, &dev_attr_vol_pedal_position);
+ device_remove_file(dev, &dev_attr_compression_threshold);
+ device_remove_file(dev, &dev_attr_pan);
+ device_remove_file(dev, &dev_attr_amp_model_setup);
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_amp_model);
+ device_remove_file(dev, &dev_attr_drive);
+ device_remove_file(dev, &dev_attr_bass);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_mid);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_lowmid);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_treble);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_highmid);
+ device_remove_file(dev, &dev_attr_chan_vol);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_reverb_mix);
+ device_remove_file(dev, &dev_attr_effect_setup);
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_1_frequency);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_presence);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_treble__bass);
+ device_remove_file(dev, &dev_attr_noise_gate_enable);
+ device_remove_file(dev, &dev_attr_gate_threshold);
+ device_remove_file(dev, &dev_attr_gate_decay_time);
+ device_remove_file(dev, &dev_attr_stomp_enable);
+ device_remove_file(dev, &dev_attr_comp_enable);
+ device_remove_file(dev, &dev_attr_stomp_time);
+ device_remove_file(dev, &dev_attr_delay_enable);
+ device_remove_file(dev, &dev_attr_mod_param_1);
+ device_remove_file(dev, &dev_attr_delay_param_1);
+ device_remove_file(dev, &dev_attr_delay_param_1_note_value);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev,
+ &dev_attr_band_2_frequency__bass);
+ device_remove_file(dev, &dev_attr_delay_param_2);
+ device_remove_file(dev, &dev_attr_delay_volume_mix);
+ device_remove_file(dev, &dev_attr_delay_param_3);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_reverb_enable);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_reverb_type);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_reverb_decay);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_reverb_tone);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_reverb_pre_delay);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_reverb_pre_post);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_2_frequency);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev,
+ &dev_attr_band_3_frequency__bass);
+ device_remove_file(dev, &dev_attr_wah_enable);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_modulation_lo_cut);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_delay_reverb_lo_cut);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_volume_pedal_minimum);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_eq_pre_post);
+ device_remove_file(dev, &dev_attr_volume_pre_post);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_di_model);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_di_delay);
+ device_remove_file(dev, &dev_attr_mod_enable);
+ device_remove_file(dev, &dev_attr_mod_param_1_note_value);
+ device_remove_file(dev, &dev_attr_mod_param_2);
+ device_remove_file(dev, &dev_attr_mod_param_3);
+ device_remove_file(dev, &dev_attr_mod_param_4);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_mod_param_5);
+ device_remove_file(dev, &dev_attr_mod_volume_mix);
+ device_remove_file(dev, &dev_attr_mod_pre_post);
+ device_remove_file(dev, &dev_attr_modulation_model);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_3_frequency);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev,
+ &dev_attr_band_4_frequency__bass);
+ device_remove_file(dev, &dev_attr_mod_param_1_double_precision);
+ device_remove_file(dev, &dev_attr_delay_param_1_double_precision);
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_eq_enable);
+ device_remove_file(dev, &dev_attr_tap);
+ device_remove_file(dev, &dev_attr_volume_tweak_pedal_assign);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_5_frequency);
+ device_remove_file(dev, &dev_attr_tuner);
+ device_remove_file(dev, &dev_attr_mic_selection);
+ device_remove_file(dev, &dev_attr_cabinet_model);
+ device_remove_file(dev, &dev_attr_stomp_model);
+ device_remove_file(dev, &dev_attr_roomlevel);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_4_frequency);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_6_frequency);
+ device_remove_file(dev, &dev_attr_stomp_param_1_note_value);
+ device_remove_file(dev, &dev_attr_stomp_param_2);
+ device_remove_file(dev, &dev_attr_stomp_param_3);
+ device_remove_file(dev, &dev_attr_stomp_param_4);
+ device_remove_file(dev, &dev_attr_stomp_param_5);
+ device_remove_file(dev, &dev_attr_stomp_param_6);
+ if ((type & (LINE6_BITS_LIVE)) != 0)
+ device_remove_file(dev, &dev_attr_amp_switch_select);
+ device_remove_file(dev, &dev_attr_delay_param_4);
+ device_remove_file(dev, &dev_attr_delay_param_5);
+ device_remove_file(dev, &dev_attr_delay_pre_post);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_delay_model);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ device_remove_file(dev, &dev_attr_delay_verb_model);
+ device_remove_file(dev, &dev_attr_tempo_msb);
+ device_remove_file(dev, &dev_attr_tempo_lsb);
+ if (firmware >= 300)
+ device_remove_file(dev, &dev_attr_wah_model);
+ if (firmware >= 214)
+ device_remove_file(dev, &dev_attr_bypass_volume);
+ if ((type & (LINE6_BITS_PRO)) != 0)
+ device_remove_file(dev, &dev_attr_fx_loop_on_off);
+ device_remove_file(dev, &dev_attr_tweak_param_select);
+ device_remove_file(dev, &dev_attr_amp1_engage);
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_1_gain);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_2_gain__bass);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_2_gain);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_3_gain__bass);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_3_gain);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_4_gain__bass);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_5_gain__bass);
+ if ((type & (LINE6_BITS_PODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_4_gain);
+ if ((type & (LINE6_BITS_BASSPODXTALL)) != 0)
+ if (firmware >= 200)
+ device_remove_file(dev, &dev_attr_band_6_gain__bass);
+}
+
+int line6_variax_create_files(int firmware, int type, struct device *dev)
+{
+ int err;
+ CHECK_RETURN(device_create_file(dev, &dev_attr_body));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_enable));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_type));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_position));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_angle));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_level));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_enable));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_type));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_position));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_angle));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_level));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_phase));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_capacitance));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tone_resistance));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_volume_resistance));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_taper));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tone_dump));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_save_tone));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_volume_dump));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuning_enable));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuning6));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuning5));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuning4));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuning3));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuning2));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuning1));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_detune6));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_detune5));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_detune4));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_detune3));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_detune2));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_detune1));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mix6));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mix5));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mix4));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mix3));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mix2));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_mix1));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_wiring));
+ return 0;
+}
+
+void line6_variax_remove_files(int firmware, int type, struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_body);
+ device_remove_file(dev, &dev_attr_pickup1_enable);
+ device_remove_file(dev, &dev_attr_pickup1_type);
+ device_remove_file(dev, &dev_attr_pickup1_position);
+ device_remove_file(dev, &dev_attr_pickup1_angle);
+ device_remove_file(dev, &dev_attr_pickup1_level);
+ device_remove_file(dev, &dev_attr_pickup2_enable);
+ device_remove_file(dev, &dev_attr_pickup2_type);
+ device_remove_file(dev, &dev_attr_pickup2_position);
+ device_remove_file(dev, &dev_attr_pickup2_angle);
+ device_remove_file(dev, &dev_attr_pickup2_level);
+ device_remove_file(dev, &dev_attr_pickup_phase);
+ device_remove_file(dev, &dev_attr_capacitance);
+ device_remove_file(dev, &dev_attr_tone_resistance);
+ device_remove_file(dev, &dev_attr_volume_resistance);
+ device_remove_file(dev, &dev_attr_taper);
+ device_remove_file(dev, &dev_attr_tone_dump);
+ device_remove_file(dev, &dev_attr_save_tone);
+ device_remove_file(dev, &dev_attr_volume_dump);
+ device_remove_file(dev, &dev_attr_tuning_enable);
+ device_remove_file(dev, &dev_attr_tuning6);
+ device_remove_file(dev, &dev_attr_tuning5);
+ device_remove_file(dev, &dev_attr_tuning4);
+ device_remove_file(dev, &dev_attr_tuning3);
+ device_remove_file(dev, &dev_attr_tuning2);
+ device_remove_file(dev, &dev_attr_tuning1);
+ device_remove_file(dev, &dev_attr_detune6);
+ device_remove_file(dev, &dev_attr_detune5);
+ device_remove_file(dev, &dev_attr_detune4);
+ device_remove_file(dev, &dev_attr_detune3);
+ device_remove_file(dev, &dev_attr_detune2);
+ device_remove_file(dev, &dev_attr_detune1);
+ device_remove_file(dev, &dev_attr_mix6);
+ device_remove_file(dev, &dev_attr_mix5);
+ device_remove_file(dev, &dev_attr_mix4);
+ device_remove_file(dev, &dev_attr_mix3);
+ device_remove_file(dev, &dev_attr_mix2);
+ device_remove_file(dev, &dev_attr_mix1);
+ device_remove_file(dev, &dev_attr_pickup_wiring);
+}
diff --git a/drivers/staging/line6/control.h b/drivers/staging/line6/control.h
new file mode 100644
index 00000000..e4c5d2ce
--- /dev/null
+++ b/drivers/staging/line6/control.h
@@ -0,0 +1,195 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef LINE6_CONTROL_H
+#define LINE6_CONTROL_H
+
+/**
+ List of PODxt Pro controls.
+ See Appendix C of the "PODxt (Pro) Pilot's Handbook" by Line6.
+ Comments after the number refer to the PODxt Pro firmware version required
+ for this feature.
+
+ Please *don't* reformat this file since "control.c" is created automatically
+ from "control.h", and this process depends on the exact formatting of the
+ code and the comments below!
+*/
+
+/* *INDENT-OFF* */
+
+enum {
+ POD_tweak = 1,
+ POD_wah_position = 4,
+ POD_compression_gain = 5, /* device: LINE6_BITS_PODXTALL */
+ POD_vol_pedal_position = 7,
+ POD_compression_threshold = 9,
+ POD_pan = 10,
+ POD_amp_model_setup = 11,
+ POD_amp_model = 12, /* firmware: 2.0 */
+ POD_drive = 13,
+ POD_bass = 14,
+ POD_mid = 15, /* device: LINE6_BITS_PODXTALL */
+ POD_lowmid = 15, /* device: LINE6_BITS_BASSPODXTALL */
+ POD_treble = 16, /* device: LINE6_BITS_PODXTALL */
+ POD_highmid = 16, /* device: LINE6_BITS_BASSPODXTALL */
+ POD_chan_vol = 17,
+ POD_reverb_mix = 18, /* device: LINE6_BITS_PODXTALL */
+ POD_effect_setup = 19,
+ POD_band_1_frequency = 20, /* firmware: 2.0 */
+ POD_presence = 21, /* device: LINE6_BITS_PODXTALL */
+ POD_treble__bass = 21, /* device: LINE6_BITS_BASSPODXTALL */
+ POD_noise_gate_enable = 22,
+ POD_gate_threshold = 23,
+ POD_gate_decay_time = 24,
+ POD_stomp_enable = 25,
+ POD_comp_enable = 26,
+ POD_stomp_time = 27,
+ POD_delay_enable = 28,
+ POD_mod_param_1 = 29,
+ POD_delay_param_1 = 30,
+ POD_delay_param_1_note_value = 31,
+ POD_band_2_frequency__bass = 32, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
+ POD_delay_param_2 = 33,
+ POD_delay_volume_mix = 34,
+ POD_delay_param_3 = 35,
+ POD_reverb_enable = 36, /* device: LINE6_BITS_PODXTALL */
+ POD_reverb_type = 37, /* device: LINE6_BITS_PODXTALL */
+ POD_reverb_decay = 38, /* device: LINE6_BITS_PODXTALL */
+ POD_reverb_tone = 39, /* device: LINE6_BITS_PODXTALL */
+ POD_reverb_pre_delay = 40, /* device: LINE6_BITS_PODXTALL */
+ POD_reverb_pre_post = 41, /* device: LINE6_BITS_PODXTALL */
+ POD_band_2_frequency = 42, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
+ POD_band_3_frequency__bass = 42, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
+ POD_wah_enable = 43,
+ POD_modulation_lo_cut = 44, /* device: LINE6_BITS_BASSPODXTALL */
+ POD_delay_reverb_lo_cut = 45, /* device: LINE6_BITS_BASSPODXTALL */
+ POD_volume_pedal_minimum = 46, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
+ POD_eq_pre_post = 46, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
+ POD_volume_pre_post = 47,
+ POD_di_model = 48, /* device: LINE6_BITS_BASSPODXTALL */
+ POD_di_delay = 49, /* device: LINE6_BITS_BASSPODXTALL */
+ POD_mod_enable = 50,
+ POD_mod_param_1_note_value = 51,
+ POD_mod_param_2 = 52,
+ POD_mod_param_3 = 53,
+ POD_mod_param_4 = 54,
+ POD_mod_param_5 = 55, /* device: LINE6_BITS_BASSPODXTALL */
+ POD_mod_volume_mix = 56,
+ POD_mod_pre_post = 57,
+ POD_modulation_model = 58,
+ POD_band_3_frequency = 60, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
+ POD_band_4_frequency__bass = 60, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
+ POD_mod_param_1_double_precision = 61,
+ POD_delay_param_1_double_precision = 62,
+ POD_eq_enable = 63, /* firmware: 2.0 */
+ POD_tap = 64,
+ POD_volume_tweak_pedal_assign = 65,
+ POD_band_5_frequency = 68, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
+ POD_tuner = 69,
+ POD_mic_selection = 70,
+ POD_cabinet_model = 71,
+ POD_stomp_model = 75,
+ POD_roomlevel = 76,
+ POD_band_4_frequency = 77, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
+ POD_band_6_frequency = 77, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
+ POD_stomp_param_1_note_value = 78,
+ POD_stomp_param_2 = 79,
+ POD_stomp_param_3 = 80,
+ POD_stomp_param_4 = 81,
+ POD_stomp_param_5 = 82,
+ POD_stomp_param_6 = 83,
+ POD_amp_switch_select = 84, /* device: LINE6_BITS_LIVE */
+ POD_delay_param_4 = 85,
+ POD_delay_param_5 = 86,
+ POD_delay_pre_post = 87,
+ POD_delay_model = 88, /* device: LINE6_BITS_PODXTALL */
+ POD_delay_verb_model = 88, /* device: LINE6_BITS_BASSPODXTALL */
+ POD_tempo_msb = 89,
+ POD_tempo_lsb = 90,
+ POD_wah_model = 91, /* firmware: 3.0 */
+ POD_bypass_volume = 105, /* firmware: 2.14 */
+ POD_fx_loop_on_off = 107, /* device: LINE6_BITS_PRO */
+ POD_tweak_param_select = 108,
+ POD_amp1_engage = 111,
+ POD_band_1_gain = 114, /* firmware: 2.0 */
+ POD_band_2_gain__bass = 115, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
+ POD_band_2_gain = 116, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
+ POD_band_3_gain__bass = 116, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
+ POD_band_3_gain = 117, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
+ POD_band_4_gain__bass = 117, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
+ POD_band_5_gain__bass = 118, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
+ POD_band_4_gain = 119, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
+ POD_band_6_gain__bass = 119 /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
+};
+
+/**
+ List of Variax workbench controls (dump).
+*/
+enum {
+ VARIAX_body = 3,
+ VARIAX_pickup1_enable = 4, /* 0: enabled, 1: disabled */
+ VARIAX_pickup1_type = 8,
+ VARIAX_pickup1_position = 9, /* type: 24 bit float */
+ VARIAX_pickup1_angle = 12, /* type: 24 bit float */
+ VARIAX_pickup1_level = 15, /* type: 24 bit float */
+ VARIAX_pickup2_enable = 18, /* 0: enabled, 1: disabled */
+ VARIAX_pickup2_type = 22,
+ VARIAX_pickup2_position = 23, /* type: 24 bit float */
+ VARIAX_pickup2_angle = 26, /* type: 24 bit float */
+ VARIAX_pickup2_level = 29, /* type: 24 bit float */
+ VARIAX_pickup_phase = 32, /* 0: in phase, 1: out of phase */
+ VARIAX_capacitance = 33, /* type: 24 bit float */
+ VARIAX_tone_resistance = 36, /* type: 24 bit float */
+ VARIAX_volume_resistance = 39, /* type: 24 bit float */
+ VARIAX_taper = 42, /* 0: Linear, 1: Audio */
+ VARIAX_tone_dump = 43, /* type: 24 bit float */
+ VARIAX_save_tone = 46,
+ VARIAX_volume_dump = 47, /* type: 24 bit float */
+ VARIAX_tuning_enable = 50,
+ VARIAX_tuning6 = 51,
+ VARIAX_tuning5 = 52,
+ VARIAX_tuning4 = 53,
+ VARIAX_tuning3 = 54,
+ VARIAX_tuning2 = 55,
+ VARIAX_tuning1 = 56,
+ VARIAX_detune6 = 57, /* type: 24 bit float */
+ VARIAX_detune5 = 60, /* type: 24 bit float */
+ VARIAX_detune4 = 63, /* type: 24 bit float */
+ VARIAX_detune3 = 66, /* type: 24 bit float */
+ VARIAX_detune2 = 69, /* type: 24 bit float */
+ VARIAX_detune1 = 72, /* type: 24 bit float */
+ VARIAX_mix6 = 75, /* type: 24 bit float */
+ VARIAX_mix5 = 78, /* type: 24 bit float */
+ VARIAX_mix4 = 81, /* type: 24 bit float */
+ VARIAX_mix3 = 84, /* type: 24 bit float */
+ VARIAX_mix2 = 87, /* type: 24 bit float */
+ VARIAX_mix1 = 90, /* type: 24 bit float */
+ VARIAX_pickup_wiring = 96 /* 0: parallel, 1: series */
+};
+
+/**
+ List of Variax workbench controls (MIDI).
+*/
+enum {
+ VARIAXMIDI_volume = 7,
+ VARIAXMIDI_tone = 79,
+};
+
+/* *INDENT-ON* */
+
+extern int line6_pod_create_files(int firmware, int type, struct device *dev);
+extern void line6_pod_remove_files(int firmware, int type, struct device *dev);
+extern int line6_variax_create_files(int firmware, int type,
+ struct device *dev);
+extern void line6_variax_remove_files(int firmware, int type,
+ struct device *dev);
+
+#endif
diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c
new file mode 100644
index 00000000..e8023afd
--- /dev/null
+++ b/drivers/staging/line6/driver.c
@@ -0,0 +1,1362 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "audio.h"
+#include "capture.h"
+#include "control.h"
+#include "driver.h"
+#include "midi.h"
+#include "playback.h"
+#include "pod.h"
+#include "podhd.h"
+#include "revision.h"
+#include "toneport.h"
+#include "usbdefs.h"
+#include "variax.h"
+
+#define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>"
+#define DRIVER_DESC "Line6 USB Driver"
+#define DRIVER_VERSION "0.9.1beta" DRIVER_REVISION
+
+/* table of devices that work with this driver */
+static const struct usb_device_id line6_id_table[] = {
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXT)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTLIVE)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTPRO)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_GUITARPORT)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_POCKETPOD)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODHD300)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODHD500)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_GX)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX1)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX2)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3LIVE)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTLIVE)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTPRO)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_GX)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX1)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX2)},
+ {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_VARIAX)},
+ {},
+};
+
+MODULE_DEVICE_TABLE(usb, line6_id_table);
+
+/* *INDENT-OFF* */
+static struct line6_properties line6_properties_table[] = {
+ { LINE6_BIT_BASSPODXT, "BassPODxt", "BassPODxt", LINE6_BIT_CONTROL_PCM_HWMON },
+ { LINE6_BIT_BASSPODXTLIVE, "BassPODxtLive", "BassPODxt Live", LINE6_BIT_CONTROL_PCM_HWMON },
+ { LINE6_BIT_BASSPODXTPRO, "BassPODxtPro", "BassPODxt Pro", LINE6_BIT_CONTROL_PCM_HWMON },
+ { LINE6_BIT_GUITARPORT, "GuitarPort", "GuitarPort", LINE6_BIT_PCM },
+ { LINE6_BIT_POCKETPOD, "PocketPOD", "Pocket POD", LINE6_BIT_CONTROL },
+ { LINE6_BIT_PODHD300, "PODHD300", "POD HD300", LINE6_BIT_CONTROL_PCM_HWMON },
+ { LINE6_BIT_PODHD500, "PODHD500", "POD HD500", LINE6_BIT_CONTROL_PCM_HWMON },
+ { LINE6_BIT_PODSTUDIO_GX, "PODStudioGX", "POD Studio GX", LINE6_BIT_PCM },
+ { LINE6_BIT_PODSTUDIO_UX1, "PODStudioUX1", "POD Studio UX1", LINE6_BIT_PCM },
+ { LINE6_BIT_PODSTUDIO_UX2, "PODStudioUX2", "POD Studio UX2", LINE6_BIT_PCM },
+ { LINE6_BIT_PODX3, "PODX3", "POD X3", LINE6_BIT_PCM },
+ { LINE6_BIT_PODX3LIVE, "PODX3Live", "POD X3 Live", LINE6_BIT_PCM },
+ { LINE6_BIT_PODXT, "PODxt", "PODxt", LINE6_BIT_CONTROL_PCM_HWMON },
+ { LINE6_BIT_PODXTLIVE, "PODxtLive", "PODxt Live", LINE6_BIT_CONTROL_PCM_HWMON },
+ { LINE6_BIT_PODXTPRO, "PODxtPro", "PODxt Pro", LINE6_BIT_CONTROL_PCM_HWMON },
+ { LINE6_BIT_TONEPORT_GX, "TonePortGX", "TonePort GX", LINE6_BIT_PCM },
+ { LINE6_BIT_TONEPORT_UX1, "TonePortUX1", "TonePort UX1", LINE6_BIT_PCM },
+ { LINE6_BIT_TONEPORT_UX2, "TonePortUX2", "TonePort UX2", LINE6_BIT_PCM },
+ { LINE6_BIT_VARIAX, "Variax", "Variax Workbench", LINE6_BIT_CONTROL },
+};
+/* *INDENT-ON* */
+
+/*
+ This is Line6's MIDI manufacturer ID.
+*/
+const unsigned char line6_midi_id[] = {
+ 0x00, 0x01, 0x0c
+};
+
+/*
+ Code to request version of POD, Variax interface
+ (and maybe other devices).
+*/
+static const char line6_request_version0[] = {
+ 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7
+};
+
+/*
+ Copy of version request code with GFP_KERNEL flag for use in URB.
+*/
+static const char *line6_request_version;
+
+struct usb_line6 *line6_devices[LINE6_MAX_DEVICES];
+
+/**
+ Class for asynchronous messages.
+*/
+struct message {
+ struct usb_line6 *line6;
+ const char *buffer;
+ int size;
+ int done;
+};
+
+/*
+ Forward declarations.
+*/
+static void line6_data_received(struct urb *urb);
+static int line6_send_raw_message_async_part(struct message *msg,
+ struct urb *urb);
+
+/*
+ Start to listen on endpoint.
+*/
+static int line6_start_listen(struct usb_line6 *line6)
+{
+ int err;
+ usb_fill_int_urb(line6->urb_listen, line6->usbdev,
+ usb_rcvintpipe(line6->usbdev, line6->ep_control_read),
+ line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
+ line6_data_received, line6, line6->interval);
+ line6->urb_listen->actual_length = 0;
+ err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC);
+ return err;
+}
+
+/*
+ Stop listening on endpoint.
+*/
+static void line6_stop_listen(struct usb_line6 *line6)
+{
+ usb_kill_urb(line6->urb_listen);
+}
+
+#ifdef CONFIG_LINE6_USB_DUMP_ANY
+/*
+ Write hexdump to syslog.
+*/
+void line6_write_hexdump(struct usb_line6 *line6, char dir,
+ const unsigned char *buffer, int size)
+{
+ static const int BYTES_PER_LINE = 8;
+ char hexdump[100];
+ char asc[BYTES_PER_LINE + 1];
+ int i, j;
+
+ for (i = 0; i < size; i += BYTES_PER_LINE) {
+ int hexdumpsize = sizeof(hexdump);
+ char *p = hexdump;
+ int n = min(size - i, BYTES_PER_LINE);
+ asc[n] = 0;
+
+ for (j = 0; j < BYTES_PER_LINE; ++j) {
+ int bytes;
+
+ if (j < n) {
+ unsigned char val = buffer[i + j];
+ bytes = snprintf(p, hexdumpsize, " %02X", val);
+ asc[j] = ((val >= 0x20)
+ && (val < 0x7f)) ? val : '.';
+ } else
+ bytes = snprintf(p, hexdumpsize, " ");
+
+ if (bytes > hexdumpsize)
+ break; /* buffer overflow */
+
+ p += bytes;
+ hexdumpsize -= bytes;
+ }
+
+ dev_info(line6->ifcdev, "%c%04X:%s %s\n", dir, i, hexdump, asc);
+ }
+}
+#endif
+
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
+/*
+ Dump URB data to syslog.
+*/
+static void line6_dump_urb(struct urb *urb)
+{
+ struct usb_line6 *line6 = (struct usb_line6 *)urb->context;
+
+ if (urb->status < 0)
+ return;
+
+ line6_write_hexdump(line6, 'R', (unsigned char *)urb->transfer_buffer,
+ urb->actual_length);
+}
+#endif
+
+/*
+ Send raw message in pieces of wMaxPacketSize bytes.
+*/
+int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
+ int size)
+{
+ int i, done = 0;
+
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
+ line6_write_hexdump(line6, 'S', buffer, size);
+#endif
+
+ for (i = 0; i < size; i += line6->max_packet_size) {
+ int partial;
+ const char *frag_buf = buffer + i;
+ int frag_size = min(line6->max_packet_size, size - i);
+ int retval;
+
+ retval = usb_interrupt_msg(line6->usbdev,
+ usb_sndintpipe(line6->usbdev,
+ line6->ep_control_write),
+ (char *)frag_buf, frag_size,
+ &partial, LINE6_TIMEOUT * HZ);
+
+ if (retval) {
+ dev_err(line6->ifcdev,
+ "usb_interrupt_msg failed (%d)\n", retval);
+ break;
+ }
+
+ done += frag_size;
+ }
+
+ return done;
+}
+
+/*
+ Notification of completion of asynchronous request transmission.
+*/
+static void line6_async_request_sent(struct urb *urb)
+{
+ struct message *msg = (struct message *)urb->context;
+
+ if (msg->done >= msg->size) {
+ usb_free_urb(urb);
+ kfree(msg);
+ } else
+ line6_send_raw_message_async_part(msg, urb);
+}
+
+/*
+ Asynchronously send part of a raw message.
+*/
+static int line6_send_raw_message_async_part(struct message *msg,
+ struct urb *urb)
+{
+ int retval;
+ struct usb_line6 *line6 = msg->line6;
+ int done = msg->done;
+ int bytes = min(msg->size - done, line6->max_packet_size);
+
+ usb_fill_int_urb(urb, line6->usbdev,
+ usb_sndintpipe(line6->usbdev, line6->ep_control_write),
+ (char *)msg->buffer + done, bytes,
+ line6_async_request_sent, msg, line6->interval);
+
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
+ line6_write_hexdump(line6, 'S', (char *)msg->buffer + done, bytes);
+#endif
+
+ msg->done += bytes;
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (retval < 0) {
+ dev_err(line6->ifcdev, "%s: usb_submit_urb failed (%d)\n",
+ __func__, retval);
+ usb_free_urb(urb);
+ kfree(msg);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ Setup and start timer.
+*/
+void line6_start_timer(struct timer_list *timer, unsigned int msecs,
+ void (*function) (unsigned long), unsigned long data)
+{
+ setup_timer(timer, function, data);
+ timer->expires = jiffies + msecs * HZ / 1000;
+ add_timer(timer);
+}
+
+/*
+ Asynchronously send raw message.
+*/
+int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer,
+ int size)
+{
+ struct message *msg;
+ struct urb *urb;
+
+ /* create message: */
+ msg = kmalloc(sizeof(struct message), GFP_ATOMIC);
+
+ if (msg == NULL) {
+ dev_err(line6->ifcdev, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ /* create URB: */
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+ if (urb == NULL) {
+ kfree(msg);
+ dev_err(line6->ifcdev, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ /* set message data: */
+ msg->line6 = line6;
+ msg->buffer = buffer;
+ msg->size = size;
+ msg->done = 0;
+
+ /* start sending: */
+ return line6_send_raw_message_async_part(msg, urb);
+}
+
+/*
+ Send asynchronous device version request.
+*/
+int line6_version_request_async(struct usb_line6 *line6)
+{
+ return line6_send_raw_message_async(line6, line6_request_version,
+ sizeof(line6_request_version0));
+}
+
+/*
+ Send sysex message in pieces of wMaxPacketSize bytes.
+*/
+int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer,
+ int size)
+{
+ return line6_send_raw_message(line6, buffer,
+ size + SYSEX_EXTRA_SIZE) -
+ SYSEX_EXTRA_SIZE;
+}
+
+/*
+ Send sysex message in pieces of wMaxPacketSize bytes.
+*/
+int line6_send_sysex_message_async(struct usb_line6 *line6, const char *buffer,
+ int size)
+{
+ return line6_send_raw_message_async(line6, buffer,
+ size + SYSEX_EXTRA_SIZE) -
+ SYSEX_EXTRA_SIZE;
+}
+
+/*
+ Allocate buffer for sysex message and prepare header.
+ @param code sysex message code
+ @param size number of bytes between code and sysex end
+*/
+char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2,
+ int size)
+{
+ char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC);
+
+ if (!buffer) {
+ dev_err(line6->ifcdev, "out of memory\n");
+ return NULL;
+ }
+
+ buffer[0] = LINE6_SYSEX_BEGIN;
+ memcpy(buffer + 1, line6_midi_id, sizeof(line6_midi_id));
+ buffer[sizeof(line6_midi_id) + 1] = code1;
+ buffer[sizeof(line6_midi_id) + 2] = code2;
+ buffer[sizeof(line6_midi_id) + 3 + size] = LINE6_SYSEX_END;
+ return buffer;
+}
+
+/*
+ Notification of data received from the Line6 device.
+*/
+static void line6_data_received(struct urb *urb)
+{
+ struct usb_line6 *line6 = (struct usb_line6 *)urb->context;
+ struct MidiBuffer *mb = &line6->line6midi->midibuf_in;
+ int done;
+
+ if (urb->status == -ESHUTDOWN)
+ return;
+
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
+ line6_dump_urb(urb);
+#endif
+
+ done =
+ line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
+
+ if (done < urb->actual_length) {
+ line6_midibuf_ignore(mb, done);
+ DEBUG_MESSAGES(dev_err
+ (line6->ifcdev,
+ "%d %d buffer overflow - message skipped\n",
+ done, urb->actual_length));
+ }
+
+ for (;;) {
+ done =
+ line6_midibuf_read(mb, line6->buffer_message,
+ LINE6_MESSAGE_MAXLEN);
+
+ if (done == 0)
+ break;
+
+ /* MIDI input filter */
+ if (line6_midibuf_skip_message
+ (mb, line6->line6midi->midi_mask_receive))
+ continue;
+
+ line6->message_length = done;
+#ifdef CONFIG_LINE6_USB_DUMP_MIDI
+ line6_write_hexdump(line6, 'r', line6->buffer_message, done);
+#endif
+ line6_midi_receive(line6, line6->buffer_message, done);
+
+ switch (line6->usbdev->descriptor.idProduct) {
+ case LINE6_DEVID_BASSPODXT:
+ case LINE6_DEVID_BASSPODXTLIVE:
+ case LINE6_DEVID_BASSPODXTPRO:
+ case LINE6_DEVID_PODXT:
+ case LINE6_DEVID_PODXTPRO:
+ case LINE6_DEVID_POCKETPOD:
+ line6_pod_process_message((struct usb_line6_pod *)
+ line6);
+ break;
+
+ case LINE6_DEVID_PODHD300:
+ case LINE6_DEVID_PODHD500:
+ break; /* let userspace handle MIDI */
+
+ case LINE6_DEVID_PODXTLIVE:
+ switch (line6->interface_number) {
+ case PODXTLIVE_INTERFACE_POD:
+ line6_pod_process_message((struct usb_line6_pod
+ *)line6);
+ break;
+
+ case PODXTLIVE_INTERFACE_VARIAX:
+ line6_variax_process_message((struct
+ usb_line6_variax
+ *)line6);
+ break;
+
+ default:
+ dev_err(line6->ifcdev,
+ "PODxt Live interface %d not supported\n",
+ line6->interface_number);
+ }
+ break;
+
+ case LINE6_DEVID_VARIAX:
+ line6_variax_process_message((struct usb_line6_variax *)
+ line6);
+ break;
+
+ default:
+ MISSING_CASE;
+ }
+ }
+
+ line6_start_listen(line6);
+}
+
+/*
+ Send channel number (i.e., switch to a different sound).
+*/
+int line6_send_program(struct usb_line6 *line6, int value)
+{
+ int retval;
+ unsigned char *buffer;
+ int partial;
+
+ buffer = kmalloc(2, GFP_KERNEL);
+
+ if (!buffer) {
+ dev_err(line6->ifcdev, "out of memory\n");
+ return -ENOMEM;
+ }
+
+ buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST;
+ buffer[1] = value;
+
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
+ line6_write_hexdump(line6, 'S', buffer, 2);
+#endif
+
+ retval = usb_interrupt_msg(line6->usbdev,
+ usb_sndintpipe(line6->usbdev,
+ line6->ep_control_write),
+ buffer, 2, &partial, LINE6_TIMEOUT * HZ);
+
+ if (retval)
+ dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n",
+ retval);
+
+ kfree(buffer);
+ return retval;
+}
+
+/*
+ Transmit Line6 control parameter.
+*/
+int line6_transmit_parameter(struct usb_line6 *line6, int param, int value)
+{
+ int retval;
+ unsigned char *buffer;
+ int partial;
+
+ buffer = kmalloc(3, GFP_KERNEL);
+
+ if (!buffer) {
+ dev_err(line6->ifcdev, "out of memory\n");
+ return -ENOMEM;
+ }
+
+ buffer[0] = LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST;
+ buffer[1] = param;
+ buffer[2] = value;
+
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
+ line6_write_hexdump(line6, 'S', buffer, 3);
+#endif
+
+ retval = usb_interrupt_msg(line6->usbdev,
+ usb_sndintpipe(line6->usbdev,
+ line6->ep_control_write),
+ buffer, 3, &partial, LINE6_TIMEOUT * HZ);
+
+ if (retval)
+ dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n",
+ retval);
+
+ kfree(buffer);
+ return retval;
+}
+
+/*
+ Read data from device.
+*/
+int line6_read_data(struct usb_line6 *line6, int address, void *data,
+ size_t datalen)
+{
+ struct usb_device *usbdev = line6->usbdev;
+ int ret;
+ unsigned char len;
+
+ /* query the serial number: */
+ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ (datalen << 8) | 0x21, address,
+ NULL, 0, LINE6_TIMEOUT * HZ);
+
+ if (ret < 0) {
+ dev_err(line6->ifcdev, "read request failed (error %d)\n", ret);
+ return ret;
+ }
+
+ /* Wait for data length. We'll get a couple of 0xff until length arrives. */
+ do {
+ ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+ USB_DIR_IN,
+ 0x0012, 0x0000, &len, 1,
+ LINE6_TIMEOUT * HZ);
+ if (ret < 0) {
+ dev_err(line6->ifcdev,
+ "receive length failed (error %d)\n", ret);
+ return ret;
+ }
+ } while (len == 0xff);
+
+ if (len != datalen) {
+ /* should be equal or something went wrong */
+ dev_err(line6->ifcdev,
+ "length mismatch (expected %d, got %d)\n",
+ (int)datalen, (int)len);
+ return -EINVAL;
+ }
+
+ /* receive the result: */
+ ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x0013, 0x0000, data, datalen,
+ LINE6_TIMEOUT * HZ);
+
+ if (ret < 0) {
+ dev_err(line6->ifcdev, "read failed (error %d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ Write data to device.
+*/
+int line6_write_data(struct usb_line6 *line6, int address, void *data,
+ size_t datalen)
+{
+ struct usb_device *usbdev = line6->usbdev;
+ int ret;
+ unsigned char status;
+
+ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 0x0022, address, data, datalen,
+ LINE6_TIMEOUT * HZ);
+
+ if (ret < 0) {
+ dev_err(line6->ifcdev,
+ "write request failed (error %d)\n", ret);
+ return ret;
+ }
+
+ do {
+ ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
+ 0x67,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+ USB_DIR_IN,
+ 0x0012, 0x0000,
+ &status, 1, LINE6_TIMEOUT * HZ);
+
+ if (ret < 0) {
+ dev_err(line6->ifcdev,
+ "receiving status failed (error %d)\n", ret);
+ return ret;
+ }
+ } while (status == 0xff);
+
+ if (status != 0) {
+ dev_err(line6->ifcdev, "write failed (error %d)\n", ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ Read Line6 device serial number.
+ (POD, TonePort, GuitarPort)
+*/
+int line6_read_serial_number(struct usb_line6 *line6, int *serial_number)
+{
+ return line6_read_data(line6, 0x80d0, serial_number,
+ sizeof(*serial_number));
+}
+
+/*
+ No operation (i.e., unsupported).
+*/
+ssize_t line6_nop_read(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return 0;
+}
+
+/*
+ No operation (i.e., unsupported).
+*/
+ssize_t line6_nop_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return count;
+}
+
+/*
+ "write" request on "raw" special file.
+*/
+#ifdef CONFIG_LINE6_USB_RAW
+ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6 *line6 = usb_get_intfdata(interface);
+ line6_send_raw_message(line6, buf, count);
+ return count;
+}
+#endif
+
+/*
+ Generic destructor.
+*/
+static void line6_destruct(struct usb_interface *interface)
+{
+ struct usb_line6 *line6;
+
+ if (interface == NULL)
+ return;
+ line6 = usb_get_intfdata(interface);
+ if (line6 == NULL)
+ return;
+
+ /* free buffer memory first: */
+ kfree(line6->buffer_message);
+ kfree(line6->buffer_listen);
+
+ /* then free URBs: */
+ usb_free_urb(line6->urb_listen);
+
+ /* make sure the device isn't destructed twice: */
+ usb_set_intfdata(interface, NULL);
+
+ /* free interface data: */
+ kfree(line6);
+}
+
+/*
+ Probe USB device.
+*/
+static int line6_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int devtype;
+ struct usb_device *usbdev;
+ struct usb_line6 *line6;
+ const struct line6_properties *properties;
+ int devnum;
+ int interface_number, alternate = 0;
+ int product;
+ int size = 0;
+ int ep_read = 0, ep_write = 0;
+ int ret;
+
+ if (interface == NULL)
+ return -ENODEV;
+ usbdev = interface_to_usbdev(interface);
+ if (usbdev == NULL)
+ return -ENODEV;
+
+ /* we don't handle multiple configurations */
+ if (usbdev->descriptor.bNumConfigurations != 1) {
+ ret = -ENODEV;
+ goto err_put;
+ }
+
+ /* check vendor and product id */
+ for (devtype = ARRAY_SIZE(line6_id_table) - 1; devtype--;) {
+ u16 idVendor = le16_to_cpu(usbdev->descriptor.idVendor);
+ u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct);
+
+ if (idVendor == line6_id_table[devtype].idVendor &&
+ idProduct == line6_id_table[devtype].idProduct)
+ break;
+ }
+
+ if (devtype < 0) {
+ ret = -ENODEV;
+ goto err_put;
+ }
+
+ /* find free slot in device table: */
+ for (devnum = 0; devnum < LINE6_MAX_DEVICES; ++devnum)
+ if (line6_devices[devnum] == NULL)
+ break;
+
+ if (devnum == LINE6_MAX_DEVICES) {
+ ret = -ENODEV;
+ goto err_put;
+ }
+
+ /* initialize device info: */
+ properties = &line6_properties_table[devtype];
+ dev_info(&interface->dev, "Line6 %s found\n", properties->name);
+ product = le16_to_cpu(usbdev->descriptor.idProduct);
+
+ /* query interface number */
+ interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
+
+ switch (product) {
+ case LINE6_DEVID_BASSPODXTLIVE:
+ case LINE6_DEVID_PODXTLIVE:
+ case LINE6_DEVID_VARIAX:
+ alternate = 1;
+ break;
+
+ case LINE6_DEVID_POCKETPOD:
+ switch (interface_number) {
+ case 0:
+ return 0; /* this interface has no endpoints */
+ case 1:
+ alternate = 0;
+ break;
+ default:
+ MISSING_CASE;
+ }
+ break;
+
+ case LINE6_DEVID_PODHD500:
+ case LINE6_DEVID_PODX3:
+ case LINE6_DEVID_PODX3LIVE:
+ switch (interface_number) {
+ case 0:
+ alternate = 1;
+ break;
+ case 1:
+ alternate = 0;
+ break;
+ default:
+ MISSING_CASE;
+ }
+ break;
+
+ case LINE6_DEVID_BASSPODXT:
+ case LINE6_DEVID_BASSPODXTPRO:
+ case LINE6_DEVID_PODXT:
+ case LINE6_DEVID_PODXTPRO:
+ case LINE6_DEVID_PODHD300:
+ alternate = 5;
+ break;
+
+ case LINE6_DEVID_GUITARPORT:
+ case LINE6_DEVID_PODSTUDIO_GX:
+ case LINE6_DEVID_PODSTUDIO_UX1:
+ case LINE6_DEVID_TONEPORT_GX:
+ case LINE6_DEVID_TONEPORT_UX1:
+ alternate = 2; /* 1..4 seem to be ok */
+ break;
+
+ case LINE6_DEVID_TONEPORT_UX2:
+ case LINE6_DEVID_PODSTUDIO_UX2:
+ switch (interface_number) {
+ case 0:
+ /* defaults to 44.1kHz, 16-bit */
+ alternate = 2;
+ break;
+ case 1:
+ /* don't know yet what this is ...
+ alternate = 1;
+ break;
+ */
+ return -ENODEV;
+ default:
+ MISSING_CASE;
+ }
+ break;
+
+ default:
+ MISSING_CASE;
+ ret = -ENODEV;
+ goto err_put;
+ }
+
+ ret = usb_set_interface(usbdev, interface_number, alternate);
+ if (ret < 0) {
+ dev_err(&interface->dev, "set_interface failed\n");
+ goto err_put;
+ }
+
+ /* initialize device data based on product id: */
+ switch (product) {
+ case LINE6_DEVID_BASSPODXT:
+ case LINE6_DEVID_BASSPODXTLIVE:
+ case LINE6_DEVID_BASSPODXTPRO:
+ case LINE6_DEVID_PODXT:
+ case LINE6_DEVID_PODXTPRO:
+ size = sizeof(struct usb_line6_pod);
+ ep_read = 0x84;
+ ep_write = 0x03;
+ break;
+
+ case LINE6_DEVID_PODHD300:
+ size = sizeof(struct usb_line6_podhd);
+ ep_read = 0x84;
+ ep_write = 0x03;
+ break;
+
+ case LINE6_DEVID_PODHD500:
+ size = sizeof(struct usb_line6_podhd);
+ ep_read = 0x81;
+ ep_write = 0x01;
+ break;
+
+ case LINE6_DEVID_POCKETPOD:
+ size = sizeof(struct usb_line6_pod);
+ ep_read = 0x82;
+ ep_write = 0x02;
+ break;
+
+ case LINE6_DEVID_PODX3:
+ case LINE6_DEVID_PODX3LIVE:
+ /* currently unused! */
+ size = sizeof(struct usb_line6_pod);
+ ep_read = 0x81;
+ ep_write = 0x01;
+ break;
+
+ case LINE6_DEVID_PODSTUDIO_GX:
+ case LINE6_DEVID_PODSTUDIO_UX1:
+ case LINE6_DEVID_PODSTUDIO_UX2:
+ case LINE6_DEVID_TONEPORT_GX:
+ case LINE6_DEVID_TONEPORT_UX1:
+ case LINE6_DEVID_TONEPORT_UX2:
+ case LINE6_DEVID_GUITARPORT:
+ size = sizeof(struct usb_line6_toneport);
+ /* these don't have a control channel */
+ break;
+
+ case LINE6_DEVID_PODXTLIVE:
+ switch (interface_number) {
+ case PODXTLIVE_INTERFACE_POD:
+ size = sizeof(struct usb_line6_pod);
+ ep_read = 0x84;
+ ep_write = 0x03;
+ break;
+
+ case PODXTLIVE_INTERFACE_VARIAX:
+ size = sizeof(struct usb_line6_variax);
+ ep_read = 0x86;
+ ep_write = 0x05;
+ break;
+
+ default:
+ ret = -ENODEV;
+ goto err_put;
+ }
+ break;
+
+ case LINE6_DEVID_VARIAX:
+ size = sizeof(struct usb_line6_variax);
+ ep_read = 0x82;
+ ep_write = 0x01;
+ break;
+
+ default:
+ MISSING_CASE;
+ ret = -ENODEV;
+ goto err_put;
+ }
+
+ if (size == 0) {
+ dev_err(&interface->dev,
+ "driver bug: interface data size not set\n");
+ ret = -ENODEV;
+ goto err_put;
+ }
+
+ line6 = kzalloc(size, GFP_KERNEL);
+
+ if (line6 == NULL) {
+ dev_err(&interface->dev, "Out of memory\n");
+ ret = -ENODEV;
+ goto err_put;
+ }
+
+ /* store basic data: */
+ line6->interface_number = interface_number;
+ line6->properties = properties;
+ line6->usbdev = usbdev;
+ line6->ifcdev = &interface->dev;
+ line6->ep_control_read = ep_read;
+ line6->ep_control_write = ep_write;
+ line6->product = product;
+
+ /* get data from endpoint descriptor (see usb_maxpacket): */
+ {
+ struct usb_host_endpoint *ep;
+ unsigned epnum =
+ usb_pipeendpoint(usb_rcvintpipe(usbdev, ep_read));
+ ep = usbdev->ep_in[epnum];
+
+ if (ep != NULL) {
+ line6->interval = ep->desc.bInterval;
+ line6->max_packet_size =
+ le16_to_cpu(ep->desc.wMaxPacketSize);
+ } else {
+ line6->interval = LINE6_FALLBACK_INTERVAL;
+ line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE;
+ dev_err(line6->ifcdev,
+ "endpoint not available, using fallback values");
+ }
+ }
+
+ usb_set_intfdata(interface, line6);
+
+ if (properties->capabilities & LINE6_BIT_CONTROL) {
+ /* initialize USB buffers: */
+ line6->buffer_listen =
+ kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL);
+
+ if (line6->buffer_listen == NULL) {
+ dev_err(&interface->dev, "Out of memory\n");
+ ret = -ENOMEM;
+ goto err_destruct;
+ }
+
+ line6->buffer_message =
+ kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
+
+ if (line6->buffer_message == NULL) {
+ dev_err(&interface->dev, "Out of memory\n");
+ ret = -ENOMEM;
+ goto err_destruct;
+ }
+
+ line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (line6->urb_listen == NULL) {
+ dev_err(&interface->dev, "Out of memory\n");
+ line6_destruct(interface);
+ ret = -ENOMEM;
+ goto err_destruct;
+ }
+
+ ret = line6_start_listen(line6);
+ if (ret < 0) {
+ dev_err(&interface->dev, "%s: usb_submit_urb failed\n",
+ __func__);
+ goto err_destruct;
+ }
+ }
+
+ /* initialize device data based on product id: */
+ switch (product) {
+ case LINE6_DEVID_BASSPODXT:
+ case LINE6_DEVID_BASSPODXTLIVE:
+ case LINE6_DEVID_BASSPODXTPRO:
+ case LINE6_DEVID_POCKETPOD:
+ case LINE6_DEVID_PODX3:
+ case LINE6_DEVID_PODX3LIVE:
+ case LINE6_DEVID_PODXT:
+ case LINE6_DEVID_PODXTPRO:
+ ret = line6_pod_init(interface, (struct usb_line6_pod *)line6);
+ break;
+
+ case LINE6_DEVID_PODHD300:
+ case LINE6_DEVID_PODHD500:
+ ret = line6_podhd_init(interface,
+ (struct usb_line6_podhd *)line6);
+ break;
+
+ case LINE6_DEVID_PODXTLIVE:
+ switch (interface_number) {
+ case PODXTLIVE_INTERFACE_POD:
+ ret =
+ line6_pod_init(interface,
+ (struct usb_line6_pod *)line6);
+ break;
+
+ case PODXTLIVE_INTERFACE_VARIAX:
+ ret =
+ line6_variax_init(interface,
+ (struct usb_line6_variax *)line6);
+ break;
+
+ default:
+ dev_err(&interface->dev,
+ "PODxt Live interface %d not supported\n",
+ interface_number);
+ ret = -ENODEV;
+ }
+
+ break;
+
+ case LINE6_DEVID_VARIAX:
+ ret =
+ line6_variax_init(interface,
+ (struct usb_line6_variax *)line6);
+ break;
+
+ case LINE6_DEVID_PODSTUDIO_GX:
+ case LINE6_DEVID_PODSTUDIO_UX1:
+ case LINE6_DEVID_PODSTUDIO_UX2:
+ case LINE6_DEVID_TONEPORT_GX:
+ case LINE6_DEVID_TONEPORT_UX1:
+ case LINE6_DEVID_TONEPORT_UX2:
+ case LINE6_DEVID_GUITARPORT:
+ ret =
+ line6_toneport_init(interface,
+ (struct usb_line6_toneport *)line6);
+ break;
+
+ default:
+ MISSING_CASE;
+ ret = -ENODEV;
+ }
+
+ if (ret < 0)
+ goto err_destruct;
+
+ ret = sysfs_create_link(&interface->dev.kobj, &usbdev->dev.kobj,
+ "usb_device");
+ if (ret < 0)
+ goto err_destruct;
+
+ /* creation of additional special files should go here */
+
+ dev_info(&interface->dev, "Line6 %s now attached\n",
+ line6->properties->name);
+ line6_devices[devnum] = line6;
+
+ switch (product) {
+ case LINE6_DEVID_PODX3:
+ case LINE6_DEVID_PODX3LIVE:
+ dev_info(&interface->dev,
+ "NOTE: the Line6 %s is detected, but not yet supported\n",
+ line6->properties->name);
+ }
+
+ /* increment reference counters: */
+ usb_get_intf(interface);
+ usb_get_dev(usbdev);
+
+ return 0;
+
+err_destruct:
+ line6_destruct(interface);
+err_put:
+ return ret;
+}
+
+/*
+ Line6 device disconnected.
+*/
+static void line6_disconnect(struct usb_interface *interface)
+{
+ struct usb_line6 *line6;
+ struct usb_device *usbdev;
+ int interface_number, i;
+
+ if (interface == NULL)
+ return;
+ usbdev = interface_to_usbdev(interface);
+ if (usbdev == NULL)
+ return;
+
+ /* removal of additional special files should go here */
+
+ sysfs_remove_link(&interface->dev.kobj, "usb_device");
+
+ interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
+ line6 = usb_get_intfdata(interface);
+
+ if (line6 != NULL) {
+ if (line6->urb_listen != NULL)
+ line6_stop_listen(line6);
+
+ if (usbdev != line6->usbdev)
+ dev_err(line6->ifcdev,
+ "driver bug: inconsistent usb device\n");
+
+ switch (line6->usbdev->descriptor.idProduct) {
+ case LINE6_DEVID_BASSPODXT:
+ case LINE6_DEVID_BASSPODXTLIVE:
+ case LINE6_DEVID_BASSPODXTPRO:
+ case LINE6_DEVID_POCKETPOD:
+ case LINE6_DEVID_PODX3:
+ case LINE6_DEVID_PODX3LIVE:
+ case LINE6_DEVID_PODXT:
+ case LINE6_DEVID_PODXTPRO:
+ line6_pod_disconnect(interface);
+ break;
+
+ case LINE6_DEVID_PODHD300:
+ case LINE6_DEVID_PODHD500:
+ line6_podhd_disconnect(interface);
+ break;
+
+ case LINE6_DEVID_PODXTLIVE:
+ switch (interface_number) {
+ case PODXTLIVE_INTERFACE_POD:
+ line6_pod_disconnect(interface);
+ break;
+
+ case PODXTLIVE_INTERFACE_VARIAX:
+ line6_variax_disconnect(interface);
+ break;
+ }
+
+ break;
+
+ case LINE6_DEVID_VARIAX:
+ line6_variax_disconnect(interface);
+ break;
+
+ case LINE6_DEVID_PODSTUDIO_GX:
+ case LINE6_DEVID_PODSTUDIO_UX1:
+ case LINE6_DEVID_PODSTUDIO_UX2:
+ case LINE6_DEVID_TONEPORT_GX:
+ case LINE6_DEVID_TONEPORT_UX1:
+ case LINE6_DEVID_TONEPORT_UX2:
+ case LINE6_DEVID_GUITARPORT:
+ line6_toneport_disconnect(interface);
+ break;
+
+ default:
+ MISSING_CASE;
+ }
+
+ dev_info(&interface->dev, "Line6 %s now disconnected\n",
+ line6->properties->name);
+
+ for (i = LINE6_MAX_DEVICES; i--;)
+ if (line6_devices[i] == line6)
+ line6_devices[i] = NULL;
+ }
+
+ line6_destruct(interface);
+
+ /* decrement reference counters: */
+ usb_put_intf(interface);
+ usb_put_dev(usbdev);
+}
+
+#ifdef CONFIG_PM
+
+/*
+ Suspend Line6 device.
+*/
+static int line6_suspend(struct usb_interface *interface, pm_message_t message)
+{
+ struct usb_line6 *line6 = usb_get_intfdata(interface);
+ struct snd_line6_pcm *line6pcm = line6->line6pcm;
+
+ snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
+
+ if (line6->properties->capabilities & LINE6_BIT_CONTROL)
+ line6_stop_listen(line6);
+
+ if (line6pcm != NULL) {
+ snd_pcm_suspend_all(line6pcm->pcm);
+ line6_pcm_disconnect(line6pcm);
+ line6pcm->flags = 0;
+ }
+
+ return 0;
+}
+
+/*
+ Resume Line6 device.
+*/
+static int line6_resume(struct usb_interface *interface)
+{
+ struct usb_line6 *line6 = usb_get_intfdata(interface);
+
+ if (line6->properties->capabilities & LINE6_BIT_CONTROL)
+ line6_start_listen(line6);
+
+ snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+
+/*
+ Resume Line6 device after reset.
+*/
+static int line6_reset_resume(struct usb_interface *interface)
+{
+ struct usb_line6 *line6 = usb_get_intfdata(interface);
+
+ switch (line6->usbdev->descriptor.idProduct) {
+ case LINE6_DEVID_PODSTUDIO_GX:
+ case LINE6_DEVID_PODSTUDIO_UX1:
+ case LINE6_DEVID_PODSTUDIO_UX2:
+ case LINE6_DEVID_TONEPORT_GX:
+ case LINE6_DEVID_TONEPORT_UX1:
+ case LINE6_DEVID_TONEPORT_UX2:
+ case LINE6_DEVID_GUITARPORT:
+ line6_toneport_reset_resume((struct usb_line6_toneport *)line6);
+ }
+
+ return line6_resume(interface);
+}
+
+#endif /* CONFIG_PM */
+
+static struct usb_driver line6_driver = {
+ .name = DRIVER_NAME,
+ .probe = line6_probe,
+ .disconnect = line6_disconnect,
+#ifdef CONFIG_PM
+ .suspend = line6_suspend,
+ .resume = line6_resume,
+ .reset_resume = line6_reset_resume,
+#endif
+ .id_table = line6_id_table,
+};
+
+/*
+ Module initialization.
+*/
+static int __init line6_init(void)
+{
+ int i, retval;
+
+ printk(KERN_INFO "%s driver version %s\n", DRIVER_NAME, DRIVER_VERSION);
+
+ for (i = LINE6_MAX_DEVICES; i--;)
+ line6_devices[i] = NULL;
+
+ retval = usb_register(&line6_driver);
+
+ if (retval) {
+ err("usb_register failed. Error number %d", retval);
+ return retval;
+ }
+
+ line6_request_version = kmalloc(sizeof(line6_request_version0),
+ GFP_KERNEL);
+
+ if (line6_request_version == NULL) {
+ err("Out of memory");
+ return -ENOMEM;
+ }
+
+ memcpy((char *)line6_request_version, line6_request_version0,
+ sizeof(line6_request_version0));
+
+ return retval;
+}
+
+/*
+ Module cleanup.
+*/
+static void __exit line6_exit(void)
+{
+ int i;
+ struct usb_line6 *line6;
+ struct snd_line6_pcm *line6pcm;
+
+ /* stop all PCM channels */
+ for (i = LINE6_MAX_DEVICES; i--;) {
+ line6 = line6_devices[i];
+
+ if (line6 == NULL)
+ continue;
+
+ line6pcm = line6->line6pcm;
+
+ if (line6pcm == NULL)
+ continue;
+
+ line6_pcm_release(line6pcm, ~0);
+ }
+
+ usb_deregister(&line6_driver);
+ kfree(line6_request_version);
+}
+
+module_init(line6_init);
+module_exit(line6_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h
new file mode 100644
index 00000000..117bf994
--- /dev/null
+++ b/drivers/staging/line6/driver.h
@@ -0,0 +1,237 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef DRIVER_H
+#define DRIVER_H
+
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <sound/core.h>
+
+#include "midi.h"
+
+#define DRIVER_NAME "line6usb"
+
+#if defined(CONFIG_LINE6_USB_DUMP_CTRL) || defined(CONFIG_LINE6_USB_DUMP_MIDI) || defined(CONFIG_LINE6_USB_DUMP_PCM)
+#define CONFIG_LINE6_USB_DUMP_ANY
+#endif
+
+#define LINE6_TIMEOUT 1
+#define LINE6_MAX_DEVICES 8
+#define LINE6_BUFSIZE_LISTEN 32
+#define LINE6_MESSAGE_MAXLEN 256
+
+/*
+ Line6 MIDI control commands
+*/
+#define LINE6_PARAM_CHANGE 0xb0
+#define LINE6_PROGRAM_CHANGE 0xc0
+#define LINE6_SYSEX_BEGIN 0xf0
+#define LINE6_SYSEX_END 0xf7
+#define LINE6_RESET 0xff
+
+/*
+ MIDI channel for messages initiated by the host
+ (and eventually echoed back by the device)
+*/
+#define LINE6_CHANNEL_HOST 0x00
+
+/*
+ MIDI channel for messages initiated by the device
+*/
+#define LINE6_CHANNEL_DEVICE 0x02
+
+#define LINE6_CHANNEL_UNKNOWN 5 /* don't know yet what this is good for */
+
+#define LINE6_CHANNEL_MASK 0x0f
+
+#ifdef CONFIG_LINE6_USB_DEBUG
+#define DEBUG_MESSAGES(x) (x)
+#else
+#define DEBUG_MESSAGES(x)
+#endif
+
+#define MISSING_CASE \
+ printk(KERN_ERR "line6usb driver bug: missing case in %s:%d\n", \
+ __FILE__, __LINE__)
+
+#define CHECK_RETURN(x) \
+do { \
+ err = x; \
+ if (err < 0) \
+ return err; \
+} while (0)
+
+#define CHECK_STARTUP_PROGRESS(x, n) \
+do { \
+ if ((x) >= (n)) \
+ return; \
+ x = (n); \
+} while (0)
+
+extern const unsigned char line6_midi_id[3];
+extern struct usb_line6 *line6_devices[LINE6_MAX_DEVICES];
+
+static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3;
+static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4;
+
+/**
+ Common properties of Line6 devices.
+*/
+struct line6_properties {
+ /**
+ Bit identifying this device in the line6usb driver.
+ */
+ int device_bit;
+
+ /**
+ Card id string (maximum 16 characters).
+ This can be used to address the device in ALSA programs as
+ "default:CARD=<id>"
+ */
+ const char *id;
+
+ /**
+ Card short name (maximum 32 characters).
+ */
+ const char *name;
+
+ /**
+ Bit vector defining this device's capabilities in the
+ line6usb driver.
+ */
+ int capabilities;
+};
+
+/**
+ Common data shared by all Line6 devices.
+ Corresponds to a pair of USB endpoints.
+*/
+struct usb_line6 {
+ /**
+ USB device.
+ */
+ struct usb_device *usbdev;
+
+ /**
+ Product id.
+ */
+ int product;
+
+ /**
+ Properties.
+ */
+ const struct line6_properties *properties;
+
+ /**
+ Interface number.
+ */
+ int interface_number;
+
+ /**
+ Interval (ms).
+ */
+ int interval;
+
+ /**
+ Maximum size of USB packet.
+ */
+ int max_packet_size;
+
+ /**
+ Device representing the USB interface.
+ */
+ struct device *ifcdev;
+
+ /**
+ Line6 sound card data structure.
+ Each device has at least MIDI or PCM.
+ */
+ struct snd_card *card;
+
+ /**
+ Line6 PCM device data structure.
+ */
+ struct snd_line6_pcm *line6pcm;
+
+ /**
+ Line6 MIDI device data structure.
+ */
+ struct snd_line6_midi *line6midi;
+
+ /**
+ USB endpoint for listening to control commands.
+ */
+ int ep_control_read;
+
+ /**
+ USB endpoint for writing control commands.
+ */
+ int ep_control_write;
+
+ /**
+ URB for listening to PODxt Pro control endpoint.
+ */
+ struct urb *urb_listen;
+
+ /**
+ Buffer for listening to PODxt Pro control endpoint.
+ */
+ unsigned char *buffer_listen;
+
+ /**
+ Buffer for message to be processed.
+ */
+ unsigned char *buffer_message;
+
+ /**
+ Length of message to be processed.
+ */
+ int message_length;
+};
+
+extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1,
+ int code2, int size);
+extern ssize_t line6_nop_read(struct device *dev,
+ struct device_attribute *attr, char *buf);
+extern ssize_t line6_nop_write(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+extern int line6_read_data(struct usb_line6 *line6, int address, void *data,
+ size_t datalen);
+extern int line6_read_serial_number(struct usb_line6 *line6,
+ int *serial_number);
+extern int line6_send_program(struct usb_line6 *line6, int value);
+extern int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
+ int size);
+extern int line6_send_raw_message_async(struct usb_line6 *line6,
+ const char *buffer, int size);
+extern int line6_send_sysex_message(struct usb_line6 *line6,
+ const char *buffer, int size);
+extern int line6_send_sysex_message_async(struct usb_line6 *line6,
+ const char *buffer, int size);
+extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+extern void line6_start_timer(struct timer_list *timer, unsigned int msecs,
+ void (*function) (unsigned long),
+ unsigned long data);
+extern int line6_transmit_parameter(struct usb_line6 *line6, int param,
+ int value);
+extern int line6_version_request_async(struct usb_line6 *line6);
+extern int line6_write_data(struct usb_line6 *line6, int address, void *data,
+ size_t datalen);
+
+#ifdef CONFIG_LINE6_USB_DUMP_ANY
+extern void line6_write_hexdump(struct usb_line6 *line6, char dir,
+ const unsigned char *buffer, int size);
+#endif
+
+#endif
diff --git a/drivers/staging/line6/dumprequest.c b/drivers/staging/line6/dumprequest.c
new file mode 100644
index 00000000..60c7bae3
--- /dev/null
+++ b/drivers/staging/line6/dumprequest.c
@@ -0,0 +1,135 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+
+#include "driver.h"
+#include "dumprequest.h"
+
+/*
+ Set "dump in progress" flag.
+*/
+void line6_dump_started(struct line6_dump_request *l6dr, int dest)
+{
+ l6dr->in_progress = dest;
+}
+
+/*
+ Invalidate current channel, i.e., set "dump in progress" flag.
+ Reading from the "dump" special file blocks until dump is completed.
+*/
+void line6_invalidate_current(struct line6_dump_request *l6dr)
+{
+ line6_dump_started(l6dr, LINE6_DUMP_CURRENT);
+}
+
+/*
+ Clear "dump in progress" flag and notify waiting processes.
+*/
+void line6_dump_finished(struct line6_dump_request *l6dr)
+{
+ l6dr->in_progress = LINE6_DUMP_NONE;
+ wake_up(&l6dr->wait);
+}
+
+/*
+ Send an asynchronous channel dump request.
+*/
+int line6_dump_request_async(struct line6_dump_request *l6dr,
+ struct usb_line6 *line6, int num, int dest)
+{
+ int ret;
+ line6_dump_started(l6dr, dest);
+ ret = line6_send_raw_message_async(line6, l6dr->reqbufs[num].buffer,
+ l6dr->reqbufs[num].length);
+
+ if (ret < 0)
+ line6_dump_finished(l6dr);
+
+ return ret;
+}
+
+/*
+ Wait for completion (interruptible).
+*/
+int line6_dump_wait_interruptible(struct line6_dump_request *l6dr)
+{
+ return wait_event_interruptible(l6dr->wait,
+ l6dr->in_progress == LINE6_DUMP_NONE);
+}
+
+/*
+ Wait for completion.
+*/
+void line6_dump_wait(struct line6_dump_request *l6dr)
+{
+ wait_event(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE);
+}
+
+/*
+ Wait for completion (with timeout).
+*/
+int line6_dump_wait_timeout(struct line6_dump_request *l6dr, long timeout)
+{
+ return wait_event_timeout(l6dr->wait,
+ l6dr->in_progress == LINE6_DUMP_NONE,
+ timeout);
+}
+
+/*
+ Initialize dump request buffer.
+*/
+int line6_dumpreq_initbuf(struct line6_dump_request *l6dr, const void *buf,
+ size_t len, int num)
+{
+ l6dr->reqbufs[num].buffer = kmemdup(buf, len, GFP_KERNEL);
+ if (l6dr->reqbufs[num].buffer == NULL)
+ return -ENOMEM;
+ l6dr->reqbufs[num].length = len;
+ return 0;
+}
+
+/*
+ Initialize dump request data structure (including one buffer).
+*/
+int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf,
+ size_t len)
+{
+ int ret;
+ ret = line6_dumpreq_initbuf(l6dr, buf, len, 0);
+ if (ret < 0)
+ return ret;
+ init_waitqueue_head(&l6dr->wait);
+ return 0;
+}
+
+/*
+ Destruct dump request data structure.
+*/
+void line6_dumpreq_destructbuf(struct line6_dump_request *l6dr, int num)
+{
+ if (l6dr == NULL)
+ return;
+ if (l6dr->reqbufs[num].buffer == NULL)
+ return;
+ kfree(l6dr->reqbufs[num].buffer);
+ l6dr->reqbufs[num].buffer = NULL;
+}
+
+/*
+ Destruct dump request data structure.
+*/
+void line6_dumpreq_destruct(struct line6_dump_request *l6dr)
+{
+ if (l6dr->reqbufs[0].buffer == NULL)
+ return;
+ line6_dumpreq_destructbuf(l6dr, 0);
+}
diff --git a/drivers/staging/line6/dumprequest.h b/drivers/staging/line6/dumprequest.h
new file mode 100644
index 00000000..c17a262f
--- /dev/null
+++ b/drivers/staging/line6/dumprequest.h
@@ -0,0 +1,76 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef DUMPREQUEST_H
+#define DUMPREQUEST_H
+
+#include <linux/usb.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+
+enum {
+ LINE6_DUMP_NONE,
+ LINE6_DUMP_CURRENT
+};
+
+struct line6_dump_reqbuf {
+ /**
+ Buffer for dump requests.
+ */
+ unsigned char *buffer;
+
+ /**
+ Size of dump request.
+ */
+ size_t length;
+};
+
+/**
+ Provides the functionality to request channel/model/... dump data from a
+ Line6 device.
+*/
+struct line6_dump_request {
+ /**
+ Wait queue for access to program dump data.
+ */
+ wait_queue_head_t wait;
+
+ /**
+ Indicates an unfinished program dump request.
+ 0: no dump
+ 1: dump current settings
+ Other device-specific values are also allowed.
+ */
+ int in_progress;
+
+ /**
+ Dump request buffers
+ */
+ struct line6_dump_reqbuf reqbufs[1];
+};
+
+extern void line6_dump_finished(struct line6_dump_request *l6dr);
+extern int line6_dump_request_async(struct line6_dump_request *l6dr,
+ struct usb_line6 *line6, int num, int dest);
+extern void line6_dump_started(struct line6_dump_request *l6dr, int dest);
+extern void line6_dumpreq_destruct(struct line6_dump_request *l6dr);
+extern void line6_dumpreq_destructbuf(struct line6_dump_request *l6dr, int num);
+extern int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf,
+ size_t len);
+extern int line6_dumpreq_initbuf(struct line6_dump_request *l6dr,
+ const void *buf, size_t len, int num);
+extern void line6_invalidate_current(struct line6_dump_request *l6dr);
+extern void line6_dump_wait(struct line6_dump_request *l6dr);
+extern int line6_dump_wait_interruptible(struct line6_dump_request *l6dr);
+extern int line6_dump_wait_timeout(struct line6_dump_request *l6dr,
+ long timeout);
+
+#endif
diff --git a/drivers/staging/line6/midi.c b/drivers/staging/line6/midi.c
new file mode 100644
index 00000000..13d02939
--- /dev/null
+++ b/drivers/staging/line6/midi.c
@@ -0,0 +1,446 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+
+#include "audio.h"
+#include "driver.h"
+#include "midi.h"
+#include "pod.h"
+#include "usbdefs.h"
+
+#define line6_rawmidi_substream_midi(substream) \
+ ((struct snd_line6_midi *)((substream)->rmidi->private_data))
+
+static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
+ int length);
+
+/*
+ Pass data received via USB to MIDI.
+*/
+void line6_midi_receive(struct usb_line6 *line6, unsigned char *data,
+ int length)
+{
+ if (line6->line6midi->substream_receive)
+ snd_rawmidi_receive(line6->line6midi->substream_receive,
+ data, length);
+}
+
+/*
+ Read data from MIDI buffer and transmit them via USB.
+*/
+static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
+{
+ struct usb_line6 *line6 =
+ line6_rawmidi_substream_midi(substream)->line6;
+ struct snd_line6_midi *line6midi = line6->line6midi;
+ struct MidiBuffer *mb = &line6midi->midibuf_out;
+ unsigned long flags;
+ unsigned char chunk[line6->max_packet_size];
+ int req, done;
+
+ spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags);
+
+ for (;;) {
+ req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size);
+ done = snd_rawmidi_transmit_peek(substream, chunk, req);
+
+ if (done == 0)
+ break;
+
+#ifdef CONFIG_LINE6_USB_DUMP_MIDI
+ line6_write_hexdump(line6, 's', chunk, done);
+#endif
+ line6_midibuf_write(mb, chunk, done);
+ snd_rawmidi_transmit_ack(substream, done);
+ }
+
+ for (;;) {
+ done = line6_midibuf_read(mb, chunk, line6->max_packet_size);
+
+ if (done == 0)
+ break;
+
+ if (line6_midibuf_skip_message
+ (mb, line6midi->midi_mask_transmit))
+ continue;
+
+ send_midi_async(line6, chunk, done);
+ }
+
+ spin_unlock_irqrestore(&line6->line6midi->midi_transmit_lock, flags);
+}
+
+/*
+ Notification of completion of MIDI transmission.
+*/
+static void midi_sent(struct urb *urb)
+{
+ unsigned long flags;
+ int status;
+ int num;
+ struct usb_line6 *line6 = (struct usb_line6 *)urb->context;
+
+ status = urb->status;
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+
+ if (status == -ESHUTDOWN)
+ return;
+
+ spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags);
+ num = --line6->line6midi->num_active_send_urbs;
+
+ if (num == 0) {
+ line6_midi_transmit(line6->line6midi->substream_transmit);
+ num = line6->line6midi->num_active_send_urbs;
+ }
+
+ if (num == 0)
+ wake_up(&line6->line6midi->send_wait);
+
+ spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags);
+}
+
+/*
+ Send an asynchronous MIDI message.
+ Assumes that line6->line6midi->send_urb_lock is held
+ (i.e., this function is serialized).
+*/
+static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
+ int length)
+{
+ struct urb *urb;
+ int retval;
+ unsigned char *transfer_buffer;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+ if (urb == NULL) {
+ dev_err(line6->ifcdev, "Out of memory\n");
+ return -ENOMEM;
+ }
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
+ line6_write_hexdump(line6, 'S', data, length);
+#endif
+
+ transfer_buffer = kmemdup(data, length, GFP_ATOMIC);
+
+ if (transfer_buffer == NULL) {
+ usb_free_urb(urb);
+ dev_err(line6->ifcdev, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ usb_fill_int_urb(urb, line6->usbdev,
+ usb_sndbulkpipe(line6->usbdev,
+ line6->ep_control_write),
+ transfer_buffer, length, midi_sent, line6,
+ line6->interval);
+ urb->actual_length = 0;
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (retval < 0) {
+ dev_err(line6->ifcdev, "usb_submit_urb failed\n");
+ usb_free_urb(urb);
+ return -EINVAL;
+ }
+
+ ++line6->line6midi->num_active_send_urbs;
+
+ switch (line6->usbdev->descriptor.idProduct) {
+ case LINE6_DEVID_BASSPODXT:
+ case LINE6_DEVID_BASSPODXTLIVE:
+ case LINE6_DEVID_BASSPODXTPRO:
+ case LINE6_DEVID_PODXT:
+ case LINE6_DEVID_PODXTLIVE:
+ case LINE6_DEVID_PODXTPRO:
+ case LINE6_DEVID_POCKETPOD:
+ line6_pod_midi_postprocess((struct usb_line6_pod *)line6, data,
+ length);
+ break;
+
+ case LINE6_DEVID_VARIAX:
+ case LINE6_DEVID_PODHD300:
+ case LINE6_DEVID_PODHD500:
+ break;
+
+ default:
+ MISSING_CASE;
+ }
+
+ return 0;
+}
+
+static int line6_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static int line6_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ unsigned long flags;
+ struct usb_line6 *line6 =
+ line6_rawmidi_substream_midi(substream)->line6;
+
+ line6->line6midi->substream_transmit = substream;
+ spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags);
+
+ if (line6->line6midi->num_active_send_urbs == 0)
+ line6_midi_transmit(substream);
+
+ spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags);
+}
+
+static void line6_midi_output_drain(struct snd_rawmidi_substream *substream)
+{
+ struct usb_line6 *line6 =
+ line6_rawmidi_substream_midi(substream)->line6;
+ struct snd_line6_midi *midi = line6->line6midi;
+ wait_event_interruptible(midi->send_wait,
+ midi->num_active_send_urbs == 0);
+}
+
+static int line6_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static int line6_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct usb_line6 *line6 =
+ line6_rawmidi_substream_midi(substream)->line6;
+
+ if (up)
+ line6->line6midi->substream_receive = substream;
+ else
+ line6->line6midi->substream_receive = 0;
+}
+
+static struct snd_rawmidi_ops line6_midi_output_ops = {
+ .open = line6_midi_output_open,
+ .close = line6_midi_output_close,
+ .trigger = line6_midi_output_trigger,
+ .drain = line6_midi_output_drain,
+};
+
+static struct snd_rawmidi_ops line6_midi_input_ops = {
+ .open = line6_midi_input_open,
+ .close = line6_midi_input_close,
+ .trigger = line6_midi_input_trigger,
+};
+
+/*
+ Cleanup the Line6 MIDI device.
+*/
+static void line6_cleanup_midi(struct snd_rawmidi *rmidi)
+{
+}
+
+/* Create a MIDI device */
+static int snd_line6_new_midi(struct snd_line6_midi *line6midi)
+{
+ struct snd_rawmidi *rmidi;
+ int err;
+
+ err = snd_rawmidi_new(line6midi->line6->card, "Line6 MIDI", 0, 1, 1,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ rmidi->private_data = line6midi;
+ rmidi->private_free = line6_cleanup_midi;
+ strcpy(rmidi->id, line6midi->line6->properties->id);
+ strcpy(rmidi->name, line6midi->line6->properties->name);
+
+ rmidi->info_flags =
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &line6_midi_output_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &line6_midi_input_ops);
+ return 0;
+}
+
+/*
+ "read" request on "midi_mask_transmit" special file.
+*/
+static ssize_t midi_get_midi_mask_transmit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6 *line6 = usb_get_intfdata(interface);
+ return sprintf(buf, "%d\n", line6->line6midi->midi_mask_transmit);
+}
+
+/*
+ "write" request on "midi_mask" special file.
+*/
+static ssize_t midi_set_midi_mask_transmit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6 *line6 = usb_get_intfdata(interface);
+ unsigned short value;
+ int ret;
+
+ ret = kstrtou16(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ line6->line6midi->midi_mask_transmit = value;
+ return count;
+}
+
+/*
+ "read" request on "midi_mask_receive" special file.
+*/
+static ssize_t midi_get_midi_mask_receive(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6 *line6 = usb_get_intfdata(interface);
+ return sprintf(buf, "%d\n", line6->line6midi->midi_mask_receive);
+}
+
+/*
+ "write" request on "midi_mask" special file.
+*/
+static ssize_t midi_set_midi_mask_receive(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6 *line6 = usb_get_intfdata(interface);
+ unsigned short value;
+ int ret;
+
+ ret = kstrtou16(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ line6->line6midi->midi_mask_receive = value;
+ return count;
+}
+
+static DEVICE_ATTR(midi_mask_transmit, S_IWUSR | S_IRUGO,
+ midi_get_midi_mask_transmit, midi_set_midi_mask_transmit);
+static DEVICE_ATTR(midi_mask_receive, S_IWUSR | S_IRUGO,
+ midi_get_midi_mask_receive, midi_set_midi_mask_receive);
+
+/* MIDI device destructor */
+static int snd_line6_midi_free(struct snd_device *device)
+{
+ struct snd_line6_midi *line6midi = device->device_data;
+ device_remove_file(line6midi->line6->ifcdev,
+ &dev_attr_midi_mask_transmit);
+ device_remove_file(line6midi->line6->ifcdev,
+ &dev_attr_midi_mask_receive);
+ line6_midibuf_destroy(&line6midi->midibuf_in);
+ line6_midibuf_destroy(&line6midi->midibuf_out);
+ return 0;
+}
+
+/*
+ Initialize the Line6 MIDI subsystem.
+*/
+int line6_init_midi(struct usb_line6 *line6)
+{
+ static struct snd_device_ops midi_ops = {
+ .dev_free = snd_line6_midi_free,
+ };
+
+ int err;
+ struct snd_line6_midi *line6midi;
+
+ if (!(line6->properties->capabilities & LINE6_BIT_CONTROL)) {
+ /* skip MIDI initialization and report success */
+ return 0;
+ }
+
+ line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL);
+
+ if (line6midi == NULL)
+ return -ENOMEM;
+
+ err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);
+ if (err < 0) {
+ kfree(line6midi);
+ return err;
+ }
+
+ err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1);
+ if (err < 0) {
+ kfree(line6midi->midibuf_in.buf);
+ kfree(line6midi);
+ return err;
+ }
+
+ line6midi->line6 = line6;
+
+ switch(line6->product) {
+ case LINE6_DEVID_PODHD300:
+ case LINE6_DEVID_PODHD500:
+ line6midi->midi_mask_transmit = 1;
+ line6midi->midi_mask_receive = 1;
+ break;
+
+ default:
+ line6midi->midi_mask_transmit = 1;
+ line6midi->midi_mask_receive = 4;
+ }
+
+ line6->line6midi = line6midi;
+
+ err = snd_device_new(line6->card, SNDRV_DEV_RAWMIDI, line6midi,
+ &midi_ops);
+ if (err < 0)
+ return err;
+
+ snd_card_set_dev(line6->card, line6->ifcdev);
+
+ err = snd_line6_new_midi(line6midi);
+ if (err < 0)
+ return err;
+
+ err = device_create_file(line6->ifcdev, &dev_attr_midi_mask_transmit);
+ if (err < 0)
+ return err;
+
+ err = device_create_file(line6->ifcdev, &dev_attr_midi_mask_receive);
+ if (err < 0)
+ return err;
+
+ init_waitqueue_head(&line6midi->send_wait);
+ spin_lock_init(&line6midi->send_urb_lock);
+ spin_lock_init(&line6midi->midi_transmit_lock);
+ return 0;
+}
diff --git a/drivers/staging/line6/midi.h b/drivers/staging/line6/midi.h
new file mode 100644
index 00000000..4a9e9f94
--- /dev/null
+++ b/drivers/staging/line6/midi.h
@@ -0,0 +1,82 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef MIDI_H
+#define MIDI_H
+
+#include <sound/rawmidi.h>
+
+#include "midibuf.h"
+
+#define MIDI_BUFFER_SIZE 1024
+
+struct snd_line6_midi {
+ /**
+ Pointer back to the Line6 driver data structure.
+ */
+ struct usb_line6 *line6;
+
+ /**
+ MIDI substream for receiving (or NULL if not active).
+ */
+ struct snd_rawmidi_substream *substream_receive;
+
+ /**
+ MIDI substream for transmitting (or NULL if not active).
+ */
+ struct snd_rawmidi_substream *substream_transmit;
+
+ /**
+ Number of currently active MIDI send URBs.
+ */
+ int num_active_send_urbs;
+
+ /**
+ Spin lock to protect updates of send_urb.
+ */
+ spinlock_t send_urb_lock;
+
+ /**
+ Spin lock to protect MIDI buffer handling.
+ */
+ spinlock_t midi_transmit_lock;
+
+ /**
+ Wait queue for MIDI transmission.
+ */
+ wait_queue_head_t send_wait;
+
+ /**
+ Bit mask for output MIDI channels.
+ */
+ unsigned short midi_mask_transmit;
+
+ /**
+ Bit mask for input MIDI channels.
+ */
+ unsigned short midi_mask_receive;
+
+ /**
+ Buffer for incoming MIDI stream.
+ */
+ struct MidiBuffer midibuf_in;
+
+ /**
+ Buffer for outgoing MIDI stream.
+ */
+ struct MidiBuffer midibuf_out;
+};
+
+extern int line6_init_midi(struct usb_line6 *line6);
+extern void line6_midi_receive(struct usb_line6 *line6, unsigned char *data,
+ int length);
+
+#endif
diff --git a/drivers/staging/line6/midibuf.c b/drivers/staging/line6/midibuf.c
new file mode 100644
index 00000000..7b532e5c
--- /dev/null
+++ b/drivers/staging/line6/midibuf.c
@@ -0,0 +1,264 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+
+#include "midibuf.h"
+
+static int midibuf_message_length(unsigned char code)
+{
+ if (code < 0x80)
+ return -1;
+ else if (code < 0xf0) {
+ static const int length[] = { 3, 3, 3, 3, 2, 2, 3 };
+ return length[(code >> 4) - 8];
+ } else {
+ /*
+ Note that according to the MIDI specification 0xf2 is
+ the "Song Position Pointer", but this is used by Line6
+ to send sysex messages to the host.
+ */
+ static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1,
+ 1, 1, 1, -1, 1, 1
+ };
+ return length[code & 0x0f];
+ }
+}
+
+static int midibuf_is_empty(struct MidiBuffer *this)
+{
+ return (this->pos_read == this->pos_write) && !this->full;
+}
+
+static int midibuf_is_full(struct MidiBuffer *this)
+{
+ return this->full;
+}
+
+void line6_midibuf_reset(struct MidiBuffer *this)
+{
+ this->pos_read = this->pos_write = this->full = 0;
+ this->command_prev = -1;
+}
+
+int line6_midibuf_init(struct MidiBuffer *this, int size, int split)
+{
+ this->buf = kmalloc(size, GFP_KERNEL);
+
+ if (this->buf == NULL)
+ return -ENOMEM;
+
+ this->size = size;
+ this->split = split;
+ line6_midibuf_reset(this);
+ return 0;
+}
+
+void line6_midibuf_status(struct MidiBuffer *this)
+{
+ printk(KERN_DEBUG "midibuf size=%d split=%d pos_read=%d pos_write=%d "
+ "full=%d command_prev=%02x\n", this->size, this->split,
+ this->pos_read, this->pos_write, this->full, this->command_prev);
+}
+
+int line6_midibuf_bytes_free(struct MidiBuffer *this)
+{
+ return
+ midibuf_is_full(this) ?
+ 0 :
+ (this->pos_read - this->pos_write + this->size - 1) % this->size +
+ 1;
+}
+
+int line6_midibuf_bytes_used(struct MidiBuffer *this)
+{
+ return
+ midibuf_is_empty(this) ?
+ 0 :
+ (this->pos_write - this->pos_read + this->size - 1) % this->size +
+ 1;
+}
+
+int line6_midibuf_write(struct MidiBuffer *this, unsigned char *data,
+ int length)
+{
+ int bytes_free;
+ int length1, length2;
+ int skip_active_sense = 0;
+
+ if (midibuf_is_full(this) || (length <= 0))
+ return 0;
+
+ /* skip trailing active sense */
+ if (data[length - 1] == 0xfe) {
+ --length;
+ skip_active_sense = 1;
+ }
+
+ bytes_free = line6_midibuf_bytes_free(this);
+
+ if (length > bytes_free)
+ length = bytes_free;
+
+ if (length > 0) {
+ length1 = this->size - this->pos_write;
+
+ if (length < length1) {
+ /* no buffer wraparound */
+ memcpy(this->buf + this->pos_write, data, length);
+ this->pos_write += length;
+ } else {
+ /* buffer wraparound */
+ length2 = length - length1;
+ memcpy(this->buf + this->pos_write, data, length1);
+ memcpy(this->buf, data + length1, length2);
+ this->pos_write = length2;
+ }
+
+ if (this->pos_write == this->pos_read)
+ this->full = 1;
+ }
+
+ return length + skip_active_sense;
+}
+
+int line6_midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
+{
+ int bytes_used;
+ int length1, length2;
+ int command;
+ int midi_length;
+ int repeat = 0;
+ int i;
+
+ /* we need to be able to store at least a 3 byte MIDI message */
+ if (length < 3)
+ return -EINVAL;
+
+ if (midibuf_is_empty(this))
+ return 0;
+
+ bytes_used = line6_midibuf_bytes_used(this);
+
+ if (length > bytes_used)
+ length = bytes_used;
+
+ length1 = this->size - this->pos_read;
+
+ /* check MIDI command length */
+ command = this->buf[this->pos_read];
+
+ if (command & 0x80) {
+ midi_length = midibuf_message_length(command);
+ this->command_prev = command;
+ } else {
+ if (this->command_prev > 0) {
+ int midi_length_prev =
+ midibuf_message_length(this->command_prev);
+
+ if (midi_length_prev > 0) {
+ midi_length = midi_length_prev - 1;
+ repeat = 1;
+ } else
+ midi_length = -1;
+ } else
+ midi_length = -1;
+ }
+
+ if (midi_length < 0) {
+ /* search for end of message */
+ if (length < length1) {
+ /* no buffer wraparound */
+ for (i = 1; i < length; ++i)
+ if (this->buf[this->pos_read + i] & 0x80)
+ break;
+
+ midi_length = i;
+ } else {
+ /* buffer wraparound */
+ length2 = length - length1;
+
+ for (i = 1; i < length1; ++i)
+ if (this->buf[this->pos_read + i] & 0x80)
+ break;
+
+ if (i < length1)
+ midi_length = i;
+ else {
+ for (i = 0; i < length2; ++i)
+ if (this->buf[i] & 0x80)
+ break;
+
+ midi_length = length1 + i;
+ }
+ }
+
+ if (midi_length == length)
+ midi_length = -1; /* end of message not found */
+ }
+
+ if (midi_length < 0) {
+ if (!this->split)
+ return 0; /* command is not yet complete */
+ } else {
+ if (length < midi_length)
+ return 0; /* command is not yet complete */
+
+ length = midi_length;
+ }
+
+ if (length < length1) {
+ /* no buffer wraparound */
+ memcpy(data + repeat, this->buf + this->pos_read, length);
+ this->pos_read += length;
+ } else {
+ /* buffer wraparound */
+ length2 = length - length1;
+ memcpy(data + repeat, this->buf + this->pos_read, length1);
+ memcpy(data + repeat + length1, this->buf, length2);
+ this->pos_read = length2;
+ }
+
+ if (repeat)
+ data[0] = this->command_prev;
+
+ this->full = 0;
+ return length + repeat;
+}
+
+int line6_midibuf_ignore(struct MidiBuffer *this, int length)
+{
+ int bytes_used = line6_midibuf_bytes_used(this);
+
+ if (length > bytes_used)
+ length = bytes_used;
+
+ this->pos_read = (this->pos_read + length) % this->size;
+ this->full = 0;
+ return length;
+}
+
+int line6_midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
+{
+ int cmd = this->command_prev;
+
+ if ((cmd >= 0x80) && (cmd < 0xf0))
+ if ((mask & (1 << (cmd & 0x0f))) == 0)
+ return 1;
+
+ return 0;
+}
+
+void line6_midibuf_destroy(struct MidiBuffer *this)
+{
+ kfree(this->buf);
+ this->buf = NULL;
+}
diff --git a/drivers/staging/line6/midibuf.h b/drivers/staging/line6/midibuf.h
new file mode 100644
index 00000000..444cb3a1
--- /dev/null
+++ b/drivers/staging/line6/midibuf.h
@@ -0,0 +1,38 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef MIDIBUF_H
+#define MIDIBUF_H
+
+struct MidiBuffer {
+ unsigned char *buf;
+ int size;
+ int split;
+ int pos_read, pos_write;
+ int full;
+ int command_prev;
+};
+
+extern int line6_midibuf_bytes_used(struct MidiBuffer *mb);
+extern int line6_midibuf_bytes_free(struct MidiBuffer *mb);
+extern void line6_midibuf_destroy(struct MidiBuffer *mb);
+extern int line6_midibuf_ignore(struct MidiBuffer *mb, int length);
+extern int line6_midibuf_init(struct MidiBuffer *mb, int size, int split);
+extern int line6_midibuf_read(struct MidiBuffer *mb, unsigned char *data,
+ int length);
+extern void line6_midibuf_reset(struct MidiBuffer *mb);
+extern int line6_midibuf_skip_message(struct MidiBuffer *mb,
+ unsigned short mask);
+extern void line6_midibuf_status(struct MidiBuffer *mb);
+extern int line6_midibuf_write(struct MidiBuffer *mb, unsigned char *data,
+ int length);
+
+#endif
diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c
new file mode 100644
index 00000000..90d2d447
--- /dev/null
+++ b/drivers/staging/line6/pcm.c
@@ -0,0 +1,564 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "audio.h"
+#include "capture.h"
+#include "driver.h"
+#include "playback.h"
+#include "pod.h"
+
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+
+static struct snd_line6_pcm *dev2pcm(struct device *dev)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6 *line6 = usb_get_intfdata(interface);
+ struct snd_line6_pcm *line6pcm = line6->line6pcm;
+ return line6pcm;
+}
+
+/*
+ "read" request on "impulse_volume" special file.
+*/
+static ssize_t pcm_get_impulse_volume(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_volume);
+}
+
+/*
+ "write" request on "impulse_volume" special file.
+*/
+static ssize_t pcm_set_impulse_volume(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_line6_pcm *line6pcm = dev2pcm(dev);
+ int value = simple_strtoul(buf, NULL, 10);
+ line6pcm->impulse_volume = value;
+
+ if (value > 0)
+ line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_IMPULSE);
+ else
+ line6_pcm_release(line6pcm, LINE6_BITS_PCM_IMPULSE);
+
+ return count;
+}
+
+/*
+ "read" request on "impulse_period" special file.
+*/
+static ssize_t pcm_get_impulse_period(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_period);
+}
+
+/*
+ "write" request on "impulse_period" special file.
+*/
+static ssize_t pcm_set_impulse_period(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ dev2pcm(dev)->impulse_period = simple_strtoul(buf, NULL, 10);
+ return count;
+}
+
+static DEVICE_ATTR(impulse_volume, S_IWUSR | S_IRUGO, pcm_get_impulse_volume,
+ pcm_set_impulse_volume);
+static DEVICE_ATTR(impulse_period, S_IWUSR | S_IRUGO, pcm_get_impulse_period,
+ pcm_set_impulse_period);
+
+#endif
+
+static bool test_flags(unsigned long flags0, unsigned long flags1,
+ unsigned long mask)
+{
+ return ((flags0 & mask) == 0) && ((flags1 & mask) != 0);
+}
+
+int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels)
+{
+ unsigned long flags_old =
+ __sync_fetch_and_or(&line6pcm->flags, channels);
+ unsigned long flags_new = flags_old | channels;
+ unsigned long flags_final = flags_old;
+ int err = 0;
+
+ line6pcm->prev_fbuf = NULL;
+
+ if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_BUFFER)) {
+ /* We may be invoked multiple times in a row so allocate once only */
+ if (!line6pcm->buffer_in) {
+ line6pcm->buffer_in =
+ kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
+ line6pcm->max_packet_size, GFP_KERNEL);
+
+ if (!line6pcm->buffer_in) {
+ dev_err(line6pcm->line6->ifcdev,
+ "cannot malloc capture buffer\n");
+ err = -ENOMEM;
+ goto pcm_acquire_error;
+ }
+
+ flags_final |= channels & LINE6_BITS_CAPTURE_BUFFER;
+ }
+ }
+
+ if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_STREAM)) {
+ /*
+ Waiting for completion of active URBs in the stop handler is
+ a bug, we therefore report an error if capturing is restarted
+ too soon.
+ */
+ if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) {
+ dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n");
+ return -EBUSY;
+ }
+
+ line6pcm->count_in = 0;
+ line6pcm->prev_fsize = 0;
+ err = line6_submit_audio_in_all_urbs(line6pcm);
+
+ if (err < 0)
+ goto pcm_acquire_error;
+
+ flags_final |= channels & LINE6_BITS_CAPTURE_STREAM;
+ }
+
+ if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_BUFFER)) {
+ /* We may be invoked multiple times in a row so allocate once only */
+ if (!line6pcm->buffer_out) {
+ line6pcm->buffer_out =
+ kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
+ line6pcm->max_packet_size, GFP_KERNEL);
+
+ if (!line6pcm->buffer_out) {
+ dev_err(line6pcm->line6->ifcdev,
+ "cannot malloc playback buffer\n");
+ err = -ENOMEM;
+ goto pcm_acquire_error;
+ }
+
+ flags_final |= channels & LINE6_BITS_PLAYBACK_BUFFER;
+ }
+ }
+
+ if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_STREAM)) {
+ /*
+ See comment above regarding PCM restart.
+ */
+ if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) {
+ dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n");
+ return -EBUSY;
+ }
+
+ line6pcm->count_out = 0;
+ err = line6_submit_audio_out_all_urbs(line6pcm);
+
+ if (err < 0)
+ goto pcm_acquire_error;
+
+ flags_final |= channels & LINE6_BITS_PLAYBACK_STREAM;
+ }
+
+ return 0;
+
+pcm_acquire_error:
+ /*
+ If not all requested resources/streams could be obtained, release
+ those which were successfully obtained (if any).
+ */
+ line6_pcm_release(line6pcm, flags_final & channels);
+ return err;
+}
+
+int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels)
+{
+ unsigned long flags_old =
+ __sync_fetch_and_and(&line6pcm->flags, ~channels);
+ unsigned long flags_new = flags_old & ~channels;
+
+ if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_STREAM))
+ line6_unlink_audio_in_urbs(line6pcm);
+
+ if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_BUFFER)) {
+ line6_wait_clear_audio_in_urbs(line6pcm);
+ line6_free_capture_buffer(line6pcm);
+ }
+
+ if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_STREAM))
+ line6_unlink_audio_out_urbs(line6pcm);
+
+ if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_BUFFER)) {
+ line6_wait_clear_audio_out_urbs(line6pcm);
+ line6_free_playback_buffer(line6pcm);
+ }
+
+ return 0;
+}
+
+/* trigger callback */
+int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+ struct snd_pcm_substream *s;
+ int err;
+ unsigned long flags;
+
+ spin_lock_irqsave(&line6pcm->lock_trigger, flags);
+ clear_bit(LINE6_INDEX_PREPARED, &line6pcm->flags);
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ switch (s->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ err = snd_line6_playback_trigger(line6pcm, cmd);
+
+ if (err < 0) {
+ spin_unlock_irqrestore(&line6pcm->lock_trigger,
+ flags);
+ return err;
+ }
+
+ break;
+
+ case SNDRV_PCM_STREAM_CAPTURE:
+ err = snd_line6_capture_trigger(line6pcm, cmd);
+
+ if (err < 0) {
+ spin_unlock_irqrestore(&line6pcm->lock_trigger,
+ flags);
+ return err;
+ }
+
+ break;
+
+ default:
+ dev_err(line6pcm->line6->ifcdev,
+ "Unknown stream direction %d\n", s->stream);
+ }
+ }
+
+ spin_unlock_irqrestore(&line6pcm->lock_trigger, flags);
+ return 0;
+}
+
+/* control info callback */
+static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 256;
+ return 0;
+}
+
+/* control get callback */
+static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+
+ for (i = 2; i--;)
+ ucontrol->value.integer.value[i] = line6pcm->volume_playback[i];
+
+ return 0;
+}
+
+/* control put callback */
+static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i, changed = 0;
+ struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+
+ for (i = 2; i--;)
+ if (line6pcm->volume_playback[i] !=
+ ucontrol->value.integer.value[i]) {
+ line6pcm->volume_playback[i] =
+ ucontrol->value.integer.value[i];
+ changed = 1;
+ }
+
+ return changed;
+}
+
+/* control definition */
+static struct snd_kcontrol_new line6_control_playback = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_line6_control_playback_info,
+ .get = snd_line6_control_playback_get,
+ .put = snd_line6_control_playback_put
+};
+
+/*
+ Cleanup the PCM device.
+*/
+static void line6_cleanup_pcm(struct snd_pcm *pcm)
+{
+ int i;
+ struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm);
+
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_volume);
+ device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_period);
+#endif
+
+ for (i = LINE6_ISO_BUFFERS; i--;) {
+ if (line6pcm->urb_audio_out[i]) {
+ usb_kill_urb(line6pcm->urb_audio_out[i]);
+ usb_free_urb(line6pcm->urb_audio_out[i]);
+ }
+ if (line6pcm->urb_audio_in[i]) {
+ usb_kill_urb(line6pcm->urb_audio_in[i]);
+ usb_free_urb(line6pcm->urb_audio_in[i]);
+ }
+ }
+}
+
+/* create a PCM device */
+static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(line6pcm->line6->card,
+ (char *)line6pcm->line6->properties->name,
+ 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = line6pcm;
+ pcm->private_free = line6_cleanup_pcm;
+ line6pcm->pcm = pcm;
+ strcpy(pcm->name, line6pcm->line6->properties->name);
+
+ /* set operators */
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_line6_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops);
+
+ /* pre-allocation of buffers */
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data
+ (GFP_KERNEL), 64 * 1024,
+ 128 * 1024);
+
+ return 0;
+}
+
+/* PCM device destructor */
+static int snd_line6_pcm_free(struct snd_device *device)
+{
+ return 0;
+}
+
+/*
+ Stop substream if still running.
+*/
+static void pcm_disconnect_substream(struct snd_pcm_substream *substream)
+{
+ if (substream->runtime && snd_pcm_running(substream))
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
+}
+
+/*
+ Stop PCM stream.
+*/
+void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm)
+{
+ pcm_disconnect_substream(get_substream
+ (line6pcm, SNDRV_PCM_STREAM_CAPTURE));
+ pcm_disconnect_substream(get_substream
+ (line6pcm, SNDRV_PCM_STREAM_PLAYBACK));
+ line6_unlink_wait_clear_audio_out_urbs(line6pcm);
+ line6_unlink_wait_clear_audio_in_urbs(line6pcm);
+}
+
+/*
+ Create and register the PCM device and mixer entries.
+ Create URBs for playback and capture.
+*/
+int line6_init_pcm(struct usb_line6 *line6,
+ struct line6_pcm_properties *properties)
+{
+ static struct snd_device_ops pcm_ops = {
+ .dev_free = snd_line6_pcm_free,
+ };
+
+ int err;
+ int ep_read = 0, ep_write = 0;
+ struct snd_line6_pcm *line6pcm;
+
+ if (!(line6->properties->capabilities & LINE6_BIT_PCM))
+ return 0; /* skip PCM initialization and report success */
+
+ /* initialize PCM subsystem based on product id: */
+ switch (line6->product) {
+ case LINE6_DEVID_BASSPODXT:
+ case LINE6_DEVID_BASSPODXTLIVE:
+ case LINE6_DEVID_BASSPODXTPRO:
+ case LINE6_DEVID_PODXT:
+ case LINE6_DEVID_PODXTLIVE:
+ case LINE6_DEVID_PODXTPRO:
+ case LINE6_DEVID_PODHD300:
+ ep_read = 0x82;
+ ep_write = 0x01;
+ break;
+
+ case LINE6_DEVID_PODHD500:
+ case LINE6_DEVID_PODX3:
+ case LINE6_DEVID_PODX3LIVE:
+ ep_read = 0x86;
+ ep_write = 0x02;
+ break;
+
+ case LINE6_DEVID_POCKETPOD:
+ ep_read = 0x82;
+ ep_write = 0x02;
+ break;
+
+ case LINE6_DEVID_GUITARPORT:
+ case LINE6_DEVID_PODSTUDIO_GX:
+ case LINE6_DEVID_PODSTUDIO_UX1:
+ case LINE6_DEVID_PODSTUDIO_UX2:
+ case LINE6_DEVID_TONEPORT_GX:
+ case LINE6_DEVID_TONEPORT_UX1:
+ case LINE6_DEVID_TONEPORT_UX2:
+ ep_read = 0x82;
+ ep_write = 0x01;
+ break;
+
+ /* this is for interface_number == 1:
+ case LINE6_DEVID_TONEPORT_UX2:
+ case LINE6_DEVID_PODSTUDIO_UX2:
+ ep_read = 0x87;
+ ep_write = 0x00;
+ break;
+ */
+
+ default:
+ MISSING_CASE;
+ }
+
+ line6pcm = kzalloc(sizeof(struct snd_line6_pcm), GFP_KERNEL);
+
+ if (line6pcm == NULL)
+ return -ENOMEM;
+
+ line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255;
+ line6pcm->volume_monitor = 255;
+ line6pcm->line6 = line6;
+ line6pcm->ep_audio_read = ep_read;
+ line6pcm->ep_audio_write = ep_write;
+
+ /* Read and write buffers are sized identically, so choose minimum */
+ line6pcm->max_packet_size = min(
+ usb_maxpacket(line6->usbdev,
+ usb_rcvisocpipe(line6->usbdev, ep_read), 0),
+ usb_maxpacket(line6->usbdev,
+ usb_sndisocpipe(line6->usbdev, ep_write), 1));
+
+ line6pcm->properties = properties;
+ line6->line6pcm = line6pcm;
+
+ /* PCM device: */
+ err = snd_device_new(line6->card, SNDRV_DEV_PCM, line6, &pcm_ops);
+ if (err < 0)
+ return err;
+
+ snd_card_set_dev(line6->card, line6->ifcdev);
+
+ err = snd_line6_new_pcm(line6pcm);
+ if (err < 0)
+ return err;
+
+ spin_lock_init(&line6pcm->lock_audio_out);
+ spin_lock_init(&line6pcm->lock_audio_in);
+ spin_lock_init(&line6pcm->lock_trigger);
+
+ err = line6_create_audio_out_urbs(line6pcm);
+ if (err < 0)
+ return err;
+
+ err = line6_create_audio_in_urbs(line6pcm);
+ if (err < 0)
+ return err;
+
+ /* mixer: */
+ err =
+ snd_ctl_add(line6->card,
+ snd_ctl_new1(&line6_control_playback, line6pcm));
+ if (err < 0)
+ return err;
+
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ /* impulse response test: */
+ err = device_create_file(line6->ifcdev, &dev_attr_impulse_volume);
+ if (err < 0)
+ return err;
+
+ err = device_create_file(line6->ifcdev, &dev_attr_impulse_period);
+ if (err < 0)
+ return err;
+
+ line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD;
+#endif
+
+ return 0;
+}
+
+/* prepare pcm callback */
+int snd_line6_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ if ((line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) == 0)
+ line6_unlink_wait_clear_audio_out_urbs(line6pcm);
+
+ break;
+
+ case SNDRV_PCM_STREAM_CAPTURE:
+ if ((line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) == 0)
+ line6_unlink_wait_clear_audio_in_urbs(line6pcm);
+
+ break;
+
+ default:
+ MISSING_CASE;
+ }
+
+ if (!test_and_set_bit(LINE6_INDEX_PREPARED, &line6pcm->flags)) {
+ line6pcm->count_out = 0;
+ line6pcm->pos_out = 0;
+ line6pcm->pos_out_done = 0;
+ line6pcm->bytes_out = 0;
+ line6pcm->count_in = 0;
+ line6pcm->pos_in_done = 0;
+ line6pcm->bytes_in = 0;
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/line6/pcm.h b/drivers/staging/line6/pcm.h
new file mode 100644
index 00000000..5210ec8d
--- /dev/null
+++ b/drivers/staging/line6/pcm.h
@@ -0,0 +1,382 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+/*
+ PCM interface to POD series devices.
+*/
+
+#ifndef PCM_H
+#define PCM_H
+
+#include <sound/pcm.h>
+
+#include "driver.h"
+#include "usbdefs.h"
+
+/* number of URBs */
+#define LINE6_ISO_BUFFERS 2
+
+/*
+ number of USB frames per URB
+ The Line6 Windows driver always transmits two frames per packet, but
+ the Linux driver performs significantly better (i.e., lower latency)
+ with only one frame per packet.
+*/
+#define LINE6_ISO_PACKETS 1
+
+/* in a "full speed" device (such as the PODxt Pro) this means 1ms */
+#define LINE6_ISO_INTERVAL 1
+
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+#define LINE6_IMPULSE_DEFAULT_PERIOD 100
+#endif
+
+/*
+ Get substream from Line6 PCM data structure
+*/
+#define get_substream(line6pcm, stream) \
+ (line6pcm->pcm->streams[stream].substream)
+
+/*
+ PCM mode bits.
+
+ There are several features of the Line6 USB driver which require PCM
+ data to be exchanged with the device:
+ *) PCM playback and capture via ALSA
+ *) software monitoring (for devices without hardware monitoring)
+ *) optional impulse response measurement
+ However, from the device's point of view, there is just a single
+ capture and playback stream, which must be shared between these
+ subsystems. It is therefore necessary to maintain the state of the
+ subsystems with respect to PCM usage. We define several constants of
+ the form LINE6_BIT_PCM_<subsystem>_<direction>_<resource> with the
+ following meanings:
+ *) <subsystem> is one of
+ -) ALSA: PCM playback and capture via ALSA
+ -) MONITOR: software monitoring
+ -) IMPULSE: optional impulse response measurement
+ *) <direction> is one of
+ -) PLAYBACK: audio output (from host to device)
+ -) CAPTURE: audio input (from device to host)
+ *) <resource> is one of
+ -) BUFFER: buffer required by PCM data stream
+ -) STREAM: actual PCM data stream
+
+ The subsystems call line6_pcm_acquire() to acquire the (shared)
+ resources needed for a particular operation (e.g., allocate the buffer
+ for ALSA playback or start the capture stream for software monitoring).
+ When a resource is no longer needed, it is released by calling
+ line6_pcm_release(). Buffer allocation and stream startup are handled
+ separately to allow the ALSA kernel driver to perform them at
+ appropriate places (since the callback which starts a PCM stream is not
+ allowed to sleep).
+*/
+enum {
+ /* individual bit indices: */
+ LINE6_INDEX_PCM_ALSA_PLAYBACK_BUFFER,
+ LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM,
+ LINE6_INDEX_PCM_ALSA_CAPTURE_BUFFER,
+ LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM,
+ LINE6_INDEX_PCM_MONITOR_PLAYBACK_BUFFER,
+ LINE6_INDEX_PCM_MONITOR_PLAYBACK_STREAM,
+ LINE6_INDEX_PCM_MONITOR_CAPTURE_BUFFER,
+ LINE6_INDEX_PCM_MONITOR_CAPTURE_STREAM,
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ LINE6_INDEX_PCM_IMPULSE_PLAYBACK_BUFFER,
+ LINE6_INDEX_PCM_IMPULSE_PLAYBACK_STREAM,
+ LINE6_INDEX_PCM_IMPULSE_CAPTURE_BUFFER,
+ LINE6_INDEX_PCM_IMPULSE_CAPTURE_STREAM,
+#endif
+ LINE6_INDEX_PAUSE_PLAYBACK,
+ LINE6_INDEX_PREPARED,
+
+ /* individual bit masks: */
+ LINE6_BIT(PCM_ALSA_PLAYBACK_BUFFER),
+ LINE6_BIT(PCM_ALSA_PLAYBACK_STREAM),
+ LINE6_BIT(PCM_ALSA_CAPTURE_BUFFER),
+ LINE6_BIT(PCM_ALSA_CAPTURE_STREAM),
+ LINE6_BIT(PCM_MONITOR_PLAYBACK_BUFFER),
+ LINE6_BIT(PCM_MONITOR_PLAYBACK_STREAM),
+ LINE6_BIT(PCM_MONITOR_CAPTURE_BUFFER),
+ LINE6_BIT(PCM_MONITOR_CAPTURE_STREAM),
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ LINE6_BIT(PCM_IMPULSE_PLAYBACK_BUFFER),
+ LINE6_BIT(PCM_IMPULSE_PLAYBACK_STREAM),
+ LINE6_BIT(PCM_IMPULSE_CAPTURE_BUFFER),
+ LINE6_BIT(PCM_IMPULSE_CAPTURE_STREAM),
+#endif
+ LINE6_BIT(PAUSE_PLAYBACK),
+ LINE6_BIT(PREPARED),
+
+ /* combined bit masks (by operation): */
+ LINE6_BITS_PCM_ALSA_BUFFER =
+ LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER |
+ LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER,
+
+ LINE6_BITS_PCM_ALSA_STREAM =
+ LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM |
+ LINE6_BIT_PCM_ALSA_CAPTURE_STREAM,
+
+ LINE6_BITS_PCM_MONITOR =
+ LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER |
+ LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM |
+ LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER |
+ LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM,
+
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ LINE6_BITS_PCM_IMPULSE =
+ LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER |
+ LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM |
+ LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER |
+ LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM,
+#endif
+
+ /* combined bit masks (by direction): */
+ LINE6_BITS_PLAYBACK_BUFFER =
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER |
+#endif
+ LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER |
+ LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER ,
+
+ LINE6_BITS_PLAYBACK_STREAM =
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM |
+#endif
+ LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM |
+ LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM ,
+
+ LINE6_BITS_CAPTURE_BUFFER =
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER |
+#endif
+ LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER |
+ LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER ,
+
+ LINE6_BITS_CAPTURE_STREAM =
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM |
+#endif
+ LINE6_BIT_PCM_ALSA_CAPTURE_STREAM |
+ LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM,
+
+ LINE6_BITS_STREAM =
+ LINE6_BITS_PLAYBACK_STREAM |
+ LINE6_BITS_CAPTURE_STREAM
+};
+
+struct line6_pcm_properties {
+ struct snd_pcm_hardware snd_line6_playback_hw, snd_line6_capture_hw;
+ struct snd_pcm_hw_constraint_ratdens snd_line6_rates;
+ int bytes_per_frame;
+};
+
+struct snd_line6_pcm {
+ /**
+ Pointer back to the Line6 driver data structure.
+ */
+ struct usb_line6 *line6;
+
+ /**
+ Properties.
+ */
+ struct line6_pcm_properties *properties;
+
+ /**
+ ALSA pcm stream
+ */
+ struct snd_pcm *pcm;
+
+ /**
+ URBs for audio playback.
+ */
+ struct urb *urb_audio_out[LINE6_ISO_BUFFERS];
+
+ /**
+ URBs for audio capture.
+ */
+ struct urb *urb_audio_in[LINE6_ISO_BUFFERS];
+
+ /**
+ Temporary buffer for playback.
+ Since the packet size is not known in advance, this buffer is
+ large enough to store maximum size packets.
+ */
+ unsigned char *buffer_out;
+
+ /**
+ Temporary buffer for capture.
+ Since the packet size is not known in advance, this buffer is
+ large enough to store maximum size packets.
+ */
+ unsigned char *buffer_in;
+
+ /**
+ Previously captured frame (for software monitoring).
+ */
+ unsigned char *prev_fbuf;
+
+ /**
+ Size of previously captured frame (for software monitoring).
+ */
+ int prev_fsize;
+
+ /**
+ Free frame position in the playback buffer.
+ */
+ snd_pcm_uframes_t pos_out;
+
+ /**
+ Count processed bytes for playback.
+ This is modulo period size (to determine when a period is
+ finished).
+ */
+ unsigned bytes_out;
+
+ /**
+ Counter to create desired playback sample rate.
+ */
+ unsigned count_out;
+
+ /**
+ Playback period size in bytes
+ */
+ unsigned period_out;
+
+ /**
+ Processed frame position in the playback buffer.
+ The contents of the output ring buffer have been consumed by
+ the USB subsystem (i.e., sent to the USB device) up to this
+ position.
+ */
+ snd_pcm_uframes_t pos_out_done;
+
+ /**
+ Count processed bytes for capture.
+ This is modulo period size (to determine when a period is
+ finished).
+ */
+ unsigned bytes_in;
+
+ /**
+ Counter to create desired capture sample rate.
+ */
+ unsigned count_in;
+
+ /**
+ Capture period size in bytes
+ */
+ unsigned period_in;
+
+ /**
+ Processed frame position in the capture buffer.
+ The contents of the output ring buffer have been consumed by
+ the USB subsystem (i.e., sent to the USB device) up to this
+ position.
+ */
+ snd_pcm_uframes_t pos_in_done;
+
+ /**
+ Bit mask of active playback URBs.
+ */
+ unsigned long active_urb_out;
+
+ /**
+ Maximum size of USB packet.
+ */
+ int max_packet_size;
+
+ /**
+ USB endpoint for listening to audio data.
+ */
+ int ep_audio_read;
+
+ /**
+ USB endpoint for writing audio data.
+ */
+ int ep_audio_write;
+
+ /**
+ Bit mask of active capture URBs.
+ */
+ unsigned long active_urb_in;
+
+ /**
+ Bit mask of playback URBs currently being unlinked.
+ */
+ unsigned long unlink_urb_out;
+
+ /**
+ Bit mask of capture URBs currently being unlinked.
+ */
+ unsigned long unlink_urb_in;
+
+ /**
+ Spin lock to protect updates of the playback buffer positions (not
+ contents!)
+ */
+ spinlock_t lock_audio_out;
+
+ /**
+ Spin lock to protect updates of the capture buffer positions (not
+ contents!)
+ */
+ spinlock_t lock_audio_in;
+
+ /**
+ Spin lock to protect trigger.
+ */
+ spinlock_t lock_trigger;
+
+ /**
+ PCM playback volume (left and right).
+ */
+ int volume_playback[2];
+
+ /**
+ PCM monitor volume.
+ */
+ int volume_monitor;
+
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ /**
+ Volume of impulse response test signal (if zero, test is disabled).
+ */
+ int impulse_volume;
+
+ /**
+ Period of impulse response test signal.
+ */
+ int impulse_period;
+
+ /**
+ Counter for impulse response test signal.
+ */
+ int impulse_count;
+#endif
+
+ /**
+ Several status bits (see LINE6_BIT_*).
+ */
+ unsigned long flags;
+
+ int last_frame_in, last_frame_out;
+};
+
+extern int line6_init_pcm(struct usb_line6 *line6,
+ struct line6_pcm_properties *properties);
+extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd);
+extern int snd_line6_prepare(struct snd_pcm_substream *substream);
+extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm);
+extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels);
+extern int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels);
+
+#endif
diff --git a/drivers/staging/line6/playback.c b/drivers/staging/line6/playback.c
new file mode 100644
index 00000000..a0ab9d04
--- /dev/null
+++ b/drivers/staging/line6/playback.c
@@ -0,0 +1,586 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "audio.h"
+#include "capture.h"
+#include "driver.h"
+#include "pcm.h"
+#include "pod.h"
+#include "playback.h"
+
+/*
+ Software stereo volume control.
+*/
+static void change_volume(struct urb *urb_out, int volume[],
+ int bytes_per_frame)
+{
+ int chn = 0;
+
+ if (volume[0] == 256 && volume[1] == 256)
+ return; /* maximum volume - no change */
+
+ if (bytes_per_frame == 4) {
+ short *p, *buf_end;
+ p = (short *)urb_out->transfer_buffer;
+ buf_end = p + urb_out->transfer_buffer_length / sizeof(*p);
+
+ for (; p < buf_end; ++p) {
+ *p = (*p * volume[chn & 1]) >> 8;
+ ++chn;
+ }
+ } else if (bytes_per_frame == 6) {
+ unsigned char *p, *buf_end;
+ p = (unsigned char *)urb_out->transfer_buffer;
+ buf_end = p + urb_out->transfer_buffer_length;
+
+ for (; p < buf_end; p += 3) {
+ int val;
+ val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16);
+ val = (val * volume[chn & 1]) >> 8;
+ p[0] = val;
+ p[1] = val >> 8;
+ p[2] = val >> 16;
+ ++chn;
+ }
+ }
+}
+
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+
+/*
+ Create signal for impulse response test.
+*/
+static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm,
+ struct urb *urb_out, int bytes_per_frame)
+{
+ int frames = urb_out->transfer_buffer_length / bytes_per_frame;
+
+ if (bytes_per_frame == 4) {
+ int i;
+ short *pi = (short *)line6pcm->prev_fbuf;
+ short *po = (short *)urb_out->transfer_buffer;
+
+ for (i = 0; i < frames; ++i) {
+ po[0] = pi[0];
+ po[1] = 0;
+ pi += 2;
+ po += 2;
+ }
+ } else if (bytes_per_frame == 6) {
+ int i, j;
+ unsigned char *pi = line6pcm->prev_fbuf;
+ unsigned char *po = urb_out->transfer_buffer;
+
+ for (i = 0; i < frames; ++i) {
+ for (j = 0; j < bytes_per_frame / 2; ++j)
+ po[j] = pi[j];
+
+ for (; j < bytes_per_frame; ++j)
+ po[j] = 0;
+
+ pi += bytes_per_frame;
+ po += bytes_per_frame;
+ }
+ }
+ if (--line6pcm->impulse_count <= 0) {
+ ((unsigned char *)(urb_out->transfer_buffer))[bytes_per_frame -
+ 1] =
+ line6pcm->impulse_volume;
+ line6pcm->impulse_count = line6pcm->impulse_period;
+ }
+}
+
+#endif
+
+/*
+ Add signal to buffer for software monitoring.
+*/
+static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
+ int volume, int bytes_per_frame)
+{
+ if (volume == 0)
+ return; /* zero volume - no change */
+
+ if (bytes_per_frame == 4) {
+ short *pi, *po, *buf_end;
+ pi = (short *)signal;
+ po = (short *)urb_out->transfer_buffer;
+ buf_end = po + urb_out->transfer_buffer_length / sizeof(*po);
+
+ for (; po < buf_end; ++pi, ++po)
+ *po += (*pi * volume) >> 8;
+ }
+
+ /*
+ We don't need to handle devices with 6 bytes per frame here
+ since they all support hardware monitoring.
+ */
+}
+
+/*
+ Find a free URB, prepare audio data, and submit URB.
+*/
+static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
+{
+ int index;
+ unsigned long flags;
+ int i, urb_size, urb_frames;
+ int ret;
+ const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+ const int frame_increment =
+ line6pcm->properties->snd_line6_rates.rats[0].num_min;
+ const int frame_factor =
+ line6pcm->properties->snd_line6_rates.rats[0].den *
+ (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
+ struct urb *urb_out;
+
+ spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
+ index =
+ find_first_zero_bit(&line6pcm->active_urb_out, LINE6_ISO_BUFFERS);
+
+ if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+ spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
+ dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
+ return -EINVAL;
+ }
+
+ urb_out = line6pcm->urb_audio_out[index];
+ urb_size = 0;
+
+ for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
+ /* compute frame size for given sampling rate */
+ int fsize = 0;
+ struct usb_iso_packet_descriptor *fout =
+ &urb_out->iso_frame_desc[i];
+
+ if (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM)
+ fsize = line6pcm->prev_fsize;
+
+ if (fsize == 0) {
+ int n;
+ line6pcm->count_out += frame_increment;
+ n = line6pcm->count_out / frame_factor;
+ line6pcm->count_out -= n * frame_factor;
+ fsize = n * bytes_per_frame;
+ }
+
+ fout->offset = urb_size;
+ fout->length = fsize;
+ urb_size += fsize;
+ }
+
+ if (urb_size == 0) {
+ /* can't determine URB size */
+ spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
+ dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); /* this is somewhat paranoid */
+ return -EINVAL;
+ }
+
+ urb_frames = urb_size / bytes_per_frame;
+ urb_out->transfer_buffer =
+ line6pcm->buffer_out +
+ index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+ urb_out->transfer_buffer_length = urb_size;
+ urb_out->context = line6pcm;
+
+ if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags) &&
+ !test_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags)) {
+ struct snd_pcm_runtime *runtime =
+ get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime;
+
+ if (line6pcm->pos_out + urb_frames > runtime->buffer_size) {
+ /*
+ The transferred area goes over buffer boundary,
+ copy the data to the temp buffer.
+ */
+ int len;
+ len = runtime->buffer_size - line6pcm->pos_out;
+
+ if (len > 0) {
+ memcpy(urb_out->transfer_buffer,
+ runtime->dma_area +
+ line6pcm->pos_out * bytes_per_frame,
+ len * bytes_per_frame);
+ memcpy(urb_out->transfer_buffer +
+ len * bytes_per_frame, runtime->dma_area,
+ (urb_frames - len) * bytes_per_frame);
+ } else
+ dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", len); /* this is somewhat paranoid */
+ } else {
+ memcpy(urb_out->transfer_buffer,
+ runtime->dma_area +
+ line6pcm->pos_out * bytes_per_frame,
+ urb_out->transfer_buffer_length);
+ }
+
+ line6pcm->pos_out += urb_frames;
+ if (line6pcm->pos_out >= runtime->buffer_size)
+ line6pcm->pos_out -= runtime->buffer_size;
+ } else {
+ memset(urb_out->transfer_buffer, 0,
+ urb_out->transfer_buffer_length);
+ }
+
+ change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame);
+
+ if (line6pcm->prev_fbuf != NULL) {
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ if (line6pcm->flags & LINE6_BITS_PCM_IMPULSE) {
+ create_impulse_test_signal(line6pcm, urb_out,
+ bytes_per_frame);
+ if (line6pcm->flags & LINE6_BIT_PCM_ALSA_CAPTURE_STREAM) {
+ line6_capture_copy(line6pcm,
+ urb_out->transfer_buffer,
+ urb_out->
+ transfer_buffer_length);
+ line6_capture_check_period(line6pcm,
+ urb_out->transfer_buffer_length);
+ }
+ } else {
+#endif
+ if (!
+ (line6pcm->line6->
+ properties->capabilities & LINE6_BIT_HWMON)
+ && (line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM)
+ && (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM))
+ add_monitor_signal(urb_out, line6pcm->prev_fbuf,
+ line6pcm->volume_monitor,
+ bytes_per_frame);
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+ }
+#endif
+ }
+#ifdef CONFIG_LINE6_USB_DUMP_PCM
+ for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
+ struct usb_iso_packet_descriptor *fout =
+ &urb_out->iso_frame_desc[i];
+ line6_write_hexdump(line6pcm->line6, 'P',
+ urb_out->transfer_buffer + fout->offset,
+ fout->length);
+ }
+#endif
+
+ ret = usb_submit_urb(urb_out, GFP_ATOMIC);
+
+ if (ret == 0)
+ set_bit(index, &line6pcm->active_urb_out);
+ else
+ dev_err(line6pcm->line6->ifcdev,
+ "URB out #%d submission failed (%d)\n", index, ret);
+
+ spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
+ return 0;
+}
+
+/*
+ Submit all currently available playback URBs.
+*/
+int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
+{
+ int ret, i;
+
+ for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+ ret = submit_audio_out_urb(line6pcm);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ Unlink all currently active playback URBs.
+*/
+void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
+{
+ unsigned int i;
+
+ for (i = LINE6_ISO_BUFFERS; i--;) {
+ if (test_bit(i, &line6pcm->active_urb_out)) {
+ if (!test_and_set_bit(i, &line6pcm->unlink_urb_out)) {
+ struct urb *u = line6pcm->urb_audio_out[i];
+ usb_unlink_urb(u);
+ }
+ }
+ }
+}
+
+/*
+ Wait until unlinking of all currently active playback URBs has been finished.
+*/
+void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
+{
+ int timeout = HZ;
+ unsigned int i;
+ int alive;
+
+ do {
+ alive = 0;
+ for (i = LINE6_ISO_BUFFERS; i--;) {
+ if (test_bit(i, &line6pcm->active_urb_out))
+ alive++;
+ }
+ if (!alive)
+ break;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (--timeout > 0);
+ if (alive)
+ snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
+}
+
+/*
+ Unlink all currently active playback URBs, and wait for finishing.
+*/
+void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
+{
+ line6_unlink_audio_out_urbs(line6pcm);
+ line6_wait_clear_audio_out_urbs(line6pcm);
+}
+
+void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm)
+{
+ kfree(line6pcm->buffer_out);
+ line6pcm->buffer_out = NULL;
+}
+
+/*
+ Callback for completed playback URB.
+*/
+static void audio_out_callback(struct urb *urb)
+{
+ int i, index, length = 0, shutdown = 0;
+ unsigned long flags;
+
+ struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
+ struct snd_pcm_substream *substream =
+ get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
+
+#if USE_CLEAR_BUFFER_WORKAROUND
+ memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
+#endif
+
+ line6pcm->last_frame_out = urb->start_frame;
+
+ /* find index of URB */
+ for (index = LINE6_ISO_BUFFERS; index--;)
+ if (urb == line6pcm->urb_audio_out[index])
+ break;
+
+ if (index < 0)
+ return; /* URB has been unlinked asynchronously */
+
+ for (i = LINE6_ISO_PACKETS; i--;)
+ length += urb->iso_frame_desc[i].length;
+
+ spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
+
+ if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags)) {
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ line6pcm->pos_out_done +=
+ length / line6pcm->properties->bytes_per_frame;
+
+ if (line6pcm->pos_out_done >= runtime->buffer_size)
+ line6pcm->pos_out_done -= runtime->buffer_size;
+ }
+
+ clear_bit(index, &line6pcm->active_urb_out);
+
+ for (i = LINE6_ISO_PACKETS; i--;)
+ if (urb->iso_frame_desc[i].status == -EXDEV) {
+ shutdown = 1;
+ break;
+ }
+
+ if (test_and_clear_bit(index, &line6pcm->unlink_urb_out))
+ shutdown = 1;
+
+ spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
+
+ if (!shutdown) {
+ submit_audio_out_urb(line6pcm);
+
+ if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags)) {
+ line6pcm->bytes_out += length;
+ if (line6pcm->bytes_out >= line6pcm->period_out) {
+ line6pcm->bytes_out %= line6pcm->period_out;
+ snd_pcm_period_elapsed(substream);
+ }
+ }
+ }
+}
+
+/* open playback callback */
+static int snd_line6_playback_open(struct snd_pcm_substream *substream)
+{
+ int err;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+
+ err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ (&line6pcm->
+ properties->snd_line6_rates));
+ if (err < 0)
+ return err;
+
+ runtime->hw = line6pcm->properties->snd_line6_playback_hw;
+ return 0;
+}
+
+/* close playback callback */
+static int snd_line6_playback_close(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/* hw_params playback callback */
+static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int ret;
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+
+ /* -- Florian Demski [FD] */
+ /* don't ask me why, but this fixes the bug on my machine */
+ if (line6pcm == NULL) {
+ if (substream->pcm == NULL)
+ return -ENOMEM;
+ if (substream->pcm->private_data == NULL)
+ return -ENOMEM;
+ substream->private_data = substream->pcm->private_data;
+ line6pcm = snd_pcm_substream_chip(substream);
+ }
+ /* -- [FD] end */
+
+ ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER);
+
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (ret < 0) {
+ line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER);
+ return ret;
+ }
+
+ line6pcm->period_out = params_period_bytes(hw_params);
+ return 0;
+}
+
+/* hw_free playback callback */
+static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+ line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* trigger playback callback */
+int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd)
+{
+ int err;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+#ifdef CONFIG_PM
+ case SNDRV_PCM_TRIGGER_RESUME:
+#endif
+ err = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM);
+
+ if (err < 0)
+ return err;
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+#ifdef CONFIG_PM
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+#endif
+ err = line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM);
+
+ if (err < 0)
+ return err;
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ set_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags);
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ clear_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* playback pointer callback */
+static snd_pcm_uframes_t
+snd_line6_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+ return line6pcm->pos_out_done;
+}
+
+/* playback operators */
+struct snd_pcm_ops snd_line6_playback_ops = {
+ .open = snd_line6_playback_open,
+ .close = snd_line6_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_line6_playback_hw_params,
+ .hw_free = snd_line6_playback_hw_free,
+ .prepare = snd_line6_prepare,
+ .trigger = snd_line6_trigger,
+ .pointer = snd_line6_playback_pointer,
+};
+
+int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
+{
+ int i;
+
+ /* create audio URBs and fill in constant values: */
+ for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+ struct urb *urb;
+
+ /* URB for audio out: */
+ urb = line6pcm->urb_audio_out[i] =
+ usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);
+
+ if (urb == NULL) {
+ dev_err(line6pcm->line6->ifcdev, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ urb->dev = line6pcm->line6->usbdev;
+ urb->pipe =
+ usb_sndisocpipe(line6pcm->line6->usbdev,
+ line6pcm->ep_audio_write &
+ USB_ENDPOINT_NUMBER_MASK);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->start_frame = -1;
+ urb->number_of_packets = LINE6_ISO_PACKETS;
+ urb->interval = LINE6_ISO_INTERVAL;
+ urb->error_count = 0;
+ urb->complete = audio_out_callback;
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/line6/playback.h b/drivers/staging/line6/playback.h
new file mode 100644
index 00000000..743bd6f7
--- /dev/null
+++ b/drivers/staging/line6/playback.h
@@ -0,0 +1,41 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef PLAYBACK_H
+#define PLAYBACK_H
+
+#include <sound/pcm.h>
+
+#include "driver.h"
+
+/*
+ * When the TonePort is used with jack in full duplex mode and the outputs are
+ * not connected, the software monitor produces an ugly noise since everything
+ * written to the output buffer (i.e., the input signal) will be repeated in
+ * the next period (sounds like a delay effect). As a workaround, the output
+ * buffer is cleared after the data have been read, but there must be a better
+ * solution. Until one is found, this workaround can be used to fix the
+ * problem.
+ */
+#define USE_CLEAR_BUFFER_WORKAROUND 1
+
+extern struct snd_pcm_ops snd_line6_playback_ops;
+
+extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm);
+extern void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm);
+extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm);
+extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm);
+extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm
+ *line6pcm);
+extern void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm);
+extern int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd);
+
+#endif
diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c
new file mode 100644
index 00000000..4dadc571
--- /dev/null
+++ b/drivers/staging/line6/pod.c
@@ -0,0 +1,1351 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <sound/control.h>
+
+#include "audio.h"
+#include "capture.h"
+#include "control.h"
+#include "driver.h"
+#include "playback.h"
+#include "pod.h"
+
+#define POD_SYSEX_CODE 3
+#define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */
+
+/* *INDENT-OFF* */
+
+enum {
+ POD_SYSEX_CLIP = 0x0f,
+ POD_SYSEX_SAVE = 0x24,
+ POD_SYSEX_SYSTEM = 0x56,
+ POD_SYSEX_SYSTEMREQ = 0x57,
+ /* POD_SYSEX_UPDATE = 0x6c, */ /* software update! */
+ POD_SYSEX_STORE = 0x71,
+ POD_SYSEX_FINISH = 0x72,
+ POD_SYSEX_DUMPMEM = 0x73,
+ POD_SYSEX_DUMP = 0x74,
+ POD_SYSEX_DUMPREQ = 0x75
+ /* POD_SYSEX_DUMPMEM2 = 0x76 */ /* dumps entire internal memory of PODxt Pro */
+};
+
+enum {
+ POD_monitor_level = 0x04,
+ POD_routing = 0x05,
+ POD_tuner_mute = 0x13,
+ POD_tuner_freq = 0x15,
+ POD_tuner_note = 0x16,
+ POD_tuner_pitch = 0x17,
+ POD_system_invalid = 0x10000
+};
+
+/* *INDENT-ON* */
+
+enum {
+ POD_DUMP_MEMORY = 2
+};
+
+enum {
+ POD_BUSY_READ,
+ POD_BUSY_WRITE,
+ POD_CHANNEL_DIRTY,
+ POD_SAVE_PRESSED,
+ POD_BUSY_MIDISEND
+};
+
+static struct snd_ratden pod_ratden = {
+ .num_min = 78125,
+ .num_max = 78125,
+ .num_step = 1,
+ .den = 2
+};
+
+static struct line6_pcm_properties pod_pcm_properties = {
+ .snd_line6_playback_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+#ifdef CONFIG_PM
+ SNDRV_PCM_INFO_RESUME |
+#endif
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .rate_min = 39062,
+ .rate_max = 39063,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 60000,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 1024},
+ .snd_line6_capture_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+#ifdef CONFIG_PM
+ SNDRV_PCM_INFO_RESUME |
+#endif
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .rate_min = 39062,
+ .rate_max = 39063,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 60000,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 1024},
+ .snd_line6_rates = {
+ .nrats = 1,
+ .rats = &pod_ratden},
+ .bytes_per_frame = POD_BYTES_PER_FRAME
+};
+
+static const char pod_request_channel[] = {
+ 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7
+};
+
+static const char pod_version_header[] = {
+ 0xf2, 0x7e, 0x7f, 0x06, 0x02
+};
+
+/* forward declarations: */
+static void pod_startup2(unsigned long data);
+static void pod_startup3(struct usb_line6_pod *pod);
+static void pod_startup4(struct usb_line6_pod *pod);
+
+/*
+ Mark all parameters as dirty and notify waiting processes.
+*/
+static void pod_mark_batch_all_dirty(struct usb_line6_pod *pod)
+{
+ int i;
+
+ for (i = 0; i < POD_CONTROL_SIZE; i++)
+ set_bit(i, pod->param_dirty);
+}
+
+static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code,
+ int size)
+{
+ return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code,
+ size);
+}
+
+/*
+ Send channel dump data to the PODxt Pro.
+*/
+static void pod_dump(struct usb_line6_pod *pod, const unsigned char *data)
+{
+ int size = 1 + sizeof(pod->prog_data);
+ char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_DUMP, size);
+ if (!sysex)
+ return;
+ /* Don't know what this is good for, but PODxt Pro transmits it, so we
+ * also do... */
+ sysex[SYSEX_DATA_OFS] = 5;
+ memcpy(sysex + SYSEX_DATA_OFS + 1, data, sizeof(pod->prog_data));
+ line6_send_sysex_message(&pod->line6, sysex, size);
+ memcpy(&pod->prog_data, data, sizeof(pod->prog_data));
+ pod_mark_batch_all_dirty(pod);
+ kfree(sysex);
+}
+
+/*
+ Store parameter value in driver memory and mark it as dirty.
+*/
+static void pod_store_parameter(struct usb_line6_pod *pod, int param, int value)
+{
+ pod->prog_data.control[param] = value;
+ set_bit(param, pod->param_dirty);
+ pod->dirty = 1;
+}
+
+/*
+ Handle SAVE button.
+*/
+static void pod_save_button_pressed(struct usb_line6_pod *pod, int type,
+ int index)
+{
+ pod->dirty = 0;
+ set_bit(POD_SAVE_PRESSED, &pod->atomic_flags);
+}
+
+/*
+ Process a completely received message.
+*/
+void line6_pod_process_message(struct usb_line6_pod *pod)
+{
+ const unsigned char *buf = pod->line6.buffer_message;
+
+ /* filter messages by type */
+ switch (buf[0] & 0xf0) {
+ case LINE6_PARAM_CHANGE:
+ case LINE6_PROGRAM_CHANGE:
+ case LINE6_SYSEX_BEGIN:
+ break; /* handle these further down */
+
+ default:
+ return; /* ignore all others */
+ }
+
+ /* process all remaining messages */
+ switch (buf[0]) {
+ case LINE6_PARAM_CHANGE | LINE6_CHANNEL_DEVICE:
+ pod_store_parameter(pod, buf[1], buf[2]);
+ /* intentionally no break here! */
+
+ case LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST:
+ if ((buf[1] == POD_amp_model_setup) ||
+ (buf[1] == POD_effect_setup))
+ /* these also affect other settings */
+ line6_dump_request_async(&pod->dumpreq, &pod->line6, 0,
+ LINE6_DUMP_CURRENT);
+
+ break;
+
+ case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE:
+ case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST:
+ pod->channel_num = buf[1];
+ pod->dirty = 0;
+ set_bit(POD_CHANNEL_DIRTY, &pod->atomic_flags);
+ line6_dump_request_async(&pod->dumpreq, &pod->line6, 0,
+ LINE6_DUMP_CURRENT);
+ break;
+
+ case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE:
+ case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN:
+ if (memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) == 0) {
+ switch (buf[5]) {
+ case POD_SYSEX_DUMP:
+ if (pod->line6.message_length ==
+ sizeof(pod->prog_data) + 7) {
+ switch (pod->dumpreq.in_progress) {
+ case LINE6_DUMP_CURRENT:
+ memcpy(&pod->prog_data, buf + 7,
+ sizeof(pod->prog_data));
+ pod_mark_batch_all_dirty(pod);
+ break;
+
+ case POD_DUMP_MEMORY:
+ memcpy(&pod->prog_data_buf,
+ buf + 7,
+ sizeof
+ (pod->prog_data_buf));
+ break;
+
+ default:
+ DEBUG_MESSAGES(dev_err
+ (pod->
+ line6.ifcdev,
+ "unknown dump code %02X\n",
+ pod->
+ dumpreq.in_progress));
+ }
+
+ line6_dump_finished(&pod->dumpreq);
+ pod_startup3(pod);
+ } else
+ DEBUG_MESSAGES(dev_err
+ (pod->line6.ifcdev,
+ "wrong size of channel dump message (%d instead of %d)\n",
+ pod->
+ line6.message_length,
+ (int)
+ sizeof(pod->prog_data) +
+ 7));
+
+ break;
+
+ case POD_SYSEX_SYSTEM:{
+ short value =
+ ((int)buf[7] << 12) | ((int)buf[8]
+ << 8) |
+ ((int)buf[9] << 4) | (int)buf[10];
+
+#define PROCESS_SYSTEM_PARAM(x) \
+ case POD_ ## x: \
+ pod->x.value = value; \
+ wake_up(&pod->x.wait); \
+ break;
+
+ switch (buf[6]) {
+ PROCESS_SYSTEM_PARAM
+ (monitor_level);
+ PROCESS_SYSTEM_PARAM(routing);
+ PROCESS_SYSTEM_PARAM
+ (tuner_mute);
+ PROCESS_SYSTEM_PARAM
+ (tuner_freq);
+ PROCESS_SYSTEM_PARAM
+ (tuner_note);
+ PROCESS_SYSTEM_PARAM
+ (tuner_pitch);
+
+#undef PROCESS_SYSTEM_PARAM
+
+ default:
+ DEBUG_MESSAGES(dev_err
+ (pod->
+ line6.ifcdev,
+ "unknown tuner/system response %02X\n",
+ buf[6]));
+ }
+
+ break;
+ }
+
+ case POD_SYSEX_FINISH:
+ /* do we need to respond to this? */
+ break;
+
+ case POD_SYSEX_SAVE:
+ pod_save_button_pressed(pod, buf[6], buf[7]);
+ break;
+
+ case POD_SYSEX_CLIP:
+ DEBUG_MESSAGES(dev_err
+ (pod->line6.ifcdev,
+ "audio clipped\n"));
+ pod->clipping.value = 1;
+ wake_up(&pod->clipping.wait);
+ break;
+
+ case POD_SYSEX_STORE:
+ DEBUG_MESSAGES(dev_err
+ (pod->line6.ifcdev,
+ "message %02X not yet implemented\n",
+ buf[5]));
+ break;
+
+ default:
+ DEBUG_MESSAGES(dev_err
+ (pod->line6.ifcdev,
+ "unknown sysex message %02X\n",
+ buf[5]));
+ }
+ } else
+ if (memcmp
+ (buf, pod_version_header,
+ sizeof(pod_version_header)) == 0) {
+ pod->firmware_version =
+ buf[13] * 100 + buf[14] * 10 + buf[15];
+ pod->device_id =
+ ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int)
+ buf[10];
+ pod_startup4(pod);
+ } else
+ DEBUG_MESSAGES(dev_err
+ (pod->line6.ifcdev,
+ "unknown sysex header\n"));
+
+ break;
+
+ case LINE6_SYSEX_END:
+ break;
+
+ default:
+ DEBUG_MESSAGES(dev_err
+ (pod->line6.ifcdev,
+ "POD: unknown message %02X\n", buf[0]));
+ }
+}
+
+/*
+ Detect some cases that require a channel dump after sending a command to the
+ device. Important notes:
+ *) The actual dump request can not be sent here since we are not allowed to
+ wait for the completion of the first message in this context, and sending
+ the dump request before completion of the previous message leaves the POD
+ in an undefined state. The dump request will be sent when the echoed
+ commands are received.
+ *) This method fails if a param change message is "chopped" after the first
+ byte.
+*/
+void line6_pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data,
+ int length)
+{
+ int i;
+
+ if (!pod->midi_postprocess)
+ return;
+
+ for (i = 0; i < length; ++i) {
+ if (data[i] == (LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST)) {
+ line6_invalidate_current(&pod->dumpreq);
+ break;
+ } else
+ if ((data[i] == (LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST))
+ && (i < length - 1))
+ if ((data[i + 1] == POD_amp_model_setup)
+ || (data[i + 1] == POD_effect_setup)) {
+ line6_invalidate_current(&pod->dumpreq);
+ break;
+ }
+ }
+}
+
+/*
+ Send channel number (i.e., switch to a different sound).
+*/
+static void pod_send_channel(struct usb_line6_pod *pod, int value)
+{
+ line6_invalidate_current(&pod->dumpreq);
+
+ if (line6_send_program(&pod->line6, value) == 0)
+ pod->channel_num = value;
+ else
+ line6_dump_finished(&pod->dumpreq);
+}
+
+/*
+ Transmit PODxt Pro control parameter.
+*/
+void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param,
+ int value)
+{
+ if (line6_transmit_parameter(&pod->line6, param, value) == 0)
+ pod_store_parameter(pod, param, value);
+
+ if ((param == POD_amp_model_setup) || (param == POD_effect_setup)) /* these also affect other settings */
+ line6_invalidate_current(&pod->dumpreq);
+}
+
+/*
+ Resolve value to memory location.
+*/
+static int pod_resolve(const char *buf, short block0, short block1,
+ unsigned char *location)
+{
+ unsigned long value;
+ short block;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ block = (value < 0x40) ? block0 : block1;
+ value &= 0x3f;
+ location[0] = block >> 7;
+ location[1] = value | (block & 0x7f);
+ return 0;
+}
+
+/*
+ Send command to store channel/effects setup/amp setup to PODxt Pro.
+*/
+static ssize_t pod_send_store_command(struct device *dev, const char *buf,
+ size_t count, short block0, short block1)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ int ret;
+ int size = 3 + sizeof(pod->prog_data_buf);
+ char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_STORE, size);
+
+ if (!sysex)
+ return 0;
+
+ sysex[SYSEX_DATA_OFS] = 5; /* see pod_dump() */
+ ret = pod_resolve(buf, block0, block1, sysex + SYSEX_DATA_OFS + 1);
+ if (ret) {
+ kfree(sysex);
+ return ret;
+ }
+
+ memcpy(sysex + SYSEX_DATA_OFS + 3, &pod->prog_data_buf,
+ sizeof(pod->prog_data_buf));
+
+ line6_send_sysex_message(&pod->line6, sysex, size);
+ kfree(sysex);
+ /* needs some delay here on AMD64 platform */
+ return count;
+}
+
+/*
+ Send command to retrieve channel/effects setup/amp setup to PODxt Pro.
+*/
+static ssize_t pod_send_retrieve_command(struct device *dev, const char *buf,
+ size_t count, short block0,
+ short block1)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ int ret;
+ int size = 4;
+ char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_DUMPMEM, size);
+
+ if (!sysex)
+ return 0;
+
+ ret = pod_resolve(buf, block0, block1, sysex + SYSEX_DATA_OFS);
+ if (ret) {
+ kfree(sysex);
+ return ret;
+ }
+ sysex[SYSEX_DATA_OFS + 2] = 0;
+ sysex[SYSEX_DATA_OFS + 3] = 0;
+ line6_dump_started(&pod->dumpreq, POD_DUMP_MEMORY);
+
+ if (line6_send_sysex_message(&pod->line6, sysex, size) < size)
+ line6_dump_finished(&pod->dumpreq);
+
+ kfree(sysex);
+ /* needs some delay here on AMD64 platform */
+ return count;
+}
+
+/*
+ Generic get name function.
+*/
+static ssize_t get_name_generic(struct usb_line6_pod *pod, const char *str,
+ char *buf)
+{
+ int length = 0;
+ const char *p1;
+ char *p2;
+ char *last_non_space = buf;
+
+ int retval = line6_dump_wait_interruptible(&pod->dumpreq);
+ if (retval < 0)
+ return retval;
+
+ for (p1 = str, p2 = buf; *p1; ++p1, ++p2) {
+ *p2 = *p1;
+ if (*p2 != ' ')
+ last_non_space = p2;
+ if (++length == POD_NAME_LENGTH)
+ break;
+ }
+
+ *(last_non_space + 1) = '\n';
+ return last_non_space - buf + 2;
+}
+
+/*
+ "read" request on "channel" special file.
+*/
+static ssize_t pod_get_channel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ return sprintf(buf, "%d\n", pod->channel_num);
+}
+
+/*
+ "write" request on "channel" special file.
+*/
+static ssize_t pod_set_channel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ unsigned long value;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ pod_send_channel(pod, value);
+ return count;
+}
+
+/*
+ "read" request on "name" special file.
+*/
+static ssize_t pod_get_name(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ return get_name_generic(pod, pod->prog_data.header + POD_NAME_OFFSET,
+ buf);
+}
+
+/*
+ "read" request on "name" special file.
+*/
+static ssize_t pod_get_name_buf(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ return get_name_generic(pod,
+ pod->prog_data_buf.header + POD_NAME_OFFSET,
+ buf);
+}
+
+/*
+ "read" request on "dump" special file.
+*/
+static ssize_t pod_get_dump(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ int retval = line6_dump_wait_interruptible(&pod->dumpreq);
+ if (retval < 0)
+ return retval;
+ memcpy(buf, &pod->prog_data, sizeof(pod->prog_data));
+ return sizeof(pod->prog_data);
+}
+
+/*
+ "write" request on "dump" special file.
+*/
+static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+
+ if (count != sizeof(pod->prog_data)) {
+ dev_err(pod->line6.ifcdev,
+ "data block must be exactly %d bytes\n",
+ (int)sizeof(pod->prog_data));
+ return -EINVAL;
+ }
+
+ pod_dump(pod, buf);
+ return sizeof(pod->prog_data);
+}
+
+/*
+ Identify system parameters related to the tuner.
+*/
+static bool pod_is_tuner(int code)
+{
+ return
+ (code == POD_tuner_mute) ||
+ (code == POD_tuner_freq) ||
+ (code == POD_tuner_note) || (code == POD_tuner_pitch);
+}
+
+/*
+ Get system parameter (as integer).
+ @param tuner non-zero, if code refers to a tuner parameter
+*/
+static int pod_get_system_param_int(struct usb_line6_pod *pod, int *value,
+ int code, struct ValueWait *param, int sign)
+{
+ char *sysex;
+ static const int size = 1;
+ int retval = 0;
+
+ if (((pod->prog_data.control[POD_tuner] & 0x40) == 0)
+ && pod_is_tuner(code))
+ return -ENODEV;
+
+ /* send value request to device: */
+ param->value = POD_system_invalid;
+ sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEMREQ, size);
+
+ if (!sysex)
+ return -ENOMEM;
+
+ sysex[SYSEX_DATA_OFS] = code;
+ line6_send_sysex_message(&pod->line6, sysex, size);
+ kfree(sysex);
+
+ /* wait for device to respond: */
+ retval =
+ wait_event_interruptible(param->wait,
+ param->value != POD_system_invalid);
+
+ if (retval < 0)
+ return retval;
+
+ *value = sign ? (int)(signed short)param->value : (int)(unsigned short)
+ param->value;
+
+ if (*value == POD_system_invalid)
+ *value = 0; /* don't report uninitialized values */
+
+ return 0;
+}
+
+/*
+ Get system parameter (as string).
+ @param tuner non-zero, if code refers to a tuner parameter
+*/
+static ssize_t pod_get_system_param_string(struct usb_line6_pod *pod, char *buf,
+ int code, struct ValueWait *param,
+ int sign)
+{
+ int retval, value = 0;
+ retval = pod_get_system_param_int(pod, &value, code, param, sign);
+
+ if (retval < 0)
+ return retval;
+
+ return sprintf(buf, "%d\n", value);
+}
+
+/*
+ Send system parameter (from integer).
+ @param tuner non-zero, if code refers to a tuner parameter
+*/
+static int pod_set_system_param_int(struct usb_line6_pod *pod, int value,
+ int code)
+{
+ char *sysex;
+ static const int size = 5;
+
+ if (((pod->prog_data.control[POD_tuner] & 0x40) == 0)
+ && pod_is_tuner(code))
+ return -EINVAL;
+
+ /* send value to tuner: */
+ sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size);
+ if (!sysex)
+ return -ENOMEM;
+ sysex[SYSEX_DATA_OFS] = code;
+ sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f;
+ sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f;
+ sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f;
+ sysex[SYSEX_DATA_OFS + 4] = (value) & 0x0f;
+ line6_send_sysex_message(&pod->line6, sysex, size);
+ kfree(sysex);
+ return 0;
+}
+
+/*
+ Send system parameter (from string).
+ @param tuner non-zero, if code refers to a tuner parameter
+*/
+static ssize_t pod_set_system_param_string(struct usb_line6_pod *pod,
+ const char *buf, int count, int code,
+ unsigned short mask)
+{
+ int retval;
+ unsigned short value = simple_strtoul(buf, NULL, 10) & mask;
+ retval = pod_set_system_param_int(pod, value, code);
+ return (retval < 0) ? retval : count;
+}
+
+/*
+ "read" request on "dump_buf" special file.
+*/
+static ssize_t pod_get_dump_buf(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ int retval = line6_dump_wait_interruptible(&pod->dumpreq);
+ if (retval < 0)
+ return retval;
+ memcpy(buf, &pod->prog_data_buf, sizeof(pod->prog_data_buf));
+ return sizeof(pod->prog_data_buf);
+}
+
+/*
+ "write" request on "dump_buf" special file.
+*/
+static ssize_t pod_set_dump_buf(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+
+ if (count != sizeof(pod->prog_data)) {
+ dev_err(pod->line6.ifcdev,
+ "data block must be exactly %d bytes\n",
+ (int)sizeof(pod->prog_data));
+ return -EINVAL;
+ }
+
+ memcpy(&pod->prog_data_buf, buf, sizeof(pod->prog_data));
+ return sizeof(pod->prog_data);
+}
+
+/*
+ "write" request on "finish" special file.
+*/
+static ssize_t pod_set_finish(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ int size = 0;
+ char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_FINISH, size);
+ if (!sysex)
+ return 0;
+ line6_send_sysex_message(&pod->line6, sysex, size);
+ kfree(sysex);
+ return count;
+}
+
+/*
+ "write" request on "store_channel" special file.
+*/
+static ssize_t pod_set_store_channel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return pod_send_store_command(dev, buf, count, 0x0000, 0x00c0);
+}
+
+/*
+ "write" request on "store_effects_setup" special file.
+*/
+static ssize_t pod_set_store_effects_setup(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return pod_send_store_command(dev, buf, count, 0x0080, 0x0080);
+}
+
+/*
+ "write" request on "store_amp_setup" special file.
+*/
+static ssize_t pod_set_store_amp_setup(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return pod_send_store_command(dev, buf, count, 0x0040, 0x0100);
+}
+
+/*
+ "write" request on "retrieve_channel" special file.
+*/
+static ssize_t pod_set_retrieve_channel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return pod_send_retrieve_command(dev, buf, count, 0x0000, 0x00c0);
+}
+
+/*
+ "write" request on "retrieve_effects_setup" special file.
+*/
+static ssize_t pod_set_retrieve_effects_setup(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return pod_send_retrieve_command(dev, buf, count, 0x0080, 0x0080);
+}
+
+/*
+ "write" request on "retrieve_amp_setup" special file.
+*/
+static ssize_t pod_set_retrieve_amp_setup(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return pod_send_retrieve_command(dev, buf, count, 0x0040, 0x0100);
+}
+
+/*
+ "read" request on "dirty" special file.
+*/
+static ssize_t pod_get_dirty(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ buf[0] = pod->dirty ? '1' : '0';
+ buf[1] = '\n';
+ return 2;
+}
+
+/*
+ "read" request on "midi_postprocess" special file.
+*/
+static ssize_t pod_get_midi_postprocess(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ return sprintf(buf, "%d\n", pod->midi_postprocess);
+}
+
+/*
+ "write" request on "midi_postprocess" special file.
+*/
+static ssize_t pod_set_midi_postprocess(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ unsigned long value;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ pod->midi_postprocess = value ? 1 : 0;
+ return count;
+}
+
+/*
+ "read" request on "serial_number" special file.
+*/
+static ssize_t pod_get_serial_number(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ return sprintf(buf, "%d\n", pod->serial_number);
+}
+
+/*
+ "read" request on "firmware_version" special file.
+*/
+static ssize_t pod_get_firmware_version(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100,
+ pod->firmware_version % 100);
+}
+
+/*
+ "read" request on "device_id" special file.
+*/
+static ssize_t pod_get_device_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ return sprintf(buf, "%d\n", pod->device_id);
+}
+
+/*
+ "read" request on "clip" special file.
+*/
+static ssize_t pod_wait_for_clip(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ return wait_event_interruptible(pod->clipping.wait,
+ pod->clipping.value != 0);
+}
+
+/*
+ POD startup procedure.
+ This is a sequence of functions with special requirements (e.g., must
+ not run immediately after initialization, must not run in interrupt
+ context). After the last one has finished, the device is ready to use.
+*/
+
+static void pod_startup1(struct usb_line6_pod *pod)
+{
+ CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT);
+
+ /* delay startup procedure: */
+ line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2,
+ (unsigned long)pod);
+}
+
+static void pod_startup2(unsigned long data)
+{
+ struct usb_line6_pod *pod = (struct usb_line6_pod *)data;
+
+ /* schedule another startup procedure until startup is complete: */
+ if (pod->startup_progress >= POD_STARTUP_LAST)
+ return;
+
+ pod->startup_progress = POD_STARTUP_DUMPREQ;
+ line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2,
+ (unsigned long)pod);
+
+ /* current channel dump: */
+ line6_dump_request_async(&pod->dumpreq, &pod->line6, 0,
+ LINE6_DUMP_CURRENT);
+}
+
+static void pod_startup3(struct usb_line6_pod *pod)
+{
+ struct usb_line6 *line6 = &pod->line6;
+ CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ);
+
+ /* request firmware version: */
+ line6_version_request_async(line6);
+}
+
+static void pod_startup4(struct usb_line6_pod *pod)
+{
+ CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_WORKQUEUE);
+
+ /* schedule work for global work queue: */
+ schedule_work(&pod->startup_work);
+}
+
+static void pod_startup5(struct work_struct *work)
+{
+ struct usb_line6_pod *pod =
+ container_of(work, struct usb_line6_pod, startup_work);
+ struct usb_line6 *line6 = &pod->line6;
+
+ CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_SETUP);
+
+ /* serial number: */
+ line6_read_serial_number(&pod->line6, &pod->serial_number);
+
+ /* ALSA audio interface: */
+ line6_register_audio(line6);
+
+ /* device files: */
+ line6_pod_create_files(pod->firmware_version,
+ line6->properties->device_bit, line6->ifcdev);
+}
+
+#define POD_GET_SYSTEM_PARAM(code, sign) \
+static ssize_t pod_get_ ## code(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct usb_interface *interface = to_usb_interface(dev); \
+ struct usb_line6_pod *pod = usb_get_intfdata(interface); \
+ return pod_get_system_param_string(pod, buf, POD_ ## code, \
+ &pod->code, sign); \
+}
+
+#define POD_GET_SET_SYSTEM_PARAM(code, mask, sign) \
+POD_GET_SYSTEM_PARAM(code, sign) \
+static ssize_t pod_set_ ## code(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct usb_interface *interface = to_usb_interface(dev); \
+ struct usb_line6_pod *pod = usb_get_intfdata(interface); \
+ return pod_set_system_param_string(pod, buf, count, POD_ ## code, mask); \
+}
+
+POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0);
+POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0);
+POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 0);
+POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 0);
+POD_GET_SYSTEM_PARAM(tuner_note, 1);
+POD_GET_SYSTEM_PARAM(tuner_pitch, 1);
+
+#undef GET_SET_SYSTEM_PARAM
+#undef GET_SYSTEM_PARAM
+
+/* POD special files: */
+static DEVICE_ATTR(channel, S_IWUSR | S_IRUGO, pod_get_channel,
+ pod_set_channel);
+static DEVICE_ATTR(clip, S_IRUGO, pod_wait_for_clip, line6_nop_write);
+static DEVICE_ATTR(device_id, S_IRUGO, pod_get_device_id, line6_nop_write);
+static DEVICE_ATTR(dirty, S_IRUGO, pod_get_dirty, line6_nop_write);
+static DEVICE_ATTR(dump, S_IWUSR | S_IRUGO, pod_get_dump, pod_set_dump);
+static DEVICE_ATTR(dump_buf, S_IWUSR | S_IRUGO, pod_get_dump_buf,
+ pod_set_dump_buf);
+static DEVICE_ATTR(finish, S_IWUSR, line6_nop_read, pod_set_finish);
+static DEVICE_ATTR(firmware_version, S_IRUGO, pod_get_firmware_version,
+ line6_nop_write);
+static DEVICE_ATTR(midi_postprocess, S_IWUSR | S_IRUGO,
+ pod_get_midi_postprocess, pod_set_midi_postprocess);
+static DEVICE_ATTR(monitor_level, S_IWUSR | S_IRUGO, pod_get_monitor_level,
+ pod_set_monitor_level);
+static DEVICE_ATTR(name, S_IRUGO, pod_get_name, line6_nop_write);
+static DEVICE_ATTR(name_buf, S_IRUGO, pod_get_name_buf, line6_nop_write);
+static DEVICE_ATTR(retrieve_amp_setup, S_IWUSR, line6_nop_read,
+ pod_set_retrieve_amp_setup);
+static DEVICE_ATTR(retrieve_channel, S_IWUSR, line6_nop_read,
+ pod_set_retrieve_channel);
+static DEVICE_ATTR(retrieve_effects_setup, S_IWUSR, line6_nop_read,
+ pod_set_retrieve_effects_setup);
+static DEVICE_ATTR(routing, S_IWUSR | S_IRUGO, pod_get_routing,
+ pod_set_routing);
+static DEVICE_ATTR(serial_number, S_IRUGO, pod_get_serial_number,
+ line6_nop_write);
+static DEVICE_ATTR(store_amp_setup, S_IWUSR, line6_nop_read,
+ pod_set_store_amp_setup);
+static DEVICE_ATTR(store_channel, S_IWUSR, line6_nop_read,
+ pod_set_store_channel);
+static DEVICE_ATTR(store_effects_setup, S_IWUSR, line6_nop_read,
+ pod_set_store_effects_setup);
+static DEVICE_ATTR(tuner_freq, S_IWUSR | S_IRUGO, pod_get_tuner_freq,
+ pod_set_tuner_freq);
+static DEVICE_ATTR(tuner_mute, S_IWUSR | S_IRUGO, pod_get_tuner_mute,
+ pod_set_tuner_mute);
+static DEVICE_ATTR(tuner_note, S_IRUGO, pod_get_tuner_note, line6_nop_write);
+static DEVICE_ATTR(tuner_pitch, S_IRUGO, pod_get_tuner_pitch, line6_nop_write);
+
+#ifdef CONFIG_LINE6_USB_RAW
+static DEVICE_ATTR(raw, S_IWUSR, line6_nop_read, line6_set_raw);
+#endif
+
+/* control info callback */
+static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 65535;
+ return 0;
+}
+
+/* control get callback */
+static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+ struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6;
+ ucontrol->value.integer.value[0] = pod->monitor_level.value;
+ return 0;
+}
+
+/* control put callback */
+static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+ struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6;
+
+ if (ucontrol->value.integer.value[0] == pod->monitor_level.value)
+ return 0;
+
+ pod->monitor_level.value = ucontrol->value.integer.value[0];
+ pod_set_system_param_int(pod, ucontrol->value.integer.value[0],
+ POD_monitor_level);
+ return 1;
+}
+
+/* control definition */
+static struct snd_kcontrol_new pod_control_monitor = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Monitor Playback Volume",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_pod_control_monitor_info,
+ .get = snd_pod_control_monitor_get,
+ .put = snd_pod_control_monitor_put
+};
+
+/*
+ POD destructor.
+*/
+static void pod_destruct(struct usb_interface *interface)
+{
+ struct usb_line6_pod *pod = usb_get_intfdata(interface);
+
+ if (pod == NULL)
+ return;
+ line6_cleanup_audio(&pod->line6);
+
+ del_timer(&pod->startup_timer);
+ cancel_work_sync(&pod->startup_work);
+
+ /* free dump request data: */
+ line6_dumpreq_destruct(&pod->dumpreq);
+}
+
+/*
+ Create sysfs entries.
+*/
+static int pod_create_files2(struct device *dev)
+{
+ int err;
+
+ CHECK_RETURN(device_create_file(dev, &dev_attr_channel));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_clip));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_device_id));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_dirty));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_dump));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_dump_buf));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_finish));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_firmware_version));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_midi_postprocess));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_monitor_level));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_name));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_name_buf));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_amp_setup));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_channel));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_effects_setup));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_routing));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_serial_number));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_store_amp_setup));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_store_channel));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_store_effects_setup));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_freq));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_mute));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_note));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_pitch));
+
+#ifdef CONFIG_LINE6_USB_RAW
+ CHECK_RETURN(device_create_file(dev, &dev_attr_raw));
+#endif
+
+ return 0;
+}
+
+/*
+ Try to init POD device.
+*/
+static int pod_try_init(struct usb_interface *interface,
+ struct usb_line6_pod *pod)
+{
+ int err;
+ struct usb_line6 *line6 = &pod->line6;
+
+ init_timer(&pod->startup_timer);
+ INIT_WORK(&pod->startup_work, pod_startup5);
+
+ if ((interface == NULL) || (pod == NULL))
+ return -ENODEV;
+
+ pod->channel_num = 255;
+
+ /* initialize wait queues: */
+ init_waitqueue_head(&pod->monitor_level.wait);
+ init_waitqueue_head(&pod->routing.wait);
+ init_waitqueue_head(&pod->tuner_mute.wait);
+ init_waitqueue_head(&pod->tuner_freq.wait);
+ init_waitqueue_head(&pod->tuner_note.wait);
+ init_waitqueue_head(&pod->tuner_pitch.wait);
+ init_waitqueue_head(&pod->clipping.wait);
+
+ memset(pod->param_dirty, 0xff, sizeof(pod->param_dirty));
+
+ /* initialize USB buffers: */
+ err = line6_dumpreq_init(&pod->dumpreq, pod_request_channel,
+ sizeof(pod_request_channel));
+ if (err < 0) {
+ dev_err(&interface->dev, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ /* create sysfs entries: */
+ err = pod_create_files2(&interface->dev);
+ if (err < 0)
+ return err;
+
+ /* initialize audio system: */
+ err = line6_init_audio(line6);
+ if (err < 0)
+ return err;
+
+ /* initialize MIDI subsystem: */
+ err = line6_init_midi(line6);
+ if (err < 0)
+ return err;
+
+ /* initialize PCM subsystem: */
+ err = line6_init_pcm(line6, &pod_pcm_properties);
+ if (err < 0)
+ return err;
+
+ /* register monitor control: */
+ err = snd_ctl_add(line6->card,
+ snd_ctl_new1(&pod_control_monitor, line6->line6pcm));
+ if (err < 0)
+ return err;
+
+ /*
+ When the sound card is registered at this point, the PODxt Live
+ displays "Invalid Code Error 07", so we do it later in the event
+ handler.
+ */
+
+ if (pod->line6.properties->capabilities & LINE6_BIT_CONTROL) {
+ pod->monitor_level.value = POD_system_invalid;
+
+ /* initiate startup procedure: */
+ pod_startup1(pod);
+ }
+
+ return 0;
+}
+
+/*
+ Init POD device (and clean up in case of failure).
+*/
+int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
+{
+ int err = pod_try_init(interface, pod);
+
+ if (err < 0)
+ pod_destruct(interface);
+
+ return err;
+}
+
+/*
+ POD device disconnected.
+*/
+void line6_pod_disconnect(struct usb_interface *interface)
+{
+ struct usb_line6_pod *pod;
+
+ if (interface == NULL)
+ return;
+ pod = usb_get_intfdata(interface);
+
+ if (pod != NULL) {
+ struct snd_line6_pcm *line6pcm = pod->line6.line6pcm;
+ struct device *dev = &interface->dev;
+
+ if (line6pcm != NULL)
+ line6_pcm_disconnect(line6pcm);
+
+ if (dev != NULL) {
+ /* remove sysfs entries: */
+ line6_pod_remove_files(pod->firmware_version,
+ pod->line6.
+ properties->device_bit, dev);
+
+ device_remove_file(dev, &dev_attr_channel);
+ device_remove_file(dev, &dev_attr_clip);
+ device_remove_file(dev, &dev_attr_device_id);
+ device_remove_file(dev, &dev_attr_dirty);
+ device_remove_file(dev, &dev_attr_dump);
+ device_remove_file(dev, &dev_attr_dump_buf);
+ device_remove_file(dev, &dev_attr_finish);
+ device_remove_file(dev, &dev_attr_firmware_version);
+ device_remove_file(dev, &dev_attr_midi_postprocess);
+ device_remove_file(dev, &dev_attr_monitor_level);
+ device_remove_file(dev, &dev_attr_name);
+ device_remove_file(dev, &dev_attr_name_buf);
+ device_remove_file(dev, &dev_attr_retrieve_amp_setup);
+ device_remove_file(dev, &dev_attr_retrieve_channel);
+ device_remove_file(dev,
+ &dev_attr_retrieve_effects_setup);
+ device_remove_file(dev, &dev_attr_routing);
+ device_remove_file(dev, &dev_attr_serial_number);
+ device_remove_file(dev, &dev_attr_store_amp_setup);
+ device_remove_file(dev, &dev_attr_store_channel);
+ device_remove_file(dev, &dev_attr_store_effects_setup);
+ device_remove_file(dev, &dev_attr_tuner_freq);
+ device_remove_file(dev, &dev_attr_tuner_mute);
+ device_remove_file(dev, &dev_attr_tuner_note);
+ device_remove_file(dev, &dev_attr_tuner_pitch);
+
+#ifdef CONFIG_LINE6_USB_RAW
+ device_remove_file(dev, &dev_attr_raw);
+#endif
+ }
+ }
+
+ pod_destruct(interface);
+}
diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h
new file mode 100644
index 00000000..18b9d08c
--- /dev/null
+++ b/drivers/staging/line6/pod.h
@@ -0,0 +1,205 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef POD_H
+#define POD_H
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+
+#include <sound/core.h>
+
+#include "driver.h"
+#include "dumprequest.h"
+
+/*
+ PODxt Live interfaces
+*/
+#define PODXTLIVE_INTERFACE_POD 0
+#define PODXTLIVE_INTERFACE_VARIAX 1
+
+/*
+ Locate name in binary program dump
+*/
+#define POD_NAME_OFFSET 0
+#define POD_NAME_LENGTH 16
+
+/*
+ Other constants
+*/
+#define POD_CONTROL_SIZE 0x80
+#define POD_BUFSIZE_DUMPREQ 7
+#define POD_STARTUP_DELAY 1000
+
+/*
+ Stages of POD startup procedure
+*/
+enum {
+ POD_STARTUP_INIT = 1,
+ POD_STARTUP_DUMPREQ,
+ POD_STARTUP_VERSIONREQ,
+ POD_STARTUP_WORKQUEUE,
+ POD_STARTUP_SETUP,
+ POD_STARTUP_LAST = POD_STARTUP_SETUP - 1
+};
+
+/**
+ Data structure for values that need to be requested explicitly.
+ This is the case for system and tuner settings.
+*/
+struct ValueWait {
+ int value;
+ wait_queue_head_t wait;
+};
+
+/**
+ Binary PODxt Pro program dump
+*/
+struct pod_program {
+ /**
+ Header information (including program name).
+ */
+ unsigned char header[0x20];
+
+ /**
+ Program parameters.
+ */
+ unsigned char control[POD_CONTROL_SIZE];
+};
+
+struct usb_line6_pod {
+ /**
+ Generic Line6 USB data.
+ */
+ struct usb_line6 line6;
+
+ /**
+ Dump request structure.
+ */
+ struct line6_dump_request dumpreq;
+
+ /**
+ Current program number.
+ */
+ unsigned char channel_num;
+
+ /**
+ Current program settings.
+ */
+ struct pod_program prog_data;
+
+ /**
+ Buffer for data retrieved from or to be stored on PODxt Pro.
+ */
+ struct pod_program prog_data_buf;
+
+ /**
+ Tuner mute mode.
+ */
+ struct ValueWait tuner_mute;
+
+ /**
+ Tuner base frequency (typically 440Hz).
+ */
+ struct ValueWait tuner_freq;
+
+ /**
+ Note received from tuner.
+ */
+ struct ValueWait tuner_note;
+
+ /**
+ Pitch value received from tuner.
+ */
+ struct ValueWait tuner_pitch;
+
+ /**
+ Instrument monitor level.
+ */
+ struct ValueWait monitor_level;
+
+ /**
+ Audio routing mode.
+ 0: send processed guitar
+ 1: send clean guitar
+ 2: send clean guitar re-amp playback
+ 3: send re-amp playback
+ */
+ struct ValueWait routing;
+
+ /**
+ Wait for audio clipping event.
+ */
+ struct ValueWait clipping;
+
+ /**
+ Timer for device initializaton.
+ */
+ struct timer_list startup_timer;
+
+ /**
+ Work handler for device initializaton.
+ */
+ struct work_struct startup_work;
+
+ /**
+ Current progress in startup procedure.
+ */
+ int startup_progress;
+
+ /**
+ Dirty flags for access to parameter data.
+ */
+ unsigned long param_dirty[POD_CONTROL_SIZE / sizeof(unsigned long)];
+
+ /**
+ Some atomic flags.
+ */
+ unsigned long atomic_flags;
+
+ /**
+ Serial number of device.
+ */
+ int serial_number;
+
+ /**
+ Firmware version (x 100).
+ */
+ int firmware_version;
+
+ /**
+ Device ID.
+ */
+ int device_id;
+
+ /**
+ Flag to indicate modification of current program settings.
+ */
+ char dirty;
+
+ /**
+ Flag to enable MIDI postprocessing.
+ */
+ char midi_postprocess;
+};
+
+extern void line6_pod_disconnect(struct usb_interface *interface);
+extern int line6_pod_init(struct usb_interface *interface,
+ struct usb_line6_pod *pod);
+extern void line6_pod_midi_postprocess(struct usb_line6_pod *pod,
+ unsigned char *data, int length);
+extern void line6_pod_process_message(struct usb_line6_pod *pod);
+extern void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param,
+ int value);
+
+#endif
diff --git a/drivers/staging/line6/podhd.c b/drivers/staging/line6/podhd.c
new file mode 100644
index 00000000..7ef45437
--- /dev/null
+++ b/drivers/staging/line6/podhd.c
@@ -0,0 +1,154 @@
+/*
+ * Line6 Pod HD
+ *
+ * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "audio.h"
+#include "driver.h"
+#include "pcm.h"
+#include "podhd.h"
+
+#define PODHD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */
+
+static struct snd_ratden podhd_ratden = {
+ .num_min = 48000,
+ .num_max = 48000,
+ .num_step = 1,
+ .den = 1,
+};
+
+static struct line6_pcm_properties podhd_pcm_properties = {
+ .snd_line6_playback_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+#ifdef CONFIG_PM
+ SNDRV_PCM_INFO_RESUME |
+#endif
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 60000,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 1024},
+ .snd_line6_capture_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+#ifdef CONFIG_PM
+ SNDRV_PCM_INFO_RESUME |
+#endif
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 60000,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 1024},
+ .snd_line6_rates = {
+ .nrats = 1,
+ .rats = &podhd_ratden},
+ .bytes_per_frame = PODHD_BYTES_PER_FRAME
+};
+
+/*
+ POD HD destructor.
+*/
+static void podhd_destruct(struct usb_interface *interface)
+{
+ struct usb_line6_podhd *podhd = usb_get_intfdata(interface);
+
+ if (podhd == NULL)
+ return;
+ line6_cleanup_audio(&podhd->line6);
+}
+
+/*
+ Try to init POD HD device.
+*/
+static int podhd_try_init(struct usb_interface *interface,
+ struct usb_line6_podhd *podhd)
+{
+ int err;
+ struct usb_line6 *line6 = &podhd->line6;
+
+ if ((interface == NULL) || (podhd == NULL))
+ return -ENODEV;
+
+ /* initialize audio system: */
+ err = line6_init_audio(line6);
+ if (err < 0)
+ return err;
+
+ /* initialize MIDI subsystem: */
+ err = line6_init_midi(line6);
+ if (err < 0)
+ return err;
+
+ /* initialize PCM subsystem: */
+ err = line6_init_pcm(line6, &podhd_pcm_properties);
+ if (err < 0)
+ return err;
+
+ /* register USB audio system: */
+ err = line6_register_audio(line6);
+ return err;
+}
+
+/*
+ Init POD HD device (and clean up in case of failure).
+*/
+int line6_podhd_init(struct usb_interface *interface,
+ struct usb_line6_podhd *podhd)
+{
+ int err = podhd_try_init(interface, podhd);
+
+ if (err < 0)
+ podhd_destruct(interface);
+
+ return err;
+}
+
+/*
+ POD HD device disconnected.
+*/
+void line6_podhd_disconnect(struct usb_interface *interface)
+{
+ struct usb_line6_podhd *podhd;
+
+ if (interface == NULL)
+ return;
+ podhd = usb_get_intfdata(interface);
+
+ if (podhd != NULL) {
+ struct snd_line6_pcm *line6pcm = podhd->line6.line6pcm;
+
+ if (line6pcm != NULL)
+ line6_pcm_disconnect(line6pcm);
+ }
+
+ podhd_destruct(interface);
+}
diff --git a/drivers/staging/line6/podhd.h b/drivers/staging/line6/podhd.h
new file mode 100644
index 00000000..652f7405
--- /dev/null
+++ b/drivers/staging/line6/podhd.h
@@ -0,0 +1,30 @@
+/*
+ * Line6 Pod HD
+ *
+ * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef PODHD_H
+#define PODHD_H
+
+#include <linux/usb.h>
+
+#include "driver.h"
+
+struct usb_line6_podhd {
+ /**
+ Generic Line6 USB data.
+ */
+ struct usb_line6 line6;
+};
+
+extern void line6_podhd_disconnect(struct usb_interface *interface);
+extern int line6_podhd_init(struct usb_interface *interface,
+ struct usb_line6_podhd *podhd);
+
+#endif /* PODHD_H */
diff --git a/drivers/staging/line6/revision.h b/drivers/staging/line6/revision.h
new file mode 100644
index 00000000..b4eee2b7
--- /dev/null
+++ b/drivers/staging/line6/revision.h
@@ -0,0 +1,4 @@
+#ifndef DRIVER_REVISION
+/* current subversion revision */
+#define DRIVER_REVISION " (904)"
+#endif
diff --git a/drivers/staging/line6/toneport.c b/drivers/staging/line6/toneport.c
new file mode 100644
index 00000000..b754f69a
--- /dev/null
+++ b/drivers/staging/line6/toneport.c
@@ -0,0 +1,455 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Emil Myhrman (emil.myhrman@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/wait.h>
+#include <sound/control.h>
+
+#include "audio.h"
+#include "capture.h"
+#include "driver.h"
+#include "playback.h"
+#include "toneport.h"
+
+static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2);
+
+#define TONEPORT_PCM_DELAY 1
+
+static struct snd_ratden toneport_ratden = {
+ .num_min = 44100,
+ .num_max = 44100,
+ .num_step = 1,
+ .den = 1
+};
+
+static struct line6_pcm_properties toneport_pcm_properties = {
+ .snd_line6_playback_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+#ifdef CONFIG_PM
+ SNDRV_PCM_INFO_RESUME |
+#endif
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 60000,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 1024},
+ .snd_line6_capture_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+#ifdef CONFIG_PM
+ SNDRV_PCM_INFO_RESUME |
+#endif
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 60000,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 1024},
+ .snd_line6_rates = {
+ .nrats = 1,
+ .rats = &toneport_ratden},
+ .bytes_per_frame = 4
+};
+
+/*
+ For the led on Guitarport.
+ Brightness goes from 0x00 to 0x26. Set a value above this to have led
+ blink.
+ (void cmd_0x02(byte red, byte green)
+*/
+static int led_red = 0x00;
+static int led_green = 0x26;
+
+struct ToneportSourceInfo {
+ const char *name;
+ int code;
+};
+
+static const struct ToneportSourceInfo toneport_source_info[] = {
+ {"Microphone", 0x0a01},
+ {"Line", 0x0801},
+ {"Instrument", 0x0b01},
+ {"Inst & Mic", 0x0901}
+};
+
+static bool toneport_has_led(short product)
+{
+ return
+ (product == LINE6_DEVID_GUITARPORT) ||
+ (product == LINE6_DEVID_TONEPORT_GX);
+ /* add your device here if you are missing support for the LEDs */
+}
+
+static void toneport_update_led(struct device *dev)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6_toneport *tp = usb_get_intfdata(interface);
+ struct usb_line6 *line6;
+
+ if (!tp)
+ return;
+
+ line6 = &tp->line6;
+ if (line6)
+ toneport_send_cmd(line6->usbdev, (led_red << 8) | 0x0002,
+ led_green);
+}
+
+static ssize_t toneport_set_led_red(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ long value;
+
+ retval = strict_strtol(buf, 10, &value);
+ if (retval)
+ return retval;
+
+ led_red = value;
+ toneport_update_led(dev);
+ return count;
+}
+
+static ssize_t toneport_set_led_green(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ long value;
+
+ retval = strict_strtol(buf, 10, &value);
+ if (retval)
+ return retval;
+
+ led_green = value;
+ toneport_update_led(dev);
+ return count;
+}
+
+static DEVICE_ATTR(led_red, S_IWUSR | S_IRUGO, line6_nop_read,
+ toneport_set_led_red);
+static DEVICE_ATTR(led_green, S_IWUSR | S_IRUGO, line6_nop_read,
+ toneport_set_led_green);
+
+static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
+{
+ int ret;
+
+ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ cmd1, cmd2, NULL, 0, LINE6_TIMEOUT * HZ);
+
+ if (ret < 0) {
+ err("send failed (error %d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* monitor info callback */
+static int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 256;
+ return 0;
+}
+
+/* monitor get callback */
+static int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = line6pcm->volume_monitor;
+ return 0;
+}
+
+/* monitor put callback */
+static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+
+ if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor)
+ return 0;
+
+ line6pcm->volume_monitor = ucontrol->value.integer.value[0];
+
+ if (line6pcm->volume_monitor > 0)
+ line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_MONITOR);
+ else
+ line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR);
+
+ return 1;
+}
+
+/* source info callback */
+static int snd_toneport_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const int size = ARRAY_SIZE(toneport_source_info);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = size;
+
+ if (uinfo->value.enumerated.item >= size)
+ uinfo->value.enumerated.item = size - 1;
+
+ strcpy(uinfo->value.enumerated.name,
+ toneport_source_info[uinfo->value.enumerated.item].name);
+
+ return 0;
+}
+
+/* source get callback */
+static int snd_toneport_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+ struct usb_line6_toneport *toneport =
+ (struct usb_line6_toneport *)line6pcm->line6;
+ ucontrol->value.enumerated.item[0] = toneport->source;
+ return 0;
+}
+
+/* source put callback */
+static int snd_toneport_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+ struct usb_line6_toneport *toneport =
+ (struct usb_line6_toneport *)line6pcm->line6;
+
+ if (ucontrol->value.enumerated.item[0] == toneport->source)
+ return 0;
+
+ toneport->source = ucontrol->value.enumerated.item[0];
+ toneport_send_cmd(toneport->line6.usbdev,
+ toneport_source_info[toneport->source].code, 0x0000);
+ return 1;
+}
+
+static void toneport_start_pcm(unsigned long arg)
+{
+ struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg;
+ struct usb_line6 *line6 = &toneport->line6;
+ line6_pcm_acquire(line6->line6pcm, LINE6_BITS_PCM_MONITOR);
+}
+
+/* control definition */
+static struct snd_kcontrol_new toneport_control_monitor = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Monitor Playback Volume",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_toneport_monitor_info,
+ .get = snd_toneport_monitor_get,
+ .put = snd_toneport_monitor_put
+};
+
+/* source selector definition */
+static struct snd_kcontrol_new toneport_control_source = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Capture Source",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_toneport_source_info,
+ .get = snd_toneport_source_get,
+ .put = snd_toneport_source_put
+};
+
+/*
+ Toneport destructor.
+*/
+static void toneport_destruct(struct usb_interface *interface)
+{
+ struct usb_line6_toneport *toneport = usb_get_intfdata(interface);
+
+ if (toneport == NULL)
+ return;
+ line6_cleanup_audio(&toneport->line6);
+}
+
+/*
+ Setup Toneport device.
+*/
+static void toneport_setup(struct usb_line6_toneport *toneport)
+{
+ int ticks;
+ struct usb_line6 *line6 = &toneport->line6;
+ struct usb_device *usbdev = line6->usbdev;
+
+ /* sync time on device with host: */
+ ticks = (int)get_seconds();
+ line6_write_data(line6, 0x80c6, &ticks, 4);
+
+ /* enable device: */
+ toneport_send_cmd(usbdev, 0x0301, 0x0000);
+
+ /* initialize source select: */
+ switch (usbdev->descriptor.idProduct) {
+ case LINE6_DEVID_TONEPORT_UX1:
+ case LINE6_DEVID_TONEPORT_UX2:
+ case LINE6_DEVID_PODSTUDIO_UX1:
+ case LINE6_DEVID_PODSTUDIO_UX2:
+ toneport_send_cmd(usbdev,
+ toneport_source_info[toneport->source].code,
+ 0x0000);
+ }
+
+ if (toneport_has_led(usbdev->descriptor.idProduct))
+ toneport_update_led(&usbdev->dev);
+}
+
+/*
+ Try to init Toneport device.
+*/
+static int toneport_try_init(struct usb_interface *interface,
+ struct usb_line6_toneport *toneport)
+{
+ int err;
+ struct usb_line6 *line6 = &toneport->line6;
+ struct usb_device *usbdev = line6->usbdev;
+
+ if ((interface == NULL) || (toneport == NULL))
+ return -ENODEV;
+
+ /* initialize audio system: */
+ err = line6_init_audio(line6);
+ if (err < 0)
+ return err;
+
+ /* initialize PCM subsystem: */
+ err = line6_init_pcm(line6, &toneport_pcm_properties);
+ if (err < 0)
+ return err;
+
+ /* register monitor control: */
+ err = snd_ctl_add(line6->card,
+ snd_ctl_new1(&toneport_control_monitor,
+ line6->line6pcm));
+ if (err < 0)
+ return err;
+
+ /* register source select control: */
+ switch (usbdev->descriptor.idProduct) {
+ case LINE6_DEVID_TONEPORT_UX1:
+ case LINE6_DEVID_TONEPORT_UX2:
+ case LINE6_DEVID_PODSTUDIO_UX1:
+ case LINE6_DEVID_PODSTUDIO_UX2:
+ err =
+ snd_ctl_add(line6->card,
+ snd_ctl_new1(&toneport_control_source,
+ line6->line6pcm));
+ if (err < 0)
+ return err;
+ }
+
+ /* register audio system: */
+ err = line6_register_audio(line6);
+ if (err < 0)
+ return err;
+
+ line6_read_serial_number(line6, &toneport->serial_number);
+ line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1);
+
+ if (toneport_has_led(usbdev->descriptor.idProduct)) {
+ CHECK_RETURN(device_create_file
+ (&interface->dev, &dev_attr_led_red));
+ CHECK_RETURN(device_create_file
+ (&interface->dev, &dev_attr_led_green));
+ }
+
+ toneport_setup(toneport);
+
+ init_timer(&toneport->timer);
+ toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ;
+ toneport->timer.function = toneport_start_pcm;
+ toneport->timer.data = (unsigned long)toneport;
+ add_timer(&toneport->timer);
+
+ return 0;
+}
+
+/*
+ Init Toneport device (and clean up in case of failure).
+*/
+int line6_toneport_init(struct usb_interface *interface,
+ struct usb_line6_toneport *toneport)
+{
+ int err = toneport_try_init(interface, toneport);
+
+ if (err < 0)
+ toneport_destruct(interface);
+
+ return err;
+}
+
+/*
+ Resume Toneport device after reset.
+*/
+void line6_toneport_reset_resume(struct usb_line6_toneport *toneport)
+{
+ toneport_setup(toneport);
+}
+
+/*
+ Toneport device disconnected.
+*/
+void line6_toneport_disconnect(struct usb_interface *interface)
+{
+ struct usb_line6_toneport *toneport;
+
+ if (interface == NULL)
+ return;
+
+ toneport = usb_get_intfdata(interface);
+ del_timer_sync(&toneport->timer);
+
+ if (toneport_has_led(toneport->line6.usbdev->descriptor.idProduct)) {
+ device_remove_file(&interface->dev, &dev_attr_led_red);
+ device_remove_file(&interface->dev, &dev_attr_led_green);
+ }
+
+ if (toneport != NULL) {
+ struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm;
+
+ if (line6pcm != NULL) {
+ line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR);
+ line6_pcm_disconnect(line6pcm);
+ }
+ }
+
+ toneport_destruct(interface);
+}
diff --git a/drivers/staging/line6/toneport.h b/drivers/staging/line6/toneport.h
new file mode 100644
index 00000000..8576b726
--- /dev/null
+++ b/drivers/staging/line6/toneport.h
@@ -0,0 +1,52 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef TONEPORT_H
+#define TONEPORT_H
+
+#include <linux/usb.h>
+#include <sound/core.h>
+
+#include "driver.h"
+
+struct usb_line6_toneport {
+ /**
+ Generic Line6 USB data.
+ */
+ struct usb_line6 line6;
+
+ /**
+ Source selector.
+ */
+ int source;
+
+ /**
+ Serial number of device.
+ */
+ int serial_number;
+
+ /**
+ Firmware version (x 100).
+ */
+ int firmware_version;
+
+ /**
+ Timer for delayed PCM startup.
+ */
+ struct timer_list timer;
+};
+
+extern void line6_toneport_disconnect(struct usb_interface *interface);
+extern int line6_toneport_init(struct usb_interface *interface,
+ struct usb_line6_toneport *toneport);
+extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport);
+
+#endif
diff --git a/drivers/staging/line6/usbdefs.h b/drivers/staging/line6/usbdefs.h
new file mode 100644
index 00000000..353d59d7
--- /dev/null
+++ b/drivers/staging/line6/usbdefs.h
@@ -0,0 +1,107 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2005-2008 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef USBDEFS_H
+#define USBDEFS_H
+
+#define LINE6_VENDOR_ID 0x0e41
+
+#define USB_INTERVALS_PER_SECOND 1000
+
+/*
+ Device ids.
+*/
+#define LINE6_DEVID_BASSPODXT 0x4250
+#define LINE6_DEVID_BASSPODXTLIVE 0x4642
+#define LINE6_DEVID_BASSPODXTPRO 0x4252
+#define LINE6_DEVID_GUITARPORT 0x4750
+#define LINE6_DEVID_POCKETPOD 0x5051
+#define LINE6_DEVID_PODHD300 0x5057
+#define LINE6_DEVID_PODHD500 0x414D
+#define LINE6_DEVID_PODSTUDIO_GX 0x4153
+#define LINE6_DEVID_PODSTUDIO_UX1 0x4150
+#define LINE6_DEVID_PODSTUDIO_UX2 0x4151
+#define LINE6_DEVID_PODX3 0x414a
+#define LINE6_DEVID_PODX3LIVE 0x414b
+#define LINE6_DEVID_PODXT 0x5044
+#define LINE6_DEVID_PODXTLIVE 0x4650
+#define LINE6_DEVID_PODXTPRO 0x5050
+#define LINE6_DEVID_TONEPORT_GX 0x4147
+#define LINE6_DEVID_TONEPORT_UX1 0x4141
+#define LINE6_DEVID_TONEPORT_UX2 0x4142
+#define LINE6_DEVID_VARIAX 0x534d
+
+#define LINE6_BIT(x) LINE6_BIT_ ## x = 1 << LINE6_INDEX_ ## x
+
+enum {
+ LINE6_INDEX_BASSPODXT,
+ LINE6_INDEX_BASSPODXTLIVE,
+ LINE6_INDEX_BASSPODXTPRO,
+ LINE6_INDEX_GUITARPORT,
+ LINE6_INDEX_POCKETPOD,
+ LINE6_INDEX_PODHD300,
+ LINE6_INDEX_PODHD500,
+ LINE6_INDEX_PODSTUDIO_GX,
+ LINE6_INDEX_PODSTUDIO_UX1,
+ LINE6_INDEX_PODSTUDIO_UX2,
+ LINE6_INDEX_PODX3,
+ LINE6_INDEX_PODX3LIVE,
+ LINE6_INDEX_PODXT,
+ LINE6_INDEX_PODXTLIVE,
+ LINE6_INDEX_PODXTPRO,
+ LINE6_INDEX_TONEPORT_GX,
+ LINE6_INDEX_TONEPORT_UX1,
+ LINE6_INDEX_TONEPORT_UX2,
+ LINE6_INDEX_VARIAX,
+
+ LINE6_BIT(BASSPODXT),
+ LINE6_BIT(BASSPODXTLIVE),
+ LINE6_BIT(BASSPODXTPRO),
+ LINE6_BIT(GUITARPORT),
+ LINE6_BIT(POCKETPOD),
+ LINE6_BIT(PODHD300),
+ LINE6_BIT(PODHD500),
+ LINE6_BIT(PODSTUDIO_GX),
+ LINE6_BIT(PODSTUDIO_UX1),
+ LINE6_BIT(PODSTUDIO_UX2),
+ LINE6_BIT(PODX3),
+ LINE6_BIT(PODX3LIVE),
+ LINE6_BIT(PODXT),
+ LINE6_BIT(PODXTLIVE),
+ LINE6_BIT(PODXTPRO),
+ LINE6_BIT(TONEPORT_GX),
+ LINE6_BIT(TONEPORT_UX1),
+ LINE6_BIT(TONEPORT_UX2),
+ LINE6_BIT(VARIAX),
+
+ LINE6_BITS_PRO = LINE6_BIT_BASSPODXTPRO | LINE6_BIT_PODXTPRO,
+ LINE6_BITS_LIVE = LINE6_BIT_BASSPODXTLIVE | LINE6_BIT_PODXTLIVE | LINE6_BIT_PODX3LIVE,
+ LINE6_BITS_PODXTALL = LINE6_BIT_PODXT | LINE6_BIT_PODXTLIVE | LINE6_BIT_PODXTPRO,
+ LINE6_BITS_PODX3ALL = LINE6_BIT_PODX3 | LINE6_BIT_PODX3LIVE,
+ LINE6_BITS_PODHDALL = LINE6_BIT_PODHD300 | LINE6_BIT_PODHD500,
+ LINE6_BITS_BASSPODXTALL = LINE6_BIT_BASSPODXT | LINE6_BIT_BASSPODXTLIVE | LINE6_BIT_BASSPODXTPRO
+};
+
+/* device supports settings parameter via USB */
+#define LINE6_BIT_CONTROL (1 << 0)
+/* device supports PCM input/output via USB */
+#define LINE6_BIT_PCM (1 << 1)
+/* device support hardware monitoring */
+#define LINE6_BIT_HWMON (1 << 2)
+
+#define LINE6_BIT_CONTROL_PCM_HWMON (LINE6_BIT_CONTROL | \
+ LINE6_BIT_PCM | \
+ LINE6_BIT_HWMON)
+
+#define LINE6_FALLBACK_INTERVAL 10
+#define LINE6_FALLBACK_MAXPACKETSIZE 16
+
+#endif
diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c
new file mode 100644
index 00000000..d3662222
--- /dev/null
+++ b/drivers/staging/line6/variax.c
@@ -0,0 +1,719 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+
+#include "audio.h"
+#include "control.h"
+#include "driver.h"
+#include "variax.h"
+
+#define VARIAX_SYSEX_CODE 7
+#define VARIAX_SYSEX_PARAM 0x3b
+#define VARIAX_SYSEX_ACTIVATE 0x2a
+#define VARIAX_MODEL_HEADER_LENGTH 7
+#define VARIAX_MODEL_MESSAGE_LENGTH 199
+#define VARIAX_OFFSET_ACTIVATE 7
+
+/*
+ This message is sent by the device during initialization and identifies
+ the connected guitar model.
+*/
+static const char variax_init_model[] = {
+ 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x69, 0x02,
+ 0x00
+};
+
+/*
+ This message is sent by the device during initialization and identifies
+ the connected guitar version.
+*/
+static const char variax_init_version[] = {
+ 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c,
+ 0x07, 0x00, 0x00, 0x00
+};
+
+/*
+ This message is the last one sent by the device during initialization.
+*/
+static const char variax_init_done[] = {
+ 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
+};
+
+static const char variax_activate[] = {
+ 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
+ 0xf7
+};
+
+static const char variax_request_bank[] = {
+ 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6d, 0xf7
+};
+
+static const char variax_request_model1[] = {
+ 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x03,
+ 0x00, 0x00, 0x00, 0xf7
+};
+
+static const char variax_request_model2[] = {
+ 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x03,
+ 0x00, 0x00, 0x00, 0xf7
+};
+
+/* forward declarations: */
+static int variax_create_files2(struct device *dev);
+static void variax_startup2(unsigned long data);
+static void variax_startup4(unsigned long data);
+static void variax_startup5(unsigned long data);
+
+/*
+ Decode data transmitted by workbench.
+*/
+static void variax_decode(const unsigned char *raw_data, unsigned char *data,
+ int raw_size)
+{
+ for (; raw_size > 0; raw_size -= 6) {
+ data[2] = raw_data[0] | (raw_data[1] << 4);
+ data[1] = raw_data[2] | (raw_data[3] << 4);
+ data[0] = raw_data[4] | (raw_data[5] << 4);
+ raw_data += 6;
+ data += 3;
+ }
+}
+
+static void variax_activate_async(struct usb_line6_variax *variax, int a)
+{
+ variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
+ line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
+ sizeof(variax_activate));
+}
+
+/*
+ Variax startup procedure.
+ This is a sequence of functions with special requirements (e.g., must
+ not run immediately after initialization, must not run in interrupt
+ context). After the last one has finished, the device is ready to use.
+*/
+
+static void variax_startup1(struct usb_line6_variax *variax)
+{
+ CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT);
+
+ /* delay startup procedure: */
+ line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
+ variax_startup2, (unsigned long)variax);
+}
+
+static void variax_startup2(unsigned long data)
+{
+ struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
+ struct usb_line6 *line6 = &variax->line6;
+
+ /* schedule another startup procedure until startup is complete: */
+ if (variax->startup_progress >= VARIAX_STARTUP_LAST)
+ return;
+
+ variax->startup_progress = VARIAX_STARTUP_VERSIONREQ;
+ line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
+ variax_startup2, (unsigned long)variax);
+
+ /* request firmware version: */
+ line6_version_request_async(line6);
+}
+
+static void variax_startup3(struct usb_line6_variax *variax)
+{
+ CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT);
+
+ /* delay startup procedure: */
+ line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3,
+ variax_startup4, (unsigned long)variax);
+}
+
+static void variax_startup4(unsigned long data)
+{
+ struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
+ CHECK_STARTUP_PROGRESS(variax->startup_progress,
+ VARIAX_STARTUP_ACTIVATE);
+
+ /* activate device: */
+ variax_activate_async(variax, 1);
+ line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4,
+ variax_startup5, (unsigned long)variax);
+}
+
+static void variax_startup5(unsigned long data)
+{
+ struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
+ CHECK_STARTUP_PROGRESS(variax->startup_progress,
+ VARIAX_STARTUP_DUMPREQ);
+
+ /* current model dump: */
+ line6_dump_request_async(&variax->dumpreq, &variax->line6, 0,
+ VARIAX_DUMP_PASS1);
+ /* passes 2 and 3 are performed implicitly before entering variax_startup6 */
+}
+
+static void variax_startup6(struct usb_line6_variax *variax)
+{
+ CHECK_STARTUP_PROGRESS(variax->startup_progress,
+ VARIAX_STARTUP_WORKQUEUE);
+
+ /* schedule work for global work queue: */
+ schedule_work(&variax->startup_work);
+}
+
+static void variax_startup7(struct work_struct *work)
+{
+ struct usb_line6_variax *variax =
+ container_of(work, struct usb_line6_variax, startup_work);
+ struct usb_line6 *line6 = &variax->line6;
+
+ CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP);
+
+ /* ALSA audio interface: */
+ line6_register_audio(&variax->line6);
+
+ /* device files: */
+ line6_variax_create_files(0, 0, line6->ifcdev);
+ variax_create_files2(line6->ifcdev);
+}
+
+/*
+ Process a completely received message.
+*/
+void line6_variax_process_message(struct usb_line6_variax *variax)
+{
+ const unsigned char *buf = variax->line6.buffer_message;
+
+ switch (buf[0]) {
+ case LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST:
+ switch (buf[1]) {
+ case VARIAXMIDI_volume:
+ variax->volume = buf[2];
+ break;
+
+ case VARIAXMIDI_tone:
+ variax->tone = buf[2];
+ }
+
+ break;
+
+ case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE:
+ case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST:
+ variax->model = buf[1];
+ line6_dump_request_async(&variax->dumpreq, &variax->line6, 0,
+ VARIAX_DUMP_PASS1);
+ break;
+
+ case LINE6_RESET:
+ dev_info(variax->line6.ifcdev, "VARIAX reset\n");
+ break;
+
+ case LINE6_SYSEX_BEGIN:
+ if (memcmp(buf + 1, variax_request_model1 + 1,
+ VARIAX_MODEL_HEADER_LENGTH - 1) == 0) {
+ if (variax->line6.message_length ==
+ VARIAX_MODEL_MESSAGE_LENGTH) {
+ switch (variax->dumpreq.in_progress) {
+ case VARIAX_DUMP_PASS1:
+ variax_decode(buf +
+ VARIAX_MODEL_HEADER_LENGTH,
+ (unsigned char *)
+ &variax->model_data,
+ (sizeof
+ (variax->model_data.
+ name) +
+ sizeof(variax->
+ model_data.
+ control)
+ / 2) * 2);
+ line6_dump_request_async
+ (&variax->dumpreq, &variax->line6,
+ 1, VARIAX_DUMP_PASS2);
+ break;
+
+ case VARIAX_DUMP_PASS2:
+ /* model name is transmitted twice, so skip it here: */
+ variax_decode(buf +
+ VARIAX_MODEL_HEADER_LENGTH,
+ (unsigned char *)
+ &variax->
+ model_data.control +
+ sizeof(variax->model_data.
+ control)
+ / 2,
+ sizeof(variax->model_data.
+ control)
+ / 2 * 2);
+ line6_dump_request_async
+ (&variax->dumpreq, &variax->line6,
+ 2, VARIAX_DUMP_PASS3);
+ }
+ } else {
+ DEBUG_MESSAGES(dev_err
+ (variax->line6.ifcdev,
+ "illegal length %d of model data\n",
+ variax->line6.message_length));
+ line6_dump_finished(&variax->dumpreq);
+ }
+ } else if (memcmp(buf + 1, variax_request_bank + 1,
+ sizeof(variax_request_bank) - 2) == 0) {
+ memcpy(variax->bank,
+ buf + sizeof(variax_request_bank) - 1,
+ sizeof(variax->bank));
+ line6_dump_finished(&variax->dumpreq);
+ variax_startup6(variax);
+ } else if (memcmp(buf + 1, variax_init_model + 1,
+ sizeof(variax_init_model) - 1) == 0) {
+ memcpy(variax->guitar,
+ buf + sizeof(variax_init_model),
+ sizeof(variax->guitar));
+ } else if (memcmp(buf + 1, variax_init_version + 1,
+ sizeof(variax_init_version) - 1) == 0) {
+ variax_startup3(variax);
+ } else if (memcmp(buf + 1, variax_init_done + 1,
+ sizeof(variax_init_done) - 1) == 0) {
+ /* notify of complete initialization: */
+ variax_startup4((unsigned long)variax);
+ }
+
+ break;
+
+ case LINE6_SYSEX_END:
+ break;
+
+ default:
+ DEBUG_MESSAGES(dev_err
+ (variax->line6.ifcdev,
+ "Variax: unknown message %02X\n", buf[0]));
+ }
+}
+
+/*
+ "read" request on "volume" special file.
+*/
+static ssize_t variax_get_volume(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ return sprintf(buf, "%d\n", variax->volume);
+}
+
+/*
+ "write" request on "volume" special file.
+*/
+static ssize_t variax_set_volume(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ unsigned long value;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ if (line6_transmit_parameter(&variax->line6, VARIAXMIDI_volume,
+ value) == 0)
+ variax->volume = value;
+
+ return count;
+}
+
+/*
+ "read" request on "model" special file.
+*/
+static ssize_t variax_get_model(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ return sprintf(buf, "%d\n", variax->model);
+}
+
+/*
+ "write" request on "model" special file.
+*/
+static ssize_t variax_set_model(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ unsigned long value;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ if (line6_send_program(&variax->line6, value) == 0)
+ variax->model = value;
+
+ return count;
+}
+
+/*
+ "read" request on "active" special file.
+*/
+static ssize_t variax_get_active(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ return sprintf(buf, "%d\n",
+ variax->buffer_activate[VARIAX_OFFSET_ACTIVATE]);
+}
+
+/*
+ "write" request on "active" special file.
+*/
+static ssize_t variax_set_active(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ unsigned long value;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ variax_activate_async(variax, value ? 1 : 0);
+ return count;
+}
+
+/*
+ "read" request on "tone" special file.
+*/
+static ssize_t variax_get_tone(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ return sprintf(buf, "%d\n", variax->tone);
+}
+
+/*
+ "write" request on "tone" special file.
+*/
+static ssize_t variax_set_tone(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ unsigned long value;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ if (line6_transmit_parameter(&variax->line6, VARIAXMIDI_tone,
+ value) == 0)
+ variax->tone = value;
+
+ return count;
+}
+
+static ssize_t get_string(char *buf, const char *data, int length)
+{
+ int i;
+ memcpy(buf, data, length);
+
+ for (i = length; i--;) {
+ char c = buf[i];
+
+ if ((c != 0) && (c != ' '))
+ break;
+ }
+
+ buf[i + 1] = '\n';
+ return i + 2;
+}
+
+/*
+ "read" request on "name" special file.
+*/
+static ssize_t variax_get_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ line6_dump_wait_interruptible(&variax->dumpreq);
+ return get_string(buf, variax->model_data.name,
+ sizeof(variax->model_data.name));
+}
+
+/*
+ "read" request on "bank" special file.
+*/
+static ssize_t variax_get_bank(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ line6_dump_wait_interruptible(&variax->dumpreq);
+ return get_string(buf, variax->bank, sizeof(variax->bank));
+}
+
+/*
+ "read" request on "dump" special file.
+*/
+static ssize_t variax_get_dump(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ int retval;
+ retval = line6_dump_wait_interruptible(&variax->dumpreq);
+ if (retval < 0)
+ return retval;
+ memcpy(buf, &variax->model_data.control,
+ sizeof(variax->model_data.control));
+ return sizeof(variax->model_data.control);
+}
+
+/*
+ "read" request on "guitar" special file.
+*/
+static ssize_t variax_get_guitar(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ return sprintf(buf, "%s\n", variax->guitar);
+}
+
+#ifdef CONFIG_LINE6_USB_RAW
+
+static char *variax_alloc_sysex_buffer(struct usb_line6_variax *variax,
+ int code, int size)
+{
+ return line6_alloc_sysex_buffer(&variax->line6, VARIAX_SYSEX_CODE, code,
+ size);
+}
+
+/*
+ "write" request on "raw" special file.
+*/
+static ssize_t variax_set_raw2(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_line6_variax *variax =
+ usb_get_intfdata(to_usb_interface(dev));
+ int size;
+ int i;
+ char *sysex;
+
+ count -= count % 3;
+ size = count * 2;
+ sysex = variax_alloc_sysex_buffer(variax, VARIAX_SYSEX_PARAM, size);
+
+ if (!sysex)
+ return 0;
+
+ for (i = 0; i < count; i += 3) {
+ const unsigned char *p1 = buf + i;
+ char *p2 = sysex + SYSEX_DATA_OFS + i * 2;
+ p2[0] = p1[2] & 0x0f;
+ p2[1] = p1[2] >> 4;
+ p2[2] = p1[1] & 0x0f;
+ p2[3] = p1[1] >> 4;
+ p2[4] = p1[0] & 0x0f;
+ p2[5] = p1[0] >> 4;
+ }
+
+ line6_send_sysex_message(&variax->line6, sysex, size);
+ kfree(sysex);
+ return count;
+}
+
+#endif
+
+/* Variax workbench special files: */
+static DEVICE_ATTR(model, S_IWUSR | S_IRUGO, variax_get_model,
+ variax_set_model);
+static DEVICE_ATTR(volume, S_IWUSR | S_IRUGO, variax_get_volume,
+ variax_set_volume);
+static DEVICE_ATTR(tone, S_IWUSR | S_IRUGO, variax_get_tone, variax_set_tone);
+static DEVICE_ATTR(name, S_IRUGO, variax_get_name, line6_nop_write);
+static DEVICE_ATTR(bank, S_IRUGO, variax_get_bank, line6_nop_write);
+static DEVICE_ATTR(dump, S_IRUGO, variax_get_dump, line6_nop_write);
+static DEVICE_ATTR(active, S_IWUSR | S_IRUGO, variax_get_active,
+ variax_set_active);
+static DEVICE_ATTR(guitar, S_IRUGO, variax_get_guitar, line6_nop_write);
+
+#ifdef CONFIG_LINE6_USB_RAW
+static DEVICE_ATTR(raw, S_IWUSR, line6_nop_read, line6_set_raw);
+static DEVICE_ATTR(raw2, S_IWUSR, line6_nop_read, variax_set_raw2);
+#endif
+
+/*
+ Variax destructor.
+*/
+static void variax_destruct(struct usb_interface *interface)
+{
+ struct usb_line6_variax *variax = usb_get_intfdata(interface);
+
+ if (variax == NULL)
+ return;
+ line6_cleanup_audio(&variax->line6);
+
+ del_timer(&variax->startup_timer1);
+ del_timer(&variax->startup_timer2);
+ cancel_work_sync(&variax->startup_work);
+
+ /* free dump request data: */
+ line6_dumpreq_destructbuf(&variax->dumpreq, 2);
+ line6_dumpreq_destructbuf(&variax->dumpreq, 1);
+ line6_dumpreq_destruct(&variax->dumpreq);
+
+ kfree(variax->buffer_activate);
+}
+
+/*
+ Create sysfs entries.
+*/
+static int variax_create_files2(struct device *dev)
+{
+ int err;
+ CHECK_RETURN(device_create_file(dev, &dev_attr_model));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_volume));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_tone));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_name));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_bank));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_dump));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_active));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_guitar));
+#ifdef CONFIG_LINE6_USB_RAW
+ CHECK_RETURN(device_create_file(dev, &dev_attr_raw));
+ CHECK_RETURN(device_create_file(dev, &dev_attr_raw2));
+#endif
+ return 0;
+}
+
+/*
+ Try to init workbench device.
+*/
+static int variax_try_init(struct usb_interface *interface,
+ struct usb_line6_variax *variax)
+{
+ int err;
+
+ init_timer(&variax->startup_timer1);
+ init_timer(&variax->startup_timer2);
+ INIT_WORK(&variax->startup_work, variax_startup7);
+
+ if ((interface == NULL) || (variax == NULL))
+ return -ENODEV;
+
+ /* initialize USB buffers: */
+ err = line6_dumpreq_init(&variax->dumpreq, variax_request_model1,
+ sizeof(variax_request_model1));
+
+ if (err < 0) {
+ dev_err(&interface->dev, "Out of memory\n");
+ return err;
+ }
+
+ err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_model2,
+ sizeof(variax_request_model2), 1);
+
+ if (err < 0) {
+ dev_err(&interface->dev, "Out of memory\n");
+ return err;
+ }
+
+ err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_bank,
+ sizeof(variax_request_bank), 2);
+
+ if (err < 0) {
+ dev_err(&interface->dev, "Out of memory\n");
+ return err;
+ }
+
+ variax->buffer_activate = kmemdup(variax_activate,
+ sizeof(variax_activate), GFP_KERNEL);
+
+ if (variax->buffer_activate == NULL) {
+ dev_err(&interface->dev, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ /* initialize audio system: */
+ err = line6_init_audio(&variax->line6);
+ if (err < 0)
+ return err;
+
+ /* initialize MIDI subsystem: */
+ err = line6_init_midi(&variax->line6);
+ if (err < 0)
+ return err;
+
+ /* initiate startup procedure: */
+ variax_startup1(variax);
+ return 0;
+}
+
+/*
+ Init workbench device (and clean up in case of failure).
+*/
+int line6_variax_init(struct usb_interface *interface,
+ struct usb_line6_variax *variax)
+{
+ int err = variax_try_init(interface, variax);
+
+ if (err < 0)
+ variax_destruct(interface);
+
+ return err;
+}
+
+/*
+ Workbench device disconnected.
+*/
+void line6_variax_disconnect(struct usb_interface *interface)
+{
+ struct device *dev;
+
+ if (interface == NULL)
+ return;
+ dev = &interface->dev;
+
+ if (dev != NULL) {
+ /* remove sysfs entries: */
+ line6_variax_remove_files(0, 0, dev);
+ device_remove_file(dev, &dev_attr_model);
+ device_remove_file(dev, &dev_attr_volume);
+ device_remove_file(dev, &dev_attr_tone);
+ device_remove_file(dev, &dev_attr_name);
+ device_remove_file(dev, &dev_attr_bank);
+ device_remove_file(dev, &dev_attr_dump);
+ device_remove_file(dev, &dev_attr_active);
+ device_remove_file(dev, &dev_attr_guitar);
+#ifdef CONFIG_LINE6_USB_RAW
+ device_remove_file(dev, &dev_attr_raw);
+ device_remove_file(dev, &dev_attr_raw2);
+#endif
+ }
+
+ variax_destruct(interface);
+}
diff --git a/drivers/staging/line6/variax.h b/drivers/staging/line6/variax.h
new file mode 100644
index 00000000..e2999ab4
--- /dev/null
+++ b/drivers/staging/line6/variax.h
@@ -0,0 +1,132 @@
+/*
+ * Line6 Linux USB driver - 0.9.1beta
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef VARIAX_H
+#define VARIAX_H
+
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+
+#include "driver.h"
+#include "dumprequest.h"
+
+#define VARIAX_STARTUP_DELAY1 1000
+#define VARIAX_STARTUP_DELAY3 100
+#define VARIAX_STARTUP_DELAY4 100
+
+/*
+ Stages of Variax startup procedure
+*/
+enum {
+ VARIAX_STARTUP_INIT = 1,
+ VARIAX_STARTUP_VERSIONREQ,
+ VARIAX_STARTUP_WAIT,
+ VARIAX_STARTUP_ACTIVATE,
+ VARIAX_STARTUP_DUMPREQ,
+ VARIAX_STARTUP_WORKQUEUE,
+ VARIAX_STARTUP_SETUP,
+ VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1
+};
+
+enum {
+ VARIAX_DUMP_PASS1 = LINE6_DUMP_CURRENT,
+ VARIAX_DUMP_PASS2,
+ VARIAX_DUMP_PASS3
+};
+
+/**
+ Binary Variax model dump
+*/
+struct variax_model {
+ /**
+ Header information (including program name).
+ */
+ unsigned char name[18];
+
+ /**
+ Model parameters.
+ */
+ unsigned char control[78 * 2];
+};
+
+struct usb_line6_variax {
+ /**
+ Generic Line6 USB data.
+ */
+ struct usb_line6 line6;
+
+ /**
+ Dump request structure.
+ Append two extra buffers for 3-pass data query.
+ */
+ struct line6_dump_request dumpreq;
+ struct line6_dump_reqbuf extrabuf[2];
+
+ /**
+ Buffer for activation code.
+ */
+ unsigned char *buffer_activate;
+
+ /**
+ Model number.
+ */
+ int model;
+
+ /**
+ Current model settings.
+ */
+ struct variax_model model_data;
+
+ /**
+ Name of connected guitar.
+ */
+ unsigned char guitar[18];
+
+ /**
+ Name of current model bank.
+ */
+ unsigned char bank[18];
+
+ /**
+ Position of volume dial.
+ */
+ int volume;
+
+ /**
+ Position of tone control dial.
+ */
+ int tone;
+
+ /**
+ Handler for device initializaton.
+ */
+ struct work_struct startup_work;
+
+ /**
+ Timers for device initializaton.
+ */
+ struct timer_list startup_timer1;
+ struct timer_list startup_timer2;
+
+ /**
+ Current progress in startup procedure.
+ */
+ int startup_progress;
+};
+
+extern void line6_variax_disconnect(struct usb_interface *interface);
+extern int line6_variax_init(struct usb_interface *interface,
+ struct usb_line6_variax *variax);
+extern void line6_variax_process_message(struct usb_line6_variax *variax);
+
+#endif