Big upstream sync.
Signed-off-by: Markus Grabner <grabner@icg.tugraz.at>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-config LINE6_USB
+menuconfig LINE6_USB
tristate "Line6 USB support"
depends on USB && SND
select SND_RAWMIDI
* 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
/*
- * 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
*
*/
-#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;
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;
/*
- * 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
/*
- * 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
*
*/
-#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;
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;
}
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;
/*
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;
}
/*
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;
} 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];
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);
}
}
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;
/* 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;
/*
- * 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
/*
- * 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
*
*/
-#include "driver.h"
-
#include <linux/usb.h>
#include "control.h"
+#include "driver.h"
#include "pod.h"
#include "usbdefs.h"
#include "variax.h"
{
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]);
if (retval)
return retval;
- pod_transmit_parameter(pod, param, value);
+ line6_pod_transmit_parameter(pod, param, value);
return count;
}
{
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]);
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;
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;
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));
(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);
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));
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);
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);
/*
- * 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,
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 */
};
/**
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 */
};
-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
/*
- * 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
*
*/
-#include "driver.h"
-
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "audio.h"
#include "capture.h"
#include "control.h"
+#include "driver.h"
#include "midi.h"
#include "playback.h"
#include "pod.h"
#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 */
{ 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) },
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;
/**
*/
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.
*/
}
#endif
-#if DO_DUMP_URB_RECEIVE
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
/*
Dump URB data to syslog.
*/
#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;
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,
break;
}
- done += actual_size;
+ done += frag_size;
}
return done;
(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
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.
*/
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.
*/
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
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");
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);
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:
break;
case LINE6_DEVID_VARIAX:
- variax_process_message((struct usb_line6_variax *)line6);
+ line6_variax_process_message((struct usb_line6_variax *)line6);
break;
default:
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;
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;
}
/*
*/
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;
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;
}
/*
/* 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);
/*
"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)
{
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.
*/
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;
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;
}
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) {
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;
}
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);
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! */
ep_write = 0x01;
break;
+ case LINE6_DEVID_PODSTUDIO_GX:
+ case LINE6_DEVID_PODSTUDIO_UX1:
+ case LINE6_DEVID_PODSTUDIO_UX2:
case LINE6_DEVID_TONEPORT_GX:
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2:
case LINE6_DEVID_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:
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:
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:
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;
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,
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:
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);
/* 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,
};
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;
}
*/
static void __exit line6_exit(void)
{
- destroy_workqueue(line6_workqueue);
+ kfree(line6_request_version);
usb_deregister(&line6_driver);
}
/*
- * 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", \
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;
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;
};
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
/*
- * 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
*
*/
-#include "driver.h"
-
#include <linux/slab.h>
+#include "driver.h"
#include "dumprequest.h"
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);
}
/*
- 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);
}
/*
if (ret < 0)
return ret;
init_waitqueue_head(&l6dr->wait);
- init_timer(&l6dr->timer);
return 0;
}
if (l6dr->reqbufs[0].buffer == NULL)
return;
line6_dumpreq_destructbuf(l6dr, 0);
- l6dr->ok = 1;
- del_timer_sync(&l6dr->timer);
}
/*
- * 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
#include <linux/usb.h>
#include <linux/wait.h>
-
#include <sound/core.h>
*/
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
*/
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);
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
/*
- * 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
*
*/
-#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))
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);
}
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);
}
return -ENOMEM;
}
-#if DO_DUMP_URB_SEND
+#ifdef CONFIG_LINE6_USB_DUMP_CTRL
line6_write_hexdump(line6, 'S', data, length);
#endif
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:
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)
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 =
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;
}
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;
/*
- * 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
/*
- * 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
*
*/
-#include "config.h"
-
#include <linux/slab.h>
#include "midibuf.h"
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 };
}
}
-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);
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) ?
(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) ?
(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;
skip_active_sense = 1;
}
- bytes_free = midibuf_bytes_free(this);
+ bytes_free = line6_midibuf_bytes_free(this);
if (length > bytes_free)
length = bytes_free;
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;
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;
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;
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;
return 0;
}
-void midibuf_destroy(struct MidiBuffer *this)
+void line6_midibuf_destroy(struct MidiBuffer *this)
{
kfree(this->buf);
this->buf = NULL;
/*
- * 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
};
-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
/*
- * 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
*
*/
-#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)
{
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,
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,
break;
default:
- dev_err(s2m(substream), "Unknown stream direction %d\n",
+ dev_err(line6pcm->line6->ifcdev, "Unknown stream direction %d\n",
s->stream);
}
}
}
/* 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;
}
/* 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;
}
}
/* 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
};
/*
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]);
/* 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,
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.
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;
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;
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;
}
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;
}
/*
- * 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 {
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.
*/
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.
*/
/**
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;
};
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
/*
- * 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
*
*/
-#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"
}
}
+#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);
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;
}
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,
*/
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];
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;
/*
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;
}
/*
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;
}
/*
- 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)
{
} 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);
}
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--;)
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);
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);
+ }
}
}
}
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;
/* 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;
/*
- * 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
/*
- * 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
*
*/
-#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"
POD_tuner_freq = 0x15,
POD_tuner_note = 0x16,
POD_tuner_pitch = 0x17,
- POD_system_invalid = 0x7fff
+ POD_system_invalid = 0x10000
};
enum {
};
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,
.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,
.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
.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);
/*
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);
}
/*
- Handle SAVE button
+ Handle SAVE button.
*/
static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int index)
{
/*
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;
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;
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:
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:
}
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));
#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]) {
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:
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"));
*) 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;
/*
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);
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;
{
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));
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;
}
}
/*
- 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(¶m->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(¶m->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;
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;
}
/*
{
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));
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;
}
{
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
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.
*/
/* free dump request data: */
line6_dumpreq_destruct(&pod->dumpreq);
-
- kfree(pod->buffer_versionreq);
}
/*
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
}
/*
- 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;
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));
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;
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);
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
}
/*
- * 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
#ifndef DRIVER_REVISION
/* current subversion revision */
-#define DRIVER_REVISION " (revision 529)"
+#define DRIVER_REVISION " (revision 665)"
#endif
/*
- * 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,
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
};
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);
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;
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.
*/
}
/*
- 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;
/* 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);
}
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);
}
}
/*
- * 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
/*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2005-2008 Markus Grabner (grabner@icg.tugraz.at)
*
#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
/*
- * 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
*
*/
-#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,
};
+/* forward declarations: */
+static int variax_create_files2(struct device *dev);
+static void variax_startup2(unsigned long data);
+static void variax_startup4(unsigned long data);
+static void variax_startup5(unsigned long data);
+
+
/*
Decode data transmitted by workbench.
*/
}
}
-static void variax_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;
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:
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:
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;
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;
}
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));
}
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));
}
{
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,
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.
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
line6_dumpreq_destruct(&variax->dumpreq);
kfree(variax->buffer_activate);
- del_timer_sync(&variax->activate_timer);
}
/*
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
}
/*
- 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;
}
if (err < 0) {
dev_err(&interface->dev, "Out of memory\n");
- variax_destruct(interface);
return err;
}
if (err < 0) {
dev_err(&interface->dev, "Out of memory\n");
- variax_destruct(interface);
return err;
}
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;
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);
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
/*
- * 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 {
/**
- 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