diff options
Diffstat (limited to 'drivers/staging/line6')
31 files changed, 2561 insertions, 3925 deletions
diff --git a/drivers/staging/line6/Kconfig b/drivers/staging/line6/Kconfig index 7852d4a960c..4f1219b4c69 100644 --- a/drivers/staging/line6/Kconfig +++ b/drivers/staging/line6/Kconfig @@ -1,7 +1,8 @@ -config LINE6_USB +menuconfig LINE6_USB tristate "Line6 USB support" depends on USB && SND select SND_RAWMIDI + select SND_PCM help This is a driver for the guitar amp, cab, and effects modeller PODxt Pro by Line6 (and similar devices), supporting the @@ -17,5 +18,21 @@ config LINE6_USB * Signal routing (record clean/processed guitar signal, re-amping) - Preliminary support for the Variax Workbench is included. + Preliminary support for the Variax Workbench and TonePort + devices is included. +if LINE6_USB + +config LINE6_USB_IMPULSE_RESPONSE + bool "measure impulse response" + default n + help + Say Y here to add code to measure the impulse response of a Line6 + device. This is more accurate than user-space methods since it + bypasses any PCM data buffering (e.g., by ALSA or jack). This is + useful for assessing the performance of new devices, but is not + required for normal operation. + + If unsure, say N. + +endif # LINE6_USB diff --git a/drivers/staging/line6/Makefile b/drivers/staging/line6/Makefile index a1c93edc6b1..ae5c374b0f8 100644 --- a/drivers/staging/line6/Makefile +++ b/drivers/staging/line6/Makefile @@ -1,15 +1,14 @@ obj-$(CONFIG_LINE6_USB) += line6usb.o -line6usb-objs := \ +line6usb-y := \ audio.o \ capture.o \ - control.o \ driver.o \ - dumprequest.o \ midi.o \ midibuf.o \ pcm.o \ playback.o \ pod.o \ toneport.o \ - variax.o + variax.o \ + podhd.o diff --git a/drivers/staging/line6/audio.c b/drivers/staging/line6/audio.c index e2ac8d60f8c..171d80c1b02 100644 --- a/drivers/staging/line6/audio.c +++ b/drivers/staging/line6/audio.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,37 +9,35 @@ * */ -#include "driver.h" -#include "audio.h" - #include <sound/core.h> #include <sound/initval.h> +#include <linux/export.h> - -static int line6_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static char *line6_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; - +#include "driver.h" +#include "audio.h" /* Initialize the Line6 USB audio system. */ int line6_init_audio(struct usb_line6 *line6) { - static int dev; struct snd_card *card; int err; - err = snd_card_create(line6_index[dev], line6_id[dev], THIS_MODULE, 0, - &card); + err = snd_card_new(line6->ifcdev, + SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); if (err < 0) return err; line6->card = card; + strcpy(card->id, line6->properties->id); strcpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, "Line6-USB"); + strcpy(card->shortname, line6->properties->name); + /* longname is 80 chars - see asound.h */ sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name, - dev_name(line6->ifcdev)); /* 80 chars - see asound.h */ + dev_name(line6->ifcdev)); return 0; } diff --git a/drivers/staging/line6/audio.h b/drivers/staging/line6/audio.h index cc0245adbcd..5f8a09a0fa9 100644 --- a/drivers/staging/line6/audio.h +++ b/drivers/staging/line6/audio.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -12,13 +12,10 @@ #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 diff --git a/drivers/staging/line6/capture.c b/drivers/staging/line6/capture.c index ea2060b4919..e6ca631e3f7 100644 --- a/drivers/staging/line6/capture.c +++ b/drivers/staging/line6/capture.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,35 +9,35 @@ * */ -#include "driver.h" - +#include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include "audio.h" +#include "capture.h" +#include "driver.h" #include "pcm.h" #include "pod.h" -#include "capture.h" - /* Find a free URB and submit it. */ -static int submit_audio_in_urb(struct snd_pcm_substream *substream) +static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) { - unsigned int index; + int index; unsigned long flags; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); int i, urb_size; + int ret; struct urb *urb_in; spin_lock_irqsave(&line6pcm->lock_audio_in, flags); - index = find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); + index = + find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); - if (index >= LINE6_ISO_BUFFERS) { + if (index < 0 || index >= LINE6_ISO_BUFFERS) { spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); - dev_err(s2m(substream), "no free URB found\n"); + dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); return -EINVAL; } @@ -45,20 +45,26 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) urb_size = 0; for (i = 0; i < LINE6_ISO_PACKETS; ++i) { - struct usb_iso_packet_descriptor *fin = &urb_in->iso_frame_desc[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 = + line6pcm->buffer_in + + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; urb_in->transfer_buffer_length = urb_size; - urb_in->context = substream; + urb_in->context = line6pcm; + + ret = usb_submit_urb(urb_in, GFP_ATOMIC); - if (usb_submit_urb(urb_in, GFP_ATOMIC) == 0) + if (ret == 0) set_bit(index, &line6pcm->active_urb_in); else - dev_err(s2m(substream), "URB in #%d submission failed\n", index); + dev_err(line6pcm->line6->ifcdev, + "URB in #%d submission failed (%d)\n", index, ret); spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); return 0; @@ -67,12 +73,12 @@ static int submit_audio_in_urb(struct snd_pcm_substream *substream) /* Submit all currently available capture URBs. */ -static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream) +int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm) { int ret, i; for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - ret = submit_audio_in_urb(substream); + ret = submit_audio_in_urb(line6pcm); if (ret < 0) return ret; } @@ -83,7 +89,7 @@ static int submit_audio_in_all_urbs(struct snd_pcm_substream *substream) /* Unlink all currently active capture URBs. */ -static void unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) +void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) { unsigned int i; @@ -91,6 +97,7 @@ static void unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) 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]; + usb_unlink_urb(u); } } @@ -101,7 +108,7 @@ static void unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) Wait until unlinking of all currently active capture URBs has been finished. */ -static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) +void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) { int timeout = HZ; unsigned int i; @@ -120,46 +127,97 @@ static void wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) } while (--timeout > 0); if (alive) snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); - - line6pcm->active_urb_in = 0; - line6pcm->unlink_urb_in = 0; } /* Unlink all currently active capture URBs, and wait for finishing. */ -void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) +void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) { - unlink_audio_in_urbs(line6pcm); - wait_clear_audio_in_urbs(line6pcm); + line6_unlink_audio_in_urbs(line6pcm); + line6_wait_clear_audio_in_urbs(line6pcm); } /* - Callback for completed capture URB. + Copy data into ALSA capture buffer. */ +void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize) +{ + struct snd_pcm_substream *substream = + get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); + struct snd_pcm_runtime *runtime = substream->runtime; + const int bytes_per_frame = line6pcm->properties->bytes_per_frame; + int frames = fsize / bytes_per_frame; + + if (runtime == NULL) + return; + + if (line6pcm->pos_in_done + frames > runtime->buffer_size) { + /* + The transferred area goes over buffer boundary, + copy two separate chunks. + */ + int len; + + len = runtime->buffer_size - line6pcm->pos_in_done; + + if (len > 0) { + memcpy(runtime->dma_area + + line6pcm->pos_in_done * bytes_per_frame, fbuf, + len * bytes_per_frame); + memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, + (frames - len) * bytes_per_frame); + } else { + /* this is somewhat paranoid */ + dev_err(line6pcm->line6->ifcdev, + "driver bug: len = %d\n", len); + } + } else { + /* copy single chunk */ + memcpy(runtime->dma_area + + line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize); + } + + line6pcm->pos_in_done += frames; + if (line6pcm->pos_in_done >= runtime->buffer_size) + line6pcm->pos_in_done -= runtime->buffer_size; +} + +void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) +{ + struct snd_pcm_substream *substream = + get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); + + line6pcm->bytes_in += length; + if (line6pcm->bytes_in >= line6pcm->period_in) { + line6pcm->bytes_in %= line6pcm->period_in; + snd_pcm_period_elapsed(substream); + } +} + +void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm) +{ + kfree(line6pcm->buffer_in); + line6pcm->buffer_in = NULL; +} + +/* + * Callback for completed capture URB. + */ static void audio_in_callback(struct urb *urb) { int i, index, length = 0, shutdown = 0; - int frames; unsigned long flags; - struct snd_pcm_substream *substream = (struct snd_pcm_substream *)urb->context; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - const int bytes_per_frame = line6pcm->properties->bytes_per_frame; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; + + line6pcm->last_frame_in = urb->start_frame; /* find index of URB */ for (index = 0; index < LINE6_ISO_BUFFERS; ++index) if (urb == line6pcm->urb_audio_in[index]) break; -#if DO_DUMP_PCM_RECEIVE - 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) { @@ -167,55 +225,50 @@ static void audio_in_callback(struct urb *urb) int fsize; struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i]; - if (fin->status == -18) { + if (fin->status == -EXDEV) { shutdown = 1; break; } fbuf = urb->transfer_buffer + fin->offset; fsize = fin->actual_length; + + if (fsize > line6pcm->max_packet_size) { + dev_err(line6pcm->line6->ifcdev, + "driver and/or device bug: packet too large (%d > %d)\n", + fsize, line6pcm->max_packet_size); + } + length += fsize; - if (fsize > 0) { - frames = fsize / bytes_per_frame; - - if (line6pcm->pos_in_done + frames > runtime->buffer_size) { - /* - The transferred area goes over buffer boundary, - copy two separate chunks. - */ - int len; - len = runtime->buffer_size - line6pcm->pos_in_done; - - if (len > 0) { - memcpy(runtime->dma_area + line6pcm->pos_in_done * bytes_per_frame, fbuf, len * bytes_per_frame); - memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, (frames - len) * bytes_per_frame); - } else - 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); - } + /* the following assumes LINE6_ISO_PACKETS == 1: */ + line6pcm->prev_fbuf = fbuf; + line6pcm->prev_fsize = fsize; - if ((line6pcm->pos_in_done += frames) >= runtime->buffer_size) - line6pcm->pos_in_done -= runtime->buffer_size; - } +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) +#endif + if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, + &line6pcm->flags) && (fsize > 0)) + line6_capture_copy(line6pcm, fbuf, fsize); } clear_bit(index, &line6pcm->active_urb_in); - if (test_bit(index, &line6pcm->unlink_urb_in)) + if (test_and_clear_bit(index, &line6pcm->unlink_urb_in)) shutdown = 1; spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); if (!shutdown) { - submit_audio_in_urb(substream); + submit_audio_in_urb(line6pcm); - if ((line6pcm->bytes_in += length) >= line6pcm->period_in) { - line6pcm->bytes_in -= line6pcm->period_in; - snd_pcm_period_elapsed(substream); - } +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) +#endif + if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, + &line6pcm->flags)) + line6_capture_check_period(line6pcm, length); } } @@ -228,7 +281,8 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream) err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - (&line6pcm->properties->snd_line6_rates)); + (&line6pcm-> + properties->snd_line6_rates)); if (err < 0) return err; @@ -261,19 +315,19 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, } /* -- [FD] end */ - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); + ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); + if (ret < 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; + ret = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (ret < 0) { + line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); + return ret; } + line6pcm->period_in = params_period_bytes(hw_params); return 0; } @@ -281,37 +335,38 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - unlink_wait_clear_audio_in_urbs(line6pcm); - - kfree(line6pcm->buffer_in); - line6pcm->buffer_in = NULL; + line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); return snd_pcm_lib_free_pages(substream); } /* trigger callback */ -int snd_line6_capture_trigger(struct snd_pcm_substream *substream, int cmd) +int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd) { - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); int err; - line6pcm->count_in = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - if (!test_and_set_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) { - err = submit_audio_in_all_urbs(substream); +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_RESUME: +#endif + err = line6_pcm_acquire(line6pcm, + LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); - if (err < 0) { - clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags); - return err; - } - } + if (err < 0) + return err; break; case SNDRV_PCM_TRIGGER_STOP: - if (test_and_clear_bit(BIT_RUNNING_CAPTURE, &line6pcm->flags)) - unlink_audio_in_urbs(line6pcm); +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_SUSPEND: +#endif + err = line6_pcm_release(line6pcm, + LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); + + if (err < 0) + return err; break; @@ -327,22 +382,23 @@ 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, + .open = snd_line6_capture_open, + .close = snd_line6_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_line6_capture_hw_params, + .hw_free = snd_line6_capture_hw_free, + .prepare = snd_line6_prepare, + .trigger = snd_line6_trigger, + .pointer = snd_line6_capture_pointer, }; -int create_audio_in_urbs(struct snd_line6_pcm *line6pcm) +int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) { int i; @@ -351,7 +407,8 @@ int create_audio_in_urbs(struct snd_line6_pcm *line6pcm) struct urb *urb; /* URB for audio in: */ - urb = line6pcm->urb_audio_in[i] = usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); + 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"); @@ -359,7 +416,10 @@ int create_audio_in_urbs(struct snd_line6_pcm *line6pcm) } urb->dev = line6pcm->line6->usbdev; - urb->pipe = usb_rcvisocpipe(line6pcm->line6->usbdev, line6pcm->ep_audio_read & USB_ENDPOINT_NUMBER_MASK); + 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; diff --git a/drivers/staging/line6/capture.h b/drivers/staging/line6/capture.h index 5c44464d29d..4157bcb598a 100644 --- a/drivers/staging/line6/capture.h +++ b/drivers/staging/line6/capture.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -12,21 +12,24 @@ #ifndef CAPTURE_H #define CAPTURE_H - -#include "driver.h" - #include <sound/pcm.h> +#include "driver.h" #include "pcm.h" - extern struct snd_pcm_ops snd_line6_capture_ops; - -extern int create_audio_in_urbs(struct snd_line6_pcm *line6pcm); -extern int snd_line6_capture_trigger(struct snd_pcm_substream *substream, - int cmd); -extern void unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm); - +extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, + int fsize); +extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm, + int length); +extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm); +extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm + *line6pcm); +extern void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm); +extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd); #endif diff --git a/drivers/staging/line6/config.h b/drivers/staging/line6/config.h deleted file mode 100644 index adad130c5dc..00000000000 --- a/drivers/staging/line6/config.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 - - -#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(KERN_INFO "line6usb: %s (%s:%d)\n", \ - __func__, __FILE__, __LINE__) -#endif - -#if DO_DEBUG_MESSAGES -#define DEBUG_MESSAGES(x) (x) -#else -#define DEBUG_MESSAGES(x) -#endif - - -#endif diff --git a/drivers/staging/line6/control.c b/drivers/staging/line6/control.c deleted file mode 100644 index 23ad08e17f8..00000000000 --- a/drivers/staging/line6/control.c +++ /dev/null @@ -1,840 +0,0 @@ -/* - * 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, \ - struct device_attribute *attr, 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, \ - struct device_attribute *attr, 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); diff --git a/drivers/staging/line6/control.h b/drivers/staging/line6/control.h deleted file mode 100644 index 2f19665d95a..00000000000 --- a/drivers/staging/line6/control.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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 diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c index f188ecee502..ef511c76a6e 100644 --- a/drivers/staging/line6/driver.c +++ b/drivers/staging/line6/driver.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,75 +9,97 @@ * */ -#include "driver.h" - #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/usb.h> #include "audio.h" #include "capture.h" -#include "control.h" +#include "driver.h" #include "midi.h" #include "playback.h" #include "pod.h" +#include "podhd.h" #include "revision.h" #include "toneport.h" #include "usbdefs.h" #include "variax.h" - #define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>" #define DRIVER_DESC "Line6 USB Driver" -#define DRIVER_VERSION "0.8.0" - +#define DRIVER_VERSION "0.9.1beta" DRIVER_REVISION /* table of devices that work with this driver */ -static struct usb_device_id line6_id_table[] = { - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXT) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTLIVE) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTPRO) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_GUITARPORT) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_POCKETPOD) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3LIVE) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTLIVE) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTPRO) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_GX) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX1) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX2) }, - { USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_VARIAX) }, - { }, +static const struct usb_device_id line6_id_table[] = { + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXT)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTLIVE)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTPRO)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_GUITARPORT)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_POCKETPOD)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODHD300)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODHD400)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODHD500)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_GX)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX1)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX2)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3LIVE)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTLIVE)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTPRO)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_GX)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX1)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX2)}, + {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_VARIAX)}, + {}, }; + MODULE_DEVICE_TABLE(usb, line6_id_table); -static struct line6_properties line6_properties_table[] = { - { "BassPODxt", LINE6_BIT_BASSPODXT, LINE6_BIT_CONTROL_PCM }, - { "BassPODxt Live", LINE6_BIT_BASSPODXTLIVE, LINE6_BIT_CONTROL_PCM }, - { "BassPODxt Pro", LINE6_BIT_BASSPODXTPRO, LINE6_BIT_CONTROL_PCM }, - { "GuitarPort", LINE6_BIT_GUITARPORT, LINE6_BIT_PCM }, - { "Pocket POD", LINE6_BIT_POCKETPOD, LINE6_BIT_CONTROL_PCM }, - { "POD X3", LINE6_BIT_PODX3, LINE6_BIT_PCM }, - { "POD X3 Live", LINE6_BIT_PODX3LIVE, LINE6_BIT_PCM }, - { "PODxt", LINE6_BIT_PODXT, LINE6_BIT_CONTROL_PCM }, - { "PODxt Live", LINE6_BIT_PODXTLIVE, LINE6_BIT_CONTROL_PCM }, - { "PODxt Pro", LINE6_BIT_PODXTPRO, LINE6_BIT_CONTROL_PCM }, - { "TonePort GX", LINE6_BIT_TONEPORT_GX, LINE6_BIT_PCM }, - { "TonePort UX1", LINE6_BIT_TONEPORT_UX1, LINE6_BIT_PCM }, - { "TonePort UX2", LINE6_BIT_TONEPORT_UX2, LINE6_BIT_PCM }, - { "Variax Workbench", LINE6_BIT_VARIAX, LINE6_BIT_CONTROL } +#define L6PROP(dev_bit, dev_id, dev_name, dev_cap)\ + {.device_bit = LINE6_BIT_##dev_bit, .id = dev_id,\ + .name = dev_name, .capabilities = LINE6_BIT_##dev_cap} + +/* *INDENT-OFF* */ +static const struct line6_properties line6_properties_table[] = { + L6PROP(BASSPODXT, "BassPODxt", "BassPODxt", CTRL_PCM_HW), + L6PROP(BASSPODXTLIVE, "BassPODxtLive", "BassPODxt Live", CTRL_PCM_HW), + L6PROP(BASSPODXTPRO, "BassPODxtPro", "BassPODxt Pro", CTRL_PCM_HW), + L6PROP(GUITARPORT, "GuitarPort", "GuitarPort", PCM), + L6PROP(POCKETPOD, "PocketPOD", "Pocket POD", CONTROL), + L6PROP(PODHD300, "PODHD300", "POD HD300", CTRL_PCM_HW), + L6PROP(PODHD400, "PODHD400", "POD HD400", CTRL_PCM_HW), + L6PROP(PODHD500, "PODHD500", "POD HD500", CTRL_PCM_HW), + L6PROP(PODSTUDIO_GX, "PODStudioGX", "POD Studio GX", PCM), + L6PROP(PODSTUDIO_UX1, "PODStudioUX1", "POD Studio UX1", PCM), + L6PROP(PODSTUDIO_UX2, "PODStudioUX2", "POD Studio UX2", PCM), + L6PROP(PODX3, "PODX3", "POD X3", PCM), + L6PROP(PODX3LIVE, "PODX3Live", "POD X3 Live", PCM), + L6PROP(PODXT, "PODxt", "PODxt", CTRL_PCM_HW), + L6PROP(PODXTLIVE, "PODxtLive", "PODxt Live", CTRL_PCM_HW), + L6PROP(PODXTPRO, "PODxtPro", "PODxt Pro", CTRL_PCM_HW), + L6PROP(TONEPORT_GX, "TonePortGX", "TonePort GX", PCM), + L6PROP(TONEPORT_UX1, "TonePortUX1", "TonePort UX1", PCM), + L6PROP(TONEPORT_UX2, "TonePortUX2", "TonePort UX2", PCM), + L6PROP(VARIAX, "Variax", "Variax Workbench", CONTROL), }; - +/* *INDENT-ON* */ /* This is Line6's MIDI manufacturer ID. */ -const unsigned char line6_midi_id[] = { 0x00, 0x01, 0x0c }; - -struct usb_line6 *line6_devices[LINE6_MAX_DEVICES]; -struct workqueue_struct *line6_workqueue; +const unsigned char line6_midi_id[] = { + 0x00, 0x01, 0x0c +}; +/* + Code to request version of POD, Variax interface + (and maybe other devices). +*/ +static const char line6_request_version[] = { + 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 +}; /** Class for asynchronous messages. @@ -89,7 +111,6 @@ struct message { int done; }; - /* Forward declarations. */ @@ -97,75 +118,29 @@ static void line6_data_received(struct urb *urb); static int line6_send_raw_message_async_part(struct message *msg, struct urb *urb); - /* Start to listen on endpoint. */ static int line6_start_listen(struct usb_line6 *line6) { + int err; + usb_fill_int_urb(line6->urb_listen, line6->usbdev, usb_rcvintpipe(line6->usbdev, line6->ep_control_read), line6->buffer_listen, LINE6_BUFSIZE_LISTEN, line6_data_received, line6, line6->interval); line6->urb_listen->actual_length = 0; - return usb_submit_urb(line6->urb_listen, GFP_KERNEL); -} - -#if DO_DUMP_ANY -/* - Write hexdump to syslog. -*/ -void line6_write_hexdump(struct usb_line6 *line6, char dir, - const unsigned char *buffer, int size) -{ - static const int BYTES_PER_LINE = 8; - char hexdump[100]; - char asc[BYTES_PER_LINE + 1]; - int i, j; - - for (i = 0; i < size; i += BYTES_PER_LINE) { - int hexdumpsize = sizeof(hexdump); - char *p = hexdump; - int n = min(size - i, BYTES_PER_LINE); - asc[n] = 0; - - for (j = 0; j < BYTES_PER_LINE; ++j) { - int bytes; - - if (j < n) { - unsigned char val = buffer[i + j]; - bytes = snprintf(p, hexdumpsize, " %02X", val); - asc[j] = ((val >= 0x20) && (val < 0x7f)) ? val : '.'; - } else - bytes = snprintf(p, hexdumpsize, " "); - - if (bytes > hexdumpsize) - break; /* buffer overflow */ - - p += bytes; - hexdumpsize -= bytes; - } - - dev_info(line6->ifcdev, "%c%04X:%s %s\n", dir, i, hexdump, asc); - } + err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC); + return err; } -#endif -#if DO_DUMP_URB_RECEIVE /* - Dump URB data to syslog. + Stop listening on endpoint. */ -static void line6_dump_urb(struct urb *urb) +static void line6_stop_listen(struct usb_line6 *line6) { - struct usb_line6 *line6 = (struct usb_line6 *)urb->context; - - if (urb->status < 0) - return; - - line6_write_hexdump(line6, 'R', (unsigned char *)urb->transfer_buffer, - urb->actual_length); + usb_kill_urb(line6->urb_listen); } -#endif /* Send raw message in pieces of wMaxPacketSize bytes. @@ -175,10 +150,6 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, { int i, done = 0; -#if DO_DUMP_URB_SEND - line6_write_hexdump(line6, 'S', buffer, size); -#endif - for (i = 0; i < size; i += line6->max_packet_size) { int partial; const char *frag_buf = buffer + i; @@ -186,10 +157,10 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, int retval; retval = usb_interrupt_msg(line6->usbdev, - usb_sndintpipe(line6->usbdev, - line6->ep_control_write), - (char *)frag_buf, frag_size, - &partial, LINE6_TIMEOUT * HZ); + usb_sndintpipe(line6->usbdev, + line6->ep_control_write), + (char *)frag_buf, frag_size, + &partial, LINE6_TIMEOUT * HZ); if (retval) { dev_err(line6->ifcdev, @@ -233,10 +204,6 @@ static int line6_send_raw_message_async_part(struct message *msg, (char *)msg->buffer + done, bytes, line6_async_request_sent, msg, line6->interval); -#if DO_DUMP_URB_SEND - line6_write_hexdump(line6, 'S', (char *)msg->buffer + done, bytes); -#endif - msg->done += bytes; retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -245,13 +212,24 @@ static int line6_send_raw_message_async_part(struct message *msg, __func__, retval); usb_free_urb(urb); kfree(msg); - return -EINVAL; + return retval; } return 0; } /* + Setup and start timer. +*/ +void line6_start_timer(struct timer_list *timer, unsigned int msecs, + void (*function)(unsigned long), unsigned long data) +{ + setup_timer(timer, function, data); + timer->expires = jiffies + msecs * HZ / 1000; + add_timer(timer); +} + +/* Asynchronously send raw message. */ int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, @@ -262,11 +240,8 @@ int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, /* create message: */ msg = kmalloc(sizeof(struct message), GFP_ATOMIC); - - if (msg == NULL) { - dev_err(line6->ifcdev, "Out of memory\n"); + if (msg == NULL) return -ENOMEM; - } /* create URB: */ urb = usb_alloc_urb(0, GFP_ATOMIC); @@ -288,12 +263,35 @@ int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, } /* + Send asynchronous device version request. +*/ +int line6_version_request_async(struct usb_line6 *line6) +{ + char *buffer; + int retval; + + buffer = kmemdup(line6_request_version, + sizeof(line6_request_version), GFP_ATOMIC); + if (buffer == NULL) { + dev_err(line6->ifcdev, "Out of memory"); + return -ENOMEM; + } + + retval = line6_send_raw_message_async(line6, buffer, + sizeof(line6_request_version)); + kfree(buffer); + return retval; +} + +/* Send sysex message in pieces of wMaxPacketSize bytes. */ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, int size) { - return line6_send_raw_message(line6, buffer, size + SYSEX_EXTRA_SIZE) - SYSEX_EXTRA_SIZE; + return line6_send_raw_message(line6, buffer, + size + SYSEX_EXTRA_SIZE) - + SYSEX_EXTRA_SIZE; } /* @@ -304,12 +302,10 @@ int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, int size) { - char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_KERNEL); + char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC); - if (!buffer) { - dev_err(line6->ifcdev, "out of memory\n"); + if (!buffer) return NULL; - } buffer[0] = LINE6_SYSEX_BEGIN; memcpy(buffer + 1, line6_midi_id, sizeof(line6_midi_id)); @@ -325,66 +321,71 @@ char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, static void line6_data_received(struct urb *urb) { struct usb_line6 *line6 = (struct usb_line6 *)urb->context; - struct MidiBuffer *mb = &line6->line6midi->midibuf_in; + struct midi_buffer *mb = &line6->line6midi->midibuf_in; int done; if (urb->status == -ESHUTDOWN) return; -#if DO_DUMP_URB_RECEIVE - line6_dump_urb(urb); -#endif - - done = midibuf_write(mb, urb->transfer_buffer, urb->actual_length); + done = + line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length); if (done < urb->actual_length) { - midibuf_ignore(mb, done); - DEBUG_MESSAGES(dev_err(line6->ifcdev, "%d %d buffer overflow - message skipped\n", done, urb->actual_length)); + line6_midibuf_ignore(mb, done); + dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n", + done, urb->actual_length); } for (;;) { - done = midibuf_read(mb, line6->buffer_message, LINE6_MESSAGE_MAXLEN); + done = + line6_midibuf_read(mb, line6->buffer_message, + LINE6_MESSAGE_MAXLEN); if (done == 0) break; - /* MIDI input filter */ - if (midibuf_skip_message(mb, line6->line6midi->midi_mask_receive)) - continue; - line6->message_length = done; -#if DO_DUMP_MIDI_RECEIVE - line6_write_hexdump(line6, 'r', line6->buffer_message, done); -#endif line6_midi_receive(line6, line6->buffer_message, done); - switch (line6->usbdev->descriptor.idProduct) { + switch (le16_to_cpu(line6->usbdev->descriptor.idProduct)) { case LINE6_DEVID_BASSPODXT: case LINE6_DEVID_BASSPODXTLIVE: case LINE6_DEVID_BASSPODXTPRO: case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTPRO: case LINE6_DEVID_POCKETPOD: - pod_process_message((struct usb_line6_pod *)line6); + line6_pod_process_message((struct usb_line6_pod *) + line6); break; + case LINE6_DEVID_PODHD300: + case LINE6_DEVID_PODHD400: + case LINE6_DEVID_PODHD500: + break; /* let userspace handle MIDI */ + case LINE6_DEVID_PODXTLIVE: switch (line6->interface_number) { case PODXTLIVE_INTERFACE_POD: - pod_process_message((struct usb_line6_pod *)line6); + line6_pod_process_message((struct usb_line6_pod + *)line6); break; case PODXTLIVE_INTERFACE_VARIAX: - variax_process_message((struct usb_line6_variax *)line6); + line6_variax_process_message((struct + usb_line6_variax + *)line6); break; default: - dev_err(line6->ifcdev, "PODxt Live interface %d not supported\n", line6->interface_number); + dev_err(line6->ifcdev, + "PODxt Live interface %d not supported\n", + line6->interface_number); } break; case LINE6_DEVID_VARIAX: - variax_process_message((struct usb_line6_variax *)line6); + line6_variax_process_message((struct usb_line6_variax *) + line6); break; default: @@ -398,33 +399,27 @@ static void line6_data_received(struct urb *urb) /* Send channel number (i.e., switch to a different sound). */ -int line6_send_program(struct usb_line6 *line6, int value) +int line6_send_program(struct usb_line6 *line6, u8 value) { int retval; unsigned char *buffer; - unsigned int partial; + int partial; buffer = kmalloc(2, GFP_KERNEL); - - if (!buffer) { - dev_err(line6->ifcdev, "out of memory\n"); + if (!buffer) return -ENOMEM; - } buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST; buffer[1] = value; -#if DO_DUMP_URB_SEND - line6_write_hexdump(line6, 'S', buffer, 2); -#endif - retval = usb_interrupt_msg(line6->usbdev, usb_sndintpipe(line6->usbdev, line6->ep_control_write), buffer, 2, &partial, LINE6_TIMEOUT * HZ); if (retval) - dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval); + dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", + retval); kfree(buffer); return retval; @@ -433,33 +428,28 @@ int line6_send_program(struct usb_line6 *line6, int value) /* Transmit Line6 control parameter. */ -int line6_transmit_parameter(struct usb_line6 *line6, int param, int value) +int line6_transmit_parameter(struct usb_line6 *line6, int param, u8 value) { int retval; unsigned char *buffer; - unsigned int partial; + int partial; buffer = kmalloc(3, GFP_KERNEL); - - if (!buffer) { - dev_err(line6->ifcdev, "out of memory\n"); + if (!buffer) return -ENOMEM; - } buffer[0] = LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST; buffer[1] = param; buffer[2] = value; -#if DO_DUMP_URB_SEND - line6_write_hexdump(line6, 'S', buffer, 3); -#endif - retval = usb_interrupt_msg(line6->usbdev, - usb_sndintpipe(line6->usbdev, line6->ep_control_write), - buffer, 3, &partial, LINE6_TIMEOUT * HZ); + usb_sndintpipe(line6->usbdev, + line6->ep_control_write), + buffer, 3, &partial, LINE6_TIMEOUT * HZ); if (retval) - dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", retval); + dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", + retval); kfree(buffer); return retval; @@ -468,7 +458,8 @@ int line6_transmit_parameter(struct usb_line6 *line6, int param, int value) /* Read data from device. */ -int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t datalen) +int line6_read_data(struct usb_line6 *line6, int address, void *data, + size_t datalen) { struct usb_device *usbdev = line6->usbdev; int ret; @@ -476,15 +467,16 @@ int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t dat /* query the serial number: */ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - (datalen << 8) | 0x21, address, NULL, 0, LINE6_TIMEOUT * HZ); + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + (datalen << 8) | 0x21, address, + NULL, 0, LINE6_TIMEOUT * HZ); if (ret < 0) { dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); return ret; } - /* Wait for data length. We'll get a couple of 0xff until length arrives. */ + /* Wait for data length. We'll get 0xff until length arrives. */ do { ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | @@ -496,9 +488,7 @@ int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t dat "receive length failed (error %d)\n", ret); return ret; } - } - while (len == 0xff) - ; + } while (len == 0xff); if (len != datalen) { /* should be equal or something went wrong */ @@ -556,9 +546,7 @@ int line6_write_data(struct usb_line6 *line6, int address, void *data, "receiving status failed (error %d)\n", ret); return ret; } - } - while (status == 0xff) - ; + } while (status == 0xff); if (status != 0) { dev_err(line6->ifcdev, "write failed (error %d)\n", ret); @@ -574,7 +562,8 @@ int line6_write_data(struct usb_line6 *line6, int address, void *data, */ int line6_read_serial_number(struct usb_line6 *line6, int *serial_number) { - return line6_read_data(line6, 0x80d0, serial_number, sizeof(*serial_number)); + return line6_read_data(line6, 0x80d0, serial_number, + sizeof(*serial_number)); } /* @@ -587,29 +576,6 @@ ssize_t line6_nop_read(struct device *dev, struct device_attribute *attr, } /* - No operation (i.e., unsupported). -*/ -ssize_t line6_nop_write(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - return count; -} - -/* - "write" request on "raw" special file. -*/ -#if CREATE_RAW_FILE -ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6 *line6 = usb_get_intfdata(interface); - line6_send_raw_message(line6, buf, count); - return count; -} -#endif - -/* Generic destructor. */ static void line6_destruct(struct usb_interface *interface) @@ -636,31 +602,16 @@ static void line6_destruct(struct usb_interface *interface) kfree(line6); } -static void line6_list_devices(void) -{ - int i; - - for (i = 0; i < LINE6_MAX_DEVICES; ++i) { - struct usb_line6 *dev = line6_devices[i]; - printk(KERN_INFO "Line6 device %d: ", i); - - if (dev == NULL) - printk("(not used)\n"); - else - printk("%s:%d\n", dev->properties->name, dev->interface_number); - } -} - /* Probe USB device. */ -static int line6_probe(struct usb_interface *interface, const struct usb_device_id *id) +static int line6_probe(struct usb_interface *interface, + const struct usb_device_id *id) { int devtype; - struct usb_device *usbdev = NULL; - struct usb_line6 *line6 = NULL; + struct usb_device *usbdev; + struct usb_line6 *line6; const struct line6_properties *properties; - int devnum; int interface_number, alternate = 0; int product; int size = 0; @@ -673,30 +624,26 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ if (usbdev == NULL) return -ENODEV; - /* increment reference counters: */ - usb_get_intf(interface); - usb_get_dev(usbdev); - /* we don't handle multiple configurations */ - if (usbdev->descriptor.bNumConfigurations != 1) - return -ENODEV; + if (usbdev->descriptor.bNumConfigurations != 1) { + ret = -ENODEV; + goto err_put; + } /* check vendor and product id */ - for (devtype = ARRAY_SIZE(line6_id_table) - 1; devtype--;) - if ((le16_to_cpu(usbdev->descriptor.idVendor) == line6_id_table[devtype].idVendor) && - (le16_to_cpu(usbdev->descriptor.idProduct) == line6_id_table[devtype].idProduct)) - break; + for (devtype = ARRAY_SIZE(line6_id_table) - 1; devtype--;) { + u16 idVendor = le16_to_cpu(usbdev->descriptor.idVendor); + u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct); - if (devtype < 0) - return -ENODEV; - - /* find free slot in device table: */ - for (devnum = 0; devnum < LINE6_MAX_DEVICES; ++devnum) - if (line6_devices[devnum] == NULL) + if (idVendor == line6_id_table[devtype].idVendor && + idProduct == line6_id_table[devtype].idProduct) break; + } - if (devnum == LINE6_MAX_DEVICES) - return -ENODEV; + if (devtype < 0) { + ret = -ENODEV; + goto err_put; + } /* initialize device info: */ properties = &line6_properties_table[devtype]; @@ -708,12 +655,24 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ switch (product) { case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_POCKETPOD: case LINE6_DEVID_PODXTLIVE: case LINE6_DEVID_VARIAX: alternate = 1; break; + case LINE6_DEVID_POCKETPOD: + switch (interface_number) { + case 0: + return 0; /* this interface has no endpoints */ + case 1: + alternate = 0; + break; + default: + MISSING_CASE; + } + break; + + case LINE6_DEVID_PODHD500: case LINE6_DEVID_PODX3: case LINE6_DEVID_PODX3LIVE: switch (interface_number) { @@ -732,24 +691,32 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ case LINE6_DEVID_BASSPODXTPRO: case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTPRO: + case LINE6_DEVID_PODHD300: + case LINE6_DEVID_PODHD400: alternate = 5; break; - case LINE6_DEVID_TONEPORT_GX: case LINE6_DEVID_GUITARPORT: - alternate = 2; /* 1..4 seem to be ok */ + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_TONEPORT_GX: + case LINE6_DEVID_TONEPORT_UX1: + alternate = 2; /* 1..4 seem to be ok */ break; - case LINE6_DEVID_TONEPORT_UX1: case LINE6_DEVID_TONEPORT_UX2: + case LINE6_DEVID_PODSTUDIO_UX2: switch (interface_number) { case 0: /* defaults to 44.1kHz, 16-bit */ alternate = 2; break; case 1: - alternate = 0; - break; + /* don't know yet what this is ... + alternate = 1; + break; + */ + return -ENODEV; default: MISSING_CASE; } @@ -757,13 +724,14 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ default: MISSING_CASE; - return -ENODEV; + ret = -ENODEV; + goto err_put; } ret = usb_set_interface(usbdev, interface_number, alternate); if (ret < 0) { dev_err(&interface->dev, "set_interface failed\n"); - return ret; + goto err_put; } /* initialize device data based on product id: */ @@ -771,22 +739,43 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ case LINE6_DEVID_BASSPODXT: case LINE6_DEVID_BASSPODXTLIVE: case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_POCKETPOD: case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTPRO: size = sizeof(struct usb_line6_pod); - ep_read = 0x84; + ep_read = 0x84; + ep_write = 0x03; + break; + + case LINE6_DEVID_PODHD300: + case LINE6_DEVID_PODHD400: + size = sizeof(struct usb_line6_podhd); + ep_read = 0x84; ep_write = 0x03; break; + case LINE6_DEVID_PODHD500: + size = sizeof(struct usb_line6_podhd); + ep_read = 0x81; + ep_write = 0x01; + break; + + case LINE6_DEVID_POCKETPOD: + size = sizeof(struct usb_line6_pod); + ep_read = 0x82; + ep_write = 0x02; + break; + case LINE6_DEVID_PODX3: case LINE6_DEVID_PODX3LIVE: /* currently unused! */ size = sizeof(struct usb_line6_pod); - ep_read = 0x81; + ep_read = 0x81; ep_write = 0x01; break; + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: case LINE6_DEVID_TONEPORT_GX: case LINE6_DEVID_TONEPORT_UX1: case LINE6_DEVID_TONEPORT_UX2: @@ -799,42 +788,45 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ switch (interface_number) { case PODXTLIVE_INTERFACE_POD: size = sizeof(struct usb_line6_pod); - ep_read = 0x84; + ep_read = 0x84; ep_write = 0x03; break; case PODXTLIVE_INTERFACE_VARIAX: size = sizeof(struct usb_line6_variax); - ep_read = 0x86; + ep_read = 0x86; ep_write = 0x05; break; default: - return -ENODEV; + ret = -ENODEV; + goto err_put; } break; case LINE6_DEVID_VARIAX: size = sizeof(struct usb_line6_variax); - ep_read = 0x82; + ep_read = 0x82; ep_write = 0x01; break; default: MISSING_CASE; - return -ENODEV; + ret = -ENODEV; + goto err_put; } if (size == 0) { - dev_err(line6->ifcdev, "driver bug: interface data size not set\n"); - return -ENODEV; + dev_err(&interface->dev, + "driver bug: interface data size not set\n"); + ret = -ENODEV; + goto err_put; } line6 = kzalloc(size, GFP_KERNEL); - if (line6 == NULL) { - dev_err(&interface->dev, "Out of memory\n"); - return -ENOMEM; + ret = -ENODEV; + goto err_put; } /* store basic data: */ @@ -849,16 +841,19 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ /* get data from endpoint descriptor (see usb_maxpacket): */ { struct usb_host_endpoint *ep; - unsigned epnum = usb_pipeendpoint(usb_rcvintpipe(usbdev, ep_read)); + unsigned epnum = + usb_pipeendpoint(usb_rcvintpipe(usbdev, ep_read)); ep = usbdev->ep_in[epnum]; if (ep != NULL) { line6->interval = ep->desc.bInterval; - line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + line6->max_packet_size = + le16_to_cpu(ep->desc.wMaxPacketSize); } else { line6->interval = LINE6_FALLBACK_INTERVAL; line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE; - dev_err(line6->ifcdev, "endpoint not available, using fallback values"); + dev_err(line6->ifcdev, + "endpoint not available, using fallback values"); } } @@ -866,20 +861,18 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ if (properties->capabilities & LINE6_BIT_CONTROL) { /* initialize USB buffers: */ - line6->buffer_listen = kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); - + line6->buffer_listen = + kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); if (line6->buffer_listen == NULL) { - dev_err(&interface->dev, "Out of memory\n"); - line6_destruct(interface); - return -ENOMEM; + ret = -ENOMEM; + goto err_destruct; } - line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL); - + line6->buffer_message = + kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL); if (line6->buffer_message == NULL) { - dev_err(&interface->dev, "Out of memory\n"); - line6_destruct(interface); - return -ENOMEM; + ret = -ENOMEM; + goto err_destruct; } line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL); @@ -887,15 +880,15 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ if (line6->urb_listen == NULL) { dev_err(&interface->dev, "Out of memory\n"); line6_destruct(interface); - return -ENOMEM; + ret = -ENOMEM; + goto err_destruct; } ret = line6_start_listen(line6); if (ret < 0) { dev_err(&interface->dev, "%s: usb_submit_urb failed\n", __func__); - line6_destruct(interface); - return ret; + goto err_destruct; } } @@ -909,17 +902,28 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ case LINE6_DEVID_PODX3LIVE: case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTPRO: - ret = pod_init(interface, (struct usb_line6_pod *)line6); + ret = line6_pod_init(interface, (struct usb_line6_pod *)line6); + break; + + case LINE6_DEVID_PODHD300: + case LINE6_DEVID_PODHD400: + case LINE6_DEVID_PODHD500: + ret = line6_podhd_init(interface, + (struct usb_line6_podhd *)line6); break; case LINE6_DEVID_PODXTLIVE: switch (interface_number) { case PODXTLIVE_INTERFACE_POD: - ret = pod_init(interface, (struct usb_line6_pod *)line6); + ret = + line6_pod_init(interface, + (struct usb_line6_pod *)line6); break; case PODXTLIVE_INTERFACE_VARIAX: - ret = variax_init(interface, (struct usb_line6_variax *)line6); + ret = + line6_variax_init(interface, + (struct usb_line6_variax *)line6); break; default: @@ -932,14 +936,21 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ break; case LINE6_DEVID_VARIAX: - ret = variax_init(interface, (struct usb_line6_variax *)line6); + ret = + line6_variax_init(interface, + (struct usb_line6_variax *)line6); break; + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: case LINE6_DEVID_TONEPORT_GX: case LINE6_DEVID_TONEPORT_UX1: case LINE6_DEVID_TONEPORT_UX2: case LINE6_DEVID_GUITARPORT: - ret = toneport_init(interface, (struct usb_line6_toneport *)line6); + ret = + line6_toneport_init(interface, + (struct usb_line6_toneport *)line6); break; default: @@ -947,22 +958,36 @@ static int line6_probe(struct usb_interface *interface, const struct usb_device_ ret = -ENODEV; } - if (ret < 0) { - line6_destruct(interface); - return ret; - } + if (ret < 0) + goto err_destruct; ret = sysfs_create_link(&interface->dev.kobj, &usbdev->dev.kobj, "usb_device"); - if (ret < 0) { - line6_destruct(interface); - return ret; - } + if (ret < 0) + goto err_destruct; + + /* creation of additional special files should go here */ dev_info(&interface->dev, "Line6 %s now attached\n", line6->properties->name); - line6_devices[devnum] = line6; - line6_list_devices(); + + switch (product) { + case LINE6_DEVID_PODX3: + case LINE6_DEVID_PODX3LIVE: + dev_info(&interface->dev, + "NOTE: the Line6 %s is detected, but not yet supported\n", + line6->properties->name); + } + + /* increment reference counters: */ + usb_get_intf(interface); + usb_get_dev(usbdev); + + return 0; + +err_destruct: + line6_destruct(interface); +err_put: return ret; } @@ -973,7 +998,7 @@ static void line6_disconnect(struct usb_interface *interface) { struct usb_line6 *line6; struct usb_device *usbdev; - int interface_number, i; + int interface_number; if (interface == NULL) return; @@ -981,6 +1006,8 @@ static void line6_disconnect(struct usb_interface *interface) if (usbdev == NULL) return; + /* removal of additional special files should go here */ + sysfs_remove_link(&interface->dev.kobj, "usb_device"); interface_number = interface->cur_altsetting->desc.bInterfaceNumber; @@ -988,13 +1015,13 @@ static void line6_disconnect(struct usb_interface *interface) if (line6 != NULL) { if (line6->urb_listen != NULL) - usb_kill_urb(line6->urb_listen); + line6_stop_listen(line6); if (usbdev != line6->usbdev) dev_err(line6->ifcdev, "driver bug: inconsistent usb device\n"); - switch (line6->usbdev->descriptor.idProduct) { + switch (le16_to_cpu(line6->usbdev->descriptor.idProduct)) { case LINE6_DEVID_BASSPODXT: case LINE6_DEVID_BASSPODXTLIVE: case LINE6_DEVID_BASSPODXTPRO: @@ -1003,42 +1030,48 @@ static void line6_disconnect(struct usb_interface *interface) case LINE6_DEVID_PODX3LIVE: case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTPRO: - pod_disconnect(interface); + line6_pod_disconnect(interface); + break; + + case LINE6_DEVID_PODHD300: + case LINE6_DEVID_PODHD400: + case LINE6_DEVID_PODHD500: + line6_podhd_disconnect(interface); break; case LINE6_DEVID_PODXTLIVE: switch (interface_number) { case PODXTLIVE_INTERFACE_POD: - pod_disconnect(interface); + line6_pod_disconnect(interface); break; case PODXTLIVE_INTERFACE_VARIAX: - variax_disconnect(interface); + line6_variax_disconnect(interface); break; } break; case LINE6_DEVID_VARIAX: - variax_disconnect(interface); + line6_variax_disconnect(interface); break; + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: case LINE6_DEVID_TONEPORT_GX: case LINE6_DEVID_TONEPORT_UX1: case LINE6_DEVID_TONEPORT_UX2: case LINE6_DEVID_GUITARPORT: - toneport_disconnect(interface); + line6_toneport_disconnect(interface); break; default: MISSING_CASE; } - dev_info(&interface->dev, "Line6 %s now disconnected\n", line6->properties->name); - - for (i = LINE6_MAX_DEVICES; i--;) - if (line6_devices[i] == line6) - line6_devices[i] = NULL; + dev_info(&interface->dev, "Line6 %s now disconnected\n", + line6->properties->name); } line6_destruct(interface); @@ -1046,55 +1079,82 @@ static void line6_disconnect(struct usb_interface *interface) /* decrement reference counters: */ usb_put_intf(interface); usb_put_dev(usbdev); - - line6_list_devices(); } -static struct usb_driver line6_driver = { - .name = DRIVER_NAME, - .probe = line6_probe, - .disconnect = line6_disconnect, - .id_table = line6_id_table, -}; +#ifdef CONFIG_PM /* - Module initialization. + Suspend Line6 device. */ -static int __init line6_init(void) +static int line6_suspend(struct usb_interface *interface, pm_message_t message) { - int i, retval; + struct usb_line6 *line6 = usb_get_intfdata(interface); + struct snd_line6_pcm *line6pcm = line6->line6pcm; - printk(KERN_INFO "%s driver version %s%s\n", - DRIVER_NAME, DRIVER_VERSION, DRIVER_REVISION); - line6_workqueue = create_workqueue(DRIVER_NAME); + snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot); - if (line6_workqueue == NULL) { - err("couldn't create workqueue"); - return -EINVAL; + if (line6->properties->capabilities & LINE6_BIT_CONTROL) + line6_stop_listen(line6); + + if (line6pcm != NULL) { + snd_pcm_suspend_all(line6pcm->pcm); + line6_pcm_disconnect(line6pcm); + line6pcm->flags = 0; } - for (i = LINE6_MAX_DEVICES; i--;) - line6_devices[i] = NULL; + return 0; +} - retval = usb_register(&line6_driver); +/* + Resume Line6 device. +*/ +static int line6_resume(struct usb_interface *interface) +{ + struct usb_line6 *line6 = usb_get_intfdata(interface); - if (retval) - err("usb_register failed. Error number %d", retval); + if (line6->properties->capabilities & LINE6_BIT_CONTROL) + line6_start_listen(line6); - return retval; + snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0); + return 0; } /* - Module cleanup. + Resume Line6 device after reset. */ -static void __exit line6_exit(void) +static int line6_reset_resume(struct usb_interface *interface) { - destroy_workqueue(line6_workqueue); - usb_deregister(&line6_driver); + struct usb_line6 *line6 = usb_get_intfdata(interface); + + switch (le16_to_cpu(line6->usbdev->descriptor.idProduct)) { + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: + case LINE6_DEVID_TONEPORT_GX: + case LINE6_DEVID_TONEPORT_UX1: + case LINE6_DEVID_TONEPORT_UX2: + case LINE6_DEVID_GUITARPORT: + line6_toneport_reset_resume((struct usb_line6_toneport *)line6); + } + + return line6_resume(interface); } -module_init(line6_init); -module_exit(line6_exit); +#endif /* CONFIG_PM */ + +static struct usb_driver line6_driver = { + .name = DRIVER_NAME, + .probe = line6_probe, + .disconnect = line6_disconnect, +#ifdef CONFIG_PM + .suspend = line6_suspend, + .resume = line6_resume, + .reset_resume = line6_reset_resume, +#endif + .id_table = line6_id_table, +}; + +module_usb_driver(line6_driver); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h index 9908bfa6afa..16e3fc2f1f1 100644 --- a/drivers/staging/line6/driver.h +++ b/drivers/staging/line6/driver.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -12,12 +12,8 @@ #ifndef DRIVER_H #define DRIVER_H - -#include "config.h" - #include <linux/spinlock.h> #include <linux/usb.h> -#include <linux/wait.h> #include <sound/core.h> #include "midi.h" @@ -25,11 +21,9 @@ #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 */ @@ -50,16 +44,14 @@ */ #define LINE6_CHANNEL_DEVICE 0x02 -#define LINE6_CHANNEL_UNKNOWN 5 /* don't know yet what this is good for */ +#define LINE6_CHANNEL_UNKNOWN 5 /* don't know yet what this is good for */ #define LINE6_CHANNEL_MASK 0x0f - #define MISSING_CASE \ - printk(KERN_ERR "line6usb driver bug: missing case in %s:%d\n", \ + pr_err("line6usb driver bug: missing case in %s:%d\n", \ __FILE__, __LINE__) - #define CHECK_RETURN(x) \ do { \ err = x; \ @@ -67,21 +59,43 @@ do { \ return err; \ } while (0) +#define CHECK_STARTUP_PROGRESS(x, n) \ +do { \ + if ((x) >= (n)) \ + return; \ + x = (n); \ +} while (0) 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; + /** + Bit identifying this device in the line6usb driver. + */ int device_bit; + + /** + Card id string (maximum 16 characters). + This can be used to address the device in ALSA programs as + "default:CARD=<id>" + */ + const char *id; + + /** + Card short name (maximum 32 characters). + */ + const char *name; + + /** + Bit vector defining this device's capabilities in the + line6usb driver. + */ int capabilities; }; @@ -172,19 +186,15 @@ struct usb_line6 { 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, struct device_attribute *attr, char *buf); -extern ssize_t line6_nop_write(struct device *dev, - struct device_attribute *attr, - 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_program(struct usb_line6 *line6, u8 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, @@ -193,12 +203,13 @@ extern int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, int size); extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +extern void line6_start_timer(struct timer_list *timer, unsigned int msecs, + void (*function)(unsigned long), + unsigned long data); extern int line6_transmit_parameter(struct usb_line6 *line6, int param, - int value); + u8 value); +extern int line6_version_request_async(struct usb_line6 *line6); extern int line6_write_data(struct usb_line6 *line6, int address, void *data, size_t datalen); -extern void line6_write_hexdump(struct usb_line6 *line6, char dir, - const unsigned char *buffer, int size); - #endif diff --git a/drivers/staging/line6/dumprequest.c b/drivers/staging/line6/dumprequest.c deleted file mode 100644 index decbaa971b6..00000000000 --- a/drivers/staging/line6/dumprequest.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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); -} diff --git a/drivers/staging/line6/dumprequest.h b/drivers/staging/line6/dumprequest.h deleted file mode 100644 index 1975d54b3c2..00000000000 --- a/drivers/staging/line6/dumprequest.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 diff --git a/drivers/staging/line6/midi.c b/drivers/staging/line6/midi.c index 89a2b17e9ca..1ac343b649c 100644 --- a/drivers/staging/line6/midi.c +++ b/drivers/staging/line6/midi.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,31 +9,23 @@ * */ -#include "driver.h" - +#include <linux/slab.h> #include <linux/usb.h> - #include <sound/core.h> #include <sound/rawmidi.h> #include "audio.h" +#include "driver.h" #include "midi.h" #include "pod.h" #include "usbdefs.h" - -#define USE_MIDIBUF 1 -#define OUTPUT_DUMP_ONLY 0 - - #define line6_rawmidi_substream_midi(substream) \ ((struct snd_line6_midi *)((substream)->rmidi->private_data)) - static int send_midi_async(struct usb_line6 *line6, unsigned char *data, int length); - /* Pass data received via USB to MIDI. */ @@ -50,38 +42,34 @@ void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, */ static void line6_midi_transmit(struct snd_rawmidi_substream *substream) { - struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6; + struct usb_line6 *line6 = + line6_rawmidi_substream_midi(substream)->line6; struct snd_line6_midi *line6midi = line6->line6midi; - struct MidiBuffer *mb = &line6midi->midibuf_out; + struct midi_buffer *mb = &line6midi->midibuf_out; unsigned long flags; - unsigned char chunk[line6->max_packet_size]; + unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE]; int req, done; spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags); for (;;) { - req = min(midibuf_bytes_free(mb), line6->max_packet_size); + req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size); done = snd_rawmidi_transmit_peek(substream, chunk, req); if (done == 0) break; -#if DO_DUMP_MIDI_SEND - line6_write_hexdump(line6, 's', chunk, done); -#endif - midibuf_write(mb, chunk, done); + line6_midibuf_write(mb, chunk, done); snd_rawmidi_transmit_ack(substream, done); } for (;;) { - done = midibuf_read(mb, chunk, line6->max_packet_size); + done = line6_midibuf_read(mb, chunk, + LINE6_FALLBACK_MAXPACKETSIZE); if (done == 0) break; - if (midibuf_skip_message(mb, line6midi->midi_mask_transmit)) - continue; - send_midi_async(line6, chunk, done); } @@ -114,7 +102,7 @@ static void midi_sent(struct urb *urb) } if (num == 0) - wake_up_interruptible(&line6->line6midi->send_wait); + wake_up(&line6->line6midi->send_wait); spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); } @@ -133,24 +121,19 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data, urb = usb_alloc_urb(0, GFP_ATOMIC); - if (urb == 0) { + if (urb == NULL) { 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 = kmemdup(data, length, GFP_ATOMIC); - transfer_buffer = kmalloc(length, GFP_ATOMIC); - - if (transfer_buffer == 0) { + if (transfer_buffer == NULL) { 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), @@ -162,27 +145,10 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data, if (retval < 0) { dev_err(line6->ifcdev, "usb_submit_urb failed\n"); usb_free_urb(urb); - return -EINVAL; + return retval; } ++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; } @@ -200,7 +166,8 @@ 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; + struct usb_line6 *line6 = + line6_rawmidi_substream_midi(substream)->line6; line6->line6midi->substream_transmit = substream; spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); @@ -213,20 +180,12 @@ static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, static void line6_midi_output_drain(struct snd_rawmidi_substream *substream) { - struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6; - wait_queue_head_t *head = &line6->line6midi->send_wait; - DECLARE_WAITQUEUE(wait, current); - add_wait_queue(head, &wait); - current->state = TASK_INTERRUPTIBLE; - - while (line6->line6midi->num_active_send_urbs > 0) - if (signal_pending(current)) - break; - else - schedule(); + struct usb_line6 *line6 = + line6_rawmidi_substream_midi(substream)->line6; + struct snd_line6_midi *midi = line6->line6midi; - current->state = TASK_RUNNING; - remove_wait_queue(head, &wait); + wait_event_interruptible(midi->send_wait, + midi->num_active_send_urbs == 0); } static int line6_midi_input_open(struct snd_rawmidi_substream *substream) @@ -242,12 +201,13 @@ static int line6_midi_input_close(struct snd_rawmidi_substream *substream) static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) { - struct usb_line6 *line6 = line6_rawmidi_substream_midi(substream)->line6; + struct usb_line6 *line6 = + line6_rawmidi_substream_midi(substream)->line6; if (up) line6->line6midi->substream_receive = substream; else - line6->line6midi->substream_receive = 0; + line6->line6midi->substream_receive = NULL; } static struct snd_rawmidi_ops line6_midi_output_ops = { @@ -283,12 +243,12 @@ static int snd_line6_new_midi(struct snd_line6_midi *line6midi) rmidi->private_data = line6midi; rmidi->private_free = line6_cleanup_midi; + strcpy(rmidi->id, line6midi->line6->properties->id); strcpy(rmidi->name, line6midi->line6->properties->name); rmidi->info_flags = - SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | - SNDRV_RAWMIDI_INFO_DUPLEX; + 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); @@ -297,69 +257,13 @@ static int snd_line6_new_midi(struct snd_line6_midi *line6midi) return 0; } -/* - "read" request on "midi_mask_transmit" special file. -*/ -static ssize_t midi_get_midi_mask_transmit(struct device *dev, - struct device_attribute *attr, - 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, - struct device_attribute *attr, - 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, - struct device_attribute *attr, - 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, - struct device_attribute *attr, - 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); + + line6_midibuf_destroy(&line6midi->midibuf_in); + line6_midibuf_destroy(&line6midi->midibuf_out); return 0; } @@ -375,25 +279,30 @@ int line6_init_midi(struct usb_line6 *line6) int err; struct snd_line6_midi *line6midi; - if (!(line6->properties->capabilities & LINE6_BIT_CONTROL)) - return 0; /* skip MIDI initialization and report success */ + if (!(line6->properties->capabilities & LINE6_BIT_CONTROL)) { + /* skip MIDI initialization and report success */ + return 0; + } 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) + err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); + if (err < 0) { + kfree(line6midi); return err; + } - err = midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); - if (err < 0) + err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); + if (err < 0) { + kfree(line6midi->midibuf_in.buf); + kfree(line6midi); return err; + } line6midi->line6 = line6; - line6midi->midi_mask_transmit = 1; - line6midi->midi_mask_receive = 4; line6->line6midi = line6midi; err = snd_device_new(line6->card, SNDRV_DEV_RAWMIDI, line6midi, @@ -401,20 +310,10 @@ int line6_init_midi(struct usb_line6 *line6) if (err < 0) return err; - snd_card_set_dev(line6->card, line6->ifcdev); - err = snd_line6_new_midi(line6midi); if (err < 0) return err; - err = device_create_file(line6->ifcdev, &dev_attr_midi_mask_transmit); - if (err < 0) - return err; - - err = device_create_file(line6->ifcdev, &dev_attr_midi_mask_receive); - if (err < 0) - return err; - init_waitqueue_head(&line6midi->send_wait); spin_lock_init(&line6midi->send_urb_lock); spin_lock_init(&line6midi->midi_transmit_lock); diff --git a/drivers/staging/line6/midi.h b/drivers/staging/line6/midi.h index c69fd118957..78f903fb4d4 100644 --- a/drivers/staging/line6/midi.h +++ b/drivers/staging/line6/midi.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -12,15 +12,12 @@ #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. @@ -58,30 +55,18 @@ struct snd_line6_midi { 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; + struct midi_buffer midibuf_in; /** Buffer for outgoing MIDI stream. */ - struct MidiBuffer midibuf_out; + struct midi_buffer 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 diff --git a/drivers/staging/line6/midibuf.c b/drivers/staging/line6/midibuf.c index ab0a5f30fbc..f0adb7baa60 100644 --- a/drivers/staging/line6/midibuf.c +++ b/drivers/staging/line6/midibuf.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,13 +9,10 @@ * */ -#include "config.h" - #include <linux/slab.h> #include "midibuf.h" - static int midibuf_message_length(unsigned char code) { if (code < 0x80) @@ -25,23 +22,34 @@ static int midibuf_message_length(unsigned char code) return length[(code >> 4) - 8]; } else { /* - Note that according to the MIDI specification 0xf2 is - the "Song Position Pointer", but this is used by Line6 - to send sysex messages to the host. - */ + Note that according to the MIDI specification 0xf2 is + the "Song Position Pointer", but this is used by Line6 + to send sysex messages to the host. + */ static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1, - 1, 1, 1, -1, 1, 1 }; + 1, 1, 1, -1, 1, 1 + }; return length[code & 0x0f]; } } -void midibuf_reset(struct MidiBuffer *this) +static int midibuf_is_empty(struct midi_buffer *this) +{ + return (this->pos_read == this->pos_write) && !this->full; +} + +static int midibuf_is_full(struct midi_buffer *this) +{ + return this->full; +} + +void line6_midibuf_reset(struct midi_buffer *this) { this->pos_read = this->pos_write = this->full = 0; this->command_prev = -1; } -int midibuf_init(struct MidiBuffer *this, int size, int split) +int line6_midibuf_init(struct midi_buffer *this, int size, int split) { this->buf = kmalloc(size, GFP_KERNEL); @@ -50,44 +58,37 @@ int midibuf_init(struct MidiBuffer *this, int size, int split) this->size = size; this->split = split; - midibuf_reset(this); + line6_midibuf_reset(this); return 0; } -void midibuf_status(struct MidiBuffer *this) +void line6_midibuf_status(struct midi_buffer *this) { - printk(KERN_DEBUG "midibuf size=%d split=%d pos_read=%d pos_write=%d " - "full=%d command_prev=%02x\n", this->size, this->split, - this->pos_read, this->pos_write, this->full, this->command_prev); -} - -static int midibuf_is_empty(struct MidiBuffer *this) -{ - return (this->pos_read == this->pos_write) && !this->full; -} - -static int midibuf_is_full(struct MidiBuffer *this) -{ - return this->full; + pr_debug("midibuf size=%d split=%d pos_read=%d pos_write=%d full=%d command_prev=%02x\n", + this->size, this->split, this->pos_read, this->pos_write, + this->full, this->command_prev); } -int midibuf_bytes_free(struct MidiBuffer *this) +int line6_midibuf_bytes_free(struct midi_buffer *this) { return - midibuf_is_full(this) ? - 0 : - (this->pos_read - this->pos_write + this->size - 1) % this->size + 1; + midibuf_is_full(this) ? + 0 : + (this->pos_read - this->pos_write + this->size - 1) % this->size + + 1; } -int midibuf_bytes_used(struct MidiBuffer *this) +int line6_midibuf_bytes_used(struct midi_buffer *this) { return - midibuf_is_empty(this) ? - 0 : - (this->pos_write - this->pos_read + this->size - 1) % this->size + 1; + 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 line6_midibuf_write(struct midi_buffer *this, unsigned char *data, + int length) { int bytes_free; int length1, length2; @@ -102,7 +103,7 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length) skip_active_sense = 1; } - bytes_free = midibuf_bytes_free(this); + bytes_free = line6_midibuf_bytes_free(this); if (length > bytes_free) length = bytes_free; @@ -129,7 +130,8 @@ int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length) return length + skip_active_sense; } -int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) +int line6_midibuf_read(struct midi_buffer *this, unsigned char *data, + int length) { int bytes_used; int length1, length2; @@ -145,7 +147,7 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) if (midibuf_is_empty(this)) return 0; - bytes_used = midibuf_bytes_used(this); + bytes_used = line6_midibuf_bytes_used(this); if (length > bytes_used) length = bytes_used; @@ -160,7 +162,8 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) this->command_prev = command; } else { if (this->command_prev > 0) { - int midi_length_prev = midibuf_message_length(this->command_prev); + int midi_length_prev = + midibuf_message_length(this->command_prev); if (midi_length_prev > 0) { midi_length = midi_length_prev - 1; @@ -200,15 +203,15 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) } if (midi_length == length) - midi_length = -1; /* end of message not found */ + midi_length = -1; /* end of message not found */ } if (midi_length < 0) { if (!this->split) - return 0; /* command is not yet complete */ + return 0; /* command is not yet complete */ } else { if (length < midi_length) - return 0; /* command is not yet complete */ + return 0; /* command is not yet complete */ length = midi_length; } @@ -232,9 +235,9 @@ int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length) return length + repeat; } -int midibuf_ignore(struct MidiBuffer *this, int length) +int line6_midibuf_ignore(struct midi_buffer *this, int length) { - int bytes_used = midibuf_bytes_used(this); + int bytes_used = line6_midibuf_bytes_used(this); if (length > bytes_used) length = bytes_used; @@ -244,7 +247,7 @@ int midibuf_ignore(struct MidiBuffer *this, int length) return length; } -int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask) +int line6_midibuf_skip_message(struct midi_buffer *this, unsigned short mask) { int cmd = this->command_prev; @@ -255,7 +258,7 @@ int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask) return 0; } -void midibuf_destroy(struct MidiBuffer *this) +void line6_midibuf_destroy(struct midi_buffer *this) { kfree(this->buf); this->buf = NULL; diff --git a/drivers/staging/line6/midibuf.h b/drivers/staging/line6/midibuf.h index 9877581bcd9..707482b940e 100644 --- a/drivers/staging/line6/midibuf.h +++ b/drivers/staging/line6/midibuf.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -12,8 +12,7 @@ #ifndef MIDIBUF_H #define MIDIBUF_H - -struct MidiBuffer { +struct midi_buffer { unsigned char *buf; int size; int split; @@ -22,18 +21,18 @@ struct MidiBuffer { 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); - +extern int line6_midibuf_bytes_used(struct midi_buffer *mb); +extern int line6_midibuf_bytes_free(struct midi_buffer *mb); +extern void line6_midibuf_destroy(struct midi_buffer *mb); +extern int line6_midibuf_ignore(struct midi_buffer *mb, int length); +extern int line6_midibuf_init(struct midi_buffer *mb, int size, int split); +extern int line6_midibuf_read(struct midi_buffer *mb, unsigned char *data, + int length); +extern void line6_midibuf_reset(struct midi_buffer *mb); +extern int line6_midibuf_skip_message(struct midi_buffer *mb, + unsigned short mask); +extern void line6_midibuf_status(struct midi_buffer *mb); +extern int line6_midibuf_write(struct midi_buffer *mb, unsigned char *data, + int length); #endif diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c index fc4113f3315..a3136b189ee 100644 --- a/drivers/staging/line6/pcm.c +++ b/drivers/staging/line6/pcm.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,8 +9,7 @@ * */ -#include "driver.h" - +#include <linux/slab.h> #include <sound/core.h> #include <sound/control.h> #include <sound/pcm.h> @@ -18,9 +17,212 @@ #include "audio.h" #include "capture.h" +#include "driver.h" #include "playback.h" #include "pod.h" +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + +static struct snd_line6_pcm *dev2pcm(struct device *dev) +{ + struct usb_interface *interface = to_usb_interface(dev); + struct usb_line6 *line6 = usb_get_intfdata(interface); + struct snd_line6_pcm *line6pcm = line6->line6pcm; + return line6pcm; +} + +/* + "read" request on "impulse_volume" special file. +*/ +static ssize_t impulse_volume_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_volume); +} + +/* + "write" request on "impulse_volume" special file. +*/ +static ssize_t impulse_volume_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_line6_pcm *line6pcm = dev2pcm(dev); + int value; + int ret; + + ret = kstrtoint(buf, 10, &value); + if (ret < 0) + return ret; + + line6pcm->impulse_volume = value; + + if (value > 0) + line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_IMPULSE); + else + line6_pcm_release(line6pcm, LINE6_BITS_PCM_IMPULSE); + + return count; +} +static DEVICE_ATTR_RW(impulse_volume); + +/* + "read" request on "impulse_period" special file. +*/ +static ssize_t impulse_period_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_period); +} + +/* + "write" request on "impulse_period" special file. +*/ +static ssize_t impulse_period_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + int ret; + + ret = kstrtoint(buf, 10, &value); + if (ret < 0) + return ret; + + dev2pcm(dev)->impulse_period = value; + return count; +} +static DEVICE_ATTR_RW(impulse_period); + +#endif + +static bool test_flags(unsigned long flags0, unsigned long flags1, + unsigned long mask) +{ + return ((flags0 & mask) == 0) && ((flags1 & mask) != 0); +} + +int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels) +{ + unsigned long flags_old, flags_new, flags_final; + int err; + + do { + flags_old = ACCESS_ONCE(line6pcm->flags); + flags_new = flags_old | channels; + } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); + + flags_final = flags_old; + + line6pcm->prev_fbuf = NULL; + + if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_BUFFER)) { + /* Invoked multiple times in a row so allocate once only */ + if (!line6pcm->buffer_in) { + line6pcm->buffer_in = + kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * + line6pcm->max_packet_size, GFP_KERNEL); + if (!line6pcm->buffer_in) { + err = -ENOMEM; + goto pcm_acquire_error; + } + + flags_final |= channels & LINE6_BITS_CAPTURE_BUFFER; + } + } + + if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_STREAM)) { + /* + Waiting for completion of active URBs in the stop handler is + a bug, we therefore report an error if capturing is restarted + too soon. + */ + if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) { + dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); + return -EBUSY; + } + + line6pcm->count_in = 0; + line6pcm->prev_fsize = 0; + err = line6_submit_audio_in_all_urbs(line6pcm); + + if (err < 0) + goto pcm_acquire_error; + + flags_final |= channels & LINE6_BITS_CAPTURE_STREAM; + } + + if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_BUFFER)) { + /* Invoked multiple times in a row so allocate once only */ + if (!line6pcm->buffer_out) { + line6pcm->buffer_out = + kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * + line6pcm->max_packet_size, GFP_KERNEL); + if (!line6pcm->buffer_out) { + err = -ENOMEM; + goto pcm_acquire_error; + } + + flags_final |= channels & LINE6_BITS_PLAYBACK_BUFFER; + } + } + + if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_STREAM)) { + /* + See comment above regarding PCM restart. + */ + if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) { + dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); + return -EBUSY; + } + + line6pcm->count_out = 0; + err = line6_submit_audio_out_all_urbs(line6pcm); + + if (err < 0) + goto pcm_acquire_error; + + flags_final |= channels & LINE6_BITS_PLAYBACK_STREAM; + } + + return 0; + +pcm_acquire_error: + /* + If not all requested resources/streams could be obtained, release + those which were successfully obtained (if any). + */ + line6_pcm_release(line6pcm, flags_final & channels); + return err; +} + +int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels) +{ + unsigned long flags_old, flags_new; + + do { + flags_old = ACCESS_ONCE(line6pcm->flags); + flags_new = flags_old & ~channels; + } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); + + if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_STREAM)) + line6_unlink_audio_in_urbs(line6pcm); + + if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_BUFFER)) { + line6_wait_clear_audio_in_urbs(line6pcm); + line6_free_capture_buffer(line6pcm); + } + + if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_STREAM)) + line6_unlink_audio_out_urbs(line6pcm); + + if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_BUFFER)) { + line6_wait_clear_audio_out_urbs(line6pcm); + line6_free_playback_buffer(line6pcm); + } + + return 0; +} /* trigger callback */ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) @@ -31,12 +233,12 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) unsigned long flags; spin_lock_irqsave(&line6pcm->lock_trigger, flags); - clear_bit(BIT_PREPARED, &line6pcm->flags); + clear_bit(LINE6_INDEX_PREPARED, &line6pcm->flags); snd_pcm_group_for_each_entry(s, substream) { switch (s->stream) { case SNDRV_PCM_STREAM_PLAYBACK: - err = snd_line6_playback_trigger(s, cmd); + err = snd_line6_playback_trigger(line6pcm, cmd); if (err < 0) { spin_unlock_irqrestore(&line6pcm->lock_trigger, @@ -47,7 +249,7 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) break; case SNDRV_PCM_STREAM_CAPTURE: - err = snd_line6_capture_trigger(s, cmd); + err = snd_line6_capture_trigger(line6pcm, cmd); if (err < 0) { spin_unlock_irqrestore(&line6pcm->lock_trigger, @@ -58,8 +260,8 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) break; default: - dev_err(s2m(substream), "Unknown stream direction %d\n", - s->stream); + dev_err(line6pcm->line6->ifcdev, + "Unknown stream direction %d\n", s->stream); } } @@ -68,8 +270,8 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) } /* control info callback */ -static int snd_line6_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; @@ -79,28 +281,30 @@ static int snd_line6_control_info(struct snd_kcontrol *kcontrol, } /* control get callback */ -static int snd_line6_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { int i; struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); for (i = 2; i--;) - ucontrol->value.integer.value[i] = line6pcm->volume[i]; + ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; return 0; } /* control put callback */ -static int snd_line6_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { int i, changed = 0; struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); for (i = 2; i--;) - if (line6pcm->volume[i] != ucontrol->value.integer.value[i]) { - line6pcm->volume[i] = ucontrol->value.integer.value[i]; + if (line6pcm->volume_playback[i] != + ucontrol->value.integer.value[i]) { + line6pcm->volume_playback[i] = + ucontrol->value.integer.value[i]; changed = 1; } @@ -108,14 +312,14 @@ static int snd_line6_control_put(struct snd_kcontrol *kcontrol, } /* control definition */ -static struct snd_kcontrol_new line6_control = { +static struct snd_kcontrol_new line6_control_playback = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", .index = 0, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_line6_control_info, - .get = snd_line6_control_get, - .put = snd_line6_control_put + .info = snd_line6_control_playback_info, + .get = snd_line6_control_playback_get, + .put = snd_line6_control_playback_put }; /* @@ -126,6 +330,11 @@ static void line6_cleanup_pcm(struct snd_pcm *pcm) int i; struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_volume); + device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_period); +#endif + for (i = LINE6_ISO_BUFFERS; i--;) { if (line6pcm->urb_audio_out[i]) { usb_kill_urb(line6pcm->urb_audio_out[i]); @@ -145,8 +354,8 @@ static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm) int err; err = snd_pcm_new(line6pcm->line6->card, - (char *)line6pcm->line6->properties->name, - 0, 1, 1, &pcm); + (char *)line6pcm->line6->properties->name, + 0, 1, 1, &pcm); if (err < 0) return err; @@ -156,13 +365,15 @@ static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm) 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_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); + snd_dma_continuous_data + (GFP_KERNEL), 64 * 1024, + 128 * 1024); return 0; } @@ -174,6 +385,31 @@ static int snd_line6_pcm_free(struct snd_device *device) } /* + Stop substream if still running. +*/ +static void pcm_disconnect_substream(struct snd_pcm_substream *substream) +{ + if (substream->runtime && snd_pcm_running(substream)) { + snd_pcm_stream_lock_irq(substream); + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + snd_pcm_stream_unlock_irq(substream); + } +} + +/* + Stop PCM stream. +*/ +void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) +{ + pcm_disconnect_substream(get_substream + (line6pcm, SNDRV_PCM_STREAM_CAPTURE)); + pcm_disconnect_substream(get_substream + (line6pcm, SNDRV_PCM_STREAM_PLAYBACK)); + line6_unlink_wait_clear_audio_out_urbs(line6pcm); + line6_unlink_wait_clear_audio_in_urbs(line6pcm); +} + +/* Create and register the PCM device and mixer entries. Create URBs for playback and capture. */ @@ -189,7 +425,7 @@ int line6_init_pcm(struct usb_line6 *line6, struct snd_line6_pcm *line6pcm; if (!(line6->properties->capabilities & LINE6_BIT_PCM)) - return 0; /* skip PCM initialization and report success */ + return 0; /* skip PCM initialization and report success */ /* initialize PCM subsystem based on product id: */ switch (line6->product) { @@ -199,54 +435,64 @@ int line6_init_pcm(struct usb_line6 *line6, case LINE6_DEVID_PODXT: case LINE6_DEVID_PODXTLIVE: case LINE6_DEVID_PODXTPRO: - ep_read = 0x82; + case LINE6_DEVID_PODHD300: + case LINE6_DEVID_PODHD400: + ep_read = 0x82; ep_write = 0x01; break; + case LINE6_DEVID_PODHD500: case LINE6_DEVID_PODX3: case LINE6_DEVID_PODX3LIVE: - ep_read = 0x86; + ep_read = 0x86; ep_write = 0x02; break; case LINE6_DEVID_POCKETPOD: - ep_read = 0x82; + ep_read = 0x82; ep_write = 0x02; break; case LINE6_DEVID_GUITARPORT: + case LINE6_DEVID_PODSTUDIO_GX: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: case LINE6_DEVID_TONEPORT_GX: - ep_read = 0x82; - ep_write = 0x01; - break; - case LINE6_DEVID_TONEPORT_UX1: - ep_read = 0x00; - ep_write = 0x00; + case LINE6_DEVID_TONEPORT_UX2: + ep_read = 0x82; + ep_write = 0x01; break; + /* this is for interface_number == 1: case LINE6_DEVID_TONEPORT_UX2: + case LINE6_DEVID_PODSTUDIO_UX2: ep_read = 0x87; ep_write = 0x00; - break; + break; */ default: MISSING_CASE; } - line6pcm = kzalloc(sizeof(struct snd_line6_pcm), GFP_KERNEL); + line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL); if (line6pcm == NULL) return -ENOMEM; - line6pcm->volume[0] = line6pcm->volume[1] = 128; + line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; + line6pcm->volume_monitor = 255; line6pcm->line6 = line6; line6pcm->ep_audio_read = ep_read; line6pcm->ep_audio_write = ep_write; - line6pcm->max_packet_size = usb_maxpacket(line6->usbdev, - usb_rcvintpipe(line6->usbdev, - ep_read), - 0); + + /* Read and write buffers are sized identically, so choose minimum */ + line6pcm->max_packet_size = min( + usb_maxpacket(line6->usbdev, + usb_rcvisocpipe(line6->usbdev, ep_read), 0), + usb_maxpacket(line6->usbdev, + usb_sndisocpipe(line6->usbdev, ep_write), 1)); + line6pcm->properties = properties; line6->line6pcm = line6pcm; @@ -255,8 +501,6 @@ int line6_init_pcm(struct usb_line6 *line6, if (err < 0) return err; - snd_card_set_dev(line6->card, line6->ifcdev); - err = snd_line6_new_pcm(line6pcm); if (err < 0) return err; @@ -265,19 +509,34 @@ int line6_init_pcm(struct usb_line6 *line6, spin_lock_init(&line6pcm->lock_audio_in); spin_lock_init(&line6pcm->lock_trigger); - err = create_audio_out_urbs(line6pcm); + err = line6_create_audio_out_urbs(line6pcm); if (err < 0) return err; - err = create_audio_in_urbs(line6pcm); + err = line6_create_audio_in_urbs(line6pcm); if (err < 0) return err; /* mixer: */ - err = snd_ctl_add(line6->card, snd_ctl_new1(&line6_control, line6pcm)); + err = + snd_ctl_add(line6->card, + snd_ctl_new1(&line6_control_playback, line6pcm)); + if (err < 0) + return err; + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + /* impulse response test: */ + err = device_create_file(line6->ifcdev, &dev_attr_impulse_volume); if (err < 0) return err; + err = device_create_file(line6->ifcdev, &dev_attr_impulse_period); + if (err < 0) + return err; + + line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; +#endif + return 0; } @@ -286,13 +545,29 @@ 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); + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + if ((line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) == 0) + line6_unlink_wait_clear_audio_out_urbs(line6pcm); + + break; + + case SNDRV_PCM_STREAM_CAPTURE: + if ((line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) == 0) + line6_unlink_wait_clear_audio_in_urbs(line6pcm); + + break; + + default: + MISSING_CASE; + } + + if (!test_and_set_bit(LINE6_INDEX_PREPARED, &line6pcm->flags)) { + line6pcm->count_out = 0; line6pcm->pos_out = 0; line6pcm->pos_out_done = 0; - - unlink_wait_clear_audio_in_urbs(line6pcm); line6pcm->bytes_out = 0; + line6pcm->count_in = 0; line6pcm->pos_in_done = 0; line6pcm->bytes_in = 0; } diff --git a/drivers/staging/line6/pcm.h b/drivers/staging/line6/pcm.h index 53db217cd42..6aa0d46a289 100644 --- a/drivers/staging/line6/pcm.h +++ b/drivers/staging/line6/pcm.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -16,38 +16,161 @@ #ifndef PCM_H #define PCM_H - #include <sound/pcm.h> #include "driver.h" #include "usbdefs.h" - /* number of URBs */ -#define LINE6_ISO_BUFFERS 8 +#define LINE6_ISO_BUFFERS 2 -/* number of USB frames per URB */ -#define LINE6_ISO_PACKETS 2 +/* + number of USB frames per URB + The Line6 Windows driver always transmits two frames per packet, but + the Linux driver performs significantly better (i.e., lower latency) + with only one frame per packet. +*/ +#define LINE6_ISO_PACKETS 1 /* in a "full speed" device (such as the PODxt Pro) this means 1ms */ #define LINE6_ISO_INTERVAL 1 -/* this should be queried dynamically from the USB interface! */ -#define LINE6_ISO_PACKET_SIZE_MAX 252 +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE +#define LINE6_IMPULSE_DEFAULT_PERIOD 100 +#endif +/* + Get substream from Line6 PCM data structure +*/ +#define get_substream(line6pcm, stream) \ + (line6pcm->pcm->streams[stream].substream) /* - Extract the messaging device from the substream instance + PCM mode bits. + + There are several features of the Line6 USB driver which require PCM + data to be exchanged with the device: + *) PCM playback and capture via ALSA + *) software monitoring (for devices without hardware monitoring) + *) optional impulse response measurement + However, from the device's point of view, there is just a single + capture and playback stream, which must be shared between these + subsystems. It is therefore necessary to maintain the state of the + subsystems with respect to PCM usage. We define several constants of + the form LINE6_BIT_PCM_<subsystem>_<direction>_<resource> with the + following meanings: + *) <subsystem> is one of + -) ALSA: PCM playback and capture via ALSA + -) MONITOR: software monitoring + -) IMPULSE: optional impulse response measurement + *) <direction> is one of + -) PLAYBACK: audio output (from host to device) + -) CAPTURE: audio input (from device to host) + *) <resource> is one of + -) BUFFER: buffer required by PCM data stream + -) STREAM: actual PCM data stream + + The subsystems call line6_pcm_acquire() to acquire the (shared) + resources needed for a particular operation (e.g., allocate the buffer + for ALSA playback or start the capture stream for software monitoring). + When a resource is no longer needed, it is released by calling + line6_pcm_release(). Buffer allocation and stream startup are handled + separately to allow the ALSA kernel driver to perform them at + appropriate places (since the callback which starts a PCM stream is not + allowed to sleep). */ -#define s2m(s) (((struct snd_line6_pcm *) \ - snd_pcm_substream_chip(s))->line6->ifcdev) +enum { + /* individual bit indices: */ + LINE6_INDEX_PCM_ALSA_PLAYBACK_BUFFER, + LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, + LINE6_INDEX_PCM_ALSA_CAPTURE_BUFFER, + LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, + LINE6_INDEX_PCM_MONITOR_PLAYBACK_BUFFER, + LINE6_INDEX_PCM_MONITOR_PLAYBACK_STREAM, + LINE6_INDEX_PCM_MONITOR_CAPTURE_BUFFER, + LINE6_INDEX_PCM_MONITOR_CAPTURE_STREAM, +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_INDEX_PCM_IMPULSE_PLAYBACK_BUFFER, + LINE6_INDEX_PCM_IMPULSE_PLAYBACK_STREAM, + LINE6_INDEX_PCM_IMPULSE_CAPTURE_BUFFER, + LINE6_INDEX_PCM_IMPULSE_CAPTURE_STREAM, +#endif + LINE6_INDEX_PAUSE_PLAYBACK, + LINE6_INDEX_PREPARED, + + /* individual bit masks: */ + LINE6_BIT(PCM_ALSA_PLAYBACK_BUFFER), + LINE6_BIT(PCM_ALSA_PLAYBACK_STREAM), + LINE6_BIT(PCM_ALSA_CAPTURE_BUFFER), + LINE6_BIT(PCM_ALSA_CAPTURE_STREAM), + LINE6_BIT(PCM_MONITOR_PLAYBACK_BUFFER), + LINE6_BIT(PCM_MONITOR_PLAYBACK_STREAM), + LINE6_BIT(PCM_MONITOR_CAPTURE_BUFFER), + LINE6_BIT(PCM_MONITOR_CAPTURE_STREAM), +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BIT(PCM_IMPULSE_PLAYBACK_BUFFER), + LINE6_BIT(PCM_IMPULSE_PLAYBACK_STREAM), + LINE6_BIT(PCM_IMPULSE_CAPTURE_BUFFER), + LINE6_BIT(PCM_IMPULSE_CAPTURE_STREAM), +#endif + LINE6_BIT(PAUSE_PLAYBACK), + LINE6_BIT(PREPARED), + + /* combined bit masks (by operation): */ + LINE6_BITS_PCM_ALSA_BUFFER = + LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | + LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER, + + LINE6_BITS_PCM_ALSA_STREAM = + LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | + LINE6_BIT_PCM_ALSA_CAPTURE_STREAM, + + LINE6_BITS_PCM_MONITOR = + LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER | + LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM | + LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER | + LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BITS_PCM_IMPULSE = + LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | + LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | + LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | + LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM, +#endif + /* combined bit masks (by direction): */ + LINE6_BITS_PLAYBACK_BUFFER = +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | +#endif + LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | + LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER , -enum { - BIT_RUNNING_PLAYBACK, - BIT_RUNNING_CAPTURE, - BIT_PAUSE_PLAYBACK, - BIT_PREPARED + LINE6_BITS_PLAYBACK_STREAM = +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | +#endif + LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | + LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM , + + LINE6_BITS_CAPTURE_BUFFER = +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | +#endif + LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER | + LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER , + + LINE6_BITS_CAPTURE_STREAM = +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM | +#endif + LINE6_BIT_PCM_ALSA_CAPTURE_STREAM | + LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, + + LINE6_BITS_STREAM = + LINE6_BITS_PLAYBACK_STREAM | + LINE6_BITS_CAPTURE_STREAM }; struct line6_pcm_properties { @@ -83,9 +206,11 @@ struct snd_line6_pcm { struct urb *urb_audio_in[LINE6_ISO_BUFFERS]; /** - Temporary buffer to hold data when playback buffer wraps. + Temporary buffer for playback. + Since the packet size is not known in advance, this buffer is + large enough to store maximum size packets. */ - unsigned char *wrap_out; + unsigned char *buffer_out; /** Temporary buffer for capture. @@ -95,6 +220,16 @@ struct snd_line6_pcm { unsigned char *buffer_in; /** + Previously captured frame (for software monitoring). + */ + unsigned char *prev_fbuf; + + /** + Size of previously captured frame (for software monitoring). + */ + int prev_fsize; + + /** Free frame position in the playback buffer. */ snd_pcm_uframes_t pos_out; @@ -204,19 +339,44 @@ struct snd_line6_pcm { /** PCM playback volume (left and right). */ - int volume[2]; + int volume_playback[2]; + + /** + PCM monitor volume. + */ + int volume_monitor; + +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + /** + Volume of impulse response test signal (if zero, test is disabled). + */ + int impulse_volume; /** - Several status bits (see BIT_*). + Period of impulse response test signal. + */ + int impulse_period; + + /** + Counter for impulse response test signal. + */ + int impulse_count; +#endif + + /** + Several status bits (see LINE6_BIT_*). */ unsigned long flags; -}; + int last_frame_in, last_frame_out; +}; extern int line6_init_pcm(struct usb_line6 *line6, struct line6_pcm_properties *properties); extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd); extern int snd_line6_prepare(struct snd_pcm_substream *substream); - +extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm); +extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels); +extern int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels); #endif diff --git a/drivers/staging/line6/playback.c b/drivers/staging/line6/playback.c index acb06126a6a..2ca8900e68c 100644 --- a/drivers/staging/line6/playback.c +++ b/drivers/staging/line6/playback.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,18 +9,18 @@ * */ -#include "driver.h" - +#include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include "audio.h" +#include "capture.h" +#include "driver.h" #include "pcm.h" #include "pod.h" #include "playback.h" - /* Software stereo volume control. */ @@ -30,10 +30,11 @@ static void change_volume(struct urb *urb_out, int volume[], int chn = 0; if (volume[0] == 256 && volume[1] == 256) - return; /* maximum volume - no change */ + 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); @@ -43,11 +44,13 @@ static void change_volume(struct urb *urb_out, int volume[], } } 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; + val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16); val = (val * volume[chn & 1]) >> 8; p[0] = val; @@ -58,27 +61,103 @@ static void change_volume(struct urb *urb_out, int volume[], } } +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + +/* + Create signal for impulse response test. +*/ +static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm, + struct urb *urb_out, int bytes_per_frame) +{ + int frames = urb_out->transfer_buffer_length / bytes_per_frame; + + if (bytes_per_frame == 4) { + int i; + short *pi = (short *)line6pcm->prev_fbuf; + short *po = (short *)urb_out->transfer_buffer; + + for (i = 0; i < frames; ++i) { + po[0] = pi[0]; + po[1] = 0; + pi += 2; + po += 2; + } + } else if (bytes_per_frame == 6) { + int i, j; + unsigned char *pi = line6pcm->prev_fbuf; + unsigned char *po = urb_out->transfer_buffer; + + for (i = 0; i < frames; ++i) { + for (j = 0; j < bytes_per_frame / 2; ++j) + po[j] = pi[j]; + + for (; j < bytes_per_frame; ++j) + po[j] = 0; + + pi += bytes_per_frame; + po += bytes_per_frame; + } + } + if (--line6pcm->impulse_count <= 0) { + ((unsigned char *)(urb_out->transfer_buffer))[bytes_per_frame - + 1] = + line6pcm->impulse_volume; + line6pcm->impulse_count = line6pcm->impulse_period; + } +} + +#endif + +/* + Add signal to buffer for software monitoring. +*/ +static void add_monitor_signal(struct urb *urb_out, unsigned char *signal, + int volume, int bytes_per_frame) +{ + if (volume == 0) + return; /* zero volume - no change */ + + if (bytes_per_frame == 4) { + short *pi, *po, *buf_end; + + pi = (short *)signal; + po = (short *)urb_out->transfer_buffer; + buf_end = po + urb_out->transfer_buffer_length / sizeof(*po); + + for (; po < buf_end; ++pi, ++po) + *po += (*pi * volume) >> 8; + } + + /* + We don't need to handle devices with 6 bytes per frame here + since they all support hardware monitoring. + */ +} + /* Find a free URB, prepare audio data, and submit URB. */ -static int submit_audio_out_urb(struct snd_pcm_substream *substream) +static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) { int index; unsigned long flags; int i, urb_size, urb_frames; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + int ret; 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; + 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 urb *urb_out; spin_lock_irqsave(&line6pcm->lock_audio_out, flags); - index = find_first_zero_bit(&line6pcm->active_urb_out, LINE6_ISO_BUFFERS); + 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"); + dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); return -EINVAL; } @@ -87,61 +166,119 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) for (i = 0; i < LINE6_ISO_PACKETS; ++i) { /* compute frame size for given sampling rate */ - int n, fs; - 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; + int fsize = 0; + struct usb_iso_packet_descriptor *fout = + &urb_out->iso_frame_desc[i]; + + if (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) + fsize = line6pcm->prev_fsize; + + if (fsize == 0) { + int n; + + line6pcm->count_out += frame_increment; + n = line6pcm->count_out / frame_factor; + line6pcm->count_out -= n * frame_factor; + fsize = n * bytes_per_frame; + } + fout->offset = urb_size; - fout->length = fs; - urb_size += fs; + fout->length = fsize; + urb_size += fsize; + } + + if (urb_size == 0) { + /* can't determine URB size */ + spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); + dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); + return -EINVAL; } urb_frames = urb_size / bytes_per_frame; + urb_out->transfer_buffer = + line6pcm->buffer_out + + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; + urb_out->transfer_buffer_length = urb_size; + urb_out->context = line6pcm; + + if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags) && + !test_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags)) { + struct snd_pcm_runtime *runtime = + get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime; - if (test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) { - urb_out->transfer_buffer = line6pcm->wrap_out; - memset(line6pcm->wrap_out, 0, urb_size); - } else { if (line6pcm->pos_out + urb_frames > runtime->buffer_size) { /* - The transferred area goes over buffer boundary, - copy the data to the temp buffer. - */ + 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); + memcpy(urb_out->transfer_buffer, + runtime->dma_area + + line6pcm->pos_out * bytes_per_frame, + len * bytes_per_frame); + memcpy(urb_out->transfer_buffer + + 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 */ + dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", + len); } else { - /* set the buffer pointer */ - urb_out->transfer_buffer = runtime->dma_area + line6pcm->pos_out * bytes_per_frame; + memcpy(urb_out->transfer_buffer, + runtime->dma_area + + line6pcm->pos_out * bytes_per_frame, + urb_out->transfer_buffer_length); } - } - - 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); + line6pcm->pos_out += urb_frames; + if (line6pcm->pos_out >= runtime->buffer_size) + line6pcm->pos_out -= runtime->buffer_size; + } else { + memset(urb_out->transfer_buffer, 0, + urb_out->transfer_buffer_length); } + + change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame); + + if (line6pcm->prev_fbuf != NULL) { +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + if (line6pcm->flags & LINE6_BITS_PCM_IMPULSE) { + create_impulse_test_signal(line6pcm, urb_out, + bytes_per_frame); + if (line6pcm->flags & + LINE6_BIT_PCM_ALSA_CAPTURE_STREAM) { + line6_capture_copy(line6pcm, + urb_out->transfer_buffer, + urb_out-> + transfer_buffer_length); + line6_capture_check_period(line6pcm, + urb_out->transfer_buffer_length); + } + } else { +#endif + if (! + (line6pcm->line6-> + properties->capabilities & LINE6_BIT_HWMON) + && (line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) + && (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM)) + add_monitor_signal(urb_out, line6pcm->prev_fbuf, + line6pcm->volume_monitor, + bytes_per_frame); +#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE + } #endif + } + + ret = usb_submit_urb(urb_out, GFP_ATOMIC); - if (usb_submit_urb(urb_out, GFP_ATOMIC) == 0) + if (ret == 0) set_bit(index, &line6pcm->active_urb_out); else - dev_err(s2m(substream), "URB out #%d submission failed\n", index); + dev_err(line6pcm->line6->ifcdev, + "URB out #%d submission failed (%d)\n", index, ret); spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); return 0; @@ -150,12 +287,12 @@ static int submit_audio_out_urb(struct snd_pcm_substream *substream) /* Submit all currently available playback URBs. */ -static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream) +int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm) { int ret, i; for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - ret = submit_audio_out_urb(substream); + ret = submit_audio_out_urb(line6pcm); if (ret < 0) return ret; } @@ -166,7 +303,7 @@ static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream) /* Unlink all currently active playback URBs. */ -static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) +void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) { unsigned int i; @@ -174,6 +311,7 @@ static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) 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]; + usb_unlink_urb(u); } } @@ -181,9 +319,10 @@ static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) } /* - Wait until unlinking of all currently active playback URBs has been finished. + Wait until unlinking of all currently active playback URBs has been + finished. */ -static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) +void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) { int timeout = HZ; unsigned int i; @@ -202,18 +341,21 @@ static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) } while (--timeout > 0); if (alive) snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); - - line6pcm->active_urb_out = 0; - line6pcm->unlink_urb_out = 0; } /* Unlink all currently active playback URBs, and wait for finishing. */ -void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) +void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) +{ + line6_unlink_audio_out_urbs(line6pcm); + line6_wait_clear_audio_out_urbs(line6pcm); +} + +void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm) { - unlink_audio_out_urbs(line6pcm); - wait_clear_audio_out_urbs(line6pcm); + kfree(line6pcm->buffer_out); + line6pcm->buffer_out = NULL; } /* @@ -223,10 +365,15 @@ static void audio_out_callback(struct urb *urb) { int i, index, length = 0, shutdown = 0; unsigned long flags; + struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; + struct snd_pcm_substream *substream = + get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK); - 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; +#if USE_CLEAR_BUFFER_WORKAROUND + memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); +#endif + + line6pcm->last_frame_out = urb->start_frame; /* find index of URB */ for (index = LINE6_ISO_BUFFERS; index--;) @@ -234,36 +381,46 @@ static void audio_out_callback(struct urb *urb) break; if (index < 0) - return; /* URB has been unlinked asynchronously */ + 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; + if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags)) { + struct snd_pcm_runtime *runtime = substream->runtime; + + line6pcm->pos_out_done += + length / line6pcm->properties->bytes_per_frame; + + if (line6pcm->pos_out_done >= runtime->buffer_size) + line6pcm->pos_out_done -= runtime->buffer_size; + } clear_bit(index, &line6pcm->active_urb_out); for (i = LINE6_ISO_PACKETS; i--;) - if (urb->iso_frame_desc[i].status == -ESHUTDOWN) { + if (urb->iso_frame_desc[i].status == -EXDEV) { shutdown = 1; break; } - if (test_bit(index, &line6pcm->unlink_urb_out)) + if (test_and_clear_bit(index, &line6pcm->unlink_urb_out)) shutdown = 1; spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); if (!shutdown) { - submit_audio_out_urb(substream); - - if ((line6pcm->bytes_out += length) >= line6pcm->period_out) { - line6pcm->bytes_out -= line6pcm->period_out; - snd_pcm_period_elapsed(substream); + submit_audio_out_urb(line6pcm); + + if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, + &line6pcm->flags)) { + line6pcm->bytes_out += length; + if (line6pcm->bytes_out >= line6pcm->period_out) { + line6pcm->bytes_out %= line6pcm->period_out; + snd_pcm_period_elapsed(substream); + } } } } @@ -276,7 +433,8 @@ static int snd_line6_playback_open(struct snd_pcm_substream *substream) struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - (&line6pcm->properties->snd_line6_rates)); + (&line6pcm-> + properties->snd_line6_rates)); if (err < 0) return err; @@ -291,7 +449,8 @@ static int snd_line6_playback_close(struct snd_pcm_substream *substream) } /* hw_params playback callback */ -static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) +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); @@ -308,19 +467,19 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, str } /* -- [FD] end */ - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); + ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); + if (ret < 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; + ret = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (ret < 0) { + line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); + return ret; } + line6pcm->period_out = params_period_bytes(hw_params); return 0; } @@ -328,46 +487,47 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, str static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - unlink_wait_clear_audio_out_urbs(line6pcm); - - kfree(line6pcm->wrap_out); - line6pcm->wrap_out = NULL; + line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); return snd_pcm_lib_free_pages(substream); } /* trigger playback callback */ -int snd_line6_playback_trigger(struct snd_pcm_substream *substream, int cmd) +int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd) { - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); int err; - line6pcm->count_out = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - if (!test_and_set_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) { - err = submit_audio_out_all_urbs(substream); +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_RESUME: +#endif + err = line6_pcm_acquire(line6pcm, + LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); - if (err < 0) { - clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags); - return err; - } - } + if (err < 0) + return err; break; case SNDRV_PCM_TRIGGER_STOP: - if (test_and_clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) - unlink_audio_out_urbs(line6pcm); +#ifdef CONFIG_PM + case SNDRV_PCM_TRIGGER_SUSPEND: +#endif + err = line6_pcm_release(line6pcm, + LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); + + if (err < 0) + return err; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - set_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags); + set_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - clear_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags); + clear_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); break; default: @@ -382,22 +542,23 @@ 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, + .open = snd_line6_playback_open, + .close = snd_line6_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_line6_playback_hw_params, + .hw_free = snd_line6_playback_hw_free, + .prepare = snd_line6_prepare, + .trigger = snd_line6_trigger, + .pointer = snd_line6_playback_pointer, }; -int create_audio_out_urbs(struct snd_line6_pcm *line6pcm) +int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) { int i; @@ -406,7 +567,8 @@ int create_audio_out_urbs(struct snd_line6_pcm *line6pcm) struct urb *urb; /* URB for audio out: */ - urb = line6pcm->urb_audio_out[i] = usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); + 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"); @@ -414,7 +576,10 @@ int create_audio_out_urbs(struct snd_line6_pcm *line6pcm) } urb->dev = line6pcm->line6->usbdev; - urb->pipe = usb_sndisocpipe(line6pcm->line6->usbdev, line6pcm->ep_audio_write & USB_ENDPOINT_NUMBER_MASK); + 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; diff --git a/drivers/staging/line6/playback.h b/drivers/staging/line6/playback.h index db1e48b3596..743bd6f74c5 100644 --- a/drivers/staging/line6/playback.h +++ b/drivers/staging/line6/playback.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -12,19 +12,30 @@ #ifndef PLAYBACK_H #define PLAYBACK_H +#include <sound/pcm.h> #include "driver.h" -#include <sound/pcm.h> - +/* + * When the TonePort is used with jack in full duplex mode and the outputs are + * not connected, the software monitor produces an ugly noise since everything + * written to the output buffer (i.e., the input signal) will be repeated in + * the next period (sounds like a delay effect). As a workaround, the output + * buffer is cleared after the data have been read, but there must be a better + * solution. Until one is found, this workaround can be used to fix the + * problem. + */ +#define USE_CLEAR_BUFFER_WORKAROUND 1 extern struct snd_pcm_ops snd_line6_playback_ops; - -extern int create_audio_out_urbs(struct snd_line6_pcm *line6pcm); -extern int snd_line6_playback_trigger(struct snd_pcm_substream *substream, - int cmd); -extern void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm); - +extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm); +extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm); +extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm + *line6pcm); +extern void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm); +extern int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd); #endif diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c index 4c5b9d58400..44f4b2f9857 100644 --- a/drivers/staging/line6/pod.c +++ b/drivers/staging/line6/pod.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,21 +9,22 @@ * */ -#include "driver.h" +#include <linux/slab.h> +#include <linux/wait.h> +#include <sound/control.h> #include "audio.h" #include "capture.h" -#include "control.h" +#include "driver.h" #include "playback.h" #include "pod.h" - #define POD_SYSEX_CODE 3 -#define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ +#define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ +/* *INDENT-OFF* */ enum { - POD_SYSEX_CLIP = 0x0f, POD_SYSEX_SAVE = 0x24, POD_SYSEX_SYSTEM = 0x56, POD_SYSEX_SYSTEMREQ = 0x57, @@ -33,19 +34,18 @@ enum { POD_SYSEX_DUMPMEM = 0x73, POD_SYSEX_DUMP = 0x74, POD_SYSEX_DUMPREQ = 0x75 - /* POD_SYSEX_DUMPMEM2 = 0x76 */ /* dumps entire internal memory of PODxt Pro */ + + /* dumps entire internal memory of PODxt Pro */ + /* POD_SYSEX_DUMPMEM2 = 0x76 */ }; enum { - POD_monitor_level = 0x04, - POD_routing = 0x05, - POD_tuner_mute = 0x13, - POD_tuner_freq = 0x15, - POD_tuner_note = 0x16, - POD_tuner_pitch = 0x17, - POD_system_invalid = 0x7fff + POD_MONITOR_LEVEL = 0x04, + POD_SYSTEM_INVALID = 0x10000 }; +/* *INDENT-ON* */ + enum { POD_DUMP_MEMORY = 2 }; @@ -58,7 +58,6 @@ enum { POD_BUSY_MIDISEND }; - static struct snd_ratden pod_ratden = { .num_min = 78125, .num_max = 78125, @@ -67,879 +66,269 @@ static struct snd_ratden pod_ratden = { }; static struct line6_pcm_properties pod_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_S24_3LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 39062, - .rate_max = 39063, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME, /* at least one URB must fit into one period */ - .period_bytes_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_S24_3LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 39062, - .rate_max = 39063, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = LINE6_ISO_PACKET_SIZE_MAX * POD_BYTES_PER_FRAME, /* at least one URB must fit into one period */ - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024 - }, + .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 | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_KNOT, + .rate_min = 39062, + .rate_max = 39063, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .snd_line6_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_KNOT, + .rate_min = 39062, + .rate_max = 39063, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, .snd_line6_rates = { - .nrats = 1, - .rats = &pod_ratden - }, + .nrats = 1, + .rats = &pod_ratden}, .bytes_per_frame = POD_BYTES_PER_FRAME }; -static const char pod_request_version[] = { 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 }; -static const char pod_request_channel[] = { 0xf0, 0x00, 0x01, 0x0c, 0x03, 0x75, 0xf7 }; -static const char pod_version_header[] = { 0xf2, 0x7e, 0x7f, 0x06, 0x02 }; - - -/* - Mark all parameters as dirty and notify waiting processes. -*/ -static void pod_mark_batch_all_dirty(struct usb_line6_pod *pod) -{ - int i; - - for (i = 0; i < POD_CONTROL_SIZE; i++) - set_bit(i, pod->param_dirty); -} - -/* - Send an asynchronous request for the POD firmware version and device ID. -*/ -static int pod_version_request_async(struct usb_line6_pod *pod) -{ - return line6_send_raw_message_async(&pod->line6, pod->buffer_versionreq, sizeof(pod_request_version)); -} - -static void pod_create_files_work(struct work_struct *work) -{ - struct usb_line6_pod *pod = container_of(work, struct usb_line6_pod, create_files_work); - - pod_create_files(pod->firmware_version, pod->line6.properties->device_bit, pod->line6.ifcdev); -} - -static void pod_startup_timeout(unsigned long arg) -{ - enum { - REQUEST_NONE, - REQUEST_DUMP, - REQUEST_VERSION - }; - - int request = REQUEST_NONE; - struct usb_line6_pod *pod = (struct usb_line6_pod *)arg; - - if (pod->dumpreq.ok) { - if (!pod->versionreq_ok) - request = REQUEST_VERSION; - } else { - if (pod->versionreq_ok) - request = REQUEST_DUMP; - else if (pod->startup_count++ & 1) - request = REQUEST_DUMP; - else - request = REQUEST_VERSION; - } - - switch (request) { - case REQUEST_DUMP: - line6_dump_request_async(&pod->dumpreq, &pod->line6, 0); - break; - - case REQUEST_VERSION: - pod_version_request_async(pod); - break; - - default: - return; - } - - line6_startup_delayed(&pod->dumpreq, 1, pod_startup_timeout, pod); -} - -static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, int size) -{ - return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, size); -} - -/* - Send channel dump data to the PODxt Pro. -*/ -static void pod_dump(struct usb_line6_pod *pod, const unsigned char *data) -{ - int size = 1 + sizeof(pod->prog_data); - char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_DUMP, size); - if (!sysex) - return; - /* Don't know what this is good for, but PODxt Pro transmits it, so we - * also do... */ - sysex[SYSEX_DATA_OFS] = 5; - memcpy(sysex + SYSEX_DATA_OFS + 1, data, sizeof(pod->prog_data)); - line6_send_sysex_message(&pod->line6, sysex, size); - memcpy(&pod->prog_data, data, sizeof(pod->prog_data)); - pod_mark_batch_all_dirty(pod); - kfree(sysex); -} +static const char pod_version_header[] = { + 0xf2, 0x7e, 0x7f, 0x06, 0x02 +}; -/* - Store parameter value in driver memory and mark it as dirty. -*/ -static void pod_store_parameter(struct usb_line6_pod *pod, int param, int value) -{ - pod->prog_data.control[param] = value; - set_bit(param, pod->param_dirty); - pod->dirty = 1; -} +/* forward declarations: */ +static void pod_startup2(unsigned long data); +static void pod_startup3(struct usb_line6_pod *pod); -/* - Handle SAVE button -*/ -static void pod_save_button_pressed(struct usb_line6_pod *pod, int type, int index) +static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, + int size) { - pod->dirty = 0; - set_bit(POD_SAVE_PRESSED, &pod->atomic_flags); + return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, + size); } /* Process a completely received message. */ -void pod_process_message(struct usb_line6_pod *pod) +void line6_pod_process_message(struct usb_line6_pod *pod) { const unsigned char *buf = pod->line6.buffer_message; - /* filter messages by type */ - switch (buf[0] & 0xf0) { - case LINE6_PARAM_CHANGE: - case LINE6_PROGRAM_CHANGE: - case LINE6_SYSEX_BEGIN: - break; /* handle these further down */ - - default: - return; /* ignore all others */ + if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { + pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; + pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | + (int) buf[10]; + pod_startup3(pod); + return; } - /* process all remaining messages */ - switch (buf[0]) { - case LINE6_PARAM_CHANGE | LINE6_CHANNEL_DEVICE: - pod_store_parameter(pod, buf[1], buf[2]); - /* intentionally no break here! */ - - case LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST: - if ((buf[1] == POD_amp_model_setup) || - (buf[1] == POD_effect_setup)) - /* these also affect other settings */ - line6_dump_request_async(&pod->dumpreq, &pod->line6, 0); - - break; - - case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE: - case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST: - pod->channel_num = buf[1]; - pod->dirty = 0; - set_bit(POD_CHANNEL_DIRTY, &pod->atomic_flags); - line6_dump_request_async(&pod->dumpreq, &pod->line6, 0); - break; - - case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE: - case LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN: - if (memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) == 0) { - switch (buf[5]) { - case POD_SYSEX_DUMP: - if (pod->line6.message_length == sizeof(pod->prog_data) + 7) { - switch (pod->dumpreq.in_progress) { - case LINE6_DUMP_CURRENT: - memcpy(&pod->prog_data, buf + 7, sizeof(pod->prog_data)); - pod_mark_batch_all_dirty(pod); - pod->dumpreq.ok = 1; - break; - - case POD_DUMP_MEMORY: - memcpy(&pod->prog_data_buf, buf + 7, sizeof(pod->prog_data_buf)); - break; - - default: - DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown dump code %02X\n", pod->dumpreq.in_progress)); - } - - line6_dump_finished(&pod->dumpreq); - } else - DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "wrong size of channel dump message (%d instead of %d)\n", - pod->line6.message_length, (int)sizeof(pod->prog_data) + 7)); - - break; - - case POD_SYSEX_SYSTEM: { - short value = ((int)buf[7] << 12) | ((int)buf[8] << 8) | ((int)buf[9] << 4) | (int)buf[10]; - -#define PROCESS_SYSTEM_PARAM(x) \ - case POD_ ## x: \ - pod->x.value = value; \ - wake_up_interruptible(&pod->x.wait); \ - break; - - switch (buf[6]) { - PROCESS_SYSTEM_PARAM(monitor_level); - PROCESS_SYSTEM_PARAM(routing); - PROCESS_SYSTEM_PARAM(tuner_mute); - PROCESS_SYSTEM_PARAM(tuner_freq); - PROCESS_SYSTEM_PARAM(tuner_note); - PROCESS_SYSTEM_PARAM(tuner_pitch); - -#undef PROCESS_SYSTEM_PARAM - - default: - DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown tuner/system response %02X\n", buf[6])); - } - - break; - } - - case POD_SYSEX_FINISH: - /* do we need to respond to this? */ - break; - - case POD_SYSEX_SAVE: - pod_save_button_pressed(pod, buf[6], buf[7]); - break; - - case POD_SYSEX_CLIP: - DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "audio clipped\n")); - pod->clipping.value = 1; - wake_up_interruptible(&pod->clipping.wait); - break; - - case POD_SYSEX_STORE: - DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "message %02X not yet implemented\n", buf[5])); - break; - - default: - DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex message %02X\n", buf[5])); - } - } else if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { - if (pod->versionreq_ok == 0) { - pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; - pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int)buf[10]; - pod->versionreq_ok = 1; - - /* Now we know the firmware version, so we schedule a bottom half - handler to create the special files: */ - INIT_WORK(&pod->create_files_work, pod_create_files_work); - queue_work(line6_workqueue, &pod->create_files_work); - } else - DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "multiple firmware version message\n")); - } else - DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "unknown sysex header\n")); - - break; - - case LINE6_SYSEX_END: - break; - - default: - DEBUG_MESSAGES(dev_err(pod->line6.ifcdev, "POD: unknown message %02X\n", buf[0])); + /* Only look for sysex messages from this device */ + if (buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE) && + buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN)) { + return; } -} - -/* - Detect some cases that require a channel dump after sending a command to the - device. Important notes: - *) The actual dump request can not be sent here since we are not allowed to - wait for the completion of the first message in this context, and sending - the dump request before completion of the previous message leaves the POD - in an undefined state. The dump request will be sent when the echoed - commands are received. - *) This method fails if a param change message is "chopped" after the first - byte. -*/ -void pod_midi_postprocess(struct usb_line6_pod *pod, unsigned char *data, int length) -{ - int i; - - if (!pod->midi_postprocess) + if (memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) != 0) return; - for (i = 0; i < length; ++i) { - if (data[i] == (LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST)) { - line6_invalidate_current(&pod->dumpreq); - break; - } else if ((data[i] == (LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST)) && (i < length - 1)) - if ((data[i + 1] == POD_amp_model_setup) || (data[i + 1] == POD_effect_setup)) { - line6_invalidate_current(&pod->dumpreq); - break; - } + if (buf[5] == POD_SYSEX_SYSTEM && buf[6] == POD_MONITOR_LEVEL) { + short value = ((int)buf[7] << 12) | ((int)buf[8] << 8) | + ((int)buf[9] << 4) | (int)buf[10]; + pod->monitor_level = value; } } /* - Send channel number (i.e., switch to a different sound). -*/ -static void pod_send_channel(struct usb_line6_pod *pod, int value) -{ - line6_invalidate_current(&pod->dumpreq); - - if (line6_send_program(&pod->line6, value) == 0) - pod->channel_num = value; - else - line6_dump_finished(&pod->dumpreq); -} - -/* Transmit PODxt Pro control parameter. */ -void pod_transmit_parameter(struct usb_line6_pod *pod, int param, int value) -{ - if (line6_transmit_parameter(&pod->line6, param, value) == 0) - pod_store_parameter(pod, param, value); - - if ((param == POD_amp_model_setup) || (param == POD_effect_setup)) /* these also affect other settings */ - line6_invalidate_current(&pod->dumpreq); -} - -/* - Resolve value to memory location. -*/ -static void pod_resolve(const char *buf, short block0, short block1, unsigned char *location) -{ - int value = simple_strtoul(buf, NULL, 10); - short block = (value < 0x40) ? block0 : block1; - value &= 0x3f; - location[0] = block >> 7; - location[1] = value | (block & 0x7f); -} - -/* - Send command to store channel/effects setup/amp setup to PODxt Pro. -*/ -static ssize_t pod_send_store_command(struct device *dev, const char *buf, size_t count, short block0, short block1) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - - int size = 3 + sizeof(pod->prog_data_buf); - char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_STORE, size); - if (!sysex) - return 0; - - sysex[SYSEX_DATA_OFS] = 5; /* see pod_dump() */ - pod_resolve(buf, block0, block1, sysex + SYSEX_DATA_OFS + 1); - memcpy(sysex + SYSEX_DATA_OFS + 3, &pod->prog_data_buf, sizeof(pod->prog_data_buf)); - - line6_send_sysex_message(&pod->line6, sysex, size); - kfree(sysex); - /* needs some delay here on AMD64 platform */ - return count; -} - -/* - Send command to retrieve channel/effects setup/amp setup to PODxt Pro. -*/ -static ssize_t pod_send_retrieve_command(struct device *dev, const char *buf, size_t count, short block0, short block1) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - int size = 4; - char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_DUMPMEM, size); - - if (!sysex) - return 0; - - pod_resolve(buf, block0, block1, sysex + SYSEX_DATA_OFS); - sysex[SYSEX_DATA_OFS + 2] = 0; - sysex[SYSEX_DATA_OFS + 3] = 0; - line6_dump_started(&pod->dumpreq, POD_DUMP_MEMORY); - - if (line6_send_sysex_message(&pod->line6, sysex, size) < size) - line6_dump_finished(&pod->dumpreq); - - kfree(sysex); - /* needs some delay here on AMD64 platform */ - return count; -} - -/* - Generic get name function. -*/ -static ssize_t get_name_generic(struct usb_line6_pod *pod, const char *str, char *buf) -{ - int length = 0; - const char *p1; - char *p2; - char *last_non_space = buf; - - int retval = line6_wait_dump(&pod->dumpreq, 0); - if (retval < 0) - return retval; - - for (p1 = str, p2 = buf; *p1; ++p1, ++p2) { - *p2 = *p1; - if (*p2 != ' ') - last_non_space = p2; - if (++length == POD_NAME_LENGTH) - break; - } - - *(last_non_space + 1) = '\n'; - return last_non_space - buf + 2; -} - -/* - "read" request on "channel" special file. -*/ -static ssize_t pod_get_channel(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - return sprintf(buf, "%d\n", pod->channel_num); -} - -/* - "write" request on "channel" special file. -*/ -static ssize_t pod_set_channel(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - int value = simple_strtoul(buf, NULL, 10); - pod_send_channel(pod, value); - return count; -} - -/* - "read" request on "name" special file. -*/ -static ssize_t pod_get_name(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - return get_name_generic(pod, pod->prog_data.header + POD_NAME_OFFSET, buf); -} - -/* - "read" request on "name" special file. -*/ -static ssize_t pod_get_name_buf(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - return get_name_generic(pod, pod->prog_data_buf.header + POD_NAME_OFFSET, buf); -} - -/* - "read" request on "dump" special file. -*/ -static ssize_t pod_get_dump(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - int retval = line6_wait_dump(&pod->dumpreq, 0); - if (retval < 0) - return retval; - memcpy(buf, &pod->prog_data, sizeof(pod->prog_data)); - return sizeof(pod->prog_data); -} - -/* - "write" request on "dump" special file. -*/ -static ssize_t pod_set_dump(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - - if (count != sizeof(pod->prog_data)) { - dev_err(pod->line6.ifcdev, - "data block must be exactly %d bytes\n", - sizeof(pod->prog_data)); - return -EINVAL; - } - - pod_dump(pod, buf); - return sizeof(pod->prog_data); -} - -/* - Request system parameter. - @param tuner non-zero, if code refers to a tuner parameter -*/ -static ssize_t pod_get_system_param(struct usb_line6_pod *pod, char *buf, int code, struct ValueWait *param, int tuner, int sign) +void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, + u8 value) { - char *sysex; - int value; - static const int size = 1; - int retval = 0; - DECLARE_WAITQUEUE(wait, current); - - if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner) - return -ENODEV; - - /* send value request to tuner: */ - param->value = POD_system_invalid; - sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEMREQ, size); - if (!sysex) - return 0; - sysex[SYSEX_DATA_OFS] = code; - line6_send_sysex_message(&pod->line6, sysex, size); - kfree(sysex); - - /* wait for tuner to respond: */ - add_wait_queue(¶m->wait, &wait); - current->state = TASK_INTERRUPTIBLE; - - while (param->value == POD_system_invalid) { - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } else - schedule(); - } - - current->state = TASK_RUNNING; - remove_wait_queue(¶m->wait, &wait); - - if (retval < 0) - return retval; - - value = sign ? (int)(signed short)param->value : (int)(unsigned short)param->value; - return sprintf(buf, "%d\n", value); + line6_transmit_parameter(&pod->line6, param, value); } /* - Send system parameter. - @param tuner non-zero, if code refers to a tuner parameter + Send system parameter (from integer). */ -static ssize_t pod_set_system_param(struct usb_line6_pod *pod, const char *buf, - int count, int code, unsigned short mask, - int tuner) +static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, + int code) { char *sysex; static const int size = 5; - unsigned short value; - - if (((pod->prog_data.control[POD_tuner] & 0x40) == 0) && tuner) - return -EINVAL; - /* send value to tuner: */ sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); if (!sysex) - return 0; - value = simple_strtoul(buf, NULL, 10) & mask; + return -ENOMEM; sysex[SYSEX_DATA_OFS] = code; sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; - sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; - sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f; - sysex[SYSEX_DATA_OFS + 4] = (value ) & 0x0f; + sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; + sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f; + sysex[SYSEX_DATA_OFS + 4] = (value) & 0x0f; line6_send_sysex_message(&pod->line6, sysex, size); kfree(sysex); - return count; + return 0; } /* - "read" request on "dump_buf" special file. + "read" request on "serial_number" special file. */ -static ssize_t pod_get_dump_buf(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_interface *interface = to_usb_interface(dev); struct usb_line6_pod *pod = usb_get_intfdata(interface); - int retval = line6_wait_dump(&pod->dumpreq, 0); - if (retval < 0) - return retval; - memcpy(buf, &pod->prog_data_buf, sizeof(pod->prog_data_buf)); - return sizeof(pod->prog_data_buf); + + return sprintf(buf, "%d\n", pod->serial_number); } /* - "write" request on "dump_buf" special file. + "read" request on "firmware_version" special file. */ -static ssize_t pod_set_dump_buf(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t firmware_version_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_interface *interface = to_usb_interface(dev); struct usb_line6_pod *pod = usb_get_intfdata(interface); - if (count != sizeof(pod->prog_data)) { - dev_err(pod->line6.ifcdev, - "data block must be exactly %d bytes\n", - sizeof(pod->prog_data)); - return -EINVAL; - } - - memcpy(&pod->prog_data_buf, buf, sizeof(pod->prog_data)); - return sizeof(pod->prog_data); + return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100, + pod->firmware_version % 100); } /* - "write" request on "finish" special file. + "read" request on "device_id" special file. */ -static ssize_t pod_set_finish(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t device_id_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_interface *interface = to_usb_interface(dev); struct usb_line6_pod *pod = usb_get_intfdata(interface); - int size = 0; - char *sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_FINISH, size); - if (!sysex) - return 0; - line6_send_sysex_message(&pod->line6, sysex, size); - kfree(sysex); - return count; -} -/* - "write" request on "store_channel" special file. -*/ -static ssize_t pod_set_store_channel(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - return pod_send_store_command(dev, buf, count, 0x0000, 0x00c0); + return sprintf(buf, "%d\n", pod->device_id); } /* - "write" request on "store_effects_setup" special file. + POD startup procedure. + This is a sequence of functions with special requirements (e.g., must + not run immediately after initialization, must not run in interrupt + context). After the last one has finished, the device is ready to use. */ -static ssize_t pod_set_store_effects_setup(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - return pod_send_store_command(dev, buf, count, 0x0080, 0x0080); -} -/* - "write" request on "store_amp_setup" special file. -*/ -static ssize_t pod_set_store_amp_setup(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static void pod_startup1(struct usb_line6_pod *pod) { - return pod_send_store_command(dev, buf, count, 0x0040, 0x0100); -} + CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT); -/* - "write" request on "retrieve_channel" special file. -*/ -static ssize_t pod_set_retrieve_channel(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - return pod_send_retrieve_command(dev, buf, count, 0x0000, 0x00c0); + /* delay startup procedure: */ + line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, + (unsigned long)pod); } -/* - "write" request on "retrieve_effects_setup" special file. -*/ -static ssize_t pod_set_retrieve_effects_setup(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static void pod_startup2(unsigned long data) { - return pod_send_retrieve_command(dev, buf, count, 0x0080, 0x0080); -} + struct usb_line6_pod *pod = (struct usb_line6_pod *)data; + struct usb_line6 *line6 = &pod->line6; -/* - "write" request on "retrieve_amp_setup" special file. -*/ -static ssize_t pod_set_retrieve_amp_setup(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - return pod_send_retrieve_command(dev, buf, count, 0x0040, 0x0100); -} + CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ); -/* - "read" request on "dirty" special file. -*/ -static ssize_t pod_get_dirty(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - buf[0] = pod->dirty ? '1' : '0'; - buf[1] = '\n'; - return 2; + /* request firmware version: */ + line6_version_request_async(line6); } -/* - "read" request on "midi_postprocess" special file. -*/ -static ssize_t pod_get_midi_postprocess(struct device *dev, - struct device_attribute *attr, - char *buf) +static void pod_startup3(struct usb_line6_pod *pod) { - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - return sprintf(buf, "%d\n", pod->midi_postprocess); -} + CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_WORKQUEUE); -/* - "write" request on "midi_postprocess" special file. -*/ -static ssize_t pod_set_midi_postprocess(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - int value = simple_strtoul(buf, NULL, 10); - pod->midi_postprocess = value ? 1 : 0; - return count; + /* schedule work for global work queue: */ + schedule_work(&pod->startup_work); } -/* - "read" request on "serial_number" special file. -*/ -static ssize_t pod_get_serial_number(struct device *dev, - struct device_attribute *attr, char *buf) +static void pod_startup4(struct work_struct *work) { - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - return sprintf(buf, "%d\n", pod->serial_number); -} + struct usb_line6_pod *pod = + container_of(work, struct usb_line6_pod, startup_work); + struct usb_line6 *line6 = &pod->line6; -/* - "read" request on "firmware_version" special file. -*/ -static ssize_t pod_get_firmware_version(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100, - pod->firmware_version % 100); -} + CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_SETUP); -/* - "read" request on "device_id" special file. -*/ -static ssize_t pod_get_device_id(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - return sprintf(buf, "%d\n", pod->device_id); -} + /* serial number: */ + line6_read_serial_number(&pod->line6, &pod->serial_number); -/* - "read" request on "clip" special file. -*/ -static ssize_t pod_wait_for_clip(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - int err = 0; - DECLARE_WAITQUEUE(wait, current); - pod->clipping.value = 0; - add_wait_queue(&pod->clipping.wait, &wait); - current->state = TASK_INTERRUPTIBLE; - - while (pod->clipping.value == 0) { - if (signal_pending(current)) { - err = -ERESTARTSYS; - break; - } else - schedule(); - } - - current->state = TASK_RUNNING; - remove_wait_queue(&pod->clipping.wait, &wait); - return err; + /* ALSA audio interface: */ + line6_register_audio(line6); } -#define POD_GET_SYSTEM_PARAM(code, tuner, sign) \ -static ssize_t pod_get_ ## code(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct usb_interface *interface = to_usb_interface(dev); \ - struct usb_line6_pod *pod = usb_get_intfdata(interface); \ - return pod_get_system_param(pod, buf, POD_ ## code, &pod->code, \ - tuner, sign); \ +/* POD special files: */ +static DEVICE_ATTR_RO(device_id); +static DEVICE_ATTR_RO(firmware_version); +static DEVICE_ATTR_RO(serial_number); + +/* control info callback */ +static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 65535; + return 0; } -#define POD_GET_SET_SYSTEM_PARAM(code, mask, tuner, sign) \ -POD_GET_SYSTEM_PARAM(code, tuner, sign) \ -static ssize_t pod_set_ ## code(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - struct usb_interface *interface = to_usb_interface(dev); \ - struct usb_line6_pod *pod = usb_get_intfdata(interface); \ - return pod_set_system_param(pod, buf, count, POD_ ## code, mask, \ - tuner); \ +/* control get callback */ +static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; + + ucontrol->value.integer.value[0] = pod->monitor_level; + return 0; } -POD_GET_SET_SYSTEM_PARAM(monitor_level, 0xffff, 0, 0); -POD_GET_SET_SYSTEM_PARAM(routing, 0x0003, 0, 0); -POD_GET_SET_SYSTEM_PARAM(tuner_mute, 0x0001, 1, 0); -POD_GET_SET_SYSTEM_PARAM(tuner_freq, 0xffff, 1, 0); -POD_GET_SYSTEM_PARAM(tuner_note, 1, 1); -POD_GET_SYSTEM_PARAM(tuner_pitch, 1, 1); +/* control put callback */ +static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; -#undef GET_SET_SYSTEM_PARAM -#undef GET_SYSTEM_PARAM + if (ucontrol->value.integer.value[0] == pod->monitor_level) + return 0; -/* POD special files: */ -static DEVICE_ATTR(channel, S_IWUGO | S_IRUGO, pod_get_channel, pod_set_channel); -static DEVICE_ATTR(clip, S_IRUGO, pod_wait_for_clip, line6_nop_write); -static DEVICE_ATTR(device_id, S_IRUGO, pod_get_device_id, line6_nop_write); -static DEVICE_ATTR(dirty, S_IRUGO, pod_get_dirty, line6_nop_write); -static DEVICE_ATTR(dump, S_IWUGO | S_IRUGO, pod_get_dump, pod_set_dump); -static DEVICE_ATTR(dump_buf, S_IWUGO | S_IRUGO, pod_get_dump_buf, pod_set_dump_buf); -static DEVICE_ATTR(finish, S_IWUGO, line6_nop_read, pod_set_finish); -static DEVICE_ATTR(firmware_version, S_IRUGO, pod_get_firmware_version, line6_nop_write); -static DEVICE_ATTR(midi_postprocess, S_IWUGO | S_IRUGO, pod_get_midi_postprocess, pod_set_midi_postprocess); -static DEVICE_ATTR(monitor_level, S_IWUGO | S_IRUGO, pod_get_monitor_level, pod_set_monitor_level); -static DEVICE_ATTR(name, S_IRUGO, pod_get_name, line6_nop_write); -static DEVICE_ATTR(name_buf, S_IRUGO, pod_get_name_buf, line6_nop_write); -static DEVICE_ATTR(retrieve_amp_setup, S_IWUGO, line6_nop_read, pod_set_retrieve_amp_setup); -static DEVICE_ATTR(retrieve_channel, S_IWUGO, line6_nop_read, pod_set_retrieve_channel); -static DEVICE_ATTR(retrieve_effects_setup, S_IWUGO, line6_nop_read, pod_set_retrieve_effects_setup); -static DEVICE_ATTR(routing, S_IWUGO | S_IRUGO, pod_get_routing, pod_set_routing); -static DEVICE_ATTR(serial_number, S_IRUGO, pod_get_serial_number, line6_nop_write); -static DEVICE_ATTR(store_amp_setup, S_IWUGO, line6_nop_read, pod_set_store_amp_setup); -static DEVICE_ATTR(store_channel, S_IWUGO, line6_nop_read, pod_set_store_channel); -static DEVICE_ATTR(store_effects_setup, S_IWUGO, line6_nop_read, pod_set_store_effects_setup); -static DEVICE_ATTR(tuner_freq, S_IWUGO | S_IRUGO, pod_get_tuner_freq, pod_set_tuner_freq); -static DEVICE_ATTR(tuner_mute, S_IWUGO | S_IRUGO, pod_get_tuner_mute, pod_set_tuner_mute); -static DEVICE_ATTR(tuner_note, S_IRUGO, pod_get_tuner_note, line6_nop_write); -static DEVICE_ATTR(tuner_pitch, S_IRUGO, pod_get_tuner_pitch, line6_nop_write); - -#if CREATE_RAW_FILE -static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw); -#endif + pod->monitor_level = ucontrol->value.integer.value[0]; + pod_set_system_param_int(pod, ucontrol->value.integer.value[0], + POD_MONITOR_LEVEL); + return 1; +} + +/* control definition */ +static struct snd_kcontrol_new pod_control_monitor = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_pod_control_monitor_info, + .get = snd_pod_control_monitor_get, + .put = snd_pod_control_monitor_put +}; /* POD destructor. @@ -947,19 +336,13 @@ static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw); static void pod_destruct(struct usb_interface *interface) { struct usb_line6_pod *pod = usb_get_intfdata(interface); - struct usb_line6 *line6; if (pod == NULL) return; - line6 = &pod->line6; - if (line6 == NULL) - return; - line6_cleanup_audio(line6); + line6_cleanup_audio(&pod->line6); - /* free dump request data: */ - line6_dumpreq_destruct(&pod->dumpreq); - - kfree(pod->buffer_versionreq); + del_timer(&pod->startup_timer); + cancel_work_sync(&pod->startup_work); } /* @@ -969,132 +352,86 @@ static int pod_create_files2(struct device *dev) { int err; - CHECK_RETURN(device_create_file(dev, &dev_attr_channel)); - CHECK_RETURN(device_create_file(dev, &dev_attr_clip)); CHECK_RETURN(device_create_file(dev, &dev_attr_device_id)); - CHECK_RETURN(device_create_file(dev, &dev_attr_dirty)); - CHECK_RETURN(device_create_file(dev, &dev_attr_dump)); - CHECK_RETURN(device_create_file(dev, &dev_attr_dump_buf)); - CHECK_RETURN(device_create_file(dev, &dev_attr_finish)); CHECK_RETURN(device_create_file(dev, &dev_attr_firmware_version)); - CHECK_RETURN(device_create_file(dev, &dev_attr_midi_postprocess)); - CHECK_RETURN(device_create_file(dev, &dev_attr_monitor_level)); - CHECK_RETURN(device_create_file(dev, &dev_attr_name)); - CHECK_RETURN(device_create_file(dev, &dev_attr_name_buf)); - CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_amp_setup)); - CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_channel)); - CHECK_RETURN(device_create_file(dev, &dev_attr_retrieve_effects_setup)); - CHECK_RETURN(device_create_file(dev, &dev_attr_routing)); CHECK_RETURN(device_create_file(dev, &dev_attr_serial_number)); - CHECK_RETURN(device_create_file(dev, &dev_attr_store_amp_setup)); - CHECK_RETURN(device_create_file(dev, &dev_attr_store_channel)); - CHECK_RETURN(device_create_file(dev, &dev_attr_store_effects_setup)); - CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_freq)); - CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_mute)); - CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_note)); - CHECK_RETURN(device_create_file(dev, &dev_attr_tuner_pitch)); - -#if CREATE_RAW_FILE - CHECK_RETURN(device_create_file(dev, &dev_attr_raw)); -#endif - return 0; } /* - Init POD device. + Try to init POD device. */ -int pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) +static int pod_try_init(struct usb_interface *interface, + struct usb_line6_pod *pod) { int err; struct usb_line6 *line6 = &pod->line6; + init_timer(&pod->startup_timer); + INIT_WORK(&pod->startup_work, pod_startup4); + if ((interface == NULL) || (pod == NULL)) return -ENODEV; - pod->channel_num = 255; - - /* initialize wait queues: */ - init_waitqueue_head(&pod->monitor_level.wait); - init_waitqueue_head(&pod->routing.wait); - init_waitqueue_head(&pod->tuner_mute.wait); - init_waitqueue_head(&pod->tuner_freq.wait); - init_waitqueue_head(&pod->tuner_note.wait); - init_waitqueue_head(&pod->tuner_pitch.wait); - init_waitqueue_head(&pod->clipping.wait); - - memset(pod->param_dirty, 0xff, sizeof(pod->param_dirty)); - - /* initialize USB buffers: */ - err = line6_dumpreq_init(&pod->dumpreq, pod_request_channel, - sizeof(pod_request_channel)); - if (err < 0) { - dev_err(&interface->dev, "Out of memory\n"); - pod_destruct(interface); - return -ENOMEM; - } - - pod->buffer_versionreq = kmalloc(sizeof(pod_request_version), - GFP_KERNEL); - - if (pod->buffer_versionreq == NULL) { - dev_err(&interface->dev, "Out of memory\n"); - pod_destruct(interface); - return -ENOMEM; - } - - memcpy(pod->buffer_versionreq, pod_request_version, - sizeof(pod_request_version)); - /* create sysfs entries: */ err = pod_create_files2(&interface->dev); - if (err < 0) { - pod_destruct(interface); + if (err < 0) return err; - } /* initialize audio system: */ err = line6_init_audio(line6); - if (err < 0) { - pod_destruct(interface); + if (err < 0) return err; - } /* initialize MIDI subsystem: */ err = line6_init_midi(line6); - if (err < 0) { - pod_destruct(interface); + if (err < 0) return err; - } /* initialize PCM subsystem: */ err = line6_init_pcm(line6, &pod_pcm_properties); - if (err < 0) { - pod_destruct(interface); + if (err < 0) return err; - } - /* register audio system: */ - err = line6_register_audio(line6); - if (err < 0) { - pod_destruct(interface); + /* register monitor control: */ + err = snd_ctl_add(line6->card, + snd_ctl_new1(&pod_control_monitor, line6->line6pcm)); + if (err < 0) return err; - } + + /* + When the sound card is registered at this point, the PODxt Live + displays "Invalid Code Error 07", so we do it later in the event + handler. + */ if (pod->line6.properties->capabilities & LINE6_BIT_CONTROL) { - /* query some data: */ - line6_startup_delayed(&pod->dumpreq, POD_STARTUP_DELAY, - pod_startup_timeout, pod); - line6_read_serial_number(&pod->line6, &pod->serial_number); + pod->monitor_level = POD_SYSTEM_INVALID; + + /* initiate startup procedure: */ + pod_startup1(pod); } return 0; } /* + Init POD device (and clean up in case of failure). +*/ +int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) +{ + int err = pod_try_init(interface, pod); + + if (err < 0) + pod_destruct(interface); + + return err; +} + +/* POD device disconnected. */ -void pod_disconnect(struct usb_interface *interface) +void line6_pod_disconnect(struct usb_interface *interface) { struct usb_line6_pod *pod; @@ -1106,44 +443,14 @@ void pod_disconnect(struct usb_interface *interface) struct snd_line6_pcm *line6pcm = pod->line6.line6pcm; struct device *dev = &interface->dev; - if (line6pcm != NULL) { - unlink_wait_clear_audio_out_urbs(line6pcm); - unlink_wait_clear_audio_in_urbs(line6pcm); - } + if (line6pcm != NULL) + line6_pcm_disconnect(line6pcm); if (dev != NULL) { /* remove sysfs entries: */ - if (pod->versionreq_ok) - pod_remove_files(pod->firmware_version, pod->line6.properties->device_bit, dev); - - device_remove_file(dev, &dev_attr_channel); - device_remove_file(dev, &dev_attr_clip); device_remove_file(dev, &dev_attr_device_id); - device_remove_file(dev, &dev_attr_dirty); - device_remove_file(dev, &dev_attr_dump); - device_remove_file(dev, &dev_attr_dump_buf); - device_remove_file(dev, &dev_attr_finish); device_remove_file(dev, &dev_attr_firmware_version); - device_remove_file(dev, &dev_attr_midi_postprocess); - device_remove_file(dev, &dev_attr_monitor_level); - device_remove_file(dev, &dev_attr_name); - device_remove_file(dev, &dev_attr_name_buf); - device_remove_file(dev, &dev_attr_retrieve_amp_setup); - device_remove_file(dev, &dev_attr_retrieve_channel); - device_remove_file(dev, &dev_attr_retrieve_effects_setup); - device_remove_file(dev, &dev_attr_routing); device_remove_file(dev, &dev_attr_serial_number); - device_remove_file(dev, &dev_attr_store_amp_setup); - device_remove_file(dev, &dev_attr_store_channel); - device_remove_file(dev, &dev_attr_store_effects_setup); - device_remove_file(dev, &dev_attr_tuner_freq); - device_remove_file(dev, &dev_attr_tuner_mute); - device_remove_file(dev, &dev_attr_tuner_note); - device_remove_file(dev, &dev_attr_tuner_pitch); - -#if CREATE_RAW_FILE - device_remove_file(dev, &dev_attr_raw); -#endif } } diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h index 7051ca61381..3e3f1671337 100644 --- a/drivers/staging/line6/pod.h +++ b/drivers/staging/line6/pod.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -12,18 +12,13 @@ #ifndef POD_H #define POD_H - -#include "driver.h" - +#include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/usb.h> -#include <linux/wait.h> -#include <linux/workqueue.h> #include <sound/core.h> -#include "dumprequest.h" - +#include "driver.h" /* PODxt Live interfaces @@ -42,163 +37,66 @@ */ #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; -}; +#define POD_STARTUP_DELAY 1000 -/** - Binary PodXT Pro program dump +/* + Stages of POD startup procedure */ -struct pod_program { - /** - Header information (including program name). - */ - unsigned char header[0x20]; - - /** - Program parameters. - */ - unsigned char control[POD_CONTROL_SIZE]; +enum { + POD_STARTUP_INIT = 1, + POD_STARTUP_VERSIONREQ, + POD_STARTUP_WORKQUEUE, + POD_STARTUP_SETUP, + POD_STARTUP_LAST = POD_STARTUP_SETUP - 1 }; struct usb_line6_pod { /** - Generic Line6 USB data. + 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. + Instrument monitor level. */ - struct ValueWait tuner_pitch; + int monitor_level; /** - Instrument monitor level. + Timer for device initializaton. */ - struct ValueWait monitor_level; + struct timer_list startup_timer; /** - Audio routing mode. - 0: send processed guitar - 1: send clean guitar - 2: send clean guitar re-amp playback - 3: send re-amp playback + Work handler for device initializaton. */ - struct ValueWait routing; + struct work_struct startup_work; /** - Wait for audio clipping event. + Current progress in startup procedure. */ - struct ValueWait clipping; + int startup_progress; /** - 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. + Serial number of device. */ int serial_number; /** - Firmware version (x 100). + Firmware version (x 100). */ int firmware_version; /** - Device ID. + Device ID. */ int device_id; - - /** - Flag to indicate modification of current program settings. - */ - 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); - +extern void line6_pod_disconnect(struct usb_interface *interface); +extern int line6_pod_init(struct usb_interface *interface, + struct usb_line6_pod *pod); +extern void line6_pod_process_message(struct usb_line6_pod *pod); +extern void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, + u8 value); #endif diff --git a/drivers/staging/line6/podhd.c b/drivers/staging/line6/podhd.c new file mode 100644 index 00000000000..7ef45437b4f --- /dev/null +++ b/drivers/staging/line6/podhd.c @@ -0,0 +1,154 @@ +/* + * Line6 Pod HD + * + * Copyright (C) 2011 Stefan Hajnoczi <stefanha@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 <sound/core.h> +#include <sound/pcm.h> + +#include "audio.h" +#include "driver.h" +#include "pcm.h" +#include "podhd.h" + +#define PODHD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ + +static struct snd_ratden podhd_ratden = { + .num_min = 48000, + .num_max = 48000, + .num_step = 1, + .den = 1, +}; + +static struct line6_pcm_properties podhd_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 | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .snd_line6_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .snd_line6_rates = { + .nrats = 1, + .rats = &podhd_ratden}, + .bytes_per_frame = PODHD_BYTES_PER_FRAME +}; + +/* + POD HD destructor. +*/ +static void podhd_destruct(struct usb_interface *interface) +{ + struct usb_line6_podhd *podhd = usb_get_intfdata(interface); + + if (podhd == NULL) + return; + line6_cleanup_audio(&podhd->line6); +} + +/* + Try to init POD HD device. +*/ +static int podhd_try_init(struct usb_interface *interface, + struct usb_line6_podhd *podhd) +{ + int err; + struct usb_line6 *line6 = &podhd->line6; + + if ((interface == NULL) || (podhd == NULL)) + return -ENODEV; + + /* initialize audio system: */ + err = line6_init_audio(line6); + if (err < 0) + return err; + + /* initialize MIDI subsystem: */ + err = line6_init_midi(line6); + if (err < 0) + return err; + + /* initialize PCM subsystem: */ + err = line6_init_pcm(line6, &podhd_pcm_properties); + if (err < 0) + return err; + + /* register USB audio system: */ + err = line6_register_audio(line6); + return err; +} + +/* + Init POD HD device (and clean up in case of failure). +*/ +int line6_podhd_init(struct usb_interface *interface, + struct usb_line6_podhd *podhd) +{ + int err = podhd_try_init(interface, podhd); + + if (err < 0) + podhd_destruct(interface); + + return err; +} + +/* + POD HD device disconnected. +*/ +void line6_podhd_disconnect(struct usb_interface *interface) +{ + struct usb_line6_podhd *podhd; + + if (interface == NULL) + return; + podhd = usb_get_intfdata(interface); + + if (podhd != NULL) { + struct snd_line6_pcm *line6pcm = podhd->line6.line6pcm; + + if (line6pcm != NULL) + line6_pcm_disconnect(line6pcm); + } + + podhd_destruct(interface); +} diff --git a/drivers/staging/line6/podhd.h b/drivers/staging/line6/podhd.h new file mode 100644 index 00000000000..652f74056bb --- /dev/null +++ b/drivers/staging/line6/podhd.h @@ -0,0 +1,30 @@ +/* + * Line6 Pod HD + * + * Copyright (C) 2011 Stefan Hajnoczi <stefanha@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. + * + */ + +#ifndef PODHD_H +#define PODHD_H + +#include <linux/usb.h> + +#include "driver.h" + +struct usb_line6_podhd { + /** + Generic Line6 USB data. + */ + struct usb_line6 line6; +}; + +extern void line6_podhd_disconnect(struct usb_interface *interface); +extern int line6_podhd_init(struct usb_interface *interface, + struct usb_line6_podhd *podhd); + +#endif /* PODHD_H */ diff --git a/drivers/staging/line6/revision.h b/drivers/staging/line6/revision.h index b2a0a85efe6..b4eee2b7383 100644 --- a/drivers/staging/line6/revision.h +++ b/drivers/staging/line6/revision.h @@ -1,4 +1,4 @@ #ifndef DRIVER_REVISION /* current subversion revision */ -#define DRIVER_REVISION " (revision 529)" +#define DRIVER_REVISION " (904)" #endif diff --git a/drivers/staging/line6/toneport.c b/drivers/staging/line6/toneport.c index eaa1229002a..69437158d38 100644 --- a/drivers/staging/line6/toneport.c +++ b/drivers/staging/line6/toneport.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * Emil Myhrman (emil.myhrman@gmail.com) * * This program is free software; you can redistribute it and/or @@ -10,16 +10,18 @@ * */ -#include "driver.h" +#include <linux/wait.h> +#include <sound/control.h> #include "audio.h" #include "capture.h" +#include "driver.h" #include "playback.h" #include "toneport.h" - static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); +#define TONEPORT_PCM_DELAY 1 static struct snd_ratden toneport_ratden = { .num_min = 44100, @@ -29,47 +31,50 @@ static struct snd_ratden toneport_ratden = { }; static struct line6_pcm_properties toneport_pcm_properties = { - .snd_line6_playback_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 44100, - .rate_max = 44100, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 180 * 4, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024 - }, - .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_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 | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_KNOT, + .rate_min = 44100, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .snd_line6_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | +#ifdef CONFIG_PM + SNDRV_PCM_INFO_RESUME | +#endif + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_KNOT, + .rate_min = 44100, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, .snd_line6_rates = { - .nrats = 1, - .rats = &toneport_ratden - }, + .nrats = 1, + .rats = &toneport_ratden}, .bytes_per_frame = 4 }; @@ -82,6 +87,24 @@ static struct line6_pcm_properties toneport_pcm_properties = { static int led_red = 0x00; static int led_green = 0x26; +static const struct { + const char *name; + int code; +} toneport_source_info[] = { + {"Microphone", 0x0a01}, + {"Line", 0x0801}, + {"Instrument", 0x0b01}, + {"Inst & Mic", 0x0901} +}; + +static bool toneport_has_led(short product) +{ + return + (product == LINE6_DEVID_GUITARPORT) || + (product == LINE6_DEVID_TONEPORT_GX); + /* add your device here if you are missing support for the LEDs */ +} + static void toneport_update_led(struct device *dev) { struct usb_interface *interface = to_usb_interface(dev); @@ -101,8 +124,12 @@ static ssize_t toneport_set_led_red(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - char *c; - led_red = simple_strtol(buf, &c, 10); + int retval; + + retval = kstrtoint(buf, 10, &led_red); + if (retval) + return retval; + toneport_update_led(dev); return count; } @@ -111,15 +138,20 @@ static ssize_t toneport_set_led_green(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - char *c; - led_green = simple_strtol(buf, &c, 10); + int retval; + + retval = kstrtoint(buf, 10, &led_green); + if (retval) + return retval; + 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 DEVICE_ATTR(led_red, S_IWUSR | S_IRUGO, line6_nop_read, + toneport_set_led_red); +static DEVICE_ATTR(led_green, S_IWUSR | S_IRUGO, line6_nop_read, + toneport_set_led_green); static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) { @@ -130,100 +162,287 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) cmd1, cmd2, NULL, 0, LINE6_TIMEOUT * HZ); if (ret < 0) { - err("send failed (error %d)\n", ret); + dev_err(&usbdev->dev, "send failed (error %d)\n", ret); return ret; } return 0; } +/* monitor info callback */ +static int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 256; + return 0; +} + +/* monitor get callback */ +static int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = line6pcm->volume_monitor; + return 0; +} + +/* monitor put callback */ +static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + + if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor) + return 0; + + line6pcm->volume_monitor = ucontrol->value.integer.value[0]; + + if (line6pcm->volume_monitor > 0) + line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_MONITOR); + else + line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); + + return 1; +} + +/* source info callback */ +static int snd_toneport_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + const int size = ARRAY_SIZE(toneport_source_info); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = size; + + if (uinfo->value.enumerated.item >= size) + uinfo->value.enumerated.item = size - 1; + + strcpy(uinfo->value.enumerated.name, + toneport_source_info[uinfo->value.enumerated.item].name); + + return 0; +} + +/* source get callback */ +static int snd_toneport_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_toneport *toneport = + (struct usb_line6_toneport *)line6pcm->line6; + ucontrol->value.enumerated.item[0] = toneport->source; + return 0; +} + +/* source put callback */ +static int snd_toneport_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + struct usb_line6_toneport *toneport = + (struct usb_line6_toneport *)line6pcm->line6; + unsigned int source; + + source = ucontrol->value.enumerated.item[0]; + if (source >= ARRAY_SIZE(toneport_source_info)) + return -EINVAL; + if (source == toneport->source) + return 0; + + toneport->source = source; + toneport_send_cmd(toneport->line6.usbdev, + toneport_source_info[source].code, 0x0000); + return 1; +} + +static void toneport_start_pcm(unsigned long arg) +{ + struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg; + struct usb_line6 *line6 = &toneport->line6; + + line6_pcm_acquire(line6->line6pcm, LINE6_BITS_PCM_MONITOR); +} + +/* control definition */ +static struct snd_kcontrol_new toneport_control_monitor = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_toneport_monitor_info, + .get = snd_toneport_monitor_get, + .put = snd_toneport_monitor_put +}; + +/* source selector definition */ +static struct snd_kcontrol_new toneport_control_source = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Capture Source", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_toneport_source_info, + .get = snd_toneport_source_get, + .put = snd_toneport_source_put +}; + /* Toneport destructor. */ 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); + line6_cleanup_audio(&toneport->line6); } /* - Init Toneport device. + Setup Toneport device. */ -int toneport_init(struct usb_interface *interface, - struct usb_line6_toneport *toneport) +static void toneport_setup(struct usb_line6_toneport *toneport) { - int err, ticks; + int ticks; struct usb_line6 *line6 = &toneport->line6; - struct usb_device *usbdev; + struct usb_device *usbdev = line6->usbdev; + u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct); + + /* sync time on device with host: */ + ticks = (int)get_seconds(); + line6_write_data(line6, 0x80c6, &ticks, 4); + + /* enable device: */ + toneport_send_cmd(usbdev, 0x0301, 0x0000); + + /* initialize source select: */ + switch (le16_to_cpu(usbdev->descriptor.idProduct)) { + case LINE6_DEVID_TONEPORT_UX1: + case LINE6_DEVID_TONEPORT_UX2: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: + toneport_send_cmd(usbdev, + toneport_source_info[toneport->source].code, + 0x0000); + } + + if (toneport_has_led(idProduct)) + toneport_update_led(&usbdev->dev); +} + +/* + Try to init Toneport device. +*/ +static int toneport_try_init(struct usb_interface *interface, + struct usb_line6_toneport *toneport) +{ + int err; + struct usb_line6 *line6 = &toneport->line6; + struct usb_device *usbdev = line6->usbdev; + u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct); if ((interface == NULL) || (toneport == NULL)) return -ENODEV; /* initialize audio system: */ err = line6_init_audio(line6); - if (err < 0) { - toneport_destruct(interface); + if (err < 0) return err; - } /* initialize PCM subsystem: */ err = line6_init_pcm(line6, &toneport_pcm_properties); - if (err < 0) { - toneport_destruct(interface); + if (err < 0) + return err; + + /* register monitor control: */ + err = snd_ctl_add(line6->card, + snd_ctl_new1(&toneport_control_monitor, + line6->line6pcm)); + if (err < 0) return err; + + /* register source select control: */ + switch (le16_to_cpu(usbdev->descriptor.idProduct)) { + case LINE6_DEVID_TONEPORT_UX1: + case LINE6_DEVID_TONEPORT_UX2: + case LINE6_DEVID_PODSTUDIO_UX1: + case LINE6_DEVID_PODSTUDIO_UX2: + err = + snd_ctl_add(line6->card, + snd_ctl_new1(&toneport_control_source, + line6->line6pcm)); + if (err < 0) + return err; } /* register audio system: */ err = line6_register_audio(line6); - if (err < 0) { - toneport_destruct(interface); + if (err < 0) return err; - } - usbdev = line6->usbdev; line6_read_serial_number(line6, &toneport->serial_number); line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); - /* sync time on device with host: */ - ticks = (int)get_seconds(); - line6_write_data(line6, 0x80c6, &ticks, 4); + if (toneport_has_led(idProduct)) { + CHECK_RETURN(device_create_file + (&interface->dev, &dev_attr_led_red)); + CHECK_RETURN(device_create_file + (&interface->dev, &dev_attr_led_green)); + } - /* - seems to work without the first two... - */ - /* toneport_send_cmd(usbdev, 0x0201, 0x0002); */ - /* toneport_send_cmd(usbdev, 0x0801, 0x0000); */ - /* only one that works for me; on GP, TP might be different? */ - toneport_send_cmd(usbdev, 0x0301, 0x0000); + toneport_setup(toneport); - if (usbdev->descriptor.idProduct != LINE6_DEVID_GUITARPORT) { - CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_red)); - CHECK_RETURN(device_create_file(&interface->dev, &dev_attr_led_green)); - toneport_update_led(&usbdev->dev); - } + init_timer(&toneport->timer); + toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ; + toneport->timer.function = toneport_start_pcm; + toneport->timer.data = (unsigned long)toneport; + add_timer(&toneport->timer); return 0; } /* + Init Toneport device (and clean up in case of failure). +*/ +int line6_toneport_init(struct usb_interface *interface, + struct usb_line6_toneport *toneport) +{ + int err = toneport_try_init(interface, toneport); + + if (err < 0) + toneport_destruct(interface); + + return err; +} + +/* + Resume Toneport device after reset. +*/ +void line6_toneport_reset_resume(struct usb_line6_toneport *toneport) +{ + toneport_setup(toneport); +} + +/* Toneport device disconnected. */ -void toneport_disconnect(struct usb_interface *interface) +void line6_toneport_disconnect(struct usb_interface *interface) { struct usb_line6_toneport *toneport; + u16 idProduct; if (interface == NULL) return; + toneport = usb_get_intfdata(interface); + del_timer_sync(&toneport->timer); + idProduct = le16_to_cpu(toneport->line6.usbdev->descriptor.idProduct); - if (toneport->line6.usbdev->descriptor.idProduct != LINE6_DEVID_GUITARPORT) { + if (toneport_has_led(idProduct)) { device_remove_file(&interface->dev, &dev_attr_led_red); device_remove_file(&interface->dev, &dev_attr_led_green); } @@ -232,8 +451,8 @@ void toneport_disconnect(struct usb_interface *interface) struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; if (line6pcm != NULL) { - unlink_wait_clear_audio_out_urbs(line6pcm); - unlink_wait_clear_audio_in_urbs(line6pcm); + line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); + line6_pcm_disconnect(line6pcm); } } diff --git a/drivers/staging/line6/toneport.h b/drivers/staging/line6/toneport.h index bddc58dd7e3..8576b726364 100644 --- a/drivers/staging/line6/toneport.h +++ b/drivers/staging/line6/toneport.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -12,34 +12,41 @@ #ifndef TONEPORT_H #define TONEPORT_H - -#include "driver.h" - #include <linux/usb.h> #include <sound/core.h> +#include "driver.h" struct usb_line6_toneport { /** - Generic Line6 USB data. + Generic Line6 USB data. */ struct usb_line6 line6; /** - Serial number of device. + Source selector. + */ + int source; + + /** + Serial number of device. */ int serial_number; /** - Firmware version (x 100). + Firmware version (x 100). */ int firmware_version; -}; - -extern void toneport_disconnect(struct usb_interface *interface); -extern int toneport_init(struct usb_interface *interface, - struct usb_line6_toneport *toneport); + /** + Timer for delayed PCM startup. + */ + struct timer_list timer; +}; +extern void line6_toneport_disconnect(struct usb_interface *interface); +extern int line6_toneport_init(struct usb_interface *interface, + struct usb_line6_toneport *toneport); +extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport); #endif diff --git a/drivers/staging/line6/usbdefs.h b/drivers/staging/line6/usbdefs.h index c38f31f2f42..2d1cc472bea 100644 --- a/drivers/staging/line6/usbdefs.h +++ b/drivers/staging/line6/usbdefs.h @@ -1,5 +1,5 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * * Copyright (C) 2005-2008 Markus Grabner (grabner@icg.tugraz.at) * @@ -12,7 +12,6 @@ #ifndef USBDEFS_H #define USBDEFS_H - #define LINE6_VENDOR_ID 0x0e41 #define USB_INTERVALS_PER_SECOND 1000 @@ -25,6 +24,12 @@ #define LINE6_DEVID_BASSPODXTPRO 0x4252 #define LINE6_DEVID_GUITARPORT 0x4750 #define LINE6_DEVID_POCKETPOD 0x5051 +#define LINE6_DEVID_PODHD300 0x5057 +#define LINE6_DEVID_PODHD400 0x5058 +#define LINE6_DEVID_PODHD500 0x414D +#define LINE6_DEVID_PODSTUDIO_GX 0x4153 +#define LINE6_DEVID_PODSTUDIO_UX1 0x4150 +#define LINE6_DEVID_PODSTUDIO_UX2 0x4151 #define LINE6_DEVID_PODX3 0x414a #define LINE6_DEVID_PODX3LIVE 0x414b #define LINE6_DEVID_PODXT 0x5044 @@ -35,40 +40,77 @@ #define LINE6_DEVID_TONEPORT_UX2 0x4142 #define LINE6_DEVID_VARIAX 0x534d -#define LINE6_BIT_BASSPODXT (1 << 0) -#define LINE6_BIT_BASSPODXTLIVE (1 << 1) -#define LINE6_BIT_BASSPODXTPRO (1 << 2) -#define LINE6_BIT_GUITARPORT (1 << 3) -#define LINE6_BIT_POCKETPOD (1 << 4) -#define LINE6_BIT_PODX3 (1 << 5) -#define LINE6_BIT_PODX3LIVE (1 << 6) -#define LINE6_BIT_PODXT (1 << 7) -#define LINE6_BIT_PODXTLIVE (1 << 8) -#define LINE6_BIT_PODXTPRO (1 << 9) -#define LINE6_BIT_TONEPORT_GX (1 << 10) -#define LINE6_BIT_TONEPORT_UX1 (1 << 11) -#define LINE6_BIT_TONEPORT_UX2 (1 << 12) -#define LINE6_BIT_VARIAX (1 << 13) +#define LINE6_BIT(x) LINE6_BIT_ ## x = 1 << LINE6_INDEX_ ## x + +enum { + LINE6_INDEX_BASSPODXT, + LINE6_INDEX_BASSPODXTLIVE, + LINE6_INDEX_BASSPODXTPRO, + LINE6_INDEX_GUITARPORT, + LINE6_INDEX_POCKETPOD, + LINE6_INDEX_PODHD300, + LINE6_INDEX_PODHD400, + LINE6_INDEX_PODHD500, + LINE6_INDEX_PODSTUDIO_GX, + LINE6_INDEX_PODSTUDIO_UX1, + LINE6_INDEX_PODSTUDIO_UX2, + LINE6_INDEX_PODX3, + LINE6_INDEX_PODX3LIVE, + LINE6_INDEX_PODXT, + LINE6_INDEX_PODXTLIVE, + LINE6_INDEX_PODXTPRO, + LINE6_INDEX_TONEPORT_GX, + LINE6_INDEX_TONEPORT_UX1, + LINE6_INDEX_TONEPORT_UX2, + LINE6_INDEX_VARIAX, -#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) + LINE6_BIT(BASSPODXT), + LINE6_BIT(BASSPODXTLIVE), + LINE6_BIT(BASSPODXTPRO), + LINE6_BIT(GUITARPORT), + LINE6_BIT(POCKETPOD), + LINE6_BIT(PODHD300), + LINE6_BIT(PODHD400), + LINE6_BIT(PODHD500), + LINE6_BIT(PODSTUDIO_GX), + LINE6_BIT(PODSTUDIO_UX1), + LINE6_BIT(PODSTUDIO_UX2), + LINE6_BIT(PODX3), + LINE6_BIT(PODX3LIVE), + LINE6_BIT(PODXT), + LINE6_BIT(PODXTLIVE), + LINE6_BIT(PODXTPRO), + LINE6_BIT(TONEPORT_GX), + LINE6_BIT(TONEPORT_UX1), + LINE6_BIT(TONEPORT_UX2), + LINE6_BIT(VARIAX), + + LINE6_BITS_PRO = LINE6_BIT_BASSPODXTPRO | LINE6_BIT_PODXTPRO, + LINE6_BITS_LIVE = LINE6_BIT_BASSPODXTLIVE | LINE6_BIT_PODXTLIVE | + LINE6_BIT_PODX3LIVE, + LINE6_BITS_PODXTALL = LINE6_BIT_PODXT | LINE6_BIT_PODXTLIVE | + LINE6_BIT_PODXTPRO, + LINE6_BITS_PODX3ALL = LINE6_BIT_PODX3 | LINE6_BIT_PODX3LIVE, + LINE6_BITS_PODHDALL = LINE6_BIT_PODHD300 | + LINE6_BIT_PODHD400 | + LINE6_BIT_PODHD500, + LINE6_BITS_BASSPODXTALL = LINE6_BIT_BASSPODXT | + LINE6_BIT_BASSPODXTLIVE | + LINE6_BIT_BASSPODXTPRO +}; /* device supports settings parameter via USB */ -#define LINE6_BIT_CONTROL (1 << 0) +#define LINE6_BIT_CONTROL (1 << 0) /* device supports PCM input/output via USB */ -#define LINE6_BIT_PCM (1 << 1) -#define LINE6_BIT_CONTROL_PCM (LINE6_BIT_CONTROL | LINE6_BIT_PCM) +#define LINE6_BIT_PCM (1 << 1) +/* device support hardware monitoring */ +#define LINE6_BIT_HWMON (1 << 2) + +#define LINE6_BIT_CTRL_PCM_HW (LINE6_BIT_CONTROL | \ + LINE6_BIT_PCM | \ + LINE6_BIT_HWMON) -#define LINE6_FALLBACK_INTERVAL 10 -#define LINE6_FALLBACK_MAXPACKETSIZE 16 +#define LINE6_FALLBACK_INTERVAL 10 +#define LINE6_FALLBACK_MAXPACKETSIZE 16 #endif diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c index f9d96984733..ae2be99f9a9 100644 --- a/drivers/staging/line6/variax.c +++ b/drivers/staging/line6/variax.c @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,538 +9,227 @@ * */ -#include "driver.h" +#include <linux/slab.h> #include "audio.h" -#include "control.h" +#include "driver.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 +/* + This message is sent by the device during initialization and identifies + the connected guitar version. +*/ +static const char variax_init_version[] = { + 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c, + 0x07, 0x00, 0x00, 0x00 +}; + +/* + This message is the last one sent by the device during initialization. +*/ +static const char variax_init_done[] = { + 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b +}; static const char variax_activate[] = { 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, 0xf7 }; -static const char variax_request_bank[] = { - 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6d, 0xf7 -}; -static const char variax_request_model1[] = { - 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x03, - 0x00, 0x00, 0x00, 0xf7 -}; -static const char variax_request_model2[] = { - 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x03, - 0x00, 0x00, 0x00, 0xf7 -}; +/* forward declarations: */ +static void variax_startup2(unsigned long data); +static void variax_startup4(unsigned long data); +static void variax_startup5(unsigned long data); -/* - Decode data transmitted by workbench. -*/ -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) +static void variax_activate_async(struct usb_line6_variax *variax, int a) { - struct usb_line6_variax *variax = (struct usb_line6_variax *)arg; - variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = 1; + variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; line6_send_raw_message_async(&variax->line6, variax->buffer_activate, sizeof(variax_activate)); } /* - Send an asynchronous activation request after a given interval. + Variax startup procedure. + This is a sequence of functions with special requirements (e.g., must + not run immediately after initialization, must not run in interrupt + context). After the last one has finished, the device is ready to use. */ -static void variax_activate_delayed(struct usb_line6_variax *variax, - int seconds) -{ - 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) +static void variax_startup1(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])); - } -} + CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT); -/* - "read" request on "volume" special file. -*/ -static ssize_t variax_get_volume(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); - return sprintf(buf, "%d\n", variax->volume); + /* delay startup procedure: */ + line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, + variax_startup2, (unsigned long)variax); } -/* - "write" request on "volume" special file. -*/ -static ssize_t variax_set_volume(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static void variax_startup2(unsigned long data) { - struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); - int value = simple_strtoul(buf, NULL, 10); + struct usb_line6_variax *variax = (struct usb_line6_variax *)data; + struct usb_line6 *line6 = &variax->line6; - if (line6_transmit_parameter(&variax->line6, VARIAXMIDI_volume, - value) == 0) - variax->volume = value; + /* schedule another startup procedure until startup is complete: */ + if (variax->startup_progress >= VARIAX_STARTUP_LAST) + return; - return count; -} + variax->startup_progress = VARIAX_STARTUP_VERSIONREQ; + line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, + variax_startup2, (unsigned long)variax); -/* - "read" request on "model" special file. -*/ -static ssize_t variax_get_model(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); - return sprintf(buf, "%d\n", variax->model); + /* request firmware version: */ + line6_version_request_async(line6); } -/* - "write" request on "model" special file. -*/ -static ssize_t variax_set_model(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static void variax_startup3(struct usb_line6_variax *variax) { - 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; + CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT); - return count; + /* delay startup procedure: */ + line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3, + variax_startup4, (unsigned long)variax); } -/* - "read" request on "active" special file. -*/ -static ssize_t variax_get_active(struct device *dev, - struct device_attribute *attr, char *buf) +static void variax_startup4(unsigned long data) { - struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); - return sprintf(buf, "%d\n", variax->buffer_activate[VARIAX_OFFSET_ACTIVATE]); -} + struct usb_line6_variax *variax = (struct usb_line6_variax *)data; -/* - "write" request on "active" special file. -*/ -static ssize_t variax_set_active(struct device *dev, - struct device_attribute *attr, - 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; -} + CHECK_STARTUP_PROGRESS(variax->startup_progress, + VARIAX_STARTUP_ACTIVATE); -/* - "read" request on "tone" special file. -*/ -static ssize_t variax_get_tone(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); - return sprintf(buf, "%d\n", variax->tone); + /* activate device: */ + variax_activate_async(variax, 1); + line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4, + variax_startup5, (unsigned long)variax); } -/* - "write" request on "tone" special file. -*/ -static ssize_t variax_set_tone(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static void variax_startup5(unsigned long data) { - struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); - int value = simple_strtoul(buf, NULL, 10); + struct usb_line6_variax *variax = (struct usb_line6_variax *)data; - if (line6_transmit_parameter(&variax->line6, VARIAXMIDI_tone, - value) == 0) - variax->tone = value; + CHECK_STARTUP_PROGRESS(variax->startup_progress, + VARIAX_STARTUP_WORKQUEUE); - return count; + /* schedule work for global work queue: */ + schedule_work(&variax->startup_work); } -static ssize_t get_string(char *buf, const char *data, int length) +static void variax_startup6(struct work_struct *work) { - int i; - memcpy(buf, data, length); - - for (i = length; i--;) { - char c = buf[i]; + struct usb_line6_variax *variax = + container_of(work, struct usb_line6_variax, startup_work); - 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, - struct device_attribute *attr, 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)); -} + CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP); -/* - "read" request on "bank" special file. -*/ -static ssize_t variax_get_bank(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); - line6_wait_dump(&variax->dumpreq, 0); - return get_string(buf, variax->bank, sizeof(variax->bank)); + /* ALSA audio interface: */ + line6_register_audio(&variax->line6); } /* - "read" request on "dump" special file. + Process a completely received message. */ -static ssize_t variax_get_dump(struct device *dev, - struct device_attribute *attr, char *buf) +void line6_variax_process_message(struct usb_line6_variax *variax) { - 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); -} + const unsigned char *buf = variax->line6.buffer_message; -#if CREATE_RAW_FILE + switch (buf[0]) { + case LINE6_RESET: + dev_info(variax->line6.ifcdev, "VARIAX reset\n"); + break; -/* - "write" request on "raw" special file. -*/ -static ssize_t variax_set_raw2(struct device *dev, - struct device_attribute *attr, - 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; + case LINE6_SYSEX_BEGIN: + if (memcmp(buf + 1, variax_init_version + 1, + sizeof(variax_init_version) - 1) == 0) { + variax_startup3(variax); + } else if (memcmp(buf + 1, variax_init_done + 1, + sizeof(variax_init_done) - 1) == 0) { + /* notify of complete initialization: */ + variax_startup4((unsigned long)variax); + } + break; } - - 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); + line6_cleanup_audio(&variax->line6); - /* free dump request data: */ - line6_dumpreq_destructbuf(&variax->dumpreq, 2); - line6_dumpreq_destructbuf(&variax->dumpreq, 1); - line6_dumpreq_destruct(&variax->dumpreq); + del_timer(&variax->startup_timer1); + del_timer(&variax->startup_timer2); + cancel_work_sync(&variax->startup_work); kfree(variax->buffer_activate); - del_timer_sync(&variax->activate_timer); } /* - Create sysfs entries. + Try to init workbench device. */ -static int variax_create_files2(struct device *dev) +static int variax_try_init(struct usb_interface *interface, + struct usb_line6_variax *variax) { 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; + init_timer(&variax->startup_timer1); + init_timer(&variax->startup_timer2); + INIT_WORK(&variax->startup_work, variax_startup6); 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); + variax->buffer_activate = kmemdup(variax_activate, + 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: */ - err = variax_create_files(0, 0, &interface->dev); - if (err < 0) { - variax_destruct(interface); - return err; - } - - err = variax_create_files2(&interface->dev); - if (err < 0) { - variax_destruct(interface); - return err; - } - /* initialize audio system: */ err = line6_init_audio(&variax->line6); - if (err < 0) { - variax_destruct(interface); + if (err < 0) return err; - } /* initialize MIDI subsystem: */ err = line6_init_midi(&variax->line6); - if (err < 0) { - variax_destruct(interface); + if (err < 0) return err; - } - /* register audio system: */ - err = line6_register_audio(&variax->line6); - if (err < 0) { + /* initiate startup procedure: */ + variax_startup1(variax); + return 0; +} + +/* + Init workbench device (and clean up in case of failure). +*/ +int line6_variax_init(struct usb_interface *interface, + struct usb_line6_variax *variax) +{ + int err = variax_try_init(interface, variax); + + if (err < 0) variax_destruct(interface); - return err; - } - variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY); - line6_startup_delayed(&variax->dumpreq, VARIAX_STARTUP_DELAY, - variax_startup_timeout, variax); - return 0; + return err; } /* Workbench device disconnected. */ -void variax_disconnect(struct usb_interface *interface) +void line6_variax_disconnect(struct usb_interface *interface) { - struct device *dev; - 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); } diff --git a/drivers/staging/line6/variax.h b/drivers/staging/line6/variax.h index ee330ba3089..24de79620d8 100644 --- a/drivers/staging/line6/variax.h +++ b/drivers/staging/line6/variax.h @@ -1,7 +1,7 @@ /* - * Line6 Linux USB driver - 0.8.0 + * Line6 Linux USB driver - 0.9.1beta * - * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) + * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -12,97 +12,61 @@ #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 -}; +#include "driver.h" +#define VARIAX_STARTUP_DELAY1 1000 +#define VARIAX_STARTUP_DELAY3 100 +#define VARIAX_STARTUP_DELAY4 100 -/** - Binary Variax model dump +/* + Stages of Variax startup procedure */ -struct variax_model { - /** - Header information (including program name). - */ - unsigned char name[18]; - - /** - Model parameters. - */ - unsigned char control[78 * 2]; +enum { + VARIAX_STARTUP_INIT = 1, + VARIAX_STARTUP_VERSIONREQ, + VARIAX_STARTUP_WAIT, + VARIAX_STARTUP_ACTIVATE, + VARIAX_STARTUP_WORKQUEUE, + VARIAX_STARTUP_SETUP, + VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1 }; struct usb_line6_variax { /** - Generic Line6 USB data. + Generic Line6 USB data. */ struct usb_line6 line6; /** - Dump request structure. - Append two extra buffers for 3-pass data query. - */ - struct line6_dump_request dumpreq; struct line6_dump_reqbuf extrabuf[2]; - - /** - Buffer for activation code. + Buffer for activation code. */ unsigned char *buffer_activate; /** - Model number. - */ - int model; - - /** - Current model settings. + Handler for device initializaton. */ - struct variax_model model_data; + struct work_struct startup_work; /** - Name of current model bank. + Timers for device initializaton. */ - unsigned char bank[18]; + struct timer_list startup_timer1; + struct timer_list startup_timer2; /** - Position of volume dial. + Current progress in startup procedure. */ - int volume; - - /** - Position of tone control dial. - */ - int tone; - - /** - Timer for delayed activation request. - */ - struct timer_list activate_timer; + int startup_progress; }; - -extern void variax_disconnect(struct usb_interface *interface); -extern int variax_init(struct usb_interface *interface, - struct usb_line6_variax *variax); -extern void variax_process_message(struct usb_line6_variax *variax); - +extern void line6_variax_disconnect(struct usb_interface *interface); +extern int line6_variax_init(struct usb_interface *interface, + struct usb_line6_variax *variax); +extern void line6_variax_process_message(struct usb_line6_variax *variax); #endif |
