staging: line6: sync with upstream
authorMarkus Grabner <grabner@icg.tugraz.at>
Wed, 11 Aug 2010 23:35:30 +0000 (01:35 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 31 Aug 2010 22:28:15 +0000 (15:28 -0700)
Big upstream sync.

Signed-off-by: Markus Grabner <grabner@icg.tugraz.at>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
27 files changed:
drivers/staging/line6/Kconfig
drivers/staging/line6/audio.c
drivers/staging/line6/audio.h
drivers/staging/line6/capture.c
drivers/staging/line6/capture.h
drivers/staging/line6/control.c
drivers/staging/line6/control.h
drivers/staging/line6/driver.c
drivers/staging/line6/driver.h
drivers/staging/line6/dumprequest.c
drivers/staging/line6/dumprequest.h
drivers/staging/line6/midi.c
drivers/staging/line6/midi.h
drivers/staging/line6/midibuf.c
drivers/staging/line6/midibuf.h
drivers/staging/line6/pcm.c
drivers/staging/line6/pcm.h
drivers/staging/line6/playback.c
drivers/staging/line6/playback.h
drivers/staging/line6/pod.c
drivers/staging/line6/pod.h
drivers/staging/line6/revision.h
drivers/staging/line6/toneport.c
drivers/staging/line6/toneport.h
drivers/staging/line6/usbdefs.h
drivers/staging/line6/variax.c
drivers/staging/line6/variax.h

index bc1ffbe..43120ff 100644 (file)
@@ -1,4 +1,4 @@
-config LINE6_USB
+menuconfig LINE6_USB
        tristate "Line6 USB support"
        depends on USB && SND
        select SND_RAWMIDI
@@ -18,5 +18,68 @@ config LINE6_USB
            * Signal routing (record clean/processed guitar signal,
              re-amping)
 
-         Preliminary support for the Variax Workbench is included.
+         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
index e2ac8d6..7b58b8b 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -9,12 +9,12 @@
  *
  */
 
-#include "driver.h"
-#include "audio.h"
-
 #include <sound/core.h>
 #include <sound/initval.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;
@@ -36,8 +36,9 @@ int line6_init_audio(struct usb_line6 *line6)
 
        line6->card = card;
 
+       strcpy(card->id, line6->properties->id);
        strcpy(card->driver, DRIVER_NAME);
-       strcpy(card->shortname, "Line6-USB");
+       strcpy(card->shortname, line6->properties->name);
        sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name,
                dev_name(line6->ifcdev));  /* 80 chars - see asound.h */
        return 0;
index cc0245a..f12acac 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
index ca09224..d4b70d3 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -9,27 +9,24 @@
  *
  */
 
-#include "driver.h"
-
-#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 "capture.h"
+
 
 /*
        Find a free URB and submit it.
 */
-static int submit_audio_in_urb(struct snd_pcm_substream *substream)
+static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
 {
-       unsigned int index;
+       int index;
        unsigned long flags;
-       struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
        int i, urb_size;
        struct urb *urb_in;
 
@@ -37,9 +34,9 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
        index =
            find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS);
 
-       if (index >= 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");
+               dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
                return -EINVAL;
        }
 
@@ -58,13 +55,13 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
            line6pcm->buffer_in +
            index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
        urb_in->transfer_buffer_length = urb_size;
-       urb_in->context = substream;
+       urb_in->context = line6pcm;
 
        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);
+               dev_err(line6pcm->line6->ifcdev,
+                       "URB in #%d submission failed\n", index);
 
        spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
        return 0;
@@ -73,12 +70,12 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
 /*
        Submit all currently available capture URBs.
 */
-static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream)
+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(substream);
+               ret = submit_audio_in_urb(line6pcm);
                if (ret < 0)
                        return ret;
        }
@@ -89,7 +86,7 @@ static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream)
 /*
        Unlink all currently active capture URBs.
 */
-static void unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm)
+void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm)
 {
        unsigned int i;
 
@@ -126,41 +123,83 @@ static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
        } 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)
+void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
 {
-       unlink_audio_in_urbs(line6pcm);
+       line6_unlink_audio_in_urbs(line6pcm);
        wait_clear_audio_in_urbs(line6pcm);
 }
 
 /*
-       Callback for completed capture URB.
+       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 (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(line6pcm->line6->ifcdev, "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);
+       }
+
+       if ((line6pcm->pos_in_done += frames) >= 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);
+
+       if ((line6pcm->bytes_in += length) >= line6pcm->period_in) {
+               line6pcm->bytes_in %= line6pcm->period_in;
+               snd_pcm_period_elapsed(substream);
+       }
+}
+
+/*
+  Callback for completed capture URB.
 */
 static void audio_in_callback(struct urb *urb)
 {
        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;
+       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;
 
-#if DO_DUMP_PCM_RECEIVE
+#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];
@@ -184,64 +223,43 @@ static void audio_in_callback(struct urb *urb)
 
                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;
 
-               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 {
-                                       /* this is somewhat paranoid */
-                                       dev_err(s2m(substream),
-                                               "driver bug: len = %d\n", len);
-                               }
-                       } else {
-                               /* copy single chunk */
-                               memcpy(runtime->dma_area +
-                                      line6pcm->pos_in_done * bytes_per_frame,
-                                      fbuf, fsize * bytes_per_frame);
-                       }
+               /* the following assumes LINE6_ISO_PACKETS == 1: */
+#if LINE6_BACKUP_MONITOR_SIGNAL
+               memcpy(line6pcm->prev_fbuf, fbuf, fsize);
+#else
+               line6pcm->prev_fbuf = fbuf;
+#endif
+               line6pcm->prev_fsize = fsize;
 
-                       line6pcm->pos_in_done += frames;
-                       if (line6pcm->pos_in_done >= runtime->buffer_size)
-                               line6pcm->pos_in_done -= runtime->buffer_size;
-               }
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+               if (!(line6pcm->flags & MASK_PCM_IMPULSE))
+#endif
+                       if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags)
+                           && (fsize > 0))
+                               line6_capture_copy(line6pcm, fbuf, fsize);
        }
 
        clear_bit(index, &line6pcm->active_urb_in);
 
-       if (test_bit(index, &line6pcm->unlink_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(substream);
+               submit_audio_in_urb(line6pcm);
 
-               line6pcm->bytes_in += length;
-               if (line6pcm->bytes_in >= line6pcm->period_in) {
-                       line6pcm->bytes_in -= line6pcm->period_in;
-                       snd_pcm_period_elapsed(substream);
-               }
+               if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags))
+                       line6_capture_check_period(line6pcm, length);
        }
 }
 
@@ -294,54 +312,40 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream,
                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);
-
-       kfree(line6pcm->buffer_in);
-       line6pcm->buffer_in = NULL;
-
        return snd_pcm_lib_free_pages(substream);
 }
 
 /* trigger callback */
-int snd_line6_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, 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);
+#ifdef CONFIG_PM
+       case SNDRV_PCM_TRIGGER_RESUME:
+#endif
+               err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_CAPTURE);
 
-                       if (err < 0) {
-                               clear_bit(BIT_RUNNING_CAPTURE,
-                                         &line6pcm->flags);
-                               return err;
-                       }
-               }
+               if (err < 0)
+                       return err;
 
                break;
 
        case SNDRV_PCM_TRIGGER_STOP:
-               if (test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags))
-                       unlink_audio_in_urbs(line6pcm);
+#ifdef CONFIG_PM
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+#endif
+               err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_CAPTURE);
+
+               if (err < 0)
+                       return err;
 
                break;
 
@@ -362,17 +366,17 @@ snd_line6_capture_pointer(struct snd_pcm_substream *substream)
 
 /* 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,
+       .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 line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
 {
        int i;
 
index 5c44464..c9ea063 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
 #define CAPTURE_H
 
 
-#include "driver.h"
-
 #include <sound/pcm.h>
 
+#include "driver.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);
-
+extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf,
+                              int fsize);
+extern int line6_create_audio_in_urbs(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 int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd);
 
 #endif
index 0b59852..b848db0 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -9,11 +9,10 @@
  *
  */
 
-#include "driver.h"
-
 #include <linux/usb.h>
 
 #include "control.h"
+#include "driver.h"
 #include "pod.h"
 #include "usbdefs.h"
 #include "variax.h"
@@ -45,7 +44,7 @@ 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);
+       int retval = line6_dump_wait_interruptible(&pod->dumpreq);
        if (retval < 0)
                return retval;
        return sprintf(buf, "%d\n", pod->prog_data.control[param]);
@@ -63,7 +62,7 @@ static ssize_t pod_set_param_int(struct device *dev, const char *buf,
        if (retval)
                return retval;
 
-       pod_transmit_parameter(pod, param, value);
+       line6_pod_transmit_parameter(pod, param, value);
        return count;
 }
 
@@ -71,7 +70,7 @@ 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);
+       int retval = line6_dump_wait_interruptible(&variax->dumpreq);
        if (retval < 0)
                return retval;
        return sprintf(buf, "%d\n", variax->model_data.control[param]);
@@ -80,12 +79,11 @@ static ssize_t variax_get_param_int(struct device *dev, char *buf, int 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)
+          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;
@@ -97,7 +95,7 @@ static ssize_t variax_get_param_float(struct device *dev, char *buf, int param)
        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);
+       int retval = line6_dump_wait_interruptible(&variax->dumpreq);
        if (retval < 0)
                return retval;
 
@@ -530,7 +528,7 @@ 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 pod_create_files(int firmware, int type, struct device *dev)
+int line6_pod_create_files(int firmware, int type, struct device *dev)
 {
        int err;
        CHECK_RETURN(device_create_file(dev, &dev_attr_tweak));
@@ -733,9 +731,10 @@ int pod_create_files(int firmware, int type, struct device *dev)
                                     (dev, &dev_attr_band_6_gain__bass));
        return 0;
 }
-EXPORT_SYMBOL(pod_create_files);
 
-void pod_remove_files(int firmware, int type, struct device *dev)
+EXPORT_SYMBOL(line6_pod_create_files);
+
+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);
@@ -908,9 +907,10 @@ void pod_remove_files(int firmware, int type, struct device *dev)
                if (firmware >= 200)
                        device_remove_file(dev, &dev_attr_band_6_gain__bass);
 }
-EXPORT_SYMBOL(pod_remove_files);
 
-int variax_create_files(int firmware, int type, struct device *dev)
+EXPORT_SYMBOL(line6_pod_remove_files);
+
+int line6_variax_create_files(int firmware, int type, struct device *dev)
 {
        int err;
        CHECK_RETURN(device_create_file(dev, &dev_attr_body));
@@ -954,9 +954,10 @@ int variax_create_files(int firmware, int type, struct device *dev)
        CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_wiring));
        return 0;
 }
-EXPORT_SYMBOL(variax_create_files);
 
-void variax_remove_files(int firmware, int type, struct device *dev)
+EXPORT_SYMBOL(line6_variax_create_files);
+
+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);
@@ -998,4 +999,5 @@ void variax_remove_files(int firmware, int type, struct device *dev)
        device_remove_file(dev, &dev_attr_mix1);
        device_remove_file(dev, &dev_attr_pickup_wiring);
 }
-EXPORT_SYMBOL(variax_remove_files);
+
+EXPORT_SYMBOL(line6_variax_remove_files);
index 47e18ab..1c15953 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
 #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,
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_compression_gain               =   5,
-
+       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_amp_model                      =  12,  /* firmware: 2.0 */
        POD_drive                          =  13,
        POD_bass                           =  14,
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_mid                            =  15,
-
-        /* device: LINE6_BITS_BASSPODXTALL */
-       POD_lowmid                         =  15,
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_treble                         =  16,
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_highmid                        =  16,
-
+       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,
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_reverb_mix                     =  18,
-
+       POD_reverb_mix                     =  18,  /* device: LINE6_BITS_PODXTALL */
        POD_effect_setup                   =  19,
        POD_band_1_frequency               =  20,  /* firmware: 2.0 */
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_presence                       =  21,
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_treble__bass                   =  21,
-
+       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,
@@ -70,137 +54,78 @@ enum {
        POD_mod_param_1                    =  29,
        POD_delay_param_1                  =  30,
        POD_delay_param_1_note_value       =  31,
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_band_2_frequency__bass         =  32,  /* firmware: 2.0 */
-
+       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,
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_reverb_enable                  =  36,
-       POD_reverb_type                    =  37,
-       POD_reverb_decay                   =  38,
-       POD_reverb_tone                    =  39,
-       POD_reverb_pre_delay               =  40,
-       POD_reverb_pre_post                =  41,
-       POD_band_2_frequency               =  42,
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_band_3_frequency__bass         =  42,  /* firmware: 2.0 */
-
+       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,
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_modulation_lo_cut              =  44,
-       POD_delay_reverb_lo_cut            =  45,
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_volume_pedal_minimum           =  46,  /* firmware: 2.0 */
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_eq_pre_post                    =  46,  /* firmware: 2.0 */
-
+       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,
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_di_model                       =  48,
-       POD_di_delay                       =  49,
-
+       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,
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_mod_param_5                    =  55,
-
+       POD_mod_param_5                    =  55,  /* device: LINE6_BITS_BASSPODXTALL */
        POD_mod_volume_mix                 =  56,
        POD_mod_pre_post                   =  57,
        POD_modulation_model               =  58,
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_band_3_frequency               =  60,  /* firmware: 2.0 */
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_band_4_frequency__bass         =  60,  /* firmware: 2.0 */
-
+       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,
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_band_5_frequency               =  68,  /* firmware: 2.0 */
-
+       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,
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_band_4_frequency               =  77,  /* firmware: 2.0 */
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_band_6_frequency               =  77,  /* firmware: 2.0 */
-
+       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,
-
-       /* device: LINE6_BITS_LIVE */
-       POD_amp_switch_select              =  84,
-
+       POD_amp_switch_select              =  84,  /* device: LINE6_BITS_LIVE */
        POD_delay_param_4                  =  85,
        POD_delay_param_5                  =  86,
        POD_delay_pre_post                 =  87,
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_delay_model                    =  88,
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_delay_verb_model               =  88,
-
+       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 */
-
-       /* device: LINE6_BITS_PRO */
-       POD_fx_loop_on_off                 = 107,
-
+       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 */
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_band_2_gain__bass              = 115,  /* firmware: 2.0 */
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_band_2_gain                    = 116,  /* firmware: 2.0 */
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_band_3_gain__bass              = 116,  /* firmware: 2.0 */
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_band_3_gain                    = 117,  /* firmware: 2.0 */
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_band_4_gain__bass              = 117,  /* firmware: 2.0 */
-       POD_band_5_gain__bass              = 118,  /* firmware: 2.0 */
-
-       /* device: LINE6_BITS_PODXTALL */
-       POD_band_4_gain                    = 119,  /* firmware: 2.0 */
-
-       /* device: LINE6_BITS_BASSPODXTALL */
-       POD_band_6_gain__bass              = 119   /* 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 */
 };
 
 /**
@@ -218,8 +143,7 @@ enum {
        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_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 */
@@ -258,10 +182,10 @@ enum {
 };
 
 
-extern int pod_create_files(int firmware, int type, struct device *dev);
-extern void pod_remove_files(int firmware, int type, struct device *dev);
-extern int variax_create_files(int firmware, int type, struct device *dev);
-extern void variax_remove_files(int firmware, int type, struct device *dev);
+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
index 27b986a..62a9da5 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -9,8 +9,6 @@
  *
  */
 
-#include "driver.h"
-
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -19,6 +17,7 @@
 #include "audio.h"
 #include "capture.h"
 #include "control.h"
+#include "driver.h"
 #include "midi.h"
 #include "playback.h"
 #include "pod.h"
@@ -30,7 +29,7 @@
 
 #define DRIVER_AUTHOR  "Markus Grabner <grabner@icg.tugraz.at>"
 #define DRIVER_DESC    "Line6 USB Driver"
-#define DRIVER_VERSION "0.8.0"
+#define DRIVER_VERSION "0.9.0"
 
 
 /* table of devices that work with this driver */
@@ -40,6 +39,9 @@ static const struct usb_device_id line6_id_table[] = {
        { 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_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) },
@@ -54,30 +56,48 @@ static const struct usb_device_id line6_id_table[] = {
 MODULE_DEVICE_TABLE(usb, line6_id_table);
 
 static struct line6_properties line6_properties_table[] = {
-       { "BassPODxt",        LINE6_BIT_BASSPODXT,     LINE6_BIT_CONTROL_PCM },
-       { "BassPODxt Live",   LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM },
-       { "BassPODxt Pro",    LINE6_BIT_BASSPODXTPRO,  LINE6_BIT_CONTROL_PCM },
-       { "GuitarPort",       LINE6_BIT_GUITARPORT,    LINE6_BIT_PCM         },
-       { "Pocket POD",       LINE6_BIT_POCKETPOD,     LINE6_BIT_CONTROL_PCM },
-       { "POD X3",           LINE6_BIT_PODX3,         LINE6_BIT_PCM         },
-       { "POD X3 Live",      LINE6_BIT_PODX3LIVE,     LINE6_BIT_PCM         },
-       { "PODxt",            LINE6_BIT_PODXT,         LINE6_BIT_CONTROL_PCM },
-       { "PODxt Live",       LINE6_BIT_PODXTLIVE,     LINE6_BIT_CONTROL_PCM },
-       { "PODxt Pro",        LINE6_BIT_PODXTPRO,      LINE6_BIT_CONTROL_PCM },
-       { "TonePort GX",      LINE6_BIT_TONEPORT_GX,   LINE6_BIT_PCM         },
-       { "TonePort UX1",     LINE6_BIT_TONEPORT_UX1,  LINE6_BIT_PCM         },
-       { "TonePort UX2",     LINE6_BIT_TONEPORT_UX2,  LINE6_BIT_PCM         },
-       { "Variax Workbench", LINE6_BIT_VARIAX,        LINE6_BIT_CONTROL     }
+       { "BassPODxt",     "BassPODxt",        LINE6_BIT_BASSPODXT,     LINE6_BIT_CONTROL_PCM_HWMON },
+       { "BassPODxtLive", "BassPODxt Live",   LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM_HWMON },
+       { "BassPODxtPro",  "BassPODxt Pro",    LINE6_BIT_BASSPODXTPRO,  LINE6_BIT_CONTROL_PCM_HWMON },
+       { "GuitarPort",    "GuitarPort",       LINE6_BIT_GUITARPORT,    LINE6_BIT_PCM               },
+       { "PocketPOD",     "Pocket POD",       LINE6_BIT_POCKETPOD,     LINE6_BIT_CONTROL           },
+       { "PODStudioGX",   "POD Studio GX",    LINE6_BIT_PODSTUDIO_GX,  LINE6_BIT_PCM               },
+       { "PODStudioUX1",  "POD Studio UX1",   LINE6_BIT_PODSTUDIO_UX1, LINE6_BIT_PCM               },
+       { "PODStudioUX2",  "POD Studio UX2",   LINE6_BIT_PODSTUDIO_UX2, LINE6_BIT_PCM               },
+       { "PODX3",         "POD X3",           LINE6_BIT_PODX3,         LINE6_BIT_PCM               },
+       { "PODX3Live",     "POD X3 Live",      LINE6_BIT_PODX3LIVE,     LINE6_BIT_PCM               },
+       { "PODxt",         "PODxt",            LINE6_BIT_PODXT,         LINE6_BIT_CONTROL_PCM_HWMON },
+       { "PODxtLive",     "PODxt Live",       LINE6_BIT_PODXTLIVE,     LINE6_BIT_CONTROL_PCM_HWMON },
+       { "PODxtPro",      "PODxt Pro",        LINE6_BIT_PODXTPRO,      LINE6_BIT_CONTROL_PCM_HWMON },
+       { "TonePortGX",    "TonePort GX",      LINE6_BIT_TONEPORT_GX,   LINE6_BIT_PCM               },
+       { "TonePortUX1",   "TonePort UX1",     LINE6_BIT_TONEPORT_UX1,  LINE6_BIT_PCM               },
+       { "TonePortUX2",   "TonePort UX2",     LINE6_BIT_TONEPORT_UX2,  LINE6_BIT_PCM               },
+       { "Variax",        "Variax Workbench", LINE6_BIT_VARIAX,        LINE6_BIT_CONTROL           }
 };
 
 
 /*
        This is Line6's MIDI manufacturer ID.
 */
-const unsigned char line6_midi_id[] = { 0x00, 0x01, 0x0c };
+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];
-struct workqueue_struct *line6_workqueue;
 
 
 /**
@@ -104,15 +124,25 @@ static int line6_send_raw_message_async_part(struct message *msg,
 */
 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;
-       return usb_submit_urb(line6->urb_listen, GFP_KERNEL);
+       err = usb_submit_urb(line6->urb_listen, GFP_KERNEL);
+       return err;
+}
+
+/*
+       Stop listening on endpoint.
+*/
+static void line6_stop_listen(struct usb_line6 *line6)
+{
+       usb_kill_urb(line6->urb_listen);
 }
 
-#if DO_DUMP_ANY
+#ifdef CONFIG_LINE6_USB_DUMP_ANY
 /*
        Write hexdump to syslog.
 */
@@ -152,7 +182,7 @@ void line6_write_hexdump(struct usb_line6 *line6, char dir,
 }
 #endif
 
-#if DO_DUMP_URB_RECEIVE
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
 /*
        Dump URB data to syslog.
 */
@@ -169,19 +199,19 @@ static void line6_dump_urb(struct urb *urb)
 #endif
 
 /*
-       Send raw message in pieces of max_packet_size bytes.
+       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;
-       int actual_size;
 
-#if DO_DUMP_URB_SEND
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
        line6_write_hexdump(line6, 'S', buffer, size);
 #endif
 
-       for (i = 0; i < size; i += actual_size) {
+       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;
@@ -190,7 +220,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
                                           usb_sndintpipe(line6->usbdev,
                                                          line6->ep_control_write),
                                           (char *)frag_buf, frag_size,
-                                          &actual_size, LINE6_TIMEOUT * HZ);
+                                          &partial, LINE6_TIMEOUT * HZ);
 
                if (retval) {
                        dev_err(line6->ifcdev,
@@ -198,7 +228,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
                        break;
                }
 
-               done += actual_size;
+               done += frag_size;
        }
 
        return done;
@@ -234,7 +264,7 @@ static int line6_send_raw_message_async_part(struct message *msg,
                         (char *)msg->buffer + done, bytes,
                         line6_async_request_sent, msg, line6->interval);
 
-#if DO_DUMP_URB_SEND
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
        line6_write_hexdump(line6, 'S', (char *)msg->buffer + done, bytes);
 #endif
 
@@ -252,6 +282,17 @@ static int line6_send_raw_message_async_part(struct message *msg,
        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.
 */
@@ -288,6 +329,14 @@ int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer,
        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.
 */
@@ -297,6 +346,15 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer,
        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
@@ -305,7 +363,7 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer,
 char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2,
                               int size)
 {
-       char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_KERNEL);
+       char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC);
 
        if (!buffer) {
                dev_err(line6->ifcdev, "out of memory\n");
@@ -332,29 +390,29 @@ static void line6_data_received(struct urb *urb)
        if (urb->status == -ESHUTDOWN)
                return;
 
-#if DO_DUMP_URB_RECEIVE
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
        line6_dump_urb(urb);
 #endif
 
-       done = midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
+       done = line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
 
        if (done < urb->actual_length) {
-               midibuf_ignore(mb, done);
+               line6_midibuf_ignore(mb, done);
                DEBUG_MESSAGES(dev_err(line6->ifcdev, "%d %d buffer overflow - message skipped\n", done, urb->actual_length));
        }
 
        for (;;) {
-               done = midibuf_read(mb, line6->buffer_message, LINE6_MESSAGE_MAXLEN);
+               done = line6_midibuf_read(mb, line6->buffer_message, LINE6_MESSAGE_MAXLEN);
 
                if (done == 0)
                        break;
 
                /* MIDI input filter */
-               if (midibuf_skip_message(mb, line6->line6midi->midi_mask_receive))
+               if (line6_midibuf_skip_message(mb, line6->line6midi->midi_mask_receive))
                        continue;
 
                line6->message_length = done;
-#if DO_DUMP_MIDI_RECEIVE
+#ifdef CONFIG_LINE6_USB_DUMP_MIDI
                line6_write_hexdump(line6, 'r', line6->buffer_message, done);
 #endif
                line6_midi_receive(line6, line6->buffer_message, done);
@@ -366,17 +424,17 @@ static void line6_data_received(struct urb *urb)
                case LINE6_DEVID_PODXT:
                case LINE6_DEVID_PODXTPRO:
                case LINE6_DEVID_POCKETPOD:
-                       pod_process_message((struct usb_line6_pod *)line6);
+                       line6_pod_process_message((struct usb_line6_pod *)line6);
                        break;
 
                case LINE6_DEVID_PODXTLIVE:
                        switch (line6->interface_number) {
                        case PODXTLIVE_INTERFACE_POD:
-                               pod_process_message((struct usb_line6_pod *)line6);
+                               line6_pod_process_message((struct usb_line6_pod *)line6);
                                break;
 
                        case PODXTLIVE_INTERFACE_VARIAX:
-                               variax_process_message((struct usb_line6_variax *)line6);
+                               line6_variax_process_message((struct usb_line6_variax *)line6);
                                break;
 
                        default:
@@ -385,7 +443,7 @@ static void line6_data_received(struct urb *urb)
                        break;
 
                case LINE6_DEVID_VARIAX:
-                       variax_process_message((struct usb_line6_variax *)line6);
+                       line6_variax_process_message((struct usb_line6_variax *)line6);
                        break;
 
                default:
@@ -396,44 +454,17 @@ static void line6_data_received(struct urb *urb)
        line6_start_listen(line6);
 }
 
-static int line6_send(struct usb_line6 *line6, unsigned char *buf, size_t len)
-{
-       int retval;
-       int partial;
-
-#if DO_DUMP_URB_SEND
-       line6_write_hexdump(line6, 'S', buf, len);
-#endif
-
-       retval = usb_interrupt_msg(line6->usbdev,
-                                  usb_sndintpipe(line6->usbdev,
-                                                 line6->ep_control_write),
-                                  buf, len, &partial,
-                                  LINE6_TIMEOUT * HZ);
-
-       if (retval) {
-               dev_err(line6->ifcdev,
-                       "usb_interrupt_msg failed (%d)\n", retval);
-       }
-
-       if (partial != len) {
-               dev_err(line6->ifcdev,
-                       "usb_interrupt_msg sent partial message (%d)\n",
-                        retval);
-       }
-
-       return retval;
-}
-
 /*
        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;
-       size_t len = 2;
+       int partial;
+
+       buffer = kmalloc(2, GFP_KERNEL);
 
-       buffer = kmalloc(len, GFP_KERNEL);
        if (!buffer) {
                dev_err(line6->ifcdev, "out of memory\n");
                return -ENOMEM;
@@ -442,7 +473,20 @@ int line6_send_program(struct usb_line6 *line6, int value)
        buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST;
        buffer[1] = value;
 
-       return line6_send(line6, buffer, len);
+#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;
 }
 
 /*
@@ -450,10 +494,12 @@ int line6_send_program(struct usb_line6 *line6, int value)
 */
 int line6_transmit_parameter(struct usb_line6 *line6, int param, int value)
 {
+       int retval;
        unsigned char *buffer;
-       size_t len = 3;
+       int partial;
+
+       buffer = kmalloc(3, GFP_KERNEL);
 
-       buffer = kmalloc(len, GFP_KERNEL);
        if (!buffer) {
                dev_err(line6->ifcdev, "out of memory\n");
                return -ENOMEM;
@@ -463,7 +509,19 @@ int line6_transmit_parameter(struct usb_line6 *line6, int param, int value)
        buffer[1] = param;
        buffer[2] = value;
 
-       return line6_send(line6, buffer, len);
+#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;
 }
 
 /*
@@ -477,10 +535,9 @@ int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t dat
 
        /* 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);
+                             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);
@@ -599,7 +656,7 @@ ssize_t line6_nop_write(struct device *dev, struct device_attribute *attr,
 /*
        "write" request on "raw" special file.
 */
-#if CREATE_RAW_FILE
+#ifdef CONFIG_LINE6_USB_RAW
 ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr,
                      const char *buf, size_t count)
 {
@@ -637,21 +694,6 @@ static void line6_destruct(struct usb_interface *interface)
        kfree(line6);
 }
 
-static void line6_list_devices(void)
-{
-       int i;
-
-       for (i = 0; i < LINE6_MAX_DEVICES; ++i) {
-               struct usb_line6 *dev = line6_devices[i];
-               printk(KERN_INFO "Line6 device %d: ", i);
-
-               if (dev == NULL)
-                       printk("(not used)\n");
-               else
-                       printk("%s:%d\n", dev->properties->name, dev->interface_number);
-       }
-}
-
 /*
        Probe USB device.
 */
@@ -674,10 +716,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
        if (usbdev == NULL)
                return -ENODEV;
 
-       /* increment reference counters: */
-       usb_get_intf(interface);
-       usb_get_dev(usbdev);
-
        /* we don't handle multiple configurations */
        if (usbdev->descriptor.bNumConfigurations != 1) {
                ret = -ENODEV;
@@ -689,8 +727,8 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
                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)
+               if (idVendor == line6_id_table[devtype].idVendor &&
+                   idProduct == line6_id_table[devtype].idProduct)
                        break;
        }
 
@@ -719,12 +757,23 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
 
        switch (product) {
        case LINE6_DEVID_BASSPODXTLIVE:
-       case LINE6_DEVID_POCKETPOD:
        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_PODX3:
        case LINE6_DEVID_PODX3LIVE:
                switch (interface_number) {
@@ -746,21 +795,27 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
                alternate = 5;
                break;
 
-       case LINE6_DEVID_TONEPORT_GX:
        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_UX1:
        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:
-                       alternate = 0;
+                       /* don't know yet what this is ...
+                       alternate = 1;
                        break;
+                       */
+                       return -ENODEV;
                default:
                        MISSING_CASE;
                }
@@ -783,7 +838,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
        case LINE6_DEVID_BASSPODXT:
        case LINE6_DEVID_BASSPODXTLIVE:
        case LINE6_DEVID_BASSPODXTPRO:
-       case LINE6_DEVID_POCKETPOD:
        case LINE6_DEVID_PODXT:
        case LINE6_DEVID_PODXTPRO:
                size = sizeof(struct usb_line6_pod);
@@ -791,6 +845,12 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
                ep_write = 0x03;
                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! */
@@ -799,6 +859,9 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
                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:
@@ -925,17 +988,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
        case LINE6_DEVID_PODX3LIVE:
        case LINE6_DEVID_PODXT:
        case LINE6_DEVID_PODXTPRO:
-               ret = pod_init(interface, (struct usb_line6_pod *)line6);
+               ret = line6_pod_init(interface, (struct usb_line6_pod *)line6);
                break;
 
        case LINE6_DEVID_PODXTLIVE:
                switch (interface_number) {
                case PODXTLIVE_INTERFACE_POD:
-                       ret = pod_init(interface, (struct usb_line6_pod *)line6);
+                       ret = line6_pod_init(interface, (struct usb_line6_pod *)line6);
                        break;
 
                case PODXTLIVE_INTERFACE_VARIAX:
-                       ret = variax_init(interface, (struct usb_line6_variax *)line6);
+                       ret = line6_variax_init(interface, (struct usb_line6_variax *)line6);
                        break;
 
                default:
@@ -948,14 +1011,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
                break;
 
        case LINE6_DEVID_VARIAX:
-               ret = variax_init(interface, (struct usb_line6_variax *)line6);
+               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 = toneport_init(interface, (struct usb_line6_toneport *)line6);
+               ret = line6_toneport_init(interface, (struct usb_line6_toneport *)line6);
                break;
 
        default:
@@ -971,10 +1037,23 @@ static int line6_probe(struct usb_interface *interface, const struct 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;
-       line6_list_devices();
+
+       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:
@@ -1000,6 +1079,8 @@ static void line6_disconnect(struct usb_interface *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;
@@ -1007,7 +1088,7 @@ static void line6_disconnect(struct usb_interface *interface)
 
        if (line6 != NULL) {
                if (line6->urb_listen != NULL)
-                       usb_kill_urb(line6->urb_listen);
+                       line6_stop_listen(line6);
 
                if (usbdev != line6->usbdev)
                        dev_err(line6->ifcdev,
@@ -1022,31 +1103,34 @@ static void line6_disconnect(struct usb_interface *interface)
                case LINE6_DEVID_PODX3LIVE:
                case LINE6_DEVID_PODXT:
                case LINE6_DEVID_PODXTPRO:
-                       pod_disconnect(interface);
+                       line6_pod_disconnect(interface);
                        break;
 
                case LINE6_DEVID_PODXTLIVE:
                        switch (interface_number) {
                        case PODXTLIVE_INTERFACE_POD:
-                               pod_disconnect(interface);
+                               line6_pod_disconnect(interface);
                                break;
 
                        case PODXTLIVE_INTERFACE_VARIAX:
-                               variax_disconnect(interface);
+                               line6_variax_disconnect(interface);
                                break;
                        }
 
                        break;
 
                case LINE6_DEVID_VARIAX:
-                       variax_disconnect(interface);
+                       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:
-                       toneport_disconnect(interface);
+                       line6_toneport_disconnect(interface);
                        break;
 
                default:
@@ -1055,10 +1139,9 @@ static void line6_disconnect(struct usb_interface *interface)
 
                dev_info(&interface->dev, "Line6 %s now disconnected\n", line6->properties->name);
 
-               for (i = LINE6_MAX_DEVICES; i--;) {
+               for (i = LINE6_MAX_DEVICES; i--;)
                        if (line6_devices[i] == line6)
                                line6_devices[i] = NULL;
-               }
        }
 
        line6_destruct(interface);
@@ -1066,14 +1149,78 @@ static void line6_disconnect(struct usb_interface *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);
 
-       line6_list_devices();
+       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,
 };
 
@@ -1086,20 +1233,27 @@ static int __init line6_init(void)
 
        printk(KERN_INFO "%s driver version %s%s\n",
               DRIVER_NAME, DRIVER_VERSION, DRIVER_REVISION);
-       line6_workqueue = create_workqueue(DRIVER_NAME);
-
-       if (line6_workqueue == NULL) {
-               err("couldn't create workqueue");
-               return -EINVAL;
-       }
 
        for (i = LINE6_MAX_DEVICES; i--;)
                line6_devices[i] = NULL;
 
        retval = usb_register(&line6_driver);
 
-       if (retval)
+       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;
 }
@@ -1109,7 +1263,7 @@ static int __init line6_init(void)
 */
 static void __exit line6_exit(void)
 {
-       destroy_workqueue(line6_workqueue);
+       kfree(line6_request_version);
        usb_deregister(&line6_driver);
 }
 
index 9908bfa..b1e5557 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
 #define DRIVER_H
 
 
-#include "config.h"
-
 #include <linux/spinlock.h>
 #include <linux/usb.h>
-#include <linux/wait.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_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", \
@@ -67,10 +74,14 @@ do {                                \
                return err;     \
 } while (0)
 
+#define CHECK_STARTUP_PROGRESS(x, n)      \
+       if((x) >= (n))                     \
+               return;                    \
+       x = (n);
+
 
 extern const unsigned char line6_midi_id[3];
 extern struct usb_line6 *line6_devices[LINE6_MAX_DEVICES];
-extern struct workqueue_struct *line6_workqueue;
 
 static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3;
 static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4;
@@ -80,8 +91,27 @@ static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4;
         Common properties of Line6 devices.
 */
 struct line6_properties {
+       /**
+                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 identifying this device in the line6usb driver.
+       */
        int device_bit;
+
+       /**
+                Bit vector defining this device's capabilities in the
+                line6usb driver.
+       */
        int capabilities;
 };
 
@@ -191,14 +221,22 @@ 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
index cd468c3..891a697 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -9,10 +9,9 @@
  *
  */
 
-#include "driver.h"
-
 #include <linux/slab.h>
 
+#include "driver.h"
 #include "dumprequest.h"
 
 
@@ -39,17 +38,17 @@ void line6_invalidate_current(struct line6_dump_request *l6dr)
 void line6_dump_finished(struct line6_dump_request *l6dr)
 {
        l6dr->in_progress = LINE6_DUMP_NONE;
-       wake_up_interruptible(&l6dr->wait);
+       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)
+                            struct usb_line6 *line6, int num, int dest)
 {
        int ret;
-       line6_invalidate_current(l6dr);
+       line6_dump_started(l6dr, dest);
        ret = line6_send_raw_message_async(line6, l6dr->reqbufs[num].buffer,
                                           l6dr->reqbufs[num].length);
 
@@ -60,43 +59,27 @@ int line6_dump_request_async(struct line6_dump_request *l6dr,
 }
 
 /*
-       Send an asynchronous dump request after a given interval.
+       Wait for completion (interruptible).
 */
-void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds,
-                          void (*function)(unsigned long), void *data)
+int line6_dump_wait_interruptible(struct line6_dump_request *l6dr)
 {
-       l6dr->timer.expires = jiffies + seconds * HZ;
-       l6dr->timer.function = function;
-       l6dr->timer.data = (unsigned long)data;
-       add_timer(&l6dr->timer);
+       return wait_event_interruptible(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE);
 }
 
 /*
        Wait for completion.
 */
-int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock)
+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)
 {
-       int retval = 0;
-       DECLARE_WAITQUEUE(wait, current);
-       add_wait_queue(&l6dr->wait, &wait);
-       current->state = TASK_INTERRUPTIBLE;
-
-       while (l6dr->in_progress) {
-               if (nonblock) {
-                       retval = -EAGAIN;
-                       break;
-               }
-
-               if (signal_pending(current)) {
-                       retval = -ERESTARTSYS;
-                       break;
-               } else
-                       schedule();
-       }
-
-       current->state = TASK_RUNNING;
-       remove_wait_queue(&l6dr->wait, &wait);
-       return retval;
+       return wait_event_timeout(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE, timeout);
 }
 
 /*
@@ -123,7 +106,6 @@ int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf,
        if (ret < 0)
                return ret;
        init_waitqueue_head(&l6dr->wait);
-       init_timer(&l6dr->timer);
        return 0;
 }
 
@@ -148,6 +130,4 @@ void line6_dumpreq_destruct(struct line6_dump_request *l6dr)
        if (l6dr->reqbufs[0].buffer == NULL)
                return;
        line6_dumpreq_destructbuf(l6dr, 0);
-       l6dr->ok = 1;
-       del_timer_sync(&l6dr->timer);
 }
index 1975d54..fce2306 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -15,7 +15,6 @@
 
 #include <linux/usb.h>
 #include <linux/wait.h>
-
 #include <sound/core.h>
 
 
@@ -55,16 +54,6 @@ struct line6_dump_request {
        */
        int in_progress;
 
-       /**
-                Timer for delayed dump request.
-       */
-       struct timer_list timer;
-
-       /**
-                Flag if initial dump request has been successful.
-       */
-       char ok;
-
        /**
                 Dump request buffers
        */
@@ -73,7 +62,7 @@ struct line6_dump_request {
 
 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);
+                                   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);
@@ -82,9 +71,10 @@ extern int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf,
 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_startup_delayed(struct line6_dump_request *l6dr, int seconds,
-                                 void (*function)(unsigned long), void *data);
-extern int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock);
+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
index 32b6ca7..3c7baff 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -9,24 +9,18 @@
  *
  */
 
-#include "driver.h"
-
-#include <linux/usb.h>
 #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 USE_MIDIBUF      1
-#define OUTPUT_DUMP_ONLY 0
-
-
 #define line6_rawmidi_substream_midi(substream) \
        ((struct snd_line6_midi *)((substream)->rmidi->private_data))
 
@@ -61,26 +55,26 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
        spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags);
 
        for (;;) {
-               req = min(midibuf_bytes_free(mb), line6->max_packet_size);
+               req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size);
                done = snd_rawmidi_transmit_peek(substream, chunk, req);
 
                if (done == 0)
                        break;
 
-#if DO_DUMP_MIDI_SEND
+#ifdef CONFIG_LINE6_USB_DUMP_MIDI
                line6_write_hexdump(line6, 's', chunk, done);
 #endif
-               midibuf_write(mb, chunk, done);
+               line6_midibuf_write(mb, chunk, done);
                snd_rawmidi_transmit_ack(substream, done);
        }
 
        for (;;) {
-               done = midibuf_read(mb, chunk, line6->max_packet_size);
+               done = line6_midibuf_read(mb, chunk, line6->max_packet_size);
 
                if (done == 0)
                        break;
 
-               if (midibuf_skip_message(mb, line6midi->midi_mask_transmit))
+               if (line6_midibuf_skip_message(mb, line6midi->midi_mask_transmit))
                        continue;
 
                send_midi_async(line6, chunk, done);
@@ -115,7 +109,7 @@ static void midi_sent(struct urb *urb)
        }
 
        if (num == 0)
-               wake_up_interruptible(&line6->line6midi->send_wait);
+               wake_up(&line6->line6midi->send_wait);
 
        spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags);
 }
@@ -139,7 +133,7 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
                return -ENOMEM;
        }
 
-#if DO_DUMP_URB_SEND
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
        line6_write_hexdump(line6, 'S', data, length);
 #endif
 
@@ -176,8 +170,8 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
        case LINE6_DEVID_PODXTLIVE:
        case LINE6_DEVID_PODXTPRO:
        case LINE6_DEVID_POCKETPOD:
-               pod_midi_postprocess((struct usb_line6_pod *)line6, data,
-                                    length);
+               line6_pod_midi_postprocess((struct usb_line6_pod *)line6, data,
+                                          length);
                break;
 
        default:
@@ -215,19 +209,8 @@ static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream,
 static void line6_midi_output_drain(struct snd_rawmidi_substream *substream)
 {
        struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6;
-       wait_queue_head_t *head = &line6->line6midi->send_wait;
-       DECLARE_WAITQUEUE(wait, current);
-       add_wait_queue(head, &wait);
-       current->state = TASK_INTERRUPTIBLE;
-
-       while (line6->line6midi->num_active_send_urbs > 0)
-               if (signal_pending(current))
-                       break;
-               else
-                       schedule();
-
-       current->state = TASK_RUNNING;
-       remove_wait_queue(head, &wait);
+       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)
@@ -284,6 +267,7 @@ static int snd_line6_new_midi(struct snd_line6_midi *line6midi)
 
        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 =
@@ -371,8 +355,8 @@ 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);
-       midibuf_destroy(&line6midi->midibuf_in);
-       midibuf_destroy(&line6midi->midibuf_out);
+       line6_midibuf_destroy(&line6midi->midibuf_in);
+       line6_midibuf_destroy(&line6midi->midibuf_out);
        return 0;
 }
 
@@ -396,11 +380,11 @@ int line6_init_midi(struct usb_line6 *line6)
        if (line6midi == NULL)
                return -ENOMEM;
 
-       err = midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);
+       err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);
        if (err < 0)
                return err;
 
-       err = midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1);
+       err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1);
        if (err < 0)
                return err;
 
index c69fd11..968cf57 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
index ab0a5f3..de8eef7 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -9,8 +9,6 @@
  *
  */
 
-#include "config.h"
-
 #include <linux/slab.h>
 
 #include "midibuf.h"
@@ -25,9 +23,9 @@ static int midibuf_message_length(unsigned char code)
                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.
+                 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 };
@@ -35,13 +33,23 @@ static int midibuf_message_length(unsigned char code)
        }
 }
 
-void midibuf_reset(struct MidiBuffer *this)
+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 midibuf_init(struct MidiBuffer *this, int size, int split)
+int line6_midibuf_init(struct MidiBuffer *this, int size, int split)
 {
        this->buf = kmalloc(size, GFP_KERNEL);
 
@@ -50,28 +58,18 @@ int midibuf_init(struct MidiBuffer *this, int size, int split)
 
        this->size = size;
        this->split = split;
-       midibuf_reset(this);
+       line6_midibuf_reset(this);
        return 0;
 }
 
-void midibuf_status(struct MidiBuffer *this)
+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);
 }
 
-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;
-}
-
-int midibuf_bytes_free(struct MidiBuffer *this)
+int line6_midibuf_bytes_free(struct MidiBuffer *this)
 {
        return
                midibuf_is_full(this) ?
@@ -79,7 +77,7 @@ int midibuf_bytes_free(struct MidiBuffer *this)
                (this->pos_read - this->pos_write + this->size - 1) % this->size + 1;
 }
 
-int midibuf_bytes_used(struct MidiBuffer *this)
+int line6_midibuf_bytes_used(struct MidiBuffer *this)
 {
        return
                midibuf_is_empty(this) ?
@@ -87,7 +85,7 @@ int midibuf_bytes_used(struct MidiBuffer *this)
                (this->pos_write - this->pos_read + this->size - 1) % this->size + 1;
 }
 
-int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
+int line6_midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
 {
        int bytes_free;
        int length1, length2;
@@ -102,7 +100,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
                skip_active_sense = 1;
        }
 
-       bytes_free = midibuf_bytes_free(this);
+       bytes_free = line6_midibuf_bytes_free(this);
 
        if (length > bytes_free)
                length = bytes_free;
@@ -129,7 +127,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
        return length + skip_active_sense;
 }
 
-int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
+int line6_midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
 {
        int bytes_used;
        int length1, length2;
@@ -145,7 +143,7 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
        if (midibuf_is_empty(this))
                return 0;
 
-       bytes_used = midibuf_bytes_used(this);
+       bytes_used = line6_midibuf_bytes_used(this);
 
        if (length > bytes_used)
                length = bytes_used;
@@ -232,9 +230,9 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
        return length + repeat;
 }
 
-int midibuf_ignore(struct MidiBuffer *this, int length)
+int line6_midibuf_ignore(struct MidiBuffer *this, int length)
 {
-       int bytes_used = midibuf_bytes_used(this);
+       int bytes_used = line6_midibuf_bytes_used(this);
 
        if (length > bytes_used)
                length = bytes_used;
@@ -244,7 +242,7 @@ int midibuf_ignore(struct MidiBuffer *this, int length)
        return length;
 }
 
-int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
+int line6_midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
 {
        int cmd = this->command_prev;
 
@@ -255,7 +253,7 @@ int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
        return 0;
 }
 
-void midibuf_destroy(struct MidiBuffer *this)
+void line6_midibuf_destroy(struct MidiBuffer *this)
 {
        kfree(this->buf);
        this->buf = NULL;
index 9877581..c3c86f4 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -23,17 +23,17 @@ struct MidiBuffer {
 };
 
 
-extern int midibuf_bytes_used(struct MidiBuffer *mb);
-extern int midibuf_bytes_free(struct MidiBuffer *mb);
-extern void midibuf_destroy(struct MidiBuffer *mb);
-extern int midibuf_ignore(struct MidiBuffer *mb, int length);
-extern int midibuf_init(struct MidiBuffer *mb, int size, int split);
-extern int midibuf_read(struct MidiBuffer *mb, unsigned char *data, int length);
-extern void midibuf_reset(struct MidiBuffer *mb);
-extern int midibuf_skip_message(struct MidiBuffer *mb, unsigned short mask);
-extern void midibuf_status(struct MidiBuffer *mb);
-extern int midibuf_write(struct MidiBuffer *mb, unsigned char *data,
-                        int length);
+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
index fbe4b08..0eac291 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -9,10 +9,7 @@
  *
  */
 
-#include "driver.h"
-
 #include <linux/slab.h>
-
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/pcm.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_start(line6pcm, MASK_PCM_IMPULSE);
+       else
+               line6_pcm_stop(line6pcm, MASK_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_IWUGO | S_IRUGO, pcm_get_impulse_volume, pcm_set_impulse_volume);
+static DEVICE_ATTR(impulse_period, S_IWUGO | S_IRUGO, pcm_get_impulse_period, pcm_set_impulse_period);
+
+#endif
+
+int line6_pcm_start(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;
+       int err = 0;
+
+#if LINE6_BACKUP_MONITOR_SIGNAL
+       if (!(line6pcm->line6->properties->capabilities & LINE6_BIT_HWMON)) {
+               line6pcm->prev_fbuf = kmalloc(LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL);
+
+               if (!line6pcm->prev_fbuf) {
+                       dev_err(line6pcm->line6->ifcdev, "cannot malloc monitor buffer\n");
+                       return -ENOMEM;
+               }
+       }
+#else
+       line6pcm->prev_fbuf = NULL;
+#endif
+               
+       if (((flags_old & MASK_CAPTURE) == 0) &&
+           ((flags_new & MASK_CAPTURE) != 0)) {
+               /*
+                 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)
+                       return -EBUSY;
+
+               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");
+                       return -ENOMEM;
+               }
+
+               line6pcm->count_in = 0;
+               line6pcm->prev_fsize = 0;
+               err = line6_submit_audio_in_all_urbs(line6pcm);
+               
+               if (err < 0) {
+                       __sync_fetch_and_and(&line6pcm->flags, ~channels);
+                       return err;
+               }
+       }
+       
+       if (((flags_old & MASK_PLAYBACK) == 0) &&
+           ((flags_new & MASK_PLAYBACK) != 0)) {
+               /*
+                 See comment above regarding PCM restart.
+               */
+               if(line6pcm->active_urb_out | line6pcm->unlink_urb_out)
+                       return -EBUSY;
+
+               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");
+                       return -ENOMEM;
+               }
+
+               line6pcm->count_out = 0;
+               err = line6_submit_audio_out_all_urbs(line6pcm);
+               
+               if (err < 0) {
+                       __sync_fetch_and_and(&line6pcm->flags, ~channels);
+                       return err;
+               }
+       }
+       
+       return 0;
+}
+
+int line6_pcm_stop(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 (((flags_old & MASK_CAPTURE) != 0) &&
+           ((flags_new & MASK_CAPTURE) == 0)) {
+               line6_unlink_audio_in_urbs(line6pcm);
+               kfree(line6pcm->buffer_in);
+               line6pcm->buffer_in = NULL;
+       }
+
+       if (((flags_old & MASK_PLAYBACK) != 0) &&
+           ((flags_new & MASK_PLAYBACK) == 0)) {
+               line6_unlink_audio_out_urbs(line6pcm);
+               kfree(line6pcm->buffer_out);
+               line6pcm->buffer_out = NULL;
+       }
+
+#if LINE6_BACKUP_MONITOR_SIGNAL
+       if (line6pcm->prev_fbuf != NULL)
+               kfree(line6pcm->prev_fbuf);
+#endif
+
+       return 0;
+}
+
 /* trigger callback */
 int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
 {
@@ -38,7 +201,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
        snd_pcm_group_for_each_entry(s, substream) {
                switch (s->stream) {
                case SNDRV_PCM_STREAM_PLAYBACK:
-                       err = snd_line6_playback_trigger(s, cmd);
+                       err = snd_line6_playback_trigger(line6pcm, cmd);
 
                        if (err < 0) {
                                spin_unlock_irqrestore(&line6pcm->lock_trigger,
@@ -49,7 +212,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
                        break;
 
                case SNDRV_PCM_STREAM_CAPTURE:
-                       err = snd_line6_capture_trigger(s, cmd);
+                       err = snd_line6_capture_trigger(line6pcm, cmd);
 
                        if (err < 0) {
                                spin_unlock_irqrestore(&line6pcm->lock_trigger,
@@ -60,7 +223,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
                        break;
 
                default:
-                       dev_err(s2m(substream), "Unknown stream direction %d\n",
+                       dev_err(line6pcm->line6->ifcdev, "Unknown stream direction %d\n",
                                s->stream);
                }
        }
@@ -70,8 +233,8 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
 }
 
 /* control info callback */
-static int snd_line6_control_info(struct snd_kcontrol *kcontrol,
-                                 struct snd_ctl_elem_info *uinfo)
+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;
@@ -81,28 +244,28 @@ static int snd_line6_control_info(struct snd_kcontrol *kcontrol,
 }
 
 /* control get callback */
-static int snd_line6_control_get(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_value *ucontrol)
+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[i];
+               ucontrol->value.integer.value[i] = line6pcm->volume_playback[i];
 
        return 0;
 }
 
 /* control put callback */
-static int snd_line6_control_put(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_value *ucontrol)
+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[i] != ucontrol->value.integer.value[i]) {
-                       line6pcm->volume[i] = ucontrol->value.integer.value[i];
+               if (line6pcm->volume_playback[i] != ucontrol->value.integer.value[i]) {
+                       line6pcm->volume_playback[i] = ucontrol->value.integer.value[i];
                        changed = 1;
                }
 
@@ -110,14 +273,14 @@ static int snd_line6_control_put(struct snd_kcontrol *kcontrol,
 }
 
 /* control definition */
-static struct snd_kcontrol_new line6_control = {
+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_info,
-       .get = snd_line6_control_get,
-       .put = snd_line6_control_put
+       .info = snd_line6_control_playback_info,
+       .get = snd_line6_control_playback_get,
+       .put = snd_line6_control_playback_put
 };
 
 /*
@@ -128,6 +291,11 @@ 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]);
@@ -160,7 +328,8 @@ static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm)
        /* 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);
+       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,
@@ -176,6 +345,27 @@ 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.
@@ -218,20 +408,23 @@ int line6_init_pcm(struct usb_line6 *line6,
                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;
 
-       case LINE6_DEVID_TONEPORT_UX1:
-               ep_read  = 0x00;
-               ep_write = 0x00;
-               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;
@@ -242,12 +435,13 @@ int line6_init_pcm(struct usb_line6 *line6,
        if (line6pcm == NULL)
                return -ENOMEM;
 
-       line6pcm->volume[0] = line6pcm->volume[1] = 128;
+       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;
        line6pcm->max_packet_size = usb_maxpacket(line6->usbdev,
-                                                usb_rcvintpipe(line6->usbdev,
+                                                 usb_rcvintpipe(line6->usbdev,
                                                                ep_read),
                                                  0);
        line6pcm->properties = properties;
@@ -268,19 +462,32 @@ int line6_init_pcm(struct usb_line6 *line6,
        spin_lock_init(&line6pcm->lock_audio_in);
        spin_lock_init(&line6pcm->lock_trigger);
 
-       err = create_audio_out_urbs(line6pcm);
+       err = line6_create_audio_out_urbs(line6pcm);
        if (err < 0)
                return err;
 
-       err = create_audio_in_urbs(line6pcm);
+       err = line6_create_audio_in_urbs(line6pcm);
        if (err < 0)
                return err;
 
        /* mixer: */
-       err = snd_ctl_add(line6->card, snd_ctl_new1(&line6_control, line6pcm));
+       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;
 }
 
@@ -290,12 +497,11 @@ int snd_line6_prepare(struct snd_pcm_substream *substream)
        struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
 
        if (!test_and_set_bit(BIT_PREPARED, &line6pcm->flags)) {
-               unlink_wait_clear_audio_out_urbs(line6pcm);
+               line6pcm->count_out = 0;
                line6pcm->pos_out = 0;
                line6pcm->pos_out_done = 0;
-
-               unlink_wait_clear_audio_in_urbs(line6pcm);
                line6pcm->bytes_out = 0;
+               line6pcm->count_in = 0;
                line6pcm->pos_in_done = 0;
                line6pcm->bytes_in = 0;
        }
index 53db217..c9ff95a 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
 
 
 /* number of URBs */
-#define LINE6_ISO_BUFFERS      8
+#define LINE6_ISO_BUFFERS      2
 
-/* number of USB frames per URB */
-#define LINE6_ISO_PACKETS      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
 
-/* this should be queried dynamically from the USB interface! */
-#define LINE6_ISO_PACKET_SIZE_MAX      252
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+#define LINE6_IMPULSE_DEFAULT_PERIOD 100
+#endif
+
+#define LINE6_BACKUP_MONITOR_SIGNAL 0
+#define LINE6_REUSE_DMA_AREA_FOR_PLAYBACK 0
 
 
 /*
-       Extract the messaging device from the substream instance
+       Get substream from Line6 PCM data structure
 */
-#define s2m(s) (((struct snd_line6_pcm *) \
-                  snd_pcm_substream_chip(s))->line6->ifcdev)
+#define get_substream(line6pcm, stream) (line6pcm->pcm->streams[stream].substream)
 
 
+/*
+       PCM mode bits and masks.
+       "ALSA": operations triggered by applications via ALSA
+       "MONITOR": software monitoring
+       "IMPULSE": optional impulse response operation
+*/
 enum {
-       BIT_RUNNING_PLAYBACK,
-       BIT_RUNNING_CAPTURE,
+       /* individual bits: */
+       BIT_PCM_ALSA_PLAYBACK,
+       BIT_PCM_ALSA_CAPTURE,
+       BIT_PCM_MONITOR_PLAYBACK,
+       BIT_PCM_MONITOR_CAPTURE,
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+       BIT_PCM_IMPULSE_PLAYBACK,
+       BIT_PCM_IMPULSE_CAPTURE,
+#endif
        BIT_PAUSE_PLAYBACK,
-       BIT_PREPARED
+       BIT_PREPARED,
+
+       /* individual masks: */
+       MASK_PCM_ALSA_PLAYBACK    = 1 << BIT_PCM_ALSA_PLAYBACK,
+       MASK_PCM_ALSA_CAPTURE     = 1 << BIT_PCM_ALSA_CAPTURE,
+       MASK_PCM_MONITOR_PLAYBACK = 1 << BIT_PCM_MONITOR_PLAYBACK,
+       MASK_PCM_MONITOR_CAPTURE  = 1 << BIT_PCM_MONITOR_CAPTURE,
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+       MASK_PCM_IMPULSE_PLAYBACK = 1 << BIT_PCM_IMPULSE_PLAYBACK,
+       MASK_PCM_IMPULSE_CAPTURE  = 1 << BIT_PCM_IMPULSE_CAPTURE,
+#endif
+       MASK_PAUSE_PLAYBACK       = 1 << BIT_PAUSE_PLAYBACK,
+       MASK_PREPARED             = 1 << BIT_PREPARED,
+
+       /* combined masks (by operation): */
+       MASK_PCM_ALSA    = MASK_PCM_ALSA_PLAYBACK    | MASK_PCM_ALSA_CAPTURE,
+       MASK_PCM_MONITOR = MASK_PCM_MONITOR_PLAYBACK | MASK_PCM_MONITOR_CAPTURE,
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+       MASK_PCM_IMPULSE = MASK_PCM_IMPULSE_PLAYBACK | MASK_PCM_IMPULSE_CAPTURE,
+#endif
+
+       /* combined masks (by direction): */
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+       MASK_PLAYBACK = MASK_PCM_ALSA_PLAYBACK | MASK_PCM_MONITOR_PLAYBACK | MASK_PCM_IMPULSE_PLAYBACK,
+       MASK_CAPTURE  = MASK_PCM_ALSA_CAPTURE  | MASK_PCM_MONITOR_CAPTURE  | MASK_PCM_IMPULSE_CAPTURE
+#else
+       MASK_PLAYBACK = MASK_PCM_ALSA_PLAYBACK | MASK_PCM_MONITOR_PLAYBACK,
+       MASK_CAPTURE  = MASK_PCM_ALSA_CAPTURE  | MASK_PCM_MONITOR_CAPTURE
+#endif
 };
 
 struct line6_pcm_properties {
@@ -83,9 +132,11 @@ struct snd_line6_pcm {
        struct urb *urb_audio_in[LINE6_ISO_BUFFERS];
 
        /**
-                Temporary buffer to hold data when playback buffer wraps.
+                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 *wrap_out;
+       unsigned char *buffer_out;
 
        /**
                 Temporary buffer for capture.
@@ -94,6 +145,21 @@ struct snd_line6_pcm {
        */
        unsigned char *buffer_in;
 
+       /**
+                Temporary buffer index for playback.
+       */
+       int index_out;
+
+       /**
+                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.
        */
@@ -204,12 +270,36 @@ struct snd_line6_pcm {
        /**
                 PCM playback volume (left and right).
        */
-       int volume[2];
+       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 BIT_*).
        */
        unsigned long flags;
+
+       int last_frame_in, last_frame_out;
 };
 
 
@@ -217,6 +307,19 @@ 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_start(struct snd_line6_pcm *line6pcm, int channels);
+extern int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels);
+
+
+#define PRINT_FRAME_DIFF(op) {                                                                                                                                         \
+       static int diff_prev = 1000;                                                                                                                                            \
+       int diff = line6pcm->last_frame_out - line6pcm->last_frame_in;  \
+       if((diff != diff_prev) && (abs(diff) < 100)) {                                                                  \
+               printk("%s frame diff = %d\n", op, diff);                                                                                       \
+               diff_prev = diff;                                                                                                                                                                                       \
+       }                                                                                                                                                                                                                                                               \
+       }
 
 
 #endif
index fbcd6e1..0c8c0c8 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -9,15 +9,13 @@
  *
  */
 
-#include "driver.h"
-
-#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"
@@ -59,22 +57,85 @@ static void change_volume(struct urb *urb_out, int volume[],
        }
 }
 
+#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) {
+               /* TODO: add code for TonePort etc. */
+       } 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_pcm_substream *substream)
+static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 {
        int index;
        unsigned long flags;
        int i, urb_size, urb_frames;
-       struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
        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 snd_pcm_runtime *runtime = substream->runtime;
        struct urb *urb_out;
 
        spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
@@ -83,7 +144,7 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
 
        if (index < 0 || index >= LINE6_ISO_BUFFERS) {
                spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
-               dev_err(s2m(substream), "no free URB found\n");
+               dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
                return -EINVAL;
        }
 
@@ -92,24 +153,49 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
 
        for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
                /* compute frame size for given sampling rate */
-               int n, fs;
+               int fsize = 0;
                struct usb_iso_packet_descriptor *fout =
                    &urb_out->iso_frame_desc[i];
-               line6pcm->count_out += frame_increment;
-               n = line6pcm->count_out / frame_factor;
-               line6pcm->count_out -= n * frame_factor;
-               fs = n * bytes_per_frame;
+
+               if (line6pcm->flags & MASK_CAPTURE) {
+                       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 = fs;
-               urb_size += fs;
+               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 +
+           line6pcm->max_packet_size * line6pcm->index_out;
+       urb_out->transfer_buffer_length = urb_size;
+       urb_out->context = line6pcm;
+
+       if (++line6pcm->index_out == LINE6_ISO_BUFFERS)
+               line6pcm->index_out = 0;
+
+       if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags) &&
+           !test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) {
+               struct snd_pcm_runtime *runtime =
+                   get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime;
 
-       if (test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) {
-               urb_out->transfer_buffer = line6pcm->wrap_out;
-               memset(line6pcm->wrap_out, 0, urb_size);
-       } else {
                if (line6pcm->pos_out + urb_frames > runtime->buffer_size) {
                        /*
                           The transferred area goes over buffer boundary,
@@ -117,38 +203,65 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
                         */
                        int len;
                        len = runtime->buffer_size - line6pcm->pos_out;
-                       urb_out->transfer_buffer = line6pcm->wrap_out;
 
                        if (len > 0) {
-                               memcpy(line6pcm->wrap_out,
+                               memcpy(urb_out->transfer_buffer,
                                       runtime->dma_area +
                                       line6pcm->pos_out * bytes_per_frame,
                                       len * bytes_per_frame);
-                               memcpy(line6pcm->wrap_out +
+                               memcpy(urb_out->transfer_buffer +
                                       len * bytes_per_frame, runtime->dma_area,
                                       (urb_frames - len) * bytes_per_frame);
-                       } else {
-                               /* this is somewhat paranoid */
-                               dev_err(s2m(substream),
-                                       "driver bug: len = %d\n", len);
-                       }
+                       } else
+                               dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", len);        /* this is somewhat paranoid */
                } else {
+#if LINE6_REUSE_DMA_AREA_FOR_PLAYBACK
                        /* set the buffer pointer */
                        urb_out->transfer_buffer =
                            runtime->dma_area +
                            line6pcm->pos_out * bytes_per_frame;
+#else
+                       /* copy data */
+                       memcpy(urb_out->transfer_buffer,
+                              runtime->dma_area +
+                              line6pcm->pos_out * bytes_per_frame,
+                              urb_out->transfer_buffer_length);
+#endif
                }
-       }
 
-       line6pcm->pos_out += urb_frames;
-       if (line6pcm->pos_out >= runtime->buffer_size)
-               line6pcm->pos_out -= runtime->buffer_size;
+               if ((line6pcm->pos_out += urb_frames) >= runtime->buffer_size)
+                       line6pcm->pos_out -= runtime->buffer_size;
+       } else {
+               memset(urb_out->transfer_buffer, 0,
+                      urb_out->transfer_buffer_length);
+       }
 
-       urb_out->transfer_buffer_length = urb_size;
-       urb_out->context = substream;
-       change_volume(urb_out, line6pcm->volume, bytes_per_frame);
+       change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame);
 
-#if DO_DUMP_PCM_SEND
+       if (line6pcm->prev_fbuf != 0) {
+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+               if (line6pcm->flags & MASK_PCM_IMPULSE) {
+                       create_impulse_test_signal(line6pcm, urb_out,
+                                                  bytes_per_frame);
+                       if (line6pcm->flags & MASK_PCM_ALSA_CAPTURE) {
+                               line6_capture_copy(line6pcm, urb_out->transfer_buffer,
+                                                  urb_out->transfer_buffer_length);
+                       }
+               } else {
+#endif
+                       if (!
+                           (line6pcm->line6->properties->
+                            capabilities & LINE6_BIT_HWMON)
+                           && (line6pcm->flags & MASK_PLAYBACK)
+                           && (line6pcm->flags & MASK_CAPTURE))
+                               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];
@@ -161,8 +274,8 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
        if (usb_submit_urb(urb_out, GFP_ATOMIC) == 0)
                set_bit(index, &line6pcm->active_urb_out);
        else
-               dev_err(s2m(substream), "URB out #%d submission failed\n",
-                       index);
+               dev_err(line6pcm->line6->ifcdev,
+                       "URB out #%d submission failed\n", index);
 
        spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
        return 0;
@@ -171,12 +284,12 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
 /*
        Submit all currently available playback URBs.
 */
-static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream)
+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(substream);
+               ret = submit_audio_out_urb(line6pcm);
                if (ret < 0)
                        return ret;
        }
@@ -187,7 +300,7 @@ static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream)
 /*
        Unlink all currently active playback URBs.
 */
-static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
+void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
 {
        unsigned int i;
 
@@ -202,7 +315,7 @@ static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
 }
 
 /*
-   Wait until unlinking of all currently active playback URBs has been finished.
+       Wait until unlinking of all currently active playback URBs has been finished.
 */
 static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
 {
@@ -223,17 +336,14 @@ static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
        } while (--timeout > 0);
        if (alive)
                snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
-
-       line6pcm->active_urb_out = 0;
-       line6pcm->unlink_urb_out = 0;
 }
 
 /*
        Unlink all currently active playback URBs, and wait for finishing.
 */
-void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
+void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
 {
-       unlink_audio_out_urbs(line6pcm);
+       line6_unlink_audio_out_urbs(line6pcm);
        wait_clear_audio_out_urbs(line6pcm);
 }
 
@@ -245,10 +355,16 @@ 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 =
-           (struct snd_pcm_substream *)urb->context;
-       struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
+           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--;)
@@ -262,11 +378,15 @@ static void audio_out_callback(struct urb *urb)
                length += urb->iso_frame_desc[i].length;
 
        spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
-       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;
+       if (test_bit(BIT_PCM_ALSA_PLAYBACK, &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);
 
@@ -276,18 +396,20 @@ static void audio_out_callback(struct urb *urb)
                        break;
                }
 
-       if (test_bit(index, &line6pcm->unlink_urb_out))
+       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(substream);
+               submit_audio_out_urb(line6pcm);
 
-               line6pcm->bytes_out += length;
-               if (line6pcm->bytes_out >= line6pcm->period_out) {
-                       line6pcm->bytes_out -= line6pcm->period_out;
-                       snd_pcm_period_elapsed(substream);
+               if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags)) {
+                       if ((line6pcm->bytes_out +=
+                            length) >= line6pcm->period_out) {
+                               line6pcm->bytes_out %= line6pcm->period_out;
+                               snd_pcm_period_elapsed(substream);
+                       }
                }
        }
 }
@@ -340,52 +462,40 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        line6pcm->period_out = params_period_bytes(hw_params);
-       line6pcm->wrap_out = kmalloc(2 * LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL);
-
-       if (!line6pcm->wrap_out) {
-               dev_err(s2m(substream), "cannot malloc wrap_out\n");
-               return -ENOMEM;
-       }
-
        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);
-       unlink_wait_clear_audio_out_urbs(line6pcm);
-
-       kfree(line6pcm->wrap_out);
-       line6pcm->wrap_out = NULL;
-
        return snd_pcm_lib_free_pages(substream);
 }
 
 /* trigger playback callback */
-int snd_line6_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd)
 {
-       struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
        int err;
-       line6pcm->count_out = 0;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               if (!test_and_set_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) {
-                       err = submit_audio_out_all_urbs(substream);
+#ifdef CONFIG_PM
+       case SNDRV_PCM_TRIGGER_RESUME:
+#endif
+               err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_PLAYBACK);
 
-                       if (err < 0) {
-                               clear_bit(BIT_RUNNING_PLAYBACK,
-                                         &line6pcm->flags);
-                               return err;
-                       }
-               }
+               if (err < 0)
+                       return err;
 
                break;
 
        case SNDRV_PCM_TRIGGER_STOP:
-               if (test_and_clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags))
-                       unlink_audio_out_urbs(line6pcm);
+#ifdef CONFIG_PM
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+#endif
+               err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_PLAYBACK);
+
+               if (err < 0)
+                       return err;
 
                break;
 
@@ -414,17 +524,17 @@ snd_line6_playback_pointer(struct snd_pcm_substream *substream)
 
 /* 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,
+       .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 create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
+int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
 {
        int i;
 
index db1e48b..8b8b974 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
 #define PLAYBACK_H
 
 
-#include "driver.h"
-
 #include <sound/pcm.h>
 
+#include "driver.h"
 
-extern struct snd_pcm_ops snd_line6_playback_ops;
+
+/*
+  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 int create_audio_out_urbs(struct snd_line6_pcm *line6pcm);
-extern int snd_line6_playback_trigger(struct snd_pcm_substream *substream,
-                                     int cmd);
-extern void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm);
+extern struct snd_pcm_ops snd_line6_playback_ops;
 
+extern int line6_create_audio_out_urbs(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 int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd);
 
 #endif
index 28f5146..3aa6f6c 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -9,13 +9,14 @@
  *
  */
 
-#include "driver.h"
-
 #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"
 
@@ -45,7 +46,7 @@ enum {
        POD_tuner_freq     = 0x15,
        POD_tuner_note     = 0x16,
        POD_tuner_pitch    = 0x17,
-       POD_system_invalid = 0x7fff
+       POD_system_invalid = 0x10000
 };
 
 enum {
@@ -69,13 +70,16 @@ static struct snd_ratden pod_ratden = {
 };
 
 static struct line6_pcm_properties pod_pcm_properties = {
-  .snd_line6_playback_hw = {
+       .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 |
-                                                SNDRV_PCM_INFO_SYNC_START),
+                        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,
@@ -83,17 +87,20 @@ static struct line6_pcm_properties pod_pcm_properties = {
                .channels_min =     2,
                .channels_max =     2,
                .buffer_bytes_max = 60000,
-               .period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME,  /* at least one URB must fit into one period */
+               .period_bytes_min = 64,
                .period_bytes_max = 8192,
                .periods_min =      1,
                .periods_max =      1024
        },
-  .snd_line6_capture_hw = {
+       .snd_line6_capture_hw = {
                .info = (SNDRV_PCM_INFO_MMAP |
-                                                SNDRV_PCM_INFO_INTERLEAVED |
-                                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
-                                                SNDRV_PCM_INFO_MMAP_VALID |
-                                                SNDRV_PCM_INFO_SYNC_START),
+                        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,
@@ -101,7 +108,7 @@ static struct line6_pcm_properties pod_pcm_properties = {
                .channels_min =     2,
                .channels_max =     2,
                .buffer_bytes_max = 60000,
-               .period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME,  /* at least one URB must fit into one period */
+               .period_bytes_min = 64,
                .period_bytes_max = 8192,
                .periods_min =      1,
                .periods_max =      1024
@@ -113,9 +120,19 @@ static struct line6_pcm_properties pod_pcm_properties = {
        .bytes_per_frame = POD_BYTES_PER_FRAME
 };
 
-static const char pod_request_version[] = { 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 };
-static const char pod_request_channel[] = { 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7 };
-static const char pod_version_header[]  = { 0xf2, 0x7e, 0x7f, 0x06, 0x02 };
+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);
 
 
 /*
@@ -129,60 +146,6 @@ static void pod_mark_batch_all_dirty(struct usb_line6_pod *pod)
                set_bit(i, pod->param_dirty);
 }
 
-/*
-       Send an asynchronous request for the POD firmware version and device ID.
-*/
-static int pod_version_request_async(struct usb_line6_pod *pod)
-{
-       return line6_send_raw_message_async(&pod->line6, pod->buffer_versionreq, sizeof(pod_request_version));
-}
-
-static void pod_create_files_work(struct work_struct *work)
-{
-       struct usb_line6_pod *pod = container_of(work, struct usb_line6_pod, create_files_work);
-
-       pod_create_files(pod->firmware_version, pod->line6.properties->device_bit, pod->line6.ifcdev);
-}
-
-static void pod_startup_timeout(unsigned long arg)
-{
-       enum {
-               REQUEST_NONE,
-               REQUEST_DUMP,
-               REQUEST_VERSION
-       };
-
-       int request = REQUEST_NONE;
-       struct usb_line6_pod *pod = (struct usb_line6_pod *)arg;
-
-       if (pod->dumpreq.ok) {
-               if (!pod->versionreq_ok)
-                       request = REQUEST_VERSION;
-       } else {
-               if (pod->versionreq_ok)
-                       request = REQUEST_DUMP;
-               else if (pod->startup_count++ & 1)
-                       request = REQUEST_DUMP;
-               else
-                       request = REQUEST_VERSION;
-       }
-
-       switch (request) {
-       case REQUEST_DUMP:
-               line6_dump_request_async(&pod->dumpreq, &pod->line6, 0);
-               break;
-
-       case REQUEST_VERSION:
-               pod_version_request_async(pod);
-               break;
-
-       default:
-               return;
-       }
-
-       line6_startup_delayed(&pod->dumpreq, 1, pod_startup_timeout, pod);
-}
-
 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);
@@ -218,7 +181,7 @@ static void pod_store_parameter(struct usb_line6_pod *pod, int param, int value)
 }
 
 /*
-       Handle SAVE button
+       Handle SAVE button.
 */
 static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int index)
 {
@@ -229,7 +192,7 @@ static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int ind
 /*
        Process a completely received message.
 */
-void pod_process_message(struct usb_line6_pod *pod)
+void line6_pod_process_message(struct usb_line6_pod *pod)
 {
        const unsigned char *buf = pod->line6.buffer_message;
 
@@ -254,7 +217,7 @@ void pod_process_message(struct usb_line6_pod *pod)
                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_request_async(&pod->dumpreq, &pod->line6, 0, LINE6_DUMP_CURRENT);
 
                break;
 
@@ -263,7 +226,7 @@ void pod_process_message(struct usb_line6_pod *pod)
                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_request_async(&pod->dumpreq, &pod->line6, 0, LINE6_DUMP_CURRENT);
                break;
 
        case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE:
@@ -276,7 +239,6 @@ void pod_process_message(struct usb_line6_pod *pod)
                                        case LINE6_DUMP_CURRENT:
                                                memcpy(&pod->prog_data, buf + 7, sizeof(pod->prog_data));
                                                pod_mark_batch_all_dirty(pod);
-                                               pod->dumpreq.ok = 1;
                                                break;
 
                                        case POD_DUMP_MEMORY:
@@ -288,6 +250,7 @@ void pod_process_message(struct usb_line6_pod *pod)
                                        }
 
                                        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));
@@ -300,7 +263,7 @@ void pod_process_message(struct usb_line6_pod *pod)
 #define PROCESS_SYSTEM_PARAM(x) \
                                        case POD_ ## x: \
                                                pod->x.value = value; \
-                                               wake_up_interruptible(&pod->x.wait); \
+                                               wake_up(&pod->x.wait); \
                                                break;
 
                                switch (buf[6]) {
@@ -331,7 +294,7 @@ void pod_process_message(struct usb_line6_pod *pod)
                        case POD_SYSEX_CLIP:
                                DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "audio clipped\n"));
                                pod->clipping.value = 1;
-                               wake_up_interruptible(&pod->clipping.wait);
+                               wake_up(&pod->clipping.wait);
                                break;
 
                        case POD_SYSEX_STORE:
@@ -342,17 +305,9 @@ void pod_process_message(struct usb_line6_pod *pod)
                                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) {
-                       if (pod->versionreq_ok == 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->versionreq_ok = 1;
-
-                               /* Now we know the firmware version, so we schedule a bottom half
-                                        handler to create the special files: */
-                               INIT_WORK(&pod->create_files_work, pod_create_files_work);
-                               queue_work(line6_workqueue, &pod->create_files_work);
-                       } else
-                               DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "multiple firmware version message\n"));
+                       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"));
 
@@ -377,7 +332,7 @@ void pod_process_message(struct usb_line6_pod *pod)
        *) This method fails if a param change message is "chopped" after the first
        byte.
 */
-void pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, int length)
+void line6_pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, int length)
 {
        int i;
 
@@ -412,7 +367,7 @@ static void pod_send_channel(struct usb_line6_pod *pod, int value)
 /*
        Transmit PODxt Pro control parameter.
 */
-void pod_transmit_parameter(struct usb_line6_pod *pod, int param, int value)
+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);
@@ -511,7 +466,7 @@ static ssize_t get_name_generic(struct usb_line6_pod *pod, const char *str, char
        char *p2;
        char *last_non_space = buf;
 
-       int retval = line6_wait_dump(&pod->dumpreq, 0);
+       int retval = line6_dump_wait_interruptible(&pod->dumpreq);
        if (retval < 0)
                return retval;
 
@@ -588,7 +543,7 @@ static ssize_t pod_get_dump(struct device *dev, struct device_attribute *attr,
 {
        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);
+       int retval = line6_dump_wait_interruptible(&pod->dumpreq);
        if (retval < 0)
                return retval;
        memcpy(buf, &pod->prog_data, sizeof(pod->prog_data));
@@ -606,8 +561,8 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr,
 
        if (count != sizeof(pod->prog_data)) {
                dev_err(pod->line6.ifcdev,
-                       "data block must be exactly %zu bytes\n",
-                       sizeof(pod->prog_data));
+                                               "data block must be exactly %d bytes\n",
+                                               (int)sizeof(pod->prog_data));
                return -EINVAL;
        }
 
@@ -616,78 +571,88 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr,
 }
 
 /*
-       Request system parameter.
+       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 ssize_t pod_get_system_param(struct usb_line6_pod *pod, char *buf, int code, struct ValueWait *param, int tuner, int sign)
+static int pod_get_system_param_int(struct usb_line6_pod *pod, int *value, int code,
+                                   struct ValueWait *param, int sign)
 {
        char *sysex;
-       int value;
        static const int size = 1;
        int retval = 0;
-       DECLARE_WAITQUEUE(wait, current);
 
-       if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner)
+       if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && pod_is_tuner(code))
                return -ENODEV;
 
-       /* send value request to tuner: */
+       /* send value request to device: */
        param->value = POD_system_invalid;
        sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEMREQ, size);
+
        if (!sysex)
-               return 0;
+               return -ENOMEM;
+
        sysex[SYSEX_DATA_OFS] = code;
        line6_send_sysex_message(&pod->line6, sysex, size);
        kfree(sysex);
 
-       /* wait for tuner to respond: */
-       add_wait_queue(&param->wait, &wait);
-       current->state = TASK_INTERRUPTIBLE;
+       /* wait for device to respond: */
+       retval = wait_event_interruptible(param->wait, param->value != POD_system_invalid);
 
-       while (param->value == POD_system_invalid) {
-               if (signal_pending(current)) {
-                       retval = -ERESTARTSYS;
-                       break;
-               } else
-                       schedule();
-       }
+       if (retval < 0)
+               return retval;
 
-       current->state = TASK_RUNNING;
-       remove_wait_queue(&param->wait, &wait);
+       *value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value;
 
-       if (retval < 0)
+       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;
 
-       value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value;
        return sprintf(buf, "%d\n", value);
 }
 
 /*
-       Send system parameter.
+       Send system parameter (from integer).
        @param tuner non-zero, if code refers to a tuner parameter
 */
-static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf,
-                                   int count, int code, unsigned short mask,
-                                   int tuner)
+static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, int code)
 {
        char *sysex;
        static const int size = 5;
-       unsigned short value;
-       unsigned long result;
-       int ret;
 
-       if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner)
+       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 0;
-
-       ret = strict_strtoul(buf, 10, &result);
-       if (ret)
-               return ret;
-
-       value = result & mask;
+               return -ENOMEM;
        sysex[SYSEX_DATA_OFS] = code;
        sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f;
        sysex[SYSEX_DATA_OFS + 2] = (value >>  8) & 0x0f;
@@ -695,7 +660,20 @@ static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf,
        sysex[SYSEX_DATA_OFS + 4] = (value      ) & 0x0f;
        line6_send_sysex_message(&pod->line6, sysex, size);
        kfree(sysex);
-       return count;
+       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;
 }
 
 /*
@@ -706,7 +684,7 @@ static ssize_t pod_get_dump_buf(struct device *dev,
 {
        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);
+       int retval = line6_dump_wait_interruptible(&pod->dumpreq);
        if (retval < 0)
                return retval;
        memcpy(buf, &pod->prog_data_buf, sizeof(pod->prog_data_buf));
@@ -725,8 +703,8 @@ static ssize_t pod_set_dump_buf(struct device *dev,
 
        if (count != sizeof(pod->prog_data)) {
                dev_err(pod->line6.ifcdev,
-                                               "data block must be exactly %zu bytes\n",
-                                               sizeof(pod->prog_data));
+                       "data block must be exactly %d bytes\n",
+                       (int)sizeof(pod->prog_data));
                return -EINVAL;
        }
 
@@ -900,53 +878,94 @@ static ssize_t pod_wait_for_clip(struct device *dev,
 {
        struct usb_interface *interface = to_usb_interface(dev);
        struct usb_line6_pod *pod = usb_get_intfdata(interface);
-       int err = 0;
-       DECLARE_WAITQUEUE(wait, current);
-       pod->clipping.value = 0;
-       add_wait_queue(&pod->clipping.wait, &wait);
-       current->state = TASK_INTERRUPTIBLE;
-
-       while (pod->clipping.value == 0) {
-               if (signal_pending(current)) {
-                       err = -ERESTARTSYS;
-                       break;
-               } else
-                       schedule();
-       }
+       return wait_event_interruptible(pod->clipping.wait, pod->clipping.value != 0);
+}
 
-       current->state = TASK_RUNNING;
-       remove_wait_queue(&pod->clipping.wait, &wait);
-       return err;
+/*
+       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, 1);
+
+       /* 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;
+       CHECK_STARTUP_PROGRESS(pod->startup_progress, 2);
+
+       /* 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, 3);
+
+       /* request firmware version: */
+       line6_version_request_async(line6);
 }
 
-#define POD_GET_SYSTEM_PARAM(code, tuner, sign) \
+static void pod_startup4(struct usb_line6_pod *pod)
+{
+       CHECK_STARTUP_PROGRESS(pod->startup_progress, 4);
+
+       /* 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, 5);
+
+       /* 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(pod, buf, POD_ ## code, &pod->code, \
-                                   tuner, sign); \
+       return pod_get_system_param_string(pod, buf, POD_ ## code,      \
+                                          &pod->code, sign);           \
 }
 
-#define POD_GET_SET_SYSTEM_PARAM(code, mask, tuner, sign) \
-POD_GET_SYSTEM_PARAM(code, tuner, 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(pod, buf, count, POD_ ## code, mask, \
-                                   tuner); \
+       return pod_set_system_param_string(pod, buf, count, POD_ ## code, mask); \
 }
 
-POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0, 0);
-POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0, 0);
-POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 1, 0);
-POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 1, 0);
-POD_GET_SYSTEM_PARAM(tuner_note, 1, 1);
-POD_GET_SYSTEM_PARAM(tuner_pitch, 1, 1);
+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
@@ -977,10 +996,57 @@ static DEVICE_ATTR(tuner_mute, S_IWUGO | S_IRUGO, pod_get_tuner_mute, pod_set_tu
 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);
 
-#if CREATE_RAW_FILE
+#ifdef CONFIG_LINE6_USB_RAW
 static DEVICE_ATTR(raw, S_IWUGO, 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.
 */
@@ -998,8 +1064,6 @@ static void pod_destruct(struct usb_interface *interface)
 
        /* free dump request data: */
        line6_dumpreq_destruct(&pod->dumpreq);
-
-       kfree(pod->buffer_versionreq);
 }
 
 /*
@@ -1034,7 +1098,7 @@ static int pod_create_files2(struct device *dev)
        CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_note));
        CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_pitch));
 
-#if CREATE_RAW_FILE
+#ifdef CONFIG_LINE6_USB_RAW
        CHECK_RETURN(device_create_file(dev, &dev_attr_raw));
 #endif
 
@@ -1042,9 +1106,9 @@ static int pod_create_files2(struct device *dev)
 }
 
 /*
-        Init POD device.
+        Try to init POD device.
 */
-int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
+static int pod_try_init(struct usb_interface *interface, struct usb_line6_pod *pod)
 {
        int err;
        struct usb_line6 *line6 = &pod->line6;
@@ -1062,6 +1126,8 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
        init_waitqueue_head(&pod->tuner_note.wait);
        init_waitqueue_head(&pod->tuner_pitch.wait);
        init_waitqueue_head(&pod->clipping.wait);
+       init_timer(&pod->startup_timer);
+       INIT_WORK(&pod->startup_work, pod_startup5);
 
        memset(pod->param_dirty, 0xff, sizeof(pod->param_dirty));
 
@@ -1070,69 +1136,73 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
                                 sizeof(pod_request_channel));
        if (err < 0) {
                dev_err(&interface->dev, "Out of memory\n");
-               pod_destruct(interface);
-               return -ENOMEM;
-       }
-
-       pod->buffer_versionreq = kmemdup(pod_request_version,
-                                        sizeof(pod_request_version),
-                                        GFP_KERNEL);
-
-       if (pod->buffer_versionreq == NULL) {
-               dev_err(&interface->dev, "Out of memory\n");
-               pod_destruct(interface);
                return -ENOMEM;
        }
 
        /* create sysfs entries: */
        err = pod_create_files2(&interface->dev);
        if (err < 0) {
-               pod_destruct(interface);
                return err;
        }
 
        /* initialize audio system: */
        err = line6_init_audio(line6);
        if (err < 0) {
-               pod_destruct(interface);
                return err;
        }
 
        /* initialize MIDI subsystem: */
        err = line6_init_midi(line6);
        if (err < 0) {
-               pod_destruct(interface);
                return err;
        }
 
        /* initialize PCM subsystem: */
        err = line6_init_pcm(line6, &pod_pcm_properties);
        if (err < 0) {
-               pod_destruct(interface);
                return err;
        }
 
-       /* register audio system: */
-       err = line6_register_audio(line6);
+       /* register monitor control: */
+       err = snd_ctl_add(line6->card, snd_ctl_new1(&pod_control_monitor, line6->line6pcm));
        if (err < 0) {
-               pod_destruct(interface);
                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) {
-               /* query some data: */
-               line6_startup_delayed(&pod->dumpreq, POD_STARTUP_DELAY,
-                                     pod_startup_timeout, pod);
-               line6_read_serial_number(&pod->line6, &pod->serial_number);
+               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 pod_disconnect(struct usb_interface *interface)
+void line6_pod_disconnect(struct usb_interface *interface)
 {
        struct usb_line6_pod *pod;
 
@@ -1145,14 +1215,12 @@ void pod_disconnect(struct usb_interface *interface)
                struct device *dev = &interface->dev;
 
                if (line6pcm != NULL) {
-                       unlink_wait_clear_audio_out_urbs(line6pcm);
-                       unlink_wait_clear_audio_in_urbs(line6pcm);
+                       line6_pcm_disconnect(line6pcm);
                }
 
                if (dev != NULL) {
                        /* remove sysfs entries: */
-                       if (pod->versionreq_ok)
-                               pod_remove_files(pod->firmware_version, pod->line6.properties->device_bit, dev);
+                       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);
@@ -1179,7 +1247,7 @@ void pod_disconnect(struct usb_interface *interface)
                        device_remove_file(dev, &dev_attr_tuner_note);
                        device_remove_file(dev, &dev_attr_tuner_pitch);
 
-#if CREATE_RAW_FILE
+#ifdef CONFIG_LINE6_USB_RAW
                        device_remove_file(dev, &dev_attr_raw);
 #endif
                }
index 7051ca6..d8e38a9 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
 #define POD_H
 
 
-#include "driver.h"
-
+#include <linux/interrupt.h>
 #include <linux/spinlock.h>
 #include <linux/usb.h>
 #include <linux/wait.h>
-#include <linux/workqueue.h>
 
 #include <sound/core.h>
 
+#include "driver.h"
 #include "dumprequest.h"
 
 
 */
 #define POD_CONTROL_SIZE 0x80
 #define POD_BUFSIZE_DUMPREQ 7
-#define POD_STARTUP_DELAY 3
-
+#define POD_STARTUP_DELAY 3000
 
 /**
-        Data structure for values that need to be requested explicitly.
-        This is the case for system and tuner settings.
+       Data structure for values that need to be requested explicitly.
+       This is the case for system and tuner settings.
 */
 struct ValueWait {
-       unsigned short value;
+       int value;
        wait_queue_head_t wait;
 };
 
 /**
-        Binary PodXT Pro program dump
+       Binary PODxt Pro program dump
 */
 struct pod_program {
        /**
-                Header information (including program name).
+               Header information (including program name).
        */
        unsigned char header[0x20];
 
        /**
-                Program parameters.
+               Program parameters.
        */
        unsigned char control[POD_CONTROL_SIZE];
 };
 
 struct usb_line6_pod {
        /**
-                Generic Line6 USB data.
+               Generic Line6 USB data.
        */
        struct usb_line6 line6;
 
        /**
-                Dump request structure.
+               Dump request structure.
        */
        struct line6_dump_request dumpreq;
 
        /**
-                Current program number.
+               Current program number.
        */
        unsigned char channel_num;
 
        /**
-                Current program settings.
+               Current program settings.
        */
        struct pod_program prog_data;
 
        /**
-                Buffer for data retrieved from or to be stored on PODxt Pro.
+               Buffer for data retrieved from or to be stored on PODxt Pro.
        */
        struct pod_program prog_data_buf;
 
        /**
-                Buffer for requesting version number.
-       */
-       unsigned char *buffer_versionreq;
-
-       /**
-                Tuner mute mode.
+               Tuner mute mode.
        */
        struct ValueWait tuner_mute;
 
        /**
-                Tuner base frequency (typically 440Hz).
+               Tuner base frequency (typically 440Hz).
        */
        struct ValueWait tuner_freq;
 
        /**
-                Note received from tuner.
+               Note received from tuner.
        */
        struct ValueWait tuner_note;
 
        /**
-                Pitch value received from tuner.
+               Pitch value received from tuner.
        */
        struct ValueWait tuner_pitch;
 
        /**
-                Instrument monitor level.
+               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
+               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.
+               Wait for audio clipping event.
        */
        struct ValueWait clipping;
 
        /**
-                Bottom-half for creation of sysfs special files.
+               Timer for device initializaton.
        */
-       struct work_struct create_files_work;
+       struct timer_list startup_timer;
 
        /**
-                Dirty flags for access to parameter data.
+               Work handler for device initializaton.
        */
-       unsigned long param_dirty[POD_CONTROL_SIZE / sizeof(unsigned long)];
+       struct work_struct startup_work;
 
        /**
-                Some atomic flags.
+               Current progress in startup procedure.
        */
-       unsigned long atomic_flags;
+       int startup_progress;
 
        /**
-                Counter for startup process.
+               Dirty flags for access to parameter data.
        */
-       int startup_count;
+       unsigned long param_dirty[POD_CONTROL_SIZE / sizeof(unsigned long)];
+
+       /**
+               Some atomic flags.
+       */
+       unsigned long atomic_flags;
 
        /**
-                Serial number of device.
+               Serial number of device.
        */
        int serial_number;
 
        /**
-                Firmware version (x 100).
+               Firmware version (x 100).
        */
        int firmware_version;
 
        /**
-                Device ID.
+               Device ID.
        */
        int device_id;
 
        /**
-                Flag to indicate modification of current program settings.
+               Flag to indicate modification of current program settings.
        */
        char dirty;
 
        /**
-                Flag if initial firmware version request has been successful.
-       */
-       char versionreq_ok;
-
-       /**
-                Flag to enable MIDI postprocessing.
+               Flag to enable MIDI postprocessing.
        */
        char midi_postprocess;
 };
 
 
-extern void pod_disconnect(struct usb_interface *interface);
-extern int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod);
-extern void pod_midi_postprocess(struct usb_line6_pod *pod,
-                                unsigned char *data, int length);
-extern void pod_process_message(struct usb_line6_pod *pod);
-extern void pod_receive_parameter(struct usb_line6_pod *pod, int param);
-extern void pod_transmit_parameter(struct usb_line6_pod *pod, int param,
-                                  int value);
+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
index b2a0a85..4bb87fc 100644 (file)
@@ -1,4 +1,4 @@
 #ifndef DRIVER_REVISION
 /* current subversion revision */
-#define DRIVER_REVISION " (revision 529)"
+#define DRIVER_REVISION " (revision 665)"
 #endif
index e6770ea..0e7e871 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
  *
  */
 
-#include "driver.h"
+#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,
@@ -28,43 +35,52 @@ static struct snd_ratden toneport_ratden = {
 
 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 |
-                                          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 = 180 * 4,
-                                 .period_bytes_max = 8192,
-                                 .periods_min = 1,
-                                 .periods_max = 1024},
+               .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 |
-                                         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 = 188 * 4,
-                                .period_bytes_max = 8192,
-                                .periods_min = 1,
-                                .periods_max = 1024},
+               .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},
+               .nrats = 1,
+               .rats = &toneport_ratden
+       },
        .bytes_per_frame = 4
 };
 
@@ -77,6 +93,27 @@ static struct line6_pcm_properties toneport_pcm_properties = {
 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);
@@ -129,6 +166,7 @@ static DEVICE_ATTR(led_red, S_IWUGO | S_IRUGO, line6_nop_read,
 static DEVICE_ATTR(led_green, S_IWUGO | S_IRUGO, line6_nop_read,
                   toneport_set_led_green);
 
+
 static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
 {
        int ret;
@@ -145,6 +183,111 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
        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];
+       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_start(line6->line6pcm, MASK_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.
 */
@@ -162,14 +305,41 @@ static void toneport_destruct(struct usb_interface *interface)
 }
 
 /*
-        Init Toneport device.
+       Setup Toneport device.
 */
-int toneport_init(struct usb_interface *interface,
-                 struct usb_line6_toneport *toneport)
+static void toneport_setup(struct usb_line6_toneport *toneport)
 {
-       int err, ticks;
+       int ticks;
        struct usb_line6 *line6 = &toneport->line6;
-       struct usb_device *usbdev;
+       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_PODSTUDIO_UX1:
+               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;
@@ -177,64 +347,93 @@ int toneport_init(struct usb_interface *interface,
        /* initialize audio system: */
        err = line6_init_audio(line6);
        if (err < 0) {
-               toneport_destruct(interface);
                return err;
        }
 
        /* initialize PCM subsystem: */
        err = line6_init_pcm(line6, &toneport_pcm_properties);
        if (err < 0) {
-               toneport_destruct(interface);
                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_PODSTUDIO_UX1:
+               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) {
-               toneport_destruct(interface);
                return err;
        }
 
-       usbdev = line6->usbdev;
        line6_read_serial_number(line6, &toneport->serial_number);
        line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1);
 
-       /* sync time on device with host: */
-       ticks = (int)get_seconds();
-       line6_write_data(line6, 0x80c6, &ticks, 4);
+       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));
+       }
 
-       /*
-          seems to work without the first two...
-        */
-       /* toneport_send_cmd(usbdev, 0x0201, 0x0002); */
-       /* toneport_send_cmd(usbdev, 0x0801, 0x0000); */
-       /* only one that works for me; on GP, TP might be different? */
-       toneport_send_cmd(usbdev, 0x0301, 0x0000);
+       toneport_setup(toneport);
 
-       if (usbdev->descriptor.idProduct != LINE6_DEVID_GUITARPORT) {
-               CHECK_RETURN(device_create_file
-                            (&interface->dev, &dev_attr_led_red));
-               CHECK_RETURN(device_create_file
-                            (&interface->dev, &dev_attr_led_green));
-               toneport_update_led(&usbdev->dev);
-       }
+       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 toneport_disconnect(struct usb_interface *interface)
+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->line6.usbdev->descriptor.idProduct !=
-           LINE6_DEVID_GUITARPORT) {
+       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);
        }
@@ -243,8 +442,8 @@ void toneport_disconnect(struct usb_interface *interface)
                struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm;
 
                if (line6pcm != NULL) {
-                       unlink_wait_clear_audio_out_urbs(line6pcm);
-                       unlink_wait_clear_audio_in_urbs(line6pcm);
+                       line6_pcm_stop(line6pcm, MASK_PCM_MONITOR);
+                       line6_pcm_disconnect(line6pcm);
                }
        }
 
index bddc58d..c2adee3 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
 #define TONEPORT_H
 
 
-#include "driver.h"
-
 #include <linux/usb.h>
 #include <sound/core.h>
 
+#include "driver.h"
+
 
 struct usb_line6_toneport {
        /**
-                Generic Line6 USB data.
+               Generic Line6 USB data.
        */
        struct usb_line6 line6;
 
        /**
-                Serial number of device.
+               Source selector.
+       */
+       int source;
+
+       /**
+               Serial number of device.
        */
        int serial_number;
 
        /**
-                Firmware version (x 100).
+               Firmware version (x 100).
        */
        int firmware_version;
+
+       /**
+                Timer for delayed PCM startup.
+       */
+       struct timer_list timer;
 };
 
 
-extern void toneport_disconnect(struct usb_interface *interface);
-extern int toneport_init(struct usb_interface *interface,
-                        struct usb_line6_toneport *toneport);
+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
index c38f31f..1e392c7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
  * Copyright (C) 2005-2008 Markus Grabner (grabner@icg.tugraz.at)
  *
@@ -25,6 +25,9 @@
 #define LINE6_DEVID_BASSPODXTPRO  0x4252
 #define LINE6_DEVID_GUITARPORT    0x4750
 #define LINE6_DEVID_POCKETPOD     0x5051
+#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_TONEPORT_UX2  0x4142
 #define LINE6_DEVID_VARIAX        0x534d
 
-#define LINE6_BIT_BASSPODXT       (1 << 0)
-#define LINE6_BIT_BASSPODXTLIVE   (1 << 1)
-#define LINE6_BIT_BASSPODXTPRO    (1 << 2)
-#define LINE6_BIT_GUITARPORT      (1 << 3)
-#define LINE6_BIT_POCKETPOD       (1 << 4)
-#define LINE6_BIT_PODX3           (1 << 5)
-#define LINE6_BIT_PODX3LIVE       (1 << 6)
-#define LINE6_BIT_PODXT           (1 << 7)
-#define LINE6_BIT_PODXTLIVE       (1 << 8)
-#define LINE6_BIT_PODXTPRO        (1 << 9)
-#define LINE6_BIT_TONEPORT_GX     (1 << 10)
-#define LINE6_BIT_TONEPORT_UX1    (1 << 11)
-#define LINE6_BIT_TONEPORT_UX2    (1 << 12)
-#define LINE6_BIT_VARIAX          (1 << 13)
+#define LINE6_BIT_BASSPODXT       (1 <<  0)
+#define LINE6_BIT_BASSPODXTLIVE   (1 <<  1)
+#define LINE6_BIT_BASSPODXTPRO    (1 <<  2)
+#define LINE6_BIT_GUITARPORT      (1 <<  3)
+#define LINE6_BIT_POCKETPOD       (1 <<  4)
+#define LINE6_BIT_PODSTUDIO_GX    (1 <<  5)
+#define LINE6_BIT_PODSTUDIO_UX1   (1 <<  6)
+#define LINE6_BIT_PODSTUDIO_UX2   (1 <<  7)
+#define LINE6_BIT_PODX3           (1 <<  8)
+#define LINE6_BIT_PODX3LIVE       (1 <<  9)
+#define LINE6_BIT_PODXT           (1 << 10)
+#define LINE6_BIT_PODXTLIVE       (1 << 11)
+#define LINE6_BIT_PODXTPRO        (1 << 12)
+#define LINE6_BIT_TONEPORT_GX     (1 << 13)
+#define LINE6_BIT_TONEPORT_UX1    (1 << 14)
+#define LINE6_BIT_TONEPORT_UX2    (1 << 15)
+#define LINE6_BIT_VARIAX          (1 << 16)
 
 #define LINE6_BITS_PRO         (LINE6_BIT_BASSPODXTPRO | \
                                 LINE6_BIT_PODXTPRO)
 #define LINE6_BIT_CONTROL      (1 << 0)
 /* device supports PCM input/output via USB */
 #define LINE6_BIT_PCM          (1 << 1)
-#define LINE6_BIT_CONTROL_PCM  (LINE6_BIT_CONTROL | LINE6_BIT_PCM)
+/* 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
index 58ddbe6..9f1b085 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
@@ -9,12 +9,11 @@
  *
  */
 
-#include "driver.h"
-
 #include <linux/slab.h>
 
 #include "audio.h"
 #include "control.h"
+#include "driver.h"
 #include "variax.h"
 
 
 #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,
@@ -45,6 +72,13 @@ static const char variax_request_model2[] = {
 };
 
 
+/* 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.
 */
@@ -60,42 +94,93 @@ static void variax_decode(const unsigned char *raw_data, unsigned char *data,
        }
 }
 
-static void variax_activate_timeout(unsigned long arg)
+static void variax_activate_async(struct usb_line6_variax *variax, int a)
 {
-       struct usb_line6_variax *variax = (struct usb_line6_variax *)arg;
-       variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = 1;
+       variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
        line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
                                     sizeof(variax_activate));
 }
 
 /*
-       Send an asynchronous activation request after a given interval.
+       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_activate_delayed(struct usb_line6_variax *variax,
-                                   int seconds)
+
+static void variax_startup1(struct usb_line6_variax *variax)
 {
-       variax->activate_timer.expires = jiffies + seconds * HZ;
-       variax->activate_timer.function = variax_activate_timeout;
-       variax->activate_timer.data = (unsigned long)variax;
-       add_timer(&variax->activate_timer);
+       CHECK_STARTUP_PROGRESS(variax->startup_progress, 1);
+
+       /* delay startup procedure: */
+       line6_start_timer(&variax->startup_timer, VARIAX_STARTUP_DELAY1, variax_startup2, (unsigned long)variax);
 }
 
-static void variax_startup_timeout(unsigned long arg)
+static void variax_startup2(unsigned long data)
 {
-       struct usb_line6_variax *variax = (struct usb_line6_variax *)arg;
+       struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
+       struct usb_line6 *line6 = &variax->line6;
+       CHECK_STARTUP_PROGRESS(variax->startup_progress, 2);
 
-       if (variax->dumpreq.ok)
-               return;
+       /* request firmware version: */
+       line6_version_request_async(line6);
+}
 
-       line6_dump_request_async(&variax->dumpreq, &variax->line6, 0);
-       line6_startup_delayed(&variax->dumpreq, 1, variax_startup_timeout,
-                             variax);
+static void variax_startup3(struct usb_line6_variax *variax)
+{
+       CHECK_STARTUP_PROGRESS(variax->startup_progress, 3);
+
+       /* delay startup procedure: */
+       line6_start_timer(&variax->startup_timer, 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, 4);
+
+       /* activate device: */
+       variax_activate_async(variax, 1);
+       line6_start_timer(&variax->startup_timer, 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, 5);
+
+       /* 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, 6);
+
+       /* 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, 7);
+
+       /* 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 variax_process_message(struct usb_line6_variax *variax)
+void line6_variax_process_message(struct usb_line6_variax *variax)
 {
        const unsigned char *buf = variax->line6.buffer_message;
 
@@ -115,12 +200,11 @@ void variax_process_message(struct usb_line6_variax *variax)
        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);
+               line6_dump_request_async(&variax->dumpreq, &variax->line6, 0, VARIAX_DUMP_PASS1);
                break;
 
        case LINE6_RESET:
                dev_info(variax->line6.ifcdev, "VARIAX reset\n");
-               variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY);
                break;
 
        case LINE6_SYSEX_BEGIN:
@@ -132,8 +216,7 @@ void variax_process_message(struct usb_line6_variax *variax)
                                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);
-                                       line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS2);
+                                       line6_dump_request_async(&variax->dumpreq, &variax->line6, 1, VARIAX_DUMP_PASS2);
                                        break;
 
                                case VARIAX_DUMP_PASS2:
@@ -141,21 +224,31 @@ void variax_process_message(struct usb_line6_variax *variax)
                                        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);
-                                       variax->dumpreq.ok = 1;
-                                       line6_dump_request_async(&variax->dumpreq, &variax->line6, 2);
-                                       line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS3);
+                                       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) {
+                                 sizeof(variax_request_bank) - 2) == 0) {
                        memcpy(variax->bank,
                               buf + sizeof(variax_request_bank) - 1,
                               sizeof(variax->bank));
-                       variax->dumpreq.ok = 1;
                        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;
@@ -256,9 +349,7 @@ static ssize_t variax_set_active(struct device *dev,
        if (ret)
                return ret;
 
-       variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = value ? 1 : 0;
-       line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
-                                    sizeof(variax_activate));
+       variax_activate_async(variax, value ? 1 : 0);
        return count;
 }
 
@@ -317,7 +408,7 @@ 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_wait_dump(&variax->dumpreq, 0);
+       line6_dump_wait_interruptible(&variax->dumpreq);
        return get_string(buf, variax->model_data.name,
                          sizeof(variax->model_data.name));
 }
@@ -329,7 +420,7 @@ 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_wait_dump(&variax->dumpreq, 0);
+       line6_dump_wait_interruptible(&variax->dumpreq);
        return get_string(buf, variax->bank, sizeof(variax->bank));
 }
 
@@ -341,7 +432,7 @@ static ssize_t variax_get_dump(struct device *dev,
 {
        struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
        int retval;
-       retval = line6_wait_dump(&variax->dumpreq, 0);
+       retval = line6_dump_wait_interruptible(&variax->dumpreq);
        if (retval < 0)
                return retval;
        memcpy(buf, &variax->model_data.control,
@@ -349,7 +440,22 @@ static ssize_t variax_get_dump(struct device *dev,
        return sizeof(variax->model_data.control);
 }
 
-#if CREATE_RAW_FILE
+/*
+       "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.
@@ -396,8 +502,9 @@ 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_IWUGO | S_IRUGO, variax_get_active, variax_set_active);
+static DEVICE_ATTR(guitar, S_IRUGO, variax_get_guitar, line6_nop_write);
 
-#if CREATE_RAW_FILE
+#ifdef CONFIG_LINE6_USB_RAW
 static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw);
 static DEVICE_ATTR(raw2, S_IWUGO, line6_nop_read, variax_set_raw2);
 #endif
@@ -424,7 +531,6 @@ static void variax_destruct(struct usb_interface *interface)
        line6_dumpreq_destruct(&variax->dumpreq);
 
        kfree(variax->buffer_activate);
-       del_timer_sync(&variax->activate_timer);
 }
 
 /*
@@ -440,7 +546,8 @@ static int variax_create_files2(struct device *dev)
        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));
-#if CREATE_RAW_FILE
+       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
@@ -448,23 +555,25 @@ static int variax_create_files2(struct device *dev)
 }
 
 /*
-        Init workbench device.
+        Try to init workbench device.
 */
-int variax_init(struct usb_interface *interface,
-               struct usb_line6_variax *variax)
+static int variax_try_init(struct usb_interface *interface,
+                          struct usb_line6_variax *variax)
 {
        int err;
 
        if ((interface == NULL) || (variax == NULL))
                return -ENODEV;
 
+       init_timer(&variax->startup_timer);
+       INIT_WORK(&variax->startup_work, variax_startup7);
+
        /* 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");
-               variax_destruct(interface);
                return err;
        }
 
@@ -473,7 +582,6 @@ int variax_init(struct usb_interface *interface,
 
        if (err < 0) {
                dev_err(&interface->dev, "Out of memory\n");
-               variax_destruct(interface);
                return err;
        }
 
@@ -482,7 +590,6 @@ int variax_init(struct usb_interface *interface,
 
        if (err < 0) {
                dev_err(&interface->dev, "Out of memory\n");
-               variax_destruct(interface);
                return err;
        }
 
@@ -491,56 +598,45 @@ int variax_init(struct usb_interface *interface,
 
        if (variax->buffer_activate == NULL) {
                dev_err(&interface->dev, "Out of memory\n");
-               variax_destruct(interface);
                return -ENOMEM;
        }
 
-       init_timer(&variax->activate_timer);
-
-       /* create sysfs entries: */
-       err = variax_create_files(0, 0, &interface->dev);
-       if (err < 0) {
-               variax_destruct(interface);
-               return err;
-       }
-
-       err = variax_create_files2(&interface->dev);
-       if (err < 0) {
-               variax_destruct(interface);
-               return err;
-       }
-
        /* initialize audio system: */
        err = line6_init_audio(&variax->line6);
        if (err < 0) {
-               variax_destruct(interface);
                return err;
        }
 
        /* initialize MIDI subsystem: */
        err = line6_init_midi(&variax->line6);
        if (err < 0) {
-               variax_destruct(interface);
                return err;
        }
 
-       /* register audio system: */
-       err = line6_register_audio(&variax->line6);
+       /* 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;
        }
 
-       variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY);
-       line6_startup_delayed(&variax->dumpreq, VARIAX_STARTUP_DELAY,
-                             variax_startup_timeout, variax);
-       return 0;
+       return err;
 }
 
 /*
        Workbench device disconnected.
 */
-void variax_disconnect(struct usb_interface *interface)
+void line6_variax_disconnect(struct usb_interface *interface)
 {
        struct device *dev;
 
@@ -550,7 +646,7 @@ void variax_disconnect(struct usb_interface *interface)
 
        if (dev != NULL) {
                /* remove sysfs entries: */
-               variax_remove_files(0, 0, dev);
+               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);
@@ -558,7 +654,8 @@ void variax_disconnect(struct usb_interface *interface)
                device_remove_file(dev, &dev_attr_bank);
                device_remove_file(dev, &dev_attr_dump);
                device_remove_file(dev, &dev_attr_active);
-#if CREATE_RAW_FILE
+               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
index ee330ba..12cb5f2 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * 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
 #define VARIAX_H
 
 
-#include "driver.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_ACTIVATE_DELAY 10
-#define VARIAX_STARTUP_DELAY 3
+#define VARIAX_STARTUP_DELAY1 1000
+#define VARIAX_STARTUP_DELAY3 100
+#define VARIAX_STARTUP_DELAY4 100
 
 
 enum {
@@ -36,73 +35,88 @@ enum {
 
 
 /**
-        Binary Variax model dump
+       Binary Variax model dump
 */
 struct variax_model {
        /**
-                Header information (including program name).
+               Header information (including program name).
        */
        unsigned char name[18];
 
        /**
-                Model parameters.
+               Model parameters.
        */
        unsigned char control[78 * 2];
 };
 
 struct usb_line6_variax {
        /**
-                Generic Line6 USB data.
+               Generic Line6 USB data.
        */
        struct usb_line6 line6;
 
        /**
-                Dump request structure.
-                Append two extra buffers for 3-pass data query.
+               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.
+               Buffer for activation code.
        */
        unsigned char *buffer_activate;
 
        /**
-                Model number.
+               Model number.
        */
        int model;
 
        /**
-                Current model settings.
+               Current model settings.
        */
        struct variax_model model_data;
 
        /**
-                Name of current model bank.
+               Name of connected guitar.
+       */
+       unsigned char guitar[18];
+
+       /**
+               Name of current model bank.
        */
        unsigned char bank[18];
 
        /**
-                Position of volume dial.
+               Position of volume dial.
        */
        int volume;
 
        /**
-                Position of tone control dial.
+               Position of tone control dial.
        */
        int tone;
 
        /**
-                Timer for delayed activation request.
+               Handler for device initializaton.
+       */
+       struct work_struct startup_work;
+
+       /**
+               Timer for device initializaton.
+       */
+       struct timer_list startup_timer;
+
+       /**
+               Current progress in startup procedure.
        */
-       struct timer_list activate_timer;
+       int startup_progress;
 };
 
 
-extern void variax_disconnect(struct usb_interface *interface);
-extern int variax_init(struct usb_interface *interface,
-                      struct usb_line6_variax *variax);
-extern void variax_process_message(struct usb_line6_variax *variax);
+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