1
0
Fork 0

Staging: add line6 usb driver

This is an experimental Linux driver for the guitar amp, cab, and
effects modeller PODxt Pro by Line6 (and similar devices), supporting
the following features:

  - Reading/writing individual parameters
  - Reading/writing complete channel, effects setup, and amp setup data
  - Channel switching
  - Virtual MIDI interface
  - Tuner access
  - Playback/capture/mixer device for any  ALSA-compatible PCM audio
    application
  - Signal routing (record clean/processed  guitar signal, re-amping)

Moreover, preliminary support for the Variax Workbench is included.

From: Markus Grabner <grabner@icg.tugraz.at>
Cc: Mariusz Kozlowski <m.kozlowski@tuxland.pl>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
wifi-calibration
Markus Grabner 2009-02-27 19:43:04 -08:00 committed by Greg Kroah-Hartman
parent e642f09951
commit 705ececd1c
29 changed files with 6962 additions and 0 deletions

View File

@ -0,0 +1,20 @@
config LINE6_USB
tristate "Line6 USB support"
depends on USB
help
This is a driver for the guitar amp, cab, and effects modeller
PODxt Pro by Line6 (and similar devices), supporting the
following features:
* Reading/writing individual parameters
* Reading/writing complete channel, effects setup, and amp
setup data
* Channel switching
* Virtual MIDI interface
* Tuner access
* Playback/capture/mixer device for any ALSA-compatible PCM
audio application
* Signal routing (record clean/processed guitar signal,
re-amping)
Preliminary support for the Variax Workbench is included.

View File

@ -0,0 +1,15 @@
obj-$(CONFIG_LINE6_USB) += line6usb.o
line6usb-objs := \
audio.o \
capture.o \
control.o \
driver.o \
dumprequest.o \
midi.o \
midibuf.o \
pcm.o \
playback.o \
pod.o \
toneport.o \
variax.o

View File

@ -0,0 +1,69 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#include "driver.h"
#include <sound/core.h>
#include <sound/initval.h>
static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
/*
Initialize the Line6 USB audio system.
*/
int line6_init_audio(struct usb_line6 *line6)
{
static int dev = 0;
struct snd_card *card;
card = snd_card_new(line6_index[dev], line6_id[dev], THIS_MODULE, 0);
if(card == NULL)
return -ENOMEM;
line6->card = card;
strcpy(card->driver, DRIVER_NAME);
strcpy(card->shortname, "Line6-USB");
sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name, line6->ifcdev->bus_id); /* 80 chars - see asound.h */
return 0;
}
/*
Register the Line6 USB audio system.
*/
int line6_register_audio(struct usb_line6 *line6)
{
int err;
if((err = snd_card_register(line6->card)) < 0)
return err;
return 0;
}
/*
Cleanup the Line6 USB audio system.
*/
void line6_cleanup_audio(struct usb_line6 *line6)
{
struct snd_card *card = line6->card;
if(card == 0)
return;
snd_card_disconnect(card);
snd_card_free(card);
line6->card = 0;
}

View File

@ -0,0 +1,24 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef AUDIO_H
#define AUDIO_H
#include "driver.h"
extern void line6_cleanup_audio(struct usb_line6 *);
extern int line6_init_audio(struct usb_line6 *);
extern int line6_register_audio(struct usb_line6 *);
#endif

View File

@ -0,0 +1,370 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#include "driver.h"
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "audio.h"
#include "pcm.h"
#include "pod.h"
/*
Find a free URB and submit it.
*/
static int submit_audio_in_urb(struct snd_pcm_substream *substream)
{
int index;
unsigned long flags;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
int i, urb_size;
struct urb *urb_in;
spin_lock_irqsave(&line6pcm->lock_audio_in, flags);
index = find_first_zero_bit(&line6pcm->active_urb_in, 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");
return -EINVAL;
}
urb_in = line6pcm->urb_audio_in[index];
urb_size = 0;
for(i = 0; i < LINE6_ISO_PACKETS; ++i) {
struct usb_iso_packet_descriptor *fin = &urb_in->iso_frame_desc[i];
fin->offset = urb_size;
fin->length = line6pcm->max_packet_size;
urb_size += line6pcm->max_packet_size;
}
urb_in->transfer_buffer = line6pcm->buffer_in + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
urb_in->transfer_buffer_length = urb_size;
urb_in->context = substream;
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);
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
return 0;
}
/*
Submit all currently available capture URBs.
*/
static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream)
{
int ret, i;
for(i = 0; i < LINE6_ISO_BUFFERS; ++i)
if((ret = submit_audio_in_urb(substream)) < 0)
return ret;
return 0;
}
/*
Unlink all currently active capture URBs.
*/
static void unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
unsigned int i;
for(i = LINE6_ISO_BUFFERS; i--;) {
if(test_bit(i, &line6pcm->active_urb_in)) {
if(!test_and_set_bit(i, &line6pcm->unlink_urb_in)) {
struct urb *u = line6pcm->urb_audio_in[i];
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
u->transfer_flags |= URB_ASYNC_UNLINK;
#endif
usb_unlink_urb(u);
}
}
}
}
/*
Wait until unlinking of all currently active capture URBs has been finished.
*/
static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
int timeout = HZ;
unsigned int i;
int alive;
do {
alive = 0;
for (i = LINE6_ISO_BUFFERS; i--;) {
if (test_bit(i, &line6pcm->active_urb_in))
alive++;
}
if (! alive)
break;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
} 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)
{
unlink_audio_in_urbs(line6pcm);
wait_clear_audio_in_urbs(line6pcm);
}
/*
Callback for completed capture URB.
*/
static void audio_in_callback(struct urb *urb PT_REGS)
{
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;
/* 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
for(i = 0; i < LINE6_ISO_PACKETS; ++i) {
struct usb_iso_packet_descriptor *fout = &urb->iso_frame_desc[i];
line6_write_hexdump(line6pcm->line6, 'C', urb->transfer_buffer + fout->offset, fout->length);
}
#endif
spin_lock_irqsave(&line6pcm->lock_audio_in, flags);
for(i = 0; i < LINE6_ISO_PACKETS; ++i) {
char *fbuf;
int fsize;
struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i];
if(fin->status == -18) {
shutdown = 1;
break;
}
fbuf = urb->transfer_buffer + fin->offset;
fsize = fin->actual_length;
length += fsize;
if(fsize > 0) {
frames = fsize / bytes_per_frame;
if(line6pcm->pos_in_done + frames > runtime->buffer_size) {
/*
The transferred area goes over buffer boundary,
copy two separate chunks.
*/
int len;
len = runtime->buffer_size - line6pcm->pos_in_done;
if(len > 0) {
memcpy(runtime->dma_area + line6pcm->pos_in_done * bytes_per_frame, fbuf, len * bytes_per_frame);
memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, (frames - len) * bytes_per_frame);
}
else
dev_err(s2m(substream), "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 * bytes_per_frame);
}
if((line6pcm->pos_in_done += frames) >= runtime->buffer_size)
line6pcm->pos_in_done -= runtime->buffer_size;
}
}
clear_bit(index, &line6pcm->active_urb_in);
if(test_bit(index, &line6pcm->unlink_urb_in))
shutdown = 1;
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
if(!shutdown) {
submit_audio_in_urb(substream);
if((line6pcm->bytes_in += length) >= line6pcm->period_in) {
line6pcm->bytes_in -= line6pcm->period_in;
snd_pcm_period_elapsed(substream);
}
}
}
/* open capture callback */
static int snd_line6_capture_open(struct snd_pcm_substream *substream)
{
int err;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
if((err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
(&line6pcm->properties->snd_line6_rates))) < 0)
return err;
runtime->hw = line6pcm->properties->snd_line6_capture_hw;
return 0;
}
/* close capture callback */
static int snd_line6_capture_close(struct snd_pcm_substream *substream)
{
return 0;
}
/* hw_params capture callback */
static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
{
int ret;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
/* -- Florian Demski [FD] */
/* don't ask me why, but this fixes the bug on my machine */
if ( line6pcm == NULL ) {
if ( substream->pcm == NULL )
return -ENOMEM;
if ( substream->pcm->private_data == NULL )
return -ENOMEM;
substream->private_data = substream->pcm->private_data;
line6pcm = snd_pcm_substream_chip( substream );
}
/* -- [FD] end */
if((ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
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);
if(line6pcm->buffer_in) {
kfree(line6pcm->buffer_in);
line6pcm->buffer_in = 0;
}
return snd_pcm_lib_free_pages(substream);
}
/* trigger callback */
int snd_line6_capture_trigger(struct snd_pcm_substream *substream, 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);
if(err < 0) {
clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags);
return err;
}
}
break;
case SNDRV_PCM_TRIGGER_STOP:
if(test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags))
unlink_audio_in_urbs(line6pcm);
break;
default:
return -EINVAL;
}
return 0;
}
/* capture pointer callback */
static snd_pcm_uframes_t
snd_line6_capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
return line6pcm->pos_in_done;
}
/* 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,
};
int create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
int i;
/* create audio URBs and fill in constant values: */
for(i = 0; i < LINE6_ISO_BUFFERS; ++i) {
struct urb *urb;
/* URB for audio in: */
urb = line6pcm->urb_audio_in[i] = usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);
if(urb == NULL) {
dev_err(line6pcm->line6->ifcdev, "Out of memory\n");
return -ENOMEM;
}
urb->dev = line6pcm->line6->usbdev;
urb->pipe = usb_rcvisocpipe(line6pcm->line6->usbdev, line6pcm->ep_audio_read & USB_ENDPOINT_NUMBER_MASK);
urb->transfer_flags = URB_ISO_ASAP;
urb->start_frame = -1;
urb->number_of_packets = LINE6_ISO_PACKETS;
urb->interval = LINE6_ISO_INTERVAL;
urb->error_count = 0;
urb->complete = audio_in_callback;
}
return 0;
}

View File

@ -0,0 +1,31 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef CAPTURE_H
#define CAPTURE_H
#include "driver.h"
#include <sound/pcm.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);
#endif

View File

@ -0,0 +1,73 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef CONFIG_H
#define CONFIG_H
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
#include <linux/config.h>
#endif
#ifdef CONFIG_USB_DEBUG
#define DEBUG 1
#endif
/**
Development tools.
*/
#define DO_DEBUG_MESSAGES 0
#define DO_DUMP_URB_SEND DO_DEBUG_MESSAGES
#define DO_DUMP_URB_RECEIVE DO_DEBUG_MESSAGES
#define DO_DUMP_PCM_SEND 0
#define DO_DUMP_PCM_RECEIVE 0
#define DO_DUMP_MIDI_SEND DO_DEBUG_MESSAGES
#define DO_DUMP_MIDI_RECEIVE DO_DEBUG_MESSAGES
#define DO_DUMP_ANY (DO_DUMP_URB_SEND || DO_DUMP_URB_RECEIVE || \
DO_DUMP_PCM_SEND || DO_DUMP_PCM_RECEIVE || \
DO_DUMP_MIDI_SEND || DO_DUMP_MIDI_RECEIVE)
#define CREATE_RAW_FILE 0
#if DO_DEBUG_MESSAGES
#define CHECKPOINT printk("line6usb: %s (%s:%d)\n", __FUNCTION__, __FILE__, __LINE__)
#endif
/**
In Linux 2.6.13 and later, the device_attribute is passed to the sysfs
get/set functions (see /usr/src/linux/include/linux/device.h).
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
#define DEVICE_ATTRIBUTE struct device_attribute *attr,
#else
#define DEVICE_ATTRIBUTE
#endif
/**
In Linux 2.6.20 and later, the pt_regs is no longer passed to USB callback
functions.
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
#define PT_REGS
#else
#define PT_REGS , struct pt_regs *regs
#endif
#if DO_DEBUG_MESSAGES
#define DEBUG_MESSAGES(x) (x)
#else
#define DEBUG_MESSAGES(x)
#endif
#endif

View File

@ -0,0 +1,702 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#include "driver.h"
#include <linux/usb.h>
#include "control.h"
#include "pod.h"
#include "usbdefs.h"
#include "variax.h"
#define DEVICE_ATTR2(_name1,_name2,_mode,_show,_store) \
struct device_attribute dev_attr_##_name1 = __ATTR(_name2,_mode,_show,_store)
#define LINE6_PARAM_R(PREFIX, prefix, type, param) \
static ssize_t prefix ## _get_ ## param(struct device *dev, DEVICE_ATTRIBUTE char *buf) \
{ \
return prefix ## _get_param_ ## type(dev, buf, PREFIX ## _ ## param); \
}
#define LINE6_PARAM_RW(PREFIX, prefix, type, param) \
LINE6_PARAM_R(PREFIX, prefix, type, param); \
static ssize_t prefix ## _set_ ## param(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) \
{ \
return prefix ## _set_param_ ## type(dev, buf, count, PREFIX ## _ ## param); \
}
#define POD_PARAM_R(type, param) LINE6_PARAM_R(POD, pod, type, param)
#define POD_PARAM_RW(type, param) LINE6_PARAM_RW(POD, pod, type, param)
#define VARIAX_PARAM_R(type, param) LINE6_PARAM_R(VARIAX, variax, type, param)
#define VARIAX_PARAM_RW(type, param) LINE6_PARAM_RW(VARIAX, variax, type, param)
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);
if(retval < 0) return retval;
return sprintf(buf, "%d\n", pod->prog_data.control[param]);
}
static ssize_t pod_set_param_int(struct device *dev, const char *buf, size_t count, int param)
{
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6_pod *pod = usb_get_intfdata(interface);
int value = simple_strtoul(buf, NULL, 10);
pod_transmit_parameter(pod, param, value);
return count;
}
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);
if(retval < 0) return retval;
return sprintf(buf, "%d\n", variax->model_data.control[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)
*/
static const int BIAS = 0x7f;
static const int OFFSET = 0xf;
static const int PRECISION = 1000;
int len = 0;
unsigned part_int, part_frac;
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);
if(retval < 0) return retval;
if((p[0] == 0) && (p[1] == 0) && (p[2] == 0))
part_int = part_frac = 0;
else {
int exponent = (((p[0] & 0x7f) << 1) | (p[1] >> 7)) - BIAS;
unsigned mantissa = (p[1] << 8) | p[2] | 0x8000;
exponent -= OFFSET;
if(exponent >= 0) {
part_int = mantissa << exponent;
part_frac = 0;
}
else {
part_int = mantissa >> -exponent;
part_frac = (mantissa << (32 + exponent)) & 0xffffffff;
}
part_frac = (part_frac / ((1UL << 31) / (PRECISION / 2 * 10)) + 5) / 10;
}
len += sprintf(buf + len, "%s%d.%03d\n", ((p[0] & 0x80) ? "-" : ""), part_int, part_frac);
return len;
}
POD_PARAM_RW(int, tweak);
POD_PARAM_RW(int, wah_position);
POD_PARAM_RW(int, compression_gain);
POD_PARAM_RW(int, vol_pedal_position);
POD_PARAM_RW(int, compression_threshold);
POD_PARAM_RW(int, pan);
POD_PARAM_RW(int, amp_model_setup);
POD_PARAM_RW(int, amp_model);
POD_PARAM_RW(int, drive);
POD_PARAM_RW(int, bass);
POD_PARAM_RW(int, mid);
POD_PARAM_RW(int, lowmid);
POD_PARAM_RW(int, treble);
POD_PARAM_RW(int, highmid);
POD_PARAM_RW(int, chan_vol);
POD_PARAM_RW(int, reverb_mix);
POD_PARAM_RW(int, effect_setup);
POD_PARAM_RW(int, band_1_frequency);
POD_PARAM_RW(int, presence);
POD_PARAM_RW(int, treble__bass);
POD_PARAM_RW(int, noise_gate_enable);
POD_PARAM_RW(int, gate_threshold);
POD_PARAM_RW(int, gate_decay_time);
POD_PARAM_RW(int, stomp_enable);
POD_PARAM_RW(int, comp_enable);
POD_PARAM_RW(int, stomp_time);
POD_PARAM_RW(int, delay_enable);
POD_PARAM_RW(int, mod_param_1);
POD_PARAM_RW(int, delay_param_1);
POD_PARAM_RW(int, delay_param_1_note_value);
POD_PARAM_RW(int, band_2_frequency__bass);
POD_PARAM_RW(int, delay_param_2);
POD_PARAM_RW(int, delay_volume_mix);
POD_PARAM_RW(int, delay_param_3);
POD_PARAM_RW(int, reverb_enable);
POD_PARAM_RW(int, reverb_type);
POD_PARAM_RW(int, reverb_decay);
POD_PARAM_RW(int, reverb_tone);
POD_PARAM_RW(int, reverb_pre_delay);
POD_PARAM_RW(int, reverb_pre_post);
POD_PARAM_RW(int, band_2_frequency);
POD_PARAM_RW(int, band_3_frequency__bass);
POD_PARAM_RW(int, wah_enable);
POD_PARAM_RW(int, modulation_lo_cut);
POD_PARAM_RW(int, delay_reverb_lo_cut);
POD_PARAM_RW(int, volume_pedal_minimum);
POD_PARAM_RW(int, eq_pre_post);
POD_PARAM_RW(int, volume_pre_post);
POD_PARAM_RW(int, di_model);
POD_PARAM_RW(int, di_delay);
POD_PARAM_RW(int, mod_enable);
POD_PARAM_RW(int, mod_param_1_note_value);
POD_PARAM_RW(int, mod_param_2);
POD_PARAM_RW(int, mod_param_3);
POD_PARAM_RW(int, mod_param_4);
POD_PARAM_RW(int, mod_param_5);
POD_PARAM_RW(int, mod_volume_mix);
POD_PARAM_RW(int, mod_pre_post);
POD_PARAM_RW(int, modulation_model);
POD_PARAM_RW(int, band_3_frequency);
POD_PARAM_RW(int, band_4_frequency__bass);
POD_PARAM_RW(int, mod_param_1_double_precision);
POD_PARAM_RW(int, delay_param_1_double_precision);
POD_PARAM_RW(int, eq_enable);
POD_PARAM_RW(int, tap);
POD_PARAM_RW(int, volume_tweak_pedal_assign);
POD_PARAM_RW(int, band_5_frequency);
POD_PARAM_RW(int, tuner);
POD_PARAM_RW(int, mic_selection);
POD_PARAM_RW(int, cabinet_model);
POD_PARAM_RW(int, stomp_model);
POD_PARAM_RW(int, roomlevel);
POD_PARAM_RW(int, band_4_frequency);
POD_PARAM_RW(int, band_6_frequency);
POD_PARAM_RW(int, stomp_param_1_note_value);
POD_PARAM_RW(int, stomp_param_2);
POD_PARAM_RW(int, stomp_param_3);
POD_PARAM_RW(int, stomp_param_4);
POD_PARAM_RW(int, stomp_param_5);
POD_PARAM_RW(int, stomp_param_6);
POD_PARAM_RW(int, amp_switch_select);
POD_PARAM_RW(int, delay_param_4);
POD_PARAM_RW(int, delay_param_5);
POD_PARAM_RW(int, delay_pre_post);
POD_PARAM_RW(int, delay_model);
POD_PARAM_RW(int, delay_verb_model);
POD_PARAM_RW(int, tempo_msb);
POD_PARAM_RW(int, tempo_lsb);
POD_PARAM_RW(int, wah_model);
POD_PARAM_RW(int, bypass_volume);
POD_PARAM_RW(int, fx_loop_on_off);
POD_PARAM_RW(int, tweak_param_select);
POD_PARAM_RW(int, amp1_engage);
POD_PARAM_RW(int, band_1_gain);
POD_PARAM_RW(int, band_2_gain__bass);
POD_PARAM_RW(int, band_2_gain);
POD_PARAM_RW(int, band_3_gain__bass);
POD_PARAM_RW(int, band_3_gain);
POD_PARAM_RW(int, band_4_gain__bass);
POD_PARAM_RW(int, band_5_gain__bass);
POD_PARAM_RW(int, band_4_gain);
POD_PARAM_RW(int, band_6_gain__bass);
VARIAX_PARAM_R(int, body);
VARIAX_PARAM_R(int, pickup1_enable);
VARIAX_PARAM_R(int, pickup1_type);
VARIAX_PARAM_R(float, pickup1_position);
VARIAX_PARAM_R(float, pickup1_angle);
VARIAX_PARAM_R(float, pickup1_level);
VARIAX_PARAM_R(int, pickup2_enable);
VARIAX_PARAM_R(int, pickup2_type);
VARIAX_PARAM_R(float, pickup2_position);
VARIAX_PARAM_R(float, pickup2_angle);
VARIAX_PARAM_R(float, pickup2_level);
VARIAX_PARAM_R(int, pickup_phase);
VARIAX_PARAM_R(float, capacitance);
VARIAX_PARAM_R(float, tone_resistance);
VARIAX_PARAM_R(float, volume_resistance);
VARIAX_PARAM_R(int, taper);
VARIAX_PARAM_R(float, tone_dump);
VARIAX_PARAM_R(int, save_tone);
VARIAX_PARAM_R(float, volume_dump);
VARIAX_PARAM_R(int, tuning_enable);
VARIAX_PARAM_R(int, tuning6);
VARIAX_PARAM_R(int, tuning5);
VARIAX_PARAM_R(int, tuning4);
VARIAX_PARAM_R(int, tuning3);
VARIAX_PARAM_R(int, tuning2);
VARIAX_PARAM_R(int, tuning1);
VARIAX_PARAM_R(float, detune6);
VARIAX_PARAM_R(float, detune5);
VARIAX_PARAM_R(float, detune4);
VARIAX_PARAM_R(float, detune3);
VARIAX_PARAM_R(float, detune2);
VARIAX_PARAM_R(float, detune1);
VARIAX_PARAM_R(float, mix6);
VARIAX_PARAM_R(float, mix5);
VARIAX_PARAM_R(float, mix4);
VARIAX_PARAM_R(float, mix3);
VARIAX_PARAM_R(float, mix2);
VARIAX_PARAM_R(float, mix1);
VARIAX_PARAM_R(int, pickup_wiring);
static DEVICE_ATTR(tweak, S_IWUGO | S_IRUGO, pod_get_tweak, pod_set_tweak);
static DEVICE_ATTR(wah_position, S_IWUGO | S_IRUGO, pod_get_wah_position, pod_set_wah_position);
static DEVICE_ATTR(compression_gain, S_IWUGO | S_IRUGO, pod_get_compression_gain, pod_set_compression_gain);
static DEVICE_ATTR(vol_pedal_position, S_IWUGO | S_IRUGO, pod_get_vol_pedal_position, pod_set_vol_pedal_position);
static DEVICE_ATTR(compression_threshold, S_IWUGO | S_IRUGO, pod_get_compression_threshold, pod_set_compression_threshold);
static DEVICE_ATTR(pan, S_IWUGO | S_IRUGO, pod_get_pan, pod_set_pan);
static DEVICE_ATTR(amp_model_setup, S_IWUGO | S_IRUGO, pod_get_amp_model_setup, pod_set_amp_model_setup);
static DEVICE_ATTR(amp_model, S_IWUGO | S_IRUGO, pod_get_amp_model, pod_set_amp_model);
static DEVICE_ATTR(drive, S_IWUGO | S_IRUGO, pod_get_drive, pod_set_drive);
static DEVICE_ATTR(bass, S_IWUGO | S_IRUGO, pod_get_bass, pod_set_bass);
static DEVICE_ATTR(mid, S_IWUGO | S_IRUGO, pod_get_mid, pod_set_mid);
static DEVICE_ATTR(lowmid, S_IWUGO | S_IRUGO, pod_get_lowmid, pod_set_lowmid);
static DEVICE_ATTR(treble, S_IWUGO | S_IRUGO, pod_get_treble, pod_set_treble);
static DEVICE_ATTR(highmid, S_IWUGO | S_IRUGO, pod_get_highmid, pod_set_highmid);
static DEVICE_ATTR(chan_vol, S_IWUGO | S_IRUGO, pod_get_chan_vol, pod_set_chan_vol);
static DEVICE_ATTR(reverb_mix, S_IWUGO | S_IRUGO, pod_get_reverb_mix, pod_set_reverb_mix);
static DEVICE_ATTR(effect_setup, S_IWUGO | S_IRUGO, pod_get_effect_setup, pod_set_effect_setup);
static DEVICE_ATTR(band_1_frequency, S_IWUGO | S_IRUGO, pod_get_band_1_frequency, pod_set_band_1_frequency);
static DEVICE_ATTR(presence, S_IWUGO | S_IRUGO, pod_get_presence, pod_set_presence);
static DEVICE_ATTR2(treble__bass, treble, S_IWUGO | S_IRUGO, pod_get_treble__bass, pod_set_treble__bass);
static DEVICE_ATTR(noise_gate_enable, S_IWUGO | S_IRUGO, pod_get_noise_gate_enable, pod_set_noise_gate_enable);
static DEVICE_ATTR(gate_threshold, S_IWUGO | S_IRUGO, pod_get_gate_threshold, pod_set_gate_threshold);
static DEVICE_ATTR(gate_decay_time, S_IWUGO | S_IRUGO, pod_get_gate_decay_time, pod_set_gate_decay_time);
static DEVICE_ATTR(stomp_enable, S_IWUGO | S_IRUGO, pod_get_stomp_enable, pod_set_stomp_enable);
static DEVICE_ATTR(comp_enable, S_IWUGO | S_IRUGO, pod_get_comp_enable, pod_set_comp_enable);
static DEVICE_ATTR(stomp_time, S_IWUGO | S_IRUGO, pod_get_stomp_time, pod_set_stomp_time);
static DEVICE_ATTR(delay_enable, S_IWUGO | S_IRUGO, pod_get_delay_enable, pod_set_delay_enable);
static DEVICE_ATTR(mod_param_1, S_IWUGO | S_IRUGO, pod_get_mod_param_1, pod_set_mod_param_1);
static DEVICE_ATTR(delay_param_1, S_IWUGO | S_IRUGO, pod_get_delay_param_1, pod_set_delay_param_1);
static DEVICE_ATTR(delay_param_1_note_value, S_IWUGO | S_IRUGO, pod_get_delay_param_1_note_value, pod_set_delay_param_1_note_value);
static DEVICE_ATTR2(band_2_frequency__bass, band_2_frequency, S_IWUGO | S_IRUGO, pod_get_band_2_frequency__bass, pod_set_band_2_frequency__bass);
static DEVICE_ATTR(delay_param_2, S_IWUGO | S_IRUGO, pod_get_delay_param_2, pod_set_delay_param_2);
static DEVICE_ATTR(delay_volume_mix, S_IWUGO | S_IRUGO, pod_get_delay_volume_mix, pod_set_delay_volume_mix);
static DEVICE_ATTR(delay_param_3, S_IWUGO | S_IRUGO, pod_get_delay_param_3, pod_set_delay_param_3);
static DEVICE_ATTR(reverb_enable, S_IWUGO | S_IRUGO, pod_get_reverb_enable, pod_set_reverb_enable);
static DEVICE_ATTR(reverb_type, S_IWUGO | S_IRUGO, pod_get_reverb_type, pod_set_reverb_type);
static DEVICE_ATTR(reverb_decay, S_IWUGO | S_IRUGO, pod_get_reverb_decay, pod_set_reverb_decay);
static DEVICE_ATTR(reverb_tone, S_IWUGO | S_IRUGO, pod_get_reverb_tone, pod_set_reverb_tone);
static DEVICE_ATTR(reverb_pre_delay, S_IWUGO | S_IRUGO, pod_get_reverb_pre_delay, pod_set_reverb_pre_delay);
static DEVICE_ATTR(reverb_pre_post, S_IWUGO | S_IRUGO, pod_get_reverb_pre_post, pod_set_reverb_pre_post);
static DEVICE_ATTR(band_2_frequency, S_IWUGO | S_IRUGO, pod_get_band_2_frequency, pod_set_band_2_frequency);
static DEVICE_ATTR2(band_3_frequency__bass, band_3_frequency, S_IWUGO | S_IRUGO, pod_get_band_3_frequency__bass, pod_set_band_3_frequency__bass);
static DEVICE_ATTR(wah_enable, S_IWUGO | S_IRUGO, pod_get_wah_enable, pod_set_wah_enable);
static DEVICE_ATTR(modulation_lo_cut, S_IWUGO | S_IRUGO, pod_get_modulation_lo_cut, pod_set_modulation_lo_cut);
static DEVICE_ATTR(delay_reverb_lo_cut, S_IWUGO | S_IRUGO, pod_get_delay_reverb_lo_cut, pod_set_delay_reverb_lo_cut);
static DEVICE_ATTR(volume_pedal_minimum, S_IWUGO | S_IRUGO, pod_get_volume_pedal_minimum, pod_set_volume_pedal_minimum);
static DEVICE_ATTR(eq_pre_post, S_IWUGO | S_IRUGO, pod_get_eq_pre_post, pod_set_eq_pre_post);
static DEVICE_ATTR(volume_pre_post, S_IWUGO | S_IRUGO, pod_get_volume_pre_post, pod_set_volume_pre_post);
static DEVICE_ATTR(di_model, S_IWUGO | S_IRUGO, pod_get_di_model, pod_set_di_model);
static DEVICE_ATTR(di_delay, S_IWUGO | S_IRUGO, pod_get_di_delay, pod_set_di_delay);
static DEVICE_ATTR(mod_enable, S_IWUGO | S_IRUGO, pod_get_mod_enable, pod_set_mod_enable);
static DEVICE_ATTR(mod_param_1_note_value, S_IWUGO | S_IRUGO, pod_get_mod_param_1_note_value, pod_set_mod_param_1_note_value);
static DEVICE_ATTR(mod_param_2, S_IWUGO | S_IRUGO, pod_get_mod_param_2, pod_set_mod_param_2);
static DEVICE_ATTR(mod_param_3, S_IWUGO | S_IRUGO, pod_get_mod_param_3, pod_set_mod_param_3);
static DEVICE_ATTR(mod_param_4, S_IWUGO | S_IRUGO, pod_get_mod_param_4, pod_set_mod_param_4);
static DEVICE_ATTR(mod_param_5, S_IWUGO | S_IRUGO, pod_get_mod_param_5, pod_set_mod_param_5);
static DEVICE_ATTR(mod_volume_mix, S_IWUGO | S_IRUGO, pod_get_mod_volume_mix, pod_set_mod_volume_mix);
static DEVICE_ATTR(mod_pre_post, S_IWUGO | S_IRUGO, pod_get_mod_pre_post, pod_set_mod_pre_post);
static DEVICE_ATTR(modulation_model, S_IWUGO | S_IRUGO, pod_get_modulation_model, pod_set_modulation_model);
static DEVICE_ATTR(band_3_frequency, S_IWUGO | S_IRUGO, pod_get_band_3_frequency, pod_set_band_3_frequency);
static DEVICE_ATTR2(band_4_frequency__bass, band_4_frequency, S_IWUGO | S_IRUGO, pod_get_band_4_frequency__bass, pod_set_band_4_frequency__bass);
static DEVICE_ATTR(mod_param_1_double_precision, S_IWUGO | S_IRUGO, pod_get_mod_param_1_double_precision, pod_set_mod_param_1_double_precision);
static DEVICE_ATTR(delay_param_1_double_precision, S_IWUGO | S_IRUGO, pod_get_delay_param_1_double_precision, pod_set_delay_param_1_double_precision);
static DEVICE_ATTR(eq_enable, S_IWUGO | S_IRUGO, pod_get_eq_enable, pod_set_eq_enable);
static DEVICE_ATTR(tap, S_IWUGO | S_IRUGO, pod_get_tap, pod_set_tap);
static DEVICE_ATTR(volume_tweak_pedal_assign, S_IWUGO | S_IRUGO, pod_get_volume_tweak_pedal_assign, pod_set_volume_tweak_pedal_assign);
static DEVICE_ATTR(band_5_frequency, S_IWUGO | S_IRUGO, pod_get_band_5_frequency, pod_set_band_5_frequency);
static DEVICE_ATTR(tuner, S_IWUGO | S_IRUGO, pod_get_tuner, pod_set_tuner);
static DEVICE_ATTR(mic_selection, S_IWUGO | S_IRUGO, pod_get_mic_selection, pod_set_mic_selection);
static DEVICE_ATTR(cabinet_model, S_IWUGO | S_IRUGO, pod_get_cabinet_model, pod_set_cabinet_model);
static DEVICE_ATTR(stomp_model, S_IWUGO | S_IRUGO, pod_get_stomp_model, pod_set_stomp_model);
static DEVICE_ATTR(roomlevel, S_IWUGO | S_IRUGO, pod_get_roomlevel, pod_set_roomlevel);
static DEVICE_ATTR(band_4_frequency, S_IWUGO | S_IRUGO, pod_get_band_4_frequency, pod_set_band_4_frequency);
static DEVICE_ATTR(band_6_frequency, S_IWUGO | S_IRUGO, pod_get_band_6_frequency, pod_set_band_6_frequency);
static DEVICE_ATTR(stomp_param_1_note_value, S_IWUGO | S_IRUGO, pod_get_stomp_param_1_note_value, pod_set_stomp_param_1_note_value);
static DEVICE_ATTR(stomp_param_2, S_IWUGO | S_IRUGO, pod_get_stomp_param_2, pod_set_stomp_param_2);
static DEVICE_ATTR(stomp_param_3, S_IWUGO | S_IRUGO, pod_get_stomp_param_3, pod_set_stomp_param_3);
static DEVICE_ATTR(stomp_param_4, S_IWUGO | S_IRUGO, pod_get_stomp_param_4, pod_set_stomp_param_4);
static DEVICE_ATTR(stomp_param_5, S_IWUGO | S_IRUGO, pod_get_stomp_param_5, pod_set_stomp_param_5);
static DEVICE_ATTR(stomp_param_6, S_IWUGO | S_IRUGO, pod_get_stomp_param_6, pod_set_stomp_param_6);
static DEVICE_ATTR(amp_switch_select, S_IWUGO | S_IRUGO, pod_get_amp_switch_select, pod_set_amp_switch_select);
static DEVICE_ATTR(delay_param_4, S_IWUGO | S_IRUGO, pod_get_delay_param_4, pod_set_delay_param_4);
static DEVICE_ATTR(delay_param_5, S_IWUGO | S_IRUGO, pod_get_delay_param_5, pod_set_delay_param_5);
static DEVICE_ATTR(delay_pre_post, S_IWUGO | S_IRUGO, pod_get_delay_pre_post, pod_set_delay_pre_post);
static DEVICE_ATTR(delay_model, S_IWUGO | S_IRUGO, pod_get_delay_model, pod_set_delay_model);
static DEVICE_ATTR(delay_verb_model, S_IWUGO | S_IRUGO, pod_get_delay_verb_model, pod_set_delay_verb_model);
static DEVICE_ATTR(tempo_msb, S_IWUGO | S_IRUGO, pod_get_tempo_msb, pod_set_tempo_msb);
static DEVICE_ATTR(tempo_lsb, S_IWUGO | S_IRUGO, pod_get_tempo_lsb, pod_set_tempo_lsb);
static DEVICE_ATTR(wah_model, S_IWUGO | S_IRUGO, pod_get_wah_model, pod_set_wah_model);
static DEVICE_ATTR(bypass_volume, S_IWUGO | S_IRUGO, pod_get_bypass_volume, pod_set_bypass_volume);
static DEVICE_ATTR(fx_loop_on_off, S_IWUGO | S_IRUGO, pod_get_fx_loop_on_off, pod_set_fx_loop_on_off);
static DEVICE_ATTR(tweak_param_select, S_IWUGO | S_IRUGO, pod_get_tweak_param_select, pod_set_tweak_param_select);
static DEVICE_ATTR(amp1_engage, S_IWUGO | S_IRUGO, pod_get_amp1_engage, pod_set_amp1_engage);
static DEVICE_ATTR(band_1_gain, S_IWUGO | S_IRUGO, pod_get_band_1_gain, pod_set_band_1_gain);
static DEVICE_ATTR2(band_2_gain__bass, band_2_gain, S_IWUGO | S_IRUGO, pod_get_band_2_gain__bass, pod_set_band_2_gain__bass);
static DEVICE_ATTR(band_2_gain, S_IWUGO | S_IRUGO, pod_get_band_2_gain, pod_set_band_2_gain);
static DEVICE_ATTR2(band_3_gain__bass, band_3_gain, S_IWUGO | S_IRUGO, pod_get_band_3_gain__bass, pod_set_band_3_gain__bass);
static DEVICE_ATTR(band_3_gain, S_IWUGO | S_IRUGO, pod_get_band_3_gain, pod_set_band_3_gain);
static DEVICE_ATTR2(band_4_gain__bass, band_4_gain, S_IWUGO | S_IRUGO, pod_get_band_4_gain__bass, pod_set_band_4_gain__bass);
static DEVICE_ATTR2(band_5_gain__bass, band_5_gain, S_IWUGO | S_IRUGO, pod_get_band_5_gain__bass, pod_set_band_5_gain__bass);
static DEVICE_ATTR(band_4_gain, S_IWUGO | S_IRUGO, pod_get_band_4_gain, pod_set_band_4_gain);
static DEVICE_ATTR2(band_6_gain__bass, band_6_gain, S_IWUGO | S_IRUGO, pod_get_band_6_gain__bass, pod_set_band_6_gain__bass);
static DEVICE_ATTR(body, S_IRUGO, variax_get_body, line6_nop_write);
static DEVICE_ATTR(pickup1_enable, S_IRUGO, variax_get_pickup1_enable, line6_nop_write);
static DEVICE_ATTR(pickup1_type, S_IRUGO, variax_get_pickup1_type, line6_nop_write);
static DEVICE_ATTR(pickup1_position, S_IRUGO, variax_get_pickup1_position, line6_nop_write);
static DEVICE_ATTR(pickup1_angle, S_IRUGO, variax_get_pickup1_angle, line6_nop_write);
static DEVICE_ATTR(pickup1_level, S_IRUGO, variax_get_pickup1_level, line6_nop_write);
static DEVICE_ATTR(pickup2_enable, S_IRUGO, variax_get_pickup2_enable, line6_nop_write);
static DEVICE_ATTR(pickup2_type, S_IRUGO, variax_get_pickup2_type, line6_nop_write);
static DEVICE_ATTR(pickup2_position, S_IRUGO, variax_get_pickup2_position, line6_nop_write);
static DEVICE_ATTR(pickup2_angle, S_IRUGO, variax_get_pickup2_angle, line6_nop_write);
static DEVICE_ATTR(pickup2_level, S_IRUGO, variax_get_pickup2_level, line6_nop_write);
static DEVICE_ATTR(pickup_phase, S_IRUGO, variax_get_pickup_phase, line6_nop_write);
static DEVICE_ATTR(capacitance, S_IRUGO, variax_get_capacitance, line6_nop_write);
static DEVICE_ATTR(tone_resistance, S_IRUGO, variax_get_tone_resistance, line6_nop_write);
static DEVICE_ATTR(volume_resistance, S_IRUGO, variax_get_volume_resistance, line6_nop_write);
static DEVICE_ATTR(taper, S_IRUGO, variax_get_taper, line6_nop_write);
static DEVICE_ATTR(tone_dump, S_IRUGO, variax_get_tone_dump, line6_nop_write);
static DEVICE_ATTR(save_tone, S_IRUGO, variax_get_save_tone, line6_nop_write);
static DEVICE_ATTR(volume_dump, S_IRUGO, variax_get_volume_dump, line6_nop_write);
static DEVICE_ATTR(tuning_enable, S_IRUGO, variax_get_tuning_enable, line6_nop_write);
static DEVICE_ATTR(tuning6, S_IRUGO, variax_get_tuning6, line6_nop_write);
static DEVICE_ATTR(tuning5, S_IRUGO, variax_get_tuning5, line6_nop_write);
static DEVICE_ATTR(tuning4, S_IRUGO, variax_get_tuning4, line6_nop_write);
static DEVICE_ATTR(tuning3, S_IRUGO, variax_get_tuning3, line6_nop_write);
static DEVICE_ATTR(tuning2, S_IRUGO, variax_get_tuning2, line6_nop_write);
static DEVICE_ATTR(tuning1, S_IRUGO, variax_get_tuning1, line6_nop_write);
static DEVICE_ATTR(detune6, S_IRUGO, variax_get_detune6, line6_nop_write);
static DEVICE_ATTR(detune5, S_IRUGO, variax_get_detune5, line6_nop_write);
static DEVICE_ATTR(detune4, S_IRUGO, variax_get_detune4, line6_nop_write);
static DEVICE_ATTR(detune3, S_IRUGO, variax_get_detune3, line6_nop_write);
static DEVICE_ATTR(detune2, S_IRUGO, variax_get_detune2, line6_nop_write);
static DEVICE_ATTR(detune1, S_IRUGO, variax_get_detune1, line6_nop_write);
static DEVICE_ATTR(mix6, S_IRUGO, variax_get_mix6, line6_nop_write);
static DEVICE_ATTR(mix5, S_IRUGO, variax_get_mix5, line6_nop_write);
static DEVICE_ATTR(mix4, S_IRUGO, variax_get_mix4, line6_nop_write);
static DEVICE_ATTR(mix3, S_IRUGO, variax_get_mix3, line6_nop_write);
static DEVICE_ATTR(mix2, S_IRUGO, variax_get_mix2, line6_nop_write);
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 err;
CHECK_RETURN(device_create_file(dev, &dev_attr_tweak));
CHECK_RETURN(device_create_file(dev, &dev_attr_wah_position));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_compression_gain));
CHECK_RETURN(device_create_file(dev, &dev_attr_vol_pedal_position));
CHECK_RETURN(device_create_file(dev, &dev_attr_compression_threshold));
CHECK_RETURN(device_create_file(dev, &dev_attr_pan));
CHECK_RETURN(device_create_file(dev, &dev_attr_amp_model_setup));
if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_amp_model));
CHECK_RETURN(device_create_file(dev, &dev_attr_drive));
CHECK_RETURN(device_create_file(dev, &dev_attr_bass));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_mid));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_lowmid));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_treble));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_highmid));
CHECK_RETURN(device_create_file(dev, &dev_attr_chan_vol));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_mix));
CHECK_RETURN(device_create_file(dev, &dev_attr_effect_setup));
if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_1_frequency));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_presence));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_treble__bass));
CHECK_RETURN(device_create_file(dev, &dev_attr_noise_gate_enable));
CHECK_RETURN(device_create_file(dev, &dev_attr_gate_threshold));
CHECK_RETURN(device_create_file(dev, &dev_attr_gate_decay_time));
CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_enable));
CHECK_RETURN(device_create_file(dev, &dev_attr_comp_enable));
CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_time));
CHECK_RETURN(device_create_file(dev, &dev_attr_delay_enable));
CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_1));
CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_1));
CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_1_note_value));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_2_frequency__bass));
CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_2));
CHECK_RETURN(device_create_file(dev, &dev_attr_delay_volume_mix));
CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_3));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_enable));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_type));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_decay));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_tone));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_pre_delay));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_reverb_pre_post));
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_2_frequency));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_3_frequency__bass));
CHECK_RETURN(device_create_file(dev, &dev_attr_wah_enable));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_modulation_lo_cut));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_delay_reverb_lo_cut));
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_volume_pedal_minimum));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_eq_pre_post));
CHECK_RETURN(device_create_file(dev, &dev_attr_volume_pre_post));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_di_model));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_di_delay));
CHECK_RETURN(device_create_file(dev, &dev_attr_mod_enable));
CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_1_note_value));
CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_2));
CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_3));
CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_4));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_5));
CHECK_RETURN(device_create_file(dev, &dev_attr_mod_volume_mix));
CHECK_RETURN(device_create_file(dev, &dev_attr_mod_pre_post));
CHECK_RETURN(device_create_file(dev, &dev_attr_modulation_model));
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_3_frequency));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_4_frequency__bass));
CHECK_RETURN(device_create_file(dev, &dev_attr_mod_param_1_double_precision));
CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_1_double_precision));
if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_eq_enable));
CHECK_RETURN(device_create_file(dev, &dev_attr_tap));
CHECK_RETURN(device_create_file(dev, &dev_attr_volume_tweak_pedal_assign));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_5_frequency));
CHECK_RETURN(device_create_file(dev, &dev_attr_tuner));
CHECK_RETURN(device_create_file(dev, &dev_attr_mic_selection));
CHECK_RETURN(device_create_file(dev, &dev_attr_cabinet_model));
CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_model));
CHECK_RETURN(device_create_file(dev, &dev_attr_roomlevel));
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_4_frequency));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_6_frequency));
CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_1_note_value));
CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_2));
CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_3));
CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_4));
CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_5));
CHECK_RETURN(device_create_file(dev, &dev_attr_stomp_param_6));
if((type & (LINE6_BITS_LIVE)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_amp_switch_select));
CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_4));
CHECK_RETURN(device_create_file(dev, &dev_attr_delay_param_5));
CHECK_RETURN(device_create_file(dev, &dev_attr_delay_pre_post));
if((type & (LINE6_BITS_PODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_delay_model));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_delay_verb_model));
CHECK_RETURN(device_create_file(dev, &dev_attr_tempo_msb));
CHECK_RETURN(device_create_file(dev, &dev_attr_tempo_lsb));
if(firmware >= 300) CHECK_RETURN(device_create_file(dev, &dev_attr_wah_model));
if(firmware >= 214) CHECK_RETURN(device_create_file(dev, &dev_attr_bypass_volume));
if((type & (LINE6_BITS_PRO)) != 0) CHECK_RETURN(device_create_file(dev, &dev_attr_fx_loop_on_off));
CHECK_RETURN(device_create_file(dev, &dev_attr_tweak_param_select));
CHECK_RETURN(device_create_file(dev, &dev_attr_amp1_engage));
if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_1_gain));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_2_gain__bass));
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_2_gain));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_3_gain__bass));
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_3_gain));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_4_gain__bass));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_5_gain__bass));
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_4_gain));
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) CHECK_RETURN(device_create_file(dev, &dev_attr_band_6_gain__bass));
return 0;
}
void 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);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_compression_gain);
device_remove_file(dev, &dev_attr_vol_pedal_position);
device_remove_file(dev, &dev_attr_compression_threshold);
device_remove_file(dev, &dev_attr_pan);
device_remove_file(dev, &dev_attr_amp_model_setup);
if(firmware >= 200) device_remove_file(dev, &dev_attr_amp_model);
device_remove_file(dev, &dev_attr_drive);
device_remove_file(dev, &dev_attr_bass);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_mid);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_lowmid);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_treble);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_highmid);
device_remove_file(dev, &dev_attr_chan_vol);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_mix);
device_remove_file(dev, &dev_attr_effect_setup);
if(firmware >= 200) device_remove_file(dev, &dev_attr_band_1_frequency);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_presence);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_treble__bass);
device_remove_file(dev, &dev_attr_noise_gate_enable);
device_remove_file(dev, &dev_attr_gate_threshold);
device_remove_file(dev, &dev_attr_gate_decay_time);
device_remove_file(dev, &dev_attr_stomp_enable);
device_remove_file(dev, &dev_attr_comp_enable);
device_remove_file(dev, &dev_attr_stomp_time);
device_remove_file(dev, &dev_attr_delay_enable);
device_remove_file(dev, &dev_attr_mod_param_1);
device_remove_file(dev, &dev_attr_delay_param_1);
device_remove_file(dev, &dev_attr_delay_param_1_note_value);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_2_frequency__bass);
device_remove_file(dev, &dev_attr_delay_param_2);
device_remove_file(dev, &dev_attr_delay_volume_mix);
device_remove_file(dev, &dev_attr_delay_param_3);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_enable);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_type);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_decay);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_tone);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_pre_delay);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_reverb_pre_post);
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_2_frequency);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_3_frequency__bass);
device_remove_file(dev, &dev_attr_wah_enable);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_modulation_lo_cut);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_delay_reverb_lo_cut);
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_volume_pedal_minimum);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_eq_pre_post);
device_remove_file(dev, &dev_attr_volume_pre_post);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_di_model);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_di_delay);
device_remove_file(dev, &dev_attr_mod_enable);
device_remove_file(dev, &dev_attr_mod_param_1_note_value);
device_remove_file(dev, &dev_attr_mod_param_2);
device_remove_file(dev, &dev_attr_mod_param_3);
device_remove_file(dev, &dev_attr_mod_param_4);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_mod_param_5);
device_remove_file(dev, &dev_attr_mod_volume_mix);
device_remove_file(dev, &dev_attr_mod_pre_post);
device_remove_file(dev, &dev_attr_modulation_model);
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_3_frequency);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_4_frequency__bass);
device_remove_file(dev, &dev_attr_mod_param_1_double_precision);
device_remove_file(dev, &dev_attr_delay_param_1_double_precision);
if(firmware >= 200) device_remove_file(dev, &dev_attr_eq_enable);
device_remove_file(dev, &dev_attr_tap);
device_remove_file(dev, &dev_attr_volume_tweak_pedal_assign);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_5_frequency);
device_remove_file(dev, &dev_attr_tuner);
device_remove_file(dev, &dev_attr_mic_selection);
device_remove_file(dev, &dev_attr_cabinet_model);
device_remove_file(dev, &dev_attr_stomp_model);
device_remove_file(dev, &dev_attr_roomlevel);
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_4_frequency);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_6_frequency);
device_remove_file(dev, &dev_attr_stomp_param_1_note_value);
device_remove_file(dev, &dev_attr_stomp_param_2);
device_remove_file(dev, &dev_attr_stomp_param_3);
device_remove_file(dev, &dev_attr_stomp_param_4);
device_remove_file(dev, &dev_attr_stomp_param_5);
device_remove_file(dev, &dev_attr_stomp_param_6);
if((type & (LINE6_BITS_LIVE)) != 0) device_remove_file(dev, &dev_attr_amp_switch_select);
device_remove_file(dev, &dev_attr_delay_param_4);
device_remove_file(dev, &dev_attr_delay_param_5);
device_remove_file(dev, &dev_attr_delay_pre_post);
if((type & (LINE6_BITS_PODXTALL)) != 0) device_remove_file(dev, &dev_attr_delay_model);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) device_remove_file(dev, &dev_attr_delay_verb_model);
device_remove_file(dev, &dev_attr_tempo_msb);
device_remove_file(dev, &dev_attr_tempo_lsb);
if(firmware >= 300) device_remove_file(dev, &dev_attr_wah_model);
if(firmware >= 214) device_remove_file(dev, &dev_attr_bypass_volume);
if((type & (LINE6_BITS_PRO)) != 0) device_remove_file(dev, &dev_attr_fx_loop_on_off);
device_remove_file(dev, &dev_attr_tweak_param_select);
device_remove_file(dev, &dev_attr_amp1_engage);
if(firmware >= 200) device_remove_file(dev, &dev_attr_band_1_gain);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_2_gain__bass);
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_2_gain);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_3_gain__bass);
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_3_gain);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_4_gain__bass);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_5_gain__bass);
if((type & (LINE6_BITS_PODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_4_gain);
if((type & (LINE6_BITS_BASSPODXTALL)) != 0) if(firmware >= 200) device_remove_file(dev, &dev_attr_band_6_gain__bass);
}
EXPORT_SYMBOL(pod_create_files);
EXPORT_SYMBOL(pod_remove_files);
int variax_create_files(int firmware, int type, struct device *dev) {
int err;
CHECK_RETURN(device_create_file(dev, &dev_attr_body));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_enable));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_type));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_position));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_angle));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup1_level));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_enable));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_type));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_position));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_angle));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup2_level));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_phase));
CHECK_RETURN(device_create_file(dev, &dev_attr_capacitance));
CHECK_RETURN(device_create_file(dev, &dev_attr_tone_resistance));
CHECK_RETURN(device_create_file(dev, &dev_attr_volume_resistance));
CHECK_RETURN(device_create_file(dev, &dev_attr_taper));
CHECK_RETURN(device_create_file(dev, &dev_attr_tone_dump));
CHECK_RETURN(device_create_file(dev, &dev_attr_save_tone));
CHECK_RETURN(device_create_file(dev, &dev_attr_volume_dump));
CHECK_RETURN(device_create_file(dev, &dev_attr_tuning_enable));
CHECK_RETURN(device_create_file(dev, &dev_attr_tuning6));
CHECK_RETURN(device_create_file(dev, &dev_attr_tuning5));
CHECK_RETURN(device_create_file(dev, &dev_attr_tuning4));
CHECK_RETURN(device_create_file(dev, &dev_attr_tuning3));
CHECK_RETURN(device_create_file(dev, &dev_attr_tuning2));
CHECK_RETURN(device_create_file(dev, &dev_attr_tuning1));
CHECK_RETURN(device_create_file(dev, &dev_attr_detune6));
CHECK_RETURN(device_create_file(dev, &dev_attr_detune5));
CHECK_RETURN(device_create_file(dev, &dev_attr_detune4));
CHECK_RETURN(device_create_file(dev, &dev_attr_detune3));
CHECK_RETURN(device_create_file(dev, &dev_attr_detune2));
CHECK_RETURN(device_create_file(dev, &dev_attr_detune1));
CHECK_RETURN(device_create_file(dev, &dev_attr_mix6));
CHECK_RETURN(device_create_file(dev, &dev_attr_mix5));
CHECK_RETURN(device_create_file(dev, &dev_attr_mix4));
CHECK_RETURN(device_create_file(dev, &dev_attr_mix3));
CHECK_RETURN(device_create_file(dev, &dev_attr_mix2));
CHECK_RETURN(device_create_file(dev, &dev_attr_mix1));
CHECK_RETURN(device_create_file(dev, &dev_attr_pickup_wiring));
return 0;
}
void 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);
device_remove_file(dev, &dev_attr_pickup1_type);
device_remove_file(dev, &dev_attr_pickup1_position);
device_remove_file(dev, &dev_attr_pickup1_angle);
device_remove_file(dev, &dev_attr_pickup1_level);
device_remove_file(dev, &dev_attr_pickup2_enable);
device_remove_file(dev, &dev_attr_pickup2_type);
device_remove_file(dev, &dev_attr_pickup2_position);
device_remove_file(dev, &dev_attr_pickup2_angle);
device_remove_file(dev, &dev_attr_pickup2_level);
device_remove_file(dev, &dev_attr_pickup_phase);
device_remove_file(dev, &dev_attr_capacitance);
device_remove_file(dev, &dev_attr_tone_resistance);
device_remove_file(dev, &dev_attr_volume_resistance);
device_remove_file(dev, &dev_attr_taper);
device_remove_file(dev, &dev_attr_tone_dump);
device_remove_file(dev, &dev_attr_save_tone);
device_remove_file(dev, &dev_attr_volume_dump);
device_remove_file(dev, &dev_attr_tuning_enable);
device_remove_file(dev, &dev_attr_tuning6);
device_remove_file(dev, &dev_attr_tuning5);
device_remove_file(dev, &dev_attr_tuning4);
device_remove_file(dev, &dev_attr_tuning3);
device_remove_file(dev, &dev_attr_tuning2);
device_remove_file(dev, &dev_attr_tuning1);
device_remove_file(dev, &dev_attr_detune6);
device_remove_file(dev, &dev_attr_detune5);
device_remove_file(dev, &dev_attr_detune4);
device_remove_file(dev, &dev_attr_detune3);
device_remove_file(dev, &dev_attr_detune2);
device_remove_file(dev, &dev_attr_detune1);
device_remove_file(dev, &dev_attr_mix6);
device_remove_file(dev, &dev_attr_mix5);
device_remove_file(dev, &dev_attr_mix4);
device_remove_file(dev, &dev_attr_mix3);
device_remove_file(dev, &dev_attr_mix2);
device_remove_file(dev, &dev_attr_mix1);
device_remove_file(dev, &dev_attr_pickup_wiring);
}
EXPORT_SYMBOL(variax_create_files);
EXPORT_SYMBOL(variax_remove_files);

View File

@ -0,0 +1,187 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#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.
*/
enum {
POD_tweak = 1,
POD_wah_position = 4,
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_drive = 13,
POD_bass = 14,
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,
POD_reverb_mix = 18, /* device: LINE6_BITS_PODXTALL */
POD_effect_setup = 19,
POD_band_1_frequency = 20, /* firmware: 2.0 */
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,
POD_stomp_enable = 25,
POD_comp_enable = 26,
POD_stomp_time = 27,
POD_delay_enable = 28,
POD_mod_param_1 = 29,
POD_delay_param_1 = 30,
POD_delay_param_1_note_value = 31,
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,
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,
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,
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,
POD_mod_param_5 = 55, /* device: LINE6_BITS_BASSPODXTALL */
POD_mod_volume_mix = 56,
POD_mod_pre_post = 57,
POD_modulation_model = 58,
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,
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,
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,
POD_amp_switch_select = 84, /* device: LINE6_BITS_LIVE */
POD_delay_param_4 = 85,
POD_delay_param_5 = 86,
POD_delay_pre_post = 87,
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 */
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 */
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 */
};
/**
List of Variax workbench controls (dump).
*/
enum {
VARIAX_body = 3,
VARIAX_pickup1_enable = 4, /* 0: enabled, 1: disabled */
VARIAX_pickup1_type = 8,
VARIAX_pickup1_position = 9, /* type: 24 bit float */
VARIAX_pickup1_angle = 12, /* type: 24 bit float */
VARIAX_pickup1_level = 15, /* type: 24 bit float */
VARIAX_pickup2_enable = 18, /* 0: enabled, 1: disabled */
VARIAX_pickup2_type = 22,
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_capacitance = 33, /* type: 24 bit float */
VARIAX_tone_resistance = 36, /* type: 24 bit float */
VARIAX_volume_resistance = 39, /* type: 24 bit float */
VARIAX_taper = 42, /* 0: Linear, 1: Audio */
VARIAX_tone_dump = 43, /* type: 24 bit float */
VARIAX_save_tone = 46,
VARIAX_volume_dump = 47, /* type: 24 bit float */
VARIAX_tuning_enable = 50,
VARIAX_tuning6 = 51,
VARIAX_tuning5 = 52,
VARIAX_tuning4 = 53,
VARIAX_tuning3 = 54,
VARIAX_tuning2 = 55,
VARIAX_tuning1 = 56,
VARIAX_detune6 = 57, /* type: 24 bit float */
VARIAX_detune5 = 60, /* type: 24 bit float */
VARIAX_detune4 = 63, /* type: 24 bit float */
VARIAX_detune3 = 66, /* type: 24 bit float */
VARIAX_detune2 = 69, /* type: 24 bit float */
VARIAX_detune1 = 72, /* type: 24 bit float */
VARIAX_mix6 = 75, /* type: 24 bit float */
VARIAX_mix5 = 78, /* type: 24 bit float */
VARIAX_mix4 = 81, /* type: 24 bit float */
VARIAX_mix3 = 84, /* type: 24 bit float */
VARIAX_mix2 = 87, /* type: 24 bit float */
VARIAX_mix1 = 90, /* type: 24 bit float */
VARIAX_pickup_wiring = 96 /* 0: parallel, 1: series */
};
/**
List of Variax workbench controls (MIDI).
*/
enum {
VARIAXMIDI_volume = 7,
VARIAXMIDI_tone = 79,
};
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);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,190 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef DRIVER_H
#define DRIVER_H
#include "config.h"
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/wait.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
#include <sound/driver.h>
#endif
#include <sound/core.h>
#include "midi.h"
#define DRIVER_NAME "line6usb"
#define LINE6_TIMEOUT 1
#define LINE6_MAX_DEVICES 8
#define LINE6_BUFSIZE_LISTEN 32
#define LINE6_MESSAGE_MAXLEN 256
/*
Line6 MIDI control commands
*/
#define LINE6_PARAM_CHANGE 0xb0
#define LINE6_PROGRAM_CHANGE 0xc0
#define LINE6_SYSEX_BEGIN 0xf0
#define LINE6_SYSEX_END 0xf7
#define LINE6_RESET 0xff
/*
MIDI channel for messages initiated by the host
(and eventually echoed back by the device)
*/
#define LINE6_CHANNEL_HOST 0x00
/*
MIDI channel for messages initiated by the device
*/
#define LINE6_CHANNEL_DEVICE 0x02
#define LINE6_CHANNEL_UNKNOWN 5 /* don't know yet what this is good for */
#define LINE6_CHANNEL_MASK 0x0f
#define MISSING_CASE printk("line6usb driver bug: missing case in %s:%d\n", __FILE__, __LINE__)
#define CHECK_RETURN(x) if((err = x) < 0) return err
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;
/**
Common properties of Line6 devices.
*/
struct line6_properties {
const char *name;
int device_bit;
int capabilities;
};
/**
Common data shared by all Line6 devices.
Corresponds to a pair of USB endpoints.
*/
struct usb_line6 {
/**
USB device.
*/
struct usb_device *usbdev;
/**
Product id.
*/
int product;
/**
Properties.
*/
const struct line6_properties *properties;
/**
Interface number.
*/
int interface_number;
/**
Interval (ms).
*/
int interval;
/**
Maximum size of USB packet.
*/
int max_packet_size;
/**
Device representing the USB interface.
*/
struct device *ifcdev;
/**
Line6 sound card data structure.
Each device has at least MIDI or PCM.
*/
struct snd_card *card;
/**
Line6 PCM device data structure.
*/
struct snd_line6_pcm *line6pcm;
/**
Line6 MIDI device data structure.
*/
struct snd_line6_midi *line6midi;
/**
USB endpoint for listening to control commands.
*/
int ep_control_read;
/**
USB endpoint for writing control commands.
*/
int ep_control_write;
/**
URB for listening to PODxt Pro control endpoint.
*/
struct urb *urb_listen;
/**
Buffer for listening to PODxt Pro control endpoint.
*/
unsigned char *buffer_listen;
/**
Buffer for message to be processed.
*/
unsigned char *buffer_message;
/**
Length of message to be processed.
*/
int message_length;
};
extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, int size);
extern ssize_t line6_nop_read(struct device *dev, DEVICE_ATTRIBUTE char *buf);
extern ssize_t line6_nop_write(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count);
extern int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t datalen);
extern int line6_read_serial_number(struct usb_line6 *line6, int *serial_number);
extern int line6_send_program(struct usb_line6 *line6, int value);
extern int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, int size);
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 ssize_t line6_set_raw(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count);
extern int line6_transmit_parameter(struct usb_line6 *line6, int param, int value);
extern int line6_write_data(struct usb_line6 *line6, int address, void *data, size_t datalen);
extern void line6_write_hexdump(struct usb_line6 *line6, char dir, const unsigned char *buffer, int size);
#endif

View File

@ -0,0 +1,143 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#include "driver.h"
#include "dumprequest.h"
/*
Set "dump in progress" flag.
*/
void line6_dump_started(struct line6_dump_request *l6dr, int dest)
{
l6dr->in_progress = dest;
}
/*
Invalidate current channel, i.e., set "dump in progress" flag.
Reading from the "dump" special file blocks until dump is completed.
*/
void line6_invalidate_current(struct line6_dump_request *l6dr)
{
line6_dump_started(l6dr, LINE6_DUMP_CURRENT);
}
/*
Clear "dump in progress" flag and notify waiting processes.
*/
void line6_dump_finished(struct line6_dump_request *l6dr)
{
l6dr->in_progress = LINE6_DUMP_NONE;
wake_up_interruptible(&l6dr->wait);
}
/*
Send an asynchronous channel dump request.
*/
int line6_dump_request_async(struct line6_dump_request *l6dr, struct usb_line6 *line6, int num)
{
int ret;
line6_invalidate_current(l6dr);
ret = line6_send_raw_message_async(line6, l6dr->reqbufs[num].buffer, l6dr->reqbufs[num].length);
if(ret < 0)
line6_dump_finished(l6dr);
return ret;
}
/*
Send an asynchronous dump request after a given interval.
*/
void line6_startup_delayed(struct line6_dump_request *l6dr, int seconds,
void (*function)(unsigned long), void *data)
{
l6dr->timer.expires = jiffies + seconds * HZ;
l6dr->timer.function = function;
l6dr->timer.data = (unsigned long)data;
add_timer(&l6dr->timer);
}
/*
Wait for completion.
*/
int line6_wait_dump(struct line6_dump_request *l6dr, int nonblock)
{
int retval = 0;
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&l6dr->wait, &wait);
current->state = TASK_INTERRUPTIBLE;
while(l6dr->in_progress) {
if(nonblock) {
retval = -EAGAIN;
break;
}
if(signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
else
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&l6dr->wait, &wait);
return retval;
}
/*
Initialize dump request buffer.
*/
int line6_dumpreq_initbuf(struct line6_dump_request *l6dr, const void *buf, size_t len, int num)
{
l6dr->reqbufs[num].buffer = kmalloc(len, GFP_KERNEL);
if(l6dr->reqbufs[num].buffer == NULL) return -ENOMEM;
memcpy(l6dr->reqbufs[num].buffer, buf, len);
l6dr->reqbufs[num].length = len;
return 0;
}
/*
Initialize dump request data structure (including one buffer).
*/
int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf, size_t len)
{
int ret;
ret = line6_dumpreq_initbuf(l6dr, buf, len, 0);
if(ret < 0) return ret;
init_waitqueue_head(&l6dr->wait);
init_timer(&l6dr->timer);
return 0;
}
/*
Destruct dump request data structure.
*/
void line6_dumpreq_destructbuf(struct line6_dump_request *l6dr, int num)
{
if(l6dr == NULL) return;
if(l6dr->reqbufs[num].buffer == NULL) return;
kfree(l6dr->reqbufs[num].buffer);
l6dr->reqbufs[num].buffer = NULL;
}
/*
Destruct dump request data structure.
*/
void line6_dumpreq_destruct(struct line6_dump_request *l6dr)
{
if(l6dr->reqbufs[0].buffer == NULL) return;
line6_dumpreq_destructbuf(l6dr, 0);
l6dr->ok = 1;
del_timer_sync(&l6dr->timer);
}

View File

@ -0,0 +1,87 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef DUMPREQUEST_H
#define DUMPREQUEST_H
#include <linux/usb.h>
#include <linux/wait.h>
#include <sound/core.h>
enum {
LINE6_DUMP_NONE,
LINE6_DUMP_CURRENT
};
struct line6_dump_reqbuf {
/**
Buffer for dump requests.
*/
unsigned char *buffer;
/**
Size of dump request.
*/
size_t length;
};
/**
Provides the functionality to request channel/model/... dump data from a
Line6 device.
*/
struct line6_dump_request {
/**
Wait queue for access to program dump data.
*/
wait_queue_head_t wait;
/**
Indicates an unfinished program dump request.
0: no dump
1: dump current settings
Other device-specific values are also allowed.
*/
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
*/
struct line6_dump_reqbuf reqbufs[1];
};
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);
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);
extern int line6_dumpreq_init(struct line6_dump_request *l6dr, const void *buf, size_t len);
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);
#endif

View File

@ -0,0 +1,398 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#include "driver.h"
#include <linux/usb.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
#include "audio.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))
static int send_midi_async(struct usb_line6 *line6, unsigned char *data, int length);
/*
Pass data received via USB to MIDI.
*/
void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, int length)
{
if(line6->line6midi->substream_receive)
snd_rawmidi_receive(line6->line6midi->substream_receive, data, length);
}
/*
Read data from MIDI buffer and transmit them via USB.
*/
static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
{
struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6;
struct snd_line6_midi *line6midi = line6->line6midi;
struct MidiBuffer *mb = &line6midi->midibuf_out;
unsigned long flags;
unsigned char chunk[line6->max_packet_size];
int req, done;
spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags);
for(;;) {
req = min(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
line6_write_hexdump(line6, 's', chunk, done);
#endif
midibuf_write(mb, chunk, done);
snd_rawmidi_transmit_ack(substream, done);
}
for(;;) {
done = midibuf_read(mb, chunk, line6->max_packet_size);
if(done == 0)
break;
if(midibuf_skip_message(mb, line6midi->midi_mask_transmit))
continue;
send_midi_async(line6, chunk, done);
}
spin_unlock_irqrestore(&line6->line6midi->midi_transmit_lock, flags);
}
/*
Notification of completion of MIDI transmission.
*/
static void midi_sent(struct urb *urb PT_REGS)
{
unsigned long flags;
int status;
int num;
struct usb_line6 *line6 = (struct usb_line6 *)urb->context;
status = urb->status;
kfree(urb->transfer_buffer);
usb_free_urb(urb);
if(status == -ESHUTDOWN)
return;
spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags);
num = --line6->line6midi->num_active_send_urbs;
if(num == 0) {
line6_midi_transmit(line6->line6midi->substream_transmit);
num = line6->line6midi->num_active_send_urbs;
}
if(num == 0)
wake_up_interruptible(&line6->line6midi->send_wait);
spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags);
}
/*
Send an asynchronous MIDI message.
Assumes that line6->line6midi->send_urb_lock is held
(i.e., this function is serialized).
*/
static int send_midi_async(struct usb_line6 *line6, unsigned char *data, int length)
{
struct urb *urb;
int retval;
unsigned char *transfer_buffer;
urb = usb_alloc_urb(0, GFP_ATOMIC);
if(urb == 0) {
dev_err(line6->ifcdev, "Out of memory\n");
return -ENOMEM;
}
#if DO_DUMP_URB_SEND
line6_write_hexdump(line6, 'S', data, length);
#endif
transfer_buffer = (unsigned char *)kmalloc(length, GFP_ATOMIC);
if(transfer_buffer == 0) {
usb_free_urb(urb);
dev_err(line6->ifcdev, "Out of memory\n");
return -ENOMEM;
}
memcpy(transfer_buffer, data, length);
usb_fill_int_urb(urb,
line6->usbdev,
usb_sndbulkpipe(line6->usbdev, line6->ep_control_write),
transfer_buffer, length, midi_sent, line6, line6->interval);
urb->actual_length = 0;
retval = usb_submit_urb(urb, GFP_ATOMIC);
if(retval < 0) {
dev_err(line6->ifcdev, "usb_submit_urb failed\n");
usb_free_urb(urb);
return -EINVAL;
}
++line6->line6midi->num_active_send_urbs;
switch(line6->usbdev->descriptor.idProduct) {
case LINE6_DEVID_BASSPODXT:
case LINE6_DEVID_BASSPODXTLIVE:
case LINE6_DEVID_BASSPODXTPRO:
case LINE6_DEVID_PODXT:
case LINE6_DEVID_PODXTLIVE:
case LINE6_DEVID_PODXTPRO:
case LINE6_DEVID_POCKETPOD:
pod_midi_postprocess((struct usb_line6_pod *)line6, data, length);
break;
default:
MISSING_CASE;
}
return 0;
}
static int line6_midi_output_open(struct snd_rawmidi_substream *substream)
{
return 0;
}
static int line6_midi_output_close(struct snd_rawmidi_substream *substream)
{
return 0;
}
static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
unsigned long flags;
struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6;
line6->line6midi->substream_transmit = substream;
spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags);
if(line6->line6midi->num_active_send_urbs == 0)
line6_midi_transmit(substream);
spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags);
}
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);
}
static int line6_midi_input_open(struct snd_rawmidi_substream *substream)
{
return 0;
}
static int line6_midi_input_close(struct snd_rawmidi_substream *substream)
{
return 0;
}
static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6;
if(up)
line6->line6midi->substream_receive = substream;
else
line6->line6midi->substream_receive = 0;
}
static struct snd_rawmidi_ops line6_midi_output_ops = {
.open = line6_midi_output_open,
.close = line6_midi_output_close,
.trigger = line6_midi_output_trigger,
.drain = line6_midi_output_drain,
};
static struct snd_rawmidi_ops line6_midi_input_ops = {
.open = line6_midi_input_open,
.close = line6_midi_input_close,
.trigger = line6_midi_input_trigger,
};
/*
Cleanup the Line6 MIDI device.
*/
static void line6_cleanup_midi(struct snd_rawmidi *rmidi)
{
}
/* Create a MIDI device */
static int snd_line6_new_midi(struct snd_line6_midi *line6midi)
{
struct snd_rawmidi *rmidi;
int err;
if((err = snd_rawmidi_new(line6midi->line6->card, "Line6 MIDI", 0, 1, 1, &rmidi)) < 0)
return err;
rmidi->private_data = line6midi;
rmidi->private_free = line6_cleanup_midi;
strcpy(rmidi->name, line6midi->line6->properties->name);
rmidi->info_flags =
SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &line6_midi_output_ops);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &line6_midi_input_ops);
return 0;
}
/*
"read" request on "midi_mask_transmit" special file.
*/
static ssize_t midi_get_midi_mask_transmit(struct device *dev, DEVICE_ATTRIBUTE char *buf)
{
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6 *line6 = usb_get_intfdata(interface);
return sprintf(buf, "%d\n", line6->line6midi->midi_mask_transmit);
}
/*
"write" request on "midi_mask" special file.
*/
static ssize_t midi_set_midi_mask_transmit(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count)
{
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6 *line6 = usb_get_intfdata(interface);
int value = simple_strtoul(buf, NULL, 10);
line6->line6midi->midi_mask_transmit = value;
return count;
}
/*
"read" request on "midi_mask_receive" special file.
*/
static ssize_t midi_get_midi_mask_receive(struct device *dev, DEVICE_ATTRIBUTE char *buf)
{
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6 *line6 = usb_get_intfdata(interface);
return sprintf(buf, "%d\n", line6->line6midi->midi_mask_receive);
}
/*
"write" request on "midi_mask" special file.
*/
static ssize_t midi_set_midi_mask_receive(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count)
{
struct usb_interface *interface = to_usb_interface(dev);
struct usb_line6 *line6 = usb_get_intfdata(interface);
int value = simple_strtoul(buf, NULL, 10);
line6->line6midi->midi_mask_receive = value;
return count;
}
static DEVICE_ATTR(midi_mask_transmit, S_IWUGO | S_IRUGO, midi_get_midi_mask_transmit, midi_set_midi_mask_transmit);
static DEVICE_ATTR(midi_mask_receive, S_IWUGO | S_IRUGO, midi_get_midi_mask_receive, midi_set_midi_mask_receive);
/* MIDI device destructor */
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);
return 0;
}
/*
Initialize the Line6 MIDI subsystem.
*/
int line6_init_midi(struct usb_line6 *line6)
{
static struct snd_device_ops midi_ops = {
.dev_free = snd_line6_midi_free,
};
int err;
struct snd_line6_midi *line6midi;
if(!(line6->properties->capabilities & LINE6_BIT_CONTROL))
return 0; /* skip MIDI initialization and report success */
line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL);
if(line6midi == NULL)
return -ENOMEM;
err = midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);
if(err < 0)
return err;
err = midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1);
if(err < 0)
return err;
line6midi->line6 = line6;
line6midi->midi_mask_transmit = 1;
line6midi->midi_mask_receive = 4;
line6->line6midi = line6midi;
if((err = snd_device_new(line6->card, SNDRV_DEV_RAWMIDI, line6midi, &midi_ops)) < 0)
return err;
snd_card_set_dev(line6->card, line6->ifcdev);
if((err = snd_line6_new_midi(line6midi)) < 0)
return err;
if((err = device_create_file(line6->ifcdev, &dev_attr_midi_mask_transmit)) < 0)
return err;
if((err = device_create_file(line6->ifcdev, &dev_attr_midi_mask_receive)) < 0)
return err;
init_waitqueue_head(&line6midi->send_wait);
spin_lock_init(&line6midi->send_urb_lock);
spin_lock_init(&line6midi->midi_transmit_lock);
return 0;
}

View File

@ -0,0 +1,87 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef MIDI_H
#define MIDI_H
#include <sound/rawmidi.h>
#include "midibuf.h"
#define MIDI_BUFFER_SIZE 1024
struct snd_line6_midi
{
/**
Pointer back to the Line6 driver data structure.
*/
struct usb_line6 *line6;
/**
MIDI substream for receiving (or NULL if not active).
*/
struct snd_rawmidi_substream *substream_receive;
/**
MIDI substream for transmitting (or NULL if not active).
*/
struct snd_rawmidi_substream *substream_transmit;
/**
Number of currently active MIDI send URBs.
*/
int num_active_send_urbs;
/**
Spin lock to protect updates of send_urb.
*/
spinlock_t send_urb_lock;
/**
Spin lock to protect MIDI buffer handling.
*/
spinlock_t midi_transmit_lock;
/**
Wait queue for MIDI transmission.
*/
wait_queue_head_t send_wait;
/**
Bit mask for output MIDI channels.
*/
int midi_mask_transmit;
/**
Bit mask for input MIDI channels.
*/
int midi_mask_receive;
/**
Buffer for incoming MIDI stream.
*/
struct MidiBuffer midibuf_in;
/**
Buffer for outgoing MIDI stream.
*/
struct MidiBuffer midibuf_out;
};
extern int line6_init_midi(struct usb_line6 *line6);
extern void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, int length);
#endif

View File

@ -0,0 +1,268 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#include "config.h"
#include <linux/slab.h>
#include "midibuf.h"
int midibuf_message_length(unsigned char code)
{
if(code < 0x80)
return -1;
else if(code < 0xf0) {
static const int length[] = { 3, 3, 3, 3, 2, 2, 3 };
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.
*/
static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1 };
return length[code & 0x0f];
}
}
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 = (unsigned char *)kmalloc(size, GFP_KERNEL);
if(this->buf == 0)
return -ENOMEM;
this->size = size;
this->split = split;
midibuf_reset(this);
return 0;
}
void midibuf_status(struct MidiBuffer *this)
{
printk("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 midibuf_is_empty(struct MidiBuffer *this)
{
return (this->pos_read == this->pos_write) && !this->full;
}
int midibuf_is_full(struct MidiBuffer *this)
{
return this->full;
}
int midibuf_bytes_free(struct MidiBuffer *this)
{
return
midibuf_is_full(this) ?
0 :
(this->pos_read - this->pos_write + this->size - 1) % this->size + 1;
}
int midibuf_bytes_used(struct MidiBuffer *this)
{
return
midibuf_is_empty(this) ?
0 :
(this->pos_write - this->pos_read + this->size - 1) % this->size + 1;
}
int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
{
int bytes_free;
int length1, length2;
int skip_active_sense = 0;
if(midibuf_is_full(this) || (length <= 0))
return 0;
/* skip trailing active sense */
if(data[length - 1] == 0xfe) {
--length;
skip_active_sense = 1;
}
bytes_free = midibuf_bytes_free(this);
if(length > bytes_free)
length = bytes_free;
if(length > 0) {
length1 = this->size - this->pos_write;
if(length < length1) {
/* no buffer wraparound */
memcpy(this->buf + this->pos_write, data, length);
this->pos_write += length;
}
else {
/* buffer wraparound */
length2 = length - length1;
memcpy(this->buf + this->pos_write, data, length1);
memcpy(this->buf, data + length1, length2);
this->pos_write = length2;
}
if(this->pos_write == this->pos_read)
this->full = 1;
}
return length + skip_active_sense;
}
int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
{
int bytes_used;
int length1, length2;
int command;
int midi_length;
int repeat = 0;
int i;
if(length < 3)
return -EINVAL; /* we need to be able to store at least a 3 byte MIDI message */
if(midibuf_is_empty(this))
return 0;
bytes_used = midibuf_bytes_used(this);
if(length > bytes_used)
length = bytes_used;
length1 = this->size - this->pos_read;
/* check MIDI command length */
command = this->buf[this->pos_read];
if(command & 0x80) {
midi_length = midibuf_message_length(command);
this->command_prev = command;
}
else {
if(this->command_prev > 0) {
int midi_length_prev = midibuf_message_length(this->command_prev);
if(midi_length_prev > 0) {
midi_length = midi_length_prev - 1;
repeat = 1;
}
else
midi_length = -1;
}
else
midi_length = -1;
}
if(midi_length < 0) {
/* search for end of message */
if(length < length1) {
/* no buffer wraparound */
for(i = 1; i < length; ++i)
if(this->buf[this->pos_read + i] & 0x80)
break;
midi_length = i;
}
else {
/* buffer wraparound */
length2 = length - length1;
for(i = 1; i < length1; ++i)
if(this->buf[this->pos_read + i] & 0x80)
break;
if(i < length1)
midi_length = i;
else {
for(i = 0; i < length2; ++i)
if(this->buf[i] & 0x80)
break;
midi_length = length1 + i;
}
}
if(midi_length == length)
midi_length = -1; /* end of message not found */
}
if(midi_length < 0) {
if(!this->split)
return 0; /* command is not yet complete */
}
else {
if(length < midi_length)
return 0; /* command is not yet complete */
length = midi_length;
}
if(length < length1) {
/* no buffer wraparound */
memcpy(data + repeat, this->buf + this->pos_read, length);
this->pos_read += length;
}
else {
/* buffer wraparound */
length2 = length - length1;
memcpy(data + repeat, this->buf + this->pos_read, length1);
memcpy(data + repeat + length1, this->buf, length2);
this->pos_read = length2;
}
if(repeat)
data[0] = this->command_prev;
this->full = 0;
return length + repeat;
}
int midibuf_ignore(struct MidiBuffer *this, int length)
{
int bytes_used = midibuf_bytes_used(this);
if(length > bytes_used)
length = bytes_used;
this->pos_read = (this->pos_read + length) % this->size;
this->full = 0;
return length;
}
int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
{
int cmd = this->command_prev;
if((cmd >= 0x80) && (cmd < 0xf0))
if((mask & (1 << (cmd & 0x0f))) == 0)
return 1;
return 0;
}
void midibuf_destroy(struct MidiBuffer *this)
{
if(this->buf != 0) {
kfree(this->buf);
this->buf = 0;
}
}

View File

@ -0,0 +1,39 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef MIDIBUF_H
#define MIDIBUF_H
struct MidiBuffer
{
unsigned char *buf;
int size;
int split;
int pos_read, pos_write;
int full;
int command_prev;
};
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);
#endif

View File

@ -0,0 +1,289 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#include "driver.h"
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "audio.h"
#include "capture.h"
#include "playback.h"
#include "pod.h"
/* trigger callback */
int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
struct list_head *pos;
#endif
struct snd_pcm_substream *s;
int err;
unsigned long flags;
spin_lock_irqsave(&line6pcm->lock_trigger, flags);
clear_bit(BIT_PREPARED, &line6pcm->flags);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
snd_pcm_group_for_each(pos, substream) {
s = snd_pcm_group_substream_entry(pos);
#else
snd_pcm_group_for_each_entry(s, substream) {
#endif
switch(s->stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
err = snd_line6_playback_trigger(s, cmd);
if(err < 0) {
spin_unlock_irqrestore(&line6pcm->lock_trigger, flags);
return err;
}
break;
case SNDRV_PCM_STREAM_CAPTURE:
err = snd_line6_capture_trigger(s, cmd);
if(err < 0) {
spin_unlock_irqrestore(&line6pcm->lock_trigger, flags);
return err;
}
break;
default:
dev_err(s2m(substream), "Unknown stream direction %d\n", s->stream);
}
}
spin_unlock_irqrestore(&line6pcm->lock_trigger, flags);
return 0;
}
/* control info callback */
static int snd_line6_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 256;
return 0;
}
/* control get callback */
static int snd_line6_control_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];
return 0;
}
/* control put callback */
static int snd_line6_control_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];
changed = 1;
}
return changed;
}
/* control definition */
static struct snd_kcontrol_new line6_control = {
.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
};
/*
Cleanup the PCM device.
*/
static void line6_cleanup_pcm(struct snd_pcm *pcm)
{
int i;
struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm);
for(i = LINE6_ISO_BUFFERS; i--;) {
if(line6pcm->urb_audio_out[i]) {
usb_kill_urb(line6pcm->urb_audio_out[i]);
usb_free_urb(line6pcm->urb_audio_out[i]);
}
if(line6pcm->urb_audio_in[i]) {
usb_kill_urb(line6pcm->urb_audio_in[i]);
usb_free_urb(line6pcm->urb_audio_in[i]);
}
}
}
/* create a PCM device */
static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm)
{
struct snd_pcm *pcm;
int err;
if((err = snd_pcm_new(line6pcm->line6->card, (char *)line6pcm->line6->properties->name, 0, 1, 1, &pcm)) < 0)
return err;
pcm->private_data = line6pcm;
pcm->private_free = line6_cleanup_pcm;
line6pcm->pcm = pcm;
strcpy(pcm->name, line6pcm->line6->properties->name);
/* 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);
/* pre-allocation of buffers */
snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
64 * 1024, 128 * 1024);
return 0;
}
/* PCM device destructor */
static int snd_line6_pcm_free(struct snd_device *device)
{
return 0;
}
/*
Create and register the PCM device and mixer entries.
Create URBs for playback and capture.
*/
int line6_init_pcm(struct usb_line6 *line6, struct line6_pcm_properties *properties)
{
static struct snd_device_ops pcm_ops = {
.dev_free = snd_line6_pcm_free,
};
int err;
int ep_read = 0, ep_write = 0;
struct snd_line6_pcm *line6pcm;
if(!(line6->properties->capabilities & LINE6_BIT_PCM))
return 0; /* skip PCM initialization and report success */
/* initialize PCM subsystem based on product id: */
switch(line6->product) {
case LINE6_DEVID_BASSPODXT:
case LINE6_DEVID_BASSPODXTLIVE:
case LINE6_DEVID_BASSPODXTPRO:
case LINE6_DEVID_PODXT:
case LINE6_DEVID_PODXTLIVE:
case LINE6_DEVID_PODXTPRO:
ep_read = 0x82;
ep_write = 0x01;
break;
case LINE6_DEVID_PODX3:
case LINE6_DEVID_PODX3LIVE:
ep_read = 0x86;
ep_write = 0x02;
break;
case LINE6_DEVID_POCKETPOD:
ep_read = 0x82;
ep_write = 0x02;
break;
case LINE6_DEVID_GUITARPORT:
case LINE6_DEVID_TONEPORT_GX:
ep_read = 0x82;
ep_write = 0x01;
break;
case LINE6_DEVID_TONEPORT_UX1:
ep_read = 0x00;
ep_write = 0x00;
break;
case LINE6_DEVID_TONEPORT_UX2:
ep_read = 0x87;
ep_write = 0x00;
break;
default:
MISSING_CASE;
}
line6pcm = kzalloc(sizeof(struct snd_line6_pcm), GFP_KERNEL);
if(line6pcm == NULL)
return -ENOMEM;
line6pcm->volume[0] = line6pcm->volume[1] = 128;
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, ep_read), 0);
line6pcm->properties = properties;
line6->line6pcm = line6pcm;
/* PCM device: */
if((err = snd_device_new(line6->card, SNDRV_DEV_PCM, line6, &pcm_ops)) < 0)
return err;
snd_card_set_dev(line6->card, line6->ifcdev);
if((err = snd_line6_new_pcm(line6pcm)) < 0)
return err;
spin_lock_init(&line6pcm->lock_audio_out);
spin_lock_init(&line6pcm->lock_audio_in);
spin_lock_init(&line6pcm->lock_trigger);
if((err = create_audio_out_urbs(line6pcm)) < 0)
return err;
if((err = create_audio_in_urbs(line6pcm)) < 0)
return err;
/* mixer: */
if((err = snd_ctl_add(line6->card, snd_ctl_new1(&line6_control, line6pcm))) < 0)
return err;
return 0;
}
/* prepare pcm callback */
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->pos_out = 0;
line6pcm->pos_out_done = 0;
unlink_wait_clear_audio_in_urbs(line6pcm);
line6pcm->bytes_out = 0;
line6pcm->pos_in_done = 0;
line6pcm->bytes_in = 0;
}
return 0;
}

View File

@ -0,0 +1,210 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
/*
PCM interface to POD series devices.
*/
#ifndef PCM_H
#define PCM_H
#include <sound/pcm.h>
#include "driver.h"
#include "usbdefs.h"
#define LINE6_ISO_BUFFERS 8 /* number of URBs */
#define LINE6_ISO_PACKETS 2 /* number of USB frames per URB */
#define LINE6_ISO_INTERVAL 1 /* in a "full speed" device (such as the PODxt Pro) this means 1ms */
#define LINE6_ISO_PACKET_SIZE_MAX 252 /* this should be queried dynamically from the USB interface! */
/*
Extract the messaging device from the substream instance
*/
#define s2m(s) (((struct snd_line6_pcm *)snd_pcm_substream_chip(s))->line6->ifcdev)
enum {
BIT_RUNNING_PLAYBACK,
BIT_RUNNING_CAPTURE,
BIT_PAUSE_PLAYBACK,
BIT_PREPARED
};
struct line6_pcm_properties {
struct snd_pcm_hardware snd_line6_playback_hw, snd_line6_capture_hw;
struct snd_pcm_hw_constraint_ratdens snd_line6_rates;
int bytes_per_frame;
};
struct snd_line6_pcm
{
/**
Pointer back to the Line6 driver data structure.
*/
struct usb_line6 *line6;
/**
Properties.
*/
struct line6_pcm_properties *properties;
/**
ALSA pcm stream
*/
struct snd_pcm *pcm;
/**
URBs for audio playback.
*/
struct urb *urb_audio_out[LINE6_ISO_BUFFERS];
/**
URBs for audio capture.
*/
struct urb *urb_audio_in[LINE6_ISO_BUFFERS];
/**
Temporary buffer to hold data when playback buffer wraps.
*/
unsigned char *wrap_out;
/**
Temporary buffer for capture.
Since the packet size is not known in advance, this buffer is large enough
to store maximum size packets.
*/
unsigned char *buffer_in;
/**
Free frame position in the playback buffer.
*/
snd_pcm_uframes_t pos_out;
/**
Count processed bytes for playback.
This is modulo period size (to determine when a period is finished).
*/
unsigned bytes_out;
/**
Counter to create desired playback sample rate.
*/
unsigned count_out;
/**
Playback period size in bytes
*/
unsigned period_out;
/**
Processed frame position in the playback buffer.
The contents of the output ring buffer have been consumed by the USB
subsystem (i.e., sent to the USB device) up to this position.
*/
snd_pcm_uframes_t pos_out_done;
/**
Count processed bytes for capture.
This is modulo period size (to determine when a period is finished).
*/
unsigned bytes_in;
/**
Counter to create desired capture sample rate.
*/
unsigned count_in;
/**
Capture period size in bytes
*/
unsigned period_in;
/**
Processed frame position in the capture buffer.
The contents of the output ring buffer have been consumed by the USB
subsystem (i.e., sent to the USB device) up to this position.
*/
snd_pcm_uframes_t pos_in_done;
/**
Bit mask of active playback URBs.
*/
unsigned long active_urb_out;
/**
Maximum size of USB packet.
*/
int max_packet_size;
/**
USB endpoint for listening to audio data.
*/
int ep_audio_read;
/**
USB endpoint for writing audio data.
*/
int ep_audio_write;
/**
Bit mask of active capture URBs.
*/
unsigned long active_urb_in;
/**
Bit mask of playback URBs currently being unlinked.
*/
unsigned long unlink_urb_out;
/**
Bit mask of capture URBs currently being unlinked.
*/
unsigned long unlink_urb_in;
/**
Spin lock to protect updates of the playback buffer positions (not
contents!)
*/
spinlock_t lock_audio_out;
/**
Spin lock to protect updates of the capture buffer positions (not
contents!)
*/
spinlock_t lock_audio_in;
/**
Spin lock to protect trigger.
*/
spinlock_t lock_trigger;
/**
PCM playback volume (left and right).
*/
int volume[2];
/**
Several status bits (see BIT_*).
*/
unsigned long flags;
};
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);
#endif

View File

@ -0,0 +1,428 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#include "driver.h"
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "audio.h"
#include "pcm.h"
#include "pod.h"
/*
Software stereo volume control.
*/
static void change_volume(struct urb *urb_out, int volume[], int bytes_per_frame)
{
int chn = 0;
if(volume[0] == 256 && volume[1] == 256)
return; /* maximum volume - no change */
if(bytes_per_frame == 4) {
short *p, *buf_end;
p = (short *)urb_out->transfer_buffer;
buf_end = p + urb_out->transfer_buffer_length / sizeof(*p);
for(; p < buf_end; ++p) {
*p = (*p * volume[chn & 1]) >> 8;
++chn;
}
}
else if(bytes_per_frame == 6) {
unsigned char *p, *buf_end;
p = (unsigned char *)urb_out->transfer_buffer;
buf_end = p + urb_out->transfer_buffer_length;
for(; p < buf_end; p += 3) {
int val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16);
val = (val * volume[chn & 1]) >> 8;
p[0] = val;
p[1] = val >> 8;
p[2] = val >> 16;
++chn;
}
}
}
/*
Find a free URB, prepare audio data, and submit URB.
*/
static int submit_audio_out_urb(struct snd_pcm_substream *substream)
{
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);
index = find_first_zero_bit(&line6pcm->active_urb_out, LINE6_ISO_BUFFERS);
if(index < 0 || index >= LINE6_ISO_BUFFERS) {
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
dev_err(s2m(substream), "no free URB found\n");
return -EINVAL;
}
urb_out = line6pcm->urb_audio_out[index];
urb_size = 0;
for(i = 0; i < LINE6_ISO_PACKETS; ++i) {
/* compute frame size for given sampling rate */
int n, fs;
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;
fout->offset = urb_size;
fout->length = fs;
urb_size += fs;
}
urb_frames = urb_size / bytes_per_frame;
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,
copy the data to the temp buffer.
*/
int len;
len = runtime->buffer_size - line6pcm->pos_out;
urb_out->transfer_buffer = line6pcm->wrap_out;
if(len > 0) {
memcpy(line6pcm->wrap_out, runtime->dma_area + line6pcm->pos_out * bytes_per_frame, len * bytes_per_frame);
memcpy(line6pcm->wrap_out + len * bytes_per_frame, runtime->dma_area, (urb_frames - len) * bytes_per_frame);
}
else
dev_err(s2m(substream), "driver bug: len = %d\n", len); /* this is somewhat paranoid */
}
else {
/* set the buffer pointer */
urb_out->transfer_buffer = runtime->dma_area + line6pcm->pos_out * bytes_per_frame;
}
}
if((line6pcm->pos_out += urb_frames) >= runtime->buffer_size)
line6pcm->pos_out -= runtime->buffer_size;
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
for(i = 0; i < LINE6_ISO_PACKETS; ++i) {
struct usb_iso_packet_descriptor *fout = &urb_out->iso_frame_desc[i];
line6_write_hexdump(line6pcm->line6, 'P', urb_out->transfer_buffer + fout->offset, fout->length);
}
#endif
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);
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
return 0;
}
/*
Submit all currently available playback URBs.
*/
static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream)
{
int ret, i;
for(i = 0; i < LINE6_ISO_BUFFERS; ++i)
if((ret = submit_audio_out_urb(substream)) < 0)
return ret;
return 0;
}
/*
Unlink all currently active playback URBs.
*/
static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
{
unsigned int i;
for(i = LINE6_ISO_BUFFERS; i--;) {
if(test_bit(i, &line6pcm->active_urb_out)) {
if(!test_and_set_bit(i, &line6pcm->unlink_urb_out)) {
struct urb *u = line6pcm->urb_audio_out[i];
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
u->transfer_flags |= URB_ASYNC_UNLINK;
#endif
usb_unlink_urb(u);
}
}
}
}
/*
Wait until unlinking of all currently active playback URBs has been finished.
*/
static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
{
int timeout = HZ;
unsigned int i;
int alive;
do {
alive = 0;
for (i = LINE6_ISO_BUFFERS; i--;) {
if (test_bit(i, &line6pcm->active_urb_out))
alive++;
}
if (! alive)
break;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
} 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)
{
unlink_audio_out_urbs(line6pcm);
wait_clear_audio_out_urbs(line6pcm);
}
/*
Callback for completed playback URB.
*/
static void audio_out_callback(struct urb *urb PT_REGS)
{
int i, index, length = 0, shutdown = 0;
unsigned long flags;
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;
/* find index of URB */
for(index = LINE6_ISO_BUFFERS; index--;)
if(urb == line6pcm->urb_audio_out[index])
break;
if(index < 0)
return; /* URB has been unlinked asynchronously */
for(i = LINE6_ISO_PACKETS; i--;)
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;
clear_bit(index, &line6pcm->active_urb_out);
for(i = LINE6_ISO_PACKETS; i--;)
if(urb->iso_frame_desc[i].status == -ESHUTDOWN) {
shutdown = 1;
break;
}
if(test_bit(index, &line6pcm->unlink_urb_out))
shutdown = 1;
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
if(!shutdown) {
submit_audio_out_urb(substream);
if((line6pcm->bytes_out += length) >= line6pcm->period_out) {
line6pcm->bytes_out -= line6pcm->period_out;
snd_pcm_period_elapsed(substream);
}
}
}
/* open playback callback */
static int snd_line6_playback_open(struct snd_pcm_substream *substream)
{
int err;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
if((err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
(&line6pcm->properties->snd_line6_rates))) < 0)
return err;
runtime->hw = line6pcm->properties->snd_line6_playback_hw;
return 0;
}
/* close playback callback */
static int snd_line6_playback_close(struct snd_pcm_substream *substream)
{
return 0;
}
/* hw_params playback callback */
static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
{
int ret;
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
/* -- Florian Demski [FD] */
/* don't ask me why, but this fixes the bug on my machine */
if ( line6pcm == NULL ) {
if ( substream->pcm == NULL )
return -ENOMEM;
if ( substream->pcm->private_data == NULL )
return -ENOMEM;
substream->private_data = substream->pcm->private_data;
line6pcm = snd_pcm_substream_chip( substream );
}
/* -- [FD] end */
if((ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
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);
if(line6pcm->wrap_out) {
kfree(line6pcm->wrap_out);
line6pcm->wrap_out = 0;
}
return snd_pcm_lib_free_pages(substream);
}
/* trigger playback callback */
int snd_line6_playback_trigger(struct snd_pcm_substream *substream, 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);
if(err < 0) {
clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags);
return err;
}
}
break;
case SNDRV_PCM_TRIGGER_STOP:
if(test_and_clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags))
unlink_audio_out_urbs(line6pcm);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
set_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags);
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
clear_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags);
break;
default:
return -EINVAL;
}
return 0;
}
/* playback pointer callback */
static snd_pcm_uframes_t
snd_line6_playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
return line6pcm->pos_out_done;
}
/* 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,
};
int create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
{
int i;
/* create audio URBs and fill in constant values: */
for(i = 0; i < LINE6_ISO_BUFFERS; ++i) {
struct urb *urb;
/* URB for audio out: */
urb = line6pcm->urb_audio_out[i] = usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);
if(urb == NULL) {
dev_err(line6pcm->line6->ifcdev, "Out of memory\n");
return -ENOMEM;
}
urb->dev = line6pcm->line6->usbdev;
urb->pipe = usb_sndisocpipe(line6pcm->line6->usbdev, line6pcm->ep_audio_write & USB_ENDPOINT_NUMBER_MASK);
urb->transfer_flags = URB_ISO_ASAP;
urb->start_frame = -1;
urb->number_of_packets = LINE6_ISO_PACKETS;
urb->interval = LINE6_ISO_INTERVAL;
urb->error_count = 0;
urb->complete = audio_out_callback;
}
return 0;
}

View File

@ -0,0 +1,29 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef PLAYBACK_H
#define PLAYBACK_H
#include "driver.h"
#include <sound/pcm.h>
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);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,203 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef POD_H
#define POD_H
#include "driver.h"
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include "dumprequest.h"
/*
PODxt Live interfaces
*/
#define PODXTLIVE_INTERFACE_POD 0
#define PODXTLIVE_INTERFACE_VARIAX 1
/*
Locate name in binary program dump
*/
#define POD_NAME_OFFSET 0
#define POD_NAME_LENGTH 16
/*
Other constants
*/
#define POD_CONTROL_SIZE 0x80
#define POD_BUFSIZE_DUMPREQ 7
#define POD_STARTUP_DELAY 3
/**
Data structure for values that need to be requested explicitly.
This is the case for system and tuner settings.
*/
struct ValueWait
{
unsigned short value;
wait_queue_head_t wait;
};
/**
Binary PodXT Pro program dump
*/
struct pod_program {
/**
Header information (including program name).
*/
unsigned char header[0x20];
/**
Program parameters.
*/
unsigned char control[POD_CONTROL_SIZE];
};
struct usb_line6_pod {
/**
Generic Line6 USB data.
*/
struct usb_line6 line6;
/**
Dump request structure.
*/
struct line6_dump_request dumpreq;
/**
Current program number.
*/
unsigned char channel_num;
/**
Current program settings.
*/
struct pod_program prog_data;
/**
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.
*/
struct ValueWait tuner_mute;
/**
Tuner base frequency (typically 440Hz).
*/
struct ValueWait tuner_freq;
/**
Note received from tuner.
*/
struct ValueWait tuner_note;
/**
Pitch value received from tuner.
*/
struct ValueWait tuner_pitch;
/**
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
*/
struct ValueWait routing;
/**
Wait for audio clipping event.
*/
struct ValueWait clipping;
/**
Bottom-half for creation of sysfs special files.
*/
struct work_struct create_files_work;
/**
Dirty flags for access to parameter data.
*/
unsigned long param_dirty[POD_CONTROL_SIZE / sizeof(unsigned long)];
/**
Some atomic flags.
*/
unsigned long atomic_flags;
/**
Counter for startup process.
*/
int startup_count;
/**
Serial number of device.
*/
int serial_number;
/**
Firmware version (x 100).
*/
int firmware_version;
/**
Device ID.
*/
int device_id;
/**
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.
*/
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);
#endif

View File

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

View File

@ -0,0 +1,219 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
* Emil Myhrman (emil.myhrman@gmail.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#include "driver.h"
#include "audio.h"
#include "capture.h"
#include "playback.h"
#include "toneport.h"
static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2);
static struct snd_ratden toneport_ratden = {
.num_min = 44100,
.num_max = 44100,
.num_step = 1,
.den = 1
};
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
},
.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
},
.snd_line6_rates = {
.nrats = 1,
.rats = &toneport_ratden
},
.bytes_per_frame = 4
};
/*
For the led on Guitarport.
Brightness goes from 0x00 to 0x26. Set a value above this to have led blink.
(void cmd_0x02(byte red, byte green)
*/
static int led_red = 0x00;
static int led_green = 0x26;
static void toneport_update_led(struct device *dev) {
struct usb_interface *interface;
struct usb_line6_toneport *tp;
struct usb_line6* line6;
if ((interface = to_usb_interface(dev)) &&
(tp = usb_get_intfdata(interface)) &&
(line6 = &tp->line6))
toneport_send_cmd(line6->usbdev, (led_red<<8)|0x0002, led_green); // for setting the LED on Guitarport
}
static ssize_t toneport_set_led_red(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) {
char* c;
led_red = simple_strtol(buf, &c, 10);
toneport_update_led(dev);
return count;
}
static ssize_t toneport_set_led_green(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count) {
char* c;
led_green = simple_strtol(buf, &c, 10);
toneport_update_led(dev);
return count;
}
static DEVICE_ATTR(led_red, S_IWUGO | S_IRUGO, line6_nop_read, toneport_set_led_red);
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;
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev,0), 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
cmd1, cmd2, 0, 0, LINE6_TIMEOUT * HZ);
if(ret < 0) {
err("send failed (error %d)\n", ret);
return ret;
}
return 0;
}
/*
Toneport destructor.
*/
static void toneport_destruct(struct usb_interface *interface)
{
struct usb_line6_toneport *toneport = usb_get_intfdata(interface);
struct usb_line6 *line6;
if(toneport == NULL) return;
line6 = &toneport->line6;
if(line6 == NULL) return;
line6_cleanup_audio(line6);
}
/*
Init Toneport device.
*/
int toneport_init(struct usb_interface *interface, struct usb_line6_toneport *toneport)
{
int err, ticks;
struct usb_line6 *line6 = &toneport->line6;
struct usb_device *usbdev;
if((interface == NULL) || (toneport == NULL))
return -ENODEV;
/* initialize audio system: */
if((err = line6_init_audio(line6)) < 0) {
toneport_destruct(interface);
return err;
}
/* initialize PCM subsystem: */
if((err = line6_init_pcm(line6, &toneport_pcm_properties)) < 0) {
toneport_destruct(interface);
return err;
}
/* register audio system: */
if((err = line6_register_audio(line6)) < 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); // ..
toneport_send_cmd(usbdev, 0x0301, 0x0000); // only one that works for me; on GP, TP might be different?
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);
}
return 0;
}
/*
Toneport device disconnected.
*/
void 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) {
device_remove_file(&interface->dev, &dev_attr_led_red);
device_remove_file(&interface->dev, &dev_attr_led_green);
}
if(toneport != NULL) {
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);
}
}
toneport_destruct(interface);
}

View File

@ -0,0 +1,44 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef TONEPORT_H
#define TONEPORT_H
#include "driver.h"
#include <linux/usb.h>
#include <sound/core.h>
struct usb_line6_toneport {
/**
Generic Line6 USB data.
*/
struct usb_line6 line6;
/**
Serial number of device.
*/
int serial_number;
/**
Firmware version (x 100).
*/
int firmware_version;
};
extern void toneport_disconnect(struct usb_interface *interface);
extern int toneport_init(struct usb_interface *interface, struct usb_line6_toneport *toneport);
#endif

View File

@ -0,0 +1,75 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2005-2008 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef USBDEFS_H
#define USBDEFS_H
#include <linux/version.h>
#define LINE6_VENDOR_ID 0x0e41
#define USB_INTERVALS_PER_SECOND 1000
/*
Device ids.
*/
#define LINE6_DEVID_BASSPODXT 0x4250
#define LINE6_DEVID_BASSPODXTLIVE 0x4642
#define LINE6_DEVID_BASSPODXTPRO 0x4252
#define LINE6_DEVID_GUITARPORT 0x4750
#define LINE6_DEVID_POCKETPOD 0x5051
#define LINE6_DEVID_PODX3 0x414a
#define LINE6_DEVID_PODX3LIVE 0x414b
#define LINE6_DEVID_PODXT 0x5044
#define LINE6_DEVID_PODXTLIVE 0x4650
#define LINE6_DEVID_PODXTPRO 0x5050
#define LINE6_DEVID_TONEPORT_GX 0x4147
#define LINE6_DEVID_TONEPORT_UX1 0x4141
#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_BITS_PRO (LINE6_BIT_BASSPODXTPRO | LINE6_BIT_PODXTPRO)
#define LINE6_BITS_LIVE (LINE6_BIT_BASSPODXTLIVE | LINE6_BIT_PODXTLIVE | LINE6_BIT_PODX3LIVE)
#define LINE6_BITS_PODXTALL (LINE6_BIT_PODXT | LINE6_BIT_PODXTLIVE | LINE6_BIT_PODXTPRO)
#define LINE6_BITS_BASSPODXTALL (LINE6_BIT_BASSPODXT | LINE6_BIT_BASSPODXTLIVE | LINE6_BIT_BASSPODXTPRO)
#define LINE6_BIT_CONTROL (1 << 0) /* device supports settings parameter via USB */
#define LINE6_BIT_PCM (1 << 1) /* device supports PCM input/output via USB */
#define LINE6_BIT_CONTROL_PCM (LINE6_BIT_CONTROL | LINE6_BIT_PCM)
#define LINE6_FALLBACK_INTERVAL 10
#define LINE6_FALLBACK_MAXPACKETSIZE 16
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
#define usb_interrupt_msg(usb_dev, pipe, data, len, actual_length, timeout) \
usb_bulk_msg(usb_dev, pipe, data, len, actual_length, timeout)
#endif
#endif

View File

@ -0,0 +1,501 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#include "driver.h"
#include "audio.h"
#include "control.h"
#include "variax.h"
#define VARIAX_SYSEX_CODE 7
#define VARIAX_SYSEX_PARAM 0x3b
#define VARIAX_SYSEX_ACTIVATE 0x2a
#define VARIAX_MODEL_HEADER_LENGTH 7
#define VARIAX_MODEL_MESSAGE_LENGTH 199
#define VARIAX_OFFSET_ACTIVATE 7
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,
0x00, 0x00, 0x00, 0xf7
};
/*
Decode data transmitted by workbench.
*/
static void variax_decode(const unsigned char *raw_data, unsigned char *data, int raw_size)
{
for(; raw_size > 0; raw_size -= 6) {
data[2] = raw_data[0] | (raw_data[1] << 4);
data[1] = raw_data[2] | (raw_data[3] << 4);
data[0] = raw_data[4] | (raw_data[5] << 4);
raw_data += 6;
data += 3;
}
}
static void variax_activate_timeout(unsigned long arg)
{
struct usb_line6_variax *variax = (struct usb_line6_variax *)arg;
variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = 1;
line6_send_raw_message_async(&variax->line6, variax->buffer_activate, sizeof(variax_activate));
}
/*
Send an asynchronous activation request after a given interval.
*/
static void variax_activate_delayed(struct usb_line6_variax *variax, int seconds)
{
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);
}
static void variax_startup_timeout(unsigned long arg)
{
struct usb_line6_variax *variax = (struct usb_line6_variax *)arg;
if(variax->dumpreq.ok)
return;
line6_dump_request_async(&variax->dumpreq, &variax->line6, 0);
line6_startup_delayed(&variax->dumpreq, 1, variax_startup_timeout, variax);
}
/*
Process a completely received message.
*/
void variax_process_message(struct usb_line6_variax *variax)
{
const unsigned char *buf = variax->line6.buffer_message;
switch(buf[0]) {
case LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST:
switch(buf[1]) {
case VARIAXMIDI_volume:
variax->volume = buf[2];
break;
case VARIAXMIDI_tone:
variax->tone = buf[2];
}
break;
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);
break;
case LINE6_RESET:
dev_info(variax->line6.ifcdev, "VARIAX reset\n");
variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY);
break;
case LINE6_SYSEX_BEGIN:
if(memcmp(buf + 1, variax_request_model1 + 1, VARIAX_MODEL_HEADER_LENGTH - 1) == 0) {
if(variax->line6.message_length == VARIAX_MODEL_MESSAGE_LENGTH) {
switch(variax->dumpreq.in_progress) {
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);
break;
case VARIAX_DUMP_PASS2:
/* model name is transmitted twice, so skip it here: */
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);
}
}
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) {
memcpy(variax->bank, buf + sizeof(variax_request_bank) - 1, sizeof(variax->bank));
variax->dumpreq.ok = 1;
line6_dump_finished(&variax->dumpreq);
}
break;
case LINE6_SYSEX_END:
break;
default:
DEBUG_MESSAGES(dev_err(variax->line6.ifcdev, "Variax: unknown message %02X\n", buf[0]));
}
}
/*
"read" request on "volume" special file.
*/
static ssize_t variax_get_volume(struct device *dev, DEVICE_ATTRIBUTE char *buf)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
return sprintf(buf, "%d\n", variax->volume);
}
/*
"write" request on "volume" special file.
*/
static ssize_t variax_set_volume(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
int value = simple_strtoul(buf, NULL, 10);
if(line6_transmit_parameter(&variax->line6, VARIAXMIDI_volume, value) == 0)
variax->volume = value;
return count;
}
/*
"read" request on "model" special file.
*/
static ssize_t variax_get_model(struct device *dev, DEVICE_ATTRIBUTE char *buf)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
return sprintf(buf, "%d\n", variax->model);
}
/*
"write" request on "model" special file.
*/
static ssize_t variax_set_model(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count)
{
struct usb_line6_variax *variax = usb_get_intfdata( to_usb_interface(dev));
int value = simple_strtoul(buf, NULL, 10);
if(line6_send_program(&variax->line6, value) == 0)
variax->model = value;
return count;
}
/*
"read" request on "active" special file.
*/
static ssize_t variax_get_active(struct device *dev, DEVICE_ATTRIBUTE char *buf)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
return sprintf(buf, "%d\n", variax->buffer_activate[VARIAX_OFFSET_ACTIVATE]);
}
/*
"write" request on "active" special file.
*/
static ssize_t variax_set_active(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
int value = simple_strtoul(buf, NULL, 10) ? 1 : 0;
variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = value;
line6_send_raw_message_async(&variax->line6, variax->buffer_activate, sizeof(variax_activate));
return count;
}
/*
"read" request on "tone" special file.
*/
static ssize_t variax_get_tone(struct device *dev, DEVICE_ATTRIBUTE char *buf)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
return sprintf(buf, "%d\n", variax->tone);
}
/*
"write" request on "tone" special file.
*/
static ssize_t variax_set_tone(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
int value = simple_strtoul(buf, NULL, 10);
if(line6_transmit_parameter(&variax->line6, VARIAXMIDI_tone, value) == 0)
variax->tone = value;
return count;
}
static ssize_t get_string(char *buf, const char *data, int length)
{
int i;
memcpy(buf, data, length);
for(i = length; i--;) {
char c = buf[i];
if((c != 0) && (c != ' '))
break;
}
buf[i + 1] = '\n';
return i + 2;
}
/*
"read" request on "name" special file.
*/
static ssize_t variax_get_name(struct device *dev, DEVICE_ATTRIBUTE char *buf)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
line6_wait_dump(&variax->dumpreq, 0);
return get_string(buf, variax->model_data.name, sizeof(variax->model_data.name));
}
/*
"read" request on "bank" special file.
*/
static ssize_t variax_get_bank(struct device *dev, DEVICE_ATTRIBUTE char *buf)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
line6_wait_dump(&variax->dumpreq, 0);
return get_string(buf, variax->bank, sizeof(variax->bank));
}
/*
"read" request on "dump" special file.
*/
static ssize_t variax_get_dump(struct device *dev, DEVICE_ATTRIBUTE char *buf)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
int retval;
retval = line6_wait_dump(&variax->dumpreq, 0);
if(retval < 0) return retval;
memcpy(buf, &variax->model_data.control, sizeof(variax->model_data.control));
return sizeof(variax->model_data.control);
}
#if CREATE_RAW_FILE
/*
"write" request on "raw" special file.
*/
static ssize_t variax_set_raw2(struct device *dev, DEVICE_ATTRIBUTE const char *buf, size_t count)
{
struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev));
int size;
int i;
char *sysex;
count -= count % 3;
size = count * 2;
sysex = variax_alloc_sysex_buffer(variax, VARIAX_SYSEX_PARAM, size);
if(!sysex)
return 0;
for(i = 0; i < count; i += 3) {
const unsigned char *p1 = buf + i;
char *p2 = sysex + SYSEX_DATA_OFS + i * 2;
p2[0] = p1[2] & 0x0f;
p2[1] = p1[2] >> 4;
p2[2] = p1[1] & 0x0f;
p2[3] = p1[1] >> 4;
p2[4] = p1[0] & 0x0f;
p2[5] = p1[0] >> 4;
}
line6_send_sysex_message(&variax->line6, sysex, size);
kfree(sysex);
return count;
}
#endif
/* Variax workbench special files: */
static DEVICE_ATTR(model, S_IWUGO | S_IRUGO, variax_get_model, variax_set_model);
static DEVICE_ATTR(volume, S_IWUGO | S_IRUGO, variax_get_volume, variax_set_volume);
static DEVICE_ATTR(tone, S_IWUGO | S_IRUGO, variax_get_tone, variax_set_tone);
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);
#if CREATE_RAW_FILE
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
/*
Variax destructor.
*/
static void variax_destruct(struct usb_interface *interface)
{
struct usb_line6_variax *variax = usb_get_intfdata(interface);
struct usb_line6 *line6;
if(variax == NULL) return;
line6 = &variax->line6;
if(line6 == NULL) return;
line6_cleanup_audio(line6);
/* free dump request data: */
line6_dumpreq_destructbuf(&variax->dumpreq, 2);
line6_dumpreq_destructbuf(&variax->dumpreq, 1);
line6_dumpreq_destruct(&variax->dumpreq);
if(variax->buffer_activate) kfree(variax->buffer_activate);
del_timer_sync(&variax->activate_timer);
}
/*
Create sysfs entries.
*/
int variax_create_files2(struct device *dev)
{
int err;
CHECK_RETURN(device_create_file(dev, &dev_attr_model));
CHECK_RETURN(device_create_file(dev, &dev_attr_volume));
CHECK_RETURN(device_create_file(dev, &dev_attr_tone));
CHECK_RETURN(device_create_file(dev, &dev_attr_name));
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_raw));
CHECK_RETURN(device_create_file(dev, &dev_attr_raw2));
#endif
return 0;
}
/*
Init workbench device.
*/
int variax_init(struct usb_interface *interface, struct usb_line6_variax *variax)
{
int err;
if((interface == NULL) || (variax == NULL)) return -ENODEV;
/* 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;
}
err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_model2, sizeof(variax_request_model2), 1);
if(err < 0) {
dev_err(&interface->dev, "Out of memory\n");
variax_destruct(interface);
return err;
}
err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_bank, sizeof(variax_request_bank), 2);
if(err < 0) {
dev_err(&interface->dev, "Out of memory\n");
variax_destruct(interface);
return err;
}
variax->buffer_activate = kmalloc(sizeof(variax_activate), GFP_KERNEL);
if(variax->buffer_activate == NULL) {
dev_err(&interface->dev, "Out of memory\n");
variax_destruct(interface);
return -ENOMEM;
}
memcpy(variax->buffer_activate, variax_activate, sizeof(variax_activate));
init_timer(&variax->activate_timer);
/* create sysfs entries: */
if((err = variax_create_files(0, 0, &interface->dev)) < 0) {
variax_destruct(interface);
return err;
}
if((err = variax_create_files2(&interface->dev)) < 0) {
variax_destruct(interface);
return err;
}
/* initialize audio system: */
if((err = line6_init_audio(&variax->line6)) < 0) {
variax_destruct(interface);
return err;
}
/* initialize MIDI subsystem: */
if((err = line6_init_midi(&variax->line6)) < 0) {
variax_destruct(interface);
return err;
}
/* register audio system: */
if((err = line6_register_audio(&variax->line6)) < 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;
}
/*
Workbench device disconnected.
*/
void variax_disconnect(struct usb_interface *interface)
{
struct device *dev;
if(interface == NULL) return;
dev = &interface->dev;
if(dev != NULL) {
/* remove sysfs entries: */
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);
device_remove_file(dev, &dev_attr_name);
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_raw);
device_remove_file(dev, &dev_attr_raw2);
#endif
}
variax_destruct(interface);
}

View File

@ -0,0 +1,107 @@
/*
* Line6 Linux USB driver - 0.8.0
*
* Copyright (C) 2004-2009 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
* published by the Free Software Foundation, version 2.
*
*/
#ifndef VARIAX_H
#define VARIAX_H
#include "driver.h"
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/wait.h>
#include <sound/core.h>
#include "dumprequest.h"
#define VARIAX_ACTIVATE_DELAY 10
#define VARIAX_STARTUP_DELAY 3
enum {
VARIAX_DUMP_PASS1 = LINE6_DUMP_CURRENT,
VARIAX_DUMP_PASS2,
VARIAX_DUMP_PASS3
};
/**
Binary Variax model dump
*/
struct variax_model {
/**
Header information (including program name).
*/
unsigned char name[18];
/**
Model parameters.
*/
unsigned char control[78 * 2];
};
struct usb_line6_variax {
/**
Generic Line6 USB data.
*/
struct usb_line6 line6;
/**
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.
*/
unsigned char *buffer_activate;
/**
Model number.
*/
int model;
/**
Current model settings.
*/
struct variax_model model_data;
/**
Name of current model bank.
*/
unsigned char bank[18];
/**
Position of volume dial.
*/
int volume;
/**
Position of tone control dial.
*/
int tone;
/**
Timer for delayed activation request.
*/
struct timer_list activate_timer;
};
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);
#endif