staging: line6: sync with upstream

Big upstream sync.

Signed-off-by: Markus Grabner <grabner@icg.tugraz.at>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Markus Grabner 2010-08-12 01:35:30 +02:00 committed by Greg Kroah-Hartman
parent 4498dbcd2d
commit 1027f476f5
27 changed files with 2152 additions and 1192 deletions

View file

@ -1,4 +1,4 @@
config LINE6_USB
menuconfig LINE6_USB
tristate "Line6 USB support"
depends on USB && SND
select SND_RAWMIDI
@ -18,5 +18,68 @@ config LINE6_USB
* Signal routing (record clean/processed guitar signal,
re-amping)
Preliminary support for the Variax Workbench is included.
Preliminary support for the Variax Workbench and TonePort
devices is included.
if LINE6_USB
config LINE6_USB_DEBUG
bool "print debug messages"
default n
help
Say Y here to write debug messages to the syslog.
If unsure, say N.
config LINE6_USB_DUMP_CTRL
bool "dump control messages"
default n
help
Say Y here to write control messages sent to and received from
Line6 devices to the syslog.
If unsure, say N.
config LINE6_USB_DUMP_MIDI
bool "dump MIDI messages"
default n
help
Say Y here to write MIDI messages sent to and received from
Line6 devices to the syslog.
If unsure, say N.
config LINE6_USB_DUMP_PCM
bool "dump PCM data"
default n
help
Say Y here to write PCM data sent to and received from Line6
devices to the syslog. This will produce a huge amount of
syslog data during playback and capture.
If unsure, say N.
config LINE6_USB_RAW
bool "raw data communication"
default n
help
Say Y here to create special files which allow to send raw data
to the device. This bypasses any sanity checks, so if you discover
the code to erase the firmware, feel free to render your device
useless, but only after reading the GPL section "NO WARRANTY".
If unsure, say N.
config LINE6_USB_IMPULSE_RESPONSE
bool "measure impulse response"
default n
help
Say Y here to add code to measure the impulse response of a Line6
device. This is more accurate than user-space methods since it
bypasses any PCM data buffering (e.g., by ALSA or jack). This is
useful for assessing the performance of new devices, but is not
required for normal operation.
If unsure, say N.
endif # LINE6_USB

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -9,12 +9,12 @@
*
*/
#include "driver.h"
#include "audio.h"
#include <sound/core.h>
#include <sound/initval.h>
#include "driver.h"
#include "audio.h"
static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@ -36,8 +36,9 @@ int line6_init_audio(struct usb_line6 *line6)
line6->card = card;
strcpy(card->id, line6->properties->id);
strcpy(card->driver, DRIVER_NAME);
strcpy(card->shortname, "Line6-USB");
strcpy(card->shortname, line6->properties->name);
sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name,
dev_name(line6->ifcdev)); /* 80 chars - see asound.h */
return 0;

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -9,27 +9,24 @@
*
*/
#include "driver.h"
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "audio.h"
#include "capture.h"
#include "driver.h"
#include "pcm.h"
#include "pod.h"
#include "capture.h"
/*
Find a free URB and submit it.
*/
static int submit_audio_in_urb(struct snd_pcm_substream *substream)
static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
{
unsigned int index;
int index;
unsigned long flags;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
int i, urb_size;
struct urb *urb_in;
@ -37,9 +34,9 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
index =
find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS);
if (index >= LINE6_ISO_BUFFERS) {
if (index < 0 || index >= LINE6_ISO_BUFFERS) {
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
dev_err(s2m(substream), "no free URB found\n");
dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
return -EINVAL;
}
@ -58,13 +55,13 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
line6pcm->buffer_in +
index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
urb_in->transfer_buffer_length = urb_size;
urb_in->context = substream;
urb_in->context = line6pcm;
if (usb_submit_urb(urb_in, GFP_ATOMIC) == 0)
set_bit(index, &line6pcm->active_urb_in);
else
dev_err(s2m(substream), "URB in #%d submission failed\n",
index);
dev_err(line6pcm->line6->ifcdev,
"URB in #%d submission failed\n", index);
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
return 0;
@ -73,12 +70,12 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
/*
Submit all currently available capture URBs.
*/
static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream)
int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
{
int ret, i;
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
ret = submit_audio_in_urb(substream);
ret = submit_audio_in_urb(line6pcm);
if (ret < 0)
return ret;
}
@ -89,7 +86,7 @@ static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream)
/*
Unlink all currently active capture URBs.
*/
static void unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm)
void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
unsigned int i;
@ -126,41 +123,83 @@ static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
} while (--timeout > 0);
if (alive)
snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
line6pcm->active_urb_in = 0;
line6pcm->unlink_urb_in = 0;
}
/*
Unlink all currently active capture URBs, and wait for finishing.
*/
void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
unlink_audio_in_urbs(line6pcm);
line6_unlink_audio_in_urbs(line6pcm);
wait_clear_audio_in_urbs(line6pcm);
}
/*
Callback for completed capture URB.
Copy data into ALSA capture buffer.
*/
void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
{
struct snd_pcm_substream *substream =
get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
struct snd_pcm_runtime *runtime = substream->runtime;
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
int frames = fsize / bytes_per_frame;
if (line6pcm->pos_in_done + frames > runtime->buffer_size) {
/*
The transferred area goes over buffer boundary,
copy two separate chunks.
*/
int len;
len = runtime->buffer_size - line6pcm->pos_in_done;
if (len > 0) {
memcpy(runtime->dma_area +
line6pcm->pos_in_done * bytes_per_frame, fbuf,
len * bytes_per_frame);
memcpy(runtime->dma_area, fbuf + len * bytes_per_frame,
(frames - len) * bytes_per_frame);
} else
dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", len); /* this is somewhat paranoid */
} else {
/* copy single chunk */
memcpy(runtime->dma_area +
line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize);
}
if ((line6pcm->pos_in_done += frames) >= runtime->buffer_size)
line6pcm->pos_in_done -= runtime->buffer_size;
}
void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
{
struct snd_pcm_substream *substream =
get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
if ((line6pcm->bytes_in += length) >= line6pcm->period_in) {
line6pcm->bytes_in %= line6pcm->period_in;
snd_pcm_period_elapsed(substream);
}
}
/*
Callback for completed capture URB.
*/
static void audio_in_callback(struct urb *urb)
{
int i, index, length = 0, shutdown = 0;
int frames;
unsigned long flags;
struct snd_pcm_substream *substream =
(struct snd_pcm_substream *)urb->context;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
line6pcm->last_frame_in = urb->start_frame;
/* find index of URB */
for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
if (urb == line6pcm->urb_audio_in[index])
break;
#if DO_DUMP_PCM_RECEIVE
#ifdef CONFIG_LINE6_USB_DUMP_PCM
for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
struct usb_iso_packet_descriptor *fout =
&urb->iso_frame_desc[i];
@ -184,64 +223,43 @@ static void audio_in_callback(struct urb *urb)
fbuf = urb->transfer_buffer + fin->offset;
fsize = fin->actual_length;
if (fsize > line6pcm->max_packet_size) {
dev_err(line6pcm->line6->ifcdev,
"driver and/or device bug: packet too large (%d > %d)\n",
fsize, line6pcm->max_packet_size);
}
length += fsize;
if (fsize > 0) {
frames = fsize / bytes_per_frame;
/* the following assumes LINE6_ISO_PACKETS == 1: */
#if LINE6_BACKUP_MONITOR_SIGNAL
memcpy(line6pcm->prev_fbuf, fbuf, fsize);
#else
line6pcm->prev_fbuf = fbuf;
#endif
line6pcm->prev_fsize = fsize;
if (line6pcm->pos_in_done + frames >
runtime->buffer_size) {
/*
The transferred area goes over buffer
boundary, copy two separate chunks.
*/
int len;
len =
runtime->buffer_size -
line6pcm->pos_in_done;
if (len > 0) {
memcpy(runtime->dma_area +
line6pcm->pos_in_done *
bytes_per_frame, fbuf,
len * bytes_per_frame);
memcpy(runtime->dma_area,
fbuf + len * bytes_per_frame,
(frames -
len) * bytes_per_frame);
} else {
/* this is somewhat paranoid */
dev_err(s2m(substream),
"driver bug: len = %d\n", len);
}
} else {
/* copy single chunk */
memcpy(runtime->dma_area +
line6pcm->pos_in_done * bytes_per_frame,
fbuf, fsize * bytes_per_frame);
}
line6pcm->pos_in_done += frames;
if (line6pcm->pos_in_done >= runtime->buffer_size)
line6pcm->pos_in_done -= runtime->buffer_size;
}
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
if (!(line6pcm->flags & MASK_PCM_IMPULSE))
#endif
if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags)
&& (fsize > 0))
line6_capture_copy(line6pcm, fbuf, fsize);
}
clear_bit(index, &line6pcm->active_urb_in);
if (test_bit(index, &line6pcm->unlink_urb_in))
if (test_and_clear_bit(index, &line6pcm->unlink_urb_in))
shutdown = 1;
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
if (!shutdown) {
submit_audio_in_urb(substream);
submit_audio_in_urb(line6pcm);
line6pcm->bytes_in += length;
if (line6pcm->bytes_in >= line6pcm->period_in) {
line6pcm->bytes_in -= line6pcm->period_in;
snd_pcm_period_elapsed(substream);
}
if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags))
line6_capture_check_period(line6pcm, length);
}
}
@ -294,54 +312,40 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream,
return ret;
line6pcm->period_in = params_period_bytes(hw_params);
line6pcm->buffer_in =
kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL);
if (!line6pcm->buffer_in) {
dev_err(s2m(substream), "cannot malloc buffer_in\n");
return -ENOMEM;
}
return 0;
}
/* hw_free capture callback */
static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream)
{
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
unlink_wait_clear_audio_in_urbs(line6pcm);
kfree(line6pcm->buffer_in);
line6pcm->buffer_in = NULL;
return snd_pcm_lib_free_pages(substream);
}
/* trigger callback */
int snd_line6_capture_trigger(struct snd_pcm_substream *substream, int cmd)
int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd)
{
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
int err;
line6pcm->count_in = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (!test_and_set_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) {
err = submit_audio_in_all_urbs(substream);
#ifdef CONFIG_PM
case SNDRV_PCM_TRIGGER_RESUME:
#endif
err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_CAPTURE);
if (err < 0) {
clear_bit(BIT_RUNNING_CAPTURE,
&line6pcm->flags);
return err;
}
}
if (err < 0)
return err;
break;
case SNDRV_PCM_TRIGGER_STOP:
if (test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags))
unlink_audio_in_urbs(line6pcm);
#ifdef CONFIG_PM
case SNDRV_PCM_TRIGGER_SUSPEND:
#endif
err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_CAPTURE);
if (err < 0)
return err;
break;
@ -362,17 +366,17 @@ snd_line6_capture_pointer(struct snd_pcm_substream *substream)
/* capture operators */
struct snd_pcm_ops snd_line6_capture_ops = {
.open = snd_line6_capture_open,
.close = snd_line6_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_line6_capture_hw_params,
.hw_free = snd_line6_capture_hw_free,
.prepare = snd_line6_prepare,
.trigger = snd_line6_trigger,
.pointer = snd_line6_capture_pointer,
.open = snd_line6_capture_open,
.close = snd_line6_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_line6_capture_hw_params,
.hw_free = snd_line6_capture_hw_free,
.prepare = snd_line6_prepare,
.trigger = snd_line6_trigger,
.pointer = snd_line6_capture_pointer,
};
int create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
int i;

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -13,20 +13,21 @@
#define CAPTURE_H
#include "driver.h"
#include <sound/pcm.h>
#include "driver.h"
#include "pcm.h"
extern struct snd_pcm_ops snd_line6_capture_ops;
extern int create_audio_in_urbs(struct snd_line6_pcm *line6pcm);
extern int snd_line6_capture_trigger(struct snd_pcm_substream *substream,
int cmd);
extern void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm);
extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf,
int fsize);
extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm);
extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm);
extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm);
extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm
*line6pcm);
extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd);
#endif

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -9,11 +9,10 @@
*
*/
#include "driver.h"
#include <linux/usb.h>
#include "control.h"
#include "driver.h"
#include "pod.h"
#include "usbdefs.h"
#include "variax.h"
@ -45,7 +44,7 @@ static ssize_t pod_get_param_int(struct device *dev, char *buf, int param)
{
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_pod *pod = usb_get_intfdata(interface);
int retval = line6_wait_dump(&pod->dumpreq, 0);
int retval = line6_dump_wait_interruptible(&pod->dumpreq);
if (retval < 0)
return retval;
return sprintf(buf, "%d\n", pod->prog_data.control[param]);
@ -63,7 +62,7 @@ static ssize_t pod_set_param_int(struct device *dev, const char *buf,
if (retval)
return retval;
pod_transmit_parameter(pod, param, value);
line6_pod_transmit_parameter(pod, param, value);
return count;
}
@ -71,7 +70,7 @@ static ssize_t variax_get_param_int(struct device *dev, char *buf, int param)
{
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_variax *variax = usb_get_intfdata(interface);
int retval = line6_wait_dump(&variax->dumpreq, 0);
int retval = line6_dump_wait_interruptible(&variax->dumpreq);
if (retval < 0)
return retval;
return sprintf(buf, "%d\n", variax->model_data.control[param]);
@ -80,12 +79,11 @@ static ssize_t variax_get_param_int(struct device *dev, char *buf, int param)
static ssize_t variax_get_param_float(struct device *dev, char *buf, int param)
{
/*
We do our own floating point handling here since floats in the
kernel are problematic for at least two reasons: - many distros
are still shipped with binary kernels optimized for the ancient
80386 without FPU
- there isn't a printf("%f")
(see http://www.kernelthread.com/publications/faq/335.html)
We do our own floating point handling here since at the time
this code was written (Jan 2006) it was highly discouraged to
use floating point arithmetic in the kernel. If you think that
this no longer applies, feel free to replace this by generic
floating point code.
*/
static const int BIAS = 0x7f;
@ -97,7 +95,7 @@ static ssize_t variax_get_param_float(struct device *dev, char *buf, int param)
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_variax *variax = usb_get_intfdata(interface);
const unsigned char *p = variax->model_data.control + param;
int retval = line6_wait_dump(&variax->dumpreq, 0);
int retval = line6_dump_wait_interruptible(&variax->dumpreq);
if (retval < 0)
return retval;
@ -530,7 +528,7 @@ static DEVICE_ATTR(mix1, S_IRUGO, variax_get_mix1, line6_nop_write);
static DEVICE_ATTR(pickup_wiring, S_IRUGO, variax_get_pickup_wiring,
line6_nop_write);
int pod_create_files(int firmware, int type, struct device *dev)
int line6_pod_create_files(int firmware, int type, struct device *dev)
{
int err;
CHECK_RETURN(device_create_file(dev, &dev_attr_tweak));
@ -733,9 +731,10 @@ int pod_create_files(int firmware, int type, struct device *dev)
(dev, &dev_attr_band_6_gain__bass));
return 0;
}
EXPORT_SYMBOL(pod_create_files);
void pod_remove_files(int firmware, int type, struct device *dev)
EXPORT_SYMBOL(line6_pod_create_files);
void line6_pod_remove_files(int firmware, int type, struct device *dev)
{
device_remove_file(dev, &dev_attr_tweak);
device_remove_file(dev, &dev_attr_wah_position);
@ -908,9 +907,10 @@ void pod_remove_files(int firmware, int type, struct device *dev)
if (firmware >= 200)
device_remove_file(dev, &dev_attr_band_6_gain__bass);
}
EXPORT_SYMBOL(pod_remove_files);
int variax_create_files(int firmware, int type, struct device *dev)
EXPORT_SYMBOL(line6_pod_remove_files);
int line6_variax_create_files(int firmware, int type, struct device *dev)
{
int err;
CHECK_RETURN(device_create_file(dev, &dev_attr_body));
@ -954,9 +954,10 @@ int variax_create_files(int firmware, int type, struct device *dev)
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_wiring));
return 0;
}
EXPORT_SYMBOL(variax_create_files);
void variax_remove_files(int firmware, int type, struct device *dev)
EXPORT_SYMBOL(line6_variax_create_files);
void line6_variax_remove_files(int firmware, int type, struct device *dev)
{
device_remove_file(dev, &dev_attr_body);
device_remove_file(dev, &dev_attr_pickup1_enable);
@ -998,4 +999,5 @@ void variax_remove_files(int firmware, int type, struct device *dev)
device_remove_file(dev, &dev_attr_mix1);
device_remove_file(dev, &dev_attr_pickup_wiring);
}
EXPORT_SYMBOL(variax_remove_files);
EXPORT_SYMBOL(line6_variax_remove_files);

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -12,54 +12,38 @@
#ifndef LINE6_CONTROL_H
#define LINE6_CONTROL_H
/**
List of PODxt Pro controls.
See Appendix C of the "PODxt (Pro) Pilot's Handbook" by Line6.
Comments after the number refer to the PODxt Pro firmware version required
for this feature.
Please *don't* reformat this file since "control.c" is created automatically
from "control.h", and this process depends on the exact formatting of the
code and the comments below!
*/
/* *INDENT-OFF* */
enum {
POD_tweak = 1,
POD_wah_position = 4,
/* device: LINE6_BITS_PODXTALL */
POD_compression_gain = 5,
POD_compression_gain = 5, /* device: LINE6_BITS_PODXTALL */
POD_vol_pedal_position = 7,
POD_compression_threshold = 9,
POD_pan = 10,
POD_amp_model_setup = 11,
POD_amp_model = 12, /* firmware: 2.0 */
POD_amp_model = 12, /* firmware: 2.0 */
POD_drive = 13,
POD_bass = 14,
/* device: LINE6_BITS_PODXTALL */
POD_mid = 15,
/* device: LINE6_BITS_BASSPODXTALL */
POD_lowmid = 15,
/* device: LINE6_BITS_PODXTALL */
POD_treble = 16,
/* device: LINE6_BITS_BASSPODXTALL */
POD_highmid = 16,
POD_mid = 15, /* device: LINE6_BITS_PODXTALL */
POD_lowmid = 15, /* device: LINE6_BITS_BASSPODXTALL */
POD_treble = 16, /* device: LINE6_BITS_PODXTALL */
POD_highmid = 16, /* device: LINE6_BITS_BASSPODXTALL */
POD_chan_vol = 17,
/* device: LINE6_BITS_PODXTALL */
POD_reverb_mix = 18,
POD_reverb_mix = 18, /* device: LINE6_BITS_PODXTALL */
POD_effect_setup = 19,
POD_band_1_frequency = 20, /* firmware: 2.0 */
/* device: LINE6_BITS_PODXTALL */
POD_presence = 21,
/* device: LINE6_BITS_BASSPODXTALL */
POD_treble__bass = 21,
POD_presence = 21, /* device: LINE6_BITS_PODXTALL */
POD_treble__bass = 21, /* device: LINE6_BITS_BASSPODXTALL */
POD_noise_gate_enable = 22,
POD_gate_threshold = 23,
POD_gate_decay_time = 24,
@ -70,137 +54,78 @@ enum {
POD_mod_param_1 = 29,
POD_delay_param_1 = 30,
POD_delay_param_1_note_value = 31,
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_2_frequency__bass = 32, /* firmware: 2.0 */
POD_band_2_frequency__bass = 32, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_delay_param_2 = 33,
POD_delay_volume_mix = 34,
POD_delay_param_3 = 35,
/* device: LINE6_BITS_PODXTALL */
POD_reverb_enable = 36,
POD_reverb_type = 37,
POD_reverb_decay = 38,
POD_reverb_tone = 39,
POD_reverb_pre_delay = 40,
POD_reverb_pre_post = 41,
POD_band_2_frequency = 42,
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_3_frequency__bass = 42, /* firmware: 2.0 */
POD_reverb_enable = 36, /* device: LINE6_BITS_PODXTALL */
POD_reverb_type = 37, /* device: LINE6_BITS_PODXTALL */
POD_reverb_decay = 38, /* device: LINE6_BITS_PODXTALL */
POD_reverb_tone = 39, /* device: LINE6_BITS_PODXTALL */
POD_reverb_pre_delay = 40, /* device: LINE6_BITS_PODXTALL */
POD_reverb_pre_post = 41, /* device: LINE6_BITS_PODXTALL */
POD_band_2_frequency = 42, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
POD_band_3_frequency__bass = 42, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_wah_enable = 43,
/* device: LINE6_BITS_BASSPODXTALL */
POD_modulation_lo_cut = 44,
POD_delay_reverb_lo_cut = 45,
/* device: LINE6_BITS_PODXTALL */
POD_volume_pedal_minimum = 46, /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */
POD_eq_pre_post = 46, /* firmware: 2.0 */
POD_modulation_lo_cut = 44, /* device: LINE6_BITS_BASSPODXTALL */
POD_delay_reverb_lo_cut = 45, /* device: LINE6_BITS_BASSPODXTALL */
POD_volume_pedal_minimum = 46, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
POD_eq_pre_post = 46, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_volume_pre_post = 47,
/* device: LINE6_BITS_BASSPODXTALL */
POD_di_model = 48,
POD_di_delay = 49,
POD_di_model = 48, /* device: LINE6_BITS_BASSPODXTALL */
POD_di_delay = 49, /* device: LINE6_BITS_BASSPODXTALL */
POD_mod_enable = 50,
POD_mod_param_1_note_value = 51,
POD_mod_param_2 = 52,
POD_mod_param_3 = 53,
POD_mod_param_4 = 54,
/* device: LINE6_BITS_BASSPODXTALL */
POD_mod_param_5 = 55,
POD_mod_param_5 = 55, /* device: LINE6_BITS_BASSPODXTALL */
POD_mod_volume_mix = 56,
POD_mod_pre_post = 57,
POD_modulation_model = 58,
/* device: LINE6_BITS_PODXTALL */
POD_band_3_frequency = 60, /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_4_frequency__bass = 60, /* firmware: 2.0 */
POD_band_3_frequency = 60, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
POD_band_4_frequency__bass = 60, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_mod_param_1_double_precision = 61,
POD_delay_param_1_double_precision = 62,
POD_eq_enable = 63, /* firmware: 2.0 */
POD_tap = 64,
POD_volume_tweak_pedal_assign = 65,
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_5_frequency = 68, /* firmware: 2.0 */
POD_band_5_frequency = 68, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_tuner = 69,
POD_mic_selection = 70,
POD_cabinet_model = 71,
POD_stomp_model = 75,
POD_roomlevel = 76,
/* device: LINE6_BITS_PODXTALL */
POD_band_4_frequency = 77, /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_6_frequency = 77, /* firmware: 2.0 */
POD_band_4_frequency = 77, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
POD_band_6_frequency = 77, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_stomp_param_1_note_value = 78,
POD_stomp_param_2 = 79,
POD_stomp_param_3 = 80,
POD_stomp_param_4 = 81,
POD_stomp_param_5 = 82,
POD_stomp_param_6 = 83,
/* device: LINE6_BITS_LIVE */
POD_amp_switch_select = 84,
POD_amp_switch_select = 84, /* device: LINE6_BITS_LIVE */
POD_delay_param_4 = 85,
POD_delay_param_5 = 86,
POD_delay_pre_post = 87,
/* device: LINE6_BITS_PODXTALL */
POD_delay_model = 88,
/* device: LINE6_BITS_BASSPODXTALL */
POD_delay_verb_model = 88,
POD_delay_model = 88, /* device: LINE6_BITS_PODXTALL */
POD_delay_verb_model = 88, /* device: LINE6_BITS_BASSPODXTALL */
POD_tempo_msb = 89,
POD_tempo_lsb = 90,
POD_wah_model = 91, /* firmware: 3.0 */
POD_bypass_volume = 105, /* firmware: 2.14 */
/* device: LINE6_BITS_PRO */
POD_fx_loop_on_off = 107,
POD_fx_loop_on_off = 107, /* device: LINE6_BITS_PRO */
POD_tweak_param_select = 108,
POD_amp1_engage = 111,
POD_band_1_gain = 114, /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_2_gain__bass = 115, /* firmware: 2.0 */
/* device: LINE6_BITS_PODXTALL */
POD_band_2_gain = 116, /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_3_gain__bass = 116, /* firmware: 2.0 */
/* device: LINE6_BITS_PODXTALL */
POD_band_3_gain = 117, /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_4_gain__bass = 117, /* firmware: 2.0 */
POD_band_5_gain__bass = 118, /* firmware: 2.0 */
/* device: LINE6_BITS_PODXTALL */
POD_band_4_gain = 119, /* firmware: 2.0 */
/* device: LINE6_BITS_BASSPODXTALL */
POD_band_6_gain__bass = 119 /* firmware: 2.0 */
POD_band_2_gain__bass = 115, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_band_2_gain = 116, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
POD_band_3_gain__bass = 116, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_band_3_gain = 117, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
POD_band_4_gain__bass = 117, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_band_5_gain__bass = 118, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
POD_band_4_gain = 119, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
POD_band_6_gain__bass = 119 /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
};
/**
@ -218,8 +143,7 @@ enum {
VARIAX_pickup2_position = 23, /* type: 24 bit float */
VARIAX_pickup2_angle = 26, /* type: 24 bit float */
VARIAX_pickup2_level = 29, /* type: 24 bit float */
VARIAX_pickup_phase = 32, /* 0: in phase,
1: out of phase */
VARIAX_pickup_phase = 32, /* 0: in phase, 1: out of phase */
VARIAX_capacitance = 33, /* type: 24 bit float */
VARIAX_tone_resistance = 36, /* type: 24 bit float */
VARIAX_volume_resistance = 39, /* type: 24 bit float */
@ -258,10 +182,10 @@ enum {
};
extern int pod_create_files(int firmware, int type, struct device *dev);
extern void pod_remove_files(int firmware, int type, struct device *dev);
extern int variax_create_files(int firmware, int type, struct device *dev);
extern void variax_remove_files(int firmware, int type, struct device *dev);
extern int line6_pod_create_files(int firmware, int type, struct device *dev);
extern void line6_pod_remove_files(int firmware, int type, struct device *dev);
extern int line6_variax_create_files(int firmware, int type, struct device *dev);
extern void line6_variax_remove_files(int firmware, int type, struct device *dev);
#endif

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -9,8 +9,6 @@
*
*/
#include "driver.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
@ -19,6 +17,7 @@
#include "audio.h"
#include "capture.h"
#include "control.h"
#include "driver.h"
#include "midi.h"
#include "playback.h"
#include "pod.h"
@ -30,7 +29,7 @@
#define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>"
#define DRIVER_DESC "Line6 USB Driver"
#define DRIVER_VERSION "0.8.0"
#define DRIVER_VERSION "0.9.0"
/* table of devices that work with this driver */
@ -40,6 +39,9 @@ static const struct usb_device_id line6_id_table[] = {
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTPRO) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_GUITARPORT) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_POCKETPOD) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_GX) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX1) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX2) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3LIVE) },
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT) },
@ -54,30 +56,48 @@ static const struct usb_device_id line6_id_table[] = {
MODULE_DEVICE_TABLE(usb, line6_id_table);
static struct line6_properties line6_properties_table[] = {
{ "BassPODxt", LINE6_BIT_BASSPODXT, LINE6_BIT_CONTROL_PCM },
{ "BassPODxt Live", LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM },
{ "BassPODxt Pro", LINE6_BIT_BASSPODXTPRO, LINE6_BIT_CONTROL_PCM },
{ "GuitarPort", LINE6_BIT_GUITARPORT, LINE6_BIT_PCM },
{ "Pocket POD", LINE6_BIT_POCKETPOD, LINE6_BIT_CONTROL_PCM },
{ "POD X3", LINE6_BIT_PODX3, LINE6_BIT_PCM },
{ "POD X3 Live", LINE6_BIT_PODX3LIVE, LINE6_BIT_PCM },
{ "PODxt", LINE6_BIT_PODXT, LINE6_BIT_CONTROL_PCM },
{ "PODxt Live", LINE6_BIT_PODXTLIVE, LINE6_BIT_CONTROL_PCM },
{ "PODxt Pro", LINE6_BIT_PODXTPRO, LINE6_BIT_CONTROL_PCM },
{ "TonePort GX", LINE6_BIT_TONEPORT_GX, LINE6_BIT_PCM },
{ "TonePort UX1", LINE6_BIT_TONEPORT_UX1, LINE6_BIT_PCM },
{ "TonePort UX2", LINE6_BIT_TONEPORT_UX2, LINE6_BIT_PCM },
{ "Variax Workbench", LINE6_BIT_VARIAX, LINE6_BIT_CONTROL }
{ "BassPODxt", "BassPODxt", LINE6_BIT_BASSPODXT, LINE6_BIT_CONTROL_PCM_HWMON },
{ "BassPODxtLive", "BassPODxt Live", LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM_HWMON },
{ "BassPODxtPro", "BassPODxt Pro", LINE6_BIT_BASSPODXTPRO, LINE6_BIT_CONTROL_PCM_HWMON },
{ "GuitarPort", "GuitarPort", LINE6_BIT_GUITARPORT, LINE6_BIT_PCM },
{ "PocketPOD", "Pocket POD", LINE6_BIT_POCKETPOD, LINE6_BIT_CONTROL },
{ "PODStudioGX", "POD Studio GX", LINE6_BIT_PODSTUDIO_GX, LINE6_BIT_PCM },
{ "PODStudioUX1", "POD Studio UX1", LINE6_BIT_PODSTUDIO_UX1, LINE6_BIT_PCM },
{ "PODStudioUX2", "POD Studio UX2", LINE6_BIT_PODSTUDIO_UX2, LINE6_BIT_PCM },
{ "PODX3", "POD X3", LINE6_BIT_PODX3, LINE6_BIT_PCM },
{ "PODX3Live", "POD X3 Live", LINE6_BIT_PODX3LIVE, LINE6_BIT_PCM },
{ "PODxt", "PODxt", LINE6_BIT_PODXT, LINE6_BIT_CONTROL_PCM_HWMON },
{ "PODxtLive", "PODxt Live", LINE6_BIT_PODXTLIVE, LINE6_BIT_CONTROL_PCM_HWMON },
{ "PODxtPro", "PODxt Pro", LINE6_BIT_PODXTPRO, LINE6_BIT_CONTROL_PCM_HWMON },
{ "TonePortGX", "TonePort GX", LINE6_BIT_TONEPORT_GX, LINE6_BIT_PCM },
{ "TonePortUX1", "TonePort UX1", LINE6_BIT_TONEPORT_UX1, LINE6_BIT_PCM },
{ "TonePortUX2", "TonePort UX2", LINE6_BIT_TONEPORT_UX2, LINE6_BIT_PCM },
{ "Variax", "Variax Workbench", LINE6_BIT_VARIAX, LINE6_BIT_CONTROL }
};
/*
This is Line6's MIDI manufacturer ID.
*/
const unsigned char line6_midi_id[] = { 0x00, 0x01, 0x0c };
const unsigned char line6_midi_id[] = {
0x00, 0x01, 0x0c
};
/*
Code to request version of POD, Variax interface
(and maybe other devices).
*/
static const char line6_request_version0[] = {
0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7
};
/*
Copy of version request code with GFP_KERNEL flag for use in URB.
*/
static const char *line6_request_version;
struct usb_line6 *line6_devices[LINE6_MAX_DEVICES];
struct workqueue_struct *line6_workqueue;
/**
@ -104,15 +124,25 @@ static int line6_send_raw_message_async_part(struct message *msg,
*/
static int line6_start_listen(struct usb_line6 *line6)
{
int err;
usb_fill_int_urb(line6->urb_listen, line6->usbdev,
usb_rcvintpipe(line6->usbdev, line6->ep_control_read),
line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
line6_data_received, line6, line6->interval);
line6->urb_listen->actual_length = 0;
return usb_submit_urb(line6->urb_listen, GFP_KERNEL);
err = usb_submit_urb(line6->urb_listen, GFP_KERNEL);
return err;
}
#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.
*/
@ -152,7 +182,7 @@ void line6_write_hexdump(struct usb_line6 *line6, char dir,
}
#endif
#if DO_DUMP_URB_RECEIVE
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
/*
Dump URB data to syslog.
*/
@ -169,19 +199,19 @@ static void line6_dump_urb(struct urb *urb)
#endif
/*
Send raw message in pieces of max_packet_size bytes.
Send raw message in pieces of wMaxPacketSize bytes.
*/
int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
int size)
{
int i, done = 0;
int actual_size;
#if DO_DUMP_URB_SEND
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
line6_write_hexdump(line6, 'S', buffer, size);
#endif
for (i = 0; i < size; i += actual_size) {
for (i = 0; i < size; i += line6->max_packet_size) {
int partial;
const char *frag_buf = buffer + i;
int frag_size = min(line6->max_packet_size, size - i);
int retval;
@ -190,7 +220,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
usb_sndintpipe(line6->usbdev,
line6->ep_control_write),
(char *)frag_buf, frag_size,
&actual_size, LINE6_TIMEOUT * HZ);
&partial, LINE6_TIMEOUT * HZ);
if (retval) {
dev_err(line6->ifcdev,
@ -198,7 +228,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
break;
}
done += actual_size;
done += frag_size;
}
return done;
@ -234,7 +264,7 @@ static int line6_send_raw_message_async_part(struct message *msg,
(char *)msg->buffer + done, bytes,
line6_async_request_sent, msg, line6->interval);
#if DO_DUMP_URB_SEND
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
line6_write_hexdump(line6, 'S', (char *)msg->buffer + done, bytes);
#endif
@ -252,6 +282,17 @@ static int line6_send_raw_message_async_part(struct message *msg,
return 0;
}
/*
Setup and start timer.
*/
void line6_start_timer(struct timer_list *timer, unsigned int msecs,
void (*function)(unsigned long), unsigned long data)
{
setup_timer(timer, function, data);
timer->expires = jiffies + msecs * HZ / 1000;
add_timer(timer);
}
/*
Asynchronously send raw message.
*/
@ -288,6 +329,14 @@ int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer,
return line6_send_raw_message_async_part(msg, urb);
}
/*
Send asynchronous device version request.
*/
int line6_version_request_async(struct usb_line6 *line6)
{
return line6_send_raw_message_async(line6, line6_request_version, sizeof(line6_request_version0));
}
/*
Send sysex message in pieces of wMaxPacketSize bytes.
*/
@ -297,6 +346,15 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer,
return line6_send_raw_message(line6, buffer, size + SYSEX_EXTRA_SIZE) - SYSEX_EXTRA_SIZE;
}
/*
Send sysex message in pieces of wMaxPacketSize bytes.
*/
int line6_send_sysex_message_async(struct usb_line6 *line6, const char *buffer,
int size)
{
return line6_send_raw_message_async(line6, buffer, size + SYSEX_EXTRA_SIZE) - SYSEX_EXTRA_SIZE;
}
/*
Allocate buffer for sysex message and prepare header.
@param code sysex message code
@ -305,7 +363,7 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer,
char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2,
int size)
{
char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_KERNEL);
char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC);
if (!buffer) {
dev_err(line6->ifcdev, "out of memory\n");
@ -332,29 +390,29 @@ static void line6_data_received(struct urb *urb)
if (urb->status == -ESHUTDOWN)
return;
#if DO_DUMP_URB_RECEIVE
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
line6_dump_urb(urb);
#endif
done = midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
done = line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
if (done < urb->actual_length) {
midibuf_ignore(mb, done);
line6_midibuf_ignore(mb, done);
DEBUG_MESSAGES(dev_err(line6->ifcdev, "%d %d buffer overflow - message skipped\n", done, urb->actual_length));
}
for (;;) {
done = midibuf_read(mb, line6->buffer_message, LINE6_MESSAGE_MAXLEN);
done = line6_midibuf_read(mb, line6->buffer_message, LINE6_MESSAGE_MAXLEN);
if (done == 0)
break;
/* MIDI input filter */
if (midibuf_skip_message(mb, line6->line6midi->midi_mask_receive))
if (line6_midibuf_skip_message(mb, line6->line6midi->midi_mask_receive))
continue;
line6->message_length = done;
#if DO_DUMP_MIDI_RECEIVE
#ifdef CONFIG_LINE6_USB_DUMP_MIDI
line6_write_hexdump(line6, 'r', line6->buffer_message, done);
#endif
line6_midi_receive(line6, line6->buffer_message, done);
@ -366,17 +424,17 @@ static void line6_data_received(struct urb *urb)
case LINE6_DEVID_PODXT:
case LINE6_DEVID_PODXTPRO:
case LINE6_DEVID_POCKETPOD:
pod_process_message((struct usb_line6_pod *)line6);
line6_pod_process_message((struct usb_line6_pod *)line6);
break;
case LINE6_DEVID_PODXTLIVE:
switch (line6->interface_number) {
case PODXTLIVE_INTERFACE_POD:
pod_process_message((struct usb_line6_pod *)line6);
line6_pod_process_message((struct usb_line6_pod *)line6);
break;
case PODXTLIVE_INTERFACE_VARIAX:
variax_process_message((struct usb_line6_variax *)line6);
line6_variax_process_message((struct usb_line6_variax *)line6);
break;
default:
@ -385,7 +443,7 @@ static void line6_data_received(struct urb *urb)
break;
case LINE6_DEVID_VARIAX:
variax_process_message((struct usb_line6_variax *)line6);
line6_variax_process_message((struct usb_line6_variax *)line6);
break;
default:
@ -396,44 +454,17 @@ static void line6_data_received(struct urb *urb)
line6_start_listen(line6);
}
static int line6_send(struct usb_line6 *line6, unsigned char *buf, size_t len)
{
int retval;
int partial;
#if DO_DUMP_URB_SEND
line6_write_hexdump(line6, 'S', buf, len);
#endif
retval = usb_interrupt_msg(line6->usbdev,
usb_sndintpipe(line6->usbdev,
line6->ep_control_write),
buf, len, &partial,
LINE6_TIMEOUT * HZ);
if (retval) {
dev_err(line6->ifcdev,
"usb_interrupt_msg failed (%d)\n", retval);
}
if (partial != len) {
dev_err(line6->ifcdev,
"usb_interrupt_msg sent partial message (%d)\n",
retval);
}
return retval;
}
/*
Send channel number (i.e., switch to a different sound).
*/
int line6_send_program(struct usb_line6 *line6, int value)
{
int retval;
unsigned char *buffer;
size_t len = 2;
int partial;
buffer = kmalloc(2, GFP_KERNEL);
buffer = kmalloc(len, GFP_KERNEL);
if (!buffer) {
dev_err(line6->ifcdev, "out of memory\n");
return -ENOMEM;
@ -442,7 +473,20 @@ int line6_send_program(struct usb_line6 *line6, int value)
buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST;
buffer[1] = value;
return line6_send(line6, buffer, len);
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
line6_write_hexdump(line6, 'S', buffer, 2);
#endif
retval = usb_interrupt_msg(line6->usbdev,
usb_sndintpipe(line6->usbdev,
line6->ep_control_write),
buffer, 2, &partial, LINE6_TIMEOUT * HZ);
if (retval)
dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval);
kfree(buffer);
return retval;
}
/*
@ -450,10 +494,12 @@ int line6_send_program(struct usb_line6 *line6, int value)
*/
int line6_transmit_parameter(struct usb_line6 *line6, int param, int value)
{
int retval;
unsigned char *buffer;
size_t len = 3;
int partial;
buffer = kmalloc(3, GFP_KERNEL);
buffer = kmalloc(len, GFP_KERNEL);
if (!buffer) {
dev_err(line6->ifcdev, "out of memory\n");
return -ENOMEM;
@ -463,7 +509,19 @@ int line6_transmit_parameter(struct usb_line6 *line6, int param, int value)
buffer[1] = param;
buffer[2] = value;
return line6_send(line6, buffer, len);
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
line6_write_hexdump(line6, 'S', buffer, 3);
#endif
retval = usb_interrupt_msg(line6->usbdev,
usb_sndintpipe(line6->usbdev, line6->ep_control_write),
buffer, 3, &partial, LINE6_TIMEOUT * HZ);
if (retval)
dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval);
kfree(buffer);
return retval;
}
/*
@ -477,10 +535,9 @@ int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t dat
/* query the serial number: */
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE
| USB_DIR_OUT,
(datalen << 8) | 0x21, address,
NULL, 0, LINE6_TIMEOUT * HZ);
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
(datalen << 8) | 0x21, address,
NULL, 0, LINE6_TIMEOUT * HZ);
if (ret < 0) {
dev_err(line6->ifcdev, "read request failed (error %d)\n", ret);
@ -599,7 +656,7 @@ ssize_t line6_nop_write(struct device *dev, struct device_attribute *attr,
/*
"write" request on "raw" special file.
*/
#if CREATE_RAW_FILE
#ifdef CONFIG_LINE6_USB_RAW
ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@ -637,21 +694,6 @@ static void line6_destruct(struct usb_interface *interface)
kfree(line6);
}
static void line6_list_devices(void)
{
int i;
for (i = 0; i < LINE6_MAX_DEVICES; ++i) {
struct usb_line6 *dev = line6_devices[i];
printk(KERN_INFO "Line6 device %d: ", i);
if (dev == NULL)
printk("(not used)\n");
else
printk("%s:%d\n", dev->properties->name, dev->interface_number);
}
}
/*
Probe USB device.
*/
@ -674,10 +716,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
if (usbdev == NULL)
return -ENODEV;
/* increment reference counters: */
usb_get_intf(interface);
usb_get_dev(usbdev);
/* we don't handle multiple configurations */
if (usbdev->descriptor.bNumConfigurations != 1) {
ret = -ENODEV;
@ -689,8 +727,8 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
u16 idVendor = le16_to_cpu(usbdev->descriptor.idVendor);
u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct);
if (idVendor == line6_id_table[devtype].idVendor
&& idProduct == line6_id_table[devtype].idProduct)
if (idVendor == line6_id_table[devtype].idVendor &&
idProduct == line6_id_table[devtype].idProduct)
break;
}
@ -719,12 +757,23 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
switch (product) {
case LINE6_DEVID_BASSPODXTLIVE:
case LINE6_DEVID_POCKETPOD:
case LINE6_DEVID_PODXTLIVE:
case LINE6_DEVID_VARIAX:
alternate = 1;
break;
case LINE6_DEVID_POCKETPOD:
switch (interface_number) {
case 0:
return 0; /* this interface has no endpoints */
case 1:
alternate = 0;
break;
default:
MISSING_CASE;
}
break;
case LINE6_DEVID_PODX3:
case LINE6_DEVID_PODX3LIVE:
switch (interface_number) {
@ -746,21 +795,27 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
alternate = 5;
break;
case LINE6_DEVID_TONEPORT_GX:
case LINE6_DEVID_GUITARPORT:
case LINE6_DEVID_PODSTUDIO_GX:
case LINE6_DEVID_PODSTUDIO_UX1:
case LINE6_DEVID_TONEPORT_GX:
case LINE6_DEVID_TONEPORT_UX1:
alternate = 2; /* 1..4 seem to be ok */
break;
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2:
case LINE6_DEVID_PODSTUDIO_UX2:
switch (interface_number) {
case 0:
/* defaults to 44.1kHz, 16-bit */
alternate = 2;
break;
case 1:
alternate = 0;
/* don't know yet what this is ...
alternate = 1;
break;
*/
return -ENODEV;
default:
MISSING_CASE;
}
@ -783,7 +838,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
case LINE6_DEVID_BASSPODXT:
case LINE6_DEVID_BASSPODXTLIVE:
case LINE6_DEVID_BASSPODXTPRO:
case LINE6_DEVID_POCKETPOD:
case LINE6_DEVID_PODXT:
case LINE6_DEVID_PODXTPRO:
size = sizeof(struct usb_line6_pod);
@ -791,6 +845,12 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
ep_write = 0x03;
break;
case LINE6_DEVID_POCKETPOD:
size = sizeof(struct usb_line6_pod);
ep_read = 0x82;
ep_write = 0x02;
break;
case LINE6_DEVID_PODX3:
case LINE6_DEVID_PODX3LIVE:
/* currently unused! */
@ -799,6 +859,9 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
ep_write = 0x01;
break;
case LINE6_DEVID_PODSTUDIO_GX:
case LINE6_DEVID_PODSTUDIO_UX1:
case LINE6_DEVID_PODSTUDIO_UX2:
case LINE6_DEVID_TONEPORT_GX:
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2:
@ -925,17 +988,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
case LINE6_DEVID_PODX3LIVE:
case LINE6_DEVID_PODXT:
case LINE6_DEVID_PODXTPRO:
ret = pod_init(interface, (struct usb_line6_pod *)line6);
ret = line6_pod_init(interface, (struct usb_line6_pod *)line6);
break;
case LINE6_DEVID_PODXTLIVE:
switch (interface_number) {
case PODXTLIVE_INTERFACE_POD:
ret = pod_init(interface, (struct usb_line6_pod *)line6);
ret = line6_pod_init(interface, (struct usb_line6_pod *)line6);
break;
case PODXTLIVE_INTERFACE_VARIAX:
ret = variax_init(interface, (struct usb_line6_variax *)line6);
ret = line6_variax_init(interface, (struct usb_line6_variax *)line6);
break;
default:
@ -948,14 +1011,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
break;
case LINE6_DEVID_VARIAX:
ret = variax_init(interface, (struct usb_line6_variax *)line6);
ret = line6_variax_init(interface, (struct usb_line6_variax *)line6);
break;
case LINE6_DEVID_PODSTUDIO_GX:
case LINE6_DEVID_PODSTUDIO_UX1:
case LINE6_DEVID_PODSTUDIO_UX2:
case LINE6_DEVID_TONEPORT_GX:
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2:
case LINE6_DEVID_GUITARPORT:
ret = toneport_init(interface, (struct usb_line6_toneport *)line6);
ret = line6_toneport_init(interface, (struct usb_line6_toneport *)line6);
break;
default:
@ -971,10 +1037,23 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
if (ret < 0)
goto err_destruct;
/* creation of additional special files should go here */
dev_info(&interface->dev, "Line6 %s now attached\n",
line6->properties->name);
line6_devices[devnum] = line6;
line6_list_devices();
switch(product) {
case LINE6_DEVID_PODX3:
case LINE6_DEVID_PODX3LIVE:
dev_info(&interface->dev, "NOTE: the Line6 %s is detected, but not yet supported\n",
line6->properties->name);
}
/* increment reference counters: */
usb_get_intf(interface);
usb_get_dev(usbdev);
return 0;
err_destruct:
@ -1000,6 +1079,8 @@ static void line6_disconnect(struct usb_interface *interface)
if (usbdev == NULL)
return;
/* removal of additional special files should go here */
sysfs_remove_link(&interface->dev.kobj, "usb_device");
interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
@ -1007,7 +1088,7 @@ static void line6_disconnect(struct usb_interface *interface)
if (line6 != NULL) {
if (line6->urb_listen != NULL)
usb_kill_urb(line6->urb_listen);
line6_stop_listen(line6);
if (usbdev != line6->usbdev)
dev_err(line6->ifcdev,
@ -1022,31 +1103,34 @@ static void line6_disconnect(struct usb_interface *interface)
case LINE6_DEVID_PODX3LIVE:
case LINE6_DEVID_PODXT:
case LINE6_DEVID_PODXTPRO:
pod_disconnect(interface);
line6_pod_disconnect(interface);
break;
case LINE6_DEVID_PODXTLIVE:
switch (interface_number) {
case PODXTLIVE_INTERFACE_POD:
pod_disconnect(interface);
line6_pod_disconnect(interface);
break;
case PODXTLIVE_INTERFACE_VARIAX:
variax_disconnect(interface);
line6_variax_disconnect(interface);
break;
}
break;
case LINE6_DEVID_VARIAX:
variax_disconnect(interface);
line6_variax_disconnect(interface);
break;
case LINE6_DEVID_PODSTUDIO_GX:
case LINE6_DEVID_PODSTUDIO_UX1:
case LINE6_DEVID_PODSTUDIO_UX2:
case LINE6_DEVID_TONEPORT_GX:
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2:
case LINE6_DEVID_GUITARPORT:
toneport_disconnect(interface);
line6_toneport_disconnect(interface);
break;
default:
@ -1055,10 +1139,9 @@ static void line6_disconnect(struct usb_interface *interface)
dev_info(&interface->dev, "Line6 %s now disconnected\n", line6->properties->name);
for (i = LINE6_MAX_DEVICES; i--;) {
for (i = LINE6_MAX_DEVICES; i--;)
if (line6_devices[i] == line6)
line6_devices[i] = NULL;
}
}
line6_destruct(interface);
@ -1066,14 +1149,78 @@ static void line6_disconnect(struct usb_interface *interface)
/* decrement reference counters: */
usb_put_intf(interface);
usb_put_dev(usbdev);
line6_list_devices();
}
#ifdef CONFIG_PM
/*
Suspend Line6 device.
*/
static int line6_suspend(struct usb_interface *interface, pm_message_t message)
{
struct usb_line6 *line6 = usb_get_intfdata(interface);
struct snd_line6_pcm *line6pcm = line6->line6pcm;
snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
if (line6->properties->capabilities & LINE6_BIT_CONTROL)
line6_stop_listen(line6);
if (line6pcm != NULL) {
snd_pcm_suspend_all(line6pcm->pcm);
line6_pcm_disconnect(line6pcm);
line6pcm->flags = 0;
}
return 0;
}
/*
Resume Line6 device.
*/
static int line6_resume(struct usb_interface *interface)
{
struct usb_line6 *line6 = usb_get_intfdata(interface);
if (line6->properties->capabilities & LINE6_BIT_CONTROL)
line6_start_listen(line6);
snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
return 0;
}
/*
Resume Line6 device after reset.
*/
static int line6_reset_resume(struct usb_interface *interface)
{
struct usb_line6 *line6 = usb_get_intfdata(interface);
switch (line6->usbdev->descriptor.idProduct) {
case LINE6_DEVID_PODSTUDIO_GX:
case LINE6_DEVID_PODSTUDIO_UX1:
case LINE6_DEVID_PODSTUDIO_UX2:
case LINE6_DEVID_TONEPORT_GX:
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2:
case LINE6_DEVID_GUITARPORT:
line6_toneport_reset_resume((struct usb_line6_toneport *)line6);
}
return line6_resume(interface);
}
#endif /* CONFIG_PM */
static struct usb_driver line6_driver = {
.name = DRIVER_NAME,
.probe = line6_probe,
.disconnect = line6_disconnect,
#ifdef CONFIG_PM
.suspend = line6_suspend,
.resume = line6_resume,
.reset_resume = line6_reset_resume,
#endif
.id_table = line6_id_table,
};
@ -1086,20 +1233,27 @@ static int __init line6_init(void)
printk(KERN_INFO "%s driver version %s%s\n",
DRIVER_NAME, DRIVER_VERSION, DRIVER_REVISION);
line6_workqueue = create_workqueue(DRIVER_NAME);
if (line6_workqueue == NULL) {
err("couldn't create workqueue");
return -EINVAL;
}
for (i = LINE6_MAX_DEVICES; i--;)
line6_devices[i] = NULL;
retval = usb_register(&line6_driver);
if (retval)
if (retval) {
err("usb_register failed. Error number %d", retval);
return retval;
}
line6_request_version = kmalloc(sizeof(line6_request_version0),
GFP_KERNEL);
if (line6_request_version == NULL) {
err("Out of memory");
return -ENOMEM;
}
memcpy((char *)line6_request_version, line6_request_version0,
sizeof(line6_request_version0));
return retval;
}
@ -1109,7 +1263,7 @@ static int __init line6_init(void)
*/
static void __exit line6_exit(void)
{
destroy_workqueue(line6_workqueue);
kfree(line6_request_version);
usb_deregister(&line6_driver);
}

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -13,23 +13,24 @@
#define DRIVER_H
#include "config.h"
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/wait.h>
#include <sound/core.h>
#include "midi.h"
#define DRIVER_NAME "line6usb"
#if defined(CONFIG_LINE6_USB_DUMP_CTRL) || defined(CONFIG_LINE6_USB_DUMP_MIDI) || defined(CONFIG_LINE6_USB_DUMP_PCM)
#define CONFIG_LINE6_USB_DUMP_ANY
#endif
#define LINE6_TIMEOUT 1
#define LINE6_MAX_DEVICES 8
#define LINE6_BUFSIZE_LISTEN 32
#define LINE6_MESSAGE_MAXLEN 256
/*
Line6 MIDI control commands
*/
@ -54,6 +55,12 @@
#define LINE6_CHANNEL_MASK 0x0f
#ifdef CONFIG_LINE6_USB_DEBUG
#define DEBUG_MESSAGES(x) (x)
#else
#define DEBUG_MESSAGES(x)
#endif
#define MISSING_CASE \
printk(KERN_ERR "line6usb driver bug: missing case in %s:%d\n", \
@ -67,10 +74,14 @@ do { \
return err; \
} while (0)
#define CHECK_STARTUP_PROGRESS(x, n) \
if((x) >= (n)) \
return; \
x = (n);
extern const unsigned char line6_midi_id[3];
extern struct usb_line6 *line6_devices[LINE6_MAX_DEVICES];
extern struct workqueue_struct *line6_workqueue;
static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3;
static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4;
@ -80,8 +91,27 @@ static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4;
Common properties of Line6 devices.
*/
struct line6_properties {
/**
Card id string (maximum 16 characters).
This can be used to address the device in ALSA programs as
"default:CARD=<id>"
*/
const char *id;
/**
Card short name (maximum 32 characters).
*/
const char *name;
/**
Bit identifying this device in the line6usb driver.
*/
int device_bit;
/**
Bit vector defining this device's capabilities in the
line6usb driver.
*/
int capabilities;
};
@ -191,14 +221,22 @@ extern int line6_send_raw_message_async(struct usb_line6 *line6,
const char *buffer, int size);
extern int line6_send_sysex_message(struct usb_line6 *line6,
const char *buffer, int size);
extern int line6_send_sysex_message_async(struct usb_line6 *line6,
const char *buffer, int size);
extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
extern void line6_start_timer(struct timer_list *timer, unsigned int msecs,
void (*function)(unsigned long), unsigned long data);
extern int line6_transmit_parameter(struct usb_line6 *line6, int param,
int value);
extern int line6_version_request_async(struct usb_line6 *line6);
extern int line6_write_data(struct usb_line6 *line6, int address, void *data,
size_t datalen);
#ifdef CONFIG_LINE6_USB_DUMP_ANY
extern void line6_write_hexdump(struct usb_line6 *line6, char dir,
const unsigned char *buffer, int size);
#endif
#endif

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -9,10 +9,9 @@
*
*/
#include "driver.h"
#include <linux/slab.h>
#include "driver.h"
#include "dumprequest.h"
@ -39,17 +38,17 @@ void line6_invalidate_current(struct line6_dump_request *l6dr)
void line6_dump_finished(struct line6_dump_request *l6dr)
{
l6dr->in_progress = LINE6_DUMP_NONE;
wake_up_interruptible(&l6dr->wait);
wake_up(&l6dr->wait);
}
/*
Send an asynchronous channel dump request.
*/
int line6_dump_request_async(struct line6_dump_request *l6dr,
struct usb_line6 *line6, int num)
struct usb_line6 *line6, int num, int dest)
{
int ret;
line6_invalidate_current(l6dr);
line6_dump_started(l6dr, dest);
ret = line6_send_raw_message_async(line6, l6dr->reqbufs[num].buffer,
l6dr->reqbufs[num].length);
@ -60,43 +59,27 @@ int line6_dump_request_async(struct line6_dump_request *l6dr,
}
/*
Send an asynchronous dump request after a given interval.
Wait for completion (interruptible).
*/
void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds,
void (*function)(unsigned long), void *data)
int line6_dump_wait_interruptible(struct line6_dump_request *l6dr)
{
l6dr->timer.expires = jiffies + seconds * HZ;
l6dr->timer.function = function;
l6dr->timer.data = (unsigned long)data;
add_timer(&l6dr->timer);
return wait_event_interruptible(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE);
}
/*
Wait for completion.
*/
int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock)
void line6_dump_wait(struct line6_dump_request *l6dr)
{
int retval = 0;
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&l6dr->wait, &wait);
current->state = TASK_INTERRUPTIBLE;
wait_event(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE);
}
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;
/*
Wait for completion (with timeout).
*/
int line6_dump_wait_timeout(struct line6_dump_request *l6dr, long timeout)
{
return wait_event_timeout(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE, timeout);
}
/*
@ -123,7 +106,6 @@ int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf,
if (ret < 0)
return ret;
init_waitqueue_head(&l6dr->wait);
init_timer(&l6dr->timer);
return 0;
}
@ -148,6 +130,4 @@ void line6_dumpreq_destruct(struct line6_dump_request *l6dr)
if (l6dr->reqbufs[0].buffer == NULL)
return;
line6_dumpreq_destructbuf(l6dr, 0);
l6dr->ok = 1;
del_timer_sync(&l6dr->timer);
}

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -15,7 +15,6 @@
#include <linux/usb.h>
#include <linux/wait.h>
#include <sound/core.h>
@ -55,16 +54,6 @@ struct line6_dump_request {
*/
int in_progress;
/**
Timer for delayed dump request.
*/
struct timer_list timer;
/**
Flag if initial dump request has been successful.
*/
char ok;
/**
Dump request buffers
*/
@ -73,7 +62,7 @@ struct line6_dump_request {
extern void line6_dump_finished(struct line6_dump_request *l6dr);
extern int line6_dump_request_async(struct line6_dump_request *l6dr,
struct usb_line6 *line6, int num);
struct usb_line6 *line6, int num, int dest);
extern void line6_dump_started(struct line6_dump_request *l6dr, int dest);
extern void line6_dumpreq_destruct(struct line6_dump_request *l6dr);
extern void line6_dumpreq_destructbuf(struct line6_dump_request *l6dr, int num);
@ -82,9 +71,10 @@ extern int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf,
extern int line6_dumpreq_initbuf(struct line6_dump_request *l6dr,
const void *buf, size_t len, int num);
extern void line6_invalidate_current(struct line6_dump_request *l6dr);
extern void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds,
void (*function)(unsigned long), void *data);
extern int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock);
extern void line6_dump_wait(struct line6_dump_request *l6dr);
extern int line6_dump_wait_interruptible(struct line6_dump_request *l6dr);
extern int line6_dump_wait_timeout(struct line6_dump_request *l6dr,
long timeout);
#endif

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -9,24 +9,18 @@
*
*/
#include "driver.h"
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
#include "audio.h"
#include "driver.h"
#include "midi.h"
#include "pod.h"
#include "usbdefs.h"
#define USE_MIDIBUF 1
#define OUTPUT_DUMP_ONLY 0
#define line6_rawmidi_substream_midi(substream) \
((struct snd_line6_midi *)((substream)->rmidi->private_data))
@ -61,26 +55,26 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags);
for (;;) {
req = min(midibuf_bytes_free(mb), line6->max_packet_size);
req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size);
done = snd_rawmidi_transmit_peek(substream, chunk, req);
if (done == 0)
break;
#if DO_DUMP_MIDI_SEND
#ifdef CONFIG_LINE6_USB_DUMP_MIDI
line6_write_hexdump(line6, 's', chunk, done);
#endif
midibuf_write(mb, chunk, done);
line6_midibuf_write(mb, chunk, done);
snd_rawmidi_transmit_ack(substream, done);
}
for (;;) {
done = midibuf_read(mb, chunk, line6->max_packet_size);
done = line6_midibuf_read(mb, chunk, line6->max_packet_size);
if (done == 0)
break;
if (midibuf_skip_message(mb, line6midi->midi_mask_transmit))
if (line6_midibuf_skip_message(mb, line6midi->midi_mask_transmit))
continue;
send_midi_async(line6, chunk, done);
@ -115,7 +109,7 @@ static void midi_sent(struct urb *urb)
}
if (num == 0)
wake_up_interruptible(&line6->line6midi->send_wait);
wake_up(&line6->line6midi->send_wait);
spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags);
}
@ -139,7 +133,7 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
return -ENOMEM;
}
#if DO_DUMP_URB_SEND
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
line6_write_hexdump(line6, 'S', data, length);
#endif
@ -176,8 +170,8 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
case LINE6_DEVID_PODXTLIVE:
case LINE6_DEVID_PODXTPRO:
case LINE6_DEVID_POCKETPOD:
pod_midi_postprocess((struct usb_line6_pod *)line6, data,
length);
line6_pod_midi_postprocess((struct usb_line6_pod *)line6, data,
length);
break;
default:
@ -215,19 +209,8 @@ static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream,
static void line6_midi_output_drain(struct snd_rawmidi_substream *substream)
{
struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6;
wait_queue_head_t *head = &line6->line6midi->send_wait;
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(head, &wait);
current->state = TASK_INTERRUPTIBLE;
while (line6->line6midi->num_active_send_urbs > 0)
if (signal_pending(current))
break;
else
schedule();
current->state = TASK_RUNNING;
remove_wait_queue(head, &wait);
struct snd_line6_midi *midi = line6->line6midi;
wait_event_interruptible(midi->send_wait, midi->num_active_send_urbs == 0);
}
static int line6_midi_input_open(struct snd_rawmidi_substream *substream)
@ -284,6 +267,7 @@ static int snd_line6_new_midi(struct snd_line6_midi *line6midi)
rmidi->private_data = line6midi;
rmidi->private_free = line6_cleanup_midi;
strcpy(rmidi->id, line6midi->line6->properties->id);
strcpy(rmidi->name, line6midi->line6->properties->name);
rmidi->info_flags =
@ -371,8 +355,8 @@ static int snd_line6_midi_free(struct snd_device *device)
struct snd_line6_midi *line6midi = device->device_data;
device_remove_file(line6midi->line6->ifcdev, &dev_attr_midi_mask_transmit);
device_remove_file(line6midi->line6->ifcdev, &dev_attr_midi_mask_receive);
midibuf_destroy(&line6midi->midibuf_in);
midibuf_destroy(&line6midi->midibuf_out);
line6_midibuf_destroy(&line6midi->midibuf_in);
line6_midibuf_destroy(&line6midi->midibuf_out);
return 0;
}
@ -396,11 +380,11 @@ int line6_init_midi(struct usb_line6 *line6)
if (line6midi == NULL)
return -ENOMEM;
err = midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);
err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);
if (err < 0)
return err;
err = midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1);
err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1);
if (err < 0)
return err;

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -9,8 +9,6 @@
*
*/
#include "config.h"
#include <linux/slab.h>
#include "midibuf.h"
@ -25,9 +23,9 @@ static int midibuf_message_length(unsigned char code)
return length[(code >> 4) - 8];
} else {
/*
Note that according to the MIDI specification 0xf2 is
the "Song Position Pointer", but this is used by Line6
to send sysex messages to the host.
Note that according to the MIDI specification 0xf2 is
the "Song Position Pointer", but this is used by Line6
to send sysex messages to the host.
*/
static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1,
1, 1, 1, -1, 1, 1 };
@ -35,32 +33,6 @@ static int midibuf_message_length(unsigned char code)
}
}
void midibuf_reset(struct MidiBuffer *this)
{
this->pos_read = this->pos_write = this->full = 0;
this->command_prev = -1;
}
int midibuf_init(struct MidiBuffer *this, int size, int split)
{
this->buf = kmalloc(size, GFP_KERNEL);
if (this->buf == NULL)
return -ENOMEM;
this->size = size;
this->split = split;
midibuf_reset(this);
return 0;
}
void midibuf_status(struct MidiBuffer *this)
{
printk(KERN_DEBUG "midibuf size=%d split=%d pos_read=%d pos_write=%d "
"full=%d command_prev=%02x\n", this->size, this->split,
this->pos_read, this->pos_write, this->full, this->command_prev);
}
static int midibuf_is_empty(struct MidiBuffer *this)
{
return (this->pos_read == this->pos_write) && !this->full;
@ -71,7 +43,33 @@ static int midibuf_is_full(struct MidiBuffer *this)
return this->full;
}
int midibuf_bytes_free(struct MidiBuffer *this)
void line6_midibuf_reset(struct MidiBuffer *this)
{
this->pos_read = this->pos_write = this->full = 0;
this->command_prev = -1;
}
int line6_midibuf_init(struct MidiBuffer *this, int size, int split)
{
this->buf = kmalloc(size, GFP_KERNEL);
if (this->buf == NULL)
return -ENOMEM;
this->size = size;
this->split = split;
line6_midibuf_reset(this);
return 0;
}
void line6_midibuf_status(struct MidiBuffer *this)
{
printk(KERN_DEBUG "midibuf size=%d split=%d pos_read=%d pos_write=%d "
"full=%d command_prev=%02x\n", this->size, this->split,
this->pos_read, this->pos_write, this->full, this->command_prev);
}
int line6_midibuf_bytes_free(struct MidiBuffer *this)
{
return
midibuf_is_full(this) ?
@ -79,7 +77,7 @@ int midibuf_bytes_free(struct MidiBuffer *this)
(this->pos_read - this->pos_write + this->size - 1) % this->size + 1;
}
int midibuf_bytes_used(struct MidiBuffer *this)
int line6_midibuf_bytes_used(struct MidiBuffer *this)
{
return
midibuf_is_empty(this) ?
@ -87,7 +85,7 @@ int midibuf_bytes_used(struct MidiBuffer *this)
(this->pos_write - this->pos_read + this->size - 1) % this->size + 1;
}
int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
int line6_midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
{
int bytes_free;
int length1, length2;
@ -102,7 +100,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
skip_active_sense = 1;
}
bytes_free = midibuf_bytes_free(this);
bytes_free = line6_midibuf_bytes_free(this);
if (length > bytes_free)
length = bytes_free;
@ -129,7 +127,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
return length + skip_active_sense;
}
int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
int line6_midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
{
int bytes_used;
int length1, length2;
@ -145,7 +143,7 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
if (midibuf_is_empty(this))
return 0;
bytes_used = midibuf_bytes_used(this);
bytes_used = line6_midibuf_bytes_used(this);
if (length > bytes_used)
length = bytes_used;
@ -232,9 +230,9 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
return length + repeat;
}
int midibuf_ignore(struct MidiBuffer *this, int length)
int line6_midibuf_ignore(struct MidiBuffer *this, int length)
{
int bytes_used = midibuf_bytes_used(this);
int bytes_used = line6_midibuf_bytes_used(this);
if (length > bytes_used)
length = bytes_used;
@ -244,7 +242,7 @@ int midibuf_ignore(struct MidiBuffer *this, int length)
return length;
}
int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
int line6_midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
{
int cmd = this->command_prev;
@ -255,7 +253,7 @@ int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
return 0;
}
void midibuf_destroy(struct MidiBuffer *this)
void line6_midibuf_destroy(struct MidiBuffer *this)
{
kfree(this->buf);
this->buf = NULL;

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -23,17 +23,17 @@ struct MidiBuffer {
};
extern int midibuf_bytes_used(struct MidiBuffer *mb);
extern int midibuf_bytes_free(struct MidiBuffer *mb);
extern void midibuf_destroy(struct MidiBuffer *mb);
extern int midibuf_ignore(struct MidiBuffer *mb, int length);
extern int midibuf_init(struct MidiBuffer *mb, int size, int split);
extern int midibuf_read(struct MidiBuffer *mb, unsigned char *data, int length);
extern void midibuf_reset(struct MidiBuffer *mb);
extern int midibuf_skip_message(struct MidiBuffer *mb, unsigned short mask);
extern void midibuf_status(struct MidiBuffer *mb);
extern int midibuf_write(struct MidiBuffer *mb, unsigned char *data,
int length);
extern int line6_midibuf_bytes_used(struct MidiBuffer *mb);
extern int line6_midibuf_bytes_free(struct MidiBuffer *mb);
extern void line6_midibuf_destroy(struct MidiBuffer *mb);
extern int line6_midibuf_ignore(struct MidiBuffer *mb, int length);
extern int line6_midibuf_init(struct MidiBuffer *mb, int size, int split);
extern int line6_midibuf_read(struct MidiBuffer *mb, unsigned char *data, int length);
extern void line6_midibuf_reset(struct MidiBuffer *mb);
extern int line6_midibuf_skip_message(struct MidiBuffer *mb, unsigned short mask);
extern void line6_midibuf_status(struct MidiBuffer *mb);
extern int line6_midibuf_write(struct MidiBuffer *mb, unsigned char *data,
int length);
#endif

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -9,10 +9,7 @@
*
*/
#include "driver.h"
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@ -20,10 +17,176 @@
#include "audio.h"
#include "capture.h"
#include "driver.h"
#include "playback.h"
#include "pod.h"
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
static struct snd_line6_pcm* dev2pcm(struct device *dev)
{
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6 *line6 = usb_get_intfdata(interface);
struct snd_line6_pcm *line6pcm = line6->line6pcm;
return line6pcm;
}
/*
"read" request on "impulse_volume" special file.
*/
static ssize_t pcm_get_impulse_volume(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_volume);
}
/*
"write" request on "impulse_volume" special file.
*/
static ssize_t pcm_set_impulse_volume(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct snd_line6_pcm *line6pcm = dev2pcm(dev);
int value = simple_strtoul(buf, NULL, 10);
line6pcm->impulse_volume = value;
if(value > 0)
line6_pcm_start(line6pcm, MASK_PCM_IMPULSE);
else
line6_pcm_stop(line6pcm, MASK_PCM_IMPULSE);
return count;
}
/*
"read" request on "impulse_period" special file.
*/
static ssize_t pcm_get_impulse_period(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_period);
}
/*
"write" request on "impulse_period" special file.
*/
static ssize_t pcm_set_impulse_period(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
dev2pcm(dev)->impulse_period = simple_strtoul(buf, NULL, 10);
return count;
}
static DEVICE_ATTR(impulse_volume, S_IWUGO | S_IRUGO, pcm_get_impulse_volume, pcm_set_impulse_volume);
static DEVICE_ATTR(impulse_period, S_IWUGO | S_IRUGO, pcm_get_impulse_period, pcm_set_impulse_period);
#endif
int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels)
{
unsigned long flags_old = __sync_fetch_and_or(&line6pcm->flags, channels);
unsigned long flags_new = flags_old | channels;
int err = 0;
#if LINE6_BACKUP_MONITOR_SIGNAL
if (!(line6pcm->line6->properties->capabilities & LINE6_BIT_HWMON)) {
line6pcm->prev_fbuf = kmalloc(LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL);
if (!line6pcm->prev_fbuf) {
dev_err(line6pcm->line6->ifcdev, "cannot malloc monitor buffer\n");
return -ENOMEM;
}
}
#else
line6pcm->prev_fbuf = NULL;
#endif
if (((flags_old & MASK_CAPTURE) == 0) &&
((flags_new & MASK_CAPTURE) != 0)) {
/*
Waiting for completion of active URBs in the stop handler is
a bug, we therefore report an error if capturing is restarted
too soon.
*/
if(line6pcm->active_urb_in | line6pcm->unlink_urb_in)
return -EBUSY;
line6pcm->buffer_in = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL);
if (!line6pcm->buffer_in) {
dev_err(line6pcm->line6->ifcdev, "cannot malloc capture buffer\n");
return -ENOMEM;
}
line6pcm->count_in = 0;
line6pcm->prev_fsize = 0;
err = line6_submit_audio_in_all_urbs(line6pcm);
if (err < 0) {
__sync_fetch_and_and(&line6pcm->flags, ~channels);
return err;
}
}
if (((flags_old & MASK_PLAYBACK) == 0) &&
((flags_new & MASK_PLAYBACK) != 0)) {
/*
See comment above regarding PCM restart.
*/
if(line6pcm->active_urb_out | line6pcm->unlink_urb_out)
return -EBUSY;
line6pcm->buffer_out = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL);
if (!line6pcm->buffer_out) {
dev_err(line6pcm->line6->ifcdev, "cannot malloc playback buffer\n");
return -ENOMEM;
}
line6pcm->count_out = 0;
err = line6_submit_audio_out_all_urbs(line6pcm);
if (err < 0) {
__sync_fetch_and_and(&line6pcm->flags, ~channels);
return err;
}
}
return 0;
}
int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels)
{
unsigned long flags_old = __sync_fetch_and_and(&line6pcm->flags, ~channels);
unsigned long flags_new = flags_old & ~channels;
if (((flags_old & MASK_CAPTURE) != 0) &&
((flags_new & MASK_CAPTURE) == 0)) {
line6_unlink_audio_in_urbs(line6pcm);
kfree(line6pcm->buffer_in);
line6pcm->buffer_in = NULL;
}
if (((flags_old & MASK_PLAYBACK) != 0) &&
((flags_new & MASK_PLAYBACK) == 0)) {
line6_unlink_audio_out_urbs(line6pcm);
kfree(line6pcm->buffer_out);
line6pcm->buffer_out = NULL;
}
#if LINE6_BACKUP_MONITOR_SIGNAL
if (line6pcm->prev_fbuf != NULL)
kfree(line6pcm->prev_fbuf);
#endif
return 0;
}
/* trigger callback */
int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
{
@ -38,7 +201,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
snd_pcm_group_for_each_entry(s, substream) {
switch (s->stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
err = snd_line6_playback_trigger(s, cmd);
err = snd_line6_playback_trigger(line6pcm, cmd);
if (err < 0) {
spin_unlock_irqrestore(&line6pcm->lock_trigger,
@ -49,7 +212,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
break;
case SNDRV_PCM_STREAM_CAPTURE:
err = snd_line6_capture_trigger(s, cmd);
err = snd_line6_capture_trigger(line6pcm, cmd);
if (err < 0) {
spin_unlock_irqrestore(&line6pcm->lock_trigger,
@ -60,7 +223,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
break;
default:
dev_err(s2m(substream), "Unknown stream direction %d\n",
dev_err(line6pcm->line6->ifcdev, "Unknown stream direction %d\n",
s->stream);
}
}
@ -70,8 +233,8 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
}
/* control info callback */
static int snd_line6_control_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
@ -81,28 +244,28 @@ static int snd_line6_control_info(struct snd_kcontrol *kcontrol,
}
/* control get callback */
static int snd_line6_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int i;
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
for (i = 2; i--;)
ucontrol->value.integer.value[i] = line6pcm->volume[i];
ucontrol->value.integer.value[i] = line6pcm->volume_playback[i];
return 0;
}
/* control put callback */
static int snd_line6_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int i, changed = 0;
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
for (i = 2; i--;)
if (line6pcm->volume[i] != ucontrol->value.integer.value[i]) {
line6pcm->volume[i] = ucontrol->value.integer.value[i];
if (line6pcm->volume_playback[i] != ucontrol->value.integer.value[i]) {
line6pcm->volume_playback[i] = ucontrol->value.integer.value[i];
changed = 1;
}
@ -110,14 +273,14 @@ static int snd_line6_control_put(struct snd_kcontrol *kcontrol,
}
/* control definition */
static struct snd_kcontrol_new line6_control = {
static struct snd_kcontrol_new line6_control_playback = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_line6_control_info,
.get = snd_line6_control_get,
.put = snd_line6_control_put
.info = snd_line6_control_playback_info,
.get = snd_line6_control_playback_get,
.put = snd_line6_control_playback_put
};
/*
@ -128,6 +291,11 @@ static void line6_cleanup_pcm(struct snd_pcm *pcm)
int i;
struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm);
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_volume);
device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_period);
#endif
for (i = LINE6_ISO_BUFFERS; i--;) {
if (line6pcm->urb_audio_out[i]) {
usb_kill_urb(line6pcm->urb_audio_out[i]);
@ -160,7 +328,8 @@ static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm)
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_line6_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_line6_capture_ops);
/* pre-allocation of buffers */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
@ -176,6 +345,27 @@ static int snd_line6_pcm_free(struct snd_device *device)
return 0;
}
/*
Stop substream if still running.
*/
static void pcm_disconnect_substream(struct snd_pcm_substream *substream)
{
if(substream->runtime && snd_pcm_running(substream)) {
snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
}
}
/*
Stop PCM stream.
*/
void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm)
{
pcm_disconnect_substream(get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE));
pcm_disconnect_substream(get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK));
line6_unlink_wait_clear_audio_out_urbs(line6pcm);
line6_unlink_wait_clear_audio_in_urbs(line6pcm);
}
/*
Create and register the PCM device and mixer entries.
Create URBs for playback and capture.
@ -218,20 +408,23 @@ int line6_init_pcm(struct usb_line6 *line6,
break;
case LINE6_DEVID_GUITARPORT:
case LINE6_DEVID_PODSTUDIO_GX:
case LINE6_DEVID_PODSTUDIO_UX1:
case LINE6_DEVID_PODSTUDIO_UX2:
case LINE6_DEVID_TONEPORT_GX:
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_TONEPORT_UX2:
ep_read = 0x82;
ep_write = 0x01;
break;
case LINE6_DEVID_TONEPORT_UX1:
ep_read = 0x00;
ep_write = 0x00;
break;
/* this is for interface_number == 1:
case LINE6_DEVID_TONEPORT_UX2:
case LINE6_DEVID_PODSTUDIO_UX2:
ep_read = 0x87;
ep_write = 0x00;
break;
*/
default:
MISSING_CASE;
@ -242,12 +435,13 @@ int line6_init_pcm(struct usb_line6 *line6,
if (line6pcm == NULL)
return -ENOMEM;
line6pcm->volume[0] = line6pcm->volume[1] = 128;
line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255;
line6pcm->volume_monitor = 255;
line6pcm->line6 = line6;
line6pcm->ep_audio_read = ep_read;
line6pcm->ep_audio_write = ep_write;
line6pcm->max_packet_size = usb_maxpacket(line6->usbdev,
usb_rcvintpipe(line6->usbdev,
usb_rcvintpipe(line6->usbdev,
ep_read),
0);
line6pcm->properties = properties;
@ -268,19 +462,32 @@ int line6_init_pcm(struct usb_line6 *line6,
spin_lock_init(&line6pcm->lock_audio_in);
spin_lock_init(&line6pcm->lock_trigger);
err = create_audio_out_urbs(line6pcm);
err = line6_create_audio_out_urbs(line6pcm);
if (err < 0)
return err;
err = create_audio_in_urbs(line6pcm);
err = line6_create_audio_in_urbs(line6pcm);
if (err < 0)
return err;
/* mixer: */
err = snd_ctl_add(line6->card, snd_ctl_new1(&line6_control, line6pcm));
err = snd_ctl_add(line6->card, snd_ctl_new1(&line6_control_playback, line6pcm));
if (err < 0)
return err;
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
/* impulse response test: */
err = device_create_file(line6->ifcdev, &dev_attr_impulse_volume);
if (err < 0)
return err;
err = device_create_file(line6->ifcdev, &dev_attr_impulse_period);
if (err < 0)
return err;
line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD;
#endif
return 0;
}
@ -290,12 +497,11 @@ int snd_line6_prepare(struct snd_pcm_substream *substream)
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
if (!test_and_set_bit(BIT_PREPARED, &line6pcm->flags)) {
unlink_wait_clear_audio_out_urbs(line6pcm);
line6pcm->count_out = 0;
line6pcm->pos_out = 0;
line6pcm->pos_out_done = 0;
unlink_wait_clear_audio_in_urbs(line6pcm);
line6pcm->bytes_out = 0;
line6pcm->count_in = 0;
line6pcm->pos_in_done = 0;
line6pcm->bytes_in = 0;
}

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -24,30 +24,79 @@
/* number of URBs */
#define LINE6_ISO_BUFFERS 8
#define LINE6_ISO_BUFFERS 2
/* number of USB frames per URB */
#define LINE6_ISO_PACKETS 2
/*
number of USB frames per URB
The Line6 Windows driver always transmits two frames per packet, but
the Linux driver performs significantly better (i.e., lower latency)
with only one frame per packet.
*/
#define LINE6_ISO_PACKETS 1
/* in a "full speed" device (such as the PODxt Pro) this means 1ms */
#define LINE6_ISO_INTERVAL 1
/* this should be queried dynamically from the USB interface! */
#define LINE6_ISO_PACKET_SIZE_MAX 252
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
#define LINE6_IMPULSE_DEFAULT_PERIOD 100
#endif
#define LINE6_BACKUP_MONITOR_SIGNAL 0
#define LINE6_REUSE_DMA_AREA_FOR_PLAYBACK 0
/*
Extract the messaging device from the substream instance
Get substream from Line6 PCM data structure
*/
#define s2m(s) (((struct snd_line6_pcm *) \
snd_pcm_substream_chip(s))->line6->ifcdev)
#define get_substream(line6pcm, stream) (line6pcm->pcm->streams[stream].substream)
/*
PCM mode bits and masks.
"ALSA": operations triggered by applications via ALSA
"MONITOR": software monitoring
"IMPULSE": optional impulse response operation
*/
enum {
BIT_RUNNING_PLAYBACK,
BIT_RUNNING_CAPTURE,
/* individual bits: */
BIT_PCM_ALSA_PLAYBACK,
BIT_PCM_ALSA_CAPTURE,
BIT_PCM_MONITOR_PLAYBACK,
BIT_PCM_MONITOR_CAPTURE,
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
BIT_PCM_IMPULSE_PLAYBACK,
BIT_PCM_IMPULSE_CAPTURE,
#endif
BIT_PAUSE_PLAYBACK,
BIT_PREPARED
BIT_PREPARED,
/* individual masks: */
MASK_PCM_ALSA_PLAYBACK = 1 << BIT_PCM_ALSA_PLAYBACK,
MASK_PCM_ALSA_CAPTURE = 1 << BIT_PCM_ALSA_CAPTURE,
MASK_PCM_MONITOR_PLAYBACK = 1 << BIT_PCM_MONITOR_PLAYBACK,
MASK_PCM_MONITOR_CAPTURE = 1 << BIT_PCM_MONITOR_CAPTURE,
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
MASK_PCM_IMPULSE_PLAYBACK = 1 << BIT_PCM_IMPULSE_PLAYBACK,
MASK_PCM_IMPULSE_CAPTURE = 1 << BIT_PCM_IMPULSE_CAPTURE,
#endif
MASK_PAUSE_PLAYBACK = 1 << BIT_PAUSE_PLAYBACK,
MASK_PREPARED = 1 << BIT_PREPARED,
/* combined masks (by operation): */
MASK_PCM_ALSA = MASK_PCM_ALSA_PLAYBACK | MASK_PCM_ALSA_CAPTURE,
MASK_PCM_MONITOR = MASK_PCM_MONITOR_PLAYBACK | MASK_PCM_MONITOR_CAPTURE,
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
MASK_PCM_IMPULSE = MASK_PCM_IMPULSE_PLAYBACK | MASK_PCM_IMPULSE_CAPTURE,
#endif
/* combined masks (by direction): */
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
MASK_PLAYBACK = MASK_PCM_ALSA_PLAYBACK | MASK_PCM_MONITOR_PLAYBACK | MASK_PCM_IMPULSE_PLAYBACK,
MASK_CAPTURE = MASK_PCM_ALSA_CAPTURE | MASK_PCM_MONITOR_CAPTURE | MASK_PCM_IMPULSE_CAPTURE
#else
MASK_PLAYBACK = MASK_PCM_ALSA_PLAYBACK | MASK_PCM_MONITOR_PLAYBACK,
MASK_CAPTURE = MASK_PCM_ALSA_CAPTURE | MASK_PCM_MONITOR_CAPTURE
#endif
};
struct line6_pcm_properties {
@ -83,9 +132,11 @@ struct snd_line6_pcm {
struct urb *urb_audio_in[LINE6_ISO_BUFFERS];
/**
Temporary buffer to hold data when playback buffer wraps.
Temporary buffer for playback.
Since the packet size is not known in advance, this buffer is
large enough to store maximum size packets.
*/
unsigned char *wrap_out;
unsigned char *buffer_out;
/**
Temporary buffer for capture.
@ -94,6 +145,21 @@ struct snd_line6_pcm {
*/
unsigned char *buffer_in;
/**
Temporary buffer index for playback.
*/
int index_out;
/**
Previously captured frame (for software monitoring).
*/
unsigned char *prev_fbuf;
/**
Size of previously captured frame (for software monitoring).
*/
int prev_fsize;
/**
Free frame position in the playback buffer.
*/
@ -204,12 +270,36 @@ struct snd_line6_pcm {
/**
PCM playback volume (left and right).
*/
int volume[2];
int volume_playback[2];
/**
PCM monitor volume.
*/
int volume_monitor;
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
/**
Volume of impulse response test signal (if zero, test is disabled).
*/
int impulse_volume;
/**
Period of impulse response test signal.
*/
int impulse_period;
/**
Counter for impulse response test signal.
*/
int impulse_count;
#endif
/**
Several status bits (see BIT_*).
*/
unsigned long flags;
int last_frame_in, last_frame_out;
};
@ -217,6 +307,19 @@ extern int line6_init_pcm(struct usb_line6 *line6,
struct line6_pcm_properties *properties);
extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd);
extern int snd_line6_prepare(struct snd_pcm_substream *substream);
extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm);
extern int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels);
extern int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels);
#define PRINT_FRAME_DIFF(op) { \
static int diff_prev = 1000; \
int diff = line6pcm->last_frame_out - line6pcm->last_frame_in; \
if((diff != diff_prev) && (abs(diff) < 100)) { \
printk("%s frame diff = %d\n", op, diff); \
diff_prev = diff; \
} \
}
#endif

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -9,15 +9,13 @@
*
*/
#include "driver.h"
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "audio.h"
#include "capture.h"
#include "driver.h"
#include "pcm.h"
#include "pod.h"
#include "playback.h"
@ -59,22 +57,85 @@ static void change_volume(struct urb *urb_out, int volume[],
}
}
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
/*
Create signal for impulse response test.
*/
static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm,
struct urb *urb_out, int bytes_per_frame)
{
int frames = urb_out->transfer_buffer_length / bytes_per_frame;
if (bytes_per_frame == 4) {
/* TODO: add code for TonePort etc. */
} else if (bytes_per_frame == 6) {
int i, j;
unsigned char *pi = line6pcm->prev_fbuf;
unsigned char *po = urb_out->transfer_buffer;
for (i = 0; i < frames; ++i) {
for (j = 0; j < bytes_per_frame / 2; ++j)
po[j] = pi[j];
for (; j < bytes_per_frame; ++j)
po[j] = 0;
pi += bytes_per_frame;
po += bytes_per_frame;
}
if (--line6pcm->impulse_count <= 0) {
((unsigned char *)(urb_out->
transfer_buffer))[bytes_per_frame -
1] =
line6pcm->impulse_volume;
line6pcm->impulse_count = line6pcm->impulse_period;
}
}
}
#endif
/*
Add signal to buffer for software monitoring.
*/
static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
int volume, int bytes_per_frame)
{
if (volume == 0)
return; /* zero volume - no change */
if (bytes_per_frame == 4) {
short *pi, *po, *buf_end;
pi = (short *)signal;
po = (short *)urb_out->transfer_buffer;
buf_end = po + urb_out->transfer_buffer_length / sizeof(*po);
for (; po < buf_end; ++pi, ++po)
*po += (*pi * volume) >> 8;
}
/*
We don't need to handle devices with 6 bytes per frame here
since they all support hardware monitoring.
*/
}
/*
Find a free URB, prepare audio data, and submit URB.
*/
static int submit_audio_out_urb(struct snd_pcm_substream *substream)
static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
{
int index;
unsigned long flags;
int i, urb_size, urb_frames;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
const int frame_increment =
line6pcm->properties->snd_line6_rates.rats[0].num_min;
const int frame_factor =
line6pcm->properties->snd_line6_rates.rats[0].den *
(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
struct snd_pcm_runtime *runtime = substream->runtime;
struct urb *urb_out;
spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
@ -83,7 +144,7 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
if (index < 0 || index >= LINE6_ISO_BUFFERS) {
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
dev_err(s2m(substream), "no free URB found\n");
dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
return -EINVAL;
}
@ -92,24 +153,49 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
/* compute frame size for given sampling rate */
int n, fs;
int fsize = 0;
struct usb_iso_packet_descriptor *fout =
&urb_out->iso_frame_desc[i];
line6pcm->count_out += frame_increment;
n = line6pcm->count_out / frame_factor;
line6pcm->count_out -= n * frame_factor;
fs = n * bytes_per_frame;
if (line6pcm->flags & MASK_CAPTURE) {
fsize = line6pcm->prev_fsize;
}
if (fsize == 0) {
int n;
line6pcm->count_out += frame_increment;
n = line6pcm->count_out / frame_factor;
line6pcm->count_out -= n * frame_factor;
fsize = n * bytes_per_frame;
}
fout->offset = urb_size;
fout->length = fs;
urb_size += fs;
fout->length = fsize;
urb_size += fsize;
}
if (urb_size == 0) {
/* can't determine URB size */
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); /* this is somewhat paranoid */
return -EINVAL;
}
urb_frames = urb_size / bytes_per_frame;
urb_out->transfer_buffer =
line6pcm->buffer_out +
line6pcm->max_packet_size * line6pcm->index_out;
urb_out->transfer_buffer_length = urb_size;
urb_out->context = line6pcm;
if (++line6pcm->index_out == LINE6_ISO_BUFFERS)
line6pcm->index_out = 0;
if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags) &&
!test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) {
struct snd_pcm_runtime *runtime =
get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime;
if (test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) {
urb_out->transfer_buffer = line6pcm->wrap_out;
memset(line6pcm->wrap_out, 0, urb_size);
} else {
if (line6pcm->pos_out + urb_frames > runtime->buffer_size) {
/*
The transferred area goes over buffer boundary,
@ -117,38 +203,65 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
*/
int len;
len = runtime->buffer_size - line6pcm->pos_out;
urb_out->transfer_buffer = line6pcm->wrap_out;
if (len > 0) {
memcpy(line6pcm->wrap_out,
memcpy(urb_out->transfer_buffer,
runtime->dma_area +
line6pcm->pos_out * bytes_per_frame,
len * bytes_per_frame);
memcpy(line6pcm->wrap_out +
memcpy(urb_out->transfer_buffer +
len * bytes_per_frame, runtime->dma_area,
(urb_frames - len) * bytes_per_frame);
} else {
/* this is somewhat paranoid */
dev_err(s2m(substream),
"driver bug: len = %d\n", len);
}
} else
dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", len); /* this is somewhat paranoid */
} else {
#if LINE6_REUSE_DMA_AREA_FOR_PLAYBACK
/* set the buffer pointer */
urb_out->transfer_buffer =
runtime->dma_area +
line6pcm->pos_out * bytes_per_frame;
#else
/* copy data */
memcpy(urb_out->transfer_buffer,
runtime->dma_area +
line6pcm->pos_out * bytes_per_frame,
urb_out->transfer_buffer_length);
#endif
}
if ((line6pcm->pos_out += urb_frames) >= runtime->buffer_size)
line6pcm->pos_out -= runtime->buffer_size;
} else {
memset(urb_out->transfer_buffer, 0,
urb_out->transfer_buffer_length);
}
line6pcm->pos_out += urb_frames;
if (line6pcm->pos_out >= runtime->buffer_size)
line6pcm->pos_out -= runtime->buffer_size;
change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame);
urb_out->transfer_buffer_length = urb_size;
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) {
struct usb_iso_packet_descriptor *fout =
&urb_out->iso_frame_desc[i];
@ -161,8 +274,8 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
if (usb_submit_urb(urb_out, GFP_ATOMIC) == 0)
set_bit(index, &line6pcm->active_urb_out);
else
dev_err(s2m(substream), "URB out #%d submission failed\n",
index);
dev_err(line6pcm->line6->ifcdev,
"URB out #%d submission failed\n", index);
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
return 0;
@ -171,12 +284,12 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
/*
Submit all currently available playback URBs.
*/
static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream)
int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
{
int ret, i;
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
ret = submit_audio_out_urb(substream);
ret = submit_audio_out_urb(line6pcm);
if (ret < 0)
return ret;
}
@ -187,7 +300,7 @@ static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream)
/*
Unlink all currently active playback URBs.
*/
static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
{
unsigned int i;
@ -202,7 +315,7 @@ static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
}
/*
Wait until unlinking of all currently active playback URBs has been finished.
Wait until unlinking of all currently active playback URBs has been finished.
*/
static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
{
@ -223,17 +336,14 @@ static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
} while (--timeout > 0);
if (alive)
snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
line6pcm->active_urb_out = 0;
line6pcm->unlink_urb_out = 0;
}
/*
Unlink all currently active playback URBs, and wait for finishing.
*/
void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
{
unlink_audio_out_urbs(line6pcm);
line6_unlink_audio_out_urbs(line6pcm);
wait_clear_audio_out_urbs(line6pcm);
}
@ -245,10 +355,16 @@ static void audio_out_callback(struct urb *urb)
int i, index, length = 0, shutdown = 0;
unsigned long flags;
struct snd_line6_pcm *line6pcm =
(struct snd_line6_pcm *)urb->context;
struct snd_pcm_substream *substream =
(struct snd_pcm_substream *)urb->context;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
#if USE_CLEAR_BUFFER_WORKAROUND
memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
#endif
line6pcm->last_frame_out = urb->start_frame;
/* find index of URB */
for (index = LINE6_ISO_BUFFERS; index--;)
@ -262,11 +378,15 @@ static void audio_out_callback(struct urb *urb)
length += urb->iso_frame_desc[i].length;
spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
line6pcm->pos_out_done +=
length / line6pcm->properties->bytes_per_frame;
if (line6pcm->pos_out_done >= runtime->buffer_size)
line6pcm->pos_out_done -= runtime->buffer_size;
if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags)) {
struct snd_pcm_runtime *runtime = substream->runtime;
line6pcm->pos_out_done +=
length / line6pcm->properties->bytes_per_frame;
if (line6pcm->pos_out_done >= runtime->buffer_size)
line6pcm->pos_out_done -= runtime->buffer_size;
}
clear_bit(index, &line6pcm->active_urb_out);
@ -276,18 +396,20 @@ static void audio_out_callback(struct urb *urb)
break;
}
if (test_bit(index, &line6pcm->unlink_urb_out))
if (test_and_clear_bit(index, &line6pcm->unlink_urb_out))
shutdown = 1;
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
if (!shutdown) {
submit_audio_out_urb(substream);
submit_audio_out_urb(line6pcm);
line6pcm->bytes_out += length;
if (line6pcm->bytes_out >= line6pcm->period_out) {
line6pcm->bytes_out -= line6pcm->period_out;
snd_pcm_period_elapsed(substream);
if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags)) {
if ((line6pcm->bytes_out +=
length) >= line6pcm->period_out) {
line6pcm->bytes_out %= line6pcm->period_out;
snd_pcm_period_elapsed(substream);
}
}
}
}
@ -340,52 +462,40 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream,
return ret;
line6pcm->period_out = params_period_bytes(hw_params);
line6pcm->wrap_out = kmalloc(2 * LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL);
if (!line6pcm->wrap_out) {
dev_err(s2m(substream), "cannot malloc wrap_out\n");
return -ENOMEM;
}
return 0;
}
/* hw_free playback callback */
static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
unlink_wait_clear_audio_out_urbs(line6pcm);
kfree(line6pcm->wrap_out);
line6pcm->wrap_out = NULL;
return snd_pcm_lib_free_pages(substream);
}
/* trigger playback callback */
int snd_line6_playback_trigger(struct snd_pcm_substream *substream, int cmd)
int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd)
{
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
int err;
line6pcm->count_out = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (!test_and_set_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) {
err = submit_audio_out_all_urbs(substream);
#ifdef CONFIG_PM
case SNDRV_PCM_TRIGGER_RESUME:
#endif
err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_PLAYBACK);
if (err < 0) {
clear_bit(BIT_RUNNING_PLAYBACK,
&line6pcm->flags);
return err;
}
}
if (err < 0)
return err;
break;
case SNDRV_PCM_TRIGGER_STOP:
if (test_and_clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags))
unlink_audio_out_urbs(line6pcm);
#ifdef CONFIG_PM
case SNDRV_PCM_TRIGGER_SUSPEND:
#endif
err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_PLAYBACK);
if (err < 0)
return err;
break;
@ -414,17 +524,17 @@ snd_line6_playback_pointer(struct snd_pcm_substream *substream)
/* playback operators */
struct snd_pcm_ops snd_line6_playback_ops = {
.open = snd_line6_playback_open,
.close = snd_line6_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_line6_playback_hw_params,
.hw_free = snd_line6_playback_hw_free,
.prepare = snd_line6_prepare,
.trigger = snd_line6_trigger,
.pointer = snd_line6_playback_pointer,
.open = snd_line6_playback_open,
.close = snd_line6_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_line6_playback_hw_params,
.hw_free = snd_line6_playback_hw_free,
.prepare = snd_line6_prepare,
.trigger = snd_line6_trigger,
.pointer = snd_line6_playback_pointer,
};
int create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
{
int i;

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -13,18 +13,29 @@
#define PLAYBACK_H
#include <sound/pcm.h>
#include "driver.h"
#include <sound/pcm.h>
/*
When the TonePort is used with jack in full duplex mode and the outputs are
not connected, the software monitor produces an ugly noise since everything
written to the output buffer (i.e., the input signal) will be repeated in the
next period (sounds like a delay effect). As a workaround, the output buffer
is cleared after the data have been read, but there must be a better
solution. Until one is found, this workaround can be used to fix the problem.
*/
#define USE_CLEAR_BUFFER_WORKAROUND 1
extern struct snd_pcm_ops snd_line6_playback_ops;
extern int create_audio_out_urbs(struct snd_line6_pcm *line6pcm);
extern int snd_line6_playback_trigger(struct snd_pcm_substream *substream,
int cmd);
extern void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm);
extern 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

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -9,13 +9,14 @@
*
*/
#include "driver.h"
#include <linux/slab.h>
#include <linux/wait.h>
#include <sound/control.h>
#include "audio.h"
#include "capture.h"
#include "control.h"
#include "driver.h"
#include "playback.h"
#include "pod.h"
@ -45,7 +46,7 @@ enum {
POD_tuner_freq = 0x15,
POD_tuner_note = 0x16,
POD_tuner_pitch = 0x17,
POD_system_invalid = 0x7fff
POD_system_invalid = 0x10000
};
enum {
@ -69,13 +70,16 @@ static struct snd_ratden pod_ratden = {
};
static struct line6_pcm_properties pod_pcm_properties = {
.snd_line6_playback_hw = {
.snd_line6_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START),
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
#ifdef CONFIG_PM
SNDRV_PCM_INFO_RESUME |
#endif
SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.rates = SNDRV_PCM_RATE_KNOT,
.rate_min = 39062,
@ -83,17 +87,20 @@ static struct line6_pcm_properties pod_pcm_properties = {
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 60000,
.period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME, /* at least one URB must fit into one period */
.period_bytes_min = 64,
.period_bytes_max = 8192,
.periods_min = 1,
.periods_max = 1024
},
.snd_line6_capture_hw = {
.snd_line6_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_SYNC_START),
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
#ifdef CONFIG_PM
SNDRV_PCM_INFO_RESUME |
#endif
SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.rates = SNDRV_PCM_RATE_KNOT,
.rate_min = 39062,
@ -101,7 +108,7 @@ static struct line6_pcm_properties pod_pcm_properties = {
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 60000,
.period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME, /* at least one URB must fit into one period */
.period_bytes_min = 64,
.period_bytes_max = 8192,
.periods_min = 1,
.periods_max = 1024
@ -113,9 +120,19 @@ static struct line6_pcm_properties pod_pcm_properties = {
.bytes_per_frame = POD_BYTES_PER_FRAME
};
static const char pod_request_version[] = { 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 };
static const char pod_request_channel[] = { 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7 };
static const char pod_version_header[] = { 0xf2, 0x7e, 0x7f, 0x06, 0x02 };
static const char pod_request_channel[] = {
0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7
};
static const char pod_version_header[] = {
0xf2, 0x7e, 0x7f, 0x06, 0x02
};
/* forward declarations: */
static void pod_startup2(unsigned long data);
static void pod_startup3(struct usb_line6_pod *pod);
static void pod_startup4(struct usb_line6_pod *pod);
/*
@ -129,60 +146,6 @@ static void pod_mark_batch_all_dirty(struct usb_line6_pod *pod)
set_bit(i, pod->param_dirty);
}
/*
Send an asynchronous request for the POD firmware version and device ID.
*/
static int pod_version_request_async(struct usb_line6_pod *pod)
{
return line6_send_raw_message_async(&pod->line6, pod->buffer_versionreq, sizeof(pod_request_version));
}
static void pod_create_files_work(struct work_struct *work)
{
struct usb_line6_pod *pod = container_of(work, struct usb_line6_pod, create_files_work);
pod_create_files(pod->firmware_version, pod->line6.properties->device_bit, pod->line6.ifcdev);
}
static void pod_startup_timeout(unsigned long arg)
{
enum {
REQUEST_NONE,
REQUEST_DUMP,
REQUEST_VERSION
};
int request = REQUEST_NONE;
struct usb_line6_pod *pod = (struct usb_line6_pod *)arg;
if (pod->dumpreq.ok) {
if (!pod->versionreq_ok)
request = REQUEST_VERSION;
} else {
if (pod->versionreq_ok)
request = REQUEST_DUMP;
else if (pod->startup_count++ & 1)
request = REQUEST_DUMP;
else
request = REQUEST_VERSION;
}
switch (request) {
case REQUEST_DUMP:
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0);
break;
case REQUEST_VERSION:
pod_version_request_async(pod);
break;
default:
return;
}
line6_startup_delayed(&pod->dumpreq, 1, pod_startup_timeout, pod);
}
static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, int size)
{
return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, size);
@ -218,7 +181,7 @@ static void pod_store_parameter(struct usb_line6_pod *pod, int param, int value)
}
/*
Handle SAVE button
Handle SAVE button.
*/
static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int index)
{
@ -229,7 +192,7 @@ static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int ind
/*
Process a completely received message.
*/
void pod_process_message(struct usb_line6_pod *pod)
void line6_pod_process_message(struct usb_line6_pod *pod)
{
const unsigned char *buf = pod->line6.buffer_message;
@ -254,7 +217,7 @@ void pod_process_message(struct usb_line6_pod *pod)
if ((buf[1] == POD_amp_model_setup) ||
(buf[1] == POD_effect_setup))
/* these also affect other settings */
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0);
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, LINE6_DUMP_CURRENT);
break;
@ -263,7 +226,7 @@ void pod_process_message(struct usb_line6_pod *pod)
pod->channel_num = buf[1];
pod->dirty = 0;
set_bit(POD_CHANNEL_DIRTY, &pod->atomic_flags);
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0);
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, LINE6_DUMP_CURRENT);
break;
case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE:
@ -276,7 +239,6 @@ void pod_process_message(struct usb_line6_pod *pod)
case LINE6_DUMP_CURRENT:
memcpy(&pod->prog_data, buf + 7, sizeof(pod->prog_data));
pod_mark_batch_all_dirty(pod);
pod->dumpreq.ok = 1;
break;
case POD_DUMP_MEMORY:
@ -288,6 +250,7 @@ void pod_process_message(struct usb_line6_pod *pod)
}
line6_dump_finished(&pod->dumpreq);
pod_startup3(pod);
} else
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "wrong size of channel dump message (%d instead of %d)\n",
pod->line6.message_length, (int)sizeof(pod->prog_data) + 7));
@ -300,7 +263,7 @@ void pod_process_message(struct usb_line6_pod *pod)
#define PROCESS_SYSTEM_PARAM(x) \
case POD_ ## x: \
pod->x.value = value; \
wake_up_interruptible(&pod->x.wait); \
wake_up(&pod->x.wait); \
break;
switch (buf[6]) {
@ -331,7 +294,7 @@ void pod_process_message(struct usb_line6_pod *pod)
case POD_SYSEX_CLIP:
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "audio clipped\n"));
pod->clipping.value = 1;
wake_up_interruptible(&pod->clipping.wait);
wake_up(&pod->clipping.wait);
break;
case POD_SYSEX_STORE:
@ -342,17 +305,9 @@ void pod_process_message(struct usb_line6_pod *pod)
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex message %02X\n", buf[5]));
}
} else if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) {
if (pod->versionreq_ok == 0) {
pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15];
pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int)buf[10];
pod->versionreq_ok = 1;
/* Now we know the firmware version, so we schedule a bottom half
handler to create the special files: */
INIT_WORK(&pod->create_files_work, pod_create_files_work);
queue_work(line6_workqueue, &pod->create_files_work);
} else
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "multiple firmware version message\n"));
pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15];
pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int)buf[10];
pod_startup4(pod);
} else
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex header\n"));
@ -377,7 +332,7 @@ void pod_process_message(struct usb_line6_pod *pod)
*) This method fails if a param change message is "chopped" after the first
byte.
*/
void pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, int length)
void line6_pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, int length)
{
int i;
@ -412,7 +367,7 @@ static void pod_send_channel(struct usb_line6_pod *pod, int value)
/*
Transmit PODxt Pro control parameter.
*/
void pod_transmit_parameter(struct usb_line6_pod *pod, int param, int value)
void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, int value)
{
if (line6_transmit_parameter(&pod->line6, param, value) == 0)
pod_store_parameter(pod, param, value);
@ -511,7 +466,7 @@ static ssize_t get_name_generic(struct usb_line6_pod *pod, const char *str, char
char *p2;
char *last_non_space = buf;
int retval = line6_wait_dump(&pod->dumpreq, 0);
int retval = line6_dump_wait_interruptible(&pod->dumpreq);
if (retval < 0)
return retval;
@ -588,7 +543,7 @@ static ssize_t pod_get_dump(struct device *dev, struct device_attribute *attr,
{
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_pod *pod = usb_get_intfdata(interface);
int retval = line6_wait_dump(&pod->dumpreq, 0);
int retval = line6_dump_wait_interruptible(&pod->dumpreq);
if (retval < 0)
return retval;
memcpy(buf, &pod->prog_data, sizeof(pod->prog_data));
@ -606,8 +561,8 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr,
if (count != sizeof(pod->prog_data)) {
dev_err(pod->line6.ifcdev,
"data block must be exactly %zu bytes\n",
sizeof(pod->prog_data));
"data block must be exactly %d bytes\n",
(int)sizeof(pod->prog_data));
return -EINVAL;
}
@ -616,78 +571,88 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr,
}
/*
Request system parameter.
Identify system parameters related to the tuner.
*/
static bool pod_is_tuner(int code)
{
return
(code == POD_tuner_mute) ||
(code == POD_tuner_freq) ||
(code == POD_tuner_note) ||
(code == POD_tuner_pitch);
}
/*
Get system parameter (as integer).
@param tuner non-zero, if code refers to a tuner parameter
*/
static ssize_t pod_get_system_param(struct usb_line6_pod *pod, char *buf, int code, struct ValueWait *param, int tuner, int sign)
static int pod_get_system_param_int(struct usb_line6_pod *pod, int *value, int code,
struct ValueWait *param, int sign)
{
char *sysex;
int value;
static const int size = 1;
int retval = 0;
DECLARE_WAITQUEUE(wait, current);
if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner)
if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && pod_is_tuner(code))
return -ENODEV;
/* send value request to tuner: */
/* send value request to device: */
param->value = POD_system_invalid;
sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEMREQ, size);
if (!sysex)
return 0;
return -ENOMEM;
sysex[SYSEX_DATA_OFS] = code;
line6_send_sysex_message(&pod->line6, sysex, size);
kfree(sysex);
/* wait for tuner to respond: */
add_wait_queue(&param->wait, &wait);
current->state = TASK_INTERRUPTIBLE;
while (param->value == POD_system_invalid) {
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
} else
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&param->wait, &wait);
/* wait for device to respond: */
retval = wait_event_interruptible(param->wait, param->value != POD_system_invalid);
if (retval < 0)
return retval;
value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value;
*value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value;
if(*value == POD_system_invalid)
*value = 0; /* don't report uninitialized values */
return 0;
}
/*
Get system parameter (as string).
@param tuner non-zero, if code refers to a tuner parameter
*/
static ssize_t pod_get_system_param_string(struct usb_line6_pod *pod, char *buf, int code,
struct ValueWait *param, int sign)
{
int retval, value = 0;
retval = pod_get_system_param_int(pod, &value, code, param, sign);
if(retval < 0)
return retval;
return sprintf(buf, "%d\n", value);
}
/*
Send system parameter.
Send system parameter (from integer).
@param tuner non-zero, if code refers to a tuner parameter
*/
static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf,
int count, int code, unsigned short mask,
int tuner)
static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, int code)
{
char *sysex;
static const int size = 5;
unsigned short value;
unsigned long result;
int ret;
if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner)
if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && pod_is_tuner(code))
return -EINVAL;
/* send value to tuner: */
sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size);
if (!sysex)
return 0;
ret = strict_strtoul(buf, 10, &result);
if (ret)
return ret;
value = result & mask;
return -ENOMEM;
sysex[SYSEX_DATA_OFS] = code;
sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f;
sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f;
@ -695,7 +660,20 @@ static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf,
sysex[SYSEX_DATA_OFS + 4] = (value ) & 0x0f;
line6_send_sysex_message(&pod->line6, sysex, size);
kfree(sysex);
return count;
return 0;
}
/*
Send system parameter (from string).
@param tuner non-zero, if code refers to a tuner parameter
*/
static ssize_t pod_set_system_param_string(struct usb_line6_pod *pod, const char *buf,
int count, int code, unsigned short mask)
{
int retval;
unsigned short value = simple_strtoul(buf, NULL, 10) & mask;
retval = pod_set_system_param_int(pod, value, code);
return (retval < 0) ? retval : count;
}
/*
@ -706,7 +684,7 @@ static ssize_t pod_get_dump_buf(struct device *dev,
{
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_pod *pod = usb_get_intfdata(interface);
int retval = line6_wait_dump(&pod->dumpreq, 0);
int retval = line6_dump_wait_interruptible(&pod->dumpreq);
if (retval < 0)
return retval;
memcpy(buf, &pod->prog_data_buf, sizeof(pod->prog_data_buf));
@ -725,8 +703,8 @@ static ssize_t pod_set_dump_buf(struct device *dev,
if (count != sizeof(pod->prog_data)) {
dev_err(pod->line6.ifcdev,
"data block must be exactly %zu bytes\n",
sizeof(pod->prog_data));
"data block must be exactly %d bytes\n",
(int)sizeof(pod->prog_data));
return -EINVAL;
}
@ -900,53 +878,94 @@ static ssize_t pod_wait_for_clip(struct device *dev,
{
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_pod *pod = usb_get_intfdata(interface);
int err = 0;
DECLARE_WAITQUEUE(wait, current);
pod->clipping.value = 0;
add_wait_queue(&pod->clipping.wait, &wait);
current->state = TASK_INTERRUPTIBLE;
while (pod->clipping.value == 0) {
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
} else
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&pod->clipping.wait, &wait);
return err;
return wait_event_interruptible(pod->clipping.wait, pod->clipping.value != 0);
}
#define POD_GET_SYSTEM_PARAM(code, tuner, sign) \
/*
POD startup procedure.
This is a sequence of functions with special requirements (e.g., must
not run immediately after initialization, must not run in interrupt
context). After the last one has finished, the device is ready to use.
*/
static void pod_startup1(struct usb_line6_pod *pod)
{
CHECK_STARTUP_PROGRESS(pod->startup_progress, 1);
/* delay startup procedure: */
line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, (unsigned long)pod);
}
static void pod_startup2(unsigned long data)
{
struct usb_line6_pod *pod = (struct usb_line6_pod *)data;
CHECK_STARTUP_PROGRESS(pod->startup_progress, 2);
/* current channel dump: */
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, LINE6_DUMP_CURRENT);
}
static void pod_startup3(struct usb_line6_pod *pod)
{
struct usb_line6 *line6 = &pod->line6;
CHECK_STARTUP_PROGRESS(pod->startup_progress, 3);
/* request firmware version: */
line6_version_request_async(line6);
}
static void pod_startup4(struct usb_line6_pod *pod)
{
CHECK_STARTUP_PROGRESS(pod->startup_progress, 4);
/* schedule work for global work queue: */
schedule_work(&pod->startup_work);
}
static void pod_startup5(struct work_struct *work)
{
struct usb_line6_pod *pod = container_of(work, struct usb_line6_pod, startup_work);
struct usb_line6 *line6 = &pod->line6;
CHECK_STARTUP_PROGRESS(pod->startup_progress, 5);
/* serial number: */
line6_read_serial_number(&pod->line6, &pod->serial_number);
/* ALSA audio interface: */
line6_register_audio(line6);
/* device files: */
line6_pod_create_files(pod->firmware_version, line6->properties->device_bit, line6->ifcdev);
}
#define POD_GET_SYSTEM_PARAM(code, sign) \
static ssize_t pod_get_ ## code(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct usb_interface *interface = to_usb_interface(dev); \
struct usb_line6_pod *pod = usb_get_intfdata(interface); \
return pod_get_system_param(pod, buf, POD_ ## code, &pod->code, \
tuner, sign); \
return pod_get_system_param_string(pod, buf, POD_ ## code, \
&pod->code, sign); \
}
#define POD_GET_SET_SYSTEM_PARAM(code, mask, tuner, sign) \
POD_GET_SYSTEM_PARAM(code, tuner, sign) \
#define POD_GET_SET_SYSTEM_PARAM(code, mask, sign) \
POD_GET_SYSTEM_PARAM(code, sign) \
static ssize_t pod_set_ ## code(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct usb_interface *interface = to_usb_interface(dev); \
struct usb_line6_pod *pod = usb_get_intfdata(interface); \
return pod_set_system_param(pod, buf, count, POD_ ## code, mask, \
tuner); \
return pod_set_system_param_string(pod, buf, count, POD_ ## code, mask); \
}
POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0, 0);
POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0, 0);
POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 1, 0);
POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 1, 0);
POD_GET_SYSTEM_PARAM(tuner_note, 1, 1);
POD_GET_SYSTEM_PARAM(tuner_pitch, 1, 1);
POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0);
POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0);
POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 0);
POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 0);
POD_GET_SYSTEM_PARAM(tuner_note, 1);
POD_GET_SYSTEM_PARAM(tuner_pitch, 1);
#undef GET_SET_SYSTEM_PARAM
#undef GET_SYSTEM_PARAM
@ -977,10 +996,57 @@ static DEVICE_ATTR(tuner_mute, S_IWUGO | S_IRUGO, pod_get_tuner_mute, pod_set_tu
static DEVICE_ATTR(tuner_note, S_IRUGO, pod_get_tuner_note, line6_nop_write);
static DEVICE_ATTR(tuner_pitch, S_IRUGO, pod_get_tuner_pitch, line6_nop_write);
#if CREATE_RAW_FILE
#ifdef CONFIG_LINE6_USB_RAW
static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw);
#endif
/* control info callback */
static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 65535;
return 0;
}
/* control get callback */
static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6;
ucontrol->value.integer.value[0] = pod->monitor_level.value;
return 0;
}
/* control put callback */
static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6;
if(ucontrol->value.integer.value[0] == pod->monitor_level.value)
return 0;
pod->monitor_level.value = ucontrol->value.integer.value[0];
pod_set_system_param_int(pod, ucontrol->value.integer.value[0], POD_monitor_level);
return 1;
}
/* control definition */
static struct snd_kcontrol_new pod_control_monitor = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitor Playback Volume",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_pod_control_monitor_info,
.get = snd_pod_control_monitor_get,
.put = snd_pod_control_monitor_put
};
/*
POD destructor.
*/
@ -998,8 +1064,6 @@ static void pod_destruct(struct usb_interface *interface)
/* free dump request data: */
line6_dumpreq_destruct(&pod->dumpreq);
kfree(pod->buffer_versionreq);
}
/*
@ -1034,7 +1098,7 @@ static int pod_create_files2(struct device *dev)
CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_note));
CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_pitch));
#if CREATE_RAW_FILE
#ifdef CONFIG_LINE6_USB_RAW
CHECK_RETURN(device_create_file(dev, &dev_attr_raw));
#endif
@ -1042,9 +1106,9 @@ static int pod_create_files2(struct device *dev)
}
/*
Init POD device.
Try to init POD device.
*/
int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
static int pod_try_init(struct usb_interface *interface, struct usb_line6_pod *pod)
{
int err;
struct usb_line6 *line6 = &pod->line6;
@ -1062,6 +1126,8 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
init_waitqueue_head(&pod->tuner_note.wait);
init_waitqueue_head(&pod->tuner_pitch.wait);
init_waitqueue_head(&pod->clipping.wait);
init_timer(&pod->startup_timer);
INIT_WORK(&pod->startup_work, pod_startup5);
memset(pod->param_dirty, 0xff, sizeof(pod->param_dirty));
@ -1070,69 +1136,73 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
sizeof(pod_request_channel));
if (err < 0) {
dev_err(&interface->dev, "Out of memory\n");
pod_destruct(interface);
return -ENOMEM;
}
pod->buffer_versionreq = kmemdup(pod_request_version,
sizeof(pod_request_version),
GFP_KERNEL);
if (pod->buffer_versionreq == NULL) {
dev_err(&interface->dev, "Out of memory\n");
pod_destruct(interface);
return -ENOMEM;
}
/* create sysfs entries: */
err = pod_create_files2(&interface->dev);
if (err < 0) {
pod_destruct(interface);
return err;
}
/* initialize audio system: */
err = line6_init_audio(line6);
if (err < 0) {
pod_destruct(interface);
return err;
}
/* initialize MIDI subsystem: */
err = line6_init_midi(line6);
if (err < 0) {
pod_destruct(interface);
return err;
}
/* initialize PCM subsystem: */
err = line6_init_pcm(line6, &pod_pcm_properties);
if (err < 0) {
pod_destruct(interface);
return err;
}
/* register audio system: */
err = line6_register_audio(line6);
/* register monitor control: */
err = snd_ctl_add(line6->card, snd_ctl_new1(&pod_control_monitor, line6->line6pcm));
if (err < 0) {
pod_destruct(interface);
return err;
}
/*
When the sound card is registered at this point, the PODxt Live
displays "Invalid Code Error 07", so we do it later in the event
handler.
*/
if (pod->line6.properties->capabilities & LINE6_BIT_CONTROL) {
/* query some data: */
line6_startup_delayed(&pod->dumpreq, POD_STARTUP_DELAY,
pod_startup_timeout, pod);
line6_read_serial_number(&pod->line6, &pod->serial_number);
pod->monitor_level.value = POD_system_invalid;
/* initiate startup procedure: */
pod_startup1(pod);
}
return 0;
}
/*
Init POD device (and clean up in case of failure).
*/
int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
{
int err = pod_try_init(interface, pod);
if (err < 0) {
pod_destruct(interface);
}
return err;
}
/*
POD device disconnected.
*/
void pod_disconnect(struct usb_interface *interface)
void line6_pod_disconnect(struct usb_interface *interface)
{
struct usb_line6_pod *pod;
@ -1145,14 +1215,12 @@ void pod_disconnect(struct usb_interface *interface)
struct device *dev = &interface->dev;
if (line6pcm != NULL) {
unlink_wait_clear_audio_out_urbs(line6pcm);
unlink_wait_clear_audio_in_urbs(line6pcm);
line6_pcm_disconnect(line6pcm);
}
if (dev != NULL) {
/* remove sysfs entries: */
if (pod->versionreq_ok)
pod_remove_files(pod->firmware_version, pod->line6.properties->device_bit, dev);
line6_pod_remove_files(pod->firmware_version, pod->line6.properties->device_bit, dev);
device_remove_file(dev, &dev_attr_channel);
device_remove_file(dev, &dev_attr_clip);
@ -1179,7 +1247,7 @@ void pod_disconnect(struct usb_interface *interface)
device_remove_file(dev, &dev_attr_tuner_note);
device_remove_file(dev, &dev_attr_tuner_pitch);
#if CREATE_RAW_FILE
#ifdef CONFIG_LINE6_USB_RAW
device_remove_file(dev, &dev_attr_raw);
#endif
}

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -13,15 +13,14 @@
#define POD_H
#include "driver.h"
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include "driver.h"
#include "dumprequest.h"
@ -42,163 +41,156 @@
*/
#define POD_CONTROL_SIZE 0x80
#define POD_BUFSIZE_DUMPREQ 7
#define POD_STARTUP_DELAY 3
#define POD_STARTUP_DELAY 3000
/**
Data structure for values that need to be requested explicitly.
This is the case for system and tuner settings.
Data structure for values that need to be requested explicitly.
This is the case for system and tuner settings.
*/
struct ValueWait {
unsigned short value;
int value;
wait_queue_head_t wait;
};
/**
Binary PodXT Pro program dump
Binary PODxt Pro program dump
*/
struct pod_program {
/**
Header information (including program name).
Header information (including program name).
*/
unsigned char header[0x20];
/**
Program parameters.
Program parameters.
*/
unsigned char control[POD_CONTROL_SIZE];
};
struct usb_line6_pod {
/**
Generic Line6 USB data.
Generic Line6 USB data.
*/
struct usb_line6 line6;
/**
Dump request structure.
Dump request structure.
*/
struct line6_dump_request dumpreq;
/**
Current program number.
Current program number.
*/
unsigned char channel_num;
/**
Current program settings.
Current program settings.
*/
struct pod_program prog_data;
/**
Buffer for data retrieved from or to be stored on PODxt Pro.
Buffer for data retrieved from or to be stored on PODxt Pro.
*/
struct pod_program prog_data_buf;
/**
Buffer for requesting version number.
*/
unsigned char *buffer_versionreq;
/**
Tuner mute mode.
Tuner mute mode.
*/
struct ValueWait tuner_mute;
/**
Tuner base frequency (typically 440Hz).
Tuner base frequency (typically 440Hz).
*/
struct ValueWait tuner_freq;
/**
Note received from tuner.
Note received from tuner.
*/
struct ValueWait tuner_note;
/**
Pitch value received from tuner.
Pitch value received from tuner.
*/
struct ValueWait tuner_pitch;
/**
Instrument monitor level.
Instrument monitor level.
*/
struct ValueWait monitor_level;
/**
Audio routing mode.
0: send processed guitar
1: send clean guitar
2: send clean guitar re-amp playback
3: send re-amp playback
Audio routing mode.
0: send processed guitar
1: send clean guitar
2: send clean guitar re-amp playback
3: send re-amp playback
*/
struct ValueWait routing;
/**
Wait for audio clipping event.
Wait for audio clipping event.
*/
struct ValueWait clipping;
/**
Bottom-half for creation of sysfs special files.
Timer for device initializaton.
*/
struct work_struct create_files_work;
struct timer_list startup_timer;
/**
Dirty flags for access to parameter data.
Work handler for device initializaton.
*/
struct work_struct startup_work;
/**
Current progress in startup procedure.
*/
int startup_progress;
/**
Dirty flags for access to parameter data.
*/
unsigned long param_dirty[POD_CONTROL_SIZE / sizeof(unsigned long)];
/**
Some atomic flags.
Some atomic flags.
*/
unsigned long atomic_flags;
/**
Counter for startup process.
*/
int startup_count;
/**
Serial number of device.
Serial number of device.
*/
int serial_number;
/**
Firmware version (x 100).
Firmware version (x 100).
*/
int firmware_version;
/**
Device ID.
Device ID.
*/
int device_id;
/**
Flag to indicate modification of current program settings.
Flag to indicate modification of current program settings.
*/
char dirty;
/**
Flag if initial firmware version request has been successful.
*/
char versionreq_ok;
/**
Flag to enable MIDI postprocessing.
Flag to enable MIDI postprocessing.
*/
char midi_postprocess;
};
extern void pod_disconnect(struct usb_interface *interface);
extern int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod);
extern void pod_midi_postprocess(struct usb_line6_pod *pod,
unsigned char *data, int length);
extern void pod_process_message(struct usb_line6_pod *pod);
extern void pod_receive_parameter(struct usb_line6_pod *pod, int param);
extern void pod_transmit_parameter(struct usb_line6_pod *pod, int param,
int value);
extern void line6_pod_disconnect(struct usb_interface *interface);
extern int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod);
extern void line6_pod_midi_postprocess(struct usb_line6_pod *pod,
unsigned char *data, int length);
extern void line6_pod_process_message(struct usb_line6_pod *pod);
extern void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param,
int value);
#endif

View file

@ -1,4 +1,4 @@
#ifndef DRIVER_REVISION
/* current subversion revision */
#define DRIVER_REVISION " (revision 529)"
#define DRIVER_REVISION " (revision 665)"
#endif

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
* Emil Myhrman (emil.myhrman@gmail.com)
*
* This program is free software; you can redistribute it and/or
@ -10,15 +10,22 @@
*
*/
#include "driver.h"
#include <linux/wait.h>
#include <sound/control.h>
#include "audio.h"
#include "capture.h"
#include "driver.h"
#include "playback.h"
#include "toneport.h"
static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2);
#define TONEPORT_PCM_DELAY 1
static struct snd_ratden toneport_ratden = {
.num_min = 44100,
.num_max = 44100,
@ -28,43 +35,52 @@ static struct snd_ratden toneport_ratden = {
static struct line6_pcm_properties toneport_pcm_properties = {
.snd_line6_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_KNOT,
.rate_min = 44100,
.rate_max = 44100,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 60000,
.period_bytes_min = 180 * 4,
.period_bytes_max = 8192,
.periods_min = 1,
.periods_max = 1024},
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
#ifdef CONFIG_PM
SNDRV_PCM_INFO_RESUME |
#endif
SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_KNOT,
.rate_min = 44100,
.rate_max = 44100,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 60000,
.period_bytes_min = 64,
.period_bytes_max = 8192,
.periods_min = 1,
.periods_max = 1024
},
.snd_line6_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_KNOT,
.rate_min = 44100,
.rate_max = 44100,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 60000,
.period_bytes_min = 188 * 4,
.period_bytes_max = 8192,
.periods_min = 1,
.periods_max = 1024},
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
#ifdef CONFIG_PM
SNDRV_PCM_INFO_RESUME |
#endif
SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_KNOT,
.rate_min = 44100,
.rate_max = 44100,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 60000,
.period_bytes_min = 64,
.period_bytes_max = 8192,
.periods_min = 1,
.periods_max = 1024
},
.snd_line6_rates = {
.nrats = 1,
.rats = &toneport_ratden},
.nrats = 1,
.rats = &toneport_ratden
},
.bytes_per_frame = 4
};
@ -77,6 +93,27 @@ static struct line6_pcm_properties toneport_pcm_properties = {
static int led_red = 0x00;
static int led_green = 0x26;
struct ToneportSourceInfo
{
const char *name;
int code;
};
static const struct ToneportSourceInfo toneport_source_info[] = {
{ "Microphone", 0x0a01 },
{ "Line" , 0x0801 },
{ "Instrument", 0x0b01 },
{ "Inst & Mic", 0x0901 }
};
static bool toneport_has_led(short product)
{
return
(product == LINE6_DEVID_GUITARPORT) ||
(product == LINE6_DEVID_TONEPORT_GX);
/* add your device here if you are missing support for the LEDs */
}
static void toneport_update_led(struct device *dev)
{
struct usb_interface *interface = to_usb_interface(dev);
@ -129,6 +166,7 @@ static DEVICE_ATTR(led_red, S_IWUGO | S_IRUGO, line6_nop_read,
static DEVICE_ATTR(led_green, S_IWUGO | S_IRUGO, line6_nop_read,
toneport_set_led_green);
static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
{
int ret;
@ -145,6 +183,111 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
return 0;
}
/* monitor info callback */
static int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 256;
return 0;
}
/* monitor get callback */
static int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = line6pcm->volume_monitor;
return 0;
}
/* monitor put callback */
static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
if(ucontrol->value.integer.value[0] == line6pcm->volume_monitor)
return 0;
line6pcm->volume_monitor = ucontrol->value.integer.value[0];
return 1;
}
/* source info callback */
static int snd_toneport_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
const int size = ARRAY_SIZE(toneport_source_info);
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = size;
if(uinfo->value.enumerated.item >= size)
uinfo->value.enumerated.item = size - 1;
strcpy(uinfo->value.enumerated.name,
toneport_source_info[uinfo->value.enumerated.item].name);
return 0;
}
/* source get callback */
static int snd_toneport_source_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)line6pcm->line6;
ucontrol->value.enumerated.item[0] = toneport->source;
return 0;
}
/* source put callback */
static int snd_toneport_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)line6pcm->line6;
if(ucontrol->value.enumerated.item[0] == toneport->source)
return 0;
toneport->source = ucontrol->value.enumerated.item[0];
toneport_send_cmd(toneport->line6.usbdev, toneport_source_info[toneport->source].code, 0x0000);
return 1;
}
static void toneport_start_pcm(unsigned long arg)
{
struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg;
struct usb_line6 *line6 = &toneport->line6;
line6_pcm_start(line6->line6pcm, MASK_PCM_MONITOR);
}
/* control definition */
static struct snd_kcontrol_new toneport_control_monitor = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitor Playback Volume",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_toneport_monitor_info,
.get = snd_toneport_monitor_get,
.put = snd_toneport_monitor_put
};
/* source selector definition */
static struct snd_kcontrol_new toneport_control_source = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Source",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_toneport_source_info,
.get = snd_toneport_source_get,
.put = snd_toneport_source_put
};
/*
Toneport destructor.
*/
@ -162,14 +305,41 @@ static void toneport_destruct(struct usb_interface *interface)
}
/*
Init Toneport device.
Setup Toneport device.
*/
int toneport_init(struct usb_interface *interface,
struct usb_line6_toneport *toneport)
static void toneport_setup(struct usb_line6_toneport *toneport)
{
int err, ticks;
int ticks;
struct usb_line6 *line6 = &toneport->line6;
struct usb_device *usbdev;
struct usb_device *usbdev = line6->usbdev;
/* sync time on device with host: */
ticks = (int)get_seconds();
line6_write_data(line6, 0x80c6, &ticks, 4);
/* enable device: */
toneport_send_cmd(usbdev, 0x0301, 0x0000);
/* initialize source select: */
switch(usbdev->descriptor.idProduct) {
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_PODSTUDIO_UX1:
toneport_send_cmd(usbdev, toneport_source_info[toneport->source].code, 0x0000);
}
if (toneport_has_led(usbdev->descriptor.idProduct))
toneport_update_led(&usbdev->dev);
}
/*
Try to init Toneport device.
*/
static int toneport_try_init(struct usb_interface *interface,
struct usb_line6_toneport *toneport)
{
int err;
struct usb_line6 *line6 = &toneport->line6;
struct usb_device *usbdev = line6->usbdev;
if ((interface == NULL) || (toneport == NULL))
return -ENODEV;
@ -177,64 +347,93 @@ int toneport_init(struct usb_interface *interface,
/* initialize audio system: */
err = line6_init_audio(line6);
if (err < 0) {
toneport_destruct(interface);
return err;
}
/* initialize PCM subsystem: */
err = line6_init_pcm(line6, &toneport_pcm_properties);
if (err < 0) {
toneport_destruct(interface);
return err;
}
/* register monitor control: */
err = snd_ctl_add(line6->card, snd_ctl_new1(&toneport_control_monitor, line6->line6pcm));
if (err < 0) {
return err;
}
/* register source select control: */
switch(usbdev->descriptor.idProduct) {
case LINE6_DEVID_TONEPORT_UX1:
case LINE6_DEVID_PODSTUDIO_UX1:
err = snd_ctl_add(line6->card, snd_ctl_new1(&toneport_control_source, line6->line6pcm));
if (err < 0) {
return err;
}
}
/* register audio system: */
err = line6_register_audio(line6);
if (err < 0) {
toneport_destruct(interface);
return err;
}
usbdev = line6->usbdev;
line6_read_serial_number(line6, &toneport->serial_number);
line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1);
/* sync time on device with host: */
ticks = (int)get_seconds();
line6_write_data(line6, 0x80c6, &ticks, 4);
/*
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) {
CHECK_RETURN(device_create_file
(&interface->dev, &dev_attr_led_red));
CHECK_RETURN(device_create_file
(&interface->dev, &dev_attr_led_green));
toneport_update_led(&usbdev->dev);
if (toneport_has_led(usbdev->descriptor.idProduct)) {
CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_red));
CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_green));
}
toneport_setup(toneport);
init_timer(&toneport->timer);
toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ;
toneport->timer.function = toneport_start_pcm;
toneport->timer.data = (unsigned long)toneport;
add_timer(&toneport->timer);
return 0;
}
/*
Init Toneport device (and clean up in case of failure).
*/
int line6_toneport_init(struct usb_interface *interface,
struct usb_line6_toneport *toneport)
{
int err = toneport_try_init(interface, toneport);
if (err < 0) {
toneport_destruct(interface);
}
return err;
}
/*
Resume Toneport device after reset.
*/
void line6_toneport_reset_resume(struct usb_line6_toneport *toneport)
{
toneport_setup(toneport);
}
/*
Toneport device disconnected.
*/
void toneport_disconnect(struct usb_interface *interface)
void line6_toneport_disconnect(struct usb_interface *interface)
{
struct usb_line6_toneport *toneport;
if (interface == NULL)
return;
toneport = usb_get_intfdata(interface);
if (toneport->line6.usbdev->descriptor.idProduct !=
LINE6_DEVID_GUITARPORT) {
toneport = usb_get_intfdata(interface);
del_timer_sync(&toneport->timer);
if (toneport_has_led(toneport->line6.usbdev->descriptor.idProduct)) {
device_remove_file(&interface->dev, &dev_attr_led_red);
device_remove_file(&interface->dev, &dev_attr_led_green);
}
@ -243,8 +442,8 @@ void toneport_disconnect(struct usb_interface *interface)
struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm;
if (line6pcm != NULL) {
unlink_wait_clear_audio_out_urbs(line6pcm);
unlink_wait_clear_audio_in_urbs(line6pcm);
line6_pcm_stop(line6pcm, MASK_PCM_MONITOR);
line6_pcm_disconnect(line6pcm);
}
}

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -13,33 +13,44 @@
#define TONEPORT_H
#include "driver.h"
#include <linux/usb.h>
#include <sound/core.h>
#include "driver.h"
struct usb_line6_toneport {
/**
Generic Line6 USB data.
Generic Line6 USB data.
*/
struct usb_line6 line6;
/**
Serial number of device.
Source selector.
*/
int source;
/**
Serial number of device.
*/
int serial_number;
/**
Firmware version (x 100).
Firmware version (x 100).
*/
int firmware_version;
/**
Timer for delayed PCM startup.
*/
struct timer_list timer;
};
extern void toneport_disconnect(struct usb_interface *interface);
extern int toneport_init(struct usb_interface *interface,
struct usb_line6_toneport *toneport);
extern void line6_toneport_disconnect(struct usb_interface *interface);
extern int line6_toneport_init(struct usb_interface *interface,
struct usb_line6_toneport *toneport);
extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport);
#endif

View file

@ -1,5 +1,5 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2005-2008 Markus Grabner (grabner@icg.tugraz.at)
*
@ -25,6 +25,9 @@
#define LINE6_DEVID_BASSPODXTPRO 0x4252
#define LINE6_DEVID_GUITARPORT 0x4750
#define LINE6_DEVID_POCKETPOD 0x5051
#define LINE6_DEVID_PODSTUDIO_GX 0x4153
#define LINE6_DEVID_PODSTUDIO_UX1 0x4150
#define LINE6_DEVID_PODSTUDIO_UX2 0x4151
#define LINE6_DEVID_PODX3 0x414a
#define LINE6_DEVID_PODX3LIVE 0x414b
#define LINE6_DEVID_PODXT 0x5044
@ -35,20 +38,23 @@
#define LINE6_DEVID_TONEPORT_UX2 0x4142
#define LINE6_DEVID_VARIAX 0x534d
#define LINE6_BIT_BASSPODXT (1 << 0)
#define LINE6_BIT_BASSPODXTLIVE (1 << 1)
#define LINE6_BIT_BASSPODXTPRO (1 << 2)
#define LINE6_BIT_GUITARPORT (1 << 3)
#define LINE6_BIT_POCKETPOD (1 << 4)
#define LINE6_BIT_PODX3 (1 << 5)
#define LINE6_BIT_PODX3LIVE (1 << 6)
#define LINE6_BIT_PODXT (1 << 7)
#define LINE6_BIT_PODXTLIVE (1 << 8)
#define LINE6_BIT_PODXTPRO (1 << 9)
#define LINE6_BIT_TONEPORT_GX (1 << 10)
#define LINE6_BIT_TONEPORT_UX1 (1 << 11)
#define LINE6_BIT_TONEPORT_UX2 (1 << 12)
#define LINE6_BIT_VARIAX (1 << 13)
#define LINE6_BIT_BASSPODXT (1 << 0)
#define LINE6_BIT_BASSPODXTLIVE (1 << 1)
#define LINE6_BIT_BASSPODXTPRO (1 << 2)
#define LINE6_BIT_GUITARPORT (1 << 3)
#define LINE6_BIT_POCKETPOD (1 << 4)
#define LINE6_BIT_PODSTUDIO_GX (1 << 5)
#define LINE6_BIT_PODSTUDIO_UX1 (1 << 6)
#define LINE6_BIT_PODSTUDIO_UX2 (1 << 7)
#define LINE6_BIT_PODX3 (1 << 8)
#define LINE6_BIT_PODX3LIVE (1 << 9)
#define LINE6_BIT_PODXT (1 << 10)
#define LINE6_BIT_PODXTLIVE (1 << 11)
#define LINE6_BIT_PODXTPRO (1 << 12)
#define LINE6_BIT_TONEPORT_GX (1 << 13)
#define LINE6_BIT_TONEPORT_UX1 (1 << 14)
#define LINE6_BIT_TONEPORT_UX2 (1 << 15)
#define LINE6_BIT_VARIAX (1 << 16)
#define LINE6_BITS_PRO (LINE6_BIT_BASSPODXTPRO | \
LINE6_BIT_PODXTPRO)
@ -66,9 +72,13 @@
#define LINE6_BIT_CONTROL (1 << 0)
/* device supports PCM input/output via USB */
#define LINE6_BIT_PCM (1 << 1)
#define LINE6_BIT_CONTROL_PCM (LINE6_BIT_CONTROL | LINE6_BIT_PCM)
/* device support hardware monitoring */
#define LINE6_BIT_HWMON (1 << 2)
#define LINE6_BIT_CONTROL_PCM_HWMON (LINE6_BIT_CONTROL | LINE6_BIT_PCM | LINE6_BIT_HWMON)
#define LINE6_FALLBACK_INTERVAL 10
#define LINE6_FALLBACK_MAXPACKETSIZE 16
#endif

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -9,12 +9,11 @@
*
*/
#include "driver.h"
#include <linux/slab.h>
#include "audio.h"
#include "control.h"
#include "driver.h"
#include "variax.h"
@ -26,18 +25,46 @@
#define VARIAX_OFFSET_ACTIVATE 7
/*
This message is sent by the device during initialization and identifies
the connected guitar model.
*/
static const char variax_init_model[] = {
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x69, 0x02,
0x00
};
/*
This message is sent by the device during initialization and identifies
the connected guitar version.
*/
static const char variax_init_version[] = {
0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c,
0x07, 0x00, 0x00, 0x00
};
/*
This message is the last one sent by the device during initialization.
*/
static const char variax_init_done[] = {
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
};
static const char variax_activate[] = {
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
0xf7
};
static const char variax_request_bank[] = {
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6d, 0xf7
};
static const char variax_request_model1[] = {
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x03,
0x00, 0x00, 0x00, 0xf7
};
static const char variax_request_model2[] = {
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x03,
@ -45,6 +72,13 @@ static const char variax_request_model2[] = {
};
/* forward declarations: */
static int variax_create_files2(struct device *dev);
static void variax_startup2(unsigned long data);
static void variax_startup4(unsigned long data);
static void variax_startup5(unsigned long data);
/*
Decode data transmitted by workbench.
*/
@ -60,42 +94,93 @@ static void variax_decode(const unsigned char *raw_data, unsigned char *data,
}
}
static void variax_activate_timeout(unsigned long arg)
static void variax_activate_async(struct usb_line6_variax *variax, int a)
{
struct usb_line6_variax *variax = (struct usb_line6_variax *)arg;
variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = 1;
variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
sizeof(variax_activate));
}
/*
Send an asynchronous activation request after a given interval.
Variax startup procedure.
This is a sequence of functions with special requirements (e.g., must
not run immediately after initialization, must not run in interrupt
context). After the last one has finished, the device is ready to use.
*/
static void variax_activate_delayed(struct usb_line6_variax *variax,
int seconds)
static void variax_startup1(struct usb_line6_variax *variax)
{
variax->activate_timer.expires = jiffies + seconds * HZ;
variax->activate_timer.function = variax_activate_timeout;
variax->activate_timer.data = (unsigned long)variax;
add_timer(&variax->activate_timer);
CHECK_STARTUP_PROGRESS(variax->startup_progress, 1);
/* delay startup procedure: */
line6_start_timer(&variax->startup_timer, VARIAX_STARTUP_DELAY1, variax_startup2, (unsigned long)variax);
}
static void variax_startup_timeout(unsigned long arg)
static void variax_startup2(unsigned long data)
{
struct usb_line6_variax *variax = (struct usb_line6_variax *)arg;
struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
struct usb_line6 *line6 = &variax->line6;
CHECK_STARTUP_PROGRESS(variax->startup_progress, 2);
if (variax->dumpreq.ok)
return;
/* request firmware version: */
line6_version_request_async(line6);
}
line6_dump_request_async(&variax->dumpreq, &variax->line6, 0);
line6_startup_delayed(&variax->dumpreq, 1, variax_startup_timeout,
variax);
static void variax_startup3(struct usb_line6_variax *variax)
{
CHECK_STARTUP_PROGRESS(variax->startup_progress, 3);
/* delay startup procedure: */
line6_start_timer(&variax->startup_timer, VARIAX_STARTUP_DELAY3, variax_startup4, (unsigned long)variax);
}
static void variax_startup4(unsigned long data)
{
struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
CHECK_STARTUP_PROGRESS(variax->startup_progress, 4);
/* activate device: */
variax_activate_async(variax, 1);
line6_start_timer(&variax->startup_timer, VARIAX_STARTUP_DELAY4, variax_startup5, (unsigned long)variax);
}
static void variax_startup5(unsigned long data)
{
struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
CHECK_STARTUP_PROGRESS(variax->startup_progress, 5);
/* current model dump: */
line6_dump_request_async(&variax->dumpreq, &variax->line6, 0, VARIAX_DUMP_PASS1);
/* passes 2 and 3 are performed implicitly before entering variax_startup6 */
}
static void variax_startup6(struct usb_line6_variax *variax)
{
CHECK_STARTUP_PROGRESS(variax->startup_progress, 6);
/* schedule work for global work queue: */
schedule_work(&variax->startup_work);
}
static void variax_startup7(struct work_struct *work)
{
struct usb_line6_variax *variax = container_of(work, struct usb_line6_variax, startup_work);
struct usb_line6 *line6 = &variax->line6;
CHECK_STARTUP_PROGRESS(variax->startup_progress, 7);
/* ALSA audio interface: */
line6_register_audio(&variax->line6);
/* device files: */
line6_variax_create_files(0, 0, line6->ifcdev);
variax_create_files2(line6->ifcdev);
}
/*
Process a completely received message.
*/
void variax_process_message(struct usb_line6_variax *variax)
void line6_variax_process_message(struct usb_line6_variax *variax)
{
const unsigned char *buf = variax->line6.buffer_message;
@ -115,12 +200,11 @@ void variax_process_message(struct usb_line6_variax *variax)
case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE:
case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST:
variax->model = buf[1];
line6_dump_request_async(&variax->dumpreq, &variax->line6, 0);
line6_dump_request_async(&variax->dumpreq, &variax->line6, 0, VARIAX_DUMP_PASS1);
break;
case LINE6_RESET:
dev_info(variax->line6.ifcdev, "VARIAX reset\n");
variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY);
break;
case LINE6_SYSEX_BEGIN:
@ -132,8 +216,7 @@ void variax_process_message(struct usb_line6_variax *variax)
case VARIAX_DUMP_PASS1:
variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH, (unsigned char *)&variax->model_data,
(sizeof(variax->model_data.name) + sizeof(variax->model_data.control) / 2) * 2);
line6_dump_request_async(&variax->dumpreq, &variax->line6, 1);
line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS2);
line6_dump_request_async(&variax->dumpreq, &variax->line6, 1, VARIAX_DUMP_PASS2);
break;
case VARIAX_DUMP_PASS2:
@ -141,21 +224,31 @@ void variax_process_message(struct usb_line6_variax *variax)
variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH,
(unsigned char *)&variax->model_data.control + sizeof(variax->model_data.control) / 2,
sizeof(variax->model_data.control) / 2 * 2);
variax->dumpreq.ok = 1;
line6_dump_request_async(&variax->dumpreq, &variax->line6, 2);
line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS3);
line6_dump_request_async(&variax->dumpreq, &variax->line6, 2, VARIAX_DUMP_PASS3);
}
} else {
DEBUG_MESSAGES(dev_err(variax->line6.ifcdev, "illegal length %d of model data\n", variax->line6.message_length));
line6_dump_finished(&variax->dumpreq);
}
} else if (memcmp(buf + 1, variax_request_bank + 1,
sizeof(variax_request_bank) - 2) == 0) {
sizeof(variax_request_bank) - 2) == 0) {
memcpy(variax->bank,
buf + sizeof(variax_request_bank) - 1,
sizeof(variax->bank));
variax->dumpreq.ok = 1;
line6_dump_finished(&variax->dumpreq);
variax_startup6(variax);
} else if (memcmp(buf + 1, variax_init_model + 1,
sizeof(variax_init_model) - 1) == 0) {
memcpy(variax->guitar,
buf + sizeof(variax_init_model),
sizeof(variax->guitar));
} else if (memcmp(buf + 1, variax_init_version + 1,
sizeof(variax_init_version) - 1) == 0) {
variax_startup3(variax);
} else if (memcmp(buf + 1, variax_init_done + 1,
sizeof(variax_init_done) - 1) == 0) {
/* notify of complete initialization: */
variax_startup4((unsigned long)variax);
}
break;
@ -256,9 +349,7 @@ static ssize_t variax_set_active(struct device *dev,
if (ret)
return ret;
variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = value ? 1 : 0;
line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
sizeof(variax_activate));
variax_activate_async(variax, value ? 1 : 0);
return count;
}
@ -317,7 +408,7 @@ static ssize_t variax_get_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
line6_wait_dump(&variax->dumpreq, 0);
line6_dump_wait_interruptible(&variax->dumpreq);
return get_string(buf, variax->model_data.name,
sizeof(variax->model_data.name));
}
@ -329,7 +420,7 @@ static ssize_t variax_get_bank(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
line6_wait_dump(&variax->dumpreq, 0);
line6_dump_wait_interruptible(&variax->dumpreq);
return get_string(buf, variax->bank, sizeof(variax->bank));
}
@ -341,7 +432,7 @@ static ssize_t variax_get_dump(struct device *dev,
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
int retval;
retval = line6_wait_dump(&variax->dumpreq, 0);
retval = line6_dump_wait_interruptible(&variax->dumpreq);
if (retval < 0)
return retval;
memcpy(buf, &variax->model_data.control,
@ -349,7 +440,22 @@ static ssize_t variax_get_dump(struct device *dev,
return sizeof(variax->model_data.control);
}
#if CREATE_RAW_FILE
/*
"read" request on "guitar" special file.
*/
static ssize_t variax_get_guitar(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
return sprintf(buf, "%s\n", variax->guitar);
}
#ifdef CONFIG_LINE6_USB_RAW
static char *variax_alloc_sysex_buffer(struct usb_line6_variax *variax, int code, int size)
{
return line6_alloc_sysex_buffer(&variax->line6, VARIAX_SYSEX_CODE, code, size);
}
/*
"write" request on "raw" special file.
@ -396,8 +502,9 @@ static DEVICE_ATTR(name, S_IRUGO, variax_get_name, line6_nop_write);
static DEVICE_ATTR(bank, S_IRUGO, variax_get_bank, line6_nop_write);
static DEVICE_ATTR(dump, S_IRUGO, variax_get_dump, line6_nop_write);
static DEVICE_ATTR(active, S_IWUGO | S_IRUGO, variax_get_active, variax_set_active);
static DEVICE_ATTR(guitar, S_IRUGO, variax_get_guitar, line6_nop_write);
#if CREATE_RAW_FILE
#ifdef CONFIG_LINE6_USB_RAW
static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw);
static DEVICE_ATTR(raw2, S_IWUGO, line6_nop_read, variax_set_raw2);
#endif
@ -424,7 +531,6 @@ static void variax_destruct(struct usb_interface *interface)
line6_dumpreq_destruct(&variax->dumpreq);
kfree(variax->buffer_activate);
del_timer_sync(&variax->activate_timer);
}
/*
@ -440,7 +546,8 @@ static int variax_create_files2(struct device *dev)
CHECK_RETURN(device_create_file(dev, &dev_attr_bank));
CHECK_RETURN(device_create_file(dev, &dev_attr_dump));
CHECK_RETURN(device_create_file(dev, &dev_attr_active));
#if CREATE_RAW_FILE
CHECK_RETURN(device_create_file(dev, &dev_attr_guitar));
#ifdef CONFIG_LINE6_USB_RAW
CHECK_RETURN(device_create_file(dev, &dev_attr_raw));
CHECK_RETURN(device_create_file(dev, &dev_attr_raw2));
#endif
@ -448,23 +555,25 @@ static int variax_create_files2(struct device *dev)
}
/*
Init workbench device.
Try to init workbench device.
*/
int variax_init(struct usb_interface *interface,
struct usb_line6_variax *variax)
static int variax_try_init(struct usb_interface *interface,
struct usb_line6_variax *variax)
{
int err;
if ((interface == NULL) || (variax == NULL))
return -ENODEV;
init_timer(&variax->startup_timer);
INIT_WORK(&variax->startup_work, variax_startup7);
/* initialize USB buffers: */
err = line6_dumpreq_init(&variax->dumpreq, variax_request_model1,
sizeof(variax_request_model1));
if (err < 0) {
dev_err(&interface->dev, "Out of memory\n");
variax_destruct(interface);
return err;
}
@ -473,7 +582,6 @@ int variax_init(struct usb_interface *interface,
if (err < 0) {
dev_err(&interface->dev, "Out of memory\n");
variax_destruct(interface);
return err;
}
@ -482,7 +590,6 @@ int variax_init(struct usb_interface *interface,
if (err < 0) {
dev_err(&interface->dev, "Out of memory\n");
variax_destruct(interface);
return err;
}
@ -491,56 +598,45 @@ int variax_init(struct usb_interface *interface,
if (variax->buffer_activate == NULL) {
dev_err(&interface->dev, "Out of memory\n");
variax_destruct(interface);
return -ENOMEM;
}
init_timer(&variax->activate_timer);
/* create sysfs entries: */
err = variax_create_files(0, 0, &interface->dev);
if (err < 0) {
variax_destruct(interface);
return err;
}
err = variax_create_files2(&interface->dev);
if (err < 0) {
variax_destruct(interface);
return err;
}
/* initialize audio system: */
err = line6_init_audio(&variax->line6);
if (err < 0) {
variax_destruct(interface);
return err;
}
/* initialize MIDI subsystem: */
err = line6_init_midi(&variax->line6);
if (err < 0) {
variax_destruct(interface);
return err;
}
/* register audio system: */
err = line6_register_audio(&variax->line6);
/* initiate startup procedure: */
variax_startup1(variax);
return 0;
}
/*
Init workbench device (and clean up in case of failure).
*/
int line6_variax_init(struct usb_interface *interface,
struct usb_line6_variax *variax)
{
int err = variax_try_init(interface, variax);
if (err < 0) {
variax_destruct(interface);
return err;
}
variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY);
line6_startup_delayed(&variax->dumpreq, VARIAX_STARTUP_DELAY,
variax_startup_timeout, variax);
return 0;
return err;
}
/*
Workbench device disconnected.
*/
void variax_disconnect(struct usb_interface *interface)
void line6_variax_disconnect(struct usb_interface *interface)
{
struct device *dev;
@ -550,7 +646,7 @@ void variax_disconnect(struct usb_interface *interface)
if (dev != NULL) {
/* remove sysfs entries: */
variax_remove_files(0, 0, dev);
line6_variax_remove_files(0, 0, dev);
device_remove_file(dev, &dev_attr_model);
device_remove_file(dev, &dev_attr_volume);
device_remove_file(dev, &dev_attr_tone);
@ -558,7 +654,8 @@ void variax_disconnect(struct usb_interface *interface)
device_remove_file(dev, &dev_attr_bank);
device_remove_file(dev, &dev_attr_dump);
device_remove_file(dev, &dev_attr_active);
#if CREATE_RAW_FILE
device_remove_file(dev, &dev_attr_guitar);
#ifdef CONFIG_LINE6_USB_RAW
device_remove_file(dev, &dev_attr_raw);
device_remove_file(dev, &dev_attr_raw2);
#endif

View file

@ -1,7 +1,7 @@
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -13,19 +13,18 @@
#define VARIAX_H
#include "driver.h"
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/wait.h>
#include <sound/core.h>
#include "driver.h"
#include "dumprequest.h"
#define VARIAX_ACTIVATE_DELAY 10
#define VARIAX_STARTUP_DELAY 3
#define VARIAX_STARTUP_DELAY1 1000
#define VARIAX_STARTUP_DELAY3 100
#define VARIAX_STARTUP_DELAY4 100
enum {
@ -36,73 +35,88 @@ enum {
/**
Binary Variax model dump
Binary Variax model dump
*/
struct variax_model {
/**
Header information (including program name).
Header information (including program name).
*/
unsigned char name[18];
/**
Model parameters.
Model parameters.
*/
unsigned char control[78 * 2];
};
struct usb_line6_variax {
/**
Generic Line6 USB data.
Generic Line6 USB data.
*/
struct usb_line6 line6;
/**
Dump request structure.
Append two extra buffers for 3-pass data query.
Dump request structure.
Append two extra buffers for 3-pass data query.
*/
struct line6_dump_request dumpreq; struct line6_dump_reqbuf extrabuf[2];
/**
Buffer for activation code.
Buffer for activation code.
*/
unsigned char *buffer_activate;
/**
Model number.
Model number.
*/
int model;
/**
Current model settings.
Current model settings.
*/
struct variax_model model_data;
/**
Name of current model bank.
Name of connected guitar.
*/
unsigned char guitar[18];
/**
Name of current model bank.
*/
unsigned char bank[18];
/**
Position of volume dial.
Position of volume dial.
*/
int volume;
/**
Position of tone control dial.
Position of tone control dial.
*/
int tone;
/**
Timer for delayed activation request.
Handler for device initializaton.
*/
struct timer_list activate_timer;
struct work_struct startup_work;
/**
Timer for device initializaton.
*/
struct timer_list startup_timer;
/**
Current progress in startup procedure.
*/
int startup_progress;
};
extern void variax_disconnect(struct usb_interface *interface);
extern int variax_init(struct usb_interface *interface,
struct usb_line6_variax *variax);
extern void variax_process_message(struct usb_line6_variax *variax);
extern void line6_variax_disconnect(struct usb_interface *interface);
extern int line6_variax_init(struct usb_interface *interface,
struct usb_line6_variax *variax);
extern void line6_variax_process_message(struct usb_line6_variax *variax);
#endif