提交 1027f476 编写于 作者: M Markus Grabner 提交者: Greg Kroah-Hartman

staging: line6: sync with upstream

Big upstream sync.
Signed-off-by: NMarkus Grabner <grabner@icg.tugraz.at>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 4498dbcd
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
/*
* 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;
......
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......@@ -9,27 +9,24 @@
*
*/
#include "driver.h"
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "audio.h"
#include "capture.h"
#include "driver.h"
#include "pcm.h"
#include "pod.h"
#include "capture.h"
/*
Find a free URB and submit it.
*/
static int submit_audio_in_urb(struct snd_pcm_substream *substream)
static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
{
unsigned int index;
int index;
unsigned long flags;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
int i, urb_size;
struct urb *urb_in;
......@@ -37,9 +34,9 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
index =
find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS);
if (index >= LINE6_ISO_BUFFERS) {
if (index < 0 || index >= LINE6_ISO_BUFFERS) {
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
dev_err(s2m(substream), "no free URB found\n");
dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
return -EINVAL;
}
......@@ -58,13 +55,13 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
line6pcm->buffer_in +
index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
urb_in->transfer_buffer_length = urb_size;
urb_in->context = substream;
urb_in->context = line6pcm;
if (usb_submit_urb(urb_in, GFP_ATOMIC) == 0)
set_bit(index, &line6pcm->active_urb_in);
else
dev_err(s2m(substream), "URB in #%d submission failed\n",
index);
dev_err(line6pcm->line6->ifcdev,
"URB in #%d submission failed\n", index);
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
return 0;
......@@ -73,12 +70,12 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
/*
Submit all currently available capture URBs.
*/
static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream)
int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
{
int ret, i;
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
ret = submit_audio_in_urb(substream);
ret = submit_audio_in_urb(line6pcm);
if (ret < 0)
return ret;
}
......@@ -89,7 +86,7 @@ static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream)
/*
Unlink all currently active capture URBs.
*/
static void unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm)
void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
unsigned int i;
......@@ -126,41 +123,83 @@ static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
} while (--timeout > 0);
if (alive)
snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
line6pcm->active_urb_in = 0;
line6pcm->unlink_urb_in = 0;
}
/*
Unlink all currently active capture URBs, and wait for finishing.
*/
void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
unlink_audio_in_urbs(line6pcm);
line6_unlink_audio_in_urbs(line6pcm);
wait_clear_audio_in_urbs(line6pcm);
}
/*
Callback for completed capture URB.
Copy data into ALSA capture buffer.
*/
void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
{
struct snd_pcm_substream *substream =
get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
struct snd_pcm_runtime *runtime = substream->runtime;
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
int frames = fsize / bytes_per_frame;
if (line6pcm->pos_in_done + frames > runtime->buffer_size) {
/*
The transferred area goes over buffer boundary,
copy two separate chunks.
*/
int len;
len = runtime->buffer_size - line6pcm->pos_in_done;
if (len > 0) {
memcpy(runtime->dma_area +
line6pcm->pos_in_done * bytes_per_frame, fbuf,
len * bytes_per_frame);
memcpy(runtime->dma_area, fbuf + len * bytes_per_frame,
(frames - len) * bytes_per_frame);
} else
dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", len); /* this is somewhat paranoid */
} else {
/* copy single chunk */
memcpy(runtime->dma_area +
line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize);
}
if ((line6pcm->pos_in_done += frames) >= runtime->buffer_size)
line6pcm->pos_in_done -= runtime->buffer_size;
}
void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
{
struct snd_pcm_substream *substream =
get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
if ((line6pcm->bytes_in += length) >= line6pcm->period_in) {
line6pcm->bytes_in %= line6pcm->period_in;
snd_pcm_period_elapsed(substream);
}
}
/*
Callback for completed capture URB.
*/
static void audio_in_callback(struct urb *urb)
{
int i, index, length = 0, shutdown = 0;
int frames;
unsigned long flags;
struct snd_pcm_substream *substream =
(struct snd_pcm_substream *)urb->context;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
line6pcm->last_frame_in = urb->start_frame;
/* find index of URB */
for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
if (urb == line6pcm->urb_audio_in[index])
break;
#if DO_DUMP_PCM_RECEIVE
#ifdef CONFIG_LINE6_USB_DUMP_PCM
for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
struct usb_iso_packet_descriptor *fout =
&urb->iso_frame_desc[i];
......@@ -184,64 +223,43 @@ static void audio_in_callback(struct urb *urb)
fbuf = urb->transfer_buffer + fin->offset;
fsize = fin->actual_length;
if (fsize > line6pcm->max_packet_size) {
dev_err(line6pcm->line6->ifcdev,
"driver and/or device bug: packet too large (%d > %d)\n",
fsize, line6pcm->max_packet_size);
}
length += fsize;
if (fsize > 0) {
frames = fsize / bytes_per_frame;
if (line6pcm->pos_in_done + frames >
runtime->buffer_size) {
/*
The transferred area goes over buffer
boundary, copy two separate chunks.
*/
int len;
len =
runtime->buffer_size -
line6pcm->pos_in_done;
if (len > 0) {
memcpy(runtime->dma_area +
line6pcm->pos_in_done *
bytes_per_frame, fbuf,
len * bytes_per_frame);
memcpy(runtime->dma_area,
fbuf + len * bytes_per_frame,
(frames -
len) * bytes_per_frame);
} else {
/* this is somewhat paranoid */
dev_err(s2m(substream),
"driver bug: len = %d\n", len);
}
} else {
/* copy single chunk */
memcpy(runtime->dma_area +
line6pcm->pos_in_done * bytes_per_frame,
fbuf, fsize * bytes_per_frame);
}
/* the following assumes LINE6_ISO_PACKETS == 1: */
#if LINE6_BACKUP_MONITOR_SIGNAL
memcpy(line6pcm->prev_fbuf, fbuf, fsize);
#else
line6pcm->prev_fbuf = fbuf;
#endif
line6pcm->prev_fsize = fsize;
line6pcm->pos_in_done += frames;
if (line6pcm->pos_in_done >= runtime->buffer_size)
line6pcm->pos_in_done -= runtime->buffer_size;
}
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
if (!(line6pcm->flags & MASK_PCM_IMPULSE))
#endif
if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags)
&& (fsize > 0))
line6_capture_copy(line6pcm, fbuf, fsize);
}
clear_bit(index, &line6pcm->active_urb_in);
if (test_bit(index, &line6pcm->unlink_urb_in))
if (test_and_clear_bit(index, &line6pcm->unlink_urb_in))
shutdown = 1;
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
if (!shutdown) {
submit_audio_in_urb(substream);
submit_audio_in_urb(line6pcm);
line6pcm->bytes_in += length;
if (line6pcm->bytes_in >= line6pcm->period_in) {
line6pcm->bytes_in -= line6pcm->period_in;
snd_pcm_period_elapsed(substream);
}
if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags))
line6_capture_check_period(line6pcm, length);
}
}
......@@ -294,54 +312,40 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream,
return ret;
line6pcm->period_in = params_period_bytes(hw_params);
line6pcm->buffer_in =
kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL);
if (!line6pcm->buffer_in) {
dev_err(s2m(substream), "cannot malloc buffer_in\n");
return -ENOMEM;
}
return 0;
}
/* hw_free capture callback */
static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream)
{
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
unlink_wait_clear_audio_in_urbs(line6pcm);
kfree(line6pcm->buffer_in);
line6pcm->buffer_in = NULL;
return snd_pcm_lib_free_pages(substream);
}
/* trigger callback */
int snd_line6_capture_trigger(struct snd_pcm_substream *substream, int cmd)
int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd)
{
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
int err;
line6pcm->count_in = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (!test_and_set_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) {
err = submit_audio_in_all_urbs(substream);
#ifdef CONFIG_PM
case SNDRV_PCM_TRIGGER_RESUME:
#endif
err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_CAPTURE);
if (err < 0) {
clear_bit(BIT_RUNNING_CAPTURE,
&line6pcm->flags);
return err;
}
}
if (err < 0)
return err;
break;
case SNDRV_PCM_TRIGGER_STOP:
if (test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags))
unlink_audio_in_urbs(line6pcm);
#ifdef CONFIG_PM
case SNDRV_PCM_TRIGGER_SUSPEND:
#endif
err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_CAPTURE);
if (err < 0)
return err;
break;
......@@ -362,17 +366,17 @@ snd_line6_capture_pointer(struct snd_pcm_substream *substream)
/* capture operators */
struct snd_pcm_ops snd_line6_capture_ops = {
.open = snd_line6_capture_open,
.close = snd_line6_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_line6_capture_hw_params,
.hw_free = snd_line6_capture_hw_free,
.prepare = snd_line6_prepare,
.trigger = snd_line6_trigger,
.pointer = snd_line6_capture_pointer,
.open = snd_line6_capture_open,
.close = snd_line6_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_line6_capture_hw_params,
.hw_free = snd_line6_capture_hw_free,
.prepare = snd_line6_prepare,
.trigger = snd_line6_trigger,
.pointer = snd_line6_capture_pointer,
};
int create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
int i;
......
/*
* 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
/*
* 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);
/*
* 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
此差异已折叠。
/*
* 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
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......@@ -9,10 +9,9 @@
*
*/
#include "driver.h"
#include <linux/slab.h>
#include "driver.h"
#include "dumprequest.h"
......@@ -39,17 +38,17 @@ void line6_invalidate_current(struct line6_dump_request *l6dr)
void line6_dump_finished(struct line6_dump_request *l6dr)
{
l6dr->in_progress = LINE6_DUMP_NONE;
wake_up_interruptible(&l6dr->wait);
wake_up(&l6dr->wait);
}
/*
Send an asynchronous channel dump request.
*/
int line6_dump_request_async(struct line6_dump_request *l6dr,
struct usb_line6 *line6, int num)
struct usb_line6 *line6, int num, int dest)
{
int ret;
line6_invalidate_current(l6dr);
line6_dump_started(l6dr, dest);
ret = line6_send_raw_message_async(line6, l6dr->reqbufs[num].buffer,
l6dr->reqbufs[num].length);
......@@ -60,43 +59,27 @@ int line6_dump_request_async(struct line6_dump_request *l6dr,
}
/*
Send an asynchronous dump request after a given interval.
Wait for completion (interruptible).
*/
void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds,
void (*function)(unsigned long), void *data)
int line6_dump_wait_interruptible(struct line6_dump_request *l6dr)
{
l6dr->timer.expires = jiffies + seconds * HZ;
l6dr->timer.function = function;
l6dr->timer.data = (unsigned long)data;
add_timer(&l6dr->timer);
return wait_event_interruptible(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE);
}
/*
Wait for completion.
*/
int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock)
void line6_dump_wait(struct line6_dump_request *l6dr)
{
wait_event(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE);
}
/*
Wait for completion (with timeout).
*/
int line6_dump_wait_timeout(struct line6_dump_request *l6dr, long timeout)
{
int retval = 0;
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&l6dr->wait, &wait);
current->state = TASK_INTERRUPTIBLE;
while (l6dr->in_progress) {
if (nonblock) {
retval = -EAGAIN;
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
} else
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&l6dr->wait, &wait);
return retval;
return wait_event_timeout(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE, timeout);
}
/*
......@@ -123,7 +106,6 @@ int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf,
if (ret < 0)
return ret;
init_waitqueue_head(&l6dr->wait);
init_timer(&l6dr->timer);
return 0;
}
......@@ -148,6 +130,4 @@ void line6_dumpreq_destruct(struct line6_dump_request *l6dr)
if (l6dr->reqbufs[0].buffer == NULL)
return;
line6_dumpreq_destructbuf(l6dr, 0);
l6dr->ok = 1;
del_timer_sync(&l6dr->timer);
}
/*
* 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
/*
* 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;
......
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......@@ -9,8 +9,6 @@
*
*/
#include "config.h"
#include <linux/slab.h>
#include "midibuf.h"
......@@ -25,9 +23,9 @@ static int midibuf_message_length(unsigned char code)
return length[(code >> 4) - 8];
} else {
/*
Note that according to the MIDI specification 0xf2 is
the "Song Position Pointer", but this is used by Line6
to send sysex messages to the host.
Note that according to the MIDI specification 0xf2 is
the "Song Position Pointer", but this is used by Line6
to send sysex messages to the host.
*/
static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1,
1, 1, 1, -1, 1, 1 };
......@@ -35,13 +33,23 @@ static int midibuf_message_length(unsigned char code)
}
}
void midibuf_reset(struct MidiBuffer *this)
static int midibuf_is_empty(struct MidiBuffer *this)
{
return (this->pos_read == this->pos_write) && !this->full;
}
static int midibuf_is_full(struct MidiBuffer *this)
{
return this->full;
}
void line6_midibuf_reset(struct MidiBuffer *this)
{
this->pos_read = this->pos_write = this->full = 0;
this->command_prev = -1;
}
int midibuf_init(struct MidiBuffer *this, int size, int split)
int line6_midibuf_init(struct MidiBuffer *this, int size, int split)
{
this->buf = kmalloc(size, GFP_KERNEL);
......@@ -50,28 +58,18 @@ int midibuf_init(struct MidiBuffer *this, int size, int split)
this->size = size;
this->split = split;
midibuf_reset(this);
line6_midibuf_reset(this);
return 0;
}
void midibuf_status(struct MidiBuffer *this)
void line6_midibuf_status(struct MidiBuffer *this)
{
printk(KERN_DEBUG "midibuf size=%d split=%d pos_read=%d pos_write=%d "
"full=%d command_prev=%02x\n", this->size, this->split,
this->pos_read, this->pos_write, this->full, this->command_prev);
}
static int midibuf_is_empty(struct MidiBuffer *this)
{
return (this->pos_read == this->pos_write) && !this->full;
}
static int midibuf_is_full(struct MidiBuffer *this)
{
return this->full;
}
int midibuf_bytes_free(struct MidiBuffer *this)
int line6_midibuf_bytes_free(struct MidiBuffer *this)
{
return
midibuf_is_full(this) ?
......@@ -79,7 +77,7 @@ int midibuf_bytes_free(struct MidiBuffer *this)
(this->pos_read - this->pos_write + this->size - 1) % this->size + 1;
}
int midibuf_bytes_used(struct MidiBuffer *this)
int line6_midibuf_bytes_used(struct MidiBuffer *this)
{
return
midibuf_is_empty(this) ?
......@@ -87,7 +85,7 @@ int midibuf_bytes_used(struct MidiBuffer *this)
(this->pos_write - this->pos_read + this->size - 1) % this->size + 1;
}
int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
int line6_midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
{
int bytes_free;
int length1, length2;
......@@ -102,7 +100,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
skip_active_sense = 1;
}
bytes_free = midibuf_bytes_free(this);
bytes_free = line6_midibuf_bytes_free(this);
if (length > bytes_free)
length = bytes_free;
......@@ -129,7 +127,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
return length + skip_active_sense;
}
int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
int line6_midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
{
int bytes_used;
int length1, length2;
......@@ -145,7 +143,7 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
if (midibuf_is_empty(this))
return 0;
bytes_used = midibuf_bytes_used(this);
bytes_used = line6_midibuf_bytes_used(this);
if (length > bytes_used)
length = bytes_used;
......@@ -232,9 +230,9 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
return length + repeat;
}
int midibuf_ignore(struct MidiBuffer *this, int length)
int line6_midibuf_ignore(struct MidiBuffer *this, int length)
{
int bytes_used = midibuf_bytes_used(this);
int bytes_used = line6_midibuf_bytes_used(this);
if (length > bytes_used)
length = bytes_used;
......@@ -244,7 +242,7 @@ int midibuf_ignore(struct MidiBuffer *this, int length)
return length;
}
int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
int line6_midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
{
int cmd = this->command_prev;
......@@ -255,7 +253,7 @@ int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
return 0;
}
void midibuf_destroy(struct MidiBuffer *this)
void line6_midibuf_destroy(struct MidiBuffer *this)
{
kfree(this->buf);
this->buf = NULL;
......
/*
* 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
/*
* 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;
}
......
/*
* 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
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......@@ -9,15 +9,13 @@
*
*/
#include "driver.h"
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "audio.h"
#include "capture.h"
#include "driver.h"
#include "pcm.h"
#include "pod.h"
#include "playback.h"
......@@ -59,22 +57,85 @@ static void change_volume(struct urb *urb_out, int volume[],
}
}
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
/*
Create signal for impulse response test.
*/
static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm,
struct urb *urb_out, int bytes_per_frame)
{
int frames = urb_out->transfer_buffer_length / bytes_per_frame;
if (bytes_per_frame == 4) {
/* TODO: add code for TonePort etc. */
} else if (bytes_per_frame == 6) {
int i, j;
unsigned char *pi = line6pcm->prev_fbuf;
unsigned char *po = urb_out->transfer_buffer;
for (i = 0; i < frames; ++i) {
for (j = 0; j < bytes_per_frame / 2; ++j)
po[j] = pi[j];
for (; j < bytes_per_frame; ++j)
po[j] = 0;
pi += bytes_per_frame;
po += bytes_per_frame;
}
if (--line6pcm->impulse_count <= 0) {
((unsigned char *)(urb_out->
transfer_buffer))[bytes_per_frame -
1] =
line6pcm->impulse_volume;
line6pcm->impulse_count = line6pcm->impulse_period;
}
}
}
#endif
/*
Add signal to buffer for software monitoring.
*/
static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
int volume, int bytes_per_frame)
{
if (volume == 0)
return; /* zero volume - no change */
if (bytes_per_frame == 4) {
short *pi, *po, *buf_end;
pi = (short *)signal;
po = (short *)urb_out->transfer_buffer;
buf_end = po + urb_out->transfer_buffer_length / sizeof(*po);
for (; po < buf_end; ++pi, ++po)
*po += (*pi * volume) >> 8;
}
/*
We don't need to handle devices with 6 bytes per frame here
since they all support hardware monitoring.
*/
}
/*
Find a free URB, prepare audio data, and submit URB.
*/
static int submit_audio_out_urb(struct snd_pcm_substream *substream)
static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
{
int index;
unsigned long flags;
int i, urb_size, urb_frames;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
const int frame_increment =
line6pcm->properties->snd_line6_rates.rats[0].num_min;
const int frame_factor =
line6pcm->properties->snd_line6_rates.rats[0].den *
(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
struct snd_pcm_runtime *runtime = substream->runtime;
struct urb *urb_out;
spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
......@@ -83,7 +144,7 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
if (index < 0 || index >= LINE6_ISO_BUFFERS) {
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
dev_err(s2m(substream), "no free URB found\n");
dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
return -EINVAL;
}
......@@ -92,24 +153,49 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
/* compute frame size for given sampling rate */
int n, fs;
int fsize = 0;
struct usb_iso_packet_descriptor *fout =
&urb_out->iso_frame_desc[i];
line6pcm->count_out += frame_increment;
n = line6pcm->count_out / frame_factor;
line6pcm->count_out -= n * frame_factor;
fs = n * bytes_per_frame;
if (line6pcm->flags & MASK_CAPTURE) {
fsize = line6pcm->prev_fsize;
}
if (fsize == 0) {
int n;
line6pcm->count_out += frame_increment;
n = line6pcm->count_out / frame_factor;
line6pcm->count_out -= n * frame_factor;
fsize = n * bytes_per_frame;
}
fout->offset = urb_size;
fout->length = fs;
urb_size += fs;
fout->length = fsize;
urb_size += fsize;
}
if (urb_size == 0) {
/* can't determine URB size */
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); /* this is somewhat paranoid */
return -EINVAL;
}
urb_frames = urb_size / bytes_per_frame;
urb_out->transfer_buffer =
line6pcm->buffer_out +
line6pcm->max_packet_size * line6pcm->index_out;
urb_out->transfer_buffer_length = urb_size;
urb_out->context = line6pcm;
if (++line6pcm->index_out == LINE6_ISO_BUFFERS)
line6pcm->index_out = 0;
if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags) &&
!test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) {
struct snd_pcm_runtime *runtime =
get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime;
if (test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) {
urb_out->transfer_buffer = line6pcm->wrap_out;
memset(line6pcm->wrap_out, 0, urb_size);
} else {
if (line6pcm->pos_out + urb_frames > runtime->buffer_size) {
/*
The transferred area goes over buffer boundary,
......@@ -117,38 +203,65 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
*/
int len;
len = runtime->buffer_size - line6pcm->pos_out;
urb_out->transfer_buffer = line6pcm->wrap_out;
if (len > 0) {
memcpy(line6pcm->wrap_out,
memcpy(urb_out->transfer_buffer,
runtime->dma_area +
line6pcm->pos_out * bytes_per_frame,
len * bytes_per_frame);
memcpy(line6pcm->wrap_out +
memcpy(urb_out->transfer_buffer +
len * bytes_per_frame, runtime->dma_area,
(urb_frames - len) * bytes_per_frame);
} else {
/* this is somewhat paranoid */
dev_err(s2m(substream),
"driver bug: len = %d\n", len);
}
} else
dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", len); /* this is somewhat paranoid */
} else {
#if LINE6_REUSE_DMA_AREA_FOR_PLAYBACK
/* set the buffer pointer */
urb_out->transfer_buffer =
runtime->dma_area +
line6pcm->pos_out * bytes_per_frame;
#else
/* copy data */
memcpy(urb_out->transfer_buffer,
runtime->dma_area +
line6pcm->pos_out * bytes_per_frame,
urb_out->transfer_buffer_length);
#endif
}
}
line6pcm->pos_out += urb_frames;
if (line6pcm->pos_out >= runtime->buffer_size)
line6pcm->pos_out -= runtime->buffer_size;
if ((line6pcm->pos_out += urb_frames) >= runtime->buffer_size)
line6pcm->pos_out -= runtime->buffer_size;
} else {
memset(urb_out->transfer_buffer, 0,
urb_out->transfer_buffer_length);
}
urb_out->transfer_buffer_length = urb_size;
urb_out->context = substream;
change_volume(urb_out, line6pcm->volume, bytes_per_frame);
change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame);
#if DO_DUMP_PCM_SEND
if (line6pcm->prev_fbuf != 0) {
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
if (line6pcm->flags & MASK_PCM_IMPULSE) {
create_impulse_test_signal(line6pcm, urb_out,
bytes_per_frame);
if (line6pcm->flags & MASK_PCM_ALSA_CAPTURE) {
line6_capture_copy(line6pcm, urb_out->transfer_buffer,
urb_out->transfer_buffer_length);
}
} else {
#endif
if (!
(line6pcm->line6->properties->
capabilities & LINE6_BIT_HWMON)
&& (line6pcm->flags & MASK_PLAYBACK)
&& (line6pcm->flags & MASK_CAPTURE))
add_monitor_signal(urb_out, line6pcm->prev_fbuf,
line6pcm->volume_monitor,
bytes_per_frame);
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
}
#endif
}
#ifdef CONFIG_LINE6_USB_DUMP_PCM
for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
struct usb_iso_packet_descriptor *fout =
&urb_out->iso_frame_desc[i];
......@@ -161,8 +274,8 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
if (usb_submit_urb(urb_out, GFP_ATOMIC) == 0)
set_bit(index, &line6pcm->active_urb_out);
else
dev_err(s2m(substream), "URB out #%d submission failed\n",
index);
dev_err(line6pcm->line6->ifcdev,
"URB out #%d submission failed\n", index);
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
return 0;
......@@ -171,12 +284,12 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
/*
Submit all currently available playback URBs.
*/
static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream)
int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
{
int ret, i;
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
ret = submit_audio_out_urb(substream);
ret = submit_audio_out_urb(line6pcm);
if (ret < 0)
return ret;
}
......@@ -187,7 +300,7 @@ static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream)
/*
Unlink all currently active playback URBs.
*/
static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
{
unsigned int i;
......@@ -202,7 +315,7 @@ static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
}
/*
Wait until unlinking of all currently active playback URBs has been finished.
Wait until unlinking of all currently active playback URBs has been finished.
*/
static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
{
......@@ -223,17 +336,14 @@ static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
} while (--timeout > 0);
if (alive)
snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
line6pcm->active_urb_out = 0;
line6pcm->unlink_urb_out = 0;
}
/*
Unlink all currently active playback URBs, and wait for finishing.
*/
void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
{
unlink_audio_out_urbs(line6pcm);
line6_unlink_audio_out_urbs(line6pcm);
wait_clear_audio_out_urbs(line6pcm);
}
......@@ -245,10 +355,16 @@ static void audio_out_callback(struct urb *urb)
int i, index, length = 0, shutdown = 0;
unsigned long flags;
struct snd_line6_pcm *line6pcm =
(struct snd_line6_pcm *)urb->context;
struct snd_pcm_substream *substream =
(struct snd_pcm_substream *)urb->context;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
#if USE_CLEAR_BUFFER_WORKAROUND
memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
#endif
line6pcm->last_frame_out = urb->start_frame;
/* find index of URB */
for (index = LINE6_ISO_BUFFERS; index--;)
......@@ -262,11 +378,15 @@ static void audio_out_callback(struct urb *urb)
length += urb->iso_frame_desc[i].length;
spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
line6pcm->pos_out_done +=
length / line6pcm->properties->bytes_per_frame;
if (line6pcm->pos_out_done >= runtime->buffer_size)
line6pcm->pos_out_done -= runtime->buffer_size;
if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags)) {
struct snd_pcm_runtime *runtime = substream->runtime;
line6pcm->pos_out_done +=
length / line6pcm->properties->bytes_per_frame;
if (line6pcm->pos_out_done >= runtime->buffer_size)
line6pcm->pos_out_done -= runtime->buffer_size;
}
clear_bit(index, &line6pcm->active_urb_out);
......@@ -276,18 +396,20 @@ static void audio_out_callback(struct urb *urb)
break;
}
if (test_bit(index, &line6pcm->unlink_urb_out))
if (test_and_clear_bit(index, &line6pcm->unlink_urb_out))
shutdown = 1;
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
if (!shutdown) {
submit_audio_out_urb(substream);
submit_audio_out_urb(line6pcm);
line6pcm->bytes_out += length;
if (line6pcm->bytes_out >= line6pcm->period_out) {
line6pcm->bytes_out -= line6pcm->period_out;
snd_pcm_period_elapsed(substream);
if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags)) {
if ((line6pcm->bytes_out +=
length) >= line6pcm->period_out) {
line6pcm->bytes_out %= line6pcm->period_out;
snd_pcm_period_elapsed(substream);
}
}
}
}
......@@ -340,52 +462,40 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream,
return ret;
line6pcm->period_out = params_period_bytes(hw_params);
line6pcm->wrap_out = kmalloc(2 * LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL);
if (!line6pcm->wrap_out) {
dev_err(s2m(substream), "cannot malloc wrap_out\n");
return -ENOMEM;
}
return 0;
}
/* hw_free playback callback */
static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
unlink_wait_clear_audio_out_urbs(line6pcm);
kfree(line6pcm->wrap_out);
line6pcm->wrap_out = NULL;
return snd_pcm_lib_free_pages(substream);
}
/* trigger playback callback */
int snd_line6_playback_trigger(struct snd_pcm_substream *substream, int cmd)
int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd)
{
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
int err;
line6pcm->count_out = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (!test_and_set_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) {
err = submit_audio_out_all_urbs(substream);
#ifdef CONFIG_PM
case SNDRV_PCM_TRIGGER_RESUME:
#endif
err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_PLAYBACK);
if (err < 0) {
clear_bit(BIT_RUNNING_PLAYBACK,
&line6pcm->flags);
return err;
}
}
if (err < 0)
return err;
break;
case SNDRV_PCM_TRIGGER_STOP:
if (test_and_clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags))
unlink_audio_out_urbs(line6pcm);
#ifdef CONFIG_PM
case SNDRV_PCM_TRIGGER_SUSPEND:
#endif
err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_PLAYBACK);
if (err < 0)
return err;
break;
......@@ -414,17 +524,17 @@ snd_line6_playback_pointer(struct snd_pcm_substream *substream)
/* playback operators */
struct snd_pcm_ops snd_line6_playback_ops = {
.open = snd_line6_playback_open,
.close = snd_line6_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_line6_playback_hw_params,
.hw_free = snd_line6_playback_hw_free,
.prepare = snd_line6_prepare,
.trigger = snd_line6_trigger,
.pointer = snd_line6_playback_pointer,
.open = snd_line6_playback_open,
.close = snd_line6_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_line6_playback_hw_params,
.hw_free = snd_line6_playback_hw_free,
.prepare = snd_line6_prepare,
.trigger = snd_line6_trigger,
.pointer = snd_line6_playback_pointer,
};
int create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
{
int i;
......
/*
* 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 "driver.h"
#include <sound/pcm.h>
#include "driver.h"
extern struct snd_pcm_ops snd_line6_playback_ops;
/*
When the TonePort is used with jack in full duplex mode and the outputs are
not connected, the software monitor produces an ugly noise since everything
written to the output buffer (i.e., the input signal) will be repeated in the
next period (sounds like a delay effect). As a workaround, the output buffer
is cleared after the data have been read, but there must be a better
solution. Until one is found, this workaround can be used to fix the problem.
*/
#define USE_CLEAR_BUFFER_WORKAROUND 1
extern int create_audio_out_urbs(struct snd_line6_pcm *line6pcm);
extern int snd_line6_playback_trigger(struct snd_pcm_substream *substream,
int cmd);
extern void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm);
extern struct snd_pcm_ops snd_line6_playback_ops;
extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm);
extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm);
extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm);
extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm
*line6pcm);
extern int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd);
#endif
此差异已折叠。
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......@@ -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.
*/
unsigned long param_dirty[POD_CONTROL_SIZE / sizeof(unsigned long)];
struct work_struct startup_work;
/**
Some atomic flags.
Current progress in startup procedure.
*/
unsigned long atomic_flags;
int startup_progress;
/**
Counter for startup process.
Dirty flags for access to parameter data.
*/
int startup_count;
unsigned long param_dirty[POD_CONTROL_SIZE / sizeof(unsigned long)];
/**
Some atomic flags.
*/
unsigned long atomic_flags;
/**
Serial number of device.
Serial number of device.
*/
int serial_number;
/**
Firmware version (x 100).
Firmware version (x 100).
*/
int firmware_version;
/**
Device ID.
Device ID.
*/
int device_id;
/**
Flag to indicate modification of current program settings.
Flag to indicate modification of current program settings.
*/
char dirty;
/**
Flag if initial firmware version request has been successful.
*/
char versionreq_ok;
/**
Flag to enable MIDI postprocessing.
Flag to enable MIDI postprocessing.
*/
char midi_postprocess;
};
extern void pod_disconnect(struct usb_interface *interface);
extern int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod);
extern void pod_midi_postprocess(struct usb_line6_pod *pod,
unsigned char *data, int length);
extern void pod_process_message(struct usb_line6_pod *pod);
extern void pod_receive_parameter(struct usb_line6_pod *pod, int param);
extern void pod_transmit_parameter(struct usb_line6_pod *pod, int param,
int value);
extern void line6_pod_disconnect(struct usb_interface *interface);
extern int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod);
extern void line6_pod_midi_postprocess(struct usb_line6_pod *pod,
unsigned char *data, int length);
extern void line6_pod_process_message(struct usb_line6_pod *pod);
extern void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param,
int value);
#endif
#ifndef DRIVER_REVISION
/* current subversion revision */
#define DRIVER_REVISION " (revision 529)"
#define DRIVER_REVISION " (revision 665)"
#endif
/*
* Line6 Linux USB driver - 0.8.0
* Line6 Linux USB driver - 0.9.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
* Emil Myhrman (emil.myhrman@gmail.com)
*
* This program is free software; you can redistribute it and/or
......@@ -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);
if (toneport_has_led(usbdev->descriptor.idProduct)) {
CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_red));
CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_green));
}
/*
seems to work without the first two...
*/
/* toneport_send_cmd(usbdev, 0x0201, 0x0002); */
/* toneport_send_cmd(usbdev, 0x0801, 0x0000); */
/* only one that works for me; on GP, TP might be different? */
toneport_send_cmd(usbdev, 0x0301, 0x0000);
toneport_setup(toneport);
if (usbdev->descriptor.idProduct != LINE6_DEVID_GUITARPORT) {
CHECK_RETURN(device_create_file
(&interface->dev, &dev_attr_led_red));
CHECK_RETURN(device_create_file
(&interface->dev, &dev_attr_led_green));
toneport_update_led(&usbdev->dev);
}
init_timer(&toneport->timer);
toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ;
toneport->timer.function = toneport_start_pcm;
toneport->timer.data = (unsigned long)toneport;
add_timer(&toneport->timer);
return 0;
}
/*
Init Toneport device (and clean up in case of failure).
*/
int line6_toneport_init(struct usb_interface *interface,
struct usb_line6_toneport *toneport)
{
int err = toneport_try_init(interface, toneport);
if (err < 0) {
toneport_destruct(interface);
}
return err;
}
/*
Resume Toneport device after reset.
*/
void line6_toneport_reset_resume(struct usb_line6_toneport *toneport)
{
toneport_setup(toneport);
}
/*
Toneport device disconnected.
*/
void toneport_disconnect(struct usb_interface *interface)
void line6_toneport_disconnect(struct usb_interface *interface)
{
struct usb_line6_toneport *toneport;
if (interface == NULL)
return;
toneport = usb_get_intfdata(interface);
del_timer_sync(&toneport->timer);
if (toneport->line6.usbdev->descriptor.idProduct !=
LINE6_DEVID_GUITARPORT) {
if (toneport_has_led(toneport->line6.usbdev->descriptor.idProduct)) {
device_remove_file(&interface->dev, &dev_attr_led_red);
device_remove_file(&interface->dev, &dev_attr_led_green);
}
......@@ -243,8 +442,8 @@ void toneport_disconnect(struct usb_interface *interface)
struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm;
if (line6pcm != NULL) {
unlink_wait_clear_audio_out_urbs(line6pcm);
unlink_wait_clear_audio_in_urbs(line6pcm);
line6_pcm_stop(line6pcm, MASK_PCM_MONITOR);
line6_pcm_disconnect(line6pcm);
}
}
......
/*
* 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
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册