mirror of
https://github.com/ukui/kernel.git
synced 2026-03-09 10:07:04 -07:00
staging: line6: sync with upstream
Big upstream sync. Signed-off-by: Markus Grabner <grabner@icg.tugraz.at> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
4498dbcd2d
commit
1027f476f5
@@ -1,4 +1,4 @@
|
||||
config LINE6_USB
|
||||
menuconfig LINE6_USB
|
||||
tristate "Line6 USB support"
|
||||
depends on USB && SND
|
||||
select SND_RAWMIDI
|
||||
@@ -18,5 +18,68 @@ config LINE6_USB
|
||||
* Signal routing (record clean/processed guitar signal,
|
||||
re-amping)
|
||||
|
||||
Preliminary support for the Variax Workbench is included.
|
||||
Preliminary support for the Variax Workbench and TonePort
|
||||
devices is included.
|
||||
|
||||
if LINE6_USB
|
||||
|
||||
config LINE6_USB_DEBUG
|
||||
bool "print debug messages"
|
||||
default n
|
||||
help
|
||||
Say Y here to write debug messages to the syslog.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config LINE6_USB_DUMP_CTRL
|
||||
bool "dump control messages"
|
||||
default n
|
||||
help
|
||||
Say Y here to write control messages sent to and received from
|
||||
Line6 devices to the syslog.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config LINE6_USB_DUMP_MIDI
|
||||
bool "dump MIDI messages"
|
||||
default n
|
||||
help
|
||||
Say Y here to write MIDI messages sent to and received from
|
||||
Line6 devices to the syslog.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config LINE6_USB_DUMP_PCM
|
||||
bool "dump PCM data"
|
||||
default n
|
||||
help
|
||||
Say Y here to write PCM data sent to and received from Line6
|
||||
devices to the syslog. This will produce a huge amount of
|
||||
syslog data during playback and capture.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config LINE6_USB_RAW
|
||||
bool "raw data communication"
|
||||
default n
|
||||
help
|
||||
Say Y here to create special files which allow to send raw data
|
||||
to the device. This bypasses any sanity checks, so if you discover
|
||||
the code to erase the firmware, feel free to render your device
|
||||
useless, but only after reading the GPL section "NO WARRANTY".
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config LINE6_USB_IMPULSE_RESPONSE
|
||||
bool "measure impulse response"
|
||||
default n
|
||||
help
|
||||
Say Y here to add code to measure the impulse response of a Line6
|
||||
device. This is more accurate than user-space methods since it
|
||||
bypasses any PCM data buffering (e.g., by ALSA or jack). This is
|
||||
useful for assessing the performance of new devices, but is not
|
||||
required for normal operation.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
endif # LINE6_USB
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -9,12 +9,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "audio.h"
|
||||
|
||||
|
||||
static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
|
||||
static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
|
||||
@@ -36,8 +36,9 @@ int line6_init_audio(struct usb_line6 *line6)
|
||||
|
||||
line6->card = card;
|
||||
|
||||
strcpy(card->id, line6->properties->id);
|
||||
strcpy(card->driver, DRIVER_NAME);
|
||||
strcpy(card->shortname, "Line6-USB");
|
||||
strcpy(card->shortname, line6->properties->name);
|
||||
sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name,
|
||||
dev_name(line6->ifcdev)); /* 80 chars - see asound.h */
|
||||
return 0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
|
||||
+117
-113
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -9,27 +9,24 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "capture.h"
|
||||
#include "driver.h"
|
||||
#include "pcm.h"
|
||||
#include "pod.h"
|
||||
#include "capture.h"
|
||||
|
||||
|
||||
/*
|
||||
Find a free URB and submit it.
|
||||
*/
|
||||
static int submit_audio_in_urb(struct snd_pcm_substream *substream)
|
||||
static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
unsigned int index;
|
||||
int index;
|
||||
unsigned long flags;
|
||||
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
|
||||
int i, urb_size;
|
||||
struct urb *urb_in;
|
||||
|
||||
@@ -37,9 +34,9 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
|
||||
index =
|
||||
find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS);
|
||||
|
||||
if (index >= LINE6_ISO_BUFFERS) {
|
||||
if (index < 0 || index >= LINE6_ISO_BUFFERS) {
|
||||
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
|
||||
dev_err(s2m(substream), "no free URB found\n");
|
||||
dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -58,13 +55,13 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
|
||||
line6pcm->buffer_in +
|
||||
index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
|
||||
urb_in->transfer_buffer_length = urb_size;
|
||||
urb_in->context = substream;
|
||||
urb_in->context = line6pcm;
|
||||
|
||||
if (usb_submit_urb(urb_in, GFP_ATOMIC) == 0)
|
||||
set_bit(index, &line6pcm->active_urb_in);
|
||||
else
|
||||
dev_err(s2m(substream), "URB in #%d submission failed\n",
|
||||
index);
|
||||
dev_err(line6pcm->line6->ifcdev,
|
||||
"URB in #%d submission failed\n", index);
|
||||
|
||||
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
|
||||
return 0;
|
||||
@@ -73,12 +70,12 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream)
|
||||
/*
|
||||
Submit all currently available capture URBs.
|
||||
*/
|
||||
static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream)
|
||||
int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
|
||||
ret = submit_audio_in_urb(substream);
|
||||
ret = submit_audio_in_urb(line6pcm);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
@@ -89,7 +86,7 @@ static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream)
|
||||
/*
|
||||
Unlink all currently active capture URBs.
|
||||
*/
|
||||
static void unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm)
|
||||
void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
@@ -126,41 +123,83 @@ static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
|
||||
} while (--timeout > 0);
|
||||
if (alive)
|
||||
snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
|
||||
|
||||
line6pcm->active_urb_in = 0;
|
||||
line6pcm->unlink_urb_in = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Unlink all currently active capture URBs, and wait for finishing.
|
||||
*/
|
||||
void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
|
||||
void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
unlink_audio_in_urbs(line6pcm);
|
||||
line6_unlink_audio_in_urbs(line6pcm);
|
||||
wait_clear_audio_in_urbs(line6pcm);
|
||||
}
|
||||
|
||||
/*
|
||||
Callback for completed capture URB.
|
||||
Copy data into ALSA capture buffer.
|
||||
*/
|
||||
void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
|
||||
{
|
||||
struct snd_pcm_substream *substream =
|
||||
get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
|
||||
int frames = fsize / bytes_per_frame;
|
||||
|
||||
if (line6pcm->pos_in_done + frames > runtime->buffer_size) {
|
||||
/*
|
||||
The transferred area goes over buffer boundary,
|
||||
copy two separate chunks.
|
||||
*/
|
||||
int len;
|
||||
len = runtime->buffer_size - line6pcm->pos_in_done;
|
||||
|
||||
if (len > 0) {
|
||||
memcpy(runtime->dma_area +
|
||||
line6pcm->pos_in_done * bytes_per_frame, fbuf,
|
||||
len * bytes_per_frame);
|
||||
memcpy(runtime->dma_area, fbuf + len * bytes_per_frame,
|
||||
(frames - len) * bytes_per_frame);
|
||||
} else
|
||||
dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", len); /* this is somewhat paranoid */
|
||||
} else {
|
||||
/* copy single chunk */
|
||||
memcpy(runtime->dma_area +
|
||||
line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize);
|
||||
}
|
||||
|
||||
if ((line6pcm->pos_in_done += frames) >= runtime->buffer_size)
|
||||
line6pcm->pos_in_done -= runtime->buffer_size;
|
||||
}
|
||||
|
||||
void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
|
||||
{
|
||||
struct snd_pcm_substream *substream =
|
||||
get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
|
||||
|
||||
if ((line6pcm->bytes_in += length) >= line6pcm->period_in) {
|
||||
line6pcm->bytes_in %= line6pcm->period_in;
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Callback for completed capture URB.
|
||||
*/
|
||||
static void audio_in_callback(struct urb *urb)
|
||||
{
|
||||
int i, index, length = 0, shutdown = 0;
|
||||
int frames;
|
||||
unsigned long flags;
|
||||
|
||||
struct snd_pcm_substream *substream =
|
||||
(struct snd_pcm_substream *)urb->context;
|
||||
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
|
||||
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
|
||||
|
||||
line6pcm->last_frame_in = urb->start_frame;
|
||||
|
||||
/* find index of URB */
|
||||
for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
|
||||
if (urb == line6pcm->urb_audio_in[index])
|
||||
break;
|
||||
|
||||
#if DO_DUMP_PCM_RECEIVE
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_PCM
|
||||
for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
|
||||
struct usb_iso_packet_descriptor *fout =
|
||||
&urb->iso_frame_desc[i];
|
||||
@@ -184,64 +223,43 @@ static void audio_in_callback(struct urb *urb)
|
||||
|
||||
fbuf = urb->transfer_buffer + fin->offset;
|
||||
fsize = fin->actual_length;
|
||||
|
||||
if (fsize > line6pcm->max_packet_size) {
|
||||
dev_err(line6pcm->line6->ifcdev,
|
||||
"driver and/or device bug: packet too large (%d > %d)\n",
|
||||
fsize, line6pcm->max_packet_size);
|
||||
}
|
||||
|
||||
length += fsize;
|
||||
|
||||
if (fsize > 0) {
|
||||
frames = fsize / bytes_per_frame;
|
||||
/* the following assumes LINE6_ISO_PACKETS == 1: */
|
||||
#if LINE6_BACKUP_MONITOR_SIGNAL
|
||||
memcpy(line6pcm->prev_fbuf, fbuf, fsize);
|
||||
#else
|
||||
line6pcm->prev_fbuf = fbuf;
|
||||
#endif
|
||||
line6pcm->prev_fsize = fsize;
|
||||
|
||||
if (line6pcm->pos_in_done + frames >
|
||||
runtime->buffer_size) {
|
||||
/*
|
||||
The transferred area goes over buffer
|
||||
boundary, copy two separate chunks.
|
||||
*/
|
||||
int len;
|
||||
len =
|
||||
runtime->buffer_size -
|
||||
line6pcm->pos_in_done;
|
||||
|
||||
if (len > 0) {
|
||||
memcpy(runtime->dma_area +
|
||||
line6pcm->pos_in_done *
|
||||
bytes_per_frame, fbuf,
|
||||
len * bytes_per_frame);
|
||||
memcpy(runtime->dma_area,
|
||||
fbuf + len * bytes_per_frame,
|
||||
(frames -
|
||||
len) * bytes_per_frame);
|
||||
} else {
|
||||
/* this is somewhat paranoid */
|
||||
dev_err(s2m(substream),
|
||||
"driver bug: len = %d\n", len);
|
||||
}
|
||||
} else {
|
||||
/* copy single chunk */
|
||||
memcpy(runtime->dma_area +
|
||||
line6pcm->pos_in_done * bytes_per_frame,
|
||||
fbuf, fsize * bytes_per_frame);
|
||||
}
|
||||
|
||||
line6pcm->pos_in_done += frames;
|
||||
if (line6pcm->pos_in_done >= runtime->buffer_size)
|
||||
line6pcm->pos_in_done -= runtime->buffer_size;
|
||||
}
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
if (!(line6pcm->flags & MASK_PCM_IMPULSE))
|
||||
#endif
|
||||
if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags)
|
||||
&& (fsize > 0))
|
||||
line6_capture_copy(line6pcm, fbuf, fsize);
|
||||
}
|
||||
|
||||
clear_bit(index, &line6pcm->active_urb_in);
|
||||
|
||||
if (test_bit(index, &line6pcm->unlink_urb_in))
|
||||
if (test_and_clear_bit(index, &line6pcm->unlink_urb_in))
|
||||
shutdown = 1;
|
||||
|
||||
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
|
||||
|
||||
if (!shutdown) {
|
||||
submit_audio_in_urb(substream);
|
||||
submit_audio_in_urb(line6pcm);
|
||||
|
||||
line6pcm->bytes_in += length;
|
||||
if (line6pcm->bytes_in >= line6pcm->period_in) {
|
||||
line6pcm->bytes_in -= line6pcm->period_in;
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
if (test_bit(BIT_PCM_ALSA_CAPTURE, &line6pcm->flags))
|
||||
line6_capture_check_period(line6pcm, length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,54 +312,40 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
|
||||
line6pcm->period_in = params_period_bytes(hw_params);
|
||||
line6pcm->buffer_in =
|
||||
kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
|
||||
LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL);
|
||||
|
||||
if (!line6pcm->buffer_in) {
|
||||
dev_err(s2m(substream), "cannot malloc buffer_in\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* hw_free capture callback */
|
||||
static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
|
||||
unlink_wait_clear_audio_in_urbs(line6pcm);
|
||||
|
||||
kfree(line6pcm->buffer_in);
|
||||
line6pcm->buffer_in = NULL;
|
||||
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
/* trigger callback */
|
||||
int snd_line6_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd)
|
||||
{
|
||||
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
|
||||
int err;
|
||||
line6pcm->count_in = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
if (!test_and_set_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) {
|
||||
err = submit_audio_in_all_urbs(substream);
|
||||
#ifdef CONFIG_PM
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
#endif
|
||||
err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_CAPTURE);
|
||||
|
||||
if (err < 0) {
|
||||
clear_bit(BIT_RUNNING_CAPTURE,
|
||||
&line6pcm->flags);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
if (test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags))
|
||||
unlink_audio_in_urbs(line6pcm);
|
||||
#ifdef CONFIG_PM
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
#endif
|
||||
err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_CAPTURE);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
break;
|
||||
|
||||
@@ -362,17 +366,17 @@ snd_line6_capture_pointer(struct snd_pcm_substream *substream)
|
||||
|
||||
/* capture operators */
|
||||
struct snd_pcm_ops snd_line6_capture_ops = {
|
||||
.open = snd_line6_capture_open,
|
||||
.close = snd_line6_capture_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_line6_capture_hw_params,
|
||||
.hw_free = snd_line6_capture_hw_free,
|
||||
.prepare = snd_line6_prepare,
|
||||
.trigger = snd_line6_trigger,
|
||||
.pointer = snd_line6_capture_pointer,
|
||||
.open = snd_line6_capture_open,
|
||||
.close = snd_line6_capture_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_line6_capture_hw_params,
|
||||
.hw_free = snd_line6_capture_hw_free,
|
||||
.prepare = snd_line6_prepare,
|
||||
.trigger = snd_line6_trigger,
|
||||
.pointer = snd_line6_capture_pointer,
|
||||
};
|
||||
|
||||
int create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
|
||||
int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -13,20 +13,21 @@
|
||||
#define CAPTURE_H
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <sound/pcm.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "pcm.h"
|
||||
|
||||
|
||||
extern struct snd_pcm_ops snd_line6_capture_ops;
|
||||
|
||||
|
||||
extern int create_audio_in_urbs(struct snd_line6_pcm *line6pcm);
|
||||
extern int snd_line6_capture_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd);
|
||||
extern void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm);
|
||||
|
||||
extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf,
|
||||
int fsize);
|
||||
extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm);
|
||||
extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm);
|
||||
extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm);
|
||||
extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm
|
||||
*line6pcm);
|
||||
extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -9,11 +9,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "control.h"
|
||||
#include "driver.h"
|
||||
#include "pod.h"
|
||||
#include "usbdefs.h"
|
||||
#include "variax.h"
|
||||
@@ -45,7 +44,7 @@ static ssize_t pod_get_param_int(struct device *dev, char *buf, int param)
|
||||
{
|
||||
struct usb_interface *interface = to_usb_interface(dev);
|
||||
struct usb_line6_pod *pod = usb_get_intfdata(interface);
|
||||
int retval = line6_wait_dump(&pod->dumpreq, 0);
|
||||
int retval = line6_dump_wait_interruptible(&pod->dumpreq);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
return sprintf(buf, "%d\n", pod->prog_data.control[param]);
|
||||
@@ -63,7 +62,7 @@ static ssize_t pod_set_param_int(struct device *dev, const char *buf,
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
pod_transmit_parameter(pod, param, value);
|
||||
line6_pod_transmit_parameter(pod, param, value);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -71,7 +70,7 @@ static ssize_t variax_get_param_int(struct device *dev, char *buf, int param)
|
||||
{
|
||||
struct usb_interface *interface = to_usb_interface(dev);
|
||||
struct usb_line6_variax *variax = usb_get_intfdata(interface);
|
||||
int retval = line6_wait_dump(&variax->dumpreq, 0);
|
||||
int retval = line6_dump_wait_interruptible(&variax->dumpreq);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
return sprintf(buf, "%d\n", variax->model_data.control[param]);
|
||||
@@ -80,12 +79,11 @@ static ssize_t variax_get_param_int(struct device *dev, char *buf, int param)
|
||||
static ssize_t variax_get_param_float(struct device *dev, char *buf, int param)
|
||||
{
|
||||
/*
|
||||
We do our own floating point handling here since floats in the
|
||||
kernel are problematic for at least two reasons: - many distros
|
||||
are still shipped with binary kernels optimized for the ancient
|
||||
80386 without FPU
|
||||
- there isn't a printf("%f")
|
||||
(see http://www.kernelthread.com/publications/faq/335.html)
|
||||
We do our own floating point handling here since at the time
|
||||
this code was written (Jan 2006) it was highly discouraged to
|
||||
use floating point arithmetic in the kernel. If you think that
|
||||
this no longer applies, feel free to replace this by generic
|
||||
floating point code.
|
||||
*/
|
||||
|
||||
static const int BIAS = 0x7f;
|
||||
@@ -97,7 +95,7 @@ static ssize_t variax_get_param_float(struct device *dev, char *buf, int param)
|
||||
struct usb_interface *interface = to_usb_interface(dev);
|
||||
struct usb_line6_variax *variax = usb_get_intfdata(interface);
|
||||
const unsigned char *p = variax->model_data.control + param;
|
||||
int retval = line6_wait_dump(&variax->dumpreq, 0);
|
||||
int retval = line6_dump_wait_interruptible(&variax->dumpreq);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
@@ -530,7 +528,7 @@ static DEVICE_ATTR(mix1, S_IRUGO, variax_get_mix1, line6_nop_write);
|
||||
static DEVICE_ATTR(pickup_wiring, S_IRUGO, variax_get_pickup_wiring,
|
||||
line6_nop_write);
|
||||
|
||||
int pod_create_files(int firmware, int type, struct device *dev)
|
||||
int line6_pod_create_files(int firmware, int type, struct device *dev)
|
||||
{
|
||||
int err;
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_tweak));
|
||||
@@ -733,9 +731,10 @@ int pod_create_files(int firmware, int type, struct device *dev)
|
||||
(dev, &dev_attr_band_6_gain__bass));
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(pod_create_files);
|
||||
|
||||
void pod_remove_files(int firmware, int type, struct device *dev)
|
||||
EXPORT_SYMBOL(line6_pod_create_files);
|
||||
|
||||
void line6_pod_remove_files(int firmware, int type, struct device *dev)
|
||||
{
|
||||
device_remove_file(dev, &dev_attr_tweak);
|
||||
device_remove_file(dev, &dev_attr_wah_position);
|
||||
@@ -908,9 +907,10 @@ void pod_remove_files(int firmware, int type, struct device *dev)
|
||||
if (firmware >= 200)
|
||||
device_remove_file(dev, &dev_attr_band_6_gain__bass);
|
||||
}
|
||||
EXPORT_SYMBOL(pod_remove_files);
|
||||
|
||||
int variax_create_files(int firmware, int type, struct device *dev)
|
||||
EXPORT_SYMBOL(line6_pod_remove_files);
|
||||
|
||||
int line6_variax_create_files(int firmware, int type, struct device *dev)
|
||||
{
|
||||
int err;
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_body));
|
||||
@@ -954,9 +954,10 @@ int variax_create_files(int firmware, int type, struct device *dev)
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_wiring));
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(variax_create_files);
|
||||
|
||||
void variax_remove_files(int firmware, int type, struct device *dev)
|
||||
EXPORT_SYMBOL(line6_variax_create_files);
|
||||
|
||||
void line6_variax_remove_files(int firmware, int type, struct device *dev)
|
||||
{
|
||||
device_remove_file(dev, &dev_attr_body);
|
||||
device_remove_file(dev, &dev_attr_pickup1_enable);
|
||||
@@ -998,4 +999,5 @@ void variax_remove_files(int firmware, int type, struct device *dev)
|
||||
device_remove_file(dev, &dev_attr_mix1);
|
||||
device_remove_file(dev, &dev_attr_pickup_wiring);
|
||||
}
|
||||
EXPORT_SYMBOL(variax_remove_files);
|
||||
|
||||
EXPORT_SYMBOL(line6_variax_remove_files);
|
||||
|
||||
+54
-130
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -12,54 +12,38 @@
|
||||
#ifndef LINE6_CONTROL_H
|
||||
#define LINE6_CONTROL_H
|
||||
|
||||
|
||||
/**
|
||||
List of PODxt Pro controls.
|
||||
See Appendix C of the "PODxt (Pro) Pilot's Handbook" by Line6.
|
||||
Comments after the number refer to the PODxt Pro firmware version required
|
||||
for this feature.
|
||||
|
||||
Please *don't* reformat this file since "control.c" is created automatically
|
||||
from "control.h", and this process depends on the exact formatting of the
|
||||
code and the comments below!
|
||||
*/
|
||||
/* *INDENT-OFF* */
|
||||
enum {
|
||||
POD_tweak = 1,
|
||||
POD_wah_position = 4,
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_compression_gain = 5,
|
||||
|
||||
POD_compression_gain = 5, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_vol_pedal_position = 7,
|
||||
POD_compression_threshold = 9,
|
||||
POD_pan = 10,
|
||||
POD_amp_model_setup = 11,
|
||||
POD_amp_model = 12, /* firmware: 2.0 */
|
||||
POD_amp_model = 12, /* firmware: 2.0 */
|
||||
POD_drive = 13,
|
||||
POD_bass = 14,
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_mid = 15,
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_lowmid = 15,
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_treble = 16,
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_highmid = 16,
|
||||
|
||||
POD_mid = 15, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_lowmid = 15, /* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_treble = 16, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_highmid = 16, /* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_chan_vol = 17,
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_reverb_mix = 18,
|
||||
|
||||
POD_reverb_mix = 18, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_effect_setup = 19,
|
||||
POD_band_1_frequency = 20, /* firmware: 2.0 */
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_presence = 21,
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_treble__bass = 21,
|
||||
|
||||
POD_presence = 21, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_treble__bass = 21, /* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_noise_gate_enable = 22,
|
||||
POD_gate_threshold = 23,
|
||||
POD_gate_decay_time = 24,
|
||||
@@ -70,137 +54,78 @@ enum {
|
||||
POD_mod_param_1 = 29,
|
||||
POD_delay_param_1 = 30,
|
||||
POD_delay_param_1_note_value = 31,
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_band_2_frequency__bass = 32, /* firmware: 2.0 */
|
||||
|
||||
POD_band_2_frequency__bass = 32, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
|
||||
POD_delay_param_2 = 33,
|
||||
POD_delay_volume_mix = 34,
|
||||
POD_delay_param_3 = 35,
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_reverb_enable = 36,
|
||||
POD_reverb_type = 37,
|
||||
POD_reverb_decay = 38,
|
||||
POD_reverb_tone = 39,
|
||||
POD_reverb_pre_delay = 40,
|
||||
POD_reverb_pre_post = 41,
|
||||
POD_band_2_frequency = 42,
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_band_3_frequency__bass = 42, /* firmware: 2.0 */
|
||||
|
||||
POD_reverb_enable = 36, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_reverb_type = 37, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_reverb_decay = 38, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_reverb_tone = 39, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_reverb_pre_delay = 40, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_reverb_pre_post = 41, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_band_2_frequency = 42, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
|
||||
POD_band_3_frequency__bass = 42, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
|
||||
POD_wah_enable = 43,
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_modulation_lo_cut = 44,
|
||||
POD_delay_reverb_lo_cut = 45,
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_volume_pedal_minimum = 46, /* firmware: 2.0 */
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_eq_pre_post = 46, /* firmware: 2.0 */
|
||||
|
||||
POD_modulation_lo_cut = 44, /* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_delay_reverb_lo_cut = 45, /* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_volume_pedal_minimum = 46, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
|
||||
POD_eq_pre_post = 46, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
|
||||
POD_volume_pre_post = 47,
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_di_model = 48,
|
||||
POD_di_delay = 49,
|
||||
|
||||
POD_di_model = 48, /* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_di_delay = 49, /* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_mod_enable = 50,
|
||||
POD_mod_param_1_note_value = 51,
|
||||
POD_mod_param_2 = 52,
|
||||
POD_mod_param_3 = 53,
|
||||
POD_mod_param_4 = 54,
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_mod_param_5 = 55,
|
||||
|
||||
POD_mod_param_5 = 55, /* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_mod_volume_mix = 56,
|
||||
POD_mod_pre_post = 57,
|
||||
POD_modulation_model = 58,
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_band_3_frequency = 60, /* firmware: 2.0 */
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_band_4_frequency__bass = 60, /* firmware: 2.0 */
|
||||
|
||||
POD_band_3_frequency = 60, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
|
||||
POD_band_4_frequency__bass = 60, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
|
||||
POD_mod_param_1_double_precision = 61,
|
||||
POD_delay_param_1_double_precision = 62,
|
||||
POD_eq_enable = 63, /* firmware: 2.0 */
|
||||
POD_tap = 64,
|
||||
POD_volume_tweak_pedal_assign = 65,
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_band_5_frequency = 68, /* firmware: 2.0 */
|
||||
|
||||
POD_band_5_frequency = 68, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
|
||||
POD_tuner = 69,
|
||||
POD_mic_selection = 70,
|
||||
POD_cabinet_model = 71,
|
||||
POD_stomp_model = 75,
|
||||
POD_roomlevel = 76,
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_band_4_frequency = 77, /* firmware: 2.0 */
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_band_6_frequency = 77, /* firmware: 2.0 */
|
||||
|
||||
POD_band_4_frequency = 77, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
|
||||
POD_band_6_frequency = 77, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
|
||||
POD_stomp_param_1_note_value = 78,
|
||||
POD_stomp_param_2 = 79,
|
||||
POD_stomp_param_3 = 80,
|
||||
POD_stomp_param_4 = 81,
|
||||
POD_stomp_param_5 = 82,
|
||||
POD_stomp_param_6 = 83,
|
||||
|
||||
/* device: LINE6_BITS_LIVE */
|
||||
POD_amp_switch_select = 84,
|
||||
|
||||
POD_amp_switch_select = 84, /* device: LINE6_BITS_LIVE */
|
||||
POD_delay_param_4 = 85,
|
||||
POD_delay_param_5 = 86,
|
||||
POD_delay_pre_post = 87,
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_delay_model = 88,
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_delay_verb_model = 88,
|
||||
|
||||
POD_delay_model = 88, /* device: LINE6_BITS_PODXTALL */
|
||||
POD_delay_verb_model = 88, /* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_tempo_msb = 89,
|
||||
POD_tempo_lsb = 90,
|
||||
POD_wah_model = 91, /* firmware: 3.0 */
|
||||
POD_bypass_volume = 105, /* firmware: 2.14 */
|
||||
|
||||
/* device: LINE6_BITS_PRO */
|
||||
POD_fx_loop_on_off = 107,
|
||||
|
||||
POD_fx_loop_on_off = 107, /* device: LINE6_BITS_PRO */
|
||||
POD_tweak_param_select = 108,
|
||||
POD_amp1_engage = 111,
|
||||
POD_band_1_gain = 114, /* firmware: 2.0 */
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_band_2_gain__bass = 115, /* firmware: 2.0 */
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_band_2_gain = 116, /* firmware: 2.0 */
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_band_3_gain__bass = 116, /* firmware: 2.0 */
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_band_3_gain = 117, /* firmware: 2.0 */
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_band_4_gain__bass = 117, /* firmware: 2.0 */
|
||||
POD_band_5_gain__bass = 118, /* firmware: 2.0 */
|
||||
|
||||
/* device: LINE6_BITS_PODXTALL */
|
||||
POD_band_4_gain = 119, /* firmware: 2.0 */
|
||||
|
||||
/* device: LINE6_BITS_BASSPODXTALL */
|
||||
POD_band_6_gain__bass = 119 /* firmware: 2.0 */
|
||||
POD_band_2_gain__bass = 115, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
|
||||
POD_band_2_gain = 116, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
|
||||
POD_band_3_gain__bass = 116, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
|
||||
POD_band_3_gain = 117, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
|
||||
POD_band_4_gain__bass = 117, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
|
||||
POD_band_5_gain__bass = 118, /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
|
||||
POD_band_4_gain = 119, /* device: LINE6_BITS_PODXTALL */ /* firmware: 2.0 */
|
||||
POD_band_6_gain__bass = 119 /* device: LINE6_BITS_BASSPODXTALL */ /* firmware: 2.0 */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -218,8 +143,7 @@ enum {
|
||||
VARIAX_pickup2_position = 23, /* type: 24 bit float */
|
||||
VARIAX_pickup2_angle = 26, /* type: 24 bit float */
|
||||
VARIAX_pickup2_level = 29, /* type: 24 bit float */
|
||||
VARIAX_pickup_phase = 32, /* 0: in phase,
|
||||
1: out of phase */
|
||||
VARIAX_pickup_phase = 32, /* 0: in phase, 1: out of phase */
|
||||
VARIAX_capacitance = 33, /* type: 24 bit float */
|
||||
VARIAX_tone_resistance = 36, /* type: 24 bit float */
|
||||
VARIAX_volume_resistance = 39, /* type: 24 bit float */
|
||||
@@ -258,10 +182,10 @@ enum {
|
||||
};
|
||||
|
||||
|
||||
extern int pod_create_files(int firmware, int type, struct device *dev);
|
||||
extern void pod_remove_files(int firmware, int type, struct device *dev);
|
||||
extern int variax_create_files(int firmware, int type, struct device *dev);
|
||||
extern void variax_remove_files(int firmware, int type, struct device *dev);
|
||||
extern int line6_pod_create_files(int firmware, int type, struct device *dev);
|
||||
extern void line6_pod_remove_files(int firmware, int type, struct device *dev);
|
||||
extern int line6_variax_create_files(int firmware, int type, struct device *dev);
|
||||
extern void line6_variax_remove_files(int firmware, int type, struct device *dev);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
+286
-132
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -13,23 +13,24 @@
|
||||
#define DRIVER_H
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/wait.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
#include "midi.h"
|
||||
|
||||
|
||||
#define DRIVER_NAME "line6usb"
|
||||
|
||||
#if defined(CONFIG_LINE6_USB_DUMP_CTRL) || defined(CONFIG_LINE6_USB_DUMP_MIDI) || defined(CONFIG_LINE6_USB_DUMP_PCM)
|
||||
#define CONFIG_LINE6_USB_DUMP_ANY
|
||||
#endif
|
||||
|
||||
#define LINE6_TIMEOUT 1
|
||||
#define LINE6_MAX_DEVICES 8
|
||||
#define LINE6_BUFSIZE_LISTEN 32
|
||||
#define LINE6_MESSAGE_MAXLEN 256
|
||||
|
||||
|
||||
/*
|
||||
Line6 MIDI control commands
|
||||
*/
|
||||
@@ -54,6 +55,12 @@
|
||||
|
||||
#define LINE6_CHANNEL_MASK 0x0f
|
||||
|
||||
#ifdef CONFIG_LINE6_USB_DEBUG
|
||||
#define DEBUG_MESSAGES(x) (x)
|
||||
#else
|
||||
#define DEBUG_MESSAGES(x)
|
||||
#endif
|
||||
|
||||
|
||||
#define MISSING_CASE \
|
||||
printk(KERN_ERR "line6usb driver bug: missing case in %s:%d\n", \
|
||||
@@ -67,10 +74,14 @@ do { \
|
||||
return err; \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_STARTUP_PROGRESS(x, n) \
|
||||
if((x) >= (n)) \
|
||||
return; \
|
||||
x = (n);
|
||||
|
||||
|
||||
extern const unsigned char line6_midi_id[3];
|
||||
extern struct usb_line6 *line6_devices[LINE6_MAX_DEVICES];
|
||||
extern struct workqueue_struct *line6_workqueue;
|
||||
|
||||
static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3;
|
||||
static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4;
|
||||
@@ -80,8 +91,27 @@ static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4;
|
||||
Common properties of Line6 devices.
|
||||
*/
|
||||
struct line6_properties {
|
||||
/**
|
||||
Card id string (maximum 16 characters).
|
||||
This can be used to address the device in ALSA programs as
|
||||
"default:CARD=<id>"
|
||||
*/
|
||||
const char *id;
|
||||
|
||||
/**
|
||||
Card short name (maximum 32 characters).
|
||||
*/
|
||||
const char *name;
|
||||
|
||||
/**
|
||||
Bit identifying this device in the line6usb driver.
|
||||
*/
|
||||
int device_bit;
|
||||
|
||||
/**
|
||||
Bit vector defining this device's capabilities in the
|
||||
line6usb driver.
|
||||
*/
|
||||
int capabilities;
|
||||
};
|
||||
|
||||
@@ -191,14 +221,22 @@ extern int line6_send_raw_message_async(struct usb_line6 *line6,
|
||||
const char *buffer, int size);
|
||||
extern int line6_send_sysex_message(struct usb_line6 *line6,
|
||||
const char *buffer, int size);
|
||||
extern int line6_send_sysex_message_async(struct usb_line6 *line6,
|
||||
const char *buffer, int size);
|
||||
extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
extern void line6_start_timer(struct timer_list *timer, unsigned int msecs,
|
||||
void (*function)(unsigned long), unsigned long data);
|
||||
extern int line6_transmit_parameter(struct usb_line6 *line6, int param,
|
||||
int value);
|
||||
extern int line6_version_request_async(struct usb_line6 *line6);
|
||||
extern int line6_write_data(struct usb_line6 *line6, int address, void *data,
|
||||
size_t datalen);
|
||||
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_ANY
|
||||
extern void line6_write_hexdump(struct usb_line6 *line6, char dir,
|
||||
const unsigned char *buffer, int size);
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -9,10 +9,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "dumprequest.h"
|
||||
|
||||
|
||||
@@ -39,17 +38,17 @@ void line6_invalidate_current(struct line6_dump_request *l6dr)
|
||||
void line6_dump_finished(struct line6_dump_request *l6dr)
|
||||
{
|
||||
l6dr->in_progress = LINE6_DUMP_NONE;
|
||||
wake_up_interruptible(&l6dr->wait);
|
||||
wake_up(&l6dr->wait);
|
||||
}
|
||||
|
||||
/*
|
||||
Send an asynchronous channel dump request.
|
||||
*/
|
||||
int line6_dump_request_async(struct line6_dump_request *l6dr,
|
||||
struct usb_line6 *line6, int num)
|
||||
struct usb_line6 *line6, int num, int dest)
|
||||
{
|
||||
int ret;
|
||||
line6_invalidate_current(l6dr);
|
||||
line6_dump_started(l6dr, dest);
|
||||
ret = line6_send_raw_message_async(line6, l6dr->reqbufs[num].buffer,
|
||||
l6dr->reqbufs[num].length);
|
||||
|
||||
@@ -60,43 +59,27 @@ int line6_dump_request_async(struct line6_dump_request *l6dr,
|
||||
}
|
||||
|
||||
/*
|
||||
Send an asynchronous dump request after a given interval.
|
||||
Wait for completion (interruptible).
|
||||
*/
|
||||
void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds,
|
||||
void (*function)(unsigned long), void *data)
|
||||
int line6_dump_wait_interruptible(struct line6_dump_request *l6dr)
|
||||
{
|
||||
l6dr->timer.expires = jiffies + seconds * HZ;
|
||||
l6dr->timer.function = function;
|
||||
l6dr->timer.data = (unsigned long)data;
|
||||
add_timer(&l6dr->timer);
|
||||
return wait_event_interruptible(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE);
|
||||
}
|
||||
|
||||
/*
|
||||
Wait for completion.
|
||||
*/
|
||||
int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock)
|
||||
void line6_dump_wait(struct line6_dump_request *l6dr)
|
||||
{
|
||||
int retval = 0;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
add_wait_queue(&l6dr->wait, &wait);
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
wait_event(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE);
|
||||
}
|
||||
|
||||
while (l6dr->in_progress) {
|
||||
if (nonblock) {
|
||||
retval = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (signal_pending(current)) {
|
||||
retval = -ERESTARTSYS;
|
||||
break;
|
||||
} else
|
||||
schedule();
|
||||
}
|
||||
|
||||
current->state = TASK_RUNNING;
|
||||
remove_wait_queue(&l6dr->wait, &wait);
|
||||
return retval;
|
||||
/*
|
||||
Wait for completion (with timeout).
|
||||
*/
|
||||
int line6_dump_wait_timeout(struct line6_dump_request *l6dr, long timeout)
|
||||
{
|
||||
return wait_event_timeout(l6dr->wait, l6dr->in_progress == LINE6_DUMP_NONE, timeout);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -123,7 +106,6 @@ int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
init_waitqueue_head(&l6dr->wait);
|
||||
init_timer(&l6dr->timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -148,6 +130,4 @@ void line6_dumpreq_destruct(struct line6_dump_request *l6dr)
|
||||
if (l6dr->reqbufs[0].buffer == NULL)
|
||||
return;
|
||||
line6_dumpreq_destructbuf(l6dr, 0);
|
||||
l6dr->ok = 1;
|
||||
del_timer_sync(&l6dr->timer);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
|
||||
|
||||
@@ -55,16 +54,6 @@ struct line6_dump_request {
|
||||
*/
|
||||
int in_progress;
|
||||
|
||||
/**
|
||||
Timer for delayed dump request.
|
||||
*/
|
||||
struct timer_list timer;
|
||||
|
||||
/**
|
||||
Flag if initial dump request has been successful.
|
||||
*/
|
||||
char ok;
|
||||
|
||||
/**
|
||||
Dump request buffers
|
||||
*/
|
||||
@@ -73,7 +62,7 @@ struct line6_dump_request {
|
||||
|
||||
extern void line6_dump_finished(struct line6_dump_request *l6dr);
|
||||
extern int line6_dump_request_async(struct line6_dump_request *l6dr,
|
||||
struct usb_line6 *line6, int num);
|
||||
struct usb_line6 *line6, int num, int dest);
|
||||
extern void line6_dump_started(struct line6_dump_request *l6dr, int dest);
|
||||
extern void line6_dumpreq_destruct(struct line6_dump_request *l6dr);
|
||||
extern void line6_dumpreq_destructbuf(struct line6_dump_request *l6dr, int num);
|
||||
@@ -82,9 +71,10 @@ extern int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf,
|
||||
extern int line6_dumpreq_initbuf(struct line6_dump_request *l6dr,
|
||||
const void *buf, size_t len, int num);
|
||||
extern void line6_invalidate_current(struct line6_dump_request *l6dr);
|
||||
extern void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds,
|
||||
void (*function)(unsigned long), void *data);
|
||||
extern int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock);
|
||||
extern void line6_dump_wait(struct line6_dump_request *l6dr);
|
||||
extern int line6_dump_wait_interruptible(struct line6_dump_request *l6dr);
|
||||
extern int line6_dump_wait_timeout(struct line6_dump_request *l6dr,
|
||||
long timeout);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -9,24 +9,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/rawmidi.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "driver.h"
|
||||
#include "midi.h"
|
||||
#include "pod.h"
|
||||
#include "usbdefs.h"
|
||||
|
||||
|
||||
#define USE_MIDIBUF 1
|
||||
#define OUTPUT_DUMP_ONLY 0
|
||||
|
||||
|
||||
#define line6_rawmidi_substream_midi(substream) \
|
||||
((struct snd_line6_midi *)((substream)->rmidi->private_data))
|
||||
|
||||
@@ -61,26 +55,26 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
|
||||
spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags);
|
||||
|
||||
for (;;) {
|
||||
req = min(midibuf_bytes_free(mb), line6->max_packet_size);
|
||||
req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size);
|
||||
done = snd_rawmidi_transmit_peek(substream, chunk, req);
|
||||
|
||||
if (done == 0)
|
||||
break;
|
||||
|
||||
#if DO_DUMP_MIDI_SEND
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_MIDI
|
||||
line6_write_hexdump(line6, 's', chunk, done);
|
||||
#endif
|
||||
midibuf_write(mb, chunk, done);
|
||||
line6_midibuf_write(mb, chunk, done);
|
||||
snd_rawmidi_transmit_ack(substream, done);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
done = midibuf_read(mb, chunk, line6->max_packet_size);
|
||||
done = line6_midibuf_read(mb, chunk, line6->max_packet_size);
|
||||
|
||||
if (done == 0)
|
||||
break;
|
||||
|
||||
if (midibuf_skip_message(mb, line6midi->midi_mask_transmit))
|
||||
if (line6_midibuf_skip_message(mb, line6midi->midi_mask_transmit))
|
||||
continue;
|
||||
|
||||
send_midi_async(line6, chunk, done);
|
||||
@@ -115,7 +109,7 @@ static void midi_sent(struct urb *urb)
|
||||
}
|
||||
|
||||
if (num == 0)
|
||||
wake_up_interruptible(&line6->line6midi->send_wait);
|
||||
wake_up(&line6->line6midi->send_wait);
|
||||
|
||||
spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags);
|
||||
}
|
||||
@@ -139,7 +133,7 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#if DO_DUMP_URB_SEND
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
|
||||
line6_write_hexdump(line6, 'S', data, length);
|
||||
#endif
|
||||
|
||||
@@ -176,8 +170,8 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
|
||||
case LINE6_DEVID_PODXTLIVE:
|
||||
case LINE6_DEVID_PODXTPRO:
|
||||
case LINE6_DEVID_POCKETPOD:
|
||||
pod_midi_postprocess((struct usb_line6_pod *)line6, data,
|
||||
length);
|
||||
line6_pod_midi_postprocess((struct usb_line6_pod *)line6, data,
|
||||
length);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -215,19 +209,8 @@ static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream,
|
||||
static void line6_midi_output_drain(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6;
|
||||
wait_queue_head_t *head = &line6->line6midi->send_wait;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
add_wait_queue(head, &wait);
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
|
||||
while (line6->line6midi->num_active_send_urbs > 0)
|
||||
if (signal_pending(current))
|
||||
break;
|
||||
else
|
||||
schedule();
|
||||
|
||||
current->state = TASK_RUNNING;
|
||||
remove_wait_queue(head, &wait);
|
||||
struct snd_line6_midi *midi = line6->line6midi;
|
||||
wait_event_interruptible(midi->send_wait, midi->num_active_send_urbs == 0);
|
||||
}
|
||||
|
||||
static int line6_midi_input_open(struct snd_rawmidi_substream *substream)
|
||||
@@ -284,6 +267,7 @@ static int snd_line6_new_midi(struct snd_line6_midi *line6midi)
|
||||
|
||||
rmidi->private_data = line6midi;
|
||||
rmidi->private_free = line6_cleanup_midi;
|
||||
strcpy(rmidi->id, line6midi->line6->properties->id);
|
||||
strcpy(rmidi->name, line6midi->line6->properties->name);
|
||||
|
||||
rmidi->info_flags =
|
||||
@@ -371,8 +355,8 @@ static int snd_line6_midi_free(struct snd_device *device)
|
||||
struct snd_line6_midi *line6midi = device->device_data;
|
||||
device_remove_file(line6midi->line6->ifcdev, &dev_attr_midi_mask_transmit);
|
||||
device_remove_file(line6midi->line6->ifcdev, &dev_attr_midi_mask_receive);
|
||||
midibuf_destroy(&line6midi->midibuf_in);
|
||||
midibuf_destroy(&line6midi->midibuf_out);
|
||||
line6_midibuf_destroy(&line6midi->midibuf_in);
|
||||
line6_midibuf_destroy(&line6midi->midibuf_out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -396,11 +380,11 @@ int line6_init_midi(struct usb_line6 *line6)
|
||||
if (line6midi == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
err = midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);
|
||||
err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1);
|
||||
err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -9,8 +9,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "midibuf.h"
|
||||
@@ -25,9 +23,9 @@ static int midibuf_message_length(unsigned char code)
|
||||
return length[(code >> 4) - 8];
|
||||
} else {
|
||||
/*
|
||||
Note that according to the MIDI specification 0xf2 is
|
||||
the "Song Position Pointer", but this is used by Line6
|
||||
to send sysex messages to the host.
|
||||
Note that according to the MIDI specification 0xf2 is
|
||||
the "Song Position Pointer", but this is used by Line6
|
||||
to send sysex messages to the host.
|
||||
*/
|
||||
static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1,
|
||||
1, 1, 1, -1, 1, 1 };
|
||||
@@ -35,32 +33,6 @@ static int midibuf_message_length(unsigned char code)
|
||||
}
|
||||
}
|
||||
|
||||
void midibuf_reset(struct MidiBuffer *this)
|
||||
{
|
||||
this->pos_read = this->pos_write = this->full = 0;
|
||||
this->command_prev = -1;
|
||||
}
|
||||
|
||||
int midibuf_init(struct MidiBuffer *this, int size, int split)
|
||||
{
|
||||
this->buf = kmalloc(size, GFP_KERNEL);
|
||||
|
||||
if (this->buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
this->size = size;
|
||||
this->split = split;
|
||||
midibuf_reset(this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void midibuf_status(struct MidiBuffer *this)
|
||||
{
|
||||
printk(KERN_DEBUG "midibuf size=%d split=%d pos_read=%d pos_write=%d "
|
||||
"full=%d command_prev=%02x\n", this->size, this->split,
|
||||
this->pos_read, this->pos_write, this->full, this->command_prev);
|
||||
}
|
||||
|
||||
static int midibuf_is_empty(struct MidiBuffer *this)
|
||||
{
|
||||
return (this->pos_read == this->pos_write) && !this->full;
|
||||
@@ -71,7 +43,33 @@ static int midibuf_is_full(struct MidiBuffer *this)
|
||||
return this->full;
|
||||
}
|
||||
|
||||
int midibuf_bytes_free(struct MidiBuffer *this)
|
||||
void line6_midibuf_reset(struct MidiBuffer *this)
|
||||
{
|
||||
this->pos_read = this->pos_write = this->full = 0;
|
||||
this->command_prev = -1;
|
||||
}
|
||||
|
||||
int line6_midibuf_init(struct MidiBuffer *this, int size, int split)
|
||||
{
|
||||
this->buf = kmalloc(size, GFP_KERNEL);
|
||||
|
||||
if (this->buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
this->size = size;
|
||||
this->split = split;
|
||||
line6_midibuf_reset(this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void line6_midibuf_status(struct MidiBuffer *this)
|
||||
{
|
||||
printk(KERN_DEBUG "midibuf size=%d split=%d pos_read=%d pos_write=%d "
|
||||
"full=%d command_prev=%02x\n", this->size, this->split,
|
||||
this->pos_read, this->pos_write, this->full, this->command_prev);
|
||||
}
|
||||
|
||||
int line6_midibuf_bytes_free(struct MidiBuffer *this)
|
||||
{
|
||||
return
|
||||
midibuf_is_full(this) ?
|
||||
@@ -79,7 +77,7 @@ int midibuf_bytes_free(struct MidiBuffer *this)
|
||||
(this->pos_read - this->pos_write + this->size - 1) % this->size + 1;
|
||||
}
|
||||
|
||||
int midibuf_bytes_used(struct MidiBuffer *this)
|
||||
int line6_midibuf_bytes_used(struct MidiBuffer *this)
|
||||
{
|
||||
return
|
||||
midibuf_is_empty(this) ?
|
||||
@@ -87,7 +85,7 @@ int midibuf_bytes_used(struct MidiBuffer *this)
|
||||
(this->pos_write - this->pos_read + this->size - 1) % this->size + 1;
|
||||
}
|
||||
|
||||
int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
|
||||
int line6_midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
|
||||
{
|
||||
int bytes_free;
|
||||
int length1, length2;
|
||||
@@ -102,7 +100,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
|
||||
skip_active_sense = 1;
|
||||
}
|
||||
|
||||
bytes_free = midibuf_bytes_free(this);
|
||||
bytes_free = line6_midibuf_bytes_free(this);
|
||||
|
||||
if (length > bytes_free)
|
||||
length = bytes_free;
|
||||
@@ -129,7 +127,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
|
||||
return length + skip_active_sense;
|
||||
}
|
||||
|
||||
int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
|
||||
int line6_midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
|
||||
{
|
||||
int bytes_used;
|
||||
int length1, length2;
|
||||
@@ -145,7 +143,7 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
|
||||
if (midibuf_is_empty(this))
|
||||
return 0;
|
||||
|
||||
bytes_used = midibuf_bytes_used(this);
|
||||
bytes_used = line6_midibuf_bytes_used(this);
|
||||
|
||||
if (length > bytes_used)
|
||||
length = bytes_used;
|
||||
@@ -232,9 +230,9 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
|
||||
return length + repeat;
|
||||
}
|
||||
|
||||
int midibuf_ignore(struct MidiBuffer *this, int length)
|
||||
int line6_midibuf_ignore(struct MidiBuffer *this, int length)
|
||||
{
|
||||
int bytes_used = midibuf_bytes_used(this);
|
||||
int bytes_used = line6_midibuf_bytes_used(this);
|
||||
|
||||
if (length > bytes_used)
|
||||
length = bytes_used;
|
||||
@@ -244,7 +242,7 @@ int midibuf_ignore(struct MidiBuffer *this, int length)
|
||||
return length;
|
||||
}
|
||||
|
||||
int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
|
||||
int line6_midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
|
||||
{
|
||||
int cmd = this->command_prev;
|
||||
|
||||
@@ -255,7 +253,7 @@ int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void midibuf_destroy(struct MidiBuffer *this)
|
||||
void line6_midibuf_destroy(struct MidiBuffer *this)
|
||||
{
|
||||
kfree(this->buf);
|
||||
this->buf = NULL;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -23,17 +23,17 @@ struct MidiBuffer {
|
||||
};
|
||||
|
||||
|
||||
extern int midibuf_bytes_used(struct MidiBuffer *mb);
|
||||
extern int midibuf_bytes_free(struct MidiBuffer *mb);
|
||||
extern void midibuf_destroy(struct MidiBuffer *mb);
|
||||
extern int midibuf_ignore(struct MidiBuffer *mb, int length);
|
||||
extern int midibuf_init(struct MidiBuffer *mb, int size, int split);
|
||||
extern int midibuf_read(struct MidiBuffer *mb, unsigned char *data, int length);
|
||||
extern void midibuf_reset(struct MidiBuffer *mb);
|
||||
extern int midibuf_skip_message(struct MidiBuffer *mb, unsigned short mask);
|
||||
extern void midibuf_status(struct MidiBuffer *mb);
|
||||
extern int midibuf_write(struct MidiBuffer *mb, unsigned char *data,
|
||||
int length);
|
||||
extern int line6_midibuf_bytes_used(struct MidiBuffer *mb);
|
||||
extern int line6_midibuf_bytes_free(struct MidiBuffer *mb);
|
||||
extern void line6_midibuf_destroy(struct MidiBuffer *mb);
|
||||
extern int line6_midibuf_ignore(struct MidiBuffer *mb, int length);
|
||||
extern int line6_midibuf_init(struct MidiBuffer *mb, int size, int split);
|
||||
extern int line6_midibuf_read(struct MidiBuffer *mb, unsigned char *data, int length);
|
||||
extern void line6_midibuf_reset(struct MidiBuffer *mb);
|
||||
extern int line6_midibuf_skip_message(struct MidiBuffer *mb, unsigned short mask);
|
||||
extern void line6_midibuf_status(struct MidiBuffer *mb);
|
||||
extern int line6_midibuf_write(struct MidiBuffer *mb, unsigned char *data,
|
||||
int length);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
+241
-35
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -9,10 +9,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/pcm.h>
|
||||
@@ -20,10 +17,176 @@
|
||||
|
||||
#include "audio.h"
|
||||
#include "capture.h"
|
||||
#include "driver.h"
|
||||
#include "playback.h"
|
||||
#include "pod.h"
|
||||
|
||||
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
|
||||
static struct snd_line6_pcm* dev2pcm(struct device *dev)
|
||||
{
|
||||
struct usb_interface *interface = to_usb_interface(dev);
|
||||
struct usb_line6 *line6 = usb_get_intfdata(interface);
|
||||
struct snd_line6_pcm *line6pcm = line6->line6pcm;
|
||||
return line6pcm;
|
||||
}
|
||||
|
||||
/*
|
||||
"read" request on "impulse_volume" special file.
|
||||
*/
|
||||
static ssize_t pcm_get_impulse_volume(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_volume);
|
||||
}
|
||||
|
||||
/*
|
||||
"write" request on "impulse_volume" special file.
|
||||
*/
|
||||
static ssize_t pcm_set_impulse_volume(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_line6_pcm *line6pcm = dev2pcm(dev);
|
||||
int value = simple_strtoul(buf, NULL, 10);
|
||||
line6pcm->impulse_volume = value;
|
||||
|
||||
if(value > 0)
|
||||
line6_pcm_start(line6pcm, MASK_PCM_IMPULSE);
|
||||
else
|
||||
line6_pcm_stop(line6pcm, MASK_PCM_IMPULSE);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
"read" request on "impulse_period" special file.
|
||||
*/
|
||||
static ssize_t pcm_get_impulse_period(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_period);
|
||||
}
|
||||
|
||||
/*
|
||||
"write" request on "impulse_period" special file.
|
||||
*/
|
||||
static ssize_t pcm_set_impulse_period(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
dev2pcm(dev)->impulse_period = simple_strtoul(buf, NULL, 10);
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(impulse_volume, S_IWUGO | S_IRUGO, pcm_get_impulse_volume, pcm_set_impulse_volume);
|
||||
static DEVICE_ATTR(impulse_period, S_IWUGO | S_IRUGO, pcm_get_impulse_period, pcm_set_impulse_period);
|
||||
|
||||
#endif
|
||||
|
||||
int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels)
|
||||
{
|
||||
unsigned long flags_old = __sync_fetch_and_or(&line6pcm->flags, channels);
|
||||
unsigned long flags_new = flags_old | channels;
|
||||
int err = 0;
|
||||
|
||||
#if LINE6_BACKUP_MONITOR_SIGNAL
|
||||
if (!(line6pcm->line6->properties->capabilities & LINE6_BIT_HWMON)) {
|
||||
line6pcm->prev_fbuf = kmalloc(LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL);
|
||||
|
||||
if (!line6pcm->prev_fbuf) {
|
||||
dev_err(line6pcm->line6->ifcdev, "cannot malloc monitor buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
#else
|
||||
line6pcm->prev_fbuf = NULL;
|
||||
#endif
|
||||
|
||||
if (((flags_old & MASK_CAPTURE) == 0) &&
|
||||
((flags_new & MASK_CAPTURE) != 0)) {
|
||||
/*
|
||||
Waiting for completion of active URBs in the stop handler is
|
||||
a bug, we therefore report an error if capturing is restarted
|
||||
too soon.
|
||||
*/
|
||||
if(line6pcm->active_urb_in | line6pcm->unlink_urb_in)
|
||||
return -EBUSY;
|
||||
|
||||
line6pcm->buffer_in = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL);
|
||||
|
||||
if (!line6pcm->buffer_in) {
|
||||
dev_err(line6pcm->line6->ifcdev, "cannot malloc capture buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
line6pcm->count_in = 0;
|
||||
line6pcm->prev_fsize = 0;
|
||||
err = line6_submit_audio_in_all_urbs(line6pcm);
|
||||
|
||||
if (err < 0) {
|
||||
__sync_fetch_and_and(&line6pcm->flags, ~channels);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (((flags_old & MASK_PLAYBACK) == 0) &&
|
||||
((flags_new & MASK_PLAYBACK) != 0)) {
|
||||
/*
|
||||
See comment above regarding PCM restart.
|
||||
*/
|
||||
if(line6pcm->active_urb_out | line6pcm->unlink_urb_out)
|
||||
return -EBUSY;
|
||||
|
||||
line6pcm->buffer_out = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL);
|
||||
|
||||
if (!line6pcm->buffer_out) {
|
||||
dev_err(line6pcm->line6->ifcdev, "cannot malloc playback buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
line6pcm->count_out = 0;
|
||||
err = line6_submit_audio_out_all_urbs(line6pcm);
|
||||
|
||||
if (err < 0) {
|
||||
__sync_fetch_and_and(&line6pcm->flags, ~channels);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels)
|
||||
{
|
||||
unsigned long flags_old = __sync_fetch_and_and(&line6pcm->flags, ~channels);
|
||||
unsigned long flags_new = flags_old & ~channels;
|
||||
|
||||
if (((flags_old & MASK_CAPTURE) != 0) &&
|
||||
((flags_new & MASK_CAPTURE) == 0)) {
|
||||
line6_unlink_audio_in_urbs(line6pcm);
|
||||
kfree(line6pcm->buffer_in);
|
||||
line6pcm->buffer_in = NULL;
|
||||
}
|
||||
|
||||
if (((flags_old & MASK_PLAYBACK) != 0) &&
|
||||
((flags_new & MASK_PLAYBACK) == 0)) {
|
||||
line6_unlink_audio_out_urbs(line6pcm);
|
||||
kfree(line6pcm->buffer_out);
|
||||
line6pcm->buffer_out = NULL;
|
||||
}
|
||||
|
||||
#if LINE6_BACKUP_MONITOR_SIGNAL
|
||||
if (line6pcm->prev_fbuf != NULL)
|
||||
kfree(line6pcm->prev_fbuf);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* trigger callback */
|
||||
int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
@@ -38,7 +201,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
switch (s->stream) {
|
||||
case SNDRV_PCM_STREAM_PLAYBACK:
|
||||
err = snd_line6_playback_trigger(s, cmd);
|
||||
err = snd_line6_playback_trigger(line6pcm, cmd);
|
||||
|
||||
if (err < 0) {
|
||||
spin_unlock_irqrestore(&line6pcm->lock_trigger,
|
||||
@@ -49,7 +212,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_STREAM_CAPTURE:
|
||||
err = snd_line6_capture_trigger(s, cmd);
|
||||
err = snd_line6_capture_trigger(line6pcm, cmd);
|
||||
|
||||
if (err < 0) {
|
||||
spin_unlock_irqrestore(&line6pcm->lock_trigger,
|
||||
@@ -60,7 +223,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(s2m(substream), "Unknown stream direction %d\n",
|
||||
dev_err(line6pcm->line6->ifcdev, "Unknown stream direction %d\n",
|
||||
s->stream);
|
||||
}
|
||||
}
|
||||
@@ -70,8 +233,8 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
}
|
||||
|
||||
/* control info callback */
|
||||
static int snd_line6_control_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 2;
|
||||
@@ -81,28 +244,28 @@ static int snd_line6_control_info(struct snd_kcontrol *kcontrol,
|
||||
}
|
||||
|
||||
/* control get callback */
|
||||
static int snd_line6_control_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int i;
|
||||
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
for (i = 2; i--;)
|
||||
ucontrol->value.integer.value[i] = line6pcm->volume[i];
|
||||
ucontrol->value.integer.value[i] = line6pcm->volume_playback[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* control put callback */
|
||||
static int snd_line6_control_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int i, changed = 0;
|
||||
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
for (i = 2; i--;)
|
||||
if (line6pcm->volume[i] != ucontrol->value.integer.value[i]) {
|
||||
line6pcm->volume[i] = ucontrol->value.integer.value[i];
|
||||
if (line6pcm->volume_playback[i] != ucontrol->value.integer.value[i]) {
|
||||
line6pcm->volume_playback[i] = ucontrol->value.integer.value[i];
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
@@ -110,14 +273,14 @@ static int snd_line6_control_put(struct snd_kcontrol *kcontrol,
|
||||
}
|
||||
|
||||
/* control definition */
|
||||
static struct snd_kcontrol_new line6_control = {
|
||||
static struct snd_kcontrol_new line6_control_playback = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "PCM Playback Volume",
|
||||
.index = 0,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = snd_line6_control_info,
|
||||
.get = snd_line6_control_get,
|
||||
.put = snd_line6_control_put
|
||||
.info = snd_line6_control_playback_info,
|
||||
.get = snd_line6_control_playback_get,
|
||||
.put = snd_line6_control_playback_put
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -128,6 +291,11 @@ static void line6_cleanup_pcm(struct snd_pcm *pcm)
|
||||
int i;
|
||||
struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm);
|
||||
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_volume);
|
||||
device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_period);
|
||||
#endif
|
||||
|
||||
for (i = LINE6_ISO_BUFFERS; i--;) {
|
||||
if (line6pcm->urb_audio_out[i]) {
|
||||
usb_kill_urb(line6pcm->urb_audio_out[i]);
|
||||
@@ -160,7 +328,8 @@ static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm)
|
||||
/* set operators */
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
&snd_line6_playback_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
|
||||
&snd_line6_capture_ops);
|
||||
|
||||
/* pre-allocation of buffers */
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
@@ -176,6 +345,27 @@ static int snd_line6_pcm_free(struct snd_device *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Stop substream if still running.
|
||||
*/
|
||||
static void pcm_disconnect_substream(struct snd_pcm_substream *substream)
|
||||
{
|
||||
if(substream->runtime && snd_pcm_running(substream)) {
|
||||
snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Stop PCM stream.
|
||||
*/
|
||||
void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
pcm_disconnect_substream(get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE));
|
||||
pcm_disconnect_substream(get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK));
|
||||
line6_unlink_wait_clear_audio_out_urbs(line6pcm);
|
||||
line6_unlink_wait_clear_audio_in_urbs(line6pcm);
|
||||
}
|
||||
|
||||
/*
|
||||
Create and register the PCM device and mixer entries.
|
||||
Create URBs for playback and capture.
|
||||
@@ -218,20 +408,23 @@ int line6_init_pcm(struct usb_line6 *line6,
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_GUITARPORT:
|
||||
case LINE6_DEVID_PODSTUDIO_GX:
|
||||
case LINE6_DEVID_PODSTUDIO_UX1:
|
||||
case LINE6_DEVID_PODSTUDIO_UX2:
|
||||
case LINE6_DEVID_TONEPORT_GX:
|
||||
case LINE6_DEVID_TONEPORT_UX1:
|
||||
case LINE6_DEVID_TONEPORT_UX2:
|
||||
ep_read = 0x82;
|
||||
ep_write = 0x01;
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_TONEPORT_UX1:
|
||||
ep_read = 0x00;
|
||||
ep_write = 0x00;
|
||||
break;
|
||||
|
||||
/* this is for interface_number == 1:
|
||||
case LINE6_DEVID_TONEPORT_UX2:
|
||||
case LINE6_DEVID_PODSTUDIO_UX2:
|
||||
ep_read = 0x87;
|
||||
ep_write = 0x00;
|
||||
break;
|
||||
*/
|
||||
|
||||
default:
|
||||
MISSING_CASE;
|
||||
@@ -242,12 +435,13 @@ int line6_init_pcm(struct usb_line6 *line6,
|
||||
if (line6pcm == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
line6pcm->volume[0] = line6pcm->volume[1] = 128;
|
||||
line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255;
|
||||
line6pcm->volume_monitor = 255;
|
||||
line6pcm->line6 = line6;
|
||||
line6pcm->ep_audio_read = ep_read;
|
||||
line6pcm->ep_audio_write = ep_write;
|
||||
line6pcm->max_packet_size = usb_maxpacket(line6->usbdev,
|
||||
usb_rcvintpipe(line6->usbdev,
|
||||
usb_rcvintpipe(line6->usbdev,
|
||||
ep_read),
|
||||
0);
|
||||
line6pcm->properties = properties;
|
||||
@@ -268,19 +462,32 @@ int line6_init_pcm(struct usb_line6 *line6,
|
||||
spin_lock_init(&line6pcm->lock_audio_in);
|
||||
spin_lock_init(&line6pcm->lock_trigger);
|
||||
|
||||
err = create_audio_out_urbs(line6pcm);
|
||||
err = line6_create_audio_out_urbs(line6pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = create_audio_in_urbs(line6pcm);
|
||||
err = line6_create_audio_in_urbs(line6pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* mixer: */
|
||||
err = snd_ctl_add(line6->card, snd_ctl_new1(&line6_control, line6pcm));
|
||||
err = snd_ctl_add(line6->card, snd_ctl_new1(&line6_control_playback, line6pcm));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
/* impulse response test: */
|
||||
err = device_create_file(line6->ifcdev, &dev_attr_impulse_volume);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = device_create_file(line6->ifcdev, &dev_attr_impulse_period);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -290,12 +497,11 @@ int snd_line6_prepare(struct snd_pcm_substream *substream)
|
||||
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
|
||||
|
||||
if (!test_and_set_bit(BIT_PREPARED, &line6pcm->flags)) {
|
||||
unlink_wait_clear_audio_out_urbs(line6pcm);
|
||||
line6pcm->count_out = 0;
|
||||
line6pcm->pos_out = 0;
|
||||
line6pcm->pos_out_done = 0;
|
||||
|
||||
unlink_wait_clear_audio_in_urbs(line6pcm);
|
||||
line6pcm->bytes_out = 0;
|
||||
line6pcm->count_in = 0;
|
||||
line6pcm->pos_in_done = 0;
|
||||
line6pcm->bytes_in = 0;
|
||||
}
|
||||
|
||||
+119
-16
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -24,30 +24,79 @@
|
||||
|
||||
|
||||
/* number of URBs */
|
||||
#define LINE6_ISO_BUFFERS 8
|
||||
#define LINE6_ISO_BUFFERS 2
|
||||
|
||||
/* number of USB frames per URB */
|
||||
#define LINE6_ISO_PACKETS 2
|
||||
/*
|
||||
number of USB frames per URB
|
||||
The Line6 Windows driver always transmits two frames per packet, but
|
||||
the Linux driver performs significantly better (i.e., lower latency)
|
||||
with only one frame per packet.
|
||||
*/
|
||||
#define LINE6_ISO_PACKETS 1
|
||||
|
||||
/* in a "full speed" device (such as the PODxt Pro) this means 1ms */
|
||||
#define LINE6_ISO_INTERVAL 1
|
||||
|
||||
/* this should be queried dynamically from the USB interface! */
|
||||
#define LINE6_ISO_PACKET_SIZE_MAX 252
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
#define LINE6_IMPULSE_DEFAULT_PERIOD 100
|
||||
#endif
|
||||
|
||||
#define LINE6_BACKUP_MONITOR_SIGNAL 0
|
||||
#define LINE6_REUSE_DMA_AREA_FOR_PLAYBACK 0
|
||||
|
||||
|
||||
/*
|
||||
Extract the messaging device from the substream instance
|
||||
Get substream from Line6 PCM data structure
|
||||
*/
|
||||
#define s2m(s) (((struct snd_line6_pcm *) \
|
||||
snd_pcm_substream_chip(s))->line6->ifcdev)
|
||||
#define get_substream(line6pcm, stream) (line6pcm->pcm->streams[stream].substream)
|
||||
|
||||
|
||||
/*
|
||||
PCM mode bits and masks.
|
||||
"ALSA": operations triggered by applications via ALSA
|
||||
"MONITOR": software monitoring
|
||||
"IMPULSE": optional impulse response operation
|
||||
*/
|
||||
enum {
|
||||
BIT_RUNNING_PLAYBACK,
|
||||
BIT_RUNNING_CAPTURE,
|
||||
/* individual bits: */
|
||||
BIT_PCM_ALSA_PLAYBACK,
|
||||
BIT_PCM_ALSA_CAPTURE,
|
||||
BIT_PCM_MONITOR_PLAYBACK,
|
||||
BIT_PCM_MONITOR_CAPTURE,
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
BIT_PCM_IMPULSE_PLAYBACK,
|
||||
BIT_PCM_IMPULSE_CAPTURE,
|
||||
#endif
|
||||
BIT_PAUSE_PLAYBACK,
|
||||
BIT_PREPARED
|
||||
BIT_PREPARED,
|
||||
|
||||
/* individual masks: */
|
||||
MASK_PCM_ALSA_PLAYBACK = 1 << BIT_PCM_ALSA_PLAYBACK,
|
||||
MASK_PCM_ALSA_CAPTURE = 1 << BIT_PCM_ALSA_CAPTURE,
|
||||
MASK_PCM_MONITOR_PLAYBACK = 1 << BIT_PCM_MONITOR_PLAYBACK,
|
||||
MASK_PCM_MONITOR_CAPTURE = 1 << BIT_PCM_MONITOR_CAPTURE,
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
MASK_PCM_IMPULSE_PLAYBACK = 1 << BIT_PCM_IMPULSE_PLAYBACK,
|
||||
MASK_PCM_IMPULSE_CAPTURE = 1 << BIT_PCM_IMPULSE_CAPTURE,
|
||||
#endif
|
||||
MASK_PAUSE_PLAYBACK = 1 << BIT_PAUSE_PLAYBACK,
|
||||
MASK_PREPARED = 1 << BIT_PREPARED,
|
||||
|
||||
/* combined masks (by operation): */
|
||||
MASK_PCM_ALSA = MASK_PCM_ALSA_PLAYBACK | MASK_PCM_ALSA_CAPTURE,
|
||||
MASK_PCM_MONITOR = MASK_PCM_MONITOR_PLAYBACK | MASK_PCM_MONITOR_CAPTURE,
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
MASK_PCM_IMPULSE = MASK_PCM_IMPULSE_PLAYBACK | MASK_PCM_IMPULSE_CAPTURE,
|
||||
#endif
|
||||
|
||||
/* combined masks (by direction): */
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
MASK_PLAYBACK = MASK_PCM_ALSA_PLAYBACK | MASK_PCM_MONITOR_PLAYBACK | MASK_PCM_IMPULSE_PLAYBACK,
|
||||
MASK_CAPTURE = MASK_PCM_ALSA_CAPTURE | MASK_PCM_MONITOR_CAPTURE | MASK_PCM_IMPULSE_CAPTURE
|
||||
#else
|
||||
MASK_PLAYBACK = MASK_PCM_ALSA_PLAYBACK | MASK_PCM_MONITOR_PLAYBACK,
|
||||
MASK_CAPTURE = MASK_PCM_ALSA_CAPTURE | MASK_PCM_MONITOR_CAPTURE
|
||||
#endif
|
||||
};
|
||||
|
||||
struct line6_pcm_properties {
|
||||
@@ -83,9 +132,11 @@ struct snd_line6_pcm {
|
||||
struct urb *urb_audio_in[LINE6_ISO_BUFFERS];
|
||||
|
||||
/**
|
||||
Temporary buffer to hold data when playback buffer wraps.
|
||||
Temporary buffer for playback.
|
||||
Since the packet size is not known in advance, this buffer is
|
||||
large enough to store maximum size packets.
|
||||
*/
|
||||
unsigned char *wrap_out;
|
||||
unsigned char *buffer_out;
|
||||
|
||||
/**
|
||||
Temporary buffer for capture.
|
||||
@@ -94,6 +145,21 @@ struct snd_line6_pcm {
|
||||
*/
|
||||
unsigned char *buffer_in;
|
||||
|
||||
/**
|
||||
Temporary buffer index for playback.
|
||||
*/
|
||||
int index_out;
|
||||
|
||||
/**
|
||||
Previously captured frame (for software monitoring).
|
||||
*/
|
||||
unsigned char *prev_fbuf;
|
||||
|
||||
/**
|
||||
Size of previously captured frame (for software monitoring).
|
||||
*/
|
||||
int prev_fsize;
|
||||
|
||||
/**
|
||||
Free frame position in the playback buffer.
|
||||
*/
|
||||
@@ -204,12 +270,36 @@ struct snd_line6_pcm {
|
||||
/**
|
||||
PCM playback volume (left and right).
|
||||
*/
|
||||
int volume[2];
|
||||
int volume_playback[2];
|
||||
|
||||
/**
|
||||
PCM monitor volume.
|
||||
*/
|
||||
int volume_monitor;
|
||||
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
/**
|
||||
Volume of impulse response test signal (if zero, test is disabled).
|
||||
*/
|
||||
int impulse_volume;
|
||||
|
||||
/**
|
||||
Period of impulse response test signal.
|
||||
*/
|
||||
int impulse_period;
|
||||
|
||||
/**
|
||||
Counter for impulse response test signal.
|
||||
*/
|
||||
int impulse_count;
|
||||
#endif
|
||||
|
||||
/**
|
||||
Several status bits (see BIT_*).
|
||||
*/
|
||||
unsigned long flags;
|
||||
|
||||
int last_frame_in, last_frame_out;
|
||||
};
|
||||
|
||||
|
||||
@@ -217,6 +307,19 @@ extern int line6_init_pcm(struct usb_line6 *line6,
|
||||
struct line6_pcm_properties *properties);
|
||||
extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd);
|
||||
extern int snd_line6_prepare(struct snd_pcm_substream *substream);
|
||||
extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm);
|
||||
extern int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels);
|
||||
extern int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels);
|
||||
|
||||
|
||||
#define PRINT_FRAME_DIFF(op) { \
|
||||
static int diff_prev = 1000; \
|
||||
int diff = line6pcm->last_frame_out - line6pcm->last_frame_in; \
|
||||
if((diff != diff_prev) && (abs(diff) < 100)) { \
|
||||
printk("%s frame diff = %d\n", op, diff); \
|
||||
diff_prev = diff; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -9,15 +9,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "capture.h"
|
||||
#include "driver.h"
|
||||
#include "pcm.h"
|
||||
#include "pod.h"
|
||||
#include "playback.h"
|
||||
@@ -59,22 +57,85 @@ static void change_volume(struct urb *urb_out, int volume[],
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
|
||||
/*
|
||||
Create signal for impulse response test.
|
||||
*/
|
||||
static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm,
|
||||
struct urb *urb_out, int bytes_per_frame)
|
||||
{
|
||||
int frames = urb_out->transfer_buffer_length / bytes_per_frame;
|
||||
|
||||
if (bytes_per_frame == 4) {
|
||||
/* TODO: add code for TonePort etc. */
|
||||
} else if (bytes_per_frame == 6) {
|
||||
int i, j;
|
||||
unsigned char *pi = line6pcm->prev_fbuf;
|
||||
unsigned char *po = urb_out->transfer_buffer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (j = 0; j < bytes_per_frame / 2; ++j)
|
||||
po[j] = pi[j];
|
||||
|
||||
for (; j < bytes_per_frame; ++j)
|
||||
po[j] = 0;
|
||||
|
||||
pi += bytes_per_frame;
|
||||
po += bytes_per_frame;
|
||||
}
|
||||
|
||||
if (--line6pcm->impulse_count <= 0) {
|
||||
((unsigned char *)(urb_out->
|
||||
transfer_buffer))[bytes_per_frame -
|
||||
1] =
|
||||
line6pcm->impulse_volume;
|
||||
line6pcm->impulse_count = line6pcm->impulse_period;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
Add signal to buffer for software monitoring.
|
||||
*/
|
||||
static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
|
||||
int volume, int bytes_per_frame)
|
||||
{
|
||||
if (volume == 0)
|
||||
return; /* zero volume - no change */
|
||||
|
||||
if (bytes_per_frame == 4) {
|
||||
short *pi, *po, *buf_end;
|
||||
pi = (short *)signal;
|
||||
po = (short *)urb_out->transfer_buffer;
|
||||
buf_end = po + urb_out->transfer_buffer_length / sizeof(*po);
|
||||
|
||||
for (; po < buf_end; ++pi, ++po)
|
||||
*po += (*pi * volume) >> 8;
|
||||
}
|
||||
|
||||
/*
|
||||
We don't need to handle devices with 6 bytes per frame here
|
||||
since they all support hardware monitoring.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
Find a free URB, prepare audio data, and submit URB.
|
||||
*/
|
||||
static int submit_audio_out_urb(struct snd_pcm_substream *substream)
|
||||
static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
int index;
|
||||
unsigned long flags;
|
||||
int i, urb_size, urb_frames;
|
||||
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
|
||||
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
|
||||
const int frame_increment =
|
||||
line6pcm->properties->snd_line6_rates.rats[0].num_min;
|
||||
const int frame_factor =
|
||||
line6pcm->properties->snd_line6_rates.rats[0].den *
|
||||
(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct urb *urb_out;
|
||||
|
||||
spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
|
||||
@@ -83,7 +144,7 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
|
||||
|
||||
if (index < 0 || index >= LINE6_ISO_BUFFERS) {
|
||||
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
|
||||
dev_err(s2m(substream), "no free URB found\n");
|
||||
dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -92,24 +153,49 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
|
||||
|
||||
for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
|
||||
/* compute frame size for given sampling rate */
|
||||
int n, fs;
|
||||
int fsize = 0;
|
||||
struct usb_iso_packet_descriptor *fout =
|
||||
&urb_out->iso_frame_desc[i];
|
||||
line6pcm->count_out += frame_increment;
|
||||
n = line6pcm->count_out / frame_factor;
|
||||
line6pcm->count_out -= n * frame_factor;
|
||||
fs = n * bytes_per_frame;
|
||||
|
||||
if (line6pcm->flags & MASK_CAPTURE) {
|
||||
fsize = line6pcm->prev_fsize;
|
||||
}
|
||||
|
||||
if (fsize == 0) {
|
||||
int n;
|
||||
line6pcm->count_out += frame_increment;
|
||||
n = line6pcm->count_out / frame_factor;
|
||||
line6pcm->count_out -= n * frame_factor;
|
||||
fsize = n * bytes_per_frame;
|
||||
}
|
||||
|
||||
fout->offset = urb_size;
|
||||
fout->length = fs;
|
||||
urb_size += fs;
|
||||
fout->length = fsize;
|
||||
urb_size += fsize;
|
||||
}
|
||||
|
||||
if (urb_size == 0) {
|
||||
/* can't determine URB size */
|
||||
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
|
||||
dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); /* this is somewhat paranoid */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
urb_frames = urb_size / bytes_per_frame;
|
||||
urb_out->transfer_buffer =
|
||||
line6pcm->buffer_out +
|
||||
line6pcm->max_packet_size * line6pcm->index_out;
|
||||
urb_out->transfer_buffer_length = urb_size;
|
||||
urb_out->context = line6pcm;
|
||||
|
||||
if (++line6pcm->index_out == LINE6_ISO_BUFFERS)
|
||||
line6pcm->index_out = 0;
|
||||
|
||||
if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags) &&
|
||||
!test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) {
|
||||
struct snd_pcm_runtime *runtime =
|
||||
get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime;
|
||||
|
||||
if (test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) {
|
||||
urb_out->transfer_buffer = line6pcm->wrap_out;
|
||||
memset(line6pcm->wrap_out, 0, urb_size);
|
||||
} else {
|
||||
if (line6pcm->pos_out + urb_frames > runtime->buffer_size) {
|
||||
/*
|
||||
The transferred area goes over buffer boundary,
|
||||
@@ -117,38 +203,65 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
|
||||
*/
|
||||
int len;
|
||||
len = runtime->buffer_size - line6pcm->pos_out;
|
||||
urb_out->transfer_buffer = line6pcm->wrap_out;
|
||||
|
||||
if (len > 0) {
|
||||
memcpy(line6pcm->wrap_out,
|
||||
memcpy(urb_out->transfer_buffer,
|
||||
runtime->dma_area +
|
||||
line6pcm->pos_out * bytes_per_frame,
|
||||
len * bytes_per_frame);
|
||||
memcpy(line6pcm->wrap_out +
|
||||
memcpy(urb_out->transfer_buffer +
|
||||
len * bytes_per_frame, runtime->dma_area,
|
||||
(urb_frames - len) * bytes_per_frame);
|
||||
} else {
|
||||
/* this is somewhat paranoid */
|
||||
dev_err(s2m(substream),
|
||||
"driver bug: len = %d\n", len);
|
||||
}
|
||||
} else
|
||||
dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", len); /* this is somewhat paranoid */
|
||||
} else {
|
||||
#if LINE6_REUSE_DMA_AREA_FOR_PLAYBACK
|
||||
/* set the buffer pointer */
|
||||
urb_out->transfer_buffer =
|
||||
runtime->dma_area +
|
||||
line6pcm->pos_out * bytes_per_frame;
|
||||
#else
|
||||
/* copy data */
|
||||
memcpy(urb_out->transfer_buffer,
|
||||
runtime->dma_area +
|
||||
line6pcm->pos_out * bytes_per_frame,
|
||||
urb_out->transfer_buffer_length);
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((line6pcm->pos_out += urb_frames) >= runtime->buffer_size)
|
||||
line6pcm->pos_out -= runtime->buffer_size;
|
||||
} else {
|
||||
memset(urb_out->transfer_buffer, 0,
|
||||
urb_out->transfer_buffer_length);
|
||||
}
|
||||
|
||||
line6pcm->pos_out += urb_frames;
|
||||
if (line6pcm->pos_out >= runtime->buffer_size)
|
||||
line6pcm->pos_out -= runtime->buffer_size;
|
||||
change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame);
|
||||
|
||||
urb_out->transfer_buffer_length = urb_size;
|
||||
urb_out->context = substream;
|
||||
change_volume(urb_out, line6pcm->volume, bytes_per_frame);
|
||||
|
||||
#if DO_DUMP_PCM_SEND
|
||||
if (line6pcm->prev_fbuf != 0) {
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
if (line6pcm->flags & MASK_PCM_IMPULSE) {
|
||||
create_impulse_test_signal(line6pcm, urb_out,
|
||||
bytes_per_frame);
|
||||
if (line6pcm->flags & MASK_PCM_ALSA_CAPTURE) {
|
||||
line6_capture_copy(line6pcm, urb_out->transfer_buffer,
|
||||
urb_out->transfer_buffer_length);
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
if (!
|
||||
(line6pcm->line6->properties->
|
||||
capabilities & LINE6_BIT_HWMON)
|
||||
&& (line6pcm->flags & MASK_PLAYBACK)
|
||||
&& (line6pcm->flags & MASK_CAPTURE))
|
||||
add_monitor_signal(urb_out, line6pcm->prev_fbuf,
|
||||
line6pcm->volume_monitor,
|
||||
bytes_per_frame);
|
||||
#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_PCM
|
||||
for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
|
||||
struct usb_iso_packet_descriptor *fout =
|
||||
&urb_out->iso_frame_desc[i];
|
||||
@@ -161,8 +274,8 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
|
||||
if (usb_submit_urb(urb_out, GFP_ATOMIC) == 0)
|
||||
set_bit(index, &line6pcm->active_urb_out);
|
||||
else
|
||||
dev_err(s2m(substream), "URB out #%d submission failed\n",
|
||||
index);
|
||||
dev_err(line6pcm->line6->ifcdev,
|
||||
"URB out #%d submission failed\n", index);
|
||||
|
||||
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
|
||||
return 0;
|
||||
@@ -171,12 +284,12 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream)
|
||||
/*
|
||||
Submit all currently available playback URBs.
|
||||
*/
|
||||
static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream)
|
||||
int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
|
||||
ret = submit_audio_out_urb(substream);
|
||||
ret = submit_audio_out_urb(line6pcm);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
@@ -187,7 +300,7 @@ static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream)
|
||||
/*
|
||||
Unlink all currently active playback URBs.
|
||||
*/
|
||||
static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
|
||||
void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
@@ -202,7 +315,7 @@ static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
|
||||
}
|
||||
|
||||
/*
|
||||
Wait until unlinking of all currently active playback URBs has been finished.
|
||||
Wait until unlinking of all currently active playback URBs has been finished.
|
||||
*/
|
||||
static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
@@ -223,17 +336,14 @@ static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
|
||||
} while (--timeout > 0);
|
||||
if (alive)
|
||||
snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
|
||||
|
||||
line6pcm->active_urb_out = 0;
|
||||
line6pcm->unlink_urb_out = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Unlink all currently active playback URBs, and wait for finishing.
|
||||
*/
|
||||
void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
|
||||
void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
unlink_audio_out_urbs(line6pcm);
|
||||
line6_unlink_audio_out_urbs(line6pcm);
|
||||
wait_clear_audio_out_urbs(line6pcm);
|
||||
}
|
||||
|
||||
@@ -245,10 +355,16 @@ static void audio_out_callback(struct urb *urb)
|
||||
int i, index, length = 0, shutdown = 0;
|
||||
unsigned long flags;
|
||||
|
||||
struct snd_line6_pcm *line6pcm =
|
||||
(struct snd_line6_pcm *)urb->context;
|
||||
struct snd_pcm_substream *substream =
|
||||
(struct snd_pcm_substream *)urb->context;
|
||||
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
|
||||
#if USE_CLEAR_BUFFER_WORKAROUND
|
||||
memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
|
||||
#endif
|
||||
|
||||
line6pcm->last_frame_out = urb->start_frame;
|
||||
|
||||
/* find index of URB */
|
||||
for (index = LINE6_ISO_BUFFERS; index--;)
|
||||
@@ -262,11 +378,15 @@ static void audio_out_callback(struct urb *urb)
|
||||
length += urb->iso_frame_desc[i].length;
|
||||
|
||||
spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
|
||||
line6pcm->pos_out_done +=
|
||||
length / line6pcm->properties->bytes_per_frame;
|
||||
|
||||
if (line6pcm->pos_out_done >= runtime->buffer_size)
|
||||
line6pcm->pos_out_done -= runtime->buffer_size;
|
||||
if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags)) {
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
line6pcm->pos_out_done +=
|
||||
length / line6pcm->properties->bytes_per_frame;
|
||||
|
||||
if (line6pcm->pos_out_done >= runtime->buffer_size)
|
||||
line6pcm->pos_out_done -= runtime->buffer_size;
|
||||
}
|
||||
|
||||
clear_bit(index, &line6pcm->active_urb_out);
|
||||
|
||||
@@ -276,18 +396,20 @@ static void audio_out_callback(struct urb *urb)
|
||||
break;
|
||||
}
|
||||
|
||||
if (test_bit(index, &line6pcm->unlink_urb_out))
|
||||
if (test_and_clear_bit(index, &line6pcm->unlink_urb_out))
|
||||
shutdown = 1;
|
||||
|
||||
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
|
||||
|
||||
if (!shutdown) {
|
||||
submit_audio_out_urb(substream);
|
||||
submit_audio_out_urb(line6pcm);
|
||||
|
||||
line6pcm->bytes_out += length;
|
||||
if (line6pcm->bytes_out >= line6pcm->period_out) {
|
||||
line6pcm->bytes_out -= line6pcm->period_out;
|
||||
snd_pcm_period_elapsed(substream);
|
||||
if (test_bit(BIT_PCM_ALSA_PLAYBACK, &line6pcm->flags)) {
|
||||
if ((line6pcm->bytes_out +=
|
||||
length) >= line6pcm->period_out) {
|
||||
line6pcm->bytes_out %= line6pcm->period_out;
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -340,52 +462,40 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
|
||||
line6pcm->period_out = params_period_bytes(hw_params);
|
||||
line6pcm->wrap_out = kmalloc(2 * LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL);
|
||||
|
||||
if (!line6pcm->wrap_out) {
|
||||
dev_err(s2m(substream), "cannot malloc wrap_out\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* hw_free playback callback */
|
||||
static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
|
||||
unlink_wait_clear_audio_out_urbs(line6pcm);
|
||||
|
||||
kfree(line6pcm->wrap_out);
|
||||
line6pcm->wrap_out = NULL;
|
||||
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
/* trigger playback callback */
|
||||
int snd_line6_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd)
|
||||
{
|
||||
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
|
||||
int err;
|
||||
line6pcm->count_out = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
if (!test_and_set_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) {
|
||||
err = submit_audio_out_all_urbs(substream);
|
||||
#ifdef CONFIG_PM
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
#endif
|
||||
err = line6_pcm_start(line6pcm, MASK_PCM_ALSA_PLAYBACK);
|
||||
|
||||
if (err < 0) {
|
||||
clear_bit(BIT_RUNNING_PLAYBACK,
|
||||
&line6pcm->flags);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
if (test_and_clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags))
|
||||
unlink_audio_out_urbs(line6pcm);
|
||||
#ifdef CONFIG_PM
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
#endif
|
||||
err = line6_pcm_stop(line6pcm, MASK_PCM_ALSA_PLAYBACK);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
break;
|
||||
|
||||
@@ -414,17 +524,17 @@ snd_line6_playback_pointer(struct snd_pcm_substream *substream)
|
||||
|
||||
/* playback operators */
|
||||
struct snd_pcm_ops snd_line6_playback_ops = {
|
||||
.open = snd_line6_playback_open,
|
||||
.close = snd_line6_playback_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_line6_playback_hw_params,
|
||||
.hw_free = snd_line6_playback_hw_free,
|
||||
.prepare = snd_line6_prepare,
|
||||
.trigger = snd_line6_trigger,
|
||||
.pointer = snd_line6_playback_pointer,
|
||||
.open = snd_line6_playback_open,
|
||||
.close = snd_line6_playback_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_line6_playback_hw_params,
|
||||
.hw_free = snd_line6_playback_hw_free,
|
||||
.prepare = snd_line6_prepare,
|
||||
.trigger = snd_line6_trigger,
|
||||
.pointer = snd_line6_playback_pointer,
|
||||
};
|
||||
|
||||
int create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
|
||||
int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -13,18 +13,29 @@
|
||||
#define PLAYBACK_H
|
||||
|
||||
|
||||
#include <sound/pcm.h>
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <sound/pcm.h>
|
||||
|
||||
/*
|
||||
When the TonePort is used with jack in full duplex mode and the outputs are
|
||||
not connected, the software monitor produces an ugly noise since everything
|
||||
written to the output buffer (i.e., the input signal) will be repeated in the
|
||||
next period (sounds like a delay effect). As a workaround, the output buffer
|
||||
is cleared after the data have been read, but there must be a better
|
||||
solution. Until one is found, this workaround can be used to fix the problem.
|
||||
*/
|
||||
#define USE_CLEAR_BUFFER_WORKAROUND 1
|
||||
|
||||
|
||||
extern struct snd_pcm_ops snd_line6_playback_ops;
|
||||
|
||||
|
||||
extern int create_audio_out_urbs(struct snd_line6_pcm *line6pcm);
|
||||
extern int snd_line6_playback_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd);
|
||||
extern void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm);
|
||||
|
||||
extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm);
|
||||
extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm);
|
||||
extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm);
|
||||
extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm
|
||||
*line6pcm);
|
||||
extern int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd);
|
||||
|
||||
#endif
|
||||
|
||||
+272
-204
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user