mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 06:31:52 +00: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:
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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@ -9,8 +9,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
@ -19,6 +17,7 @@
|
||||
#include "audio.h"
|
||||
#include "capture.h"
|
||||
#include "control.h"
|
||||
#include "driver.h"
|
||||
#include "midi.h"
|
||||
#include "playback.h"
|
||||
#include "pod.h"
|
||||
@ -30,7 +29,7 @@
|
||||
|
||||
#define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>"
|
||||
#define DRIVER_DESC "Line6 USB Driver"
|
||||
#define DRIVER_VERSION "0.8.0"
|
||||
#define DRIVER_VERSION "0.9.0"
|
||||
|
||||
|
||||
/* table of devices that work with this driver */
|
||||
@ -40,6 +39,9 @@ static const struct usb_device_id line6_id_table[] = {
|
||||
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTPRO) },
|
||||
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_GUITARPORT) },
|
||||
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_POCKETPOD) },
|
||||
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_GX) },
|
||||
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX1) },
|
||||
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX2) },
|
||||
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3) },
|
||||
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3LIVE) },
|
||||
{ USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT) },
|
||||
@ -54,30 +56,48 @@ static const struct usb_device_id line6_id_table[] = {
|
||||
MODULE_DEVICE_TABLE(usb, line6_id_table);
|
||||
|
||||
static struct line6_properties line6_properties_table[] = {
|
||||
{ "BassPODxt", LINE6_BIT_BASSPODXT, LINE6_BIT_CONTROL_PCM },
|
||||
{ "BassPODxt Live", LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM },
|
||||
{ "BassPODxt Pro", LINE6_BIT_BASSPODXTPRO, LINE6_BIT_CONTROL_PCM },
|
||||
{ "GuitarPort", LINE6_BIT_GUITARPORT, LINE6_BIT_PCM },
|
||||
{ "Pocket POD", LINE6_BIT_POCKETPOD, LINE6_BIT_CONTROL_PCM },
|
||||
{ "POD X3", LINE6_BIT_PODX3, LINE6_BIT_PCM },
|
||||
{ "POD X3 Live", LINE6_BIT_PODX3LIVE, LINE6_BIT_PCM },
|
||||
{ "PODxt", LINE6_BIT_PODXT, LINE6_BIT_CONTROL_PCM },
|
||||
{ "PODxt Live", LINE6_BIT_PODXTLIVE, LINE6_BIT_CONTROL_PCM },
|
||||
{ "PODxt Pro", LINE6_BIT_PODXTPRO, LINE6_BIT_CONTROL_PCM },
|
||||
{ "TonePort GX", LINE6_BIT_TONEPORT_GX, LINE6_BIT_PCM },
|
||||
{ "TonePort UX1", LINE6_BIT_TONEPORT_UX1, LINE6_BIT_PCM },
|
||||
{ "TonePort UX2", LINE6_BIT_TONEPORT_UX2, LINE6_BIT_PCM },
|
||||
{ "Variax Workbench", LINE6_BIT_VARIAX, LINE6_BIT_CONTROL }
|
||||
{ "BassPODxt", "BassPODxt", LINE6_BIT_BASSPODXT, LINE6_BIT_CONTROL_PCM_HWMON },
|
||||
{ "BassPODxtLive", "BassPODxt Live", LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM_HWMON },
|
||||
{ "BassPODxtPro", "BassPODxt Pro", LINE6_BIT_BASSPODXTPRO, LINE6_BIT_CONTROL_PCM_HWMON },
|
||||
{ "GuitarPort", "GuitarPort", LINE6_BIT_GUITARPORT, LINE6_BIT_PCM },
|
||||
{ "PocketPOD", "Pocket POD", LINE6_BIT_POCKETPOD, LINE6_BIT_CONTROL },
|
||||
{ "PODStudioGX", "POD Studio GX", LINE6_BIT_PODSTUDIO_GX, LINE6_BIT_PCM },
|
||||
{ "PODStudioUX1", "POD Studio UX1", LINE6_BIT_PODSTUDIO_UX1, LINE6_BIT_PCM },
|
||||
{ "PODStudioUX2", "POD Studio UX2", LINE6_BIT_PODSTUDIO_UX2, LINE6_BIT_PCM },
|
||||
{ "PODX3", "POD X3", LINE6_BIT_PODX3, LINE6_BIT_PCM },
|
||||
{ "PODX3Live", "POD X3 Live", LINE6_BIT_PODX3LIVE, LINE6_BIT_PCM },
|
||||
{ "PODxt", "PODxt", LINE6_BIT_PODXT, LINE6_BIT_CONTROL_PCM_HWMON },
|
||||
{ "PODxtLive", "PODxt Live", LINE6_BIT_PODXTLIVE, LINE6_BIT_CONTROL_PCM_HWMON },
|
||||
{ "PODxtPro", "PODxt Pro", LINE6_BIT_PODXTPRO, LINE6_BIT_CONTROL_PCM_HWMON },
|
||||
{ "TonePortGX", "TonePort GX", LINE6_BIT_TONEPORT_GX, LINE6_BIT_PCM },
|
||||
{ "TonePortUX1", "TonePort UX1", LINE6_BIT_TONEPORT_UX1, LINE6_BIT_PCM },
|
||||
{ "TonePortUX2", "TonePort UX2", LINE6_BIT_TONEPORT_UX2, LINE6_BIT_PCM },
|
||||
{ "Variax", "Variax Workbench", LINE6_BIT_VARIAX, LINE6_BIT_CONTROL }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
This is Line6's MIDI manufacturer ID.
|
||||
*/
|
||||
const unsigned char line6_midi_id[] = { 0x00, 0x01, 0x0c };
|
||||
const unsigned char line6_midi_id[] = {
|
||||
0x00, 0x01, 0x0c
|
||||
};
|
||||
|
||||
/*
|
||||
Code to request version of POD, Variax interface
|
||||
(and maybe other devices).
|
||||
*/
|
||||
static const char line6_request_version0[] = {
|
||||
0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7
|
||||
};
|
||||
|
||||
/*
|
||||
Copy of version request code with GFP_KERNEL flag for use in URB.
|
||||
*/
|
||||
static const char *line6_request_version;
|
||||
|
||||
|
||||
struct usb_line6 *line6_devices[LINE6_MAX_DEVICES];
|
||||
struct workqueue_struct *line6_workqueue;
|
||||
|
||||
|
||||
/**
|
||||
@ -104,15 +124,25 @@ static int line6_send_raw_message_async_part(struct message *msg,
|
||||
*/
|
||||
static int line6_start_listen(struct usb_line6 *line6)
|
||||
{
|
||||
int err;
|
||||
usb_fill_int_urb(line6->urb_listen, line6->usbdev,
|
||||
usb_rcvintpipe(line6->usbdev, line6->ep_control_read),
|
||||
line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
|
||||
line6_data_received, line6, line6->interval);
|
||||
line6->urb_listen->actual_length = 0;
|
||||
return usb_submit_urb(line6->urb_listen, GFP_KERNEL);
|
||||
err = usb_submit_urb(line6->urb_listen, GFP_KERNEL);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if DO_DUMP_ANY
|
||||
/*
|
||||
Stop listening on endpoint.
|
||||
*/
|
||||
static void line6_stop_listen(struct usb_line6 *line6)
|
||||
{
|
||||
usb_kill_urb(line6->urb_listen);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_ANY
|
||||
/*
|
||||
Write hexdump to syslog.
|
||||
*/
|
||||
@ -152,7 +182,7 @@ void line6_write_hexdump(struct usb_line6 *line6, char dir,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DO_DUMP_URB_RECEIVE
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
|
||||
/*
|
||||
Dump URB data to syslog.
|
||||
*/
|
||||
@ -169,19 +199,19 @@ static void line6_dump_urb(struct urb *urb)
|
||||
#endif
|
||||
|
||||
/*
|
||||
Send raw message in pieces of max_packet_size bytes.
|
||||
Send raw message in pieces of wMaxPacketSize bytes.
|
||||
*/
|
||||
int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
|
||||
int size)
|
||||
{
|
||||
int i, done = 0;
|
||||
int actual_size;
|
||||
|
||||
#if DO_DUMP_URB_SEND
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
|
||||
line6_write_hexdump(line6, 'S', buffer, size);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < size; i += actual_size) {
|
||||
for (i = 0; i < size; i += line6->max_packet_size) {
|
||||
int partial;
|
||||
const char *frag_buf = buffer + i;
|
||||
int frag_size = min(line6->max_packet_size, size - i);
|
||||
int retval;
|
||||
@ -190,7 +220,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
|
||||
usb_sndintpipe(line6->usbdev,
|
||||
line6->ep_control_write),
|
||||
(char *)frag_buf, frag_size,
|
||||
&actual_size, LINE6_TIMEOUT * HZ);
|
||||
&partial, LINE6_TIMEOUT * HZ);
|
||||
|
||||
if (retval) {
|
||||
dev_err(line6->ifcdev,
|
||||
@ -198,7 +228,7 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
|
||||
break;
|
||||
}
|
||||
|
||||
done += actual_size;
|
||||
done += frag_size;
|
||||
}
|
||||
|
||||
return done;
|
||||
@ -234,7 +264,7 @@ static int line6_send_raw_message_async_part(struct message *msg,
|
||||
(char *)msg->buffer + done, bytes,
|
||||
line6_async_request_sent, msg, line6->interval);
|
||||
|
||||
#if DO_DUMP_URB_SEND
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
|
||||
line6_write_hexdump(line6, 'S', (char *)msg->buffer + done, bytes);
|
||||
#endif
|
||||
|
||||
@ -252,6 +282,17 @@ static int line6_send_raw_message_async_part(struct message *msg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Setup and start timer.
|
||||
*/
|
||||
void line6_start_timer(struct timer_list *timer, unsigned int msecs,
|
||||
void (*function)(unsigned long), unsigned long data)
|
||||
{
|
||||
setup_timer(timer, function, data);
|
||||
timer->expires = jiffies + msecs * HZ / 1000;
|
||||
add_timer(timer);
|
||||
}
|
||||
|
||||
/*
|
||||
Asynchronously send raw message.
|
||||
*/
|
||||
@ -288,6 +329,14 @@ int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer,
|
||||
return line6_send_raw_message_async_part(msg, urb);
|
||||
}
|
||||
|
||||
/*
|
||||
Send asynchronous device version request.
|
||||
*/
|
||||
int line6_version_request_async(struct usb_line6 *line6)
|
||||
{
|
||||
return line6_send_raw_message_async(line6, line6_request_version, sizeof(line6_request_version0));
|
||||
}
|
||||
|
||||
/*
|
||||
Send sysex message in pieces of wMaxPacketSize bytes.
|
||||
*/
|
||||
@ -297,6 +346,15 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer,
|
||||
return line6_send_raw_message(line6, buffer, size + SYSEX_EXTRA_SIZE) - SYSEX_EXTRA_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
Send sysex message in pieces of wMaxPacketSize bytes.
|
||||
*/
|
||||
int line6_send_sysex_message_async(struct usb_line6 *line6, const char *buffer,
|
||||
int size)
|
||||
{
|
||||
return line6_send_raw_message_async(line6, buffer, size + SYSEX_EXTRA_SIZE) - SYSEX_EXTRA_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
Allocate buffer for sysex message and prepare header.
|
||||
@param code sysex message code
|
||||
@ -305,7 +363,7 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer,
|
||||
char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2,
|
||||
int size)
|
||||
{
|
||||
char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_KERNEL);
|
||||
char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC);
|
||||
|
||||
if (!buffer) {
|
||||
dev_err(line6->ifcdev, "out of memory\n");
|
||||
@ -332,29 +390,29 @@ static void line6_data_received(struct urb *urb)
|
||||
if (urb->status == -ESHUTDOWN)
|
||||
return;
|
||||
|
||||
#if DO_DUMP_URB_RECEIVE
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
|
||||
line6_dump_urb(urb);
|
||||
#endif
|
||||
|
||||
done = midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
|
||||
done = line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
|
||||
|
||||
if (done < urb->actual_length) {
|
||||
midibuf_ignore(mb, done);
|
||||
line6_midibuf_ignore(mb, done);
|
||||
DEBUG_MESSAGES(dev_err(line6->ifcdev, "%d %d buffer overflow - message skipped\n", done, urb->actual_length));
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
done = midibuf_read(mb, line6->buffer_message, LINE6_MESSAGE_MAXLEN);
|
||||
done = line6_midibuf_read(mb, line6->buffer_message, LINE6_MESSAGE_MAXLEN);
|
||||
|
||||
if (done == 0)
|
||||
break;
|
||||
|
||||
/* MIDI input filter */
|
||||
if (midibuf_skip_message(mb, line6->line6midi->midi_mask_receive))
|
||||
if (line6_midibuf_skip_message(mb, line6->line6midi->midi_mask_receive))
|
||||
continue;
|
||||
|
||||
line6->message_length = done;
|
||||
#if DO_DUMP_MIDI_RECEIVE
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_MIDI
|
||||
line6_write_hexdump(line6, 'r', line6->buffer_message, done);
|
||||
#endif
|
||||
line6_midi_receive(line6, line6->buffer_message, done);
|
||||
@ -366,17 +424,17 @@ static void line6_data_received(struct urb *urb)
|
||||
case LINE6_DEVID_PODXT:
|
||||
case LINE6_DEVID_PODXTPRO:
|
||||
case LINE6_DEVID_POCKETPOD:
|
||||
pod_process_message((struct usb_line6_pod *)line6);
|
||||
line6_pod_process_message((struct usb_line6_pod *)line6);
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_PODXTLIVE:
|
||||
switch (line6->interface_number) {
|
||||
case PODXTLIVE_INTERFACE_POD:
|
||||
pod_process_message((struct usb_line6_pod *)line6);
|
||||
line6_pod_process_message((struct usb_line6_pod *)line6);
|
||||
break;
|
||||
|
||||
case PODXTLIVE_INTERFACE_VARIAX:
|
||||
variax_process_message((struct usb_line6_variax *)line6);
|
||||
line6_variax_process_message((struct usb_line6_variax *)line6);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -385,7 +443,7 @@ static void line6_data_received(struct urb *urb)
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_VARIAX:
|
||||
variax_process_message((struct usb_line6_variax *)line6);
|
||||
line6_variax_process_message((struct usb_line6_variax *)line6);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -396,44 +454,17 @@ static void line6_data_received(struct urb *urb)
|
||||
line6_start_listen(line6);
|
||||
}
|
||||
|
||||
static int line6_send(struct usb_line6 *line6, unsigned char *buf, size_t len)
|
||||
{
|
||||
int retval;
|
||||
int partial;
|
||||
|
||||
#if DO_DUMP_URB_SEND
|
||||
line6_write_hexdump(line6, 'S', buf, len);
|
||||
#endif
|
||||
|
||||
retval = usb_interrupt_msg(line6->usbdev,
|
||||
usb_sndintpipe(line6->usbdev,
|
||||
line6->ep_control_write),
|
||||
buf, len, &partial,
|
||||
LINE6_TIMEOUT * HZ);
|
||||
|
||||
if (retval) {
|
||||
dev_err(line6->ifcdev,
|
||||
"usb_interrupt_msg failed (%d)\n", retval);
|
||||
}
|
||||
|
||||
if (partial != len) {
|
||||
dev_err(line6->ifcdev,
|
||||
"usb_interrupt_msg sent partial message (%d)\n",
|
||||
retval);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
Send channel number (i.e., switch to a different sound).
|
||||
*/
|
||||
int line6_send_program(struct usb_line6 *line6, int value)
|
||||
{
|
||||
int retval;
|
||||
unsigned char *buffer;
|
||||
size_t len = 2;
|
||||
int partial;
|
||||
|
||||
buffer = kmalloc(2, GFP_KERNEL);
|
||||
|
||||
buffer = kmalloc(len, GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
dev_err(line6->ifcdev, "out of memory\n");
|
||||
return -ENOMEM;
|
||||
@ -442,7 +473,20 @@ int line6_send_program(struct usb_line6 *line6, int value)
|
||||
buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST;
|
||||
buffer[1] = value;
|
||||
|
||||
return line6_send(line6, buffer, len);
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
|
||||
line6_write_hexdump(line6, 'S', buffer, 2);
|
||||
#endif
|
||||
|
||||
retval = usb_interrupt_msg(line6->usbdev,
|
||||
usb_sndintpipe(line6->usbdev,
|
||||
line6->ep_control_write),
|
||||
buffer, 2, &partial, LINE6_TIMEOUT * HZ);
|
||||
|
||||
if (retval)
|
||||
dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval);
|
||||
|
||||
kfree(buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -450,10 +494,12 @@ int line6_send_program(struct usb_line6 *line6, int value)
|
||||
*/
|
||||
int line6_transmit_parameter(struct usb_line6 *line6, int param, int value)
|
||||
{
|
||||
int retval;
|
||||
unsigned char *buffer;
|
||||
size_t len = 3;
|
||||
int partial;
|
||||
|
||||
buffer = kmalloc(3, GFP_KERNEL);
|
||||
|
||||
buffer = kmalloc(len, GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
dev_err(line6->ifcdev, "out of memory\n");
|
||||
return -ENOMEM;
|
||||
@ -463,7 +509,19 @@ int line6_transmit_parameter(struct usb_line6 *line6, int param, int value)
|
||||
buffer[1] = param;
|
||||
buffer[2] = value;
|
||||
|
||||
return line6_send(line6, buffer, len);
|
||||
#ifdef CONFIG_LINE6_USB_DUMP_CTRL
|
||||
line6_write_hexdump(line6, 'S', buffer, 3);
|
||||
#endif
|
||||
|
||||
retval = usb_interrupt_msg(line6->usbdev,
|
||||
usb_sndintpipe(line6->usbdev, line6->ep_control_write),
|
||||
buffer, 3, &partial, LINE6_TIMEOUT * HZ);
|
||||
|
||||
if (retval)
|
||||
dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval);
|
||||
|
||||
kfree(buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -477,10 +535,9 @@ int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t dat
|
||||
|
||||
/* query the serial number: */
|
||||
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67,
|
||||
USB_TYPE_VENDOR | USB_RECIP_DEVICE
|
||||
| USB_DIR_OUT,
|
||||
(datalen << 8) | 0x21, address,
|
||||
NULL, 0, LINE6_TIMEOUT * HZ);
|
||||
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
|
||||
(datalen << 8) | 0x21, address,
|
||||
NULL, 0, LINE6_TIMEOUT * HZ);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(line6->ifcdev, "read request failed (error %d)\n", ret);
|
||||
@ -599,7 +656,7 @@ ssize_t line6_nop_write(struct device *dev, struct device_attribute *attr,
|
||||
/*
|
||||
"write" request on "raw" special file.
|
||||
*/
|
||||
#if CREATE_RAW_FILE
|
||||
#ifdef CONFIG_LINE6_USB_RAW
|
||||
ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
@ -637,21 +694,6 @@ static void line6_destruct(struct usb_interface *interface)
|
||||
kfree(line6);
|
||||
}
|
||||
|
||||
static void line6_list_devices(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LINE6_MAX_DEVICES; ++i) {
|
||||
struct usb_line6 *dev = line6_devices[i];
|
||||
printk(KERN_INFO "Line6 device %d: ", i);
|
||||
|
||||
if (dev == NULL)
|
||||
printk("(not used)\n");
|
||||
else
|
||||
printk("%s:%d\n", dev->properties->name, dev->interface_number);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Probe USB device.
|
||||
*/
|
||||
@ -674,10 +716,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
|
||||
if (usbdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
/* increment reference counters: */
|
||||
usb_get_intf(interface);
|
||||
usb_get_dev(usbdev);
|
||||
|
||||
/* we don't handle multiple configurations */
|
||||
if (usbdev->descriptor.bNumConfigurations != 1) {
|
||||
ret = -ENODEV;
|
||||
@ -689,8 +727,8 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
|
||||
u16 idVendor = le16_to_cpu(usbdev->descriptor.idVendor);
|
||||
u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct);
|
||||
|
||||
if (idVendor == line6_id_table[devtype].idVendor
|
||||
&& idProduct == line6_id_table[devtype].idProduct)
|
||||
if (idVendor == line6_id_table[devtype].idVendor &&
|
||||
idProduct == line6_id_table[devtype].idProduct)
|
||||
break;
|
||||
}
|
||||
|
||||
@ -719,12 +757,23 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
|
||||
|
||||
switch (product) {
|
||||
case LINE6_DEVID_BASSPODXTLIVE:
|
||||
case LINE6_DEVID_POCKETPOD:
|
||||
case LINE6_DEVID_PODXTLIVE:
|
||||
case LINE6_DEVID_VARIAX:
|
||||
alternate = 1;
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_POCKETPOD:
|
||||
switch (interface_number) {
|
||||
case 0:
|
||||
return 0; /* this interface has no endpoints */
|
||||
case 1:
|
||||
alternate = 0;
|
||||
break;
|
||||
default:
|
||||
MISSING_CASE;
|
||||
}
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_PODX3:
|
||||
case LINE6_DEVID_PODX3LIVE:
|
||||
switch (interface_number) {
|
||||
@ -746,21 +795,27 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
|
||||
alternate = 5;
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_TONEPORT_GX:
|
||||
case LINE6_DEVID_GUITARPORT:
|
||||
case LINE6_DEVID_PODSTUDIO_GX:
|
||||
case LINE6_DEVID_PODSTUDIO_UX1:
|
||||
case LINE6_DEVID_TONEPORT_GX:
|
||||
case LINE6_DEVID_TONEPORT_UX1:
|
||||
alternate = 2; /* 1..4 seem to be ok */
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_TONEPORT_UX1:
|
||||
case LINE6_DEVID_TONEPORT_UX2:
|
||||
case LINE6_DEVID_PODSTUDIO_UX2:
|
||||
switch (interface_number) {
|
||||
case 0:
|
||||
/* defaults to 44.1kHz, 16-bit */
|
||||
alternate = 2;
|
||||
break;
|
||||
case 1:
|
||||
alternate = 0;
|
||||
/* don't know yet what this is ...
|
||||
alternate = 1;
|
||||
break;
|
||||
*/
|
||||
return -ENODEV;
|
||||
default:
|
||||
MISSING_CASE;
|
||||
}
|
||||
@ -783,7 +838,6 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
|
||||
case LINE6_DEVID_BASSPODXT:
|
||||
case LINE6_DEVID_BASSPODXTLIVE:
|
||||
case LINE6_DEVID_BASSPODXTPRO:
|
||||
case LINE6_DEVID_POCKETPOD:
|
||||
case LINE6_DEVID_PODXT:
|
||||
case LINE6_DEVID_PODXTPRO:
|
||||
size = sizeof(struct usb_line6_pod);
|
||||
@ -791,6 +845,12 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
|
||||
ep_write = 0x03;
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_POCKETPOD:
|
||||
size = sizeof(struct usb_line6_pod);
|
||||
ep_read = 0x82;
|
||||
ep_write = 0x02;
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_PODX3:
|
||||
case LINE6_DEVID_PODX3LIVE:
|
||||
/* currently unused! */
|
||||
@ -799,6 +859,9 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
|
||||
ep_write = 0x01;
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_PODSTUDIO_GX:
|
||||
case LINE6_DEVID_PODSTUDIO_UX1:
|
||||
case LINE6_DEVID_PODSTUDIO_UX2:
|
||||
case LINE6_DEVID_TONEPORT_GX:
|
||||
case LINE6_DEVID_TONEPORT_UX1:
|
||||
case LINE6_DEVID_TONEPORT_UX2:
|
||||
@ -925,17 +988,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
|
||||
case LINE6_DEVID_PODX3LIVE:
|
||||
case LINE6_DEVID_PODXT:
|
||||
case LINE6_DEVID_PODXTPRO:
|
||||
ret = pod_init(interface, (struct usb_line6_pod *)line6);
|
||||
ret = line6_pod_init(interface, (struct usb_line6_pod *)line6);
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_PODXTLIVE:
|
||||
switch (interface_number) {
|
||||
case PODXTLIVE_INTERFACE_POD:
|
||||
ret = pod_init(interface, (struct usb_line6_pod *)line6);
|
||||
ret = line6_pod_init(interface, (struct usb_line6_pod *)line6);
|
||||
break;
|
||||
|
||||
case PODXTLIVE_INTERFACE_VARIAX:
|
||||
ret = variax_init(interface, (struct usb_line6_variax *)line6);
|
||||
ret = line6_variax_init(interface, (struct usb_line6_variax *)line6);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -948,14 +1011,17 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_VARIAX:
|
||||
ret = variax_init(interface, (struct usb_line6_variax *)line6);
|
||||
ret = line6_variax_init(interface, (struct usb_line6_variax *)line6);
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_PODSTUDIO_GX:
|
||||
case LINE6_DEVID_PODSTUDIO_UX1:
|
||||
case LINE6_DEVID_PODSTUDIO_UX2:
|
||||
case LINE6_DEVID_TONEPORT_GX:
|
||||
case LINE6_DEVID_TONEPORT_UX1:
|
||||
case LINE6_DEVID_TONEPORT_UX2:
|
||||
case LINE6_DEVID_GUITARPORT:
|
||||
ret = toneport_init(interface, (struct usb_line6_toneport *)line6);
|
||||
ret = line6_toneport_init(interface, (struct usb_line6_toneport *)line6);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -971,10 +1037,23 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_
|
||||
if (ret < 0)
|
||||
goto err_destruct;
|
||||
|
||||
/* creation of additional special files should go here */
|
||||
|
||||
dev_info(&interface->dev, "Line6 %s now attached\n",
|
||||
line6->properties->name);
|
||||
line6_devices[devnum] = line6;
|
||||
line6_list_devices();
|
||||
|
||||
switch(product) {
|
||||
case LINE6_DEVID_PODX3:
|
||||
case LINE6_DEVID_PODX3LIVE:
|
||||
dev_info(&interface->dev, "NOTE: the Line6 %s is detected, but not yet supported\n",
|
||||
line6->properties->name);
|
||||
}
|
||||
|
||||
/* increment reference counters: */
|
||||
usb_get_intf(interface);
|
||||
usb_get_dev(usbdev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_destruct:
|
||||
@ -1000,6 +1079,8 @@ static void line6_disconnect(struct usb_interface *interface)
|
||||
if (usbdev == NULL)
|
||||
return;
|
||||
|
||||
/* removal of additional special files should go here */
|
||||
|
||||
sysfs_remove_link(&interface->dev.kobj, "usb_device");
|
||||
|
||||
interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
|
||||
@ -1007,7 +1088,7 @@ static void line6_disconnect(struct usb_interface *interface)
|
||||
|
||||
if (line6 != NULL) {
|
||||
if (line6->urb_listen != NULL)
|
||||
usb_kill_urb(line6->urb_listen);
|
||||
line6_stop_listen(line6);
|
||||
|
||||
if (usbdev != line6->usbdev)
|
||||
dev_err(line6->ifcdev,
|
||||
@ -1022,31 +1103,34 @@ static void line6_disconnect(struct usb_interface *interface)
|
||||
case LINE6_DEVID_PODX3LIVE:
|
||||
case LINE6_DEVID_PODXT:
|
||||
case LINE6_DEVID_PODXTPRO:
|
||||
pod_disconnect(interface);
|
||||
line6_pod_disconnect(interface);
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_PODXTLIVE:
|
||||
switch (interface_number) {
|
||||
case PODXTLIVE_INTERFACE_POD:
|
||||
pod_disconnect(interface);
|
||||
line6_pod_disconnect(interface);
|
||||
break;
|
||||
|
||||
case PODXTLIVE_INTERFACE_VARIAX:
|
||||
variax_disconnect(interface);
|
||||
line6_variax_disconnect(interface);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_VARIAX:
|
||||
variax_disconnect(interface);
|
||||
line6_variax_disconnect(interface);
|
||||
break;
|
||||
|
||||
case LINE6_DEVID_PODSTUDIO_GX:
|
||||
case LINE6_DEVID_PODSTUDIO_UX1:
|
||||
case LINE6_DEVID_PODSTUDIO_UX2:
|
||||
case LINE6_DEVID_TONEPORT_GX:
|
||||
case LINE6_DEVID_TONEPORT_UX1:
|
||||
case LINE6_DEVID_TONEPORT_UX2:
|
||||
case LINE6_DEVID_GUITARPORT:
|
||||
toneport_disconnect(interface);
|
||||
line6_toneport_disconnect(interface);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1055,10 +1139,9 @@ static void line6_disconnect(struct usb_interface *interface)
|
||||
|
||||
dev_info(&interface->dev, "Line6 %s now disconnected\n", line6->properties->name);
|
||||
|
||||
for (i = LINE6_MAX_DEVICES; i--;) {
|
||||
for (i = LINE6_MAX_DEVICES; i--;)
|
||||
if (line6_devices[i] == line6)
|
||||
line6_devices[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
line6_destruct(interface);
|
||||
@ -1066,14 +1149,78 @@ static void line6_disconnect(struct usb_interface *interface)
|
||||
/* decrement reference counters: */
|
||||
usb_put_intf(interface);
|
||||
usb_put_dev(usbdev);
|
||||
|
||||
line6_list_devices();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/*
|
||||
Suspend Line6 device.
|
||||
*/
|
||||
static int line6_suspend(struct usb_interface *interface, pm_message_t message)
|
||||
{
|
||||
struct usb_line6 *line6 = usb_get_intfdata(interface);
|
||||
struct snd_line6_pcm *line6pcm = line6->line6pcm;
|
||||
|
||||
snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
|
||||
|
||||
if (line6->properties->capabilities & LINE6_BIT_CONTROL)
|
||||
line6_stop_listen(line6);
|
||||
|
||||
if (line6pcm != NULL) {
|
||||
snd_pcm_suspend_all(line6pcm->pcm);
|
||||
line6_pcm_disconnect(line6pcm);
|
||||
line6pcm->flags = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Resume Line6 device.
|
||||
*/
|
||||
static int line6_resume(struct usb_interface *interface)
|
||||
{
|
||||
struct usb_line6 *line6 = usb_get_intfdata(interface);
|
||||
|
||||
if (line6->properties->capabilities & LINE6_BIT_CONTROL)
|
||||
line6_start_listen(line6);
|
||||
|
||||
snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Resume Line6 device after reset.
|
||||
*/
|
||||
static int line6_reset_resume(struct usb_interface *interface)
|
||||
{
|
||||
struct usb_line6 *line6 = usb_get_intfdata(interface);
|
||||
|
||||
switch (line6->usbdev->descriptor.idProduct) {
|
||||
case LINE6_DEVID_PODSTUDIO_GX:
|
||||
case LINE6_DEVID_PODSTUDIO_UX1:
|
||||
case LINE6_DEVID_PODSTUDIO_UX2:
|
||||
case LINE6_DEVID_TONEPORT_GX:
|
||||
case LINE6_DEVID_TONEPORT_UX1:
|
||||
case LINE6_DEVID_TONEPORT_UX2:
|
||||
case LINE6_DEVID_GUITARPORT:
|
||||
line6_toneport_reset_resume((struct usb_line6_toneport *)line6);
|
||||
}
|
||||
|
||||
return line6_resume(interface);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct usb_driver line6_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.probe = line6_probe,
|
||||
.disconnect = line6_disconnect,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = line6_suspend,
|
||||
.resume = line6_resume,
|
||||
.reset_resume = line6_reset_resume,
|
||||
#endif
|
||||
.id_table = line6_id_table,
|
||||
};
|
||||
|
||||
@ -1086,20 +1233,27 @@ static int __init line6_init(void)
|
||||
|
||||
printk(KERN_INFO "%s driver version %s%s\n",
|
||||
DRIVER_NAME, DRIVER_VERSION, DRIVER_REVISION);
|
||||
line6_workqueue = create_workqueue(DRIVER_NAME);
|
||||
|
||||
if (line6_workqueue == NULL) {
|
||||
err("couldn't create workqueue");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = LINE6_MAX_DEVICES; i--;)
|
||||
line6_devices[i] = NULL;
|
||||
|
||||
retval = usb_register(&line6_driver);
|
||||
|
||||
if (retval)
|
||||
if (retval) {
|
||||
err("usb_register failed. Error number %d", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
line6_request_version = kmalloc(sizeof(line6_request_version0),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (line6_request_version == NULL) {
|
||||
err("Out of memory");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy((char *)line6_request_version, line6_request_version0,
|
||||
sizeof(line6_request_version0));
|
||||
|
||||
return retval;
|
||||
}
|
||||
@ -1109,7 +1263,7 @@ static int __init line6_init(void)
|
||||
*/
|
||||
static void __exit line6_exit(void)
|
||||
{
|
||||
destroy_workqueue(line6_workqueue);
|
||||
kfree(line6_request_version);
|
||||
usb_deregister(&line6_driver);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@ -9,13 +9,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <sound/control.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "capture.h"
|
||||
#include "control.h"
|
||||
#include "driver.h"
|
||||
#include "playback.h"
|
||||
#include "pod.h"
|
||||
|
||||
@ -45,7 +46,7 @@ enum {
|
||||
POD_tuner_freq = 0x15,
|
||||
POD_tuner_note = 0x16,
|
||||
POD_tuner_pitch = 0x17,
|
||||
POD_system_invalid = 0x7fff
|
||||
POD_system_invalid = 0x10000
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -69,13 +70,16 @@ static struct snd_ratden pod_ratden = {
|
||||
};
|
||||
|
||||
static struct line6_pcm_properties pod_pcm_properties = {
|
||||
.snd_line6_playback_hw = {
|
||||
.snd_line6_playback_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_SYNC_START),
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
#ifdef CONFIG_PM
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
#endif
|
||||
SNDRV_PCM_INFO_SYNC_START),
|
||||
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = 39062,
|
||||
@ -83,17 +87,20 @@ static struct line6_pcm_properties pod_pcm_properties = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 60000,
|
||||
.period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME, /* at least one URB must fit into one period */
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = 8192,
|
||||
.periods_min = 1,
|
||||
.periods_max = 1024
|
||||
},
|
||||
.snd_line6_capture_hw = {
|
||||
.snd_line6_capture_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_SYNC_START),
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
#ifdef CONFIG_PM
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
#endif
|
||||
SNDRV_PCM_INFO_SYNC_START),
|
||||
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = 39062,
|
||||
@ -101,7 +108,7 @@ static struct line6_pcm_properties pod_pcm_properties = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 60000,
|
||||
.period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME, /* at least one URB must fit into one period */
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = 8192,
|
||||
.periods_min = 1,
|
||||
.periods_max = 1024
|
||||
@ -113,9 +120,19 @@ static struct line6_pcm_properties pod_pcm_properties = {
|
||||
.bytes_per_frame = POD_BYTES_PER_FRAME
|
||||
};
|
||||
|
||||
static const char pod_request_version[] = { 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 };
|
||||
static const char pod_request_channel[] = { 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7 };
|
||||
static const char pod_version_header[] = { 0xf2, 0x7e, 0x7f, 0x06, 0x02 };
|
||||
static const char pod_request_channel[] = {
|
||||
0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7
|
||||
};
|
||||
|
||||
static const char pod_version_header[] = {
|
||||
0xf2, 0x7e, 0x7f, 0x06, 0x02
|
||||
};
|
||||
|
||||
|
||||
/* forward declarations: */
|
||||
static void pod_startup2(unsigned long data);
|
||||
static void pod_startup3(struct usb_line6_pod *pod);
|
||||
static void pod_startup4(struct usb_line6_pod *pod);
|
||||
|
||||
|
||||
/*
|
||||
@ -129,60 +146,6 @@ static void pod_mark_batch_all_dirty(struct usb_line6_pod *pod)
|
||||
set_bit(i, pod->param_dirty);
|
||||
}
|
||||
|
||||
/*
|
||||
Send an asynchronous request for the POD firmware version and device ID.
|
||||
*/
|
||||
static int pod_version_request_async(struct usb_line6_pod *pod)
|
||||
{
|
||||
return line6_send_raw_message_async(&pod->line6, pod->buffer_versionreq, sizeof(pod_request_version));
|
||||
}
|
||||
|
||||
static void pod_create_files_work(struct work_struct *work)
|
||||
{
|
||||
struct usb_line6_pod *pod = container_of(work, struct usb_line6_pod, create_files_work);
|
||||
|
||||
pod_create_files(pod->firmware_version, pod->line6.properties->device_bit, pod->line6.ifcdev);
|
||||
}
|
||||
|
||||
static void pod_startup_timeout(unsigned long arg)
|
||||
{
|
||||
enum {
|
||||
REQUEST_NONE,
|
||||
REQUEST_DUMP,
|
||||
REQUEST_VERSION
|
||||
};
|
||||
|
||||
int request = REQUEST_NONE;
|
||||
struct usb_line6_pod *pod = (struct usb_line6_pod *)arg;
|
||||
|
||||
if (pod->dumpreq.ok) {
|
||||
if (!pod->versionreq_ok)
|
||||
request = REQUEST_VERSION;
|
||||
} else {
|
||||
if (pod->versionreq_ok)
|
||||
request = REQUEST_DUMP;
|
||||
else if (pod->startup_count++ & 1)
|
||||
request = REQUEST_DUMP;
|
||||
else
|
||||
request = REQUEST_VERSION;
|
||||
}
|
||||
|
||||
switch (request) {
|
||||
case REQUEST_DUMP:
|
||||
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0);
|
||||
break;
|
||||
|
||||
case REQUEST_VERSION:
|
||||
pod_version_request_async(pod);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
line6_startup_delayed(&pod->dumpreq, 1, pod_startup_timeout, pod);
|
||||
}
|
||||
|
||||
static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, int size)
|
||||
{
|
||||
return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, size);
|
||||
@ -218,7 +181,7 @@ static void pod_store_parameter(struct usb_line6_pod *pod, int param, int value)
|
||||
}
|
||||
|
||||
/*
|
||||
Handle SAVE button
|
||||
Handle SAVE button.
|
||||
*/
|
||||
static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int index)
|
||||
{
|
||||
@ -229,7 +192,7 @@ static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int ind
|
||||
/*
|
||||
Process a completely received message.
|
||||
*/
|
||||
void pod_process_message(struct usb_line6_pod *pod)
|
||||
void line6_pod_process_message(struct usb_line6_pod *pod)
|
||||
{
|
||||
const unsigned char *buf = pod->line6.buffer_message;
|
||||
|
||||
@ -254,7 +217,7 @@ void pod_process_message(struct usb_line6_pod *pod)
|
||||
if ((buf[1] == POD_amp_model_setup) ||
|
||||
(buf[1] == POD_effect_setup))
|
||||
/* these also affect other settings */
|
||||
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0);
|
||||
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, LINE6_DUMP_CURRENT);
|
||||
|
||||
break;
|
||||
|
||||
@ -263,7 +226,7 @@ void pod_process_message(struct usb_line6_pod *pod)
|
||||
pod->channel_num = buf[1];
|
||||
pod->dirty = 0;
|
||||
set_bit(POD_CHANNEL_DIRTY, &pod->atomic_flags);
|
||||
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0);
|
||||
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, LINE6_DUMP_CURRENT);
|
||||
break;
|
||||
|
||||
case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE:
|
||||
@ -276,7 +239,6 @@ void pod_process_message(struct usb_line6_pod *pod)
|
||||
case LINE6_DUMP_CURRENT:
|
||||
memcpy(&pod->prog_data, buf + 7, sizeof(pod->prog_data));
|
||||
pod_mark_batch_all_dirty(pod);
|
||||
pod->dumpreq.ok = 1;
|
||||
break;
|
||||
|
||||
case POD_DUMP_MEMORY:
|
||||
@ -288,6 +250,7 @@ void pod_process_message(struct usb_line6_pod *pod)
|
||||
}
|
||||
|
||||
line6_dump_finished(&pod->dumpreq);
|
||||
pod_startup3(pod);
|
||||
} else
|
||||
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "wrong size of channel dump message (%d instead of %d)\n",
|
||||
pod->line6.message_length, (int)sizeof(pod->prog_data) + 7));
|
||||
@ -300,7 +263,7 @@ void pod_process_message(struct usb_line6_pod *pod)
|
||||
#define PROCESS_SYSTEM_PARAM(x) \
|
||||
case POD_ ## x: \
|
||||
pod->x.value = value; \
|
||||
wake_up_interruptible(&pod->x.wait); \
|
||||
wake_up(&pod->x.wait); \
|
||||
break;
|
||||
|
||||
switch (buf[6]) {
|
||||
@ -331,7 +294,7 @@ void pod_process_message(struct usb_line6_pod *pod)
|
||||
case POD_SYSEX_CLIP:
|
||||
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "audio clipped\n"));
|
||||
pod->clipping.value = 1;
|
||||
wake_up_interruptible(&pod->clipping.wait);
|
||||
wake_up(&pod->clipping.wait);
|
||||
break;
|
||||
|
||||
case POD_SYSEX_STORE:
|
||||
@ -342,17 +305,9 @@ void pod_process_message(struct usb_line6_pod *pod)
|
||||
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex message %02X\n", buf[5]));
|
||||
}
|
||||
} else if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) {
|
||||
if (pod->versionreq_ok == 0) {
|
||||
pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15];
|
||||
pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int)buf[10];
|
||||
pod->versionreq_ok = 1;
|
||||
|
||||
/* Now we know the firmware version, so we schedule a bottom half
|
||||
handler to create the special files: */
|
||||
INIT_WORK(&pod->create_files_work, pod_create_files_work);
|
||||
queue_work(line6_workqueue, &pod->create_files_work);
|
||||
} else
|
||||
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "multiple firmware version message\n"));
|
||||
pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15];
|
||||
pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int)buf[10];
|
||||
pod_startup4(pod);
|
||||
} else
|
||||
DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex header\n"));
|
||||
|
||||
@ -377,7 +332,7 @@ void pod_process_message(struct usb_line6_pod *pod)
|
||||
*) This method fails if a param change message is "chopped" after the first
|
||||
byte.
|
||||
*/
|
||||
void pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, int length)
|
||||
void line6_pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, int length)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -412,7 +367,7 @@ static void pod_send_channel(struct usb_line6_pod *pod, int value)
|
||||
/*
|
||||
Transmit PODxt Pro control parameter.
|
||||
*/
|
||||
void pod_transmit_parameter(struct usb_line6_pod *pod, int param, int value)
|
||||
void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, int value)
|
||||
{
|
||||
if (line6_transmit_parameter(&pod->line6, param, value) == 0)
|
||||
pod_store_parameter(pod, param, value);
|
||||
@ -511,7 +466,7 @@ static ssize_t get_name_generic(struct usb_line6_pod *pod, const char *str, char
|
||||
char *p2;
|
||||
char *last_non_space = buf;
|
||||
|
||||
int retval = line6_wait_dump(&pod->dumpreq, 0);
|
||||
int retval = line6_dump_wait_interruptible(&pod->dumpreq);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
@ -588,7 +543,7 @@ static ssize_t pod_get_dump(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct usb_interface *interface = to_usb_interface(dev);
|
||||
struct usb_line6_pod *pod = usb_get_intfdata(interface);
|
||||
int retval = line6_wait_dump(&pod->dumpreq, 0);
|
||||
int retval = line6_dump_wait_interruptible(&pod->dumpreq);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
memcpy(buf, &pod->prog_data, sizeof(pod->prog_data));
|
||||
@ -606,8 +561,8 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
if (count != sizeof(pod->prog_data)) {
|
||||
dev_err(pod->line6.ifcdev,
|
||||
"data block must be exactly %zu bytes\n",
|
||||
sizeof(pod->prog_data));
|
||||
"data block must be exactly %d bytes\n",
|
||||
(int)sizeof(pod->prog_data));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -616,78 +571,88 @@ static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr,
|
||||
}
|
||||
|
||||
/*
|
||||
Request system parameter.
|
||||
Identify system parameters related to the tuner.
|
||||
*/
|
||||
static bool pod_is_tuner(int code)
|
||||
{
|
||||
return
|
||||
(code == POD_tuner_mute) ||
|
||||
(code == POD_tuner_freq) ||
|
||||
(code == POD_tuner_note) ||
|
||||
(code == POD_tuner_pitch);
|
||||
}
|
||||
|
||||
/*
|
||||
Get system parameter (as integer).
|
||||
@param tuner non-zero, if code refers to a tuner parameter
|
||||
*/
|
||||
static ssize_t pod_get_system_param(struct usb_line6_pod *pod, char *buf, int code, struct ValueWait *param, int tuner, int sign)
|
||||
static int pod_get_system_param_int(struct usb_line6_pod *pod, int *value, int code,
|
||||
struct ValueWait *param, int sign)
|
||||
{
|
||||
char *sysex;
|
||||
int value;
|
||||
static const int size = 1;
|
||||
int retval = 0;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner)
|
||||
if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && pod_is_tuner(code))
|
||||
return -ENODEV;
|
||||
|
||||
/* send value request to tuner: */
|
||||
/* send value request to device: */
|
||||
param->value = POD_system_invalid;
|
||||
sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEMREQ, size);
|
||||
|
||||
if (!sysex)
|
||||
return 0;
|
||||
return -ENOMEM;
|
||||
|
||||
sysex[SYSEX_DATA_OFS] = code;
|
||||
line6_send_sysex_message(&pod->line6, sysex, size);
|
||||
kfree(sysex);
|
||||
|
||||
/* wait for tuner to respond: */
|
||||
add_wait_queue(¶m->wait, &wait);
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
|
||||
while (param->value == POD_system_invalid) {
|
||||
if (signal_pending(current)) {
|
||||
retval = -ERESTARTSYS;
|
||||
break;
|
||||
} else
|
||||
schedule();
|
||||
}
|
||||
|
||||
current->state = TASK_RUNNING;
|
||||
remove_wait_queue(¶m->wait, &wait);
|
||||
/* wait for device to respond: */
|
||||
retval = wait_event_interruptible(param->wait, param->value != POD_system_invalid);
|
||||
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value;
|
||||
*value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value;
|
||||
|
||||
if(*value == POD_system_invalid)
|
||||
*value = 0; /* don't report uninitialized values */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Get system parameter (as string).
|
||||
@param tuner non-zero, if code refers to a tuner parameter
|
||||
*/
|
||||
static ssize_t pod_get_system_param_string(struct usb_line6_pod *pod, char *buf, int code,
|
||||
struct ValueWait *param, int sign)
|
||||
{
|
||||
int retval, value = 0;
|
||||
retval = pod_get_system_param_int(pod, &value, code, param, sign);
|
||||
|
||||
if(retval < 0)
|
||||
return retval;
|
||||
|
||||
return sprintf(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
/*
|
||||
Send system parameter.
|
||||
Send system parameter (from integer).
|
||||
@param tuner non-zero, if code refers to a tuner parameter
|
||||
*/
|
||||
static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf,
|
||||
int count, int code, unsigned short mask,
|
||||
int tuner)
|
||||
static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, int code)
|
||||
{
|
||||
char *sysex;
|
||||
static const int size = 5;
|
||||
unsigned short value;
|
||||
unsigned long result;
|
||||
int ret;
|
||||
|
||||
if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner)
|
||||
if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && pod_is_tuner(code))
|
||||
return -EINVAL;
|
||||
|
||||
/* send value to tuner: */
|
||||
sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size);
|
||||
if (!sysex)
|
||||
return 0;
|
||||
|
||||
ret = strict_strtoul(buf, 10, &result);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
value = result & mask;
|
||||
return -ENOMEM;
|
||||
sysex[SYSEX_DATA_OFS] = code;
|
||||
sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f;
|
||||
sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f;
|
||||
@ -695,7 +660,20 @@ static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf,
|
||||
sysex[SYSEX_DATA_OFS + 4] = (value ) & 0x0f;
|
||||
line6_send_sysex_message(&pod->line6, sysex, size);
|
||||
kfree(sysex);
|
||||
return count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Send system parameter (from string).
|
||||
@param tuner non-zero, if code refers to a tuner parameter
|
||||
*/
|
||||
static ssize_t pod_set_system_param_string(struct usb_line6_pod *pod, const char *buf,
|
||||
int count, int code, unsigned short mask)
|
||||
{
|
||||
int retval;
|
||||
unsigned short value = simple_strtoul(buf, NULL, 10) & mask;
|
||||
retval = pod_set_system_param_int(pod, value, code);
|
||||
return (retval < 0) ? retval : count;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -706,7 +684,7 @@ static ssize_t pod_get_dump_buf(struct device *dev,
|
||||
{
|
||||
struct usb_interface *interface = to_usb_interface(dev);
|
||||
struct usb_line6_pod *pod = usb_get_intfdata(interface);
|
||||
int retval = line6_wait_dump(&pod->dumpreq, 0);
|
||||
int retval = line6_dump_wait_interruptible(&pod->dumpreq);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
memcpy(buf, &pod->prog_data_buf, sizeof(pod->prog_data_buf));
|
||||
@ -725,8 +703,8 @@ static ssize_t pod_set_dump_buf(struct device *dev,
|
||||
|
||||
if (count != sizeof(pod->prog_data)) {
|
||||
dev_err(pod->line6.ifcdev,
|
||||
"data block must be exactly %zu bytes\n",
|
||||
sizeof(pod->prog_data));
|
||||
"data block must be exactly %d bytes\n",
|
||||
(int)sizeof(pod->prog_data));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -900,53 +878,94 @@ static ssize_t pod_wait_for_clip(struct device *dev,
|
||||
{
|
||||
struct usb_interface *interface = to_usb_interface(dev);
|
||||
struct usb_line6_pod *pod = usb_get_intfdata(interface);
|
||||
int err = 0;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
pod->clipping.value = 0;
|
||||
add_wait_queue(&pod->clipping.wait, &wait);
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
|
||||
while (pod->clipping.value == 0) {
|
||||
if (signal_pending(current)) {
|
||||
err = -ERESTARTSYS;
|
||||
break;
|
||||
} else
|
||||
schedule();
|
||||
}
|
||||
|
||||
current->state = TASK_RUNNING;
|
||||
remove_wait_queue(&pod->clipping.wait, &wait);
|
||||
return err;
|
||||
return wait_event_interruptible(pod->clipping.wait, pod->clipping.value != 0);
|
||||
}
|
||||
|
||||
#define POD_GET_SYSTEM_PARAM(code, tuner, sign) \
|
||||
/*
|
||||
POD startup procedure.
|
||||
This is a sequence of functions with special requirements (e.g., must
|
||||
not run immediately after initialization, must not run in interrupt
|
||||
context). After the last one has finished, the device is ready to use.
|
||||
*/
|
||||
|
||||
static void pod_startup1(struct usb_line6_pod *pod)
|
||||
{
|
||||
CHECK_STARTUP_PROGRESS(pod->startup_progress, 1);
|
||||
|
||||
/* delay startup procedure: */
|
||||
line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, (unsigned long)pod);
|
||||
}
|
||||
|
||||
static void pod_startup2(unsigned long data)
|
||||
{
|
||||
struct usb_line6_pod *pod = (struct usb_line6_pod *)data;
|
||||
CHECK_STARTUP_PROGRESS(pod->startup_progress, 2);
|
||||
|
||||
/* current channel dump: */
|
||||
line6_dump_request_async(&pod->dumpreq, &pod->line6, 0, LINE6_DUMP_CURRENT);
|
||||
}
|
||||
|
||||
static void pod_startup3(struct usb_line6_pod *pod)
|
||||
{
|
||||
struct usb_line6 *line6 = &pod->line6;
|
||||
CHECK_STARTUP_PROGRESS(pod->startup_progress, 3);
|
||||
|
||||
/* request firmware version: */
|
||||
line6_version_request_async(line6);
|
||||
}
|
||||
|
||||
static void pod_startup4(struct usb_line6_pod *pod)
|
||||
{
|
||||
CHECK_STARTUP_PROGRESS(pod->startup_progress, 4);
|
||||
|
||||
/* schedule work for global work queue: */
|
||||
schedule_work(&pod->startup_work);
|
||||
}
|
||||
|
||||
static void pod_startup5(struct work_struct *work)
|
||||
{
|
||||
struct usb_line6_pod *pod = container_of(work, struct usb_line6_pod, startup_work);
|
||||
struct usb_line6 *line6 = &pod->line6;
|
||||
|
||||
CHECK_STARTUP_PROGRESS(pod->startup_progress, 5);
|
||||
|
||||
/* serial number: */
|
||||
line6_read_serial_number(&pod->line6, &pod->serial_number);
|
||||
|
||||
/* ALSA audio interface: */
|
||||
line6_register_audio(line6);
|
||||
|
||||
/* device files: */
|
||||
line6_pod_create_files(pod->firmware_version, line6->properties->device_bit, line6->ifcdev);
|
||||
}
|
||||
|
||||
#define POD_GET_SYSTEM_PARAM(code, sign) \
|
||||
static ssize_t pod_get_ ## code(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct usb_interface *interface = to_usb_interface(dev); \
|
||||
struct usb_line6_pod *pod = usb_get_intfdata(interface); \
|
||||
return pod_get_system_param(pod, buf, POD_ ## code, &pod->code, \
|
||||
tuner, sign); \
|
||||
return pod_get_system_param_string(pod, buf, POD_ ## code, \
|
||||
&pod->code, sign); \
|
||||
}
|
||||
|
||||
#define POD_GET_SET_SYSTEM_PARAM(code, mask, tuner, sign) \
|
||||
POD_GET_SYSTEM_PARAM(code, tuner, sign) \
|
||||
#define POD_GET_SET_SYSTEM_PARAM(code, mask, sign) \
|
||||
POD_GET_SYSTEM_PARAM(code, sign) \
|
||||
static ssize_t pod_set_ ## code(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct usb_interface *interface = to_usb_interface(dev); \
|
||||
struct usb_line6_pod *pod = usb_get_intfdata(interface); \
|
||||
return pod_set_system_param(pod, buf, count, POD_ ## code, mask, \
|
||||
tuner); \
|
||||
return pod_set_system_param_string(pod, buf, count, POD_ ## code, mask); \
|
||||
}
|
||||
|
||||
POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0, 0);
|
||||
POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0, 0);
|
||||
POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 1, 0);
|
||||
POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 1, 0);
|
||||
POD_GET_SYSTEM_PARAM(tuner_note, 1, 1);
|
||||
POD_GET_SYSTEM_PARAM(tuner_pitch, 1, 1);
|
||||
POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0);
|
||||
POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0);
|
||||
POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 0);
|
||||
POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 0);
|
||||
POD_GET_SYSTEM_PARAM(tuner_note, 1);
|
||||
POD_GET_SYSTEM_PARAM(tuner_pitch, 1);
|
||||
|
||||
#undef GET_SET_SYSTEM_PARAM
|
||||
#undef GET_SYSTEM_PARAM
|
||||
@ -977,10 +996,57 @@ static DEVICE_ATTR(tuner_mute, S_IWUGO | S_IRUGO, pod_get_tuner_mute, pod_set_tu
|
||||
static DEVICE_ATTR(tuner_note, S_IRUGO, pod_get_tuner_note, line6_nop_write);
|
||||
static DEVICE_ATTR(tuner_pitch, S_IRUGO, pod_get_tuner_pitch, line6_nop_write);
|
||||
|
||||
#if CREATE_RAW_FILE
|
||||
#ifdef CONFIG_LINE6_USB_RAW
|
||||
static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw);
|
||||
#endif
|
||||
|
||||
/* control info callback */
|
||||
static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 65535;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* control get callback */
|
||||
static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
|
||||
struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6;
|
||||
ucontrol->value.integer.value[0] = pod->monitor_level.value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* control put callback */
|
||||
static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
|
||||
struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6;
|
||||
|
||||
if(ucontrol->value.integer.value[0] == pod->monitor_level.value)
|
||||
return 0;
|
||||
|
||||
pod->monitor_level.value = ucontrol->value.integer.value[0];
|
||||
pod_set_system_param_int(pod, ucontrol->value.integer.value[0], POD_monitor_level);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* control definition */
|
||||
static struct snd_kcontrol_new pod_control_monitor = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Monitor Playback Volume",
|
||||
.index = 0,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = snd_pod_control_monitor_info,
|
||||
.get = snd_pod_control_monitor_get,
|
||||
.put = snd_pod_control_monitor_put
|
||||
};
|
||||
|
||||
/*
|
||||
POD destructor.
|
||||
*/
|
||||
@ -998,8 +1064,6 @@ static void pod_destruct(struct usb_interface *interface)
|
||||
|
||||
/* free dump request data: */
|
||||
line6_dumpreq_destruct(&pod->dumpreq);
|
||||
|
||||
kfree(pod->buffer_versionreq);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1034,7 +1098,7 @@ static int pod_create_files2(struct device *dev)
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_note));
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_pitch));
|
||||
|
||||
#if CREATE_RAW_FILE
|
||||
#ifdef CONFIG_LINE6_USB_RAW
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_raw));
|
||||
#endif
|
||||
|
||||
@ -1042,9 +1106,9 @@ static int pod_create_files2(struct device *dev)
|
||||
}
|
||||
|
||||
/*
|
||||
Init POD device.
|
||||
Try to init POD device.
|
||||
*/
|
||||
int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
|
||||
static int pod_try_init(struct usb_interface *interface, struct usb_line6_pod *pod)
|
||||
{
|
||||
int err;
|
||||
struct usb_line6 *line6 = &pod->line6;
|
||||
@ -1062,6 +1126,8 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
|
||||
init_waitqueue_head(&pod->tuner_note.wait);
|
||||
init_waitqueue_head(&pod->tuner_pitch.wait);
|
||||
init_waitqueue_head(&pod->clipping.wait);
|
||||
init_timer(&pod->startup_timer);
|
||||
INIT_WORK(&pod->startup_work, pod_startup5);
|
||||
|
||||
memset(pod->param_dirty, 0xff, sizeof(pod->param_dirty));
|
||||
|
||||
@ -1070,69 +1136,73 @@ int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
|
||||
sizeof(pod_request_channel));
|
||||
if (err < 0) {
|
||||
dev_err(&interface->dev, "Out of memory\n");
|
||||
pod_destruct(interface);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pod->buffer_versionreq = kmemdup(pod_request_version,
|
||||
sizeof(pod_request_version),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (pod->buffer_versionreq == NULL) {
|
||||
dev_err(&interface->dev, "Out of memory\n");
|
||||
pod_destruct(interface);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* create sysfs entries: */
|
||||
err = pod_create_files2(&interface->dev);
|
||||
if (err < 0) {
|
||||
pod_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* initialize audio system: */
|
||||
err = line6_init_audio(line6);
|
||||
if (err < 0) {
|
||||
pod_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* initialize MIDI subsystem: */
|
||||
err = line6_init_midi(line6);
|
||||
if (err < 0) {
|
||||
pod_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* initialize PCM subsystem: */
|
||||
err = line6_init_pcm(line6, &pod_pcm_properties);
|
||||
if (err < 0) {
|
||||
pod_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* register audio system: */
|
||||
err = line6_register_audio(line6);
|
||||
/* register monitor control: */
|
||||
err = snd_ctl_add(line6->card, snd_ctl_new1(&pod_control_monitor, line6->line6pcm));
|
||||
if (err < 0) {
|
||||
pod_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
When the sound card is registered at this point, the PODxt Live
|
||||
displays "Invalid Code Error 07", so we do it later in the event
|
||||
handler.
|
||||
*/
|
||||
|
||||
if (pod->line6.properties->capabilities & LINE6_BIT_CONTROL) {
|
||||
/* query some data: */
|
||||
line6_startup_delayed(&pod->dumpreq, POD_STARTUP_DELAY,
|
||||
pod_startup_timeout, pod);
|
||||
line6_read_serial_number(&pod->line6, &pod->serial_number);
|
||||
pod->monitor_level.value = POD_system_invalid;
|
||||
|
||||
/* initiate startup procedure: */
|
||||
pod_startup1(pod);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Init POD device (and clean up in case of failure).
|
||||
*/
|
||||
int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod)
|
||||
{
|
||||
int err = pod_try_init(interface, pod);
|
||||
|
||||
if (err < 0) {
|
||||
pod_destruct(interface);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
POD device disconnected.
|
||||
*/
|
||||
void pod_disconnect(struct usb_interface *interface)
|
||||
void line6_pod_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct usb_line6_pod *pod;
|
||||
|
||||
@ -1145,14 +1215,12 @@ void pod_disconnect(struct usb_interface *interface)
|
||||
struct device *dev = &interface->dev;
|
||||
|
||||
if (line6pcm != NULL) {
|
||||
unlink_wait_clear_audio_out_urbs(line6pcm);
|
||||
unlink_wait_clear_audio_in_urbs(line6pcm);
|
||||
line6_pcm_disconnect(line6pcm);
|
||||
}
|
||||
|
||||
if (dev != NULL) {
|
||||
/* remove sysfs entries: */
|
||||
if (pod->versionreq_ok)
|
||||
pod_remove_files(pod->firmware_version, pod->line6.properties->device_bit, dev);
|
||||
line6_pod_remove_files(pod->firmware_version, pod->line6.properties->device_bit, dev);
|
||||
|
||||
device_remove_file(dev, &dev_attr_channel);
|
||||
device_remove_file(dev, &dev_attr_clip);
|
||||
@ -1179,7 +1247,7 @@ void pod_disconnect(struct usb_interface *interface)
|
||||
device_remove_file(dev, &dev_attr_tuner_note);
|
||||
device_remove_file(dev, &dev_attr_tuner_pitch);
|
||||
|
||||
#if CREATE_RAW_FILE
|
||||
#ifdef CONFIG_LINE6_USB_RAW
|
||||
device_remove_file(dev, &dev_attr_raw);
|
||||
#endif
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@ -13,15 +13,14 @@
|
||||
#define POD_H
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "dumprequest.h"
|
||||
|
||||
|
||||
@ -42,163 +41,156 @@
|
||||
*/
|
||||
#define POD_CONTROL_SIZE 0x80
|
||||
#define POD_BUFSIZE_DUMPREQ 7
|
||||
#define POD_STARTUP_DELAY 3
|
||||
|
||||
#define POD_STARTUP_DELAY 3000
|
||||
|
||||
/**
|
||||
Data structure for values that need to be requested explicitly.
|
||||
This is the case for system and tuner settings.
|
||||
Data structure for values that need to be requested explicitly.
|
||||
This is the case for system and tuner settings.
|
||||
*/
|
||||
struct ValueWait {
|
||||
unsigned short value;
|
||||
int value;
|
||||
wait_queue_head_t wait;
|
||||
};
|
||||
|
||||
/**
|
||||
Binary PodXT Pro program dump
|
||||
Binary PODxt Pro program dump
|
||||
*/
|
||||
struct pod_program {
|
||||
/**
|
||||
Header information (including program name).
|
||||
Header information (including program name).
|
||||
*/
|
||||
unsigned char header[0x20];
|
||||
|
||||
/**
|
||||
Program parameters.
|
||||
Program parameters.
|
||||
*/
|
||||
unsigned char control[POD_CONTROL_SIZE];
|
||||
};
|
||||
|
||||
struct usb_line6_pod {
|
||||
/**
|
||||
Generic Line6 USB data.
|
||||
Generic Line6 USB data.
|
||||
*/
|
||||
struct usb_line6 line6;
|
||||
|
||||
/**
|
||||
Dump request structure.
|
||||
Dump request structure.
|
||||
*/
|
||||
struct line6_dump_request dumpreq;
|
||||
|
||||
/**
|
||||
Current program number.
|
||||
Current program number.
|
||||
*/
|
||||
unsigned char channel_num;
|
||||
|
||||
/**
|
||||
Current program settings.
|
||||
Current program settings.
|
||||
*/
|
||||
struct pod_program prog_data;
|
||||
|
||||
/**
|
||||
Buffer for data retrieved from or to be stored on PODxt Pro.
|
||||
Buffer for data retrieved from or to be stored on PODxt Pro.
|
||||
*/
|
||||
struct pod_program prog_data_buf;
|
||||
|
||||
/**
|
||||
Buffer for requesting version number.
|
||||
*/
|
||||
unsigned char *buffer_versionreq;
|
||||
|
||||
/**
|
||||
Tuner mute mode.
|
||||
Tuner mute mode.
|
||||
*/
|
||||
struct ValueWait tuner_mute;
|
||||
|
||||
/**
|
||||
Tuner base frequency (typically 440Hz).
|
||||
Tuner base frequency (typically 440Hz).
|
||||
*/
|
||||
struct ValueWait tuner_freq;
|
||||
|
||||
/**
|
||||
Note received from tuner.
|
||||
Note received from tuner.
|
||||
*/
|
||||
struct ValueWait tuner_note;
|
||||
|
||||
/**
|
||||
Pitch value received from tuner.
|
||||
Pitch value received from tuner.
|
||||
*/
|
||||
struct ValueWait tuner_pitch;
|
||||
|
||||
/**
|
||||
Instrument monitor level.
|
||||
Instrument monitor level.
|
||||
*/
|
||||
struct ValueWait monitor_level;
|
||||
|
||||
/**
|
||||
Audio routing mode.
|
||||
0: send processed guitar
|
||||
1: send clean guitar
|
||||
2: send clean guitar re-amp playback
|
||||
3: send re-amp playback
|
||||
Audio routing mode.
|
||||
0: send processed guitar
|
||||
1: send clean guitar
|
||||
2: send clean guitar re-amp playback
|
||||
3: send re-amp playback
|
||||
*/
|
||||
struct ValueWait routing;
|
||||
|
||||
/**
|
||||
Wait for audio clipping event.
|
||||
Wait for audio clipping event.
|
||||
*/
|
||||
struct ValueWait clipping;
|
||||
|
||||
/**
|
||||
Bottom-half for creation of sysfs special files.
|
||||
Timer for device initializaton.
|
||||
*/
|
||||
struct work_struct create_files_work;
|
||||
struct timer_list startup_timer;
|
||||
|
||||
/**
|
||||
Dirty flags for access to parameter data.
|
||||
Work handler for device initializaton.
|
||||
*/
|
||||
struct work_struct startup_work;
|
||||
|
||||
/**
|
||||
Current progress in startup procedure.
|
||||
*/
|
||||
int startup_progress;
|
||||
|
||||
/**
|
||||
Dirty flags for access to parameter data.
|
||||
*/
|
||||
unsigned long param_dirty[POD_CONTROL_SIZE / sizeof(unsigned long)];
|
||||
|
||||
/**
|
||||
Some atomic flags.
|
||||
Some atomic flags.
|
||||
*/
|
||||
unsigned long atomic_flags;
|
||||
|
||||
/**
|
||||
Counter for startup process.
|
||||
*/
|
||||
int startup_count;
|
||||
|
||||
/**
|
||||
Serial number of device.
|
||||
Serial number of device.
|
||||
*/
|
||||
int serial_number;
|
||||
|
||||
/**
|
||||
Firmware version (x 100).
|
||||
Firmware version (x 100).
|
||||
*/
|
||||
int firmware_version;
|
||||
|
||||
/**
|
||||
Device ID.
|
||||
Device ID.
|
||||
*/
|
||||
int device_id;
|
||||
|
||||
/**
|
||||
Flag to indicate modification of current program settings.
|
||||
Flag to indicate modification of current program settings.
|
||||
*/
|
||||
char dirty;
|
||||
|
||||
/**
|
||||
Flag if initial firmware version request has been successful.
|
||||
*/
|
||||
char versionreq_ok;
|
||||
|
||||
/**
|
||||
Flag to enable MIDI postprocessing.
|
||||
Flag to enable MIDI postprocessing.
|
||||
*/
|
||||
char midi_postprocess;
|
||||
};
|
||||
|
||||
|
||||
extern void pod_disconnect(struct usb_interface *interface);
|
||||
extern int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod);
|
||||
extern void pod_midi_postprocess(struct usb_line6_pod *pod,
|
||||
unsigned char *data, int length);
|
||||
extern void pod_process_message(struct usb_line6_pod *pod);
|
||||
extern void pod_receive_parameter(struct usb_line6_pod *pod, int param);
|
||||
extern void pod_transmit_parameter(struct usb_line6_pod *pod, int param,
|
||||
int value);
|
||||
extern void line6_pod_disconnect(struct usb_interface *interface);
|
||||
extern int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod);
|
||||
extern void line6_pod_midi_postprocess(struct usb_line6_pod *pod,
|
||||
unsigned char *data, int length);
|
||||
extern void line6_pod_process_message(struct usb_line6_pod *pod);
|
||||
extern void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param,
|
||||
int value);
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
#ifndef DRIVER_REVISION
|
||||
/* current subversion revision */
|
||||
#define DRIVER_REVISION " (revision 529)"
|
||||
#define DRIVER_REVISION " (revision 665)"
|
||||
#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)
|
||||
* Emil Myhrman (emil.myhrman@gmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@ -10,15 +10,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
#include <linux/wait.h>
|
||||
#include <sound/control.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "capture.h"
|
||||
#include "driver.h"
|
||||
#include "playback.h"
|
||||
#include "toneport.h"
|
||||
|
||||
|
||||
static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2);
|
||||
|
||||
|
||||
#define TONEPORT_PCM_DELAY 1
|
||||
|
||||
|
||||
static struct snd_ratden toneport_ratden = {
|
||||
.num_min = 44100,
|
||||
.num_max = 44100,
|
||||
@ -28,43 +35,52 @@ static struct snd_ratden toneport_ratden = {
|
||||
|
||||
static struct line6_pcm_properties toneport_pcm_properties = {
|
||||
.snd_line6_playback_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_SYNC_START),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 44100,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 60000,
|
||||
.period_bytes_min = 180 * 4,
|
||||
.period_bytes_max = 8192,
|
||||
.periods_min = 1,
|
||||
.periods_max = 1024},
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
#ifdef CONFIG_PM
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
#endif
|
||||
SNDRV_PCM_INFO_SYNC_START),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 44100,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 60000,
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = 8192,
|
||||
.periods_min = 1,
|
||||
.periods_max = 1024
|
||||
},
|
||||
.snd_line6_capture_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_SYNC_START),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 44100,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 60000,
|
||||
.period_bytes_min = 188 * 4,
|
||||
.period_bytes_max = 8192,
|
||||
.periods_min = 1,
|
||||
.periods_max = 1024},
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
#ifdef CONFIG_PM
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
#endif
|
||||
SNDRV_PCM_INFO_SYNC_START),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 44100,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 60000,
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = 8192,
|
||||
.periods_min = 1,
|
||||
.periods_max = 1024
|
||||
},
|
||||
.snd_line6_rates = {
|
||||
.nrats = 1,
|
||||
.rats = &toneport_ratden},
|
||||
.nrats = 1,
|
||||
.rats = &toneport_ratden
|
||||
},
|
||||
.bytes_per_frame = 4
|
||||
};
|
||||
|
||||
@ -77,6 +93,27 @@ static struct line6_pcm_properties toneport_pcm_properties = {
|
||||
static int led_red = 0x00;
|
||||
static int led_green = 0x26;
|
||||
|
||||
struct ToneportSourceInfo
|
||||
{
|
||||
const char *name;
|
||||
int code;
|
||||
};
|
||||
|
||||
static const struct ToneportSourceInfo toneport_source_info[] = {
|
||||
{ "Microphone", 0x0a01 },
|
||||
{ "Line" , 0x0801 },
|
||||
{ "Instrument", 0x0b01 },
|
||||
{ "Inst & Mic", 0x0901 }
|
||||
};
|
||||
|
||||
static bool toneport_has_led(short product)
|
||||
{
|
||||
return
|
||||
(product == LINE6_DEVID_GUITARPORT) ||
|
||||
(product == LINE6_DEVID_TONEPORT_GX);
|
||||
/* add your device here if you are missing support for the LEDs */
|
||||
}
|
||||
|
||||
static void toneport_update_led(struct device *dev)
|
||||
{
|
||||
struct usb_interface *interface = to_usb_interface(dev);
|
||||
@ -129,6 +166,7 @@ static DEVICE_ATTR(led_red, S_IWUGO | S_IRUGO, line6_nop_read,
|
||||
static DEVICE_ATTR(led_green, S_IWUGO | S_IRUGO, line6_nop_read,
|
||||
toneport_set_led_green);
|
||||
|
||||
|
||||
static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
|
||||
{
|
||||
int ret;
|
||||
@ -145,6 +183,111 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* monitor info callback */
|
||||
static int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 256;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* monitor get callback */
|
||||
static int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
|
||||
ucontrol->value.integer.value[0] = line6pcm->volume_monitor;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* monitor put callback */
|
||||
static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if(ucontrol->value.integer.value[0] == line6pcm->volume_monitor)
|
||||
return 0;
|
||||
|
||||
line6pcm->volume_monitor = ucontrol->value.integer.value[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* source info callback */
|
||||
static int snd_toneport_source_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
const int size = ARRAY_SIZE(toneport_source_info);
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = size;
|
||||
|
||||
if(uinfo->value.enumerated.item >= size)
|
||||
uinfo->value.enumerated.item = size - 1;
|
||||
|
||||
strcpy(uinfo->value.enumerated.name,
|
||||
toneport_source_info[uinfo->value.enumerated.item].name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* source get callback */
|
||||
static int snd_toneport_source_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
|
||||
struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)line6pcm->line6;
|
||||
ucontrol->value.enumerated.item[0] = toneport->source;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* source put callback */
|
||||
static int snd_toneport_source_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
|
||||
struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)line6pcm->line6;
|
||||
|
||||
if(ucontrol->value.enumerated.item[0] == toneport->source)
|
||||
return 0;
|
||||
|
||||
toneport->source = ucontrol->value.enumerated.item[0];
|
||||
toneport_send_cmd(toneport->line6.usbdev, toneport_source_info[toneport->source].code, 0x0000);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void toneport_start_pcm(unsigned long arg)
|
||||
{
|
||||
struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg;
|
||||
struct usb_line6 *line6 = &toneport->line6;
|
||||
line6_pcm_start(line6->line6pcm, MASK_PCM_MONITOR);
|
||||
}
|
||||
|
||||
/* control definition */
|
||||
static struct snd_kcontrol_new toneport_control_monitor = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Monitor Playback Volume",
|
||||
.index = 0,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = snd_toneport_monitor_info,
|
||||
.get = snd_toneport_monitor_get,
|
||||
.put = snd_toneport_monitor_put
|
||||
};
|
||||
|
||||
/* source selector definition */
|
||||
static struct snd_kcontrol_new toneport_control_source = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "PCM Capture Source",
|
||||
.index = 0,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = snd_toneport_source_info,
|
||||
.get = snd_toneport_source_get,
|
||||
.put = snd_toneport_source_put
|
||||
};
|
||||
|
||||
/*
|
||||
Toneport destructor.
|
||||
*/
|
||||
@ -162,14 +305,41 @@ static void toneport_destruct(struct usb_interface *interface)
|
||||
}
|
||||
|
||||
/*
|
||||
Init Toneport device.
|
||||
Setup Toneport device.
|
||||
*/
|
||||
int toneport_init(struct usb_interface *interface,
|
||||
struct usb_line6_toneport *toneport)
|
||||
static void toneport_setup(struct usb_line6_toneport *toneport)
|
||||
{
|
||||
int err, ticks;
|
||||
int ticks;
|
||||
struct usb_line6 *line6 = &toneport->line6;
|
||||
struct usb_device *usbdev;
|
||||
struct usb_device *usbdev = line6->usbdev;
|
||||
|
||||
/* sync time on device with host: */
|
||||
ticks = (int)get_seconds();
|
||||
line6_write_data(line6, 0x80c6, &ticks, 4);
|
||||
|
||||
/* enable device: */
|
||||
toneport_send_cmd(usbdev, 0x0301, 0x0000);
|
||||
|
||||
/* initialize source select: */
|
||||
switch(usbdev->descriptor.idProduct) {
|
||||
case LINE6_DEVID_TONEPORT_UX1:
|
||||
case LINE6_DEVID_PODSTUDIO_UX1:
|
||||
toneport_send_cmd(usbdev, toneport_source_info[toneport->source].code, 0x0000);
|
||||
}
|
||||
|
||||
if (toneport_has_led(usbdev->descriptor.idProduct))
|
||||
toneport_update_led(&usbdev->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
Try to init Toneport device.
|
||||
*/
|
||||
static int toneport_try_init(struct usb_interface *interface,
|
||||
struct usb_line6_toneport *toneport)
|
||||
{
|
||||
int err;
|
||||
struct usb_line6 *line6 = &toneport->line6;
|
||||
struct usb_device *usbdev = line6->usbdev;
|
||||
|
||||
if ((interface == NULL) || (toneport == NULL))
|
||||
return -ENODEV;
|
||||
@ -177,64 +347,93 @@ int toneport_init(struct usb_interface *interface,
|
||||
/* initialize audio system: */
|
||||
err = line6_init_audio(line6);
|
||||
if (err < 0) {
|
||||
toneport_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* initialize PCM subsystem: */
|
||||
err = line6_init_pcm(line6, &toneport_pcm_properties);
|
||||
if (err < 0) {
|
||||
toneport_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* register monitor control: */
|
||||
err = snd_ctl_add(line6->card, snd_ctl_new1(&toneport_control_monitor, line6->line6pcm));
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* register source select control: */
|
||||
switch(usbdev->descriptor.idProduct) {
|
||||
case LINE6_DEVID_TONEPORT_UX1:
|
||||
case LINE6_DEVID_PODSTUDIO_UX1:
|
||||
err = snd_ctl_add(line6->card, snd_ctl_new1(&toneport_control_source, line6->line6pcm));
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* register audio system: */
|
||||
err = line6_register_audio(line6);
|
||||
if (err < 0) {
|
||||
toneport_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
usbdev = line6->usbdev;
|
||||
line6_read_serial_number(line6, &toneport->serial_number);
|
||||
line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1);
|
||||
|
||||
/* sync time on device with host: */
|
||||
ticks = (int)get_seconds();
|
||||
line6_write_data(line6, 0x80c6, &ticks, 4);
|
||||
|
||||
/*
|
||||
seems to work without the first two...
|
||||
*/
|
||||
/* toneport_send_cmd(usbdev, 0x0201, 0x0002); */
|
||||
/* toneport_send_cmd(usbdev, 0x0801, 0x0000); */
|
||||
/* only one that works for me; on GP, TP might be different? */
|
||||
toneport_send_cmd(usbdev, 0x0301, 0x0000);
|
||||
|
||||
if (usbdev->descriptor.idProduct != LINE6_DEVID_GUITARPORT) {
|
||||
CHECK_RETURN(device_create_file
|
||||
(&interface->dev, &dev_attr_led_red));
|
||||
CHECK_RETURN(device_create_file
|
||||
(&interface->dev, &dev_attr_led_green));
|
||||
toneport_update_led(&usbdev->dev);
|
||||
if (toneport_has_led(usbdev->descriptor.idProduct)) {
|
||||
CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_red));
|
||||
CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_green));
|
||||
}
|
||||
|
||||
toneport_setup(toneport);
|
||||
|
||||
init_timer(&toneport->timer);
|
||||
toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ;
|
||||
toneport->timer.function = toneport_start_pcm;
|
||||
toneport->timer.data = (unsigned long)toneport;
|
||||
add_timer(&toneport->timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Init Toneport device (and clean up in case of failure).
|
||||
*/
|
||||
int line6_toneport_init(struct usb_interface *interface,
|
||||
struct usb_line6_toneport *toneport)
|
||||
{
|
||||
int err = toneport_try_init(interface, toneport);
|
||||
|
||||
if (err < 0) {
|
||||
toneport_destruct(interface);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
Resume Toneport device after reset.
|
||||
*/
|
||||
void line6_toneport_reset_resume(struct usb_line6_toneport *toneport)
|
||||
{
|
||||
toneport_setup(toneport);
|
||||
}
|
||||
|
||||
/*
|
||||
Toneport device disconnected.
|
||||
*/
|
||||
void toneport_disconnect(struct usb_interface *interface)
|
||||
void line6_toneport_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct usb_line6_toneport *toneport;
|
||||
|
||||
if (interface == NULL)
|
||||
return;
|
||||
toneport = usb_get_intfdata(interface);
|
||||
|
||||
if (toneport->line6.usbdev->descriptor.idProduct !=
|
||||
LINE6_DEVID_GUITARPORT) {
|
||||
toneport = usb_get_intfdata(interface);
|
||||
del_timer_sync(&toneport->timer);
|
||||
|
||||
if (toneport_has_led(toneport->line6.usbdev->descriptor.idProduct)) {
|
||||
device_remove_file(&interface->dev, &dev_attr_led_red);
|
||||
device_remove_file(&interface->dev, &dev_attr_led_green);
|
||||
}
|
||||
@ -243,8 +442,8 @@ void toneport_disconnect(struct usb_interface *interface)
|
||||
struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm;
|
||||
|
||||
if (line6pcm != NULL) {
|
||||
unlink_wait_clear_audio_out_urbs(line6pcm);
|
||||
unlink_wait_clear_audio_in_urbs(line6pcm);
|
||||
line6_pcm_stop(line6pcm, MASK_PCM_MONITOR);
|
||||
line6_pcm_disconnect(line6pcm);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@ -13,33 +13,44 @@
|
||||
#define TONEPORT_H
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
|
||||
struct usb_line6_toneport {
|
||||
/**
|
||||
Generic Line6 USB data.
|
||||
Generic Line6 USB data.
|
||||
*/
|
||||
struct usb_line6 line6;
|
||||
|
||||
/**
|
||||
Serial number of device.
|
||||
Source selector.
|
||||
*/
|
||||
int source;
|
||||
|
||||
/**
|
||||
Serial number of device.
|
||||
*/
|
||||
int serial_number;
|
||||
|
||||
/**
|
||||
Firmware version (x 100).
|
||||
Firmware version (x 100).
|
||||
*/
|
||||
int firmware_version;
|
||||
|
||||
/**
|
||||
Timer for delayed PCM startup.
|
||||
*/
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
|
||||
extern void toneport_disconnect(struct usb_interface *interface);
|
||||
extern int toneport_init(struct usb_interface *interface,
|
||||
struct usb_line6_toneport *toneport);
|
||||
extern void line6_toneport_disconnect(struct usb_interface *interface);
|
||||
extern int line6_toneport_init(struct usb_interface *interface,
|
||||
struct usb_line6_toneport *toneport);
|
||||
extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport);
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2005-2008 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
@ -25,6 +25,9 @@
|
||||
#define LINE6_DEVID_BASSPODXTPRO 0x4252
|
||||
#define LINE6_DEVID_GUITARPORT 0x4750
|
||||
#define LINE6_DEVID_POCKETPOD 0x5051
|
||||
#define LINE6_DEVID_PODSTUDIO_GX 0x4153
|
||||
#define LINE6_DEVID_PODSTUDIO_UX1 0x4150
|
||||
#define LINE6_DEVID_PODSTUDIO_UX2 0x4151
|
||||
#define LINE6_DEVID_PODX3 0x414a
|
||||
#define LINE6_DEVID_PODX3LIVE 0x414b
|
||||
#define LINE6_DEVID_PODXT 0x5044
|
||||
@ -35,20 +38,23 @@
|
||||
#define LINE6_DEVID_TONEPORT_UX2 0x4142
|
||||
#define LINE6_DEVID_VARIAX 0x534d
|
||||
|
||||
#define LINE6_BIT_BASSPODXT (1 << 0)
|
||||
#define LINE6_BIT_BASSPODXTLIVE (1 << 1)
|
||||
#define LINE6_BIT_BASSPODXTPRO (1 << 2)
|
||||
#define LINE6_BIT_GUITARPORT (1 << 3)
|
||||
#define LINE6_BIT_POCKETPOD (1 << 4)
|
||||
#define LINE6_BIT_PODX3 (1 << 5)
|
||||
#define LINE6_BIT_PODX3LIVE (1 << 6)
|
||||
#define LINE6_BIT_PODXT (1 << 7)
|
||||
#define LINE6_BIT_PODXTLIVE (1 << 8)
|
||||
#define LINE6_BIT_PODXTPRO (1 << 9)
|
||||
#define LINE6_BIT_TONEPORT_GX (1 << 10)
|
||||
#define LINE6_BIT_TONEPORT_UX1 (1 << 11)
|
||||
#define LINE6_BIT_TONEPORT_UX2 (1 << 12)
|
||||
#define LINE6_BIT_VARIAX (1 << 13)
|
||||
#define LINE6_BIT_BASSPODXT (1 << 0)
|
||||
#define LINE6_BIT_BASSPODXTLIVE (1 << 1)
|
||||
#define LINE6_BIT_BASSPODXTPRO (1 << 2)
|
||||
#define LINE6_BIT_GUITARPORT (1 << 3)
|
||||
#define LINE6_BIT_POCKETPOD (1 << 4)
|
||||
#define LINE6_BIT_PODSTUDIO_GX (1 << 5)
|
||||
#define LINE6_BIT_PODSTUDIO_UX1 (1 << 6)
|
||||
#define LINE6_BIT_PODSTUDIO_UX2 (1 << 7)
|
||||
#define LINE6_BIT_PODX3 (1 << 8)
|
||||
#define LINE6_BIT_PODX3LIVE (1 << 9)
|
||||
#define LINE6_BIT_PODXT (1 << 10)
|
||||
#define LINE6_BIT_PODXTLIVE (1 << 11)
|
||||
#define LINE6_BIT_PODXTPRO (1 << 12)
|
||||
#define LINE6_BIT_TONEPORT_GX (1 << 13)
|
||||
#define LINE6_BIT_TONEPORT_UX1 (1 << 14)
|
||||
#define LINE6_BIT_TONEPORT_UX2 (1 << 15)
|
||||
#define LINE6_BIT_VARIAX (1 << 16)
|
||||
|
||||
#define LINE6_BITS_PRO (LINE6_BIT_BASSPODXTPRO | \
|
||||
LINE6_BIT_PODXTPRO)
|
||||
@ -66,9 +72,13 @@
|
||||
#define LINE6_BIT_CONTROL (1 << 0)
|
||||
/* device supports PCM input/output via USB */
|
||||
#define LINE6_BIT_PCM (1 << 1)
|
||||
#define LINE6_BIT_CONTROL_PCM (LINE6_BIT_CONTROL | LINE6_BIT_PCM)
|
||||
/* device support hardware monitoring */
|
||||
#define LINE6_BIT_HWMON (1 << 2)
|
||||
|
||||
#define LINE6_BIT_CONTROL_PCM_HWMON (LINE6_BIT_CONTROL | LINE6_BIT_PCM | LINE6_BIT_HWMON)
|
||||
|
||||
#define LINE6_FALLBACK_INTERVAL 10
|
||||
#define LINE6_FALLBACK_MAXPACKETSIZE 16
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@ -9,12 +9,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "control.h"
|
||||
#include "driver.h"
|
||||
#include "variax.h"
|
||||
|
||||
|
||||
@ -26,18 +25,46 @@
|
||||
#define VARIAX_OFFSET_ACTIVATE 7
|
||||
|
||||
|
||||
/*
|
||||
This message is sent by the device during initialization and identifies
|
||||
the connected guitar model.
|
||||
*/
|
||||
static const char variax_init_model[] = {
|
||||
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x69, 0x02,
|
||||
0x00
|
||||
};
|
||||
|
||||
/*
|
||||
This message is sent by the device during initialization and identifies
|
||||
the connected guitar version.
|
||||
*/
|
||||
static const char variax_init_version[] = {
|
||||
0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c,
|
||||
0x07, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/*
|
||||
This message is the last one sent by the device during initialization.
|
||||
*/
|
||||
static const char variax_init_done[] = {
|
||||
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
|
||||
};
|
||||
|
||||
static const char variax_activate[] = {
|
||||
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
|
||||
0xf7
|
||||
};
|
||||
|
||||
static const char variax_request_bank[] = {
|
||||
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6d, 0xf7
|
||||
};
|
||||
|
||||
static const char variax_request_model1[] = {
|
||||
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x03,
|
||||
0x00, 0x00, 0x00, 0xf7
|
||||
};
|
||||
|
||||
static const char variax_request_model2[] = {
|
||||
0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x03,
|
||||
@ -45,6 +72,13 @@ static const char variax_request_model2[] = {
|
||||
};
|
||||
|
||||
|
||||
/* forward declarations: */
|
||||
static int variax_create_files2(struct device *dev);
|
||||
static void variax_startup2(unsigned long data);
|
||||
static void variax_startup4(unsigned long data);
|
||||
static void variax_startup5(unsigned long data);
|
||||
|
||||
|
||||
/*
|
||||
Decode data transmitted by workbench.
|
||||
*/
|
||||
@ -60,42 +94,93 @@ static void variax_decode(const unsigned char *raw_data, unsigned char *data,
|
||||
}
|
||||
}
|
||||
|
||||
static void variax_activate_timeout(unsigned long arg)
|
||||
static void variax_activate_async(struct usb_line6_variax *variax, int a)
|
||||
{
|
||||
struct usb_line6_variax *variax = (struct usb_line6_variax *)arg;
|
||||
variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = 1;
|
||||
variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
|
||||
line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
|
||||
sizeof(variax_activate));
|
||||
}
|
||||
|
||||
/*
|
||||
Send an asynchronous activation request after a given interval.
|
||||
Variax startup procedure.
|
||||
This is a sequence of functions with special requirements (e.g., must
|
||||
not run immediately after initialization, must not run in interrupt
|
||||
context). After the last one has finished, the device is ready to use.
|
||||
*/
|
||||
static void variax_activate_delayed(struct usb_line6_variax *variax,
|
||||
int seconds)
|
||||
|
||||
static void variax_startup1(struct usb_line6_variax *variax)
|
||||
{
|
||||
variax->activate_timer.expires = jiffies + seconds * HZ;
|
||||
variax->activate_timer.function = variax_activate_timeout;
|
||||
variax->activate_timer.data = (unsigned long)variax;
|
||||
add_timer(&variax->activate_timer);
|
||||
CHECK_STARTUP_PROGRESS(variax->startup_progress, 1);
|
||||
|
||||
/* delay startup procedure: */
|
||||
line6_start_timer(&variax->startup_timer, VARIAX_STARTUP_DELAY1, variax_startup2, (unsigned long)variax);
|
||||
}
|
||||
|
||||
static void variax_startup_timeout(unsigned long arg)
|
||||
static void variax_startup2(unsigned long data)
|
||||
{
|
||||
struct usb_line6_variax *variax = (struct usb_line6_variax *)arg;
|
||||
struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
|
||||
struct usb_line6 *line6 = &variax->line6;
|
||||
CHECK_STARTUP_PROGRESS(variax->startup_progress, 2);
|
||||
|
||||
if (variax->dumpreq.ok)
|
||||
return;
|
||||
/* request firmware version: */
|
||||
line6_version_request_async(line6);
|
||||
}
|
||||
|
||||
line6_dump_request_async(&variax->dumpreq, &variax->line6, 0);
|
||||
line6_startup_delayed(&variax->dumpreq, 1, variax_startup_timeout,
|
||||
variax);
|
||||
static void variax_startup3(struct usb_line6_variax *variax)
|
||||
{
|
||||
CHECK_STARTUP_PROGRESS(variax->startup_progress, 3);
|
||||
|
||||
/* delay startup procedure: */
|
||||
line6_start_timer(&variax->startup_timer, VARIAX_STARTUP_DELAY3, variax_startup4, (unsigned long)variax);
|
||||
}
|
||||
|
||||
static void variax_startup4(unsigned long data)
|
||||
{
|
||||
struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
|
||||
CHECK_STARTUP_PROGRESS(variax->startup_progress, 4);
|
||||
|
||||
/* activate device: */
|
||||
variax_activate_async(variax, 1);
|
||||
line6_start_timer(&variax->startup_timer, VARIAX_STARTUP_DELAY4, variax_startup5, (unsigned long)variax);
|
||||
}
|
||||
|
||||
static void variax_startup5(unsigned long data)
|
||||
{
|
||||
struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
|
||||
CHECK_STARTUP_PROGRESS(variax->startup_progress, 5);
|
||||
|
||||
/* current model dump: */
|
||||
line6_dump_request_async(&variax->dumpreq, &variax->line6, 0, VARIAX_DUMP_PASS1);
|
||||
/* passes 2 and 3 are performed implicitly before entering variax_startup6 */
|
||||
}
|
||||
|
||||
static void variax_startup6(struct usb_line6_variax *variax)
|
||||
{
|
||||
CHECK_STARTUP_PROGRESS(variax->startup_progress, 6);
|
||||
|
||||
/* schedule work for global work queue: */
|
||||
schedule_work(&variax->startup_work);
|
||||
}
|
||||
|
||||
static void variax_startup7(struct work_struct *work)
|
||||
{
|
||||
struct usb_line6_variax *variax = container_of(work, struct usb_line6_variax, startup_work);
|
||||
struct usb_line6 *line6 = &variax->line6;
|
||||
|
||||
CHECK_STARTUP_PROGRESS(variax->startup_progress, 7);
|
||||
|
||||
/* ALSA audio interface: */
|
||||
line6_register_audio(&variax->line6);
|
||||
|
||||
/* device files: */
|
||||
line6_variax_create_files(0, 0, line6->ifcdev);
|
||||
variax_create_files2(line6->ifcdev);
|
||||
}
|
||||
|
||||
/*
|
||||
Process a completely received message.
|
||||
*/
|
||||
void variax_process_message(struct usb_line6_variax *variax)
|
||||
void line6_variax_process_message(struct usb_line6_variax *variax)
|
||||
{
|
||||
const unsigned char *buf = variax->line6.buffer_message;
|
||||
|
||||
@ -115,12 +200,11 @@ void variax_process_message(struct usb_line6_variax *variax)
|
||||
case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE:
|
||||
case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST:
|
||||
variax->model = buf[1];
|
||||
line6_dump_request_async(&variax->dumpreq, &variax->line6, 0);
|
||||
line6_dump_request_async(&variax->dumpreq, &variax->line6, 0, VARIAX_DUMP_PASS1);
|
||||
break;
|
||||
|
||||
case LINE6_RESET:
|
||||
dev_info(variax->line6.ifcdev, "VARIAX reset\n");
|
||||
variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY);
|
||||
break;
|
||||
|
||||
case LINE6_SYSEX_BEGIN:
|
||||
@ -132,8 +216,7 @@ void variax_process_message(struct usb_line6_variax *variax)
|
||||
case VARIAX_DUMP_PASS1:
|
||||
variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH, (unsigned char *)&variax->model_data,
|
||||
(sizeof(variax->model_data.name) + sizeof(variax->model_data.control) / 2) * 2);
|
||||
line6_dump_request_async(&variax->dumpreq, &variax->line6, 1);
|
||||
line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS2);
|
||||
line6_dump_request_async(&variax->dumpreq, &variax->line6, 1, VARIAX_DUMP_PASS2);
|
||||
break;
|
||||
|
||||
case VARIAX_DUMP_PASS2:
|
||||
@ -141,21 +224,31 @@ void variax_process_message(struct usb_line6_variax *variax)
|
||||
variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH,
|
||||
(unsigned char *)&variax->model_data.control + sizeof(variax->model_data.control) / 2,
|
||||
sizeof(variax->model_data.control) / 2 * 2);
|
||||
variax->dumpreq.ok = 1;
|
||||
line6_dump_request_async(&variax->dumpreq, &variax->line6, 2);
|
||||
line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS3);
|
||||
line6_dump_request_async(&variax->dumpreq, &variax->line6, 2, VARIAX_DUMP_PASS3);
|
||||
}
|
||||
} else {
|
||||
DEBUG_MESSAGES(dev_err(variax->line6.ifcdev, "illegal length %d of model data\n", variax->line6.message_length));
|
||||
line6_dump_finished(&variax->dumpreq);
|
||||
}
|
||||
} else if (memcmp(buf + 1, variax_request_bank + 1,
|
||||
sizeof(variax_request_bank) - 2) == 0) {
|
||||
sizeof(variax_request_bank) - 2) == 0) {
|
||||
memcpy(variax->bank,
|
||||
buf + sizeof(variax_request_bank) - 1,
|
||||
sizeof(variax->bank));
|
||||
variax->dumpreq.ok = 1;
|
||||
line6_dump_finished(&variax->dumpreq);
|
||||
variax_startup6(variax);
|
||||
} else if (memcmp(buf + 1, variax_init_model + 1,
|
||||
sizeof(variax_init_model) - 1) == 0) {
|
||||
memcpy(variax->guitar,
|
||||
buf + sizeof(variax_init_model),
|
||||
sizeof(variax->guitar));
|
||||
} else if (memcmp(buf + 1, variax_init_version + 1,
|
||||
sizeof(variax_init_version) - 1) == 0) {
|
||||
variax_startup3(variax);
|
||||
} else if (memcmp(buf + 1, variax_init_done + 1,
|
||||
sizeof(variax_init_done) - 1) == 0) {
|
||||
/* notify of complete initialization: */
|
||||
variax_startup4((unsigned long)variax);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -256,9 +349,7 @@ static ssize_t variax_set_active(struct device *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = value ? 1 : 0;
|
||||
line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
|
||||
sizeof(variax_activate));
|
||||
variax_activate_async(variax, value ? 1 : 0);
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -317,7 +408,7 @@ static ssize_t variax_get_name(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
|
||||
line6_wait_dump(&variax->dumpreq, 0);
|
||||
line6_dump_wait_interruptible(&variax->dumpreq);
|
||||
return get_string(buf, variax->model_data.name,
|
||||
sizeof(variax->model_data.name));
|
||||
}
|
||||
@ -329,7 +420,7 @@ static ssize_t variax_get_bank(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
|
||||
line6_wait_dump(&variax->dumpreq, 0);
|
||||
line6_dump_wait_interruptible(&variax->dumpreq);
|
||||
return get_string(buf, variax->bank, sizeof(variax->bank));
|
||||
}
|
||||
|
||||
@ -341,7 +432,7 @@ static ssize_t variax_get_dump(struct device *dev,
|
||||
{
|
||||
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
|
||||
int retval;
|
||||
retval = line6_wait_dump(&variax->dumpreq, 0);
|
||||
retval = line6_dump_wait_interruptible(&variax->dumpreq);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
memcpy(buf, &variax->model_data.control,
|
||||
@ -349,7 +440,22 @@ static ssize_t variax_get_dump(struct device *dev,
|
||||
return sizeof(variax->model_data.control);
|
||||
}
|
||||
|
||||
#if CREATE_RAW_FILE
|
||||
/*
|
||||
"read" request on "guitar" special file.
|
||||
*/
|
||||
static ssize_t variax_get_guitar(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
|
||||
return sprintf(buf, "%s\n", variax->guitar);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LINE6_USB_RAW
|
||||
|
||||
static char *variax_alloc_sysex_buffer(struct usb_line6_variax *variax, int code, int size)
|
||||
{
|
||||
return line6_alloc_sysex_buffer(&variax->line6, VARIAX_SYSEX_CODE, code, size);
|
||||
}
|
||||
|
||||
/*
|
||||
"write" request on "raw" special file.
|
||||
@ -396,8 +502,9 @@ static DEVICE_ATTR(name, S_IRUGO, variax_get_name, line6_nop_write);
|
||||
static DEVICE_ATTR(bank, S_IRUGO, variax_get_bank, line6_nop_write);
|
||||
static DEVICE_ATTR(dump, S_IRUGO, variax_get_dump, line6_nop_write);
|
||||
static DEVICE_ATTR(active, S_IWUGO | S_IRUGO, variax_get_active, variax_set_active);
|
||||
static DEVICE_ATTR(guitar, S_IRUGO, variax_get_guitar, line6_nop_write);
|
||||
|
||||
#if CREATE_RAW_FILE
|
||||
#ifdef CONFIG_LINE6_USB_RAW
|
||||
static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw);
|
||||
static DEVICE_ATTR(raw2, S_IWUGO, line6_nop_read, variax_set_raw2);
|
||||
#endif
|
||||
@ -424,7 +531,6 @@ static void variax_destruct(struct usb_interface *interface)
|
||||
line6_dumpreq_destruct(&variax->dumpreq);
|
||||
|
||||
kfree(variax->buffer_activate);
|
||||
del_timer_sync(&variax->activate_timer);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -440,7 +546,8 @@ static int variax_create_files2(struct device *dev)
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_bank));
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_dump));
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_active));
|
||||
#if CREATE_RAW_FILE
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_guitar));
|
||||
#ifdef CONFIG_LINE6_USB_RAW
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_raw));
|
||||
CHECK_RETURN(device_create_file(dev, &dev_attr_raw2));
|
||||
#endif
|
||||
@ -448,23 +555,25 @@ static int variax_create_files2(struct device *dev)
|
||||
}
|
||||
|
||||
/*
|
||||
Init workbench device.
|
||||
Try to init workbench device.
|
||||
*/
|
||||
int variax_init(struct usb_interface *interface,
|
||||
struct usb_line6_variax *variax)
|
||||
static int variax_try_init(struct usb_interface *interface,
|
||||
struct usb_line6_variax *variax)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((interface == NULL) || (variax == NULL))
|
||||
return -ENODEV;
|
||||
|
||||
init_timer(&variax->startup_timer);
|
||||
INIT_WORK(&variax->startup_work, variax_startup7);
|
||||
|
||||
/* initialize USB buffers: */
|
||||
err = line6_dumpreq_init(&variax->dumpreq, variax_request_model1,
|
||||
sizeof(variax_request_model1));
|
||||
|
||||
if (err < 0) {
|
||||
dev_err(&interface->dev, "Out of memory\n");
|
||||
variax_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -473,7 +582,6 @@ int variax_init(struct usb_interface *interface,
|
||||
|
||||
if (err < 0) {
|
||||
dev_err(&interface->dev, "Out of memory\n");
|
||||
variax_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -482,7 +590,6 @@ int variax_init(struct usb_interface *interface,
|
||||
|
||||
if (err < 0) {
|
||||
dev_err(&interface->dev, "Out of memory\n");
|
||||
variax_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -491,56 +598,45 @@ int variax_init(struct usb_interface *interface,
|
||||
|
||||
if (variax->buffer_activate == NULL) {
|
||||
dev_err(&interface->dev, "Out of memory\n");
|
||||
variax_destruct(interface);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
init_timer(&variax->activate_timer);
|
||||
|
||||
/* create sysfs entries: */
|
||||
err = variax_create_files(0, 0, &interface->dev);
|
||||
if (err < 0) {
|
||||
variax_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = variax_create_files2(&interface->dev);
|
||||
if (err < 0) {
|
||||
variax_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* initialize audio system: */
|
||||
err = line6_init_audio(&variax->line6);
|
||||
if (err < 0) {
|
||||
variax_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* initialize MIDI subsystem: */
|
||||
err = line6_init_midi(&variax->line6);
|
||||
if (err < 0) {
|
||||
variax_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* register audio system: */
|
||||
err = line6_register_audio(&variax->line6);
|
||||
/* initiate startup procedure: */
|
||||
variax_startup1(variax);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Init workbench device (and clean up in case of failure).
|
||||
*/
|
||||
int line6_variax_init(struct usb_interface *interface,
|
||||
struct usb_line6_variax *variax)
|
||||
{
|
||||
int err = variax_try_init(interface, variax);
|
||||
|
||||
if (err < 0) {
|
||||
variax_destruct(interface);
|
||||
return err;
|
||||
}
|
||||
|
||||
variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY);
|
||||
line6_startup_delayed(&variax->dumpreq, VARIAX_STARTUP_DELAY,
|
||||
variax_startup_timeout, variax);
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
Workbench device disconnected.
|
||||
*/
|
||||
void variax_disconnect(struct usb_interface *interface)
|
||||
void line6_variax_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
@ -550,7 +646,7 @@ void variax_disconnect(struct usb_interface *interface)
|
||||
|
||||
if (dev != NULL) {
|
||||
/* remove sysfs entries: */
|
||||
variax_remove_files(0, 0, dev);
|
||||
line6_variax_remove_files(0, 0, dev);
|
||||
device_remove_file(dev, &dev_attr_model);
|
||||
device_remove_file(dev, &dev_attr_volume);
|
||||
device_remove_file(dev, &dev_attr_tone);
|
||||
@ -558,7 +654,8 @@ void variax_disconnect(struct usb_interface *interface)
|
||||
device_remove_file(dev, &dev_attr_bank);
|
||||
device_remove_file(dev, &dev_attr_dump);
|
||||
device_remove_file(dev, &dev_attr_active);
|
||||
#if CREATE_RAW_FILE
|
||||
device_remove_file(dev, &dev_attr_guitar);
|
||||
#ifdef CONFIG_LINE6_USB_RAW
|
||||
device_remove_file(dev, &dev_attr_raw);
|
||||
device_remove_file(dev, &dev_attr_raw2);
|
||||
#endif
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Line6 Linux USB driver - 0.8.0
|
||||
* Line6 Linux USB driver - 0.9.0
|
||||
*
|
||||
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
|
||||
* Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@ -13,19 +13,18 @@
|
||||
#define VARIAX_H
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "dumprequest.h"
|
||||
|
||||
|
||||
#define VARIAX_ACTIVATE_DELAY 10
|
||||
#define VARIAX_STARTUP_DELAY 3
|
||||
#define VARIAX_STARTUP_DELAY1 1000
|
||||
#define VARIAX_STARTUP_DELAY3 100
|
||||
#define VARIAX_STARTUP_DELAY4 100
|
||||
|
||||
|
||||
enum {
|
||||
@ -36,73 +35,88 @@ enum {
|
||||
|
||||
|
||||
/**
|
||||
Binary Variax model dump
|
||||
Binary Variax model dump
|
||||
*/
|
||||
struct variax_model {
|
||||
/**
|
||||
Header information (including program name).
|
||||
Header information (including program name).
|
||||
*/
|
||||
unsigned char name[18];
|
||||
|
||||
/**
|
||||
Model parameters.
|
||||
Model parameters.
|
||||
*/
|
||||
unsigned char control[78 * 2];
|
||||
};
|
||||
|
||||
struct usb_line6_variax {
|
||||
/**
|
||||
Generic Line6 USB data.
|
||||
Generic Line6 USB data.
|
||||
*/
|
||||
struct usb_line6 line6;
|
||||
|
||||
/**
|
||||
Dump request structure.
|
||||
Append two extra buffers for 3-pass data query.
|
||||
Dump request structure.
|
||||
Append two extra buffers for 3-pass data query.
|
||||
*/
|
||||
struct line6_dump_request dumpreq; struct line6_dump_reqbuf extrabuf[2];
|
||||
|
||||
/**
|
||||
Buffer for activation code.
|
||||
Buffer for activation code.
|
||||
*/
|
||||
unsigned char *buffer_activate;
|
||||
|
||||
/**
|
||||
Model number.
|
||||
Model number.
|
||||
*/
|
||||
int model;
|
||||
|
||||
/**
|
||||
Current model settings.
|
||||
Current model settings.
|
||||
*/
|
||||
struct variax_model model_data;
|
||||
|
||||
/**
|
||||
Name of current model bank.
|
||||
Name of connected guitar.
|
||||
*/
|
||||
unsigned char guitar[18];
|
||||
|
||||
/**
|
||||
Name of current model bank.
|
||||
*/
|
||||
unsigned char bank[18];
|
||||
|
||||
/**
|
||||
Position of volume dial.
|
||||
Position of volume dial.
|
||||
*/
|
||||
int volume;
|
||||
|
||||
/**
|
||||
Position of tone control dial.
|
||||
Position of tone control dial.
|
||||
*/
|
||||
int tone;
|
||||
|
||||
/**
|
||||
Timer for delayed activation request.
|
||||
Handler for device initializaton.
|
||||
*/
|
||||
struct timer_list activate_timer;
|
||||
struct work_struct startup_work;
|
||||
|
||||
/**
|
||||
Timer for device initializaton.
|
||||
*/
|
||||
struct timer_list startup_timer;
|
||||
|
||||
/**
|
||||
Current progress in startup procedure.
|
||||
*/
|
||||
int startup_progress;
|
||||
};
|
||||
|
||||
|
||||
extern void variax_disconnect(struct usb_interface *interface);
|
||||
extern int variax_init(struct usb_interface *interface,
|
||||
struct usb_line6_variax *variax);
|
||||
extern void variax_process_message(struct usb_line6_variax *variax);
|
||||
extern void line6_variax_disconnect(struct usb_interface *interface);
|
||||
extern int line6_variax_init(struct usb_interface *interface,
|
||||
struct usb_line6_variax *variax);
|
||||
extern void line6_variax_process_message(struct usb_line6_variax *variax);
|
||||
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user