mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
e642f09951
commit
705ececd1c
|
@ -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.
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
|
@ -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
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
#ifndef DRIVER_REVISION
|
||||
/* current subversion revision */
|
||||
#define DRIVER_REVISION " (revision 529)"
|
||||
#endif
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue