aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging
diff options
context:
space:
mode:
authorMarkus Grabner <grabner@icg.tugraz.at>2009-02-27 19:43:04 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2009-04-03 14:54:24 -0700
commit705ececd1c60d0f5d6ef2a719008847883516970 (patch)
treee2a96ac85e15850919e493181fe47f7fdd471af1 /drivers/staging
parente642f09951f7cbb69983781b07bb9cd881546ac4 (diff)
Staging: add line6 usb driver
This is an experimental Linux 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) Moreover, preliminary support for the Variax Workbench is included. From: Markus Grabner <grabner@icg.tugraz.at> Cc: Mariusz Kozlowski <m.kozlowski@tuxland.pl> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/line6/Kconfig20
-rw-r--r--drivers/staging/line6/Makefile15
-rw-r--r--drivers/staging/line6/audio.c69
-rw-r--r--drivers/staging/line6/audio.h24
-rw-r--r--drivers/staging/line6/capture.c370
-rw-r--r--drivers/staging/line6/capture.h31
-rw-r--r--drivers/staging/line6/config.h73
-rw-r--r--drivers/staging/line6/control.c702
-rw-r--r--drivers/staging/line6/control.h187
-rw-r--r--drivers/staging/line6/driver.c1050
-rw-r--r--drivers/staging/line6/driver.h190
-rw-r--r--drivers/staging/line6/dumprequest.c143
-rw-r--r--drivers/staging/line6/dumprequest.h87
-rw-r--r--drivers/staging/line6/midi.c398
-rw-r--r--drivers/staging/line6/midi.h87
-rw-r--r--drivers/staging/line6/midibuf.c268
-rw-r--r--drivers/staging/line6/midibuf.h39
-rw-r--r--drivers/staging/line6/pcm.c289
-rw-r--r--drivers/staging/line6/pcm.h210
-rw-r--r--drivers/staging/line6/playback.c428
-rw-r--r--drivers/staging/line6/playback.h29
-rw-r--r--drivers/staging/line6/pod.c1100
-rw-r--r--drivers/staging/line6/pod.h203
-rw-r--r--drivers/staging/line6/revision.h4
-rw-r--r--drivers/staging/line6/toneport.c219
-rw-r--r--drivers/staging/line6/toneport.h44
-rw-r--r--drivers/staging/line6/usbdefs.h75
-rw-r--r--drivers/staging/line6/variax.c501
-rw-r--r--drivers/staging/line6/variax.h107
29 files changed, 6962 insertions, 0 deletions
diff --git a/drivers/staging/line6/Kconfig b/drivers/staging/line6/Kconfig
new file mode 100644
index 00000000000..3c1ffcb8cb1
--- /dev/null
+++ b/drivers/staging/line6/Kconfig
@@ -0,0 +1,20 @@
+config LINE6_USB
+ tristate "Line6 USB support"
+ depends on USB
+ 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 is included.
+
diff --git a/drivers/staging/line6/Makefile b/drivers/staging/line6/Makefile
new file mode 100644
index 00000000000..a1c93edc6b1
--- /dev/null
+++ b/drivers/staging/line6/Makefile
@@ -0,0 +1,15 @@
+obj-$(CONFIG_LINE6_USB) += line6usb.o
+
+line6usb-objs := \
+ audio.o \
+ capture.o \
+ control.o \
+ driver.o \
+ dumprequest.o \
+ midi.o \
+ midibuf.o \
+ pcm.o \
+ playback.o \
+ pod.o \
+ toneport.o \
+ variax.o
diff --git a/drivers/staging/line6/audio.c b/drivers/staging/line6/audio.c
new file mode 100644
index 00000000000..e15fa1f6ee4
--- /dev/null
+++ b/drivers/staging/line6/audio.c
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ *
+ */
+
+#include "driver.h"
+
+#include <sound/core.h>
+#include <sound/initval.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 = 0;
+ struct snd_card *card;
+
+ card = snd_card_new(line6_index[dev], line6_id[dev], THIS_MODULE, 0);
+
+ if(card == NULL)
+ return -ENOMEM;
+
+ line6->card = card;
+
+ strcpy(card->driver, DRIVER_NAME);
+ strcpy(card->shortname, "Line6-USB");
+ sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name, line6->ifcdev->bus_id); /* 80 chars - see asound.h */
+ return 0;
+}
+
+/*
+ Register the Line6 USB audio system.
+*/
+int line6_register_audio(struct usb_line6 *line6)
+{
+ int err;
+
+ if((err = snd_card_register(line6->card)) < 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 == 0)
+ return;
+
+ snd_card_disconnect(card);
+ snd_card_free(card);
+ line6->card = 0;
+}
diff --git a/drivers/staging/line6/audio.h b/drivers/staging/line6/audio.h
new file mode 100644
index 00000000000..cc0245adbcd
--- /dev/null
+++ b/drivers/staging/line6/audio.h
@@ -0,0 +1,24 @@
+/*
+ * 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 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 00000000000..5dec3bfff04
--- /dev/null
+++ b/drivers/staging/line6/capture.c
@@ -0,0 +1,370 @@
+/*
+ * 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.
+ *
+ */
+
+#include "driver.h"
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "audio.h"
+#include "pcm.h"
+#include "pod.h"
+
+
+/*
+ Find a free URB and submit it.
+*/
+static int submit_audio_in_urb(struct snd_pcm_substream *substream)
+{
+ int index;
+ unsigned long flags;
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+ int i, urb_size;
+ 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(s2m(substream), "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 = substream;
+
+ if(usb_submit_urb(urb_in, GFP_ATOMIC) == 0)
+ set_bit(index, &line6pcm->active_urb_in);
+ else
+ dev_err(s2m(substream), "URB in #%d submission failed\n", index);
+
+ spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
+ return 0;
+}
+
+/*
+ Submit all currently available capture URBs.
+*/
+static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream)
+{
+ int ret, i;
+
+ for(i = 0; i < LINE6_ISO_BUFFERS; ++i)
+ if((ret = submit_audio_in_urb(substream)) < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ Unlink all currently active capture URBs.
+*/
+static void 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];
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
+ u->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+ usb_unlink_urb(u);
+ }
+ }
+ }
+}
+
+/*
+ Wait until unlinking of all currently active capture URBs has been finished.
+*/
+static void 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);
+
+ line6pcm->active_urb_in = 0;
+ line6pcm->unlink_urb_in = 0;
+}
+
+/*
+ Unlink all currently active capture URBs, and wait for finishing.
+*/
+void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
+{
+ unlink_audio_in_urbs(line6pcm);
+ wait_clear_audio_in_urbs(line6pcm);
+}
+
+/*
+ Callback for completed capture URB.
+*/
+static void audio_in_callback(struct urb *urb PT_REGS)
+{
+ int i, index, length = 0, shutdown = 0;
+ int frames;
+ unsigned long flags;
+
+ struct snd_pcm_substream *substream = (struct snd_pcm_substream *)urb->context;
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+ const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ /* find index of URB */
+ for(index = 0; index < LINE6_ISO_BUFFERS; ++index)
+ if(urb == line6pcm->urb_audio_in[index])
+ break;
+
+#if DO_DUMP_PCM_RECEIVE
+ 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 == -18) {
+ shutdown = 1;
+ break;
+ }
+
+ fbuf = urb->transfer_buffer + fin->offset;
+ fsize = fin->actual_length;
+ length += fsize;
+
+ if(fsize > 0) {
+ frames = fsize / bytes_per_frame;
+
+ 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
+ dev_err(s2m(substream), "driver bug: len = %d\n", len); /* this is somewhat paranoid */
+ }
+ else {
+ /* copy single chunk */
+ memcpy(runtime->dma_area + line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize * bytes_per_frame);
+ }
+
+ if((line6pcm->pos_in_done += frames) >= runtime->buffer_size)
+ line6pcm->pos_in_done -= runtime->buffer_size;
+ }
+ }
+
+ clear_bit(index, &line6pcm->active_urb_in);
+
+ if(test_bit(index, &line6pcm->unlink_urb_in))
+ shutdown = 1;
+
+ spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
+
+ if(!shutdown) {
+ submit_audio_in_urb(substream);
+
+ if((line6pcm->bytes_in += length) >= line6pcm->period_in) {
+ line6pcm->bytes_in -= line6pcm->period_in;
+ snd_pcm_period_elapsed(substream);
+ }
+ }
+}
+
+/* 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);
+
+ if((err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ (&line6pcm->properties->snd_line6_rates))) < 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 */
+
+ if((ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ return ret;
+
+ line6pcm->period_in = params_period_bytes(hw_params);
+ line6pcm->buffer_in = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL);
+
+ if(!line6pcm->buffer_in) {
+ dev_err(s2m(substream), "cannot malloc buffer_in\n");
+ return -ENOMEM;
+ }
+
+ 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);
+ unlink_wait_clear_audio_in_urbs(line6pcm);
+
+ if(line6pcm->buffer_in) {
+ kfree(line6pcm->buffer_in);
+ line6pcm->buffer_in = 0;
+ }
+
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* trigger callback */
+int snd_line6_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+ int err;
+ line6pcm->count_in = 0;
+
+ switch(cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if(!test_and_set_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) {
+ err = submit_audio_in_all_urbs(substream);
+
+ if(err < 0) {
+ clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags);
+ return err;
+ }
+ }
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ if(test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags))
+ unlink_audio_in_urbs(line6pcm);
+
+ 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 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 00000000000..7b92e4de392
--- /dev/null
+++ b/drivers/staging/line6/capture.h
@@ -0,0 +1,31 @@
+/*
+ * 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 CAPTURE_H
+#define CAPTURE_H
+
+
+#include "driver.h"
+
+#include <sound/pcm.h>
+
+#include "pcm.h"
+
+
+extern struct snd_pcm_ops snd_line6_capture_ops;
+
+
+extern int create_audio_in_urbs(struct snd_line6_pcm *line6pcm);
+extern int snd_line6_capture_trigger(struct snd_pcm_substream *substream, int cmd);
+extern void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm);
+
+
+#endif
diff --git a/drivers/staging/line6/config.h b/drivers/staging/line6/config.h
new file mode 100644
index 00000000000..d5ed1a740b0
--- /dev/null
+++ b/drivers/staging/line6/config.h
@@ -0,0 +1,73 @@
+/*
+ * 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
+
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+#include <linux/config.h>
+#endif
+
+#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("line6usb: %s (%s:%d)\n", __FUNCTION__, __FILE__, __LINE__)
+#endif
+
+/**
+ In Linux 2.6.13 and later, the device_attribute is passed to the sysfs
+ get/set functions (see /usr/src/linux/include/linux/device.h).
+*/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
+#define DEVICE_ATTRIBUTE struct device_attribute *attr,
+#else
+#define DEVICE_ATTRIBUTE
+#endif
+
+/**
+ In Linux 2.6.20 and later, the pt_regs is no longer passed to USB callback
+ functions.
+*/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+#define PT_REGS
+#else
+#define PT_REGS , struct pt_regs *regs
+#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 00000000000..d44d06d7b13
--- /dev/null
+++ b/drivers/staging/line6/control.c
@@ -0,0 +1,702 @@
+/*
+ * 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.
+ *
+ */
+
+#include "driver.h"
+
+#include <linux/usb.h>
+
+#include "control.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, DEVICE_ATTRIBUTE 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, DEVICE_ATTRIBUTE 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_wait_dump(&pod->dumpreq, 0);
+ 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);
+ int value = simple_strtoul(buf, NULL, 10);
+ 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_wait_dump(&variax->dumpreq, 0);
+ 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 floats in the kernel are
+ problematic for at least two reasons:
+ - many distros are still shipped with binary kernels optimized for the
+ ancient 80386 without FPU
+ - there isn't a printf("%f")
+ (see http://www.kernelthread.com/publications/faq/335.html)
+ */
+
+ 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_wait_dump(&variax->dumpreq, 0);
+ 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);