Commit 1027f476 authored by Markus Grabner's avatar Markus Grabner Committed by Greg Kroah-Hartman

staging: line6: sync with upstream

Big upstream sync.
Signed-off-by: default avatarMarkus Grabner <grabner@icg.tugraz.at>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 4498dbcd
config LINE6_USB menuconfig LINE6_USB
tristate "Line6 USB support" tristate "Line6 USB support"
depends on USB && SND depends on USB && SND
select SND_RAWMIDI select SND_RAWMIDI
...@@ -18,5 +18,68 @@ config LINE6_USB ...@@ -18,5 +18,68 @@ config LINE6_USB
* Signal routing (record clean/processed guitar signal, * Signal routing (record clean/processed guitar signal,
re-amping) 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -9,12 +9,12 @@ ...@@ -9,12 +9,12 @@
* *
*/ */
#include "driver.h"
#include "audio.h"
#include <sound/core.h> #include <sound/core.h>
#include <sound/initval.h> #include <sound/initval.h>
#include "driver.h"
#include "audio.h"
static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
...@@ -36,8 +36,9 @@ int line6_init_audio(struct usb_line6 *line6) ...@@ -36,8 +36,9 @@ int line6_init_audio(struct usb_line6 *line6)
line6->card = card; line6->card = card;
strcpy(card->id, line6->properties->id);
strcpy(card->driver, DRIVER_NAME); 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, sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name,
dev_name(line6->ifcdev)); /* 80 chars - see asound.h */ dev_name(line6->ifcdev)); /* 80 chars - see asound.h */
return 0; 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -9,27 +9,24 @@ ...@@ -9,27 +9,24 @@
* *
*/ */
#include "driver.h"
#include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include "audio.h" #include "audio.h"
#include "capture.h"
#include "driver.h"
#include "pcm.h" #include "pcm.h"
#include "pod.h" #include "pod.h"
#include "capture.h"
/* /*
Find a free URB and submit it. 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; unsigned long flags;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
int i, urb_size; int i, urb_size;
struct urb *urb_in; struct urb *urb_in;
...@@ -37,9 +34,9 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) ...@@ -37,9 +34,9 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
index = index =
find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); 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); 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; return -EINVAL;
} }
...@@ -58,13 +55,13 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) ...@@ -58,13 +55,13 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
line6pcm->buffer_in + line6pcm->buffer_in +
index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
urb_in->transfer_buffer_length = urb_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) if (usb_submit_urb(urb_in, GFP_ATOMIC) == 0)
set_bit(index, &line6pcm->active_urb_in); set_bit(index, &line6pcm->active_urb_in);
else else
dev_err(s2m(substream), "URB in #%d submission failed\n", dev_err(line6pcm->line6->ifcdev,
index); "URB in #%d submission failed\n", index);
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
return 0; return 0;
...@@ -73,12 +70,12 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) ...@@ -73,12 +70,12 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
/* /*
Submit all currently available capture URBs. 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; int ret, i;
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
ret = submit_audio_in_urb(substream); ret = submit_audio_in_urb(line6pcm);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
...@@ -89,7 +86,7 @@ static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream) ...@@ -89,7 +86,7 @@ static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream)
/* /*
Unlink all currently active capture URBs. 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; unsigned int i;
...@@ -126,41 +123,83 @@ static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) ...@@ -126,41 +123,83 @@ static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
} while (--timeout > 0); } while (--timeout > 0);
if (alive) if (alive)
snd_printk(KERN_ERR "timeout: still %d active urbs..\n", 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. 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); wait_clear_audio_in_urbs(line6pcm);
} }
/*
Copy data into ALSA capture buffer.
*/
void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
{
struct snd_pcm_substream *substream =
get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
struct snd_pcm_runtime *runtime = substream->runtime;
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
int frames = fsize / bytes_per_frame;
if (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. Callback for completed capture URB.
*/ */
static void audio_in_callback(struct urb *urb) static void audio_in_callback(struct urb *urb)
{ {
int i, index, length = 0, shutdown = 0; int i, index, length = 0, shutdown = 0;
int frames;
unsigned long flags; unsigned long flags;
struct snd_pcm_substream *substream = struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
(struct snd_pcm_substream *)urb->context;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); line6pcm->last_frame_in = urb->start_frame;
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
struct snd_pcm_runtime *runtime = substream->runtime;
/* find index of URB */ /* find index of URB */
for (index = 0; index < LINE6_ISO_BUFFERS; ++index) for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
if (urb == line6pcm->urb_audio_in[index]) if (urb == line6pcm->urb_audio_in[index])
break; break;
#if DO_DUMP_PCM_RECEIVE #ifdef CONFIG_LINE6_USB_DUMP_PCM
for (i = 0; i < LINE6_ISO_PACKETS; ++i) { for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
struct usb_iso_packet_descriptor *fout = struct usb_iso_packet_descriptor *fout =
&urb->iso_frame_desc[i]; &urb->iso_frame_desc[i];
...@@ -184,64 +223,43 @@ static void audio_in_callback(struct urb *urb) ...@@ -184,64 +223,43 @@ static void audio_in_callback(struct urb *urb)
fbuf = urb->transfer_buffer + fin->offset; fbuf = urb->transfer_buffer + fin->offset;
fsize = fin->actual_length; fsize = fin->actual_length;
length += fsize;
if (fsize > 0) { if (fsize > line6pcm->max_packet_size) {
frames = fsize / bytes_per_frame; dev_err(line6pcm->line6->ifcdev,
"driver and/or device bug: packet too large (%d > %d)\n",
fsize, line6pcm->max_packet_size);
}
if (line6pcm->pos_in_done + frames > length += fsize;
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) { /* the following assumes LINE6_ISO_PACKETS == 1: */
memcpy(runtime->dma_area + #if LINE6_BACKUP_MONITOR_SIGNAL
line6pcm->pos_in_done * memcpy(line6pcm->prev_fbuf, fbuf, fsize);
bytes_per_frame, fbuf, #else
len * bytes_per_frame); line6pcm->prev_fbuf = fbuf;
memcpy(runtime->dma_area, #endif
fbuf + len * bytes_per_frame, line6pcm->prev_fsize = fsize;
(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);
}
line6pcm->pos_in_done += frames; #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
if (line6pcm->pos_in_done >= runtime->buffer_size) if (!(line6pcm->flags & MASK_PCM_IMPULSE))
line6pcm->pos_in_done -= runtime->buffer_size; #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); 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; shutdown = 1;
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
if (!shutdown) { if (!shutdown) {
submit_audio_in_urb(substream); submit_audio_in_urb(line6pcm);
line6pcm->bytes_in += length; if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags))
if (line6pcm->bytes_in >= line6pcm->period_in) { line6_capture_check_period(line6pcm, length);
line6pcm->bytes_in -= line6pcm->period_in;
snd_pcm_period_elapsed(substream);
}
} }
} }
...@@ -294,54 +312,40 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, ...@@ -294,54 +312,40 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream,
return ret; return ret;
line6pcm->period_in = params_period_bytes(hw_params); 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; return 0;
} }
/* hw_free capture callback */ /* hw_free capture callback */
static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) 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); return snd_pcm_lib_free_pages(substream);
} }
/* trigger callback */ /* 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; int err;
line6pcm->count_in = 0;
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
if (!test_and_set_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) { #ifdef CONFIG_PM
err = submit_audio_in_all_urbs(substream); case SNDRV_PCM_TRIGGER_RESUME:
#endif
err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_CAPTURE);
if (err < 0) { if (err < 0)
clear_bit(BIT_RUNNING_CAPTURE,
&line6pcm->flags);
return err; return err;
}
}
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
if (test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) #ifdef CONFIG_PM
unlink_audio_in_urbs(line6pcm); case SNDRV_PCM_TRIGGER_SUSPEND:
#endif
err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_CAPTURE);
if (err < 0)
return err;
break; break;
...@@ -372,7 +376,7 @@ struct snd_pcm_ops snd_line6_capture_ops = { ...@@ -372,7 +376,7 @@ struct snd_pcm_ops snd_line6_capture_ops = {
.pointer = snd_line6_capture_pointer, .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; 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -13,20 +13,21 @@ ...@@ -13,20 +13,21 @@
#define CAPTURE_H #define CAPTURE_H
#include "driver.h"
#include <sound/pcm.h> #include <sound/pcm.h>
#include "driver.h"
#include "pcm.h" #include "pcm.h"
extern struct snd_pcm_ops snd_line6_capture_ops; extern struct snd_pcm_ops snd_line6_capture_ops;
extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf,
extern int create_audio_in_urbs(struct snd_line6_pcm *line6pcm); int fsize);
extern int snd_line6_capture_trigger(struct snd_pcm_substream *substream, extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm);
int cmd); extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm);
extern void unlink_wait_clear_audio_in_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 #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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -9,11 +9,10 @@ ...@@ -9,11 +9,10 @@
* *
*/ */
#include "driver.h"
#include <linux/usb.h> #include <linux/usb.h>
#include "control.h" #include "control.h"
#include "driver.h"
#include "pod.h" #include "pod.h"
#include "usbdefs.h" #include "usbdefs.h"
#include "variax.h" #include "variax.h"
...@@ -45,7 +44,7 @@ static ssize_t pod_get_param_int(struct device *dev, char *buf, int param) ...@@ -45,7 +44,7 @@ static ssize_t pod_get_param_int(struct device *dev, char *buf, int param)
{ {
struct usb_interface *interface = to_usb_interface(dev); struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_pod *pod = usb_get_intfdata(interface); 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) if (retval < 0)
return retval; return retval;
return sprintf(buf, "%d\n", pod->prog_data.control[param]); return sprintf(buf, "%d\n", pod->prog_data.control[param]);
...@@ -63,7 +62,7 @@ static ssize_t pod_set_param_int(struct device *dev, const char *buf, ...@@ -63,7 +62,7 @@ static ssize_t pod_set_param_int(struct device *dev, const char *buf,
if (retval) if (retval)
return retval; return retval;
pod_transmit_parameter(pod, param, value); line6_pod_transmit_parameter(pod, param, value);
return count; return count;
} }
...@@ -71,7 +70,7 @@ static ssize_t variax_get_param_int(struct device *dev, char *buf, int param) ...@@ -71,7 +70,7 @@ static ssize_t variax_get_param_int(struct device *dev, char *buf, int param)
{ {
struct usb_interface *interface = to_usb_interface(dev); struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_variax *variax = usb_get_intfdata(interface); 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) if (retval < 0)
return retval; return retval;
return sprintf(buf, "%d\n", variax->model_data.control[param]); return sprintf(buf, "%d\n", variax->model_data.control[param]);
...@@ -80,12 +79,11 @@ static ssize_t variax_get_param_int(struct device *dev, char *buf, int param) ...@@ -80,12 +79,11 @@ static ssize_t variax_get_param_int(struct device *dev, char *buf, int param)
static ssize_t variax_get_param_float(struct device *dev, char *buf, int param) 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 We do our own floating point handling here since at the time
kernel are problematic for at least two reasons: - many distros this code was written (Jan 2006) it was highly discouraged to
are still shipped with binary kernels optimized for the ancient use floating point arithmetic in the kernel. If you think that
80386 without FPU this no longer applies, feel free to replace this by generic
- there isn't a printf("%f") floating point code.
(see http://www.kernelthread.com/publications/faq/335.html)
*/ */
static const int BIAS = 0x7f; static const int BIAS = 0x7f;
...@@ -97,7 +95,7 @@ static ssize_t variax_get_param_float(struct device *dev, char *buf, int param) ...@@ -97,7 +95,7 @@ static ssize_t variax_get_param_float(struct device *dev, char *buf, int param)
struct usb_interface *interface = to_usb_interface(dev); struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_variax *variax = usb_get_intfdata(interface); struct usb_line6_variax *variax = usb_get_intfdata(interface);
const unsigned char *p = variax->model_data.control + param; 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) if (retval < 0)
return retval; return retval;
...@@ -530,7 +528,7 @@ static DEVICE_ATTR(mix1, S_IRUGO, variax_get_mix1, line6_nop_write); ...@@ -530,7 +528,7 @@ static DEVICE_ATTR(mix1, S_IRUGO, variax_get_mix1, line6_nop_write);
static DEVICE_ATTR(pickup_wiring, S_IRUGO, variax_get_pickup_wiring, static DEVICE_ATTR(pickup_wiring, S_IRUGO, variax_get_pickup_wiring,
line6_nop_write); 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; int err;
CHECK_RETURN(device_create_file(dev, &dev_attr_tweak)); CHECK_RETURN(device_create_file(dev, &dev_attr_tweak));
...@@ -733,9 +731,10 @@ int pod_create_files(int firmware, int type, struct device *dev) ...@@ -733,9 +731,10 @@ int pod_create_files(int firmware, int type, struct device *dev)
(dev, &dev_attr_band_6_gain__bass)); (dev, &dev_attr_band_6_gain__bass));
return 0; 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_tweak);
device_remove_file(dev, &dev_attr_wah_position); device_remove_file(dev, &dev_attr_wah_position);
...@@ -908,9 +907,10 @@ void pod_remove_files(int firmware, int type, struct device *dev) ...@@ -908,9 +907,10 @@ void pod_remove_files(int firmware, int type, struct device *dev)
if (firmware >= 200) if (firmware >= 200)
device_remove_file(dev, &dev_attr_band_6_gain__bass); 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; int err;
CHECK_RETURN(device_create_file(dev, &dev_attr_body)); CHECK_RETURN(device_create_file(dev, &dev_attr_body));
...@@ -954,9 +954,10 @@ int variax_create_files(int firmware, int type, struct device *dev) ...@@ -954,9 +954,10 @@ int variax_create_files(int firmware, int type, struct device *dev)
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_wiring)); CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_wiring));
return 0; 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_body);
device_remove_file(dev, &dev_attr_pickup1_enable); device_remove_file(dev, &dev_attr_pickup1_enable);
...@@ -998,4 +999,5 @@ void variax_remove_files(int firmware, int type, struct device *dev) ...@@ -998,4 +999,5 @@ void variax_remove_files(int firmware, int type, struct device *dev)
device_remove_file(dev, &dev_attr_mix1); device_remove_file(dev, &dev_attr_mix1);
device_remove_file(dev, &dev_attr_pickup_wiring); 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -12,20 +12,21 @@ ...@@ -12,20 +12,21 @@
#ifndef LINE6_CONTROL_H #ifndef LINE6_CONTROL_H
#define LINE6_CONTROL_H #define LINE6_CONTROL_H
/** /**
List of PODxt Pro controls. List of PODxt Pro controls.
See Appendix C of the "PODxt (Pro) Pilot's Handbook" by Line6. See Appendix C of the "PODxt (Pro) Pilot's Handbook" by Line6.
Comments after the number refer to the PODxt Pro firmware version required Comments after the number refer to the PODxt Pro firmware version required
for this feature. 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 { enum {
POD_tweak = 1, POD_tweak = 1,
POD_wah_position = 4, POD_wah_position = 4,
POD_compression_gain = 5, /* device: LINE6_BITS_PODXTALL */
/* device: LINE6_BITS_PODXTALL */
POD_compression_gain = 5,
POD_vol_pedal_position = 7, POD_vol_pedal_position = 7,
POD_compression_threshold = 9, POD_compression_threshold = 9,
POD_pan = 10, POD_pan = 10,
...@@ -33,33 +34,16 @@ enum { ...@@ -33,33 +34,16 @@ enum {
POD_amp_model = 12, /* firmware: 2.0 */ POD_amp_model = 12, /* firmware: 2.0 */
POD_drive = 13, POD_drive = 13,
POD_bass = 14, POD_bass = 14,
POD_mid = 15, /* device: LINE6_BITS_PODXTALL */
/* device: LINE6_BITS_PODXTALL */ POD_lowmid = 15, /* device: LINE6_BITS_BASSPODXTALL */
POD_mid = 15, POD_treble = 16, /* device: LINE6_BITS_PODXTALL */
POD_highmid = 16, /* device: LINE6_BITS_BASSPODXTALL */
/* device: LINE6_BITS_BASSPODXTALL */
POD_lowmid = 15,
/* device: LINE6_BITS_PODXTALL */
POD_treble = 16,
/* device: LINE6_BITS_BASSPODXTALL */
POD_highmid = 16,
POD_chan_vol = 17, POD_chan_vol = 17,
POD_reverb_mix = 18, /* device: LINE6_BITS_PODXTALL */
/* device: LINE6_BITS_PODXTALL */
POD_reverb_mix = 18,
POD_effect_setup = 19, POD_effect_setup = 19,
POD_band_1_frequency = 20, /* firmware: 2.0 */ POD_band_1_frequency = 20, /* firmware: 2.0 */
POD_presence = 21, /* device: LINE6_BITS_PODXTALL */
/* device: LINE6_BITS_PODXTALL */ POD_treble__bass = 21, /* device: LINE6_BITS_BASSPODXTALL */
POD_presence = 21,
/* device: LINE6_BITS_BASSPODXTALL */
POD_treble__bass = 21,
POD_noise_gate_enable = 22, POD_noise_gate_enable = 22,
POD_gate_threshold = 23, POD_gate_threshold = 23,
POD_gate_decay_time = 24, POD_gate_decay_time = 24,
...@@ -70,137 +54,78 @@ enum { ...@@ -70,137 +54,78 @@ enum {
POD_mod_param_1 = 29, POD_mod_param_1 = 29,
POD_delay_param_1 = 30, POD_delay_param_1 = 30,
POD_delay_param_1_note_value = 31, POD_delay_param_1_note_value = 31,
POD_band_2_frequency__bass = 32, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_2_frequency__bass = 32, /* firmware: 2.0 */
POD_delay_param_2 = 33, POD_delay_param_2 = 33,
POD_delay_volume_mix = 34, POD_delay_volume_mix = 34,
POD_delay_param_3 = 35, POD_delay_param_3 = 35,
POD_reverb_enable = 36, /* device: LINE6_BITS_PODXTALL */
/* device: LINE6_BITS_PODXTALL */ POD_reverb_type = 37, /* device: LINE6_BITS_PODXTALL */
POD_reverb_enable = 36, POD_reverb_decay = 38, /* device: LINE6_BITS_PODXTALL */
POD_reverb_type = 37, POD_reverb_tone = 39, /* device: LINE6_BITS_PODXTALL */
POD_reverb_decay = 38, POD_reverb_pre_delay = 40, /* device: LINE6_BITS_PODXTALL */
POD_reverb_tone = 39, POD_reverb_pre_post = 41, /* device: LINE6_BITS_PODXTALL */
POD_reverb_pre_delay = 40, POD_band_2_frequency = 42, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
POD_reverb_pre_post = 41, POD_band_3_frequency__bass = 42, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_band_2_frequency = 42,
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_3_frequency__bass = 42, /* firmware: 2.0 */
POD_wah_enable = 43, POD_wah_enable = 43,
POD_modulation_lo_cut = 44, /* device: LINE6_BITS_BASSPODXTALL */
/* device: LINE6_BITS_BASSPODXTALL */ POD_delay_reverb_lo_cut = 45, /* device: LINE6_BITS_BASSPODXTALL */
POD_modulation_lo_cut = 44, POD_volume_pedal_minimum = 46, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
POD_delay_reverb_lo_cut = 45, POD_eq_pre_post = 46, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
/* 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_volume_pre_post = 47, POD_volume_pre_post = 47,
POD_di_model = 48, /* device: LINE6_BITS_BASSPODXTALL */
/* device: LINE6_BITS_BASSPODXTALL */ POD_di_delay = 49, /* device: LINE6_BITS_BASSPODXTALL */
POD_di_model = 48,
POD_di_delay = 49,
POD_mod_enable = 50, POD_mod_enable = 50,
POD_mod_param_1_note_value = 51, POD_mod_param_1_note_value = 51,
POD_mod_param_2 = 52, POD_mod_param_2 = 52,
POD_mod_param_3 = 53, POD_mod_param_3 = 53,
POD_mod_param_4 = 54, POD_mod_param_4 = 54,
POD_mod_param_5 = 55, /* device: LINE6_BITS_BASSPODXTALL */
/* device: LINE6_BITS_BASSPODXTALL */
POD_mod_param_5 = 55,
POD_mod_volume_mix = 56, POD_mod_volume_mix = 56,
POD_mod_pre_post = 57, POD_mod_pre_post = 57,
POD_modulation_model = 58, POD_modulation_model = 58,
POD_band_3_frequency = 60, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
/* device: LINE6_BITS_PODXTALL */ POD_band_4_frequency__bass = 60, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_band_3_frequency = 60, /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_4_frequency__bass = 60, /* firmware: 2.0 */
POD_mod_param_1_double_precision = 61, POD_mod_param_1_double_precision = 61,
POD_delay_param_1_double_precision = 62, POD_delay_param_1_double_precision = 62,
POD_eq_enable = 63, /* firmware: 2.0 */ POD_eq_enable = 63, /* firmware: 2.0 */
POD_tap = 64, POD_tap = 64,
POD_volume_tweak_pedal_assign = 65, POD_volume_tweak_pedal_assign = 65,
POD_band_5_frequency = 68, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_5_frequency = 68, /* firmware: 2.0 */
POD_tuner = 69, POD_tuner = 69,
POD_mic_selection = 70, POD_mic_selection = 70,
POD_cabinet_model = 71, POD_cabinet_model = 71,
POD_stomp_model = 75, POD_stomp_model = 75,
POD_roomlevel = 76, POD_roomlevel = 76,
POD_band_4_frequency = 77, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
/* device: LINE6_BITS_PODXTALL */ POD_band_6_frequency = 77, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_band_4_frequency = 77, /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_6_frequency = 77, /* firmware: 2.0 */
POD_stomp_param_1_note_value = 78, POD_stomp_param_1_note_value = 78,
POD_stomp_param_2 = 79, POD_stomp_param_2 = 79,
POD_stomp_param_3 = 80, POD_stomp_param_3 = 80,
POD_stomp_param_4 = 81, POD_stomp_param_4 = 81,
POD_stomp_param_5 = 82, POD_stomp_param_5 = 82,
POD_stomp_param_6 = 83, POD_stomp_param_6 = 83,
POD_amp_switch_select = 84, /* device: LINE6_BITS_LIVE */
/* device: LINE6_BITS_LIVE */
POD_amp_switch_select = 84,
POD_delay_param_4 = 85, POD_delay_param_4 = 85,
POD_delay_param_5 = 86, POD_delay_param_5 = 86,
POD_delay_pre_post = 87, POD_delay_pre_post = 87,
POD_delay_model = 88, /* device: LINE6_BITS_PODXTALL */
/* device: LINE6_BITS_PODXTALL */ POD_delay_verb_model = 88, /* device: LINE6_BITS_BASSPODXTALL */
POD_delay_model = 88,
/* device: LINE6_BITS_BASSPODXTALL */
POD_delay_verb_model = 88,
POD_tempo_msb = 89, POD_tempo_msb = 89,
POD_tempo_lsb = 90, POD_tempo_lsb = 90,
POD_wah_model = 91, /* firmware: 3.0 */ POD_wah_model = 91, /* firmware: 3.0 */
POD_bypass_volume = 105, /* firmware: 2.14 */ POD_bypass_volume = 105, /* firmware: 2.14 */
POD_fx_loop_on_off = 107, /* device: LINE6_BITS_PRO */
/* device: LINE6_BITS_PRO */
POD_fx_loop_on_off = 107,
POD_tweak_param_select = 108, POD_tweak_param_select = 108,
POD_amp1_engage = 111, POD_amp1_engage = 111,
POD_band_1_gain = 114, /* firmware: 2.0 */ POD_band_1_gain = 114, /* firmware: 2.0 */
POD_band_2_gain__bass = 115, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */ POD_band_2_gain = 116, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
POD_band_2_gain__bass = 115, /* 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 */
/* device: LINE6_BITS_PODXTALL */ POD_band_4_gain__bass = 117, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_band_2_gain = 116, /* 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 */
/* device: LINE6_BITS_BASSPODXTALL */ POD_band_6_gain__bass = 119 /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
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 */
}; };
/** /**
...@@ -218,8 +143,7 @@ enum { ...@@ -218,8 +143,7 @@ enum {
VARIAX_pickup2_position = 23, /* type: 24 bit float */ VARIAX_pickup2_position = 23, /* type: 24 bit float */
VARIAX_pickup2_angle = 26, /* type: 24 bit float */ VARIAX_pickup2_angle = 26, /* type: 24 bit float */
VARIAX_pickup2_level = 29, /* type: 24 bit float */ VARIAX_pickup2_level = 29, /* type: 24 bit float */
VARIAX_pickup_phase = 32, /* 0: in phase, VARIAX_pickup_phase = 32, /* 0: in phase, 1: out of phase */
1: out of phase */
VARIAX_capacitance = 33, /* type: 24 bit float */ VARIAX_capacitance = 33, /* type: 24 bit float */
VARIAX_tone_resistance = 36, /* type: 24 bit float */ VARIAX_tone_resistance = 36, /* type: 24 bit float */
VARIAX_volume_resistance = 39, /* type: 24 bit float */ VARIAX_volume_resistance = 39, /* type: 24 bit float */
...@@ -258,10 +182,10 @@ enum { ...@@ -258,10 +182,10 @@ enum {
}; };
extern int pod_create_files(int firmware, int type, struct device *dev); extern int line6_pod_create_files(int firmware, int type, struct device *dev);
extern void pod_remove_files(int firmware, int type, struct device *dev); extern void line6_pod_remove_files(int firmware, int type, struct device *dev);
extern int variax_create_files(int firmware, int type, struct device *dev); extern int line6_variax_create_files(int firmware, int type, struct device *dev);
extern void variax_remove_files(int firmware, int type, struct device *dev); extern void line6_variax_remove_files(int firmware, int type, struct device *dev);
#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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
* *
*/ */
#include "driver.h"
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -19,6 +17,7 @@ ...@@ -19,6 +17,7 @@
#include "audio.h" #include "audio.h"
#include "capture.h" #include "capture.h"
#include "control.h" #include "control.h"
#include "driver.h"
#include "midi.h" #include "midi.h"
#include "playback.h" #include "playback.h"
#include "pod.h" #include "pod.h"
...@@ -30,7 +29,7 @@ ...@@ -30,7 +29,7 @@
#define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>" #define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>"
#define DRIVER_DESC "Line6 USB Driver" #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 */ /* table of devices that work with this driver */
...@@ -40,6 +39,9 @@ static const struct usb_device_id line6_id_table[] = { ...@@ -40,6 +39,9 @@ static const struct usb_device_id line6_id_table[] = {
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTPRO) }, { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTPRO) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_GUITARPORT) }, { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_GUITARPORT) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_POCKETPOD) }, { 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_PODX3) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3LIVE) }, { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3LIVE) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT) }, { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT) },
...@@ -54,30 +56,48 @@ static const struct usb_device_id line6_id_table[] = { ...@@ -54,30 +56,48 @@ static const struct usb_device_id line6_id_table[] = {
MODULE_DEVICE_TABLE(usb, line6_id_table); MODULE_DEVICE_TABLE(usb, line6_id_table);
static struct line6_properties line6_properties_table[] = { static struct line6_properties line6_properties_table[] = {
{ "BassPODxt", LINE6_BIT_BASSPODXT, LINE6_BIT_CONTROL_PCM }, { "BassPODxt", "BassPODxt", LINE6_BIT_BASSPODXT, LINE6_BIT_CONTROL_PCM_HWMON },
{ "BassPODxt Live", LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM }, { "BassPODxtLive", "BassPODxt Live", LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM_HWMON },
{ "BassPODxt Pro", LINE6_BIT_BASSPODXTPRO, LINE6_BIT_CONTROL_PCM }, { "BassPODxtPro", "BassPODxt Pro", LINE6_BIT_BASSPODXTPRO, LINE6_BIT_CONTROL_PCM_HWMON },
{ "GuitarPort", LINE6_BIT_GUITARPORT, LINE6_BIT_PCM }, { "GuitarPort", "GuitarPort", LINE6_BIT_GUITARPORT, LINE6_BIT_PCM },
{ "Pocket POD", LINE6_BIT_POCKETPOD, LINE6_BIT_CONTROL_PCM }, { "PocketPOD", "Pocket POD", LINE6_BIT_POCKETPOD, LINE6_BIT_CONTROL },
{ "POD X3", LINE6_BIT_PODX3, LINE6_BIT_PCM }, { "PODStudioGX", "POD Studio GX", LINE6_BIT_PODSTUDIO_GX, LINE6_BIT_PCM },
{ "POD X3 Live", LINE6_BIT_PODX3LIVE, LINE6_BIT_PCM }, { "PODStudioUX1", "POD Studio UX1", LINE6_BIT_PODSTUDIO_UX1, LINE6_BIT_PCM },
{ "PODxt", LINE6_BIT_PODXT, LINE6_BIT_CONTROL_PCM }, { "PODStudioUX2", "POD Studio UX2", LINE6_BIT_PODSTUDIO_UX2, LINE6_BIT_PCM },
{ "PODxt Live", LINE6_BIT_PODXTLIVE, LINE6_BIT_CONTROL_PCM }, { "PODX3", "POD X3", LINE6_BIT_PODX3, LINE6_BIT_PCM },
{ "PODxt Pro", LINE6_BIT_PODXTPRO, LINE6_BIT_CONTROL_PCM }, { "PODX3Live", "POD X3 Live", LINE6_BIT_PODX3LIVE, LINE6_BIT_PCM },
{ "TonePort GX", LINE6_BIT_TONEPORT_GX, LINE6_BIT_PCM }, { "PODxt", "PODxt", LINE6_BIT_PODXT, LINE6_BIT_CONTROL_PCM_HWMON },
{ "TonePort UX1", LINE6_BIT_TONEPORT_UX1, LINE6_BIT_PCM }, { "PODxtLive", "PODxt Live", LINE6_BIT_PODXTLIVE, LINE6_BIT_CONTROL_PCM_HWMON },
{ "TonePort UX2", LINE6_BIT_TONEPORT_UX2, LINE6_BIT_PCM }, { "PODxtPro", "PODxt Pro", LINE6_BIT_PODXTPRO, LINE6_BIT_CONTROL_PCM_HWMON },
{ "Variax Workbench", LINE6_BIT_VARIAX, LINE6_BIT_CONTROL } { "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. 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 usb_line6 *line6_devices[LINE6_MAX_DEVICES];
struct workqueue_struct *line6_workqueue;
/** /**
...@@ -104,15 +124,25 @@ static int line6_send_raw_message_async_part(struct message *msg, ...@@ -104,15 +124,25 @@ static int line6_send_raw_message_async_part(struct message *msg,
*/ */
static int line6_start_listen(struct usb_line6 *line6) static int line6_start_listen(struct usb_line6 *line6)
{ {
int err;
usb_fill_int_urb(line6->urb_listen, line6->usbdev, usb_fill_int_urb(line6->urb_listen, line6->usbdev,
usb_rcvintpipe(line6->usbdev, line6->ep_control_read), usb_rcvintpipe(line6->usbdev, line6->ep_control_read),
line6->buffer_listen, LINE6_BUFSIZE_LISTEN, line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
line6_data_received, line6, line6->interval); line6_data_received, line6, line6->interval);
line6->urb_listen->actual_length = 0; 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;
} }
#if DO_DUMP_ANY /*
Stop listening on endpoint.
*/
static void line6_stop_listen(struct usb_line6 *line6)
{
usb_kill_urb(line6->urb_listen);
}
#ifdef CONFIG_LINE6_USB_DUMP_ANY
/* /*
Write hexdump to syslog. Write hexdump to syslog.
*/ */
...@@ -152,7 +182,7 @@ void line6_write_hexdump(struct usb_line6 *line6, char dir, ...@@ -152,7 +182,7 @@ void line6_write_hexdump(struct usb_line6 *line6, char dir,
} }
#endif #endif
#if DO_DUMP_URB_RECEIVE #ifdef CONFIG_LINE6_USB_DUMP_CTRL
/* /*
Dump URB data to syslog. Dump URB data to syslog.
*/ */
...@@ -169,19 +199,19 @@ static void line6_dump_urb(struct urb *urb) ...@@ -169,19 +199,19 @@ static void line6_dump_urb(struct urb *urb)
#endif #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 line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
int size) int size)
{ {
int i, done = 0; 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); line6_write_hexdump(line6, 'S', buffer, size);
#endif #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; const char *frag_buf = buffer + i;
int frag_size = min(line6->max_packet_size, size - i); int frag_size = min(line6->max_packet_size, size - i);
int retval; int retval;
...@@ -190,7 +220,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, ...@@ -190,7 +220,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
usb_sndintpipe(line6->usbdev, usb_sndintpipe(line6->usbdev,
line6->ep_control_write), line6->ep_control_write),
(char *)frag_buf, frag_size, (char *)frag_buf, frag_size,
&actual_size, LINE6_TIMEOUT * HZ); &partial, LINE6_TIMEOUT * HZ);
if (retval) { if (retval) {
dev_err(line6->ifcdev, dev_err(line6->ifcdev,
...@@ -198,7 +228,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, ...@@ -198,7 +228,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
break; break;
} }
done += actual_size; done += frag_size;
} }
return done; return done;
...@@ -234,7 +264,7 @@ static int line6_send_raw_message_async_part(struct message *msg, ...@@ -234,7 +264,7 @@ static int line6_send_raw_message_async_part(struct message *msg,
(char *)msg->buffer + done, bytes, (char *)msg->buffer + done, bytes,
line6_async_request_sent, msg, line6->interval); 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); line6_write_hexdump(line6, 'S', (char *)msg->buffer + done, bytes);
#endif #endif
...@@ -252,6 +282,17 @@ static int line6_send_raw_message_async_part(struct message *msg, ...@@ -252,6 +282,17 @@ static int line6_send_raw_message_async_part(struct message *msg,
return 0; 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. Asynchronously send raw message.
*/ */
...@@ -288,6 +329,14 @@ int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, ...@@ -288,6 +329,14 @@ int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer,
return line6_send_raw_message_async_part(msg, urb); 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. Send sysex message in pieces of wMaxPacketSize bytes.
*/ */
...@@ -297,6 +346,15 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, ...@@ -297,6 +346,15 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer,
return line6_send_raw_message(line6, buffer, size + SYSEX_EXTRA_SIZE) - SYSEX_EXTRA_SIZE; 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. Allocate buffer for sysex message and prepare header.
@param code sysex message code @param code sysex message code
...@@ -305,7 +363,7 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, ...@@ -305,7 +363,7 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer,
char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2,
int size) int size)
{ {
char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_KERNEL); char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC);
if (!buffer) { if (!buffer) {
dev_err(line6->ifcdev, "out of memory\n"); dev_err(line6->ifcdev, "out of memory\n");
...@@ -332,29 +390,29 @@ static void line6_data_received(struct urb *urb) ...@@ -332,29 +390,29 @@ static void line6_data_received(struct urb *urb)
if (urb->status == -ESHUTDOWN) if (urb->status == -ESHUTDOWN)
return; return;
#if DO_DUMP_URB_RECEIVE #ifdef CONFIG_LINE6_USB_DUMP_CTRL
line6_dump_urb(urb); line6_dump_urb(urb);
#endif #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) { 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)); DEBUG_MESSAGES(dev_err(line6->ifcdev, "%d %d buffer overflow - message skipped\n", done, urb->actual_length));
} }
for (;;) { 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) if (done == 0)
break; break;
/* MIDI input filter */ /* 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; continue;
line6->message_length = done; line6->message_length = done;
#if DO_DUMP_MIDI_RECEIVE #ifdef CONFIG_LINE6_USB_DUMP_MIDI
line6_write_hexdump(line6, 'r', line6->buffer_message, done); line6_write_hexdump(line6, 'r', line6->buffer_message, done);
#endif #endif
line6_midi_receive(line6, line6->buffer_message, done); line6_midi_receive(line6, line6->buffer_message, done);
...@@ -366,17 +424,17 @@ static void line6_data_received(struct urb *urb) ...@@ -366,17 +424,17 @@ static void line6_data_received(struct urb *urb)
case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXT:
case LINE6_DEVID_PODXTPRO: case LINE6_DEVID_PODXTPRO:
case LINE6_DEVID_POCKETPOD: case LINE6_DEVID_POCKETPOD:
pod_process_message((struct usb_line6_pod *)line6); line6_pod_process_message((struct usb_line6_pod *)line6);
break; break;
case LINE6_DEVID_PODXTLIVE: case LINE6_DEVID_PODXTLIVE:
switch (line6->interface_number) { switch (line6->interface_number) {
case PODXTLIVE_INTERFACE_POD: case PODXTLIVE_INTERFACE_POD:
pod_process_message((struct usb_line6_pod *)line6); line6_pod_process_message((struct usb_line6_pod *)line6);
break; break;
case PODXTLIVE_INTERFACE_VARIAX: case PODXTLIVE_INTERFACE_VARIAX:
variax_process_message((struct usb_line6_variax *)line6); line6_variax_process_message((struct usb_line6_variax *)line6);
break; break;
default: default:
...@@ -385,7 +443,7 @@ static void line6_data_received(struct urb *urb) ...@@ -385,7 +443,7 @@ static void line6_data_received(struct urb *urb)
break; break;
case LINE6_DEVID_VARIAX: case LINE6_DEVID_VARIAX:
variax_process_message((struct usb_line6_variax *)line6); line6_variax_process_message((struct usb_line6_variax *)line6);
break; break;
default: default:
...@@ -396,44 +454,17 @@ static void line6_data_received(struct urb *urb) ...@@ -396,44 +454,17 @@ static void line6_data_received(struct urb *urb)
line6_start_listen(line6); 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). Send channel number (i.e., switch to a different sound).
*/ */
int line6_send_program(struct usb_line6 *line6, int value) int line6_send_program(struct usb_line6 *line6, int value)
{ {
int retval;
unsigned char *buffer; unsigned char *buffer;
size_t len = 2; int partial;
buffer = kmalloc(2, GFP_KERNEL);
buffer = kmalloc(len, GFP_KERNEL);
if (!buffer) { if (!buffer) {
dev_err(line6->ifcdev, "out of memory\n"); dev_err(line6->ifcdev, "out of memory\n");
return -ENOMEM; return -ENOMEM;
...@@ -442,7 +473,20 @@ int line6_send_program(struct usb_line6 *line6, int value) ...@@ -442,7 +473,20 @@ int line6_send_program(struct usb_line6 *line6, int value)
buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST; buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST;
buffer[1] = value; buffer[1] = value;
return line6_send(line6, buffer, len); #ifdef CONFIG_LINE6_USB_DUMP_CTRL
line6_write_hexdump(line6, 'S', buffer, 2);
#endif
retval = usb_interrupt_msg(line6->usbdev,
usb_sndintpipe(line6->usbdev,
line6->ep_control_write),
buffer, 2, &partial, LINE6_TIMEOUT * HZ);
if (retval)
dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval);
kfree(buffer);
return retval;
} }
/* /*
...@@ -450,10 +494,12 @@ int line6_send_program(struct usb_line6 *line6, int value) ...@@ -450,10 +494,12 @@ int line6_send_program(struct usb_line6 *line6, int value)
*/ */
int line6_transmit_parameter(struct usb_line6 *line6, int param, int value) int line6_transmit_parameter(struct usb_line6 *line6, int param, int value)
{ {
int retval;
unsigned char *buffer; unsigned char *buffer;
size_t len = 3; int partial;
buffer = kmalloc(3, GFP_KERNEL);
buffer = kmalloc(len, GFP_KERNEL);
if (!buffer) { if (!buffer) {
dev_err(line6->ifcdev, "out of memory\n"); dev_err(line6->ifcdev, "out of memory\n");
return -ENOMEM; return -ENOMEM;
...@@ -463,7 +509,19 @@ int line6_transmit_parameter(struct usb_line6 *line6, int param, int value) ...@@ -463,7 +509,19 @@ int line6_transmit_parameter(struct usb_line6 *line6, int param, int value)
buffer[1] = param; buffer[1] = param;
buffer[2] = value; buffer[2] = value;
return line6_send(line6, buffer, len); #ifdef CONFIG_LINE6_USB_DUMP_CTRL
line6_write_hexdump(line6, 'S', buffer, 3);
#endif
retval = usb_interrupt_msg(line6->usbdev,
usb_sndintpipe(line6->usbdev, line6->ep_control_write),
buffer, 3, &partial, LINE6_TIMEOUT * HZ);
if (retval)
dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval);
kfree(buffer);
return retval;
} }
/* /*
...@@ -477,8 +535,7 @@ int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t dat ...@@ -477,8 +535,7 @@ int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t dat
/* query the serial number: */ /* query the serial number: */
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
| USB_DIR_OUT,
(datalen << 8) | 0x21, address, (datalen << 8) | 0x21, address,
NULL, 0, LINE6_TIMEOUT * HZ); NULL, 0, LINE6_TIMEOUT * HZ);
...@@ -599,7 +656,7 @@ ssize_t line6_nop_write(struct device *dev, struct device_attribute *attr, ...@@ -599,7 +656,7 @@ ssize_t line6_nop_write(struct device *dev, struct device_attribute *attr,
/* /*
"write" request on "raw" special file. "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, ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
...@@ -637,21 +694,6 @@ static void line6_destruct(struct usb_interface *interface) ...@@ -637,21 +694,6 @@ static void line6_destruct(struct usb_interface *interface)
kfree(line6); 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. Probe USB device.
*/ */
...@@ -674,10 +716,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ...@@ -674,10 +716,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
if (usbdev == NULL) if (usbdev == NULL)
return -ENODEV; return -ENODEV;
/* increment reference counters: */
usb_get_intf(interface);
usb_get_dev(usbdev);
/* we don't handle multiple configurations */ /* we don't handle multiple configurations */
if (usbdev->descriptor.bNumConfigurations != 1) { if (usbdev->descriptor.bNumConfigurations != 1) {
ret = -ENODEV; ret = -ENODEV;
...@@ -689,8 +727,8 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ...@@ -689,8 +727,8 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
u16 idVendor = le16_to_cpu(usbdev->descriptor.idVendor); u16 idVendor = le16_to_cpu(usbdev->descriptor.idVendor);
u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct); u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct);
if (idVendor == line6_id_table[devtype].idVendor if (idVendor == line6_id_table[devtype].idVendor &&
&& idProduct == line6_id_table[devtype].idProduct) idProduct == line6_id_table[devtype].idProduct)
break; break;
} }
...@@ -719,12 +757,23 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ...@@ -719,12 +757,23 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
switch (product) { switch (product) {
case LINE6_DEVID_BASSPODXTLIVE: case LINE6_DEVID_BASSPODXTLIVE:
case LINE6_DEVID_POCKETPOD:
case LINE6_DEVID_PODXTLIVE: case LINE6_DEVID_PODXTLIVE:
case LINE6_DEVID_VARIAX: case LINE6_DEVID_VARIAX:
alternate = 1; alternate = 1;
break; 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_PODX3:
case LINE6_DEVID_PODX3LIVE: case LINE6_DEVID_PODX3LIVE:
switch (interface_number) { switch (interface_number) {
...@@ -746,21 +795,27 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ...@@ -746,21 +795,27 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
alternate = 5; alternate = 5;
break; break;
case LINE6_DEVID_TONEPORT_GX:
case LINE6_DEVID_GUITARPORT: 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 */ alternate = 2; /* 1..4 seem to be ok */
break; break;
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2: case LINE6_DEVID_TONEPORT_UX2:
case LINE6_DEVID_PODSTUDIO_UX2:
switch (interface_number) { switch (interface_number) {
case 0: case 0:
/* defaults to 44.1kHz, 16-bit */ /* defaults to 44.1kHz, 16-bit */
alternate = 2; alternate = 2;
break; break;
case 1: case 1:
alternate = 0; /* don't know yet what this is ...
alternate = 1;
break; break;
*/
return -ENODEV;
default: default:
MISSING_CASE; MISSING_CASE;
} }
...@@ -783,7 +838,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ...@@ -783,7 +838,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
case LINE6_DEVID_BASSPODXT: case LINE6_DEVID_BASSPODXT:
case LINE6_DEVID_BASSPODXTLIVE: case LINE6_DEVID_BASSPODXTLIVE:
case LINE6_DEVID_BASSPODXTPRO: case LINE6_DEVID_BASSPODXTPRO:
case LINE6_DEVID_POCKETPOD:
case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXT:
case LINE6_DEVID_PODXTPRO: case LINE6_DEVID_PODXTPRO:
size = sizeof(struct usb_line6_pod); size = sizeof(struct usb_line6_pod);
...@@ -791,6 +845,12 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ...@@ -791,6 +845,12 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
ep_write = 0x03; ep_write = 0x03;
break; 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_PODX3:
case LINE6_DEVID_PODX3LIVE: case LINE6_DEVID_PODX3LIVE:
/* currently unused! */ /* currently unused! */
...@@ -799,6 +859,9 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ...@@ -799,6 +859,9 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
ep_write = 0x01; ep_write = 0x01;
break; 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_GX:
case LINE6_DEVID_TONEPORT_UX1: case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2: case LINE6_DEVID_TONEPORT_UX2:
...@@ -925,17 +988,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ...@@ -925,17 +988,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
case LINE6_DEVID_PODX3LIVE: case LINE6_DEVID_PODX3LIVE:
case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXT:
case LINE6_DEVID_PODXTPRO: case LINE6_DEVID_PODXTPRO:
ret = pod_init(interface, (struct usb_line6_pod *)line6); ret = line6_pod_init(interface, (struct usb_line6_pod *)line6);
break; break;
case LINE6_DEVID_PODXTLIVE: case LINE6_DEVID_PODXTLIVE:
switch (interface_number) { switch (interface_number) {
case PODXTLIVE_INTERFACE_POD: case PODXTLIVE_INTERFACE_POD:
ret = pod_init(interface, (struct usb_line6_pod *)line6); ret = line6_pod_init(interface, (struct usb_line6_pod *)line6);
break; break;
case PODXTLIVE_INTERFACE_VARIAX: case PODXTLIVE_INTERFACE_VARIAX:
ret = variax_init(interface, (struct usb_line6_variax *)line6); ret = line6_variax_init(interface, (struct usb_line6_variax *)line6);
break; break;
default: default:
...@@ -948,14 +1011,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ...@@ -948,14 +1011,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
break; break;
case LINE6_DEVID_VARIAX: case LINE6_DEVID_VARIAX:
ret = variax_init(interface, (struct usb_line6_variax *)line6); ret = line6_variax_init(interface, (struct usb_line6_variax *)line6);
break; 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_GX:
case LINE6_DEVID_TONEPORT_UX1: case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2: case LINE6_DEVID_TONEPORT_UX2:
case LINE6_DEVID_GUITARPORT: case LINE6_DEVID_GUITARPORT:
ret = toneport_init(interface, (struct usb_line6_toneport *)line6); ret = line6_toneport_init(interface, (struct usb_line6_toneport *)line6);
break; break;
default: default:
...@@ -971,10 +1037,23 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ...@@ -971,10 +1037,23 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
if (ret < 0) if (ret < 0)
goto err_destruct; goto err_destruct;
/* creation of additional special files should go here */
dev_info(&interface->dev, "Line6 %s now attached\n", dev_info(&interface->dev, "Line6 %s now attached\n",
line6->properties->name); line6->properties->name);
line6_devices[devnum] = line6; 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; return 0;
err_destruct: err_destruct:
...@@ -1000,6 +1079,8 @@ static void line6_disconnect(struct usb_interface *interface) ...@@ -1000,6 +1079,8 @@ static void line6_disconnect(struct usb_interface *interface)
if (usbdev == NULL) if (usbdev == NULL)
return; return;
/* removal of additional special files should go here */
sysfs_remove_link(&interface->dev.kobj, "usb_device"); sysfs_remove_link(&interface->dev.kobj, "usb_device");
interface_number = interface->cur_altsetting->desc.bInterfaceNumber; interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
...@@ -1007,7 +1088,7 @@ static void line6_disconnect(struct usb_interface *interface) ...@@ -1007,7 +1088,7 @@ static void line6_disconnect(struct usb_interface *interface)
if (line6 != NULL) { if (line6 != NULL) {
if (line6->urb_listen != NULL) if (line6->urb_listen != NULL)
usb_kill_urb(line6->urb_listen); line6_stop_listen(line6);
if (usbdev != line6->usbdev) if (usbdev != line6->usbdev)
dev_err(line6->ifcdev, dev_err(line6->ifcdev,
...@@ -1022,31 +1103,34 @@ static void line6_disconnect(struct usb_interface *interface) ...@@ -1022,31 +1103,34 @@ static void line6_disconnect(struct usb_interface *interface)
case LINE6_DEVID_PODX3LIVE: case LINE6_DEVID_PODX3LIVE:
case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXT:
case LINE6_DEVID_PODXTPRO: case LINE6_DEVID_PODXTPRO:
pod_disconnect(interface); line6_pod_disconnect(interface);
break; break;
case LINE6_DEVID_PODXTLIVE: case LINE6_DEVID_PODXTLIVE:
switch (interface_number) { switch (interface_number) {
case PODXTLIVE_INTERFACE_POD: case PODXTLIVE_INTERFACE_POD:
pod_disconnect(interface); line6_pod_disconnect(interface);
break; break;
case PODXTLIVE_INTERFACE_VARIAX: case PODXTLIVE_INTERFACE_VARIAX:
variax_disconnect(interface); line6_variax_disconnect(interface);
break; break;
} }
break; break;
case LINE6_DEVID_VARIAX: case LINE6_DEVID_VARIAX:
variax_disconnect(interface); line6_variax_disconnect(interface);
break; 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_GX:
case LINE6_DEVID_TONEPORT_UX1: case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2: case LINE6_DEVID_TONEPORT_UX2:
case LINE6_DEVID_GUITARPORT: case LINE6_DEVID_GUITARPORT:
toneport_disconnect(interface); line6_toneport_disconnect(interface);
break; break;
default: default:
...@@ -1055,25 +1139,88 @@ static void line6_disconnect(struct usb_interface *interface) ...@@ -1055,25 +1139,88 @@ static void line6_disconnect(struct usb_interface *interface)
dev_info(&interface->dev, "Line6 %s now disconnected\n", line6->properties->name); 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) if (line6_devices[i] == line6)
line6_devices[i] = NULL; line6_devices[i] = NULL;
} }
}
line6_destruct(interface); line6_destruct(interface);
/* decrement reference counters: */ /* decrement reference counters: */
usb_put_intf(interface); usb_put_intf(interface);
usb_put_dev(usbdev); usb_put_dev(usbdev);
}
#ifdef CONFIG_PM
line6_list_devices(); /*
Suspend Line6 device.
*/
static int line6_suspend(struct usb_interface *interface, pm_message_t message)
{
struct usb_line6 *line6 = usb_get_intfdata(interface);
struct snd_line6_pcm *line6pcm = line6->line6pcm;
snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
if (line6->properties->capabilities & LINE6_BIT_CONTROL)
line6_stop_listen(line6);
if (line6pcm != NULL) {
snd_pcm_suspend_all(line6pcm->pcm);
line6_pcm_disconnect(line6pcm);
line6pcm->flags = 0;
}
return 0;
} }
/*
Resume Line6 device.
*/
static int line6_resume(struct usb_interface *interface)
{
struct usb_line6 *line6 = usb_get_intfdata(interface);
if (line6->properties->capabilities & LINE6_BIT_CONTROL)
line6_start_listen(line6);
snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
return 0;
}
/*
Resume Line6 device after reset.
*/
static int line6_reset_resume(struct usb_interface *interface)
{
struct usb_line6 *line6 = usb_get_intfdata(interface);
switch (line6->usbdev->descriptor.idProduct) {
case LINE6_DEVID_PODSTUDIO_GX:
case LINE6_DEVID_PODSTUDIO_UX1:
case LINE6_DEVID_PODSTUDIO_UX2:
case LINE6_DEVID_TONEPORT_GX:
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2:
case LINE6_DEVID_GUITARPORT:
line6_toneport_reset_resume((struct usb_line6_toneport *)line6);
}
return line6_resume(interface);
}
#endif /* CONFIG_PM */
static struct usb_driver line6_driver = { static struct usb_driver line6_driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.probe = line6_probe, .probe = line6_probe,
.disconnect = line6_disconnect, .disconnect = line6_disconnect,
#ifdef CONFIG_PM
.suspend = line6_suspend,
.resume = line6_resume,
.reset_resume = line6_reset_resume,
#endif
.id_table = line6_id_table, .id_table = line6_id_table,
}; };
...@@ -1086,20 +1233,27 @@ static int __init line6_init(void) ...@@ -1086,20 +1233,27 @@ static int __init line6_init(void)
printk(KERN_INFO "%s driver version %s%s\n", printk(KERN_INFO "%s driver version %s%s\n",
DRIVER_NAME, DRIVER_VERSION, DRIVER_REVISION); 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--;) for (i = LINE6_MAX_DEVICES; i--;)
line6_devices[i] = NULL; line6_devices[i] = NULL;
retval = usb_register(&line6_driver); retval = usb_register(&line6_driver);
if (retval) if (retval) {
err("usb_register failed. Error number %d", 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; return retval;
} }
...@@ -1109,7 +1263,7 @@ static int __init line6_init(void) ...@@ -1109,7 +1263,7 @@ static int __init line6_init(void)
*/ */
static void __exit line6_exit(void) static void __exit line6_exit(void)
{ {
destroy_workqueue(line6_workqueue); kfree(line6_request_version);
usb_deregister(&line6_driver); 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -13,23 +13,24 @@ ...@@ -13,23 +13,24 @@
#define DRIVER_H #define DRIVER_H
#include "config.h"
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/wait.h>
#include <sound/core.h> #include <sound/core.h>
#include "midi.h" #include "midi.h"
#define DRIVER_NAME "line6usb" #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_TIMEOUT 1
#define LINE6_MAX_DEVICES 8 #define LINE6_MAX_DEVICES 8
#define LINE6_BUFSIZE_LISTEN 32 #define LINE6_BUFSIZE_LISTEN 32
#define LINE6_MESSAGE_MAXLEN 256 #define LINE6_MESSAGE_MAXLEN 256
/* /*
Line6 MIDI control commands Line6 MIDI control commands
*/ */
...@@ -54,6 +55,12 @@ ...@@ -54,6 +55,12 @@
#define LINE6_CHANNEL_MASK 0x0f #define LINE6_CHANNEL_MASK 0x0f
#ifdef CONFIG_LINE6_USB_DEBUG
#define DEBUG_MESSAGES(x) (x)
#else
#define DEBUG_MESSAGES(x)
#endif
#define MISSING_CASE \ #define MISSING_CASE \
printk(KERN_ERR "line6usb driver bug: missing case in %s:%d\n", \ printk(KERN_ERR "line6usb driver bug: missing case in %s:%d\n", \
...@@ -67,10 +74,14 @@ do { \ ...@@ -67,10 +74,14 @@ do { \
return err; \ return err; \
} while (0) } while (0)
#define CHECK_STARTUP_PROGRESS(x, n) \
if((x) >= (n)) \
return; \
x = (n);
extern const unsigned char line6_midi_id[3]; extern const unsigned char line6_midi_id[3];
extern struct usb_line6 *line6_devices[LINE6_MAX_DEVICES]; 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_DATA_OFS = sizeof(line6_midi_id) + 3;
static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4; static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4;
...@@ -80,8 +91,27 @@ static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4; ...@@ -80,8 +91,27 @@ static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4;
Common properties of Line6 devices. Common properties of Line6 devices.
*/ */
struct line6_properties { 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; const char *name;
/**
Bit identifying this device in the line6usb driver.
*/
int device_bit; int device_bit;
/**
Bit vector defining this device's capabilities in the
line6usb driver.
*/
int capabilities; int capabilities;
}; };
...@@ -191,14 +221,22 @@ extern int line6_send_raw_message_async(struct usb_line6 *line6, ...@@ -191,14 +221,22 @@ extern int line6_send_raw_message_async(struct usb_line6 *line6,
const char *buffer, int size); const char *buffer, int size);
extern int line6_send_sysex_message(struct usb_line6 *line6, extern int line6_send_sysex_message(struct usb_line6 *line6,
const char *buffer, int size); 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, extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count); 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, extern int line6_transmit_parameter(struct usb_line6 *line6, int param,
int value); 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, extern int line6_write_data(struct usb_line6 *line6, int address, void *data,
size_t datalen); size_t datalen);
#ifdef CONFIG_LINE6_USB_DUMP_ANY
extern void line6_write_hexdump(struct usb_line6 *line6, char dir, extern void line6_write_hexdump(struct usb_line6 *line6, char dir,
const unsigned char *buffer, int size); const unsigned char *buffer, int size);
#endif
#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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -9,10 +9,9 @@ ...@@ -9,10 +9,9 @@
* *
*/ */
#include "driver.h"
#include <linux/slab.h> #include <linux/slab.h>
#include "driver.h"
#include "dumprequest.h" #include "dumprequest.h"
...@@ -39,17 +38,17 @@ void line6_invalidate_current(struct line6_dump_request *l6dr) ...@@ -39,17 +38,17 @@ void line6_invalidate_current(struct line6_dump_request *l6dr)
void line6_dump_finished(struct line6_dump_request *l6dr) void line6_dump_finished(struct line6_dump_request *l6dr)
{ {
l6dr->in_progress = LINE6_DUMP_NONE; l6dr->in_progress = LINE6_DUMP_NONE;
wake_up_interruptible(&l6dr->wait); wake_up(&l6dr->wait);
} }
/* /*
Send an asynchronous channel dump request. Send an asynchronous channel dump request.
*/ */
int line6_dump_request_async(struct line6_dump_request *l6dr, 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; int ret;
line6_invalidate_current(l6dr); line6_dump_started(l6dr, dest);
ret = line6_send_raw_message_async(line6, l6dr->reqbufs[num].buffer, ret = line6_send_raw_message_async(line6, l6dr->reqbufs[num].buffer,
l6dr->reqbufs[num].length); l6dr->reqbufs[num].length);
...@@ -60,43 +59,27 @@ int line6_dump_request_async(struct line6_dump_request *l6dr, ...@@ -60,43 +59,27 @@ int line6_dump_request_async(struct line6_dump_request *l6dr,
} }
/* /*
Send an asynchronous dump request after a given interval. Wait for completion (interruptible).
*/ */
void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds, int line6_dump_wait_interruptible(struct line6_dump_request *l6dr)
void (*function)(unsigned long), void *data)
{ {
l6dr->timer.expires = jiffies + seconds * HZ; return wait_event_interruptible(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE);
l6dr->timer.function = function;
l6dr->timer.data = (unsigned long)data;
add_timer(&l6dr->timer);
} }
/* /*
Wait for completion. 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; return wait_event_timeout(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE, timeout);
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;
} }
/* /*
...@@ -123,7 +106,6 @@ int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf, ...@@ -123,7 +106,6 @@ int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf,
if (ret < 0) if (ret < 0)
return ret; return ret;
init_waitqueue_head(&l6dr->wait); init_waitqueue_head(&l6dr->wait);
init_timer(&l6dr->timer);
return 0; return 0;
} }
...@@ -148,6 +130,4 @@ void line6_dumpreq_destruct(struct line6_dump_request *l6dr) ...@@ -148,6 +130,4 @@ void line6_dumpreq_destruct(struct line6_dump_request *l6dr)
if (l6dr->reqbufs[0].buffer == NULL) if (l6dr->reqbufs[0].buffer == NULL)
return; return;
line6_dumpreq_destructbuf(l6dr, 0); 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <sound/core.h> #include <sound/core.h>
...@@ -55,16 +54,6 @@ struct line6_dump_request { ...@@ -55,16 +54,6 @@ struct line6_dump_request {
*/ */
int in_progress; 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 Dump request buffers
*/ */
...@@ -73,7 +62,7 @@ struct line6_dump_request { ...@@ -73,7 +62,7 @@ struct line6_dump_request {
extern void line6_dump_finished(struct line6_dump_request *l6dr); extern void line6_dump_finished(struct line6_dump_request *l6dr);
extern int line6_dump_request_async(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_dump_started(struct line6_dump_request *l6dr, int dest);
extern void line6_dumpreq_destruct(struct line6_dump_request *l6dr); extern void line6_dumpreq_destruct(struct line6_dump_request *l6dr);
extern void line6_dumpreq_destructbuf(struct line6_dump_request *l6dr, int num); extern void line6_dumpreq_destructbuf(struct line6_dump_request *l6dr, int num);
...@@ -82,9 +71,10 @@ extern int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf, ...@@ -82,9 +71,10 @@ extern int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf,
extern int line6_dumpreq_initbuf(struct line6_dump_request *l6dr, extern int line6_dumpreq_initbuf(struct line6_dump_request *l6dr,
const void *buf, size_t len, int num); const void *buf, size_t len, int num);
extern void line6_invalidate_current(struct line6_dump_request *l6dr); extern void line6_invalidate_current(struct line6_dump_request *l6dr);
extern void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds, extern void line6_dump_wait(struct line6_dump_request *l6dr);
void (*function)(unsigned long), void *data); extern int line6_dump_wait_interruptible(struct line6_dump_request *l6dr);
extern int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock); extern int line6_dump_wait_timeout(struct line6_dump_request *l6dr,
long timeout);
#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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -9,24 +9,18 @@ ...@@ -9,24 +9,18 @@
* *
*/ */
#include "driver.h"
#include <linux/usb.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/usb.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/rawmidi.h> #include <sound/rawmidi.h>
#include "audio.h" #include "audio.h"
#include "driver.h"
#include "midi.h" #include "midi.h"
#include "pod.h" #include "pod.h"
#include "usbdefs.h" #include "usbdefs.h"
#define USE_MIDIBUF 1
#define OUTPUT_DUMP_ONLY 0
#define line6_rawmidi_substream_midi(substream) \ #define line6_rawmidi_substream_midi(substream) \
((struct snd_line6_midi *)((substream)->rmidi->private_data)) ((struct snd_line6_midi *)((substream)->rmidi->private_data))
...@@ -61,26 +55,26 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream) ...@@ -61,26 +55,26 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags); spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags);
for (;;) { 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); done = snd_rawmidi_transmit_peek(substream, chunk, req);
if (done == 0) if (done == 0)
break; break;
#if DO_DUMP_MIDI_SEND #ifdef CONFIG_LINE6_USB_DUMP_MIDI
line6_write_hexdump(line6, 's', chunk, done); line6_write_hexdump(line6, 's', chunk, done);
#endif #endif
midibuf_write(mb, chunk, done); line6_midibuf_write(mb, chunk, done);
snd_rawmidi_transmit_ack(substream, done); snd_rawmidi_transmit_ack(substream, done);
} }
for (;;) { for (;;) {
done = midibuf_read(mb, chunk, line6->max_packet_size); done = line6_midibuf_read(mb, chunk, line6->max_packet_size);
if (done == 0) if (done == 0)
break; break;
if (midibuf_skip_message(mb, line6midi->midi_mask_transmit)) if (line6_midibuf_skip_message(mb, line6midi->midi_mask_transmit))
continue; continue;
send_midi_async(line6, chunk, done); send_midi_async(line6, chunk, done);
...@@ -115,7 +109,7 @@ static void midi_sent(struct urb *urb) ...@@ -115,7 +109,7 @@ static void midi_sent(struct urb *urb)
} }
if (num == 0) 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); spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags);
} }
...@@ -139,7 +133,7 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data, ...@@ -139,7 +133,7 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
return -ENOMEM; return -ENOMEM;
} }
#if DO_DUMP_URB_SEND #ifdef CONFIG_LINE6_USB_DUMP_CTRL
line6_write_hexdump(line6, 'S', data, length); line6_write_hexdump(line6, 'S', data, length);
#endif #endif
...@@ -176,7 +170,7 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data, ...@@ -176,7 +170,7 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
case LINE6_DEVID_PODXTLIVE: case LINE6_DEVID_PODXTLIVE:
case LINE6_DEVID_PODXTPRO: case LINE6_DEVID_PODXTPRO:
case LINE6_DEVID_POCKETPOD: case LINE6_DEVID_POCKETPOD:
pod_midi_postprocess((struct usb_line6_pod *)line6, data, line6_pod_midi_postprocess((struct usb_line6_pod *)line6, data,
length); length);
break; break;
...@@ -215,19 +209,8 @@ static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, ...@@ -215,19 +209,8 @@ static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream,
static void line6_midi_output_drain(struct snd_rawmidi_substream *substream) static void line6_midi_output_drain(struct snd_rawmidi_substream *substream)
{ {
struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6; struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6;
wait_queue_head_t *head = &line6->line6midi->send_wait; struct snd_line6_midi *midi = line6->line6midi;
DECLARE_WAITQUEUE(wait, current); wait_event_interruptible(midi->send_wait, midi->num_active_send_urbs == 0);
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);
} }
static int line6_midi_input_open(struct snd_rawmidi_substream *substream) static int line6_midi_input_open(struct snd_rawmidi_substream *substream)
...@@ -284,6 +267,7 @@ static int snd_line6_new_midi(struct snd_line6_midi *line6midi) ...@@ -284,6 +267,7 @@ static int snd_line6_new_midi(struct snd_line6_midi *line6midi)
rmidi->private_data = line6midi; rmidi->private_data = line6midi;
rmidi->private_free = line6_cleanup_midi; rmidi->private_free = line6_cleanup_midi;
strcpy(rmidi->id, line6midi->line6->properties->id);
strcpy(rmidi->name, line6midi->line6->properties->name); strcpy(rmidi->name, line6midi->line6->properties->name);
rmidi->info_flags = rmidi->info_flags =
...@@ -371,8 +355,8 @@ static int snd_line6_midi_free(struct snd_device *device) ...@@ -371,8 +355,8 @@ static int snd_line6_midi_free(struct snd_device *device)
struct snd_line6_midi *line6midi = device->device_data; 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_transmit);
device_remove_file(line6midi->line6->ifcdev, &dev_attr_midi_mask_receive); device_remove_file(line6midi->line6->ifcdev, &dev_attr_midi_mask_receive);
midibuf_destroy(&line6midi->midibuf_in); line6_midibuf_destroy(&line6midi->midibuf_in);
midibuf_destroy(&line6midi->midibuf_out); line6_midibuf_destroy(&line6midi->midibuf_out);
return 0; return 0;
} }
...@@ -396,11 +380,11 @@ int line6_init_midi(struct usb_line6 *line6) ...@@ -396,11 +380,11 @@ int line6_init_midi(struct usb_line6 *line6)
if (line6midi == NULL) if (line6midi == NULL)
return -ENOMEM; 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) if (err < 0)
return err; 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) if (err < 0)
return err; 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
* *
*/ */
#include "config.h"
#include <linux/slab.h> #include <linux/slab.h>
#include "midibuf.h" #include "midibuf.h"
...@@ -35,13 +33,23 @@ static int midibuf_message_length(unsigned char code) ...@@ -35,13 +33,23 @@ static int midibuf_message_length(unsigned char code)
} }
} }
void midibuf_reset(struct MidiBuffer *this) static int midibuf_is_empty(struct MidiBuffer *this)
{
return (this->pos_read == this->pos_write) && !this->full;
}
static int midibuf_is_full(struct MidiBuffer *this)
{
return this->full;
}
void line6_midibuf_reset(struct MidiBuffer *this)
{ {
this->pos_read = this->pos_write = this->full = 0; this->pos_read = this->pos_write = this->full = 0;
this->command_prev = -1; 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->buf = kmalloc(size, GFP_KERNEL);
...@@ -50,28 +58,18 @@ int midibuf_init(struct MidiBuffer *this, int size, int split) ...@@ -50,28 +58,18 @@ int midibuf_init(struct MidiBuffer *this, int size, int split)
this->size = size; this->size = size;
this->split = split; this->split = split;
midibuf_reset(this); line6_midibuf_reset(this);
return 0; 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 " printk(KERN_DEBUG "midibuf size=%d split=%d pos_read=%d pos_write=%d "
"full=%d command_prev=%02x\n", this->size, this->split, "full=%d command_prev=%02x\n", this->size, this->split,
this->pos_read, this->pos_write, this->full, this->command_prev); this->pos_read, this->pos_write, this->full, this->command_prev);
} }
static int midibuf_is_empty(struct MidiBuffer *this) int line6_midibuf_bytes_free(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)
{ {
return return
midibuf_is_full(this) ? midibuf_is_full(this) ?
...@@ -79,7 +77,7 @@ int midibuf_bytes_free(struct MidiBuffer *this) ...@@ -79,7 +77,7 @@ int midibuf_bytes_free(struct MidiBuffer *this)
(this->pos_read - this->pos_write + this->size - 1) % this->size + 1; (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 return
midibuf_is_empty(this) ? midibuf_is_empty(this) ?
...@@ -87,7 +85,7 @@ int midibuf_bytes_used(struct MidiBuffer *this) ...@@ -87,7 +85,7 @@ int midibuf_bytes_used(struct MidiBuffer *this)
(this->pos_write - this->pos_read + this->size - 1) % this->size + 1; (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 bytes_free;
int length1, length2; int length1, length2;
...@@ -102,7 +100,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length) ...@@ -102,7 +100,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
skip_active_sense = 1; skip_active_sense = 1;
} }
bytes_free = midibuf_bytes_free(this); bytes_free = line6_midibuf_bytes_free(this);
if (length > bytes_free) if (length > bytes_free)
length = bytes_free; length = bytes_free;
...@@ -129,7 +127,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length) ...@@ -129,7 +127,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
return length + skip_active_sense; 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 bytes_used;
int length1, length2; int length1, length2;
...@@ -145,7 +143,7 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) ...@@ -145,7 +143,7 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
if (midibuf_is_empty(this)) if (midibuf_is_empty(this))
return 0; return 0;
bytes_used = midibuf_bytes_used(this); bytes_used = line6_midibuf_bytes_used(this);
if (length > bytes_used) if (length > bytes_used)
length = bytes_used; length = bytes_used;
...@@ -232,9 +230,9 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) ...@@ -232,9 +230,9 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
return length + repeat; 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) if (length > bytes_used)
length = bytes_used; length = bytes_used;
...@@ -244,7 +242,7 @@ int midibuf_ignore(struct MidiBuffer *this, int length) ...@@ -244,7 +242,7 @@ int midibuf_ignore(struct MidiBuffer *this, int length)
return length; return length;
} }
int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask) int line6_midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
{ {
int cmd = this->command_prev; int cmd = this->command_prev;
...@@ -255,7 +253,7 @@ int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask) ...@@ -255,7 +253,7 @@ int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
return 0; return 0;
} }
void midibuf_destroy(struct MidiBuffer *this) void line6_midibuf_destroy(struct MidiBuffer *this)
{ {
kfree(this->buf); kfree(this->buf);
this->buf = NULL; 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -23,16 +23,16 @@ struct MidiBuffer { ...@@ -23,16 +23,16 @@ struct MidiBuffer {
}; };
extern int midibuf_bytes_used(struct MidiBuffer *mb); extern int line6_midibuf_bytes_used(struct MidiBuffer *mb);
extern int midibuf_bytes_free(struct MidiBuffer *mb); extern int line6_midibuf_bytes_free(struct MidiBuffer *mb);
extern void midibuf_destroy(struct MidiBuffer *mb); extern void line6_midibuf_destroy(struct MidiBuffer *mb);
extern int midibuf_ignore(struct MidiBuffer *mb, int length); extern int line6_midibuf_ignore(struct MidiBuffer *mb, int length);
extern int midibuf_init(struct MidiBuffer *mb, int size, int split); extern int line6_midibuf_init(struct MidiBuffer *mb, int size, int split);
extern int midibuf_read(struct MidiBuffer *mb, unsigned char *data, int length); extern int line6_midibuf_read(struct MidiBuffer *mb, unsigned char *data, int length);
extern void midibuf_reset(struct MidiBuffer *mb); extern void line6_midibuf_reset(struct MidiBuffer *mb);
extern int midibuf_skip_message(struct MidiBuffer *mb, unsigned short mask); extern int line6_midibuf_skip_message(struct MidiBuffer *mb, unsigned short mask);
extern void midibuf_status(struct MidiBuffer *mb); extern void line6_midibuf_status(struct MidiBuffer *mb);
extern int midibuf_write(struct MidiBuffer *mb, unsigned char *data, extern int line6_midibuf_write(struct MidiBuffer *mb, unsigned char *data,
int length); int length);
......
/* /*
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -9,10 +9,7 @@ ...@@ -9,10 +9,7 @@
* *
*/ */
#include "driver.h"
#include <linux/slab.h> #include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/pcm.h> #include <sound/pcm.h>
...@@ -20,10 +17,176 @@ ...@@ -20,10 +17,176 @@
#include "audio.h" #include "audio.h"
#include "capture.h" #include "capture.h"
#include "driver.h"
#include "playback.h" #include "playback.h"
#include "pod.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 */ /* trigger callback */
int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
{ {
...@@ -38,7 +201,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -38,7 +201,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
snd_pcm_group_for_each_entry(s, substream) { snd_pcm_group_for_each_entry(s, substream) {
switch (s->stream) { switch (s->stream) {
case SNDRV_PCM_STREAM_PLAYBACK: case SNDRV_PCM_STREAM_PLAYBACK:
err = snd_line6_playback_trigger(s, cmd); err = snd_line6_playback_trigger(line6pcm, cmd);
if (err < 0) { if (err < 0) {
spin_unlock_irqrestore(&line6pcm->lock_trigger, spin_unlock_irqrestore(&line6pcm->lock_trigger,
...@@ -49,7 +212,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -49,7 +212,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
break; break;
case SNDRV_PCM_STREAM_CAPTURE: case SNDRV_PCM_STREAM_CAPTURE:
err = snd_line6_capture_trigger(s, cmd); err = snd_line6_capture_trigger(line6pcm, cmd);
if (err < 0) { if (err < 0) {
spin_unlock_irqrestore(&line6pcm->lock_trigger, spin_unlock_irqrestore(&line6pcm->lock_trigger,
...@@ -60,7 +223,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -60,7 +223,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
break; break;
default: default:
dev_err(s2m(substream), "Unknown stream direction %d\n", dev_err(line6pcm->line6->ifcdev, "Unknown stream direction %d\n",
s->stream); s->stream);
} }
} }
...@@ -70,7 +233,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -70,7 +233,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
} }
/* control info callback */ /* control info callback */
static int snd_line6_control_info(struct snd_kcontrol *kcontrol, static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
{ {
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
...@@ -81,28 +244,28 @@ static int snd_line6_control_info(struct snd_kcontrol *kcontrol, ...@@ -81,28 +244,28 @@ static int snd_line6_control_info(struct snd_kcontrol *kcontrol,
} }
/* control get callback */ /* control get callback */
static int snd_line6_control_get(struct snd_kcontrol *kcontrol, static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
int i; int i;
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
for (i = 2; i--;) for (i = 2; i--;)
ucontrol->value.integer.value[i] = line6pcm->volume[i]; ucontrol->value.integer.value[i] = line6pcm->volume_playback[i];
return 0; return 0;
} }
/* control put callback */ /* control put callback */
static int snd_line6_control_put(struct snd_kcontrol *kcontrol, static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
int i, changed = 0; int i, changed = 0;
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
for (i = 2; i--;) for (i = 2; i--;)
if (line6pcm->volume[i] != ucontrol->value.integer.value[i]) { if (line6pcm->volume_playback[i] != ucontrol->value.integer.value[i]) {
line6pcm->volume[i] = ucontrol->value.integer.value[i]; line6pcm->volume_playback[i] = ucontrol->value.integer.value[i];
changed = 1; changed = 1;
} }
...@@ -110,14 +273,14 @@ static int snd_line6_control_put(struct snd_kcontrol *kcontrol, ...@@ -110,14 +273,14 @@ static int snd_line6_control_put(struct snd_kcontrol *kcontrol,
} }
/* control definition */ /* control definition */
static struct snd_kcontrol_new line6_control = { static struct snd_kcontrol_new line6_control_playback = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume", .name = "PCM Playback Volume",
.index = 0, .index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_line6_control_info, .info = snd_line6_control_playback_info,
.get = snd_line6_control_get, .get = snd_line6_control_playback_get,
.put = snd_line6_control_put .put = snd_line6_control_playback_put
}; };
/* /*
...@@ -128,6 +291,11 @@ static void line6_cleanup_pcm(struct snd_pcm *pcm) ...@@ -128,6 +291,11 @@ static void line6_cleanup_pcm(struct snd_pcm *pcm)
int i; int i;
struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); 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--;) { for (i = LINE6_ISO_BUFFERS; i--;) {
if (line6pcm->urb_audio_out[i]) { if (line6pcm->urb_audio_out[i]) {
usb_kill_urb(line6pcm->urb_audio_out[i]); usb_kill_urb(line6pcm->urb_audio_out[i]);
...@@ -160,7 +328,8 @@ static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm) ...@@ -160,7 +328,8 @@ static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm)
/* set operators */ /* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_line6_playback_ops); &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 */ /* pre-allocation of buffers */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
...@@ -176,6 +345,27 @@ static int snd_line6_pcm_free(struct snd_device *device) ...@@ -176,6 +345,27 @@ static int snd_line6_pcm_free(struct snd_device *device)
return 0; 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 and register the PCM device and mixer entries.
Create URBs for playback and capture. Create URBs for playback and capture.
...@@ -218,20 +408,23 @@ int line6_init_pcm(struct usb_line6 *line6, ...@@ -218,20 +408,23 @@ int line6_init_pcm(struct usb_line6 *line6,
break; break;
case LINE6_DEVID_GUITARPORT: 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_GX:
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2:
ep_read = 0x82; ep_read = 0x82;
ep_write = 0x01; ep_write = 0x01;
break; break;
case LINE6_DEVID_TONEPORT_UX1: /* this is for interface_number == 1:
ep_read = 0x00;
ep_write = 0x00;
break;
case LINE6_DEVID_TONEPORT_UX2: case LINE6_DEVID_TONEPORT_UX2:
case LINE6_DEVID_PODSTUDIO_UX2:
ep_read = 0x87; ep_read = 0x87;
ep_write = 0x00; ep_write = 0x00;
break; break;
*/
default: default:
MISSING_CASE; MISSING_CASE;
...@@ -242,7 +435,8 @@ int line6_init_pcm(struct usb_line6 *line6, ...@@ -242,7 +435,8 @@ int line6_init_pcm(struct usb_line6 *line6,
if (line6pcm == NULL) if (line6pcm == NULL)
return -ENOMEM; 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->line6 = line6;
line6pcm->ep_audio_read = ep_read; line6pcm->ep_audio_read = ep_read;
line6pcm->ep_audio_write = ep_write; line6pcm->ep_audio_write = ep_write;
...@@ -268,19 +462,32 @@ int line6_init_pcm(struct usb_line6 *line6, ...@@ -268,19 +462,32 @@ int line6_init_pcm(struct usb_line6 *line6,
spin_lock_init(&line6pcm->lock_audio_in); spin_lock_init(&line6pcm->lock_audio_in);
spin_lock_init(&line6pcm->lock_trigger); spin_lock_init(&line6pcm->lock_trigger);
err = create_audio_out_urbs(line6pcm); err = line6_create_audio_out_urbs(line6pcm);
if (err < 0) if (err < 0)
return err; return err;
err = create_audio_in_urbs(line6pcm); err = line6_create_audio_in_urbs(line6pcm);
if (err < 0) if (err < 0)
return err; return err;
/* mixer: */ /* 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) if (err < 0)
return err; return err;
line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD;
#endif
return 0; return 0;
} }
...@@ -290,12 +497,11 @@ int snd_line6_prepare(struct snd_pcm_substream *substream) ...@@ -290,12 +497,11 @@ int snd_line6_prepare(struct snd_pcm_substream *substream)
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
if (!test_and_set_bit(BIT_PREPARED, &line6pcm->flags)) { 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 = 0;
line6pcm->pos_out_done = 0; line6pcm->pos_out_done = 0;
unlink_wait_clear_audio_in_urbs(line6pcm);
line6pcm->bytes_out = 0; line6pcm->bytes_out = 0;
line6pcm->count_in = 0;
line6pcm->pos_in_done = 0; line6pcm->pos_in_done = 0;
line6pcm->bytes_in = 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -24,30 +24,79 @@ ...@@ -24,30 +24,79 @@
/* number of URBs */ /* 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 */ /* in a "full speed" device (such as the PODxt Pro) this means 1ms */
#define LINE6_ISO_INTERVAL 1 #define LINE6_ISO_INTERVAL 1
/* this should be queried dynamically from the USB interface! */ #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
#define LINE6_ISO_PACKET_SIZE_MAX 252 #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 *) \ #define get_substream(line6pcm, stream) (line6pcm->pcm->streams[stream].substream)
snd_pcm_substream_chip(s))->line6->ifcdev)
/*
PCM mode bits and masks.
"ALSA": operations triggered by applications via ALSA
"MONITOR": software monitoring
"IMPULSE": optional impulse response operation
*/
enum { enum {
BIT_RUNNING_PLAYBACK, /* individual bits: */
BIT_RUNNING_CAPTURE, 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_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 line6_pcm_properties {
...@@ -83,9 +132,11 @@ struct snd_line6_pcm { ...@@ -83,9 +132,11 @@ struct snd_line6_pcm {
struct urb *urb_audio_in[LINE6_ISO_BUFFERS]; 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. Temporary buffer for capture.
...@@ -94,6 +145,21 @@ struct snd_line6_pcm { ...@@ -94,6 +145,21 @@ struct snd_line6_pcm {
*/ */
unsigned char *buffer_in; 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. Free frame position in the playback buffer.
*/ */
...@@ -204,12 +270,36 @@ struct snd_line6_pcm { ...@@ -204,12 +270,36 @@ struct snd_line6_pcm {
/** /**
PCM playback volume (left and right). 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_*). Several status bits (see BIT_*).
*/ */
unsigned long flags; unsigned long flags;
int last_frame_in, last_frame_out;
}; };
...@@ -217,6 +307,19 @@ extern int line6_init_pcm(struct usb_line6 *line6, ...@@ -217,6 +307,19 @@ extern int line6_init_pcm(struct usb_line6 *line6,
struct line6_pcm_properties *properties); struct line6_pcm_properties *properties);
extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd); extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd);
extern int snd_line6_prepare(struct snd_pcm_substream *substream); 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 #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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -9,15 +9,13 @@ ...@@ -9,15 +9,13 @@
* *
*/ */
#include "driver.h"
#include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include "audio.h" #include "audio.h"
#include "capture.h"
#include "driver.h"
#include "pcm.h" #include "pcm.h"
#include "pod.h" #include "pod.h"
#include "playback.h" #include "playback.h"
...@@ -59,22 +57,85 @@ static void change_volume(struct urb *urb_out, int volume[], ...@@ -59,22 +57,85 @@ static void change_volume(struct urb *urb_out, int volume[],
} }
} }
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
/*
Create signal for impulse response test.
*/
static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm,
struct urb *urb_out, int bytes_per_frame)
{
int frames = urb_out->transfer_buffer_length / bytes_per_frame;
if (bytes_per_frame == 4) {
/* TODO: add code for TonePort etc. */
} else if (bytes_per_frame == 6) {
int i, j;
unsigned char *pi = line6pcm->prev_fbuf;
unsigned char *po = urb_out->transfer_buffer;
for (i = 0; i < frames; ++i) {
for (j = 0; j < bytes_per_frame / 2; ++j)
po[j] = pi[j];
for (; j < bytes_per_frame; ++j)
po[j] = 0;
pi += bytes_per_frame;
po += bytes_per_frame;
}
if (--line6pcm->impulse_count <= 0) {
((unsigned char *)(urb_out->
transfer_buffer))[bytes_per_frame -
1] =
line6pcm->impulse_volume;
line6pcm->impulse_count = line6pcm->impulse_period;
}
}
}
#endif
/*
Add signal to buffer for software monitoring.
*/
static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
int volume, int bytes_per_frame)
{
if (volume == 0)
return; /* zero volume - no change */
if (bytes_per_frame == 4) {
short *pi, *po, *buf_end;
pi = (short *)signal;
po = (short *)urb_out->transfer_buffer;
buf_end = po + urb_out->transfer_buffer_length / sizeof(*po);
for (; po < buf_end; ++pi, ++po)
*po += (*pi * volume) >> 8;
}
/*
We don't need to handle devices with 6 bytes per frame here
since they all support hardware monitoring.
*/
}
/* /*
Find a free URB, prepare audio data, and submit URB. 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; int index;
unsigned long flags; unsigned long flags;
int i, urb_size, urb_frames; 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 bytes_per_frame = line6pcm->properties->bytes_per_frame;
const int frame_increment = const int frame_increment =
line6pcm->properties->snd_line6_rates.rats[0].num_min; line6pcm->properties->snd_line6_rates.rats[0].num_min;
const int frame_factor = const int frame_factor =
line6pcm->properties->snd_line6_rates.rats[0].den * line6pcm->properties->snd_line6_rates.rats[0].den *
(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL); (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
struct snd_pcm_runtime *runtime = substream->runtime;
struct urb *urb_out; struct urb *urb_out;
spin_lock_irqsave(&line6pcm->lock_audio_out, flags); spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
...@@ -83,7 +144,7 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) ...@@ -83,7 +144,7 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
if (index < 0 || index >= LINE6_ISO_BUFFERS) { if (index < 0 || index >= LINE6_ISO_BUFFERS) {
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); 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; return -EINVAL;
} }
...@@ -92,24 +153,49 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) ...@@ -92,24 +153,49 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
for (i = 0; i < LINE6_ISO_PACKETS; ++i) { for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
/* compute frame size for given sampling rate */ /* compute frame size for given sampling rate */
int n, fs; int fsize = 0;
struct usb_iso_packet_descriptor *fout = struct usb_iso_packet_descriptor *fout =
&urb_out->iso_frame_desc[i]; &urb_out->iso_frame_desc[i];
if (line6pcm->flags & MASK_CAPTURE) {
fsize = line6pcm->prev_fsize;
}
if (fsize == 0) {
int n;
line6pcm->count_out += frame_increment; line6pcm->count_out += frame_increment;
n = line6pcm->count_out / frame_factor; n = line6pcm->count_out / frame_factor;
line6pcm->count_out -= n * frame_factor; line6pcm->count_out -= n * frame_factor;
fs = n * bytes_per_frame; fsize = n * bytes_per_frame;
}
fout->offset = urb_size; fout->offset = urb_size;
fout->length = fs; fout->length = fsize;
urb_size += fs; 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_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) { if (line6pcm->pos_out + urb_frames > runtime->buffer_size) {
/* /*
The transferred area goes over buffer boundary, The transferred area goes over buffer boundary,
...@@ -117,38 +203,65 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) ...@@ -117,38 +203,65 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
*/ */
int len; int len;
len = runtime->buffer_size - line6pcm->pos_out; len = runtime->buffer_size - line6pcm->pos_out;
urb_out->transfer_buffer = line6pcm->wrap_out;
if (len > 0) { if (len > 0) {
memcpy(line6pcm->wrap_out, memcpy(urb_out->transfer_buffer,
runtime->dma_area + runtime->dma_area +
line6pcm->pos_out * bytes_per_frame, line6pcm->pos_out * bytes_per_frame,
len * bytes_per_frame); len * bytes_per_frame);
memcpy(line6pcm->wrap_out + memcpy(urb_out->transfer_buffer +
len * bytes_per_frame, runtime->dma_area, len * bytes_per_frame, runtime->dma_area,
(urb_frames - len) * bytes_per_frame); (urb_frames - len) * bytes_per_frame);
} else
dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", len); /* this is somewhat paranoid */
} else { } else {
/* this is somewhat paranoid */ #if LINE6_REUSE_DMA_AREA_FOR_PLAYBACK
dev_err(s2m(substream),
"driver bug: len = %d\n", len);
}
} else {
/* set the buffer pointer */ /* set the buffer pointer */
urb_out->transfer_buffer = urb_out->transfer_buffer =
runtime->dma_area + runtime->dma_area +
line6pcm->pos_out * bytes_per_frame; 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 += urb_frames) >= runtime->buffer_size)
if (line6pcm->pos_out >= runtime->buffer_size)
line6pcm->pos_out -= 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; change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame);
urb_out->context = substream;
change_volume(urb_out, line6pcm->volume, 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) { for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
struct usb_iso_packet_descriptor *fout = struct usb_iso_packet_descriptor *fout =
&urb_out->iso_frame_desc[i]; &urb_out->iso_frame_desc[i];
...@@ -161,8 +274,8 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) ...@@ -161,8 +274,8 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
if (usb_submit_urb(urb_out, GFP_ATOMIC) == 0) if (usb_submit_urb(urb_out, GFP_ATOMIC) == 0)
set_bit(index, &line6pcm->active_urb_out); set_bit(index, &line6pcm->active_urb_out);
else else
dev_err(s2m(substream), "URB out #%d submission failed\n", dev_err(line6pcm->line6->ifcdev,
index); "URB out #%d submission failed\n", index);
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
return 0; return 0;
...@@ -171,12 +284,12 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) ...@@ -171,12 +284,12 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
/* /*
Submit all currently available playback URBs. 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; int ret, i;
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
ret = submit_audio_out_urb(substream); ret = submit_audio_out_urb(line6pcm);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
...@@ -187,7 +300,7 @@ static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream) ...@@ -187,7 +300,7 @@ static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream)
/* /*
Unlink all currently active playback URBs. 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; unsigned int i;
...@@ -223,17 +336,14 @@ static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) ...@@ -223,17 +336,14 @@ static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
} while (--timeout > 0); } while (--timeout > 0);
if (alive) if (alive)
snd_printk(KERN_ERR "timeout: still %d active urbs..\n", 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. 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); wait_clear_audio_out_urbs(line6pcm);
} }
...@@ -245,10 +355,16 @@ static void audio_out_callback(struct urb *urb) ...@@ -245,10 +355,16 @@ static void audio_out_callback(struct urb *urb)
int i, index, length = 0, shutdown = 0; int i, index, length = 0, shutdown = 0;
unsigned long flags; unsigned long flags;
struct snd_line6_pcm *line6pcm =
(struct snd_line6_pcm *)urb->context;
struct snd_pcm_substream *substream = struct snd_pcm_substream *substream =
(struct snd_pcm_substream *)urb->context; get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; #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 */ /* find index of URB */
for (index = LINE6_ISO_BUFFERS; index--;) for (index = LINE6_ISO_BUFFERS; index--;)
...@@ -262,11 +378,15 @@ static void audio_out_callback(struct urb *urb) ...@@ -262,11 +378,15 @@ static void audio_out_callback(struct urb *urb)
length += urb->iso_frame_desc[i].length; length += urb->iso_frame_desc[i].length;
spin_lock_irqsave(&line6pcm->lock_audio_out, flags); spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags)) {
struct snd_pcm_runtime *runtime = substream->runtime;
line6pcm->pos_out_done += line6pcm->pos_out_done +=
length / line6pcm->properties->bytes_per_frame; length / line6pcm->properties->bytes_per_frame;
if (line6pcm->pos_out_done >= runtime->buffer_size) if (line6pcm->pos_out_done >= runtime->buffer_size)
line6pcm->pos_out_done -= runtime->buffer_size; line6pcm->pos_out_done -= runtime->buffer_size;
}
clear_bit(index, &line6pcm->active_urb_out); clear_bit(index, &line6pcm->active_urb_out);
...@@ -276,20 +396,22 @@ static void audio_out_callback(struct urb *urb) ...@@ -276,20 +396,22 @@ static void audio_out_callback(struct urb *urb)
break; break;
} }
if (test_bit(index, &line6pcm->unlink_urb_out)) if (test_and_clear_bit(index, &line6pcm->unlink_urb_out))
shutdown = 1; shutdown = 1;
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
if (!shutdown) { if (!shutdown) {
submit_audio_out_urb(substream); submit_audio_out_urb(line6pcm);
line6pcm->bytes_out += length; if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags)) {
if (line6pcm->bytes_out >= line6pcm->period_out) { if ((line6pcm->bytes_out +=
line6pcm->bytes_out -= line6pcm->period_out; length) >= line6pcm->period_out) {
line6pcm->bytes_out %= line6pcm->period_out;
snd_pcm_period_elapsed(substream); snd_pcm_period_elapsed(substream);
} }
} }
}
} }
/* open playback callback */ /* open playback callback */
...@@ -340,52 +462,40 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, ...@@ -340,52 +462,40 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream,
return ret; return ret;
line6pcm->period_out = params_period_bytes(hw_params); 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; return 0;
} }
/* hw_free playback callback */ /* hw_free playback callback */
static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) 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); return snd_pcm_lib_free_pages(substream);
} }
/* trigger playback callback */ /* 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; int err;
line6pcm->count_out = 0;
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
if (!test_and_set_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) { #ifdef CONFIG_PM
err = submit_audio_out_all_urbs(substream); case SNDRV_PCM_TRIGGER_RESUME:
#endif
err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_PLAYBACK);
if (err < 0) { if (err < 0)
clear_bit(BIT_RUNNING_PLAYBACK,
&line6pcm->flags);
return err; return err;
}
}
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
if (test_and_clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) #ifdef CONFIG_PM
unlink_audio_out_urbs(line6pcm); case SNDRV_PCM_TRIGGER_SUSPEND:
#endif
err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_PLAYBACK);
if (err < 0)
return err;
break; break;
...@@ -424,7 +534,7 @@ struct snd_pcm_ops snd_line6_playback_ops = { ...@@ -424,7 +534,7 @@ struct snd_pcm_ops snd_line6_playback_ops = {
.pointer = snd_line6_playback_pointer, .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; 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -13,18 +13,29 @@ ...@@ -13,18 +13,29 @@
#define PLAYBACK_H #define PLAYBACK_H
#include "driver.h"
#include <sound/pcm.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 struct snd_pcm_ops snd_line6_playback_ops;
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 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 #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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -9,13 +9,14 @@ ...@@ -9,13 +9,14 @@
* *
*/ */
#include "driver.h"
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/wait.h>
#include <sound/control.h>
#include "audio.h" #include "audio.h"
#include "capture.h" #include "capture.h"
#include "control.h" #include "control.h"
#include "driver.h"
#include "playback.h" #include "playback.h"
#include "pod.h" #include "pod.h"
...@@ -45,7 +46,7 @@ enum { ...@@ -45,7 +46,7 @@ enum {
POD_tuner_freq = 0x15, POD_tuner_freq = 0x15,
POD_tuner_note = 0x16, POD_tuner_note = 0x16,
POD_tuner_pitch = 0x17, POD_tuner_pitch = 0x17,
POD_system_invalid = 0x7fff POD_system_invalid = 0x10000
}; };
enum { enum {
...@@ -75,6 +76,9 @@ static struct line6_pcm_properties pod_pcm_properties = { ...@@ -75,6 +76,9 @@ static struct line6_pcm_properties pod_pcm_properties = {
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_PAUSE |
#ifdef CONFIG_PM
SNDRV_PCM_INFO_RESUME |
#endif
SNDRV_PCM_INFO_SYNC_START), SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S24_3LE, .formats = SNDRV_PCM_FMTBIT_S24_3LE,
.rates = SNDRV_PCM_RATE_KNOT, .rates = SNDRV_PCM_RATE_KNOT,
...@@ -83,7 +87,7 @@ static struct line6_pcm_properties pod_pcm_properties = { ...@@ -83,7 +87,7 @@ static struct line6_pcm_properties pod_pcm_properties = {
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
.buffer_bytes_max = 60000, .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, .period_bytes_max = 8192,
.periods_min = 1, .periods_min = 1,
.periods_max = 1024 .periods_max = 1024
...@@ -93,6 +97,9 @@ static struct line6_pcm_properties pod_pcm_properties = { ...@@ -93,6 +97,9 @@ static struct line6_pcm_properties pod_pcm_properties = {
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
#ifdef CONFIG_PM
SNDRV_PCM_INFO_RESUME |
#endif
SNDRV_PCM_INFO_SYNC_START), SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S24_3LE, .formats = SNDRV_PCM_FMTBIT_S24_3LE,
.rates = SNDRV_PCM_RATE_KNOT, .rates = SNDRV_PCM_RATE_KNOT,
...@@ -101,7 +108,7 @@ static struct line6_pcm_properties pod_pcm_properties = { ...@@ -101,7 +108,7 @@ static struct line6_pcm_properties pod_pcm_properties = {
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
.buffer_bytes_max = 60000, .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, .period_bytes_max = 8192,
.periods_min = 1, .periods_min = 1,
.periods_max = 1024 .periods_max = 1024
...@@ -113,9 +120,19 @@ static struct line6_pcm_properties pod_pcm_properties = { ...@@ -113,9 +120,19 @@ static struct line6_pcm_properties pod_pcm_properties = {
.bytes_per_frame = POD_BYTES_PER_FRAME .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[] = {
static const char pod_request_channel[] = { 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7 }; 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7
static const char pod_version_header[] = { 0xf2, 0x7e, 0x7f, 0x06, 0x02 }; };
static const char pod_version_header[] = {
0xf2, 0x7e, 0x7f, 0x06, 0x02
};
/* forward declarations: */
static void pod_startup2(unsigned long data);
static void pod_startup3(struct usb_line6_pod *pod);
static void pod_startup4(struct usb_line6_pod *pod);
/* /*
...@@ -129,60 +146,6 @@ static void pod_mark_batch_all_dirty(struct usb_line6_pod *pod) ...@@ -129,60 +146,6 @@ static void pod_mark_batch_all_dirty(struct usb_line6_pod *pod)
set_bit(i, pod->param_dirty); 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) 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); return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, size);
...@@ -218,7 +181,7 @@ static void pod_store_parameter(struct usb_line6_pod *pod, int param, int value) ...@@ -218,7 +181,7 @@ static void pod_store_parameter(struct usb_line6_pod *pod, int param, int value)
} }
/* /*
Handle SAVE button Handle SAVE button.
*/ */
static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int index) static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int index)
{ {
...@@ -229,7 +192,7 @@ static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int ind ...@@ -229,7 +192,7 @@ static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int ind
/* /*
Process a completely received message. 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; const unsigned char *buf = pod->line6.buffer_message;
...@@ -254,7 +217,7 @@ void pod_process_message(struct usb_line6_pod *pod) ...@@ -254,7 +217,7 @@ void pod_process_message(struct usb_line6_pod *pod)
if ((buf[1] == POD_amp_model_setup) || if ((buf[1] == POD_amp_model_setup) ||
(buf[1] == POD_effect_setup)) (buf[1] == POD_effect_setup))
/* these also affect other settings */ /* 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; break;
...@@ -263,7 +226,7 @@ void pod_process_message(struct usb_line6_pod *pod) ...@@ -263,7 +226,7 @@ void pod_process_message(struct usb_line6_pod *pod)
pod->channel_num = buf[1]; pod->channel_num = buf[1];
pod->dirty = 0; pod->dirty = 0;
set_bit(POD_CHANNEL_DIRTY, &pod->atomic_flags); 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; break;
case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE: case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE:
...@@ -276,7 +239,6 @@ void pod_process_message(struct usb_line6_pod *pod) ...@@ -276,7 +239,6 @@ void pod_process_message(struct usb_line6_pod *pod)
case LINE6_DUMP_CURRENT: case LINE6_DUMP_CURRENT:
memcpy(&pod->prog_data, buf + 7, sizeof(pod->prog_data)); memcpy(&pod->prog_data, buf + 7, sizeof(pod->prog_data));
pod_mark_batch_all_dirty(pod); pod_mark_batch_all_dirty(pod);
pod->dumpreq.ok = 1;
break; break;
case POD_DUMP_MEMORY: case POD_DUMP_MEMORY:
...@@ -288,6 +250,7 @@ void pod_process_message(struct usb_line6_pod *pod) ...@@ -288,6 +250,7 @@ void pod_process_message(struct usb_line6_pod *pod)
} }
line6_dump_finished(&pod->dumpreq); line6_dump_finished(&pod->dumpreq);
pod_startup3(pod);
} else } else
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "wrong size of channel dump message (%d instead of %d)\n", 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)); pod->line6.message_length, (int)sizeof(pod->prog_data) + 7));
...@@ -300,7 +263,7 @@ void pod_process_message(struct usb_line6_pod *pod) ...@@ -300,7 +263,7 @@ void pod_process_message(struct usb_line6_pod *pod)
#define PROCESS_SYSTEM_PARAM(x) \ #define PROCESS_SYSTEM_PARAM(x) \
case POD_ ## x: \ case POD_ ## x: \
pod->x.value = value; \ pod->x.value = value; \
wake_up_interruptible(&pod->x.wait); \ wake_up(&pod->x.wait); \
break; break;
switch (buf[6]) { switch (buf[6]) {
...@@ -331,7 +294,7 @@ void pod_process_message(struct usb_line6_pod *pod) ...@@ -331,7 +294,7 @@ void pod_process_message(struct usb_line6_pod *pod)
case POD_SYSEX_CLIP: case POD_SYSEX_CLIP:
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "audio clipped\n")); DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "audio clipped\n"));
pod->clipping.value = 1; pod->clipping.value = 1;
wake_up_interruptible(&pod->clipping.wait); wake_up(&pod->clipping.wait);
break; break;
case POD_SYSEX_STORE: case POD_SYSEX_STORE:
...@@ -342,17 +305,9 @@ void pod_process_message(struct usb_line6_pod *pod) ...@@ -342,17 +305,9 @@ void pod_process_message(struct usb_line6_pod *pod)
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex message %02X\n", buf[5])); 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) { } 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->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->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int)buf[10];
pod->versionreq_ok = 1; pod_startup4(pod);
/* 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"));
} else } else
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex header\n")); DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex header\n"));
...@@ -377,7 +332,7 @@ void pod_process_message(struct usb_line6_pod *pod) ...@@ -377,7 +332,7 @@ void pod_process_message(struct usb_line6_pod *pod)
*) This method fails if a param change message is "chopped" after the first *) This method fails if a param change message is "chopped" after the first
byte. 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; int i;
...@@ -412,7 +367,7 @@ static void pod_send_channel(struct usb_line6_pod *pod, int value) ...@@ -412,7 +367,7 @@ static void pod_send_channel(struct usb_line6_pod *pod, int value)
/* /*
Transmit PODxt Pro control parameter. 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) if (line6_transmit_parameter(&pod->line6, param, value) == 0)
pod_store_parameter(pod, param, value); pod_store_parameter(pod, param, value);
...@@ -511,7 +466,7 @@ static ssize_t get_name_generic(struct usb_line6_pod *pod, const char *str, char ...@@ -511,7 +466,7 @@ static ssize_t get_name_generic(struct usb_line6_pod *pod, const char *str, char
char *p2; char *p2;
char *last_non_space = buf; char *last_non_space = buf;
int retval = line6_wait_dump(&pod->dumpreq, 0); int retval = line6_dump_wait_interruptible(&pod->dumpreq);
if (retval < 0) if (retval < 0)
return retval; return retval;
...@@ -588,7 +543,7 @@ static ssize_t pod_get_dump(struct device *dev, struct device_attribute *attr, ...@@ -588,7 +543,7 @@ static ssize_t pod_get_dump(struct device *dev, struct device_attribute *attr,
{ {
struct usb_interface *interface = to_usb_interface(dev); struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_pod *pod = usb_get_intfdata(interface); 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) if (retval < 0)
return retval; return retval;
memcpy(buf, &pod->prog_data, sizeof(pod->prog_data)); memcpy(buf, &pod->prog_data, sizeof(pod->prog_data));
...@@ -606,8 +561,8 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr, ...@@ -606,8 +561,8 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr,
if (count != sizeof(pod->prog_data)) { if (count != sizeof(pod->prog_data)) {
dev_err(pod->line6.ifcdev, dev_err(pod->line6.ifcdev,
"data block must be exactly %zu bytes\n", "data block must be exactly %d bytes\n",
sizeof(pod->prog_data)); (int)sizeof(pod->prog_data));
return -EINVAL; return -EINVAL;
} }
...@@ -616,78 +571,88 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr, ...@@ -616,78 +571,88 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr,
} }
/* /*
Request system parameter. Identify system parameters related to the tuner.
*/
static bool pod_is_tuner(int code)
{
return
(code == POD_tuner_mute) ||
(code == POD_tuner_freq) ||
(code == POD_tuner_note) ||
(code == POD_tuner_pitch);
}
/*
Get system parameter (as integer).
@param tuner non-zero, if code refers to a tuner parameter @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; char *sysex;
int value;
static const int size = 1; static const int size = 1;
int retval = 0; 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; return -ENODEV;
/* send value request to tuner: */ /* send value request to device: */
param->value = POD_system_invalid; param->value = POD_system_invalid;
sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEMREQ, size); sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEMREQ, size);
if (!sysex) if (!sysex)
return 0; return -ENOMEM;
sysex[SYSEX_DATA_OFS] = code; sysex[SYSEX_DATA_OFS] = code;
line6_send_sysex_message(&pod->line6, sysex, size); line6_send_sysex_message(&pod->line6, sysex, size);
kfree(sysex); kfree(sysex);
/* wait for tuner to respond: */ /* wait for device to respond: */
add_wait_queue(&param->wait, &wait); retval = wait_event_interruptible(param->wait, param->value != POD_system_invalid);
current->state = TASK_INTERRUPTIBLE;
while (param->value == POD_system_invalid) { if (retval < 0)
if (signal_pending(current)) { return retval;
retval = -ERESTARTSYS;
break;
} else
schedule();
}
current->state = TASK_RUNNING; *value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value;
remove_wait_queue(&param->wait, &wait);
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; return retval;
value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value;
return sprintf(buf, "%d\n", 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 @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, static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, int code)
int count, int code, unsigned short mask,
int tuner)
{ {
char *sysex; char *sysex;
static const int size = 5; 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; return -EINVAL;
/* send value to tuner: */ /* send value to tuner: */
sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size);
if (!sysex) if (!sysex)
return 0; return -ENOMEM;
ret = strict_strtoul(buf, 10, &result);
if (ret)
return ret;
value = result & mask;
sysex[SYSEX_DATA_OFS] = code; sysex[SYSEX_DATA_OFS] = code;
sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f;
sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f;
...@@ -695,7 +660,20 @@ static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf, ...@@ -695,7 +660,20 @@ static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf,
sysex[SYSEX_DATA_OFS + 4] = (value ) & 0x0f; sysex[SYSEX_DATA_OFS + 4] = (value ) & 0x0f;
line6_send_sysex_message(&pod->line6, sysex, size); line6_send_sysex_message(&pod->line6, sysex, size);
kfree(sysex); kfree(sysex);
return count; return 0;
}
/*
Send system parameter (from string).
@param tuner non-zero, if code refers to a tuner parameter
*/
static ssize_t pod_set_system_param_string(struct usb_line6_pod *pod, const char *buf,
int count, int code, unsigned short mask)
{
int retval;
unsigned short value = simple_strtoul(buf, NULL, 10) & mask;
retval = pod_set_system_param_int(pod, value, code);
return (retval < 0) ? retval : count;
} }
/* /*
...@@ -706,7 +684,7 @@ static ssize_t pod_get_dump_buf(struct device *dev, ...@@ -706,7 +684,7 @@ static ssize_t pod_get_dump_buf(struct device *dev,
{ {
struct usb_interface *interface = to_usb_interface(dev); struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_pod *pod = usb_get_intfdata(interface); 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) if (retval < 0)
return retval; return retval;
memcpy(buf, &pod->prog_data_buf, sizeof(pod->prog_data_buf)); memcpy(buf, &pod->prog_data_buf, sizeof(pod->prog_data_buf));
...@@ -725,8 +703,8 @@ static ssize_t pod_set_dump_buf(struct device *dev, ...@@ -725,8 +703,8 @@ static ssize_t pod_set_dump_buf(struct device *dev,
if (count != sizeof(pod->prog_data)) { if (count != sizeof(pod->prog_data)) {
dev_err(pod->line6.ifcdev, dev_err(pod->line6.ifcdev,
"data block must be exactly %zu bytes\n", "data block must be exactly %d bytes\n",
sizeof(pod->prog_data)); (int)sizeof(pod->prog_data));
return -EINVAL; return -EINVAL;
} }
...@@ -900,53 +878,94 @@ static ssize_t pod_wait_for_clip(struct device *dev, ...@@ -900,53 +878,94 @@ static ssize_t pod_wait_for_clip(struct device *dev,
{ {
struct usb_interface *interface = to_usb_interface(dev); struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_pod *pod = usb_get_intfdata(interface); struct usb_line6_pod *pod = usb_get_intfdata(interface);
int err = 0; return wait_event_interruptible(pod->clipping.wait, pod->clipping.value != 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();
}
current->state = TASK_RUNNING; /*
remove_wait_queue(&pod->clipping.wait, &wait); POD startup procedure.
return err; 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);
}
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);
} }
#define POD_GET_SYSTEM_PARAM(code, tuner, sign) \ 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, \ static ssize_t pod_get_ ## code(struct device *dev, \
struct device_attribute *attr, char *buf) \ struct device_attribute *attr, char *buf) \
{ \ { \
struct usb_interface *interface = to_usb_interface(dev); \ struct usb_interface *interface = to_usb_interface(dev); \
struct usb_line6_pod *pod = usb_get_intfdata(interface); \ struct usb_line6_pod *pod = usb_get_intfdata(interface); \
return pod_get_system_param(pod, buf, POD_ ## code, &pod->code, \ return pod_get_system_param_string(pod, buf, POD_ ## code, \
tuner, sign); \ &pod->code, sign); \
} }
#define POD_GET_SET_SYSTEM_PARAM(code, mask, tuner, sign) \ #define POD_GET_SET_SYSTEM_PARAM(code, mask, sign) \
POD_GET_SYSTEM_PARAM(code, tuner, sign) \ POD_GET_SYSTEM_PARAM(code, sign) \
static ssize_t pod_set_ ## code(struct device *dev, \ static ssize_t pod_set_ ## code(struct device *dev, \
struct device_attribute *attr, \ struct device_attribute *attr, \
const char *buf, size_t count) \ const char *buf, size_t count) \
{ \ { \
struct usb_interface *interface = to_usb_interface(dev); \ struct usb_interface *interface = to_usb_interface(dev); \
struct usb_line6_pod *pod = usb_get_intfdata(interface); \ struct usb_line6_pod *pod = usb_get_intfdata(interface); \
return pod_set_system_param(pod, buf, count, POD_ ## code, mask, \ return pod_set_system_param_string(pod, buf, count, POD_ ## code, mask); \
tuner); \
} }
POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0, 0); POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0);
POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0, 0); POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0);
POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 1, 0); POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 0);
POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 1, 0); POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 0);
POD_GET_SYSTEM_PARAM(tuner_note, 1, 1); POD_GET_SYSTEM_PARAM(tuner_note, 1);
POD_GET_SYSTEM_PARAM(tuner_pitch, 1, 1); POD_GET_SYSTEM_PARAM(tuner_pitch, 1);
#undef GET_SET_SYSTEM_PARAM #undef GET_SET_SYSTEM_PARAM
#undef GET_SYSTEM_PARAM #undef GET_SYSTEM_PARAM
...@@ -977,10 +996,57 @@ static DEVICE_ATTR(tuner_mute, S_IWUGO | S_IRUGO, pod_get_tuner_mute, pod_set_tu ...@@ -977,10 +996,57 @@ static DEVICE_ATTR(tuner_mute, S_IWUGO | S_IRUGO, pod_get_tuner_mute, pod_set_tu
static DEVICE_ATTR(tuner_note, S_IRUGO, pod_get_tuner_note, line6_nop_write); static DEVICE_ATTR(tuner_note, S_IRUGO, pod_get_tuner_note, line6_nop_write);
static DEVICE_ATTR(tuner_pitch, S_IRUGO, pod_get_tuner_pitch, 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); static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw);
#endif #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. POD destructor.
*/ */
...@@ -998,8 +1064,6 @@ static void pod_destruct(struct usb_interface *interface) ...@@ -998,8 +1064,6 @@ static void pod_destruct(struct usb_interface *interface)
/* free dump request data: */ /* free dump request data: */
line6_dumpreq_destruct(&pod->dumpreq); line6_dumpreq_destruct(&pod->dumpreq);
kfree(pod->buffer_versionreq);
} }
/* /*
...@@ -1034,7 +1098,7 @@ static int pod_create_files2(struct device *dev) ...@@ -1034,7 +1098,7 @@ static int pod_create_files2(struct device *dev)
CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_note)); CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_note));
CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_pitch)); 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)); CHECK_RETURN(device_create_file(dev, &dev_attr_raw));
#endif #endif
...@@ -1042,9 +1106,9 @@ static int pod_create_files2(struct device *dev) ...@@ -1042,9 +1106,9 @@ static int pod_create_files2(struct device *dev)
} }
/* /*
Init POD device. Try to init POD device.
*/ */
int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) static int pod_try_init(struct usb_interface *interface, struct usb_line6_pod *pod)
{ {
int err; int err;
struct usb_line6 *line6 = &pod->line6; struct usb_line6 *line6 = &pod->line6;
...@@ -1062,6 +1126,8 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) ...@@ -1062,6 +1126,8 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
init_waitqueue_head(&pod->tuner_note.wait); init_waitqueue_head(&pod->tuner_note.wait);
init_waitqueue_head(&pod->tuner_pitch.wait); init_waitqueue_head(&pod->tuner_pitch.wait);
init_waitqueue_head(&pod->clipping.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)); memset(pod->param_dirty, 0xff, sizeof(pod->param_dirty));
...@@ -1070,69 +1136,73 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) ...@@ -1070,69 +1136,73 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
sizeof(pod_request_channel)); sizeof(pod_request_channel));
if (err < 0) { if (err < 0) {
dev_err(&interface->dev, "Out of memory\n"); 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; return -ENOMEM;
} }
/* create sysfs entries: */ /* create sysfs entries: */
err = pod_create_files2(&interface->dev); err = pod_create_files2(&interface->dev);
if (err < 0) { if (err < 0) {
pod_destruct(interface);
return err; return err;
} }
/* initialize audio system: */ /* initialize audio system: */
err = line6_init_audio(line6); err = line6_init_audio(line6);
if (err < 0) { if (err < 0) {
pod_destruct(interface);
return err; return err;
} }
/* initialize MIDI subsystem: */ /* initialize MIDI subsystem: */
err = line6_init_midi(line6); err = line6_init_midi(line6);
if (err < 0) { if (err < 0) {
pod_destruct(interface);
return err; return err;
} }
/* initialize PCM subsystem: */ /* initialize PCM subsystem: */
err = line6_init_pcm(line6, &pod_pcm_properties); err = line6_init_pcm(line6, &pod_pcm_properties);
if (err < 0) { if (err < 0) {
pod_destruct(interface);
return err; return err;
} }
/* register audio system: */ /* register monitor control: */
err = line6_register_audio(line6); err = snd_ctl_add(line6->card, snd_ctl_new1(&pod_control_monitor, line6->line6pcm));
if (err < 0) { if (err < 0) {
pod_destruct(interface);
return err; 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) { if (pod->line6.properties->capabilities & LINE6_BIT_CONTROL) {
/* query some data: */ pod->monitor_level.value = POD_system_invalid;
line6_startup_delayed(&pod->dumpreq, POD_STARTUP_DELAY,
pod_startup_timeout, pod); /* initiate startup procedure: */
line6_read_serial_number(&pod->line6, &pod->serial_number); pod_startup1(pod);
} }
return 0; 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. POD device disconnected.
*/ */
void pod_disconnect(struct usb_interface *interface) void line6_pod_disconnect(struct usb_interface *interface)
{ {
struct usb_line6_pod *pod; struct usb_line6_pod *pod;
...@@ -1145,14 +1215,12 @@ void pod_disconnect(struct usb_interface *interface) ...@@ -1145,14 +1215,12 @@ void pod_disconnect(struct usb_interface *interface)
struct device *dev = &interface->dev; struct device *dev = &interface->dev;
if (line6pcm != NULL) { if (line6pcm != NULL) {
unlink_wait_clear_audio_out_urbs(line6pcm); line6_pcm_disconnect(line6pcm);
unlink_wait_clear_audio_in_urbs(line6pcm);
} }
if (dev != NULL) { if (dev != NULL) {
/* remove sysfs entries: */ /* remove sysfs entries: */
if (pod->versionreq_ok) line6_pod_remove_files(pod->firmware_version, pod->line6.properties->device_bit, dev);
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_channel);
device_remove_file(dev, &dev_attr_clip); device_remove_file(dev, &dev_attr_clip);
...@@ -1179,7 +1247,7 @@ void pod_disconnect(struct usb_interface *interface) ...@@ -1179,7 +1247,7 @@ void pod_disconnect(struct usb_interface *interface)
device_remove_file(dev, &dev_attr_tuner_note); device_remove_file(dev, &dev_attr_tuner_note);
device_remove_file(dev, &dev_attr_tuner_pitch); device_remove_file(dev, &dev_attr_tuner_pitch);
#if CREATE_RAW_FILE #ifdef CONFIG_LINE6_USB_RAW
device_remove_file(dev, &dev_attr_raw); device_remove_file(dev, &dev_attr_raw);
#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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -13,15 +13,14 @@ ...@@ -13,15 +13,14 @@
#define POD_H #define POD_H
#include "driver.h" #include <linux/interrupt.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/workqueue.h>
#include <sound/core.h> #include <sound/core.h>
#include "driver.h"
#include "dumprequest.h" #include "dumprequest.h"
...@@ -42,20 +41,19 @@ ...@@ -42,20 +41,19 @@
*/ */
#define POD_CONTROL_SIZE 0x80 #define POD_CONTROL_SIZE 0x80
#define POD_BUFSIZE_DUMPREQ 7 #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. Data structure for values that need to be requested explicitly.
This is the case for system and tuner settings. This is the case for system and tuner settings.
*/ */
struct ValueWait { struct ValueWait {
unsigned short value; int value;
wait_queue_head_t wait; wait_queue_head_t wait;
}; };
/** /**
Binary PodXT Pro program dump Binary PODxt Pro program dump
*/ */
struct pod_program { struct pod_program {
/** /**
...@@ -95,11 +93,6 @@ struct usb_line6_pod { ...@@ -95,11 +93,6 @@ struct usb_line6_pod {
*/ */
struct pod_program prog_data_buf; struct pod_program prog_data_buf;
/**
Buffer for requesting version number.
*/
unsigned char *buffer_versionreq;
/** /**
Tuner mute mode. Tuner mute mode.
*/ */
...@@ -140,9 +133,19 @@ struct usb_line6_pod { ...@@ -140,9 +133,19 @@ struct usb_line6_pod {
struct ValueWait clipping; struct ValueWait clipping;
/** /**
Bottom-half for creation of sysfs special files. Timer for device initializaton.
*/
struct timer_list startup_timer;
/**
Work handler for device initializaton.
*/ */
struct work_struct create_files_work; struct work_struct startup_work;
/**
Current progress in startup procedure.
*/
int startup_progress;
/** /**
Dirty flags for access to parameter data. Dirty flags for access to parameter data.
...@@ -154,11 +157,6 @@ struct usb_line6_pod { ...@@ -154,11 +157,6 @@ struct usb_line6_pod {
*/ */
unsigned long atomic_flags; unsigned long atomic_flags;
/**
Counter for startup process.
*/
int startup_count;
/** /**
Serial number of device. Serial number of device.
*/ */
...@@ -179,11 +177,6 @@ struct usb_line6_pod { ...@@ -179,11 +177,6 @@ struct usb_line6_pod {
*/ */
char dirty; char dirty;
/**
Flag if initial firmware version request has been successful.
*/
char versionreq_ok;
/** /**
Flag to enable MIDI postprocessing. Flag to enable MIDI postprocessing.
*/ */
...@@ -191,13 +184,12 @@ struct usb_line6_pod { ...@@ -191,13 +184,12 @@ struct usb_line6_pod {
}; };
extern void pod_disconnect(struct usb_interface *interface); extern void line6_pod_disconnect(struct usb_interface *interface);
extern int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod); extern int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod);
extern void pod_midi_postprocess(struct usb_line6_pod *pod, extern void line6_pod_midi_postprocess(struct usb_line6_pod *pod,
unsigned char *data, int length); unsigned char *data, int length);
extern void pod_process_message(struct usb_line6_pod *pod); extern void line6_pod_process_message(struct usb_line6_pod *pod);
extern void pod_receive_parameter(struct usb_line6_pod *pod, int param); extern void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param,
extern void pod_transmit_parameter(struct usb_line6_pod *pod, int param,
int value); int value);
......
#ifndef DRIVER_REVISION #ifndef DRIVER_REVISION
/* current subversion revision */ /* current subversion revision */
#define DRIVER_REVISION " (revision 529)" #define DRIVER_REVISION " (revision 665)"
#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)
* Emil Myhrman (emil.myhrman@gmail.com) * Emil Myhrman (emil.myhrman@gmail.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
...@@ -10,15 +10,22 @@ ...@@ -10,15 +10,22 @@
* *
*/ */
#include "driver.h" #include <linux/wait.h>
#include <sound/control.h>
#include "audio.h" #include "audio.h"
#include "capture.h" #include "capture.h"
#include "driver.h"
#include "playback.h" #include "playback.h"
#include "toneport.h" #include "toneport.h"
static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2);
#define TONEPORT_PCM_DELAY 1
static struct snd_ratden toneport_ratden = { static struct snd_ratden toneport_ratden = {
.num_min = 44100, .num_min = 44100,
.num_max = 44100, .num_max = 44100,
...@@ -33,6 +40,9 @@ static struct line6_pcm_properties toneport_pcm_properties = { ...@@ -33,6 +40,9 @@ static struct line6_pcm_properties toneport_pcm_properties = {
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_PAUSE |
#ifdef CONFIG_PM
SNDRV_PCM_INFO_RESUME |
#endif
SNDRV_PCM_INFO_SYNC_START), SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S16_LE, .formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_KNOT, .rates = SNDRV_PCM_RATE_KNOT,
...@@ -41,15 +51,19 @@ static struct line6_pcm_properties toneport_pcm_properties = { ...@@ -41,15 +51,19 @@ static struct line6_pcm_properties toneport_pcm_properties = {
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
.buffer_bytes_max = 60000, .buffer_bytes_max = 60000,
.period_bytes_min = 180 * 4, .period_bytes_min = 64,
.period_bytes_max = 8192, .period_bytes_max = 8192,
.periods_min = 1, .periods_min = 1,
.periods_max = 1024}, .periods_max = 1024
},
.snd_line6_capture_hw = { .snd_line6_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP | .info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
#ifdef CONFIG_PM
SNDRV_PCM_INFO_RESUME |
#endif
SNDRV_PCM_INFO_SYNC_START), SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S16_LE, .formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_KNOT, .rates = SNDRV_PCM_RATE_KNOT,
...@@ -58,13 +72,15 @@ static struct line6_pcm_properties toneport_pcm_properties = { ...@@ -58,13 +72,15 @@ static struct line6_pcm_properties toneport_pcm_properties = {
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
.buffer_bytes_max = 60000, .buffer_bytes_max = 60000,
.period_bytes_min = 188 * 4, .period_bytes_min = 64,
.period_bytes_max = 8192, .period_bytes_max = 8192,
.periods_min = 1, .periods_min = 1,
.periods_max = 1024}, .periods_max = 1024
},
.snd_line6_rates = { .snd_line6_rates = {
.nrats = 1, .nrats = 1,
.rats = &toneport_ratden}, .rats = &toneport_ratden
},
.bytes_per_frame = 4 .bytes_per_frame = 4
}; };
...@@ -77,6 +93,27 @@ static struct line6_pcm_properties toneport_pcm_properties = { ...@@ -77,6 +93,27 @@ static struct line6_pcm_properties toneport_pcm_properties = {
static int led_red = 0x00; static int led_red = 0x00;
static int led_green = 0x26; 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) static void toneport_update_led(struct device *dev)
{ {
struct usb_interface *interface = to_usb_interface(dev); struct usb_interface *interface = to_usb_interface(dev);
...@@ -129,6 +166,7 @@ static DEVICE_ATTR(led_red, S_IWUGO | S_IRUGO, line6_nop_read, ...@@ -129,6 +166,7 @@ static DEVICE_ATTR(led_red, S_IWUGO | S_IRUGO, line6_nop_read,
static DEVICE_ATTR(led_green, S_IWUGO | S_IRUGO, line6_nop_read, static DEVICE_ATTR(led_green, S_IWUGO | S_IRUGO, line6_nop_read,
toneport_set_led_green); toneport_set_led_green);
static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
{ {
int ret; int ret;
...@@ -145,6 +183,111 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) ...@@ -145,6 +183,111 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
return 0; 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. Toneport destructor.
*/ */
...@@ -162,14 +305,41 @@ static void toneport_destruct(struct usb_interface *interface) ...@@ -162,14 +305,41 @@ static void toneport_destruct(struct usb_interface *interface)
} }
/* /*
Init Toneport device. Setup Toneport device.
*/ */
int toneport_init(struct usb_interface *interface, static void toneport_setup(struct usb_line6_toneport *toneport)
{
int ticks;
struct usb_line6 *line6 = &toneport->line6;
struct usb_device *usbdev = line6->usbdev;
/* sync time on device with host: */
ticks = (int)get_seconds();
line6_write_data(line6, 0x80c6, &ticks, 4);
/* enable device: */
toneport_send_cmd(usbdev, 0x0301, 0x0000);
/* initialize source select: */
switch(usbdev->descriptor.idProduct) {
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_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) struct usb_line6_toneport *toneport)
{ {
int err, ticks; int err;
struct usb_line6 *line6 = &toneport->line6; struct usb_line6 *line6 = &toneport->line6;
struct usb_device *usbdev; struct usb_device *usbdev = line6->usbdev;
if ((interface == NULL) || (toneport == NULL)) if ((interface == NULL) || (toneport == NULL))
return -ENODEV; return -ENODEV;
...@@ -177,64 +347,93 @@ int toneport_init(struct usb_interface *interface, ...@@ -177,64 +347,93 @@ int toneport_init(struct usb_interface *interface,
/* initialize audio system: */ /* initialize audio system: */
err = line6_init_audio(line6); err = line6_init_audio(line6);
if (err < 0) { if (err < 0) {
toneport_destruct(interface);
return err; return err;
} }
/* initialize PCM subsystem: */ /* initialize PCM subsystem: */
err = line6_init_pcm(line6, &toneport_pcm_properties); err = line6_init_pcm(line6, &toneport_pcm_properties);
if (err < 0) { if (err < 0) {
toneport_destruct(interface);
return err; 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: */ /* register audio system: */
err = line6_register_audio(line6); err = line6_register_audio(line6);
if (err < 0) { if (err < 0) {
toneport_destruct(interface);
return err; return err;
} }
usbdev = line6->usbdev;
line6_read_serial_number(line6, &toneport->serial_number); line6_read_serial_number(line6, &toneport->serial_number);
line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1);
/* sync time on device with host: */ if (toneport_has_led(usbdev->descriptor.idProduct)) {
ticks = (int)get_seconds(); CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_red));
line6_write_data(line6, 0x80c6, &ticks, 4); CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_green));
}
/* toneport_setup(toneport);
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);
if (usbdev->descriptor.idProduct != LINE6_DEVID_GUITARPORT) { init_timer(&toneport->timer);
CHECK_RETURN(device_create_file toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ;
(&interface->dev, &dev_attr_led_red)); toneport->timer.function = toneport_start_pcm;
CHECK_RETURN(device_create_file toneport->timer.data = (unsigned long)toneport;
(&interface->dev, &dev_attr_led_green)); add_timer(&toneport->timer);
toneport_update_led(&usbdev->dev);
}
return 0; 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. Toneport device disconnected.
*/ */
void toneport_disconnect(struct usb_interface *interface) void line6_toneport_disconnect(struct usb_interface *interface)
{ {
struct usb_line6_toneport *toneport; struct usb_line6_toneport *toneport;
if (interface == NULL) if (interface == NULL)
return; return;
toneport = usb_get_intfdata(interface); toneport = usb_get_intfdata(interface);
del_timer_sync(&toneport->timer);
if (toneport->line6.usbdev->descriptor.idProduct != if (toneport_has_led(toneport->line6.usbdev->descriptor.idProduct)) {
LINE6_DEVID_GUITARPORT) {
device_remove_file(&interface->dev, &dev_attr_led_red); device_remove_file(&interface->dev, &dev_attr_led_red);
device_remove_file(&interface->dev, &dev_attr_led_green); device_remove_file(&interface->dev, &dev_attr_led_green);
} }
...@@ -243,8 +442,8 @@ void toneport_disconnect(struct usb_interface *interface) ...@@ -243,8 +442,8 @@ void toneport_disconnect(struct usb_interface *interface)
struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm;
if (line6pcm != NULL) { if (line6pcm != NULL) {
unlink_wait_clear_audio_out_urbs(line6pcm); line6_pcm_stop(line6pcm, MASK_PCM_MONITOR);
unlink_wait_clear_audio_in_urbs(line6pcm); 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -13,11 +13,11 @@ ...@@ -13,11 +13,11 @@
#define TONEPORT_H #define TONEPORT_H
#include "driver.h"
#include <linux/usb.h> #include <linux/usb.h>
#include <sound/core.h> #include <sound/core.h>
#include "driver.h"
struct usb_line6_toneport { struct usb_line6_toneport {
/** /**
...@@ -25,6 +25,11 @@ struct usb_line6_toneport { ...@@ -25,6 +25,11 @@ struct usb_line6_toneport {
*/ */
struct usb_line6 line6; struct usb_line6 line6;
/**
Source selector.
*/
int source;
/** /**
Serial number of device. Serial number of device.
*/ */
...@@ -34,12 +39,18 @@ struct usb_line6_toneport { ...@@ -34,12 +39,18 @@ struct usb_line6_toneport {
Firmware version (x 100). Firmware version (x 100).
*/ */
int firmware_version; int firmware_version;
/**
Timer for delayed PCM startup.
*/
struct timer_list timer;
}; };
extern void toneport_disconnect(struct usb_interface *interface); extern void line6_toneport_disconnect(struct usb_interface *interface);
extern int toneport_init(struct usb_interface *interface, extern int line6_toneport_init(struct usb_interface *interface,
struct usb_line6_toneport *toneport); struct usb_line6_toneport *toneport);
extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport);
#endif #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) * Copyright (C) 2005-2008 Markus Grabner (grabner@icg.tugraz.at)
* *
...@@ -25,6 +25,9 @@ ...@@ -25,6 +25,9 @@
#define LINE6_DEVID_BASSPODXTPRO 0x4252 #define LINE6_DEVID_BASSPODXTPRO 0x4252
#define LINE6_DEVID_GUITARPORT 0x4750 #define LINE6_DEVID_GUITARPORT 0x4750
#define LINE6_DEVID_POCKETPOD 0x5051 #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_PODX3 0x414a
#define LINE6_DEVID_PODX3LIVE 0x414b #define LINE6_DEVID_PODX3LIVE 0x414b
#define LINE6_DEVID_PODXT 0x5044 #define LINE6_DEVID_PODXT 0x5044
...@@ -40,15 +43,18 @@ ...@@ -40,15 +43,18 @@
#define LINE6_BIT_BASSPODXTPRO (1 << 2) #define LINE6_BIT_BASSPODXTPRO (1 << 2)
#define LINE6_BIT_GUITARPORT (1 << 3) #define LINE6_BIT_GUITARPORT (1 << 3)
#define LINE6_BIT_POCKETPOD (1 << 4) #define LINE6_BIT_POCKETPOD (1 << 4)
#define LINE6_BIT_PODX3 (1 << 5) #define LINE6_BIT_PODSTUDIO_GX (1 << 5)
#define LINE6_BIT_PODX3LIVE (1 << 6) #define LINE6_BIT_PODSTUDIO_UX1 (1 << 6)
#define LINE6_BIT_PODXT (1 << 7) #define LINE6_BIT_PODSTUDIO_UX2 (1 << 7)
#define LINE6_BIT_PODXTLIVE (1 << 8) #define LINE6_BIT_PODX3 (1 << 8)
#define LINE6_BIT_PODXTPRO (1 << 9) #define LINE6_BIT_PODX3LIVE (1 << 9)
#define LINE6_BIT_TONEPORT_GX (1 << 10) #define LINE6_BIT_PODXT (1 << 10)
#define LINE6_BIT_TONEPORT_UX1 (1 << 11) #define LINE6_BIT_PODXTLIVE (1 << 11)
#define LINE6_BIT_TONEPORT_UX2 (1 << 12) #define LINE6_BIT_PODXTPRO (1 << 12)
#define LINE6_BIT_VARIAX (1 << 13) #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 | \ #define LINE6_BITS_PRO (LINE6_BIT_BASSPODXTPRO | \
LINE6_BIT_PODXTPRO) LINE6_BIT_PODXTPRO)
...@@ -66,9 +72,13 @@ ...@@ -66,9 +72,13 @@
#define LINE6_BIT_CONTROL (1 << 0) #define LINE6_BIT_CONTROL (1 << 0)
/* device supports PCM input/output via USB */ /* device supports PCM input/output via USB */
#define LINE6_BIT_PCM (1 << 1) #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_INTERVAL 10
#define LINE6_FALLBACK_MAXPACKETSIZE 16 #define LINE6_FALLBACK_MAXPACKETSIZE 16
#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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -9,12 +9,11 @@ ...@@ -9,12 +9,11 @@
* *
*/ */
#include "driver.h"
#include <linux/slab.h> #include <linux/slab.h>
#include "audio.h" #include "audio.h"
#include "control.h" #include "control.h"
#include "driver.h"
#include "variax.h" #include "variax.h"
...@@ -26,18 +25,46 @@ ...@@ -26,18 +25,46 @@
#define VARIAX_OFFSET_ACTIVATE 7 #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[] = { static const char variax_activate[] = {
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
0xf7 0xf7
}; };
static const char variax_request_bank[] = { static const char variax_request_bank[] = {
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6d, 0xf7 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6d, 0xf7
}; };
static const char variax_request_model1[] = { static const char variax_request_model1[] = {
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x03,
0x00, 0x00, 0x00, 0xf7 0x00, 0x00, 0x00, 0xf7
}; };
static const char variax_request_model2[] = { static const char variax_request_model2[] = {
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x03,
...@@ -45,6 +72,13 @@ static const char variax_request_model2[] = { ...@@ -45,6 +72,13 @@ static const char variax_request_model2[] = {
}; };
/* forward declarations: */
static int variax_create_files2(struct device *dev);
static void variax_startup2(unsigned long data);
static void variax_startup4(unsigned long data);
static void variax_startup5(unsigned long data);
/* /*
Decode data transmitted by workbench. Decode data transmitted by workbench.
*/ */
...@@ -60,42 +94,93 @@ static void variax_decode(const unsigned char *raw_data, unsigned char *data, ...@@ -60,42 +94,93 @@ static void variax_decode(const unsigned char *raw_data, unsigned char *data,
} }
} }
static void variax_activate_timeout(unsigned long arg) static void variax_activate_async(struct usb_line6_variax *variax, int a)
{ {
struct usb_line6_variax *variax = (struct usb_line6_variax *)arg; variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = 1;
line6_send_raw_message_async(&variax->line6, variax->buffer_activate, line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
sizeof(variax_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; CHECK_STARTUP_PROGRESS(variax->startup_progress, 1);
variax->activate_timer.function = variax_activate_timeout;
variax->activate_timer.data = (unsigned long)variax; /* delay startup procedure: */
add_timer(&variax->activate_timer); 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) /* request firmware version: */
return; line6_version_request_async(line6);
}
static void variax_startup3(struct usb_line6_variax *variax)
{
CHECK_STARTUP_PROGRESS(variax->startup_progress, 3);
line6_dump_request_async(&variax->dumpreq, &variax->line6, 0); /* delay startup procedure: */
line6_startup_delayed(&variax->dumpreq, 1, variax_startup_timeout, line6_start_timer(&variax->startup_timer, VARIAX_STARTUP_DELAY3, variax_startup4, (unsigned long)variax);
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. 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; const unsigned char *buf = variax->line6.buffer_message;
...@@ -115,12 +200,11 @@ void variax_process_message(struct usb_line6_variax *variax) ...@@ -115,12 +200,11 @@ void variax_process_message(struct usb_line6_variax *variax)
case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE: case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE:
case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST: case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST:
variax->model = buf[1]; 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; break;
case LINE6_RESET: case LINE6_RESET:
dev_info(variax->line6.ifcdev, "VARIAX reset\n"); dev_info(variax->line6.ifcdev, "VARIAX reset\n");
variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY);
break; break;
case LINE6_SYSEX_BEGIN: case LINE6_SYSEX_BEGIN:
...@@ -132,8 +216,7 @@ void variax_process_message(struct usb_line6_variax *variax) ...@@ -132,8 +216,7 @@ void variax_process_message(struct usb_line6_variax *variax)
case VARIAX_DUMP_PASS1: case VARIAX_DUMP_PASS1:
variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH, (unsigned char *)&variax->model_data, variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH, (unsigned char *)&variax->model_data,
(sizeof(variax->model_data.name) + sizeof(variax->model_data.control) / 2) * 2); (sizeof(variax->model_data.name) + sizeof(variax->model_data.control) / 2) * 2);
line6_dump_request_async(&variax->dumpreq, &variax->line6, 1); line6_dump_request_async(&variax->dumpreq, &variax->line6, 1, VARIAX_DUMP_PASS2);
line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS2);
break; break;
case VARIAX_DUMP_PASS2: case VARIAX_DUMP_PASS2:
...@@ -141,9 +224,7 @@ void variax_process_message(struct usb_line6_variax *variax) ...@@ -141,9 +224,7 @@ void variax_process_message(struct usb_line6_variax *variax)
variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH, variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH,
(unsigned char *)&variax->model_data.control + sizeof(variax->model_data.control) / 2, (unsigned char *)&variax->model_data.control + sizeof(variax->model_data.control) / 2,
sizeof(variax->model_data.control) / 2 * 2); sizeof(variax->model_data.control) / 2 * 2);
variax->dumpreq.ok = 1; line6_dump_request_async(&variax->dumpreq, &variax->line6, 2, VARIAX_DUMP_PASS3);
line6_dump_request_async(&variax->dumpreq, &variax->line6, 2);
line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS3);
} }
} else { } else {
DEBUG_MESSAGES(dev_err(variax->line6.ifcdev, "illegal length %d of model data\n", variax->line6.message_length)); DEBUG_MESSAGES(dev_err(variax->line6.ifcdev, "illegal length %d of model data\n", variax->line6.message_length));
...@@ -154,8 +235,20 @@ void variax_process_message(struct usb_line6_variax *variax) ...@@ -154,8 +235,20 @@ void variax_process_message(struct usb_line6_variax *variax)
memcpy(variax->bank, memcpy(variax->bank,
buf + sizeof(variax_request_bank) - 1, buf + sizeof(variax_request_bank) - 1,
sizeof(variax->bank)); sizeof(variax->bank));
variax->dumpreq.ok = 1;
line6_dump_finished(&variax->dumpreq); 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; break;
...@@ -256,9 +349,7 @@ static ssize_t variax_set_active(struct device *dev, ...@@ -256,9 +349,7 @@ static ssize_t variax_set_active(struct device *dev,
if (ret) if (ret)
return ret; return ret;
variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = value ? 1 : 0; variax_activate_async(variax, value ? 1 : 0);
line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
sizeof(variax_activate));
return count; return count;
} }
...@@ -317,7 +408,7 @@ static ssize_t variax_get_name(struct device *dev, ...@@ -317,7 +408,7 @@ static ssize_t variax_get_name(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); 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, return get_string(buf, variax->model_data.name,
sizeof(variax->model_data.name)); sizeof(variax->model_data.name));
} }
...@@ -329,7 +420,7 @@ static ssize_t variax_get_bank(struct device *dev, ...@@ -329,7 +420,7 @@ static ssize_t variax_get_bank(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); 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)); return get_string(buf, variax->bank, sizeof(variax->bank));
} }
...@@ -341,7 +432,7 @@ static ssize_t variax_get_dump(struct device *dev, ...@@ -341,7 +432,7 @@ static ssize_t variax_get_dump(struct device *dev,
{ {
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
int retval; int retval;
retval = line6_wait_dump(&variax->dumpreq, 0); retval = line6_dump_wait_interruptible(&variax->dumpreq);
if (retval < 0) if (retval < 0)
return retval; return retval;
memcpy(buf, &variax->model_data.control, memcpy(buf, &variax->model_data.control,
...@@ -349,7 +440,22 @@ static ssize_t variax_get_dump(struct device *dev, ...@@ -349,7 +440,22 @@ static ssize_t variax_get_dump(struct device *dev,
return sizeof(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. "write" request on "raw" special file.
...@@ -396,8 +502,9 @@ static DEVICE_ATTR(name, S_IRUGO, variax_get_name, line6_nop_write); ...@@ -396,8 +502,9 @@ static DEVICE_ATTR(name, S_IRUGO, variax_get_name, line6_nop_write);
static DEVICE_ATTR(bank, S_IRUGO, variax_get_bank, line6_nop_write); static DEVICE_ATTR(bank, S_IRUGO, variax_get_bank, line6_nop_write);
static DEVICE_ATTR(dump, S_IRUGO, variax_get_dump, 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(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(raw, S_IWUGO, line6_nop_read, line6_set_raw);
static DEVICE_ATTR(raw2, S_IWUGO, line6_nop_read, variax_set_raw2); static DEVICE_ATTR(raw2, S_IWUGO, line6_nop_read, variax_set_raw2);
#endif #endif
...@@ -424,7 +531,6 @@ static void variax_destruct(struct usb_interface *interface) ...@@ -424,7 +531,6 @@ static void variax_destruct(struct usb_interface *interface)
line6_dumpreq_destruct(&variax->dumpreq); line6_dumpreq_destruct(&variax->dumpreq);
kfree(variax->buffer_activate); kfree(variax->buffer_activate);
del_timer_sync(&variax->activate_timer);
} }
/* /*
...@@ -440,7 +546,8 @@ static int variax_create_files2(struct device *dev) ...@@ -440,7 +546,8 @@ static int variax_create_files2(struct device *dev)
CHECK_RETURN(device_create_file(dev, &dev_attr_bank)); CHECK_RETURN(device_create_file(dev, &dev_attr_bank));
CHECK_RETURN(device_create_file(dev, &dev_attr_dump)); CHECK_RETURN(device_create_file(dev, &dev_attr_dump));
CHECK_RETURN(device_create_file(dev, &dev_attr_active)); CHECK_RETURN(device_create_file(dev, &dev_attr_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_raw));
CHECK_RETURN(device_create_file(dev, &dev_attr_raw2)); CHECK_RETURN(device_create_file(dev, &dev_attr_raw2));
#endif #endif
...@@ -448,9 +555,9 @@ static int variax_create_files2(struct device *dev) ...@@ -448,9 +555,9 @@ static int variax_create_files2(struct device *dev)
} }
/* /*
Init workbench device. Try to init workbench device.
*/ */
int variax_init(struct usb_interface *interface, static int variax_try_init(struct usb_interface *interface,
struct usb_line6_variax *variax) struct usb_line6_variax *variax)
{ {
int err; int err;
...@@ -458,13 +565,15 @@ int variax_init(struct usb_interface *interface, ...@@ -458,13 +565,15 @@ int variax_init(struct usb_interface *interface,
if ((interface == NULL) || (variax == NULL)) if ((interface == NULL) || (variax == NULL))
return -ENODEV; return -ENODEV;
init_timer(&variax->startup_timer);
INIT_WORK(&variax->startup_work, variax_startup7);
/* initialize USB buffers: */ /* initialize USB buffers: */
err = line6_dumpreq_init(&variax->dumpreq, variax_request_model1, err = line6_dumpreq_init(&variax->dumpreq, variax_request_model1,
sizeof(variax_request_model1)); sizeof(variax_request_model1));
if (err < 0) { if (err < 0) {
dev_err(&interface->dev, "Out of memory\n"); dev_err(&interface->dev, "Out of memory\n");
variax_destruct(interface);
return err; return err;
} }
...@@ -473,7 +582,6 @@ int variax_init(struct usb_interface *interface, ...@@ -473,7 +582,6 @@ int variax_init(struct usb_interface *interface,
if (err < 0) { if (err < 0) {
dev_err(&interface->dev, "Out of memory\n"); dev_err(&interface->dev, "Out of memory\n");
variax_destruct(interface);
return err; return err;
} }
...@@ -482,7 +590,6 @@ int variax_init(struct usb_interface *interface, ...@@ -482,7 +590,6 @@ int variax_init(struct usb_interface *interface,
if (err < 0) { if (err < 0) {
dev_err(&interface->dev, "Out of memory\n"); dev_err(&interface->dev, "Out of memory\n");
variax_destruct(interface);
return err; return err;
} }
...@@ -491,56 +598,45 @@ int variax_init(struct usb_interface *interface, ...@@ -491,56 +598,45 @@ int variax_init(struct usb_interface *interface,
if (variax->buffer_activate == NULL) { if (variax->buffer_activate == NULL) {
dev_err(&interface->dev, "Out of memory\n"); dev_err(&interface->dev, "Out of memory\n");
variax_destruct(interface);
return -ENOMEM; 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: */ /* initialize audio system: */
err = line6_init_audio(&variax->line6); err = line6_init_audio(&variax->line6);
if (err < 0) { if (err < 0) {
variax_destruct(interface);
return err; return err;
} }
/* initialize MIDI subsystem: */ /* initialize MIDI subsystem: */
err = line6_init_midi(&variax->line6); err = line6_init_midi(&variax->line6);
if (err < 0) { if (err < 0) {
variax_destruct(interface);
return err; return err;
} }
/* register audio system: */ /* initiate startup procedure: */
err = line6_register_audio(&variax->line6); 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) { if (err < 0) {
variax_destruct(interface); variax_destruct(interface);
return err;
} }
variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY); return err;
line6_startup_delayed(&variax->dumpreq, VARIAX_STARTUP_DELAY,
variax_startup_timeout, variax);
return 0;
} }
/* /*
Workbench device disconnected. Workbench device disconnected.
*/ */
void variax_disconnect(struct usb_interface *interface) void line6_variax_disconnect(struct usb_interface *interface)
{ {
struct device *dev; struct device *dev;
...@@ -550,7 +646,7 @@ void variax_disconnect(struct usb_interface *interface) ...@@ -550,7 +646,7 @@ void variax_disconnect(struct usb_interface *interface)
if (dev != NULL) { if (dev != NULL) {
/* remove sysfs entries: */ /* 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_model);
device_remove_file(dev, &dev_attr_volume); device_remove_file(dev, &dev_attr_volume);
device_remove_file(dev, &dev_attr_tone); device_remove_file(dev, &dev_attr_tone);
...@@ -558,7 +654,8 @@ void variax_disconnect(struct usb_interface *interface) ...@@ -558,7 +654,8 @@ void variax_disconnect(struct usb_interface *interface)
device_remove_file(dev, &dev_attr_bank); device_remove_file(dev, &dev_attr_bank);
device_remove_file(dev, &dev_attr_dump); device_remove_file(dev, &dev_attr_dump);
device_remove_file(dev, &dev_attr_active); 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_raw);
device_remove_file(dev, &dev_attr_raw2); device_remove_file(dev, &dev_attr_raw2);
#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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
...@@ -13,19 +13,18 @@ ...@@ -13,19 +13,18 @@
#define VARIAX_H #define VARIAX_H
#include "driver.h"
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <sound/core.h> #include <sound/core.h>
#include "driver.h"
#include "dumprequest.h" #include "dumprequest.h"
#define VARIAX_ACTIVATE_DELAY 10 #define VARIAX_STARTUP_DELAY1 1000
#define VARIAX_STARTUP_DELAY 3 #define VARIAX_STARTUP_DELAY3 100
#define VARIAX_STARTUP_DELAY4 100
enum { enum {
...@@ -77,6 +76,11 @@ struct usb_line6_variax { ...@@ -77,6 +76,11 @@ struct usb_line6_variax {
*/ */
struct variax_model model_data; struct variax_model model_data;
/**
Name of connected guitar.
*/
unsigned char guitar[18];
/** /**
Name of current model bank. Name of current model bank.
*/ */
...@@ -93,16 +97,26 @@ struct usb_line6_variax { ...@@ -93,16 +97,26 @@ struct usb_line6_variax {
int tone; 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 void line6_variax_disconnect(struct usb_interface *interface);
extern int variax_init(struct usb_interface *interface, extern int line6_variax_init(struct usb_interface *interface,
struct usb_line6_variax *variax); struct usb_line6_variax *variax);
extern void variax_process_message(struct usb_line6_variax *variax); extern void line6_variax_process_message(struct usb_line6_variax *variax);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment